blob: 1e9818dec3d52cd7ac0689fc0face8ebd037b986 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001//! Tools for rendering and writing BUILD and other Starlark files
2
3mod template_engine;
4
Adam Snaider1c095c92023-07-08 02:09:58 -04005use std::collections::{BTreeMap, BTreeSet};
Brian Silvermancc09f182022-03-09 15:40:20 -08006use std::fs;
Adam Snaider1c095c92023-07-08 02:09:58 -04007use std::iter::FromIterator;
Brian Silvermancc09f182022-03-09 15:40:20 -08008use std::path::{Path, PathBuf};
9use std::str::FromStr;
10
11use anyhow::{bail, Context as AnyhowContext, Result};
Adam Snaider1c095c92023-07-08 02:09:58 -040012use indoc::formatdoc;
Brian Silvermancc09f182022-03-09 15:40:20 -080013
Adam Snaider1c095c92023-07-08 02:09:58 -040014use crate::config::{RenderConfig, VendorMode};
15use crate::context::crate_context::{CrateContext, CrateDependency, Rule};
16use crate::context::{Context, TargetAttributes};
Brian Silvermancc09f182022-03-09 15:40:20 -080017use crate::rendering::template_engine::TemplateEngine;
18use crate::splicing::default_splicing_package_crate_id;
Adam Snaider1c095c92023-07-08 02:09:58 -040019use crate::utils::starlark::{
20 self, Alias, CargoBuildScript, CommonAttrs, Data, ExportsFiles, Filegroup, Glob, Label, Load,
21 Package, RustBinary, RustLibrary, RustProcMacro, Select, SelectDict, SelectList, SelectMap,
22 Starlark, TargetCompatibleWith,
23};
24use crate::utils::{self, sanitize_repository_name};
25
26// Configuration remapper used to convert from cfg expressions like "cfg(unix)"
27// to platform labels like "@rules_rust//rust/platform:x86_64-unknown-linux-gnu".
28pub(crate) type Platforms = BTreeMap<String, BTreeSet<String>>;
Brian Silvermancc09f182022-03-09 15:40:20 -080029
30pub struct Renderer {
31 config: RenderConfig,
Adam Snaider1c095c92023-07-08 02:09:58 -040032 supported_platform_triples: BTreeSet<String>,
33 generate_target_compatible_with: bool,
Brian Silvermancc09f182022-03-09 15:40:20 -080034 engine: TemplateEngine,
35}
36
37impl Renderer {
Adam Snaider1c095c92023-07-08 02:09:58 -040038 pub fn new(
39 config: RenderConfig,
40 supported_platform_triples: BTreeSet<String>,
41 generate_target_compatible_with: bool,
42 ) -> Self {
Brian Silvermancc09f182022-03-09 15:40:20 -080043 let engine = TemplateEngine::new(&config);
Adam Snaider1c095c92023-07-08 02:09:58 -040044 Self {
45 config,
46 supported_platform_triples,
47 generate_target_compatible_with,
48 engine,
49 }
Brian Silvermancc09f182022-03-09 15:40:20 -080050 }
51
52 pub fn render(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
53 let mut output = BTreeMap::new();
54
Adam Snaider1c095c92023-07-08 02:09:58 -040055 let platforms = self.render_platform_labels(context);
56 output.extend(self.render_build_files(context, &platforms)?);
57 output.extend(self.render_crates_module(context, &platforms)?);
Brian Silvermancc09f182022-03-09 15:40:20 -080058
59 if let Some(vendor_mode) = &self.config.vendor_mode {
60 match vendor_mode {
61 crate::config::VendorMode::Local => {
62 // Nothing to do for local vendor crate
63 }
64 crate::config::VendorMode::Remote => {
65 output.extend(self.render_vendor_support_files(context)?);
66 }
67 }
68 }
69
70 Ok(output)
71 }
72
Adam Snaider1c095c92023-07-08 02:09:58 -040073 fn render_platform_labels(&self, context: &Context) -> BTreeMap<String, BTreeSet<String>> {
74 context
75 .conditions
76 .iter()
77 .map(|(cfg, triples)| {
78 (
79 cfg.clone(),
80 triples
81 .iter()
82 .map(|triple| {
83 render_platform_constraint_label(
84 &self.config.platforms_template,
85 triple,
86 )
87 })
88 .collect(),
89 )
90 })
91 .collect()
92 }
93
94 fn render_crates_module(
95 &self,
96 context: &Context,
97 platforms: &Platforms,
98 ) -> Result<BTreeMap<PathBuf, String>> {
Brian Silvermancc09f182022-03-09 15:40:20 -080099 let module_label = render_module_label(&self.config.crates_module_template, "defs.bzl")
100 .context("Failed to resolve string to module file label")?;
101 let module_build_label =
102 render_module_label(&self.config.crates_module_template, "BUILD.bazel")
103 .context("Failed to resolve string to module file label")?;
104
105 let mut map = BTreeMap::new();
106 map.insert(
107 Renderer::label_to_path(&module_label),
Adam Snaider1c095c92023-07-08 02:09:58 -0400108 self.engine.render_module_bzl(context, platforms)?,
Brian Silvermancc09f182022-03-09 15:40:20 -0800109 );
110 map.insert(
111 Renderer::label_to_path(&module_build_label),
Adam Snaider1c095c92023-07-08 02:09:58 -0400112 self.render_module_build_file(context)?,
Brian Silvermancc09f182022-03-09 15:40:20 -0800113 );
114
115 Ok(map)
116 }
117
Adam Snaider1c095c92023-07-08 02:09:58 -0400118 fn render_module_build_file(&self, context: &Context) -> Result<String> {
119 let mut starlark = Vec::new();
120
121 // Banner comment for top of the file.
122 let header = self.engine.render_header()?;
123 starlark.push(Starlark::Verbatim(header));
124
125 // Package visibility, exported bzl files.
126 let package = Package::default_visibility_public();
127 starlark.push(Starlark::Package(package));
128
129 let mut exports_files = ExportsFiles {
130 paths: BTreeSet::from(["cargo-bazel.json".to_owned(), "defs.bzl".to_owned()]),
131 globs: Glob {
132 include: BTreeSet::from(["*.bazel".to_owned()]),
133 exclude: BTreeSet::new(),
134 },
135 };
136 if let Some(VendorMode::Remote) = self.config.vendor_mode {
137 exports_files.paths.insert("crates.bzl".to_owned());
138 }
139 starlark.push(Starlark::ExportsFiles(exports_files));
140
141 let filegroup = Filegroup {
142 name: "srcs".to_owned(),
143 srcs: Glob {
144 include: BTreeSet::from(["*.bazel".to_owned(), "*.bzl".to_owned()]),
145 exclude: BTreeSet::new(),
146 },
147 };
148 starlark.push(Starlark::Filegroup(filegroup));
149
150 // An `alias` for each direct dependency of a workspace member crate.
151 let mut dependencies = Vec::new();
152 for dep in context.workspace_member_deps() {
153 let krate = &context.crates[&dep.id];
154 if let Some(library_target_name) = &krate.library_target_name {
155 let rename = dep.alias.as_ref().unwrap_or(&krate.name);
156 dependencies.push(Alias {
157 // If duplicates exist, include version to disambiguate them.
158 name: if context.has_duplicate_workspace_member_dep(dep) {
159 format!("{}-{}", rename, krate.version)
160 } else {
161 rename.clone()
162 },
163 actual: self.crate_label(&krate.name, &krate.version, library_target_name),
164 tags: BTreeSet::from(["manual".to_owned()]),
165 });
166 }
167 }
168 if !dependencies.is_empty() {
169 let comment = "# Workspace Member Dependencies".to_owned();
170 starlark.push(Starlark::Verbatim(comment));
171 starlark.extend(dependencies.into_iter().map(Starlark::Alias));
172 }
173
174 // An `alias` for each binary dependency.
175 let mut binaries = Vec::new();
176 for crate_id in &context.binary_crates {
177 let krate = &context.crates[crate_id];
178 for rule in &krate.targets {
179 if let Rule::Binary(bin) = rule {
180 binaries.push(Alias {
181 // If duplicates exist, include version to disambiguate them.
182 name: if context.has_duplicate_binary_crate(crate_id) {
183 format!("{}-{}__{}", krate.name, krate.version, bin.crate_name)
184 } else {
185 format!("{}__{}", krate.name, bin.crate_name)
186 },
187 actual: self.crate_label(
188 &krate.name,
189 &krate.version,
190 &format!("{}__bin", bin.crate_name),
191 ),
192 tags: BTreeSet::from(["manual".to_owned()]),
193 });
194 }
195 }
196 }
197 if !binaries.is_empty() {
198 let comment = "# Binaries".to_owned();
199 starlark.push(Starlark::Verbatim(comment));
200 starlark.extend(binaries.into_iter().map(Starlark::Alias));
201 }
202
203 let starlark = starlark::serialize(&starlark)?;
204 Ok(starlark)
205 }
206
207 fn render_build_files(
208 &self,
209 context: &Context,
210 platforms: &Platforms,
211 ) -> Result<BTreeMap<PathBuf, String>> {
Brian Silvermancc09f182022-03-09 15:40:20 -0800212 let default_splicing_package_id = default_splicing_package_crate_id();
Adam Snaider1c095c92023-07-08 02:09:58 -0400213 context
214 .crates
215 .keys()
Brian Silvermancc09f182022-03-09 15:40:20 -0800216 // Do not render the default splicing package
Adam Snaider1c095c92023-07-08 02:09:58 -0400217 .filter(|id| *id != &default_splicing_package_id)
Brian Silvermancc09f182022-03-09 15:40:20 -0800218 // Do not render local packages
Adam Snaider1c095c92023-07-08 02:09:58 -0400219 .filter(|id| !context.workspace_members.contains_key(id))
220 .map(|id| {
Brian Silvermancc09f182022-03-09 15:40:20 -0800221 let label = match render_build_file_template(
222 &self.config.build_file_template,
Adam Snaider1c095c92023-07-08 02:09:58 -0400223 &id.name,
224 &id.version,
Brian Silvermancc09f182022-03-09 15:40:20 -0800225 ) {
226 Ok(label) => label,
227 Err(e) => bail!(e),
228 };
229
230 let filename = Renderer::label_to_path(&label);
Adam Snaider1c095c92023-07-08 02:09:58 -0400231 let content = self.render_one_build_file(platforms, &context.crates[id])?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800232 Ok((filename, content))
233 })
234 .collect()
235 }
236
Adam Snaider1c095c92023-07-08 02:09:58 -0400237 fn render_one_build_file(&self, platforms: &Platforms, krate: &CrateContext) -> Result<String> {
238 let mut starlark = Vec::new();
239
240 // Banner comment for top of the file.
241 let header = self.engine.render_header()?;
242 starlark.push(Starlark::Verbatim(header));
243
244 // Loads: map of bzl file to set of items imported from that file. These
245 // get inserted into `starlark` at the bottom of this function.
246 let mut loads: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
247 let mut load = |bzl: &str, item: &str| {
248 loads
249 .entry(bzl.to_owned())
250 .or_default()
251 .insert(item.to_owned())
252 };
253
254 let disable_visibility = "# buildifier: disable=bzl-visibility".to_owned();
255 starlark.push(Starlark::Verbatim(disable_visibility));
256 starlark.push(Starlark::Load(Load {
257 bzl: "@rules_rust//crate_universe/private:selects.bzl".to_owned(),
258 items: BTreeSet::from(["selects".to_owned()]),
259 }));
260
261 // Package visibility.
262 let package = Package::default_visibility_public();
263 starlark.push(Starlark::Package(package));
264
265 if let Some(license) = &krate.license {
266 starlark.push(Starlark::Verbatim(formatdoc! {r#"
267 # licenses([
268 # "TODO", # {license}
269 # ])
270 "#}));
271 }
272
273 for rule in &krate.targets {
274 match rule {
275 Rule::BuildScript(target) => {
276 load("@rules_rust//cargo:defs.bzl", "cargo_build_script");
277 let cargo_build_script =
278 self.make_cargo_build_script(platforms, krate, target)?;
279 starlark.push(Starlark::CargoBuildScript(cargo_build_script));
280 starlark.push(Starlark::Alias(Alias {
281 name: target.crate_name.clone(),
282 actual: format!("{}_build_script", krate.name),
283 tags: BTreeSet::from(["manual".to_owned()]),
284 }));
285 }
286 Rule::ProcMacro(target) => {
287 load("@rules_rust//rust:defs.bzl", "rust_proc_macro");
288 let rust_proc_macro = self.make_rust_proc_macro(platforms, krate, target)?;
289 starlark.push(Starlark::RustProcMacro(rust_proc_macro));
290 }
291 Rule::Library(target) => {
292 load("@rules_rust//rust:defs.bzl", "rust_library");
293 let rust_library = self.make_rust_library(platforms, krate, target)?;
294 starlark.push(Starlark::RustLibrary(rust_library));
295 }
296 Rule::Binary(target) => {
297 load("@rules_rust//rust:defs.bzl", "rust_binary");
298 let rust_binary = self.make_rust_binary(platforms, krate, target)?;
299 starlark.push(Starlark::RustBinary(rust_binary));
300 }
301 }
302 }
303
304 if let Some(additive_build_file_content) = &krate.additive_build_file_content {
305 let comment = "# Additive BUILD file content".to_owned();
306 starlark.push(Starlark::Verbatim(comment));
307 starlark.push(Starlark::Verbatim(additive_build_file_content.clone()));
308 }
309
310 // Insert all the loads immediately after the header banner comment.
311 let loads = loads
312 .into_iter()
313 .map(|(bzl, items)| Starlark::Load(Load { bzl, items }));
314 starlark.splice(1..1, loads);
315
316 let starlark = starlark::serialize(&starlark)?;
317 Ok(starlark)
318 }
319
320 fn make_cargo_build_script(
321 &self,
322 platforms: &Platforms,
323 krate: &CrateContext,
324 target: &TargetAttributes,
325 ) -> Result<CargoBuildScript> {
326 let empty_set = BTreeSet::<String>::new();
327 let empty_list = SelectList::<String>::default();
328 let empty_deps = SelectList::<CrateDependency>::default();
329 let attrs = krate.build_script_attrs.as_ref();
330
331 Ok(CargoBuildScript {
332 // Because `cargo_build_script` does some invisible target name
333 // mutating to determine the package and crate name for a build
334 // script, the Bazel target name of any build script cannot be the
335 // Cargo canonical name of "cargo_build_script" without losing out
336 // on having certain Cargo environment variables set.
337 //
338 // Do not change this name to "cargo_build_script".
339 name: format!("{}_build_script", krate.name),
340 aliases: self
341 .make_aliases(krate, true, false)
342 .remap_configurations(platforms),
343 build_script_env: attrs
344 .map_or_else(SelectDict::default, |attrs| attrs.build_script_env.clone())
345 .remap_configurations(platforms),
346 compile_data: make_data(
347 platforms,
348 &empty_set,
349 attrs.map_or(&empty_list, |attrs| &attrs.compile_data),
350 ),
351 crate_features: SelectList::from(&krate.common_attrs.crate_features)
352 .map_configuration_names(|triple| {
353 render_platform_constraint_label(&self.config.platforms_template, &triple)
354 }),
355 crate_name: utils::sanitize_module_name(&target.crate_name),
356 crate_root: target.crate_root.clone(),
357 data: make_data(
358 platforms,
359 attrs.map_or(&empty_set, |attrs| &attrs.data_glob),
360 attrs.map_or(&empty_list, |attrs| &attrs.data),
361 ),
362 deps: self
363 .make_deps(
364 attrs.map_or(&empty_deps, |attrs| &attrs.deps),
365 attrs.map_or(&empty_set, |attrs| &attrs.extra_deps),
366 )
367 .remap_configurations(platforms),
368 edition: krate.common_attrs.edition.clone(),
369 linker_script: krate.common_attrs.linker_script.clone(),
370 links: attrs.and_then(|attrs| attrs.links.clone()),
371 proc_macro_deps: self
372 .make_deps(
373 attrs.map_or(&empty_deps, |attrs| &attrs.proc_macro_deps),
374 attrs.map_or(&empty_set, |attrs| &attrs.extra_proc_macro_deps),
375 )
376 .remap_configurations(platforms),
377 rustc_env: attrs
378 .map_or_else(SelectDict::default, |attrs| attrs.rustc_env.clone())
379 .remap_configurations(platforms),
380 rustc_env_files: attrs
381 .map_or_else(SelectList::default, |attrs| attrs.rustc_env_files.clone())
382 .remap_configurations(platforms),
383 rustc_flags: {
384 let mut rustc_flags =
385 attrs.map_or_else(SelectList::default, |attrs| attrs.rustc_flags.clone());
386 // In most cases, warnings in 3rd party crates are not
387 // interesting as they're out of the control of consumers. The
388 // flag here silences warnings. For more details see:
389 // https://doc.rust-lang.org/rustc/lints/levels.html
390 rustc_flags.insert("--cap-lints=allow".to_owned(), None);
391 rustc_flags.remap_configurations(platforms)
392 },
393 srcs: target.srcs.clone(),
394 tags: {
395 let mut tags = BTreeSet::from_iter(krate.common_attrs.tags.iter().cloned());
396 tags.insert("cargo-bazel".to_owned());
397 tags.insert("manual".to_owned());
398 tags.insert("noclippy".to_owned());
399 tags.insert("norustfmt".to_owned());
400 tags.insert(format!("crate-name={}", krate.name));
401 tags
402 },
403 tools: attrs
404 .map_or_else(SelectList::default, |attrs| attrs.tools.clone())
405 .remap_configurations(platforms),
406 toolchains: attrs.map_or_else(BTreeSet::new, |attrs| attrs.toolchains.clone()),
407 version: krate.common_attrs.version.clone(),
408 visibility: BTreeSet::from(["//visibility:private".to_owned()]),
409 })
410 }
411
412 fn make_rust_proc_macro(
413 &self,
414 platforms: &Platforms,
415 krate: &CrateContext,
416 target: &TargetAttributes,
417 ) -> Result<RustProcMacro> {
418 Ok(RustProcMacro {
419 name: target.crate_name.clone(),
420 deps: self
421 .make_deps(&krate.common_attrs.deps, &krate.common_attrs.extra_deps)
422 .remap_configurations(platforms),
423 proc_macro_deps: self
424 .make_deps(
425 &krate.common_attrs.proc_macro_deps,
426 &krate.common_attrs.extra_proc_macro_deps,
427 )
428 .remap_configurations(platforms),
429 aliases: self
430 .make_aliases(krate, false, false)
431 .remap_configurations(platforms),
432 common: self.make_common_attrs(platforms, krate, target)?,
433 })
434 }
435
436 fn make_rust_library(
437 &self,
438 platforms: &Platforms,
439 krate: &CrateContext,
440 target: &TargetAttributes,
441 ) -> Result<RustLibrary> {
442 Ok(RustLibrary {
443 name: target.crate_name.clone(),
444 deps: self
445 .make_deps(&krate.common_attrs.deps, &krate.common_attrs.extra_deps)
446 .remap_configurations(platforms),
447 proc_macro_deps: self
448 .make_deps(
449 &krate.common_attrs.proc_macro_deps,
450 &krate.common_attrs.extra_proc_macro_deps,
451 )
452 .remap_configurations(platforms),
453 aliases: self
454 .make_aliases(krate, false, false)
455 .remap_configurations(platforms),
456 common: self.make_common_attrs(platforms, krate, target)?,
457 disable_pipelining: krate.disable_pipelining,
458 })
459 }
460
461 fn make_rust_binary(
462 &self,
463 platforms: &Platforms,
464 krate: &CrateContext,
465 target: &TargetAttributes,
466 ) -> Result<RustBinary> {
467 Ok(RustBinary {
468 name: format!("{}__bin", target.crate_name),
469 deps: {
470 let mut deps =
471 self.make_deps(&krate.common_attrs.deps, &krate.common_attrs.extra_deps);
472 if let Some(library_target_name) = &krate.library_target_name {
473 deps.insert(format!(":{library_target_name}"), None);
474 }
475 deps.remap_configurations(platforms)
476 },
477 proc_macro_deps: self
478 .make_deps(
479 &krate.common_attrs.proc_macro_deps,
480 &krate.common_attrs.extra_proc_macro_deps,
481 )
482 .remap_configurations(platforms),
483 aliases: self
484 .make_aliases(krate, false, false)
485 .remap_configurations(platforms),
486 common: self.make_common_attrs(platforms, krate, target)?,
487 })
488 }
489
490 fn make_common_attrs(
491 &self,
492 platforms: &Platforms,
493 krate: &CrateContext,
494 target: &TargetAttributes,
495 ) -> Result<CommonAttrs> {
496 Ok(CommonAttrs {
497 compile_data: make_data(
498 platforms,
499 &krate.common_attrs.compile_data_glob,
500 &krate.common_attrs.compile_data,
501 ),
502 crate_features: SelectList::from(&krate.common_attrs.crate_features)
503 .map_configuration_names(|triple| {
504 render_platform_constraint_label(&self.config.platforms_template, &triple)
505 }),
506 crate_root: target.crate_root.clone(),
507 data: make_data(
508 platforms,
509 &krate.common_attrs.data_glob,
510 &krate.common_attrs.data,
511 ),
512 edition: krate.common_attrs.edition.clone(),
513 linker_script: krate.common_attrs.linker_script.clone(),
514 rustc_env: krate
515 .common_attrs
516 .rustc_env
517 .clone()
518 .remap_configurations(platforms),
519 rustc_env_files: krate
520 .common_attrs
521 .rustc_env_files
522 .clone()
523 .remap_configurations(platforms),
524 rustc_flags: {
525 let mut rustc_flags = krate.common_attrs.rustc_flags.clone();
526 // In most cases, warnings in 3rd party crates are not
527 // interesting as they're out of the control of consumers. The
528 // flag here silences warnings. For more details see:
529 // https://doc.rust-lang.org/rustc/lints/levels.html
530 rustc_flags.insert(0, "--cap-lints=allow".to_owned());
531 rustc_flags
532 },
533 srcs: target.srcs.clone(),
534 tags: {
535 let mut tags = BTreeSet::from_iter(krate.common_attrs.tags.iter().cloned());
536 tags.insert("cargo-bazel".to_owned());
537 tags.insert("manual".to_owned());
538 tags.insert("noclippy".to_owned());
539 tags.insert("norustfmt".to_owned());
540 tags.insert(format!("crate-name={}", krate.name));
541 tags
542 },
543 target_compatible_with: self.generate_target_compatible_with.then(|| {
544 TargetCompatibleWith::new(
545 self.supported_platform_triples
546 .iter()
547 .map(|triple| {
548 render_platform_constraint_label(
549 &self.config.platforms_template,
550 triple,
551 )
552 })
553 .collect(),
554 )
555 }),
556 version: krate.common_attrs.version.clone(),
557 })
558 }
559
560 /// Filter a crate's dependencies to only ones with aliases
561 fn make_aliases(
562 &self,
563 krate: &CrateContext,
564 build: bool,
565 include_dev: bool,
566 ) -> SelectDict<String> {
567 let mut dep_lists = Vec::new();
568 if build {
569 if let Some(build_script_attrs) = &krate.build_script_attrs {
570 dep_lists.push(&build_script_attrs.deps);
571 dep_lists.push(&build_script_attrs.proc_macro_deps);
572 }
573 } else {
574 dep_lists.push(&krate.common_attrs.deps);
575 dep_lists.push(&krate.common_attrs.proc_macro_deps);
576 if include_dev {
577 dep_lists.push(&krate.common_attrs.deps_dev);
578 dep_lists.push(&krate.common_attrs.proc_macro_deps_dev);
579 }
580 }
581
582 let mut aliases = SelectDict::default();
583 for (dep, conf) in dep_lists.into_iter().flat_map(|deps| {
584 deps.configurations().into_iter().flat_map(move |conf| {
585 deps.get_iter(conf)
586 .expect("Iterating over known keys should never panic")
587 .map(move |dep| (dep, conf))
588 })
589 }) {
590 if let Some(alias) = &dep.alias {
591 let label = self.crate_label(&dep.id.name, &dep.id.version, &dep.target);
592 aliases.insert(label, alias.clone(), conf.cloned());
593 }
594 }
595 aliases
596 }
597
598 fn make_deps(
599 &self,
600 deps: &SelectList<CrateDependency>,
601 extra_deps: &BTreeSet<String>,
602 ) -> SelectList<String> {
603 let mut deps = deps
604 .clone()
605 .map(|dep| self.crate_label(&dep.id.name, &dep.id.version, &dep.target));
606 for extra_dep in extra_deps {
607 deps.insert(extra_dep.clone(), None);
608 }
609 deps
610 }
611
Brian Silvermancc09f182022-03-09 15:40:20 -0800612 fn render_vendor_support_files(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
613 let module_label = render_module_label(&self.config.crates_module_template, "crates.bzl")
614 .context("Failed to resolve string to module file label")?;
615
616 let mut map = BTreeMap::new();
617 map.insert(
618 Renderer::label_to_path(&module_label),
619 self.engine.render_vendor_module_file(context)?,
620 );
621
622 Ok(map)
623 }
624
625 fn label_to_path(label: &Label) -> PathBuf {
626 match &label.package {
627 Some(package) => PathBuf::from(format!("{}/{}", package, label.target)),
628 None => PathBuf::from(&label.target),
629 }
630 }
Adam Snaider1c095c92023-07-08 02:09:58 -0400631
632 fn crate_label(&self, name: &str, version: &str, target: &str) -> String {
633 sanitize_repository_name(&render_crate_bazel_label(
634 &self.config.crate_label_template,
635 &self.config.repository_name,
636 name,
637 version,
638 target,
639 ))
640 }
Brian Silvermancc09f182022-03-09 15:40:20 -0800641}
642
Adam Snaider1c095c92023-07-08 02:09:58 -0400643/// Write a set of [crate::context::crate_context::CrateContext] to disk.
Brian Silvermancc09f182022-03-09 15:40:20 -0800644pub fn write_outputs(
645 outputs: BTreeMap<PathBuf, String>,
646 out_dir: &Path,
647 dry_run: bool,
648) -> Result<()> {
649 let outputs: BTreeMap<PathBuf, String> = outputs
650 .into_iter()
651 .map(|(path, content)| (out_dir.join(path), content))
652 .collect();
653
654 if dry_run {
655 for (path, content) in outputs {
656 println!(
657 "==============================================================================="
658 );
659 println!("{}", path.display());
660 println!(
661 "==============================================================================="
662 );
Adam Snaider1c095c92023-07-08 02:09:58 -0400663 println!("{content}\n");
Brian Silvermancc09f182022-03-09 15:40:20 -0800664 }
665 } else {
666 for (path, content) in outputs {
667 // Ensure the output directory exists
668 fs::create_dir_all(
669 path.parent()
670 .expect("All file paths should have valid directories"),
671 )?;
672
673 fs::write(&path, content.as_bytes())
674 .context(format!("Failed to write file to disk: {}", path.display()))?;
675 }
676 }
677
678 Ok(())
679}
680
681/// Render the Bazel label of a crate
682pub fn render_crate_bazel_label(
683 template: &str,
684 repository_name: &str,
685 name: &str,
686 version: &str,
687 target: &str,
688) -> String {
689 template
690 .replace("{repository}", repository_name)
691 .replace("{name}", name)
692 .replace("{version}", version)
693 .replace("{target}", target)
694}
695
696/// Render the Bazel label of a crate
697pub fn render_crate_bazel_repository(
698 template: &str,
699 repository_name: &str,
700 name: &str,
701 version: &str,
702) -> String {
703 template
704 .replace("{repository}", repository_name)
705 .replace("{name}", name)
706 .replace("{version}", version)
707}
708
709/// Render the Bazel label of a crate
710pub fn render_crate_build_file(template: &str, name: &str, version: &str) -> String {
711 template
712 .replace("{name}", name)
713 .replace("{version}", version)
714}
715
716/// Render the Bazel label of a vendor module label
717pub fn render_module_label(template: &str, name: &str) -> Result<Label> {
718 Label::from_str(&template.replace("{file}", name))
719}
720
721/// Render the Bazel label of a platform triple
Adam Snaider1c095c92023-07-08 02:09:58 -0400722fn render_platform_constraint_label(template: &str, triple: &str) -> String {
Brian Silvermancc09f182022-03-09 15:40:20 -0800723 template.replace("{triple}", triple)
724}
725
726fn render_build_file_template(template: &str, name: &str, version: &str) -> Result<Label> {
727 Label::from_str(
728 &template
729 .replace("{name}", name)
730 .replace("{version}", version),
731 )
732}
733
Adam Snaider1c095c92023-07-08 02:09:58 -0400734fn make_data(platforms: &Platforms, glob: &BTreeSet<String>, select: &SelectList<String>) -> Data {
735 const COMMON_GLOB_EXCLUDES: &[&str] = &[
736 "**/* *",
737 "BUILD.bazel",
738 "BUILD",
739 "WORKSPACE.bazel",
740 "WORKSPACE",
741 ".tmp_git_root/**/*",
742 ];
743
744 Data {
745 glob: Glob {
746 include: glob.clone(),
747 exclude: COMMON_GLOB_EXCLUDES
748 .iter()
749 .map(|&glob| glob.to_owned())
750 .collect(),
751 },
752 select: select.clone().remap_configurations(platforms),
753 }
754}
755
Brian Silvermancc09f182022-03-09 15:40:20 -0800756#[cfg(test)]
757mod test {
758 use super::*;
759
Adam Snaider1c095c92023-07-08 02:09:58 -0400760 use indoc::indoc;
761 use std::collections::BTreeSet;
762
Brian Silvermancc09f182022-03-09 15:40:20 -0800763 use crate::config::{Config, CrateId, VendorMode};
764 use crate::context::crate_context::{CrateContext, Rule};
Adam Snaider1c095c92023-07-08 02:09:58 -0400765 use crate::context::{
766 BuildScriptAttributes, CommonAttributes, Context, CrateFeatures, TargetAttributes,
767 };
Brian Silvermancc09f182022-03-09 15:40:20 -0800768 use crate::metadata::Annotations;
769 use crate::test;
Adam Snaider1c095c92023-07-08 02:09:58 -0400770 use crate::utils::starlark::SelectList;
Brian Silvermancc09f182022-03-09 15:40:20 -0800771
772 fn mock_target_attributes() -> TargetAttributes {
773 TargetAttributes {
774 crate_name: "mock_crate".to_owned(),
775 crate_root: Some("src/root.rs".to_owned()),
776 ..TargetAttributes::default()
777 }
778 }
779
Adam Snaider1c095c92023-07-08 02:09:58 -0400780 fn mock_render_config(vendor_mode: Option<VendorMode>) -> RenderConfig {
781 RenderConfig {
782 repository_name: "test_rendering".to_owned(),
783 regen_command: "cargo_bazel_regen_command".to_owned(),
784 vendor_mode,
785 ..RenderConfig::default()
786 }
787 }
788
789 fn mock_supported_platform_triples() -> BTreeSet<String> {
790 BTreeSet::from([
791 "aarch64-apple-darwin".to_owned(),
792 "aarch64-apple-ios".to_owned(),
793 "aarch64-linux-android".to_owned(),
794 "aarch64-pc-windows-msvc".to_owned(),
795 "aarch64-unknown-linux-gnu".to_owned(),
796 "arm-unknown-linux-gnueabi".to_owned(),
797 "armv7-unknown-linux-gnueabi".to_owned(),
798 "i686-apple-darwin".to_owned(),
799 "i686-linux-android".to_owned(),
800 "i686-pc-windows-msvc".to_owned(),
801 "i686-unknown-freebsd".to_owned(),
802 "i686-unknown-linux-gnu".to_owned(),
803 "powerpc-unknown-linux-gnu".to_owned(),
804 "s390x-unknown-linux-gnu".to_owned(),
805 "wasm32-unknown-unknown".to_owned(),
806 "wasm32-wasi".to_owned(),
807 "x86_64-apple-darwin".to_owned(),
808 "x86_64-apple-ios".to_owned(),
809 "x86_64-linux-android".to_owned(),
810 "x86_64-pc-windows-msvc".to_owned(),
811 "x86_64-unknown-freebsd".to_owned(),
812 "x86_64-unknown-linux-gnu".to_owned(),
813 ])
814 }
815
Brian Silvermancc09f182022-03-09 15:40:20 -0800816 #[test]
817 fn render_rust_library() {
818 let mut context = Context::default();
819 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
820 context.crates.insert(
821 crate_id.clone(),
822 CrateContext {
823 name: crate_id.name,
824 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -0400825 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800826 ..CrateContext::default()
827 },
828 );
829
Adam Snaider1c095c92023-07-08 02:09:58 -0400830 let renderer = Renderer::new(
831 mock_render_config(None),
832 mock_supported_platform_triples(),
833 true,
834 );
Brian Silvermancc09f182022-03-09 15:40:20 -0800835 let output = renderer.render(&context).unwrap();
836
837 let build_file_content = output
838 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
839 .unwrap();
840
841 assert!(build_file_content.contains("rust_library("));
842 assert!(build_file_content.contains("name = \"mock_crate\""));
Adam Snaider1c095c92023-07-08 02:09:58 -0400843 assert!(build_file_content.contains("\"crate-name=mock_crate\""));
844 }
845
846 #[test]
847 fn test_disable_pipelining() {
848 let mut context = Context::default();
849 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
850 context.crates.insert(
851 crate_id.clone(),
852 CrateContext {
853 name: crate_id.name,
854 version: crate_id.version,
855 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
856 disable_pipelining: true,
857 ..CrateContext::default()
858 },
859 );
860
861 let renderer = Renderer::new(
862 mock_render_config(None),
863 mock_supported_platform_triples(),
864 true,
865 );
866 let output = renderer.render(&context).unwrap();
867
868 let build_file_content = output
869 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
870 .unwrap();
871
872 assert!(build_file_content.contains("disable_pipelining = True"));
Brian Silvermancc09f182022-03-09 15:40:20 -0800873 }
874
875 #[test]
876 fn render_cargo_build_script() {
877 let mut context = Context::default();
878 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
879 context.crates.insert(
880 crate_id.clone(),
881 CrateContext {
882 name: crate_id.name,
883 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -0400884 targets: BTreeSet::from([Rule::BuildScript(TargetAttributes {
Brian Silvermancc09f182022-03-09 15:40:20 -0800885 crate_name: "build_script_build".to_owned(),
886 crate_root: Some("build.rs".to_owned()),
887 ..TargetAttributes::default()
Adam Snaider1c095c92023-07-08 02:09:58 -0400888 })]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800889 // Build script attributes are required.
890 build_script_attrs: Some(BuildScriptAttributes::default()),
891 ..CrateContext::default()
892 },
893 );
894
Adam Snaider1c095c92023-07-08 02:09:58 -0400895 let renderer = Renderer::new(
896 mock_render_config(None),
897 mock_supported_platform_triples(),
898 true,
899 );
Brian Silvermancc09f182022-03-09 15:40:20 -0800900 let output = renderer.render(&context).unwrap();
901
902 let build_file_content = output
903 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
904 .unwrap();
905
906 assert!(build_file_content.contains("cargo_build_script("));
907 assert!(build_file_content.contains("name = \"build_script_build\""));
Adam Snaider1c095c92023-07-08 02:09:58 -0400908 assert!(build_file_content.contains("\"crate-name=mock_crate\""));
Brian Silvermancc09f182022-03-09 15:40:20 -0800909
910 // Ensure `cargo_build_script` requirements are met
911 assert!(build_file_content.contains("name = \"mock_crate_build_script\""));
912 }
913
914 #[test]
915 fn render_proc_macro() {
916 let mut context = Context::default();
917 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
918 context.crates.insert(
919 crate_id.clone(),
920 CrateContext {
921 name: crate_id.name,
922 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -0400923 targets: BTreeSet::from([Rule::ProcMacro(mock_target_attributes())]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800924 ..CrateContext::default()
925 },
926 );
927
Adam Snaider1c095c92023-07-08 02:09:58 -0400928 let renderer = Renderer::new(
929 mock_render_config(None),
930 mock_supported_platform_triples(),
931 true,
932 );
Brian Silvermancc09f182022-03-09 15:40:20 -0800933 let output = renderer.render(&context).unwrap();
934
935 let build_file_content = output
936 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
937 .unwrap();
938
939 assert!(build_file_content.contains("rust_proc_macro("));
940 assert!(build_file_content.contains("name = \"mock_crate\""));
Adam Snaider1c095c92023-07-08 02:09:58 -0400941 assert!(build_file_content.contains("\"crate-name=mock_crate\""));
Brian Silvermancc09f182022-03-09 15:40:20 -0800942 }
943
944 #[test]
945 fn render_binary() {
946 let mut context = Context::default();
947 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
948 context.crates.insert(
949 crate_id.clone(),
950 CrateContext {
951 name: crate_id.name,
952 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -0400953 targets: BTreeSet::from([Rule::Binary(mock_target_attributes())]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800954 ..CrateContext::default()
955 },
956 );
957
Adam Snaider1c095c92023-07-08 02:09:58 -0400958 let renderer = Renderer::new(
959 mock_render_config(None),
960 mock_supported_platform_triples(),
961 true,
962 );
Brian Silvermancc09f182022-03-09 15:40:20 -0800963 let output = renderer.render(&context).unwrap();
964
965 let build_file_content = output
966 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
967 .unwrap();
968
969 assert!(build_file_content.contains("rust_binary("));
970 assert!(build_file_content.contains("name = \"mock_crate__bin\""));
Adam Snaider1c095c92023-07-08 02:09:58 -0400971 assert!(build_file_content.contains("\"crate-name=mock_crate\""));
Brian Silvermancc09f182022-03-09 15:40:20 -0800972 }
973
974 #[test]
975 fn render_additive_build_contents() {
976 let mut context = Context::default();
977 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
978 context.crates.insert(
979 crate_id.clone(),
980 CrateContext {
981 name: crate_id.name,
982 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -0400983 targets: BTreeSet::from([Rule::Binary(mock_target_attributes())]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800984 additive_build_file_content: Some(
985 "# Hello World from additive section!".to_owned(),
986 ),
987 ..CrateContext::default()
988 },
989 );
990
Adam Snaider1c095c92023-07-08 02:09:58 -0400991 let renderer = Renderer::new(
992 mock_render_config(None),
993 mock_supported_platform_triples(),
994 true,
995 );
Brian Silvermancc09f182022-03-09 15:40:20 -0800996 let output = renderer.render(&context).unwrap();
997
998 let build_file_content = output
999 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1000 .unwrap();
1001
1002 assert!(build_file_content.contains("# Hello World from additive section!"));
1003 }
1004
1005 #[test]
1006 fn render_aliases() {
Adam Snaider1c095c92023-07-08 02:09:58 -04001007 let config = Config {
1008 generate_binaries: true,
1009 ..Config::default()
1010 };
1011 let annotations =
1012 Annotations::new(test::metadata::alias(), test::lockfile::alias(), config).unwrap();
Brian Silvermancc09f182022-03-09 15:40:20 -08001013 let context = Context::new(annotations).unwrap();
1014
Adam Snaider1c095c92023-07-08 02:09:58 -04001015 let renderer = Renderer::new(
1016 mock_render_config(None),
1017 mock_supported_platform_triples(),
1018 true,
1019 );
Brian Silvermancc09f182022-03-09 15:40:20 -08001020 let output = renderer.render(&context).unwrap();
1021
1022 let build_file_content = output.get(&PathBuf::from("BUILD.bazel")).unwrap();
1023
1024 assert!(build_file_content.contains(r#"name = "names-0.12.1-dev__names","#));
1025 assert!(build_file_content.contains(r#"name = "names-0.13.0__names","#));
1026 }
1027
1028 #[test]
1029 fn render_crate_repositories() {
1030 let mut context = Context::default();
1031 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
1032 context.crates.insert(
1033 crate_id.clone(),
1034 CrateContext {
1035 name: crate_id.name,
1036 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -04001037 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
Brian Silvermancc09f182022-03-09 15:40:20 -08001038 ..CrateContext::default()
1039 },
1040 );
1041
Adam Snaider1c095c92023-07-08 02:09:58 -04001042 let renderer = Renderer::new(
1043 mock_render_config(None),
1044 mock_supported_platform_triples(),
1045 true,
1046 );
Brian Silvermancc09f182022-03-09 15:40:20 -08001047 let output = renderer.render(&context).unwrap();
1048
1049 let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
1050
1051 assert!(defs_module.contains("def crate_repositories():"));
1052 }
1053
1054 #[test]
1055 fn remote_remote_vendor_mode() {
1056 let mut context = Context::default();
1057 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
1058 context.crates.insert(
1059 crate_id.clone(),
1060 CrateContext {
1061 name: crate_id.name,
1062 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -04001063 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
Brian Silvermancc09f182022-03-09 15:40:20 -08001064 ..CrateContext::default()
1065 },
1066 );
1067
1068 // Enable remote vendor mode
Adam Snaider1c095c92023-07-08 02:09:58 -04001069 let renderer = Renderer::new(
1070 mock_render_config(Some(VendorMode::Remote)),
1071 mock_supported_platform_triples(),
1072 true,
1073 );
Brian Silvermancc09f182022-03-09 15:40:20 -08001074 let output = renderer.render(&context).unwrap();
1075
1076 let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
1077 assert!(defs_module.contains("def crate_repositories():"));
1078
1079 let crates_module = output.get(&PathBuf::from("crates.bzl")).unwrap();
1080 assert!(crates_module.contains("def crate_repositories():"));
1081 }
1082
1083 #[test]
1084 fn remote_local_vendor_mode() {
1085 let mut context = Context::default();
1086 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
1087 context.crates.insert(
1088 crate_id.clone(),
1089 CrateContext {
1090 name: crate_id.name,
1091 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -04001092 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
Brian Silvermancc09f182022-03-09 15:40:20 -08001093 ..CrateContext::default()
1094 },
1095 );
1096
1097 // Enable local vendor mode
Adam Snaider1c095c92023-07-08 02:09:58 -04001098 let renderer = Renderer::new(
1099 mock_render_config(Some(VendorMode::Local)),
1100 mock_supported_platform_triples(),
1101 true,
1102 );
Brian Silvermancc09f182022-03-09 15:40:20 -08001103 let output = renderer.render(&context).unwrap();
1104
1105 // Local vendoring does not produce a `crate_repositories` macro
1106 let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
1107 assert!(!defs_module.contains("def crate_repositories():"));
1108
1109 // Local vendoring does not produce a `crates.bzl` file.
1110 assert!(output.get(&PathBuf::from("crates.bzl")).is_none());
1111 }
Brian Silverman5f6f2762022-08-13 19:30:05 -07001112
1113 #[test]
1114 fn duplicate_rustc_flags() {
1115 let mut context = Context::default();
1116 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
1117
1118 let rustc_flags = vec![
1119 "-l".to_owned(),
1120 "dylib=ssl".to_owned(),
1121 "-l".to_owned(),
1122 "dylib=crypto".to_owned(),
1123 ];
1124
1125 context.crates.insert(
1126 crate_id.clone(),
1127 CrateContext {
1128 name: crate_id.name,
1129 version: crate_id.version,
Adam Snaider1c095c92023-07-08 02:09:58 -04001130 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
Brian Silverman5f6f2762022-08-13 19:30:05 -07001131 common_attrs: CommonAttributes {
1132 rustc_flags: rustc_flags.clone(),
1133 ..CommonAttributes::default()
1134 },
1135 ..CrateContext::default()
1136 },
1137 );
1138
1139 // Enable local vendor mode
Adam Snaider1c095c92023-07-08 02:09:58 -04001140 let renderer = Renderer::new(
1141 mock_render_config(Some(VendorMode::Local)),
1142 mock_supported_platform_triples(),
1143 true,
1144 );
Brian Silverman5f6f2762022-08-13 19:30:05 -07001145 let output = renderer.render(&context).unwrap();
1146
1147 let build_file_content = output
1148 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1149 .unwrap();
1150
1151 // Strip all spaces from the generated BUILD file and ensure it has the flags
1152 // represented by `rustc_flags` in the same order.
1153 assert!(build_file_content.replace(' ', "").contains(
1154 &rustc_flags
1155 .iter()
Adam Snaider1c095c92023-07-08 02:09:58 -04001156 .map(|s| format!("\"{s}\","))
Brian Silverman5f6f2762022-08-13 19:30:05 -07001157 .collect::<Vec<String>>()
1158 .join("\n")
1159 ));
1160 }
Adam Snaider1c095c92023-07-08 02:09:58 -04001161
1162 #[test]
1163 fn test_render_build_file_deps() {
1164 let config: Config = serde_json::from_value(serde_json::json!({
1165 "generate_binaries": false,
1166 "generate_build_scripts": false,
1167 "rendering": {
1168 "repository_name": "multi_cfg_dep",
1169 "regen_command": "bazel test //crate_universe:unit_test",
1170 },
1171 "supported_platform_triples": [
1172 "x86_64-apple-darwin",
1173 "x86_64-unknown-linux-gnu",
1174 "aarch64-apple-darwin",
1175 "aarch64-unknown-linux-gnu",
1176 ],
1177 }))
1178 .unwrap();
1179 let metadata = test::metadata::multi_cfg_dep();
1180 let lockfile = test::lockfile::multi_cfg_dep();
1181
1182 let annotations = Annotations::new(metadata, lockfile, config.clone()).unwrap();
1183 let context = Context::new(annotations).unwrap();
1184
1185 let renderer = Renderer::new(config.rendering, config.supported_platform_triples, true);
1186 let output = renderer.render(&context).unwrap();
1187
1188 let build_file_content = output
1189 .get(&PathBuf::from("BUILD.cpufeatures-0.2.7.bazel"))
1190 .unwrap();
1191
1192 // This is unfortunately somewhat brittle. Alas. Ultimately we wish to demonstrate that the
1193 // original cfg(...) strings are preserved in the `deps` list for ease of debugging.
1194 let expected = indoc! {r#"
1195 deps = select({
1196 "@rules_rust//rust/platform:aarch64-apple-darwin": [
1197 "@multi_cfg_dep__libc-0.2.117//:libc", # cfg(all(target_arch = "aarch64", target_vendor = "apple"))
1198 ],
1199 "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [
1200 "@multi_cfg_dep__libc-0.2.117//:libc", # cfg(all(target_arch = "aarch64", target_os = "linux"))
1201 ],
1202 "//conditions:default": [],
1203 }),
1204 "#};
1205
1206 assert!(
1207 build_file_content.contains(&expected.replace('\n', "\n ")),
1208 "{}",
1209 build_file_content,
1210 );
1211 }
1212
1213 #[test]
1214 fn legacy_crate_features() {
1215 let mut context = Context::default();
1216 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
1217 context.crates.insert(
1218 crate_id.clone(),
1219 CrateContext {
1220 name: crate_id.name,
1221 version: crate_id.version,
1222 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1223 common_attrs: CommonAttributes {
1224 crate_features: CrateFeatures::LegacySet(BTreeSet::from([
1225 "foo".to_owned(),
1226 "bar".to_owned(),
1227 ])),
1228 ..CommonAttributes::default()
1229 },
1230 ..CrateContext::default()
1231 },
1232 );
1233
1234 let renderer = Renderer::new(
1235 mock_render_config(None),
1236 mock_supported_platform_triples(),
1237 true,
1238 );
1239 let output = renderer.render(&context).unwrap();
1240
1241 let build_file_content = output
1242 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1243 .unwrap();
1244 assert!(build_file_content.replace(' ', "").contains(
1245 &r#"crate_features = [
1246 "bar",
1247 "foo",
1248],"#
1249 .replace(' ', "")
1250 ));
1251 }
1252 #[test]
1253 fn crate_features_by_target() {
1254 let mut context = Context::default();
1255 let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
1256 let mut features = SelectList::default();
1257 features.insert("foo".to_owned(), Some("aarch64-apple-darwin".to_owned()));
1258 features.insert("bar".to_owned(), None);
1259 context.crates.insert(
1260 crate_id.clone(),
1261 CrateContext {
1262 name: crate_id.name,
1263 version: crate_id.version,
1264 targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1265 common_attrs: CommonAttributes {
1266 crate_features: CrateFeatures::SelectList(features),
1267 ..CommonAttributes::default()
1268 },
1269 ..CrateContext::default()
1270 },
1271 );
1272
1273 let renderer = Renderer::new(
1274 mock_render_config(None),
1275 mock_supported_platform_triples(),
1276 true,
1277 );
1278 let output = renderer.render(&context).unwrap();
1279
1280 let build_file_content = output
1281 .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1282 .unwrap();
1283 assert!(build_file_content.replace(' ', "").contains(
1284 &r#"crate_features = [
1285 "bar",
1286 ] + select({
1287 "@rules_rust//rust/platform:aarch64-apple-darwin": [
1288 "foo",
1289 ],
1290 "//conditions:default": [],
1291}),"#
1292 .replace(' ', "")
1293 ));
1294 }
Brian Silvermancc09f182022-03-09 15:40:20 -08001295}