blob: 16f5c235ecd718cc46a7d56e6ac2770802ed7677 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001//! Crate specific information embedded into [crate::context::Context] objects.
2
3use std::collections::{BTreeMap, BTreeSet};
4
5use cargo_metadata::{Node, Package, PackageId};
6use serde::{Deserialize, Serialize};
7
Adam Snaider1c095c92023-07-08 02:09:58 -04008use crate::config::{CrateId, GenBinaries};
Brian Silvermancc09f182022-03-09 15:40:20 -08009use crate::metadata::{CrateAnnotation, Dependency, PairredExtras, SourceAnnotation};
10use crate::utils::sanitize_module_name;
11use crate::utils::starlark::{Glob, SelectList, SelectMap, SelectStringDict, SelectStringList};
12
13#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
14pub struct CrateDependency {
15 /// The [CrateId] of the dependency
16 pub id: CrateId,
17
18 /// The target name of the dependency. Note this may differ from the
19 /// dependency's package name in cases such as build scripts.
20 pub target: String,
21
22 /// Some dependencies are assigned aliases. This is tracked here
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub alias: Option<String>,
25}
26
27#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
28#[serde(default)]
29pub struct TargetAttributes {
30 /// The module name of the crate (notably, not the package name).
Adam Snaider1c095c92023-07-08 02:09:58 -040031 //
32 // This must be the first field of `TargetAttributes` to make it the
33 // lexicographically first thing the derived `Ord` implementation will sort
34 // by. The `Ord` impl controls the order of multiple rules of the same type
35 // in the same BUILD file. In particular, this makes packages with multiple
36 // bin crates generate those `rust_binary` targets in alphanumeric order.
Brian Silvermancc09f182022-03-09 15:40:20 -080037 pub crate_name: String,
38
39 /// The path to the crate's root source file, relative to the manifest.
40 pub crate_root: Option<String>,
41
42 /// A glob pattern of all source files required by the target
43 pub srcs: Glob,
44}
45
46#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
47pub enum Rule {
Adam Snaider1c095c92023-07-08 02:09:58 -040048 /// `rust_library`
49 Library(TargetAttributes),
Brian Silvermancc09f182022-03-09 15:40:20 -080050
51 /// `rust_proc_macro`
52 ProcMacro(TargetAttributes),
53
Brian Silvermancc09f182022-03-09 15:40:20 -080054 /// `rust_binary`
55 Binary(TargetAttributes),
Adam Snaider1c095c92023-07-08 02:09:58 -040056
57 /// `cargo_build_script`
58 BuildScript(TargetAttributes),
59}
60
61#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
62#[serde(untagged)]
63pub enum CrateFeatures {
64 // Not populated going forward. This just exists for backward compatiblity
65 // with old lock files.
66 LegacySet(BTreeSet<String>),
67
68 /// Map from target triplet to set of features.
69 SelectList(SelectList<String>),
70}
71
72impl Default for CrateFeatures {
73 fn default() -> Self {
74 CrateFeatures::SelectList(Default::default())
75 }
76}
77
78impl From<&CrateFeatures> for SelectList<String> {
79 fn from(value: &CrateFeatures) -> Self {
80 match value {
81 CrateFeatures::LegacySet(s) => {
82 let mut sl = SelectList::default();
83 for v in s {
84 sl.insert(v.clone(), None);
85 }
86 sl
87 }
88 CrateFeatures::SelectList(sl) => sl.clone(),
89 }
90 }
91}
92
93impl CrateFeatures {
94 pub fn is_empty(&self) -> bool {
95 match self {
96 CrateFeatures::LegacySet(s) => s.is_empty(),
97 CrateFeatures::SelectList(sl) => sl.is_empty(),
98 }
99 }
Brian Silvermancc09f182022-03-09 15:40:20 -0800100}
101
102/// A set of attributes common to most `rust_library`, `rust_proc_macro`, and other
103/// [core rules of `rules_rust`](https://bazelbuild.github.io/rules_rust/defs.html).
104#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
105#[serde(default)]
106pub struct CommonAttributes {
Adam Snaider1c095c92023-07-08 02:09:58 -0400107 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800108 pub compile_data: SelectStringList,
109
110 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
111 pub compile_data_glob: BTreeSet<String>,
112
Adam Snaider1c095c92023-07-08 02:09:58 -0400113 #[serde(skip_serializing_if = "CrateFeatures::is_empty")]
114 pub crate_features: CrateFeatures,
Brian Silvermancc09f182022-03-09 15:40:20 -0800115
Adam Snaider1c095c92023-07-08 02:09:58 -0400116 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800117 pub data: SelectStringList,
118
119 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
120 pub data_glob: BTreeSet<String>,
121
Adam Snaider1c095c92023-07-08 02:09:58 -0400122 #[serde(skip_serializing_if = "SelectList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800123 pub deps: SelectList<CrateDependency>,
124
125 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
126 pub extra_deps: BTreeSet<String>,
127
Adam Snaider1c095c92023-07-08 02:09:58 -0400128 #[serde(skip_serializing_if = "SelectList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800129 pub deps_dev: SelectList<CrateDependency>,
130
131 pub edition: String,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub linker_script: Option<String>,
135
Adam Snaider1c095c92023-07-08 02:09:58 -0400136 #[serde(skip_serializing_if = "SelectList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800137 pub proc_macro_deps: SelectList<CrateDependency>,
138
139 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
140 pub extra_proc_macro_deps: BTreeSet<String>,
141
Adam Snaider1c095c92023-07-08 02:09:58 -0400142 #[serde(skip_serializing_if = "SelectList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800143 pub proc_macro_deps_dev: SelectList<CrateDependency>,
144
Adam Snaider1c095c92023-07-08 02:09:58 -0400145 #[serde(skip_serializing_if = "SelectStringDict::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800146 pub rustc_env: SelectStringDict,
147
Adam Snaider1c095c92023-07-08 02:09:58 -0400148 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800149 pub rustc_env_files: SelectStringList,
150
Brian Silverman5f6f2762022-08-13 19:30:05 -0700151 #[serde(skip_serializing_if = "Vec::is_empty")]
152 pub rustc_flags: Vec<String>,
Brian Silvermancc09f182022-03-09 15:40:20 -0800153
154 pub version: String,
155
156 #[serde(skip_serializing_if = "Vec::is_empty")]
157 pub tags: Vec<String>,
158}
159
160impl Default for CommonAttributes {
161 fn default() -> Self {
162 Self {
163 compile_data: Default::default(),
164 // Generated targets include all files in their package by default
165 compile_data_glob: BTreeSet::from(["**".to_owned()]),
166 crate_features: Default::default(),
167 data: Default::default(),
168 data_glob: Default::default(),
169 deps: Default::default(),
170 extra_deps: Default::default(),
171 deps_dev: Default::default(),
172 edition: Default::default(),
173 linker_script: Default::default(),
174 proc_macro_deps: Default::default(),
175 extra_proc_macro_deps: Default::default(),
176 proc_macro_deps_dev: Default::default(),
177 rustc_env: Default::default(),
178 rustc_env_files: Default::default(),
179 rustc_flags: Default::default(),
180 version: Default::default(),
181 tags: Default::default(),
182 }
183 }
184}
185
186// Build script attributes. See
187// https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script
188#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
189#[serde(default)]
190pub struct BuildScriptAttributes {
Adam Snaider1c095c92023-07-08 02:09:58 -0400191 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800192 pub compile_data: SelectStringList,
193
Adam Snaider1c095c92023-07-08 02:09:58 -0400194 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800195 pub data: SelectStringList,
196
197 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
198 pub data_glob: BTreeSet<String>,
199
Adam Snaider1c095c92023-07-08 02:09:58 -0400200 #[serde(skip_serializing_if = "SelectList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800201 pub deps: SelectList<CrateDependency>,
202
203 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
204 pub extra_deps: BTreeSet<String>,
205
Adam Snaider1c095c92023-07-08 02:09:58 -0400206 #[serde(skip_serializing_if = "SelectStringDict::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800207 pub build_script_env: SelectStringDict,
208
209 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
210 pub extra_proc_macro_deps: BTreeSet<String>,
211
Adam Snaider1c095c92023-07-08 02:09:58 -0400212 #[serde(skip_serializing_if = "SelectList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800213 pub proc_macro_deps: SelectList<CrateDependency>,
214
Adam Snaider1c095c92023-07-08 02:09:58 -0400215 #[serde(skip_serializing_if = "SelectStringDict::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800216 pub rustc_env: SelectStringDict,
217
Adam Snaider1c095c92023-07-08 02:09:58 -0400218 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800219 pub rustc_flags: SelectStringList,
220
Adam Snaider1c095c92023-07-08 02:09:58 -0400221 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800222 pub rustc_env_files: SelectStringList,
223
Adam Snaider1c095c92023-07-08 02:09:58 -0400224 #[serde(skip_serializing_if = "SelectStringList::is_empty")]
Brian Silvermancc09f182022-03-09 15:40:20 -0800225 pub tools: SelectStringList,
226
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub links: Option<String>,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700229
230 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
231 pub toolchains: BTreeSet<String>,
Brian Silvermancc09f182022-03-09 15:40:20 -0800232}
233
234impl Default for BuildScriptAttributes {
235 fn default() -> Self {
236 Self {
237 compile_data: Default::default(),
238 data: Default::default(),
239 // Build scripts include all sources by default
240 data_glob: BTreeSet::from(["**".to_owned()]),
241 deps: Default::default(),
242 extra_deps: Default::default(),
243 build_script_env: Default::default(),
244 extra_proc_macro_deps: Default::default(),
245 proc_macro_deps: Default::default(),
246 rustc_env: Default::default(),
247 rustc_flags: Default::default(),
248 rustc_env_files: Default::default(),
249 tools: Default::default(),
250 links: Default::default(),
Brian Silverman5f6f2762022-08-13 19:30:05 -0700251 toolchains: Default::default(),
Brian Silvermancc09f182022-03-09 15:40:20 -0800252 }
253 }
254}
255
256#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
257#[serde(default)]
258pub struct CrateContext {
259 /// The package name of the current crate
260 pub name: String,
261
262 /// The full version of the current crate
263 pub version: String,
264
265 /// Optional source annotations if they were discoverable in the
266 /// lockfile. Workspace Members will not have source annotations and
267 /// potentially others.
268 pub repository: Option<SourceAnnotation>,
269
270 /// A list of all targets (lib, proc-macro, bin) associated with this package
Adam Snaider1c095c92023-07-08 02:09:58 -0400271 pub targets: BTreeSet<Rule>,
Brian Silvermancc09f182022-03-09 15:40:20 -0800272
273 /// The name of the crate's root library target. This is the target that a dependent
274 /// would get if they were to depend on `{crate_name}`.
275 pub library_target_name: Option<String>,
276
277 /// A set of attributes common to most [Rule] types or target types.
278 pub common_attrs: CommonAttributes,
279
280 /// Optional attributes for build scripts. This field is only populated if
281 /// a build script (`custom-build`) target is defined for the crate.
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub build_script_attrs: Option<BuildScriptAttributes>,
284
285 /// The license used by the crate
286 pub license: Option<String>,
287
288 /// Additional text to add to the generated BUILD file.
289 #[serde(skip_serializing_if = "Option::is_none")]
290 pub additive_build_file_content: Option<String>,
Adam Snaider1c095c92023-07-08 02:09:58 -0400291
292 /// If true, disables pipelining for library targets generated for this crate
293 #[serde(skip_serializing_if = "std::ops::Not::not")]
294 pub disable_pipelining: bool,
Brian Silvermancc09f182022-03-09 15:40:20 -0800295}
296
297impl CrateContext {
298 pub fn new(
299 annotation: &CrateAnnotation,
300 packages: &BTreeMap<PackageId, Package>,
301 source_annotations: &BTreeMap<PackageId, SourceAnnotation>,
302 extras: &BTreeMap<CrateId, PairredExtras>,
Adam Snaider1c095c92023-07-08 02:09:58 -0400303 features: &BTreeMap<CrateId, SelectList<String>>,
304 include_binaries: bool,
Brian Silvermancc09f182022-03-09 15:40:20 -0800305 include_build_scripts: bool,
306 ) -> Self {
307 let package: &Package = &packages[&annotation.node.id];
308 let current_crate_id = CrateId::new(package.name.clone(), package.version.to_string());
309
310 let new_crate_dep = |dep: Dependency| -> CrateDependency {
311 let pkg = &packages[&dep.package_id];
312
313 // Unfortunately, The package graph and resolve graph of cargo metadata have different representations
314 // for the crate names (resolve graph sanitizes names to match module names) so to get the rest of this
315 // content to align when rendering, the dependency target needs to be explicitly sanitized.
316 let target = sanitize_module_name(&dep.target_name);
317
318 CrateDependency {
319 id: CrateId::new(pkg.name.clone(), pkg.version.to_string()),
320 target,
321 alias: dep.alias,
322 }
323 };
324
325 // Convert the dependencies into renderable strings
326 let deps = annotation.deps.normal_deps.clone().map(new_crate_dep);
327 let deps_dev = annotation.deps.normal_dev_deps.clone().map(new_crate_dep);
328 let proc_macro_deps = annotation.deps.proc_macro_deps.clone().map(new_crate_dep);
329 let proc_macro_deps_dev = annotation
330 .deps
331 .proc_macro_dev_deps
332 .clone()
333 .map(new_crate_dep);
334
335 // Gather all "common" attributes
336 let mut common_attrs = CommonAttributes {
Adam Snaider1c095c92023-07-08 02:09:58 -0400337 crate_features: CrateFeatures::SelectList(
338 features
339 .get(&current_crate_id)
340 .map_or_else(SelectList::default, |f| f.clone()),
341 ),
Brian Silvermancc09f182022-03-09 15:40:20 -0800342 deps,
343 deps_dev,
Adam Snaider1c095c92023-07-08 02:09:58 -0400344 edition: package.edition.as_str().to_string(),
Brian Silvermancc09f182022-03-09 15:40:20 -0800345 proc_macro_deps,
346 proc_macro_deps_dev,
347 version: package.version.to_string(),
348 ..Default::default()
349 };
350
Adam Snaider1c095c92023-07-08 02:09:58 -0400351 // Locate extra settings for the current package.
352 let package_extra = extras
353 .iter()
354 .find(|(_, settings)| settings.package_id == package.id);
355
Brian Silvermancc09f182022-03-09 15:40:20 -0800356 let include_build_scripts =
Adam Snaider1c095c92023-07-08 02:09:58 -0400357 Self::crate_includes_build_script(package_extra, include_build_scripts);
358
359 let gen_none = GenBinaries::Some(BTreeSet::new());
360 let gen_binaries = package_extra
361 .and_then(|(_, settings)| settings.crate_extra.gen_binaries.as_ref())
362 .unwrap_or(if include_binaries {
363 &GenBinaries::All
364 } else {
365 &gen_none
366 });
Brian Silvermancc09f182022-03-09 15:40:20 -0800367
368 // Iterate over each target and produce a Bazel target for all supported "kinds"
Adam Snaider1c095c92023-07-08 02:09:58 -0400369 let targets = Self::collect_targets(
370 &annotation.node,
371 packages,
372 gen_binaries,
373 include_build_scripts,
374 );
Brian Silvermancc09f182022-03-09 15:40:20 -0800375
376 // Parse the library crate name from the set of included targets
377 let library_target_name = {
378 let lib_targets: Vec<&TargetAttributes> = targets
379 .iter()
380 .filter_map(|t| match t {
381 Rule::ProcMacro(attrs) => Some(attrs),
382 Rule::Library(attrs) => Some(attrs),
383 _ => None,
384 })
385 .collect();
386
387 // TODO: There should only be at most 1 library target. This case
388 // should be handled in a more intelligent way.
389 assert!(lib_targets.len() <= 1);
390 lib_targets
391 .iter()
392 .last()
393 .map(|attr| attr.crate_name.clone())
394 };
395
396 // Gather any build-script related attributes
397 let build_script_target = targets.iter().find_map(|r| match r {
398 Rule::BuildScript(attr) => Some(attr),
399 _ => None,
400 });
401
402 let build_script_attrs = if let Some(target) = build_script_target {
403 // Track the build script dependency
404 common_attrs.deps.insert(
405 CrateDependency {
406 id: current_crate_id,
407 target: target.crate_name.clone(),
408 alias: None,
409 },
410 None,
411 );
412
413 let build_deps = annotation.deps.build_deps.clone().map(new_crate_dep);
414 let build_proc_macro_deps = annotation
415 .deps
416 .build_proc_macro_deps
417 .clone()
418 .map(new_crate_dep);
419
420 Some(BuildScriptAttributes {
421 deps: build_deps,
422 proc_macro_deps: build_proc_macro_deps,
423 links: package.links.clone(),
424 ..Default::default()
425 })
426 } else {
427 None
428 };
429
430 // Save the repository information for the current crate
431 let repository = source_annotations.get(&package.id).cloned();
432
433 // Identify the license type
434 let license = package.license.clone();
435
436 // Create the crate's context and apply extra settings
437 CrateContext {
438 name: package.name.clone(),
439 version: package.version.to_string(),
440 repository,
441 targets,
442 library_target_name,
443 common_attrs,
444 build_script_attrs,
445 license,
446 additive_build_file_content: None,
Adam Snaider1c095c92023-07-08 02:09:58 -0400447 disable_pipelining: false,
Brian Silvermancc09f182022-03-09 15:40:20 -0800448 }
449 .with_overrides(extras)
450 }
451
452 fn with_overrides(mut self, extras: &BTreeMap<CrateId, PairredExtras>) -> Self {
453 let id = CrateId::new(self.name.clone(), self.version.clone());
454
455 // Insert all overrides/extras
456 if let Some(pairred_override) = extras.get(&id) {
457 let crate_extra = &pairred_override.crate_extra;
458
459 // Deps
460 if let Some(extra) = &crate_extra.deps {
461 self.common_attrs.extra_deps = extra.clone();
462 }
463
464 // Proc macro deps
465 if let Some(extra) = &crate_extra.proc_macro_deps {
466 self.common_attrs.extra_proc_macro_deps = extra.clone();
467 }
468
469 // Compile data
470 if let Some(extra) = &crate_extra.compile_data {
471 for data in extra.iter() {
472 self.common_attrs.compile_data.insert(data.clone(), None);
473 }
474 }
475
476 // Compile data glob
477 if let Some(extra) = &crate_extra.compile_data_glob {
478 self.common_attrs.compile_data_glob.extend(extra.clone());
479 }
480
481 // Crate features
482 if let Some(extra) = &crate_extra.crate_features {
Adam Snaider1c095c92023-07-08 02:09:58 -0400483 match &mut self.common_attrs.crate_features {
484 CrateFeatures::LegacySet(s) => s.append(&mut extra.clone()),
485 CrateFeatures::SelectList(sl) => {
486 for data in extra.iter() {
487 sl.insert(data.clone(), None);
488 }
489 }
Brian Silvermancc09f182022-03-09 15:40:20 -0800490 }
491 }
492
493 // Data
494 if let Some(extra) = &crate_extra.data {
495 for data in extra.iter() {
496 self.common_attrs.data.insert(data.clone(), None);
497 }
498 }
499
500 // Data glob
501 if let Some(extra) = &crate_extra.data_glob {
502 self.common_attrs.data_glob.extend(extra.clone());
503 }
504
Adam Snaider1c095c92023-07-08 02:09:58 -0400505 // Disable pipelining
506 if crate_extra.disable_pipelining {
507 self.disable_pipelining = true;
508 }
509
Brian Silvermancc09f182022-03-09 15:40:20 -0800510 // Rustc flags
Brian Silvermancc09f182022-03-09 15:40:20 -0800511 if let Some(extra) = &crate_extra.rustc_flags {
Brian Silverman5f6f2762022-08-13 19:30:05 -0700512 self.common_attrs.rustc_flags.append(&mut extra.clone());
Brian Silvermancc09f182022-03-09 15:40:20 -0800513 }
514
515 // Rustc env
516 if let Some(extra) = &crate_extra.rustc_env {
Adam Snaider1c095c92023-07-08 02:09:58 -0400517 self.common_attrs.rustc_env.extend(extra.clone(), None);
Brian Silvermancc09f182022-03-09 15:40:20 -0800518 }
519
520 // Rustc env files
521 if let Some(extra) = &crate_extra.rustc_env_files {
522 for data in extra.iter() {
523 self.common_attrs.rustc_env_files.insert(data.clone(), None);
524 }
525 }
526
527 // Build script Attributes
528 if let Some(attrs) = &mut self.build_script_attrs {
529 // Deps
530 if let Some(extra) = &crate_extra.build_script_deps {
531 attrs.extra_deps = extra.clone();
532 }
533
534 // Proc macro deps
535 if let Some(extra) = &crate_extra.build_script_proc_macro_deps {
536 attrs.extra_proc_macro_deps = extra.clone();
537 }
538
539 // Data
540 if let Some(extra) = &crate_extra.build_script_data {
541 for data in extra {
542 attrs.data.insert(data.clone(), None);
543 }
544 }
545
Brian Silverman5f6f2762022-08-13 19:30:05 -0700546 // Tools
547 if let Some(extra) = &crate_extra.build_script_tools {
548 for data in extra {
549 attrs.tools.insert(data.clone(), None);
550 }
551 }
552
553 // Toolchains
554 if let Some(extra) = &crate_extra.build_script_toolchains {
555 for data in extra {
556 attrs.toolchains.insert(data.clone());
557 }
558 }
559
Brian Silvermancc09f182022-03-09 15:40:20 -0800560 // Data glob
561 if let Some(extra) = &crate_extra.build_script_data_glob {
562 attrs.data_glob.extend(extra.clone());
563 }
564
565 // Rustc env
566 if let Some(extra) = &crate_extra.build_script_rustc_env {
Adam Snaider1c095c92023-07-08 02:09:58 -0400567 attrs.rustc_env.extend(extra.clone(), None);
Brian Silvermancc09f182022-03-09 15:40:20 -0800568 }
569
570 // Build script env
571 if let Some(extra) = &crate_extra.build_script_env {
Adam Snaider1c095c92023-07-08 02:09:58 -0400572 attrs.build_script_env.extend(extra.clone(), None);
Brian Silvermancc09f182022-03-09 15:40:20 -0800573 }
574 }
575
576 // Extra build contents
577 self.additive_build_file_content = crate_extra
578 .additive_build_file_content
579 .as_ref()
580 .map(|content| {
581 // For prettier rendering, dedent the build contents
582 textwrap::dedent(content)
583 });
584
585 // Git shallow_since
586 if let Some(SourceAnnotation::Git { shallow_since, .. }) = &mut self.repository {
587 *shallow_since = crate_extra.shallow_since.clone()
588 }
589
590 // Patch attributes
591 if let Some(repository) = &mut self.repository {
592 match repository {
593 SourceAnnotation::Git {
594 patch_args,
595 patch_tool,
596 patches,
597 ..
598 } => {
599 *patch_args = crate_extra.patch_args.clone();
600 *patch_tool = crate_extra.patch_tool.clone();
601 *patches = crate_extra.patches.clone();
602 }
603 SourceAnnotation::Http {
604 patch_args,
605 patch_tool,
606 patches,
607 ..
608 } => {
609 *patch_args = crate_extra.patch_args.clone();
610 *patch_tool = crate_extra.patch_tool.clone();
611 *patches = crate_extra.patches.clone();
612 }
613 }
614 }
615 }
616
617 self
618 }
619
620 /// Determine whether or not a crate __should__ include a build script
621 /// (build.rs) if it happens to have one.
622 fn crate_includes_build_script(
Adam Snaider1c095c92023-07-08 02:09:58 -0400623 package_extra: Option<(&CrateId, &PairredExtras)>,
Brian Silvermancc09f182022-03-09 15:40:20 -0800624 default_generate_build_script: bool,
625 ) -> bool {
Brian Silvermancc09f182022-03-09 15:40:20 -0800626 // If the crate has extra settings, which explicitly set `gen_build_script`, always use
627 // this value, otherwise, fallback to the provided default.
Adam Snaider1c095c92023-07-08 02:09:58 -0400628 package_extra
Brian Silvermancc09f182022-03-09 15:40:20 -0800629 .and_then(|(_, settings)| settings.crate_extra.gen_build_script)
630 .unwrap_or(default_generate_build_script)
631 }
632
633 /// Collect all Bazel targets that should be generated for a particular Package
634 fn collect_targets(
635 node: &Node,
636 packages: &BTreeMap<PackageId, Package>,
Adam Snaider1c095c92023-07-08 02:09:58 -0400637 gen_binaries: &GenBinaries,
Brian Silvermancc09f182022-03-09 15:40:20 -0800638 include_build_scripts: bool,
Adam Snaider1c095c92023-07-08 02:09:58 -0400639 ) -> BTreeSet<Rule> {
Brian Silvermancc09f182022-03-09 15:40:20 -0800640 let package = &packages[&node.id];
641
642 let package_root = package
643 .manifest_path
644 .as_std_path()
645 .parent()
646 .expect("Every manifest should have a parent directory");
647
648 package
649 .targets
650 .iter()
651 .flat_map(|target| {
Adam Snaider1c095c92023-07-08 02:09:58 -0400652 target.kind.iter().filter_map(move |kind| {
653 // Unfortunately, The package graph and resolve graph of cargo metadata have different representations
654 // for the crate names (resolve graph sanitizes names to match module names) so to get the rest of this
655 // content to align when rendering, the package target names are always sanitized.
656 let crate_name = sanitize_module_name(&target.name);
Brian Silvermancc09f182022-03-09 15:40:20 -0800657
Adam Snaider1c095c92023-07-08 02:09:58 -0400658 // Locate the crate's root source file relative to the package root normalized for unix
659 let crate_root = pathdiff::diff_paths(&target.src_path, package_root).map(
660 // Normalize the path so that it always renders the same regardless of platform
661 |root| root.to_string_lossy().replace('\\', "/"),
662 );
Brian Silvermancc09f182022-03-09 15:40:20 -0800663
Adam Snaider1c095c92023-07-08 02:09:58 -0400664 // Conditionally check to see if the dependencies is a build-script target
665 if include_build_scripts && kind == "custom-build" {
666 return Some(Rule::BuildScript(TargetAttributes {
667 crate_name,
668 crate_root,
669 srcs: Glob::new_rust_srcs(),
670 }));
671 }
672
673 // Check to see if the dependencies is a proc-macro target
674 if kind == "proc-macro" {
675 return Some(Rule::ProcMacro(TargetAttributes {
676 crate_name,
677 crate_root,
678 srcs: Glob::new_rust_srcs(),
679 }));
680 }
681
682 // Check to see if the dependencies is a library target
683 if ["lib", "rlib"].contains(&kind.as_str()) {
684 return Some(Rule::Library(TargetAttributes {
685 crate_name,
686 crate_root,
687 srcs: Glob::new_rust_srcs(),
688 }));
689 }
690
691 // Check if the target kind is binary and is one of the ones included in gen_binaries
692 if kind == "bin"
693 && match gen_binaries {
694 GenBinaries::All => true,
695 GenBinaries::Some(set) => set.contains(&target.name),
Brian Silvermancc09f182022-03-09 15:40:20 -0800696 }
Adam Snaider1c095c92023-07-08 02:09:58 -0400697 {
698 return Some(Rule::Binary(TargetAttributes {
699 crate_name: target.name.clone(),
700 crate_root,
701 srcs: Glob::new_rust_srcs(),
702 }));
703 }
Brian Silvermancc09f182022-03-09 15:40:20 -0800704
Adam Snaider1c095c92023-07-08 02:09:58 -0400705 None
706 })
Brian Silvermancc09f182022-03-09 15:40:20 -0800707 })
708 .collect()
709 }
710}
711
712#[cfg(test)]
713mod test {
714 use super::*;
715
716 use crate::config::CrateAnnotations;
717 use crate::metadata::Annotations;
718
719 fn common_annotations() -> Annotations {
720 Annotations::new(
721 crate::test::metadata::common(),
722 crate::test::lockfile::common(),
723 crate::config::Config::default(),
724 )
725 .unwrap()
726 }
727
728 #[test]
729 fn new_context() {
730 let annotations = common_annotations();
731
732 let crate_annotation = &annotations.metadata.crates[&PackageId {
733 repr: "common 0.1.0 (path+file://{TEMP_DIR}/common)".to_owned(),
734 }];
735
Adam Snaider1c095c92023-07-08 02:09:58 -0400736 let include_binaries = false;
737 let include_build_scripts = false;
Brian Silvermancc09f182022-03-09 15:40:20 -0800738 let context = CrateContext::new(
739 crate_annotation,
740 &annotations.metadata.packages,
741 &annotations.lockfile.crates,
742 &annotations.pairred_extras,
Adam Snaider1c095c92023-07-08 02:09:58 -0400743 &annotations.features,
744 include_binaries,
745 include_build_scripts,
Brian Silvermancc09f182022-03-09 15:40:20 -0800746 );
747
748 assert_eq!(context.name, "common");
749 assert_eq!(
750 context.targets,
Adam Snaider1c095c92023-07-08 02:09:58 -0400751 BTreeSet::from([Rule::Library(TargetAttributes {
752 crate_name: "common".to_owned(),
753 crate_root: Some("lib.rs".to_owned()),
754 srcs: Glob::new_rust_srcs(),
755 })]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800756 );
757 }
758
759 #[test]
760 fn context_with_overrides() {
761 let annotations = common_annotations();
762
763 let package_id = PackageId {
764 repr: "common 0.1.0 (path+file://{TEMP_DIR}/common)".to_owned(),
765 };
766
767 let crate_annotation = &annotations.metadata.crates[&package_id];
768
769 let mut pairred_extras = BTreeMap::new();
770 pairred_extras.insert(
771 CrateId::new("common".to_owned(), "0.1.0".to_owned()),
772 PairredExtras {
773 package_id,
774 crate_extra: CrateAnnotations {
Adam Snaider1c095c92023-07-08 02:09:58 -0400775 gen_binaries: Some(GenBinaries::All),
Brian Silvermancc09f182022-03-09 15:40:20 -0800776 data_glob: Some(BTreeSet::from(["**/data_glob/**".to_owned()])),
777 ..CrateAnnotations::default()
778 },
779 },
780 );
781
Adam Snaider1c095c92023-07-08 02:09:58 -0400782 let include_binaries = false;
783 let include_build_scripts = false;
Brian Silvermancc09f182022-03-09 15:40:20 -0800784 let context = CrateContext::new(
785 crate_annotation,
786 &annotations.metadata.packages,
787 &annotations.lockfile.crates,
788 &pairred_extras,
Adam Snaider1c095c92023-07-08 02:09:58 -0400789 &annotations.features,
790 include_binaries,
791 include_build_scripts,
Brian Silvermancc09f182022-03-09 15:40:20 -0800792 );
793
794 assert_eq!(context.name, "common");
795 assert_eq!(
796 context.targets,
Adam Snaider1c095c92023-07-08 02:09:58 -0400797 BTreeSet::from([
Brian Silvermancc09f182022-03-09 15:40:20 -0800798 Rule::Library(TargetAttributes {
799 crate_name: "common".to_owned(),
800 crate_root: Some("lib.rs".to_owned()),
801 srcs: Glob::new_rust_srcs(),
802 }),
803 Rule::Binary(TargetAttributes {
804 crate_name: "common-bin".to_owned(),
805 crate_root: Some("main.rs".to_owned()),
806 srcs: Glob::new_rust_srcs(),
807 }),
Adam Snaider1c095c92023-07-08 02:09:58 -0400808 ]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800809 );
810 assert_eq!(
811 context.common_attrs.data_glob,
812 BTreeSet::from(["**/data_glob/**".to_owned()])
813 );
814 }
815
816 fn build_script_annotations() -> Annotations {
817 Annotations::new(
818 crate::test::metadata::build_scripts(),
819 crate::test::lockfile::build_scripts(),
820 crate::config::Config::default(),
821 )
822 .unwrap()
823 }
824
825 fn crate_type_annotations() -> Annotations {
826 Annotations::new(
827 crate::test::metadata::crate_types(),
828 crate::test::lockfile::crate_types(),
829 crate::config::Config::default(),
830 )
831 .unwrap()
832 }
833
834 #[test]
835 fn context_with_build_script() {
836 let annotations = build_script_annotations();
837
838 let package_id = PackageId {
Adam Snaider1c095c92023-07-08 02:09:58 -0400839 repr: "openssl-sys 0.9.87 (registry+https://github.com/rust-lang/crates.io-index)"
Brian Silvermancc09f182022-03-09 15:40:20 -0800840 .to_owned(),
841 };
842
843 let crate_annotation = &annotations.metadata.crates[&package_id];
844
Adam Snaider1c095c92023-07-08 02:09:58 -0400845 let include_binaries = false;
846 let include_build_scripts = true;
Brian Silvermancc09f182022-03-09 15:40:20 -0800847 let context = CrateContext::new(
848 crate_annotation,
849 &annotations.metadata.packages,
850 &annotations.lockfile.crates,
851 &annotations.pairred_extras,
Adam Snaider1c095c92023-07-08 02:09:58 -0400852 &annotations.features,
853 include_binaries,
854 include_build_scripts,
Brian Silvermancc09f182022-03-09 15:40:20 -0800855 );
856
857 assert_eq!(context.name, "openssl-sys");
858 assert!(context.build_script_attrs.is_some());
859 assert_eq!(
860 context.targets,
Adam Snaider1c095c92023-07-08 02:09:58 -0400861 BTreeSet::from([
Brian Silvermancc09f182022-03-09 15:40:20 -0800862 Rule::Library(TargetAttributes {
863 crate_name: "openssl_sys".to_owned(),
864 crate_root: Some("src/lib.rs".to_owned()),
865 srcs: Glob::new_rust_srcs(),
866 }),
867 Rule::BuildScript(TargetAttributes {
868 crate_name: "build_script_main".to_owned(),
869 crate_root: Some("build/main.rs".to_owned()),
870 srcs: Glob::new_rust_srcs(),
871 })
Adam Snaider1c095c92023-07-08 02:09:58 -0400872 ]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800873 );
874
875 // Cargo build scripts should include all sources
876 assert!(context.build_script_attrs.unwrap().data_glob.contains("**"));
877 }
878
879 #[test]
880 fn context_disabled_build_script() {
881 let annotations = build_script_annotations();
882
883 let package_id = PackageId {
Adam Snaider1c095c92023-07-08 02:09:58 -0400884 repr: "openssl-sys 0.9.87 (registry+https://github.com/rust-lang/crates.io-index)"
Brian Silvermancc09f182022-03-09 15:40:20 -0800885 .to_owned(),
886 };
887
888 let crate_annotation = &annotations.metadata.crates[&package_id];
889
Adam Snaider1c095c92023-07-08 02:09:58 -0400890 let include_binaries = false;
891 let include_build_scripts = false;
Brian Silvermancc09f182022-03-09 15:40:20 -0800892 let context = CrateContext::new(
893 crate_annotation,
894 &annotations.metadata.packages,
895 &annotations.lockfile.crates,
896 &annotations.pairred_extras,
Adam Snaider1c095c92023-07-08 02:09:58 -0400897 &annotations.features,
898 include_binaries,
899 include_build_scripts,
Brian Silvermancc09f182022-03-09 15:40:20 -0800900 );
901
902 assert_eq!(context.name, "openssl-sys");
903 assert!(context.build_script_attrs.is_none());
904 assert_eq!(
905 context.targets,
Adam Snaider1c095c92023-07-08 02:09:58 -0400906 BTreeSet::from([Rule::Library(TargetAttributes {
Brian Silvermancc09f182022-03-09 15:40:20 -0800907 crate_name: "openssl_sys".to_owned(),
908 crate_root: Some("src/lib.rs".to_owned()),
909 srcs: Glob::new_rust_srcs(),
Adam Snaider1c095c92023-07-08 02:09:58 -0400910 })]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800911 );
912 }
913
914 #[test]
915 fn context_rlib_crate_type() {
916 let annotations = crate_type_annotations();
917
918 let package_id = PackageId {
919 repr: "sysinfo 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)"
920 .to_owned(),
921 };
922
923 let crate_annotation = &annotations.metadata.crates[&package_id];
924
Adam Snaider1c095c92023-07-08 02:09:58 -0400925 let include_binaries = false;
926 let include_build_scripts = false;
Brian Silvermancc09f182022-03-09 15:40:20 -0800927 let context = CrateContext::new(
928 crate_annotation,
929 &annotations.metadata.packages,
930 &annotations.lockfile.crates,
931 &annotations.pairred_extras,
Adam Snaider1c095c92023-07-08 02:09:58 -0400932 &annotations.features,
933 include_binaries,
934 include_build_scripts,
Brian Silvermancc09f182022-03-09 15:40:20 -0800935 );
936
937 assert_eq!(context.name, "sysinfo");
938 assert!(context.build_script_attrs.is_none());
939 assert_eq!(
940 context.targets,
Adam Snaider1c095c92023-07-08 02:09:58 -0400941 BTreeSet::from([Rule::Library(TargetAttributes {
Brian Silvermancc09f182022-03-09 15:40:20 -0800942 crate_name: "sysinfo".to_owned(),
943 crate_root: Some("src/lib.rs".to_owned()),
944 srcs: Glob::new_rust_srcs(),
Adam Snaider1c095c92023-07-08 02:09:58 -0400945 })]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800946 );
947 }
948}