1*d4726bddSHONG Yifan //! Tools for rendering and writing BUILD and other Starlark files
2*d4726bddSHONG Yifan
3*d4726bddSHONG Yifan mod template_engine;
4*d4726bddSHONG Yifan
5*d4726bddSHONG Yifan use std::collections::{BTreeMap, BTreeSet};
6*d4726bddSHONG Yifan use std::fs;
7*d4726bddSHONG Yifan use std::path::PathBuf;
8*d4726bddSHONG Yifan use std::str::FromStr;
9*d4726bddSHONG Yifan
10*d4726bddSHONG Yifan use anyhow::{bail, Context as AnyhowContext, Result};
11*d4726bddSHONG Yifan use itertools::Itertools;
12*d4726bddSHONG Yifan
13*d4726bddSHONG Yifan use crate::config::{AliasRule, RenderConfig, VendorMode};
14*d4726bddSHONG Yifan use crate::context::crate_context::{CrateContext, CrateDependency, Rule};
15*d4726bddSHONG Yifan use crate::context::{Context, TargetAttributes};
16*d4726bddSHONG Yifan use crate::rendering::template_engine::TemplateEngine;
17*d4726bddSHONG Yifan use crate::select::Select;
18*d4726bddSHONG Yifan use crate::splicing::default_splicing_package_crate_id;
19*d4726bddSHONG Yifan use crate::utils::starlark::{
20*d4726bddSHONG Yifan self, Alias, CargoBuildScript, CommonAttrs, Data, ExportsFiles, Filegroup, Glob, Label, Load,
21*d4726bddSHONG Yifan Package, RustBinary, RustLibrary, RustProcMacro, SelectDict, SelectList, SelectScalar,
22*d4726bddSHONG Yifan SelectSet, Starlark, TargetCompatibleWith,
23*d4726bddSHONG Yifan };
24*d4726bddSHONG Yifan use crate::utils::target_triple::TargetTriple;
25*d4726bddSHONG Yifan use crate::utils::{self, sanitize_repository_name};
26*d4726bddSHONG Yifan
27*d4726bddSHONG Yifan // Configuration remapper used to convert from cfg expressions like "cfg(unix)"
28*d4726bddSHONG Yifan // to platform labels like "@rules_rust//rust/platform:x86_64-unknown-linux-gnu".
29*d4726bddSHONG Yifan pub(crate) type Platforms = BTreeMap<String, BTreeSet<String>>;
30*d4726bddSHONG Yifan
31*d4726bddSHONG Yifan pub(crate) struct Renderer {
32*d4726bddSHONG Yifan config: RenderConfig,
33*d4726bddSHONG Yifan supported_platform_triples: BTreeSet<TargetTriple>,
34*d4726bddSHONG Yifan engine: TemplateEngine,
35*d4726bddSHONG Yifan }
36*d4726bddSHONG Yifan
37*d4726bddSHONG Yifan impl Renderer {
new( config: RenderConfig, supported_platform_triples: BTreeSet<TargetTriple>, ) -> Self38*d4726bddSHONG Yifan pub(crate) fn new(
39*d4726bddSHONG Yifan config: RenderConfig,
40*d4726bddSHONG Yifan supported_platform_triples: BTreeSet<TargetTriple>,
41*d4726bddSHONG Yifan ) -> Self {
42*d4726bddSHONG Yifan let engine = TemplateEngine::new(&config);
43*d4726bddSHONG Yifan Self {
44*d4726bddSHONG Yifan config,
45*d4726bddSHONG Yifan supported_platform_triples,
46*d4726bddSHONG Yifan engine,
47*d4726bddSHONG Yifan }
48*d4726bddSHONG Yifan }
49*d4726bddSHONG Yifan
render(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>>50*d4726bddSHONG Yifan pub(crate) fn render(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
51*d4726bddSHONG Yifan let mut output = BTreeMap::new();
52*d4726bddSHONG Yifan
53*d4726bddSHONG Yifan let platforms = self.render_platform_labels(context);
54*d4726bddSHONG Yifan output.extend(self.render_build_files(context, &platforms)?);
55*d4726bddSHONG Yifan output.extend(self.render_crates_module(context, &platforms)?);
56*d4726bddSHONG Yifan
57*d4726bddSHONG Yifan if let Some(vendor_mode) = &self.config.vendor_mode {
58*d4726bddSHONG Yifan match vendor_mode {
59*d4726bddSHONG Yifan crate::config::VendorMode::Local => {
60*d4726bddSHONG Yifan // Nothing to do for local vendor crate
61*d4726bddSHONG Yifan }
62*d4726bddSHONG Yifan crate::config::VendorMode::Remote => {
63*d4726bddSHONG Yifan output.extend(self.render_vendor_support_files(context)?);
64*d4726bddSHONG Yifan }
65*d4726bddSHONG Yifan }
66*d4726bddSHONG Yifan }
67*d4726bddSHONG Yifan
68*d4726bddSHONG Yifan Ok(output)
69*d4726bddSHONG Yifan }
70*d4726bddSHONG Yifan
render_platform_labels(&self, context: &Context) -> BTreeMap<String, BTreeSet<String>>71*d4726bddSHONG Yifan fn render_platform_labels(&self, context: &Context) -> BTreeMap<String, BTreeSet<String>> {
72*d4726bddSHONG Yifan context
73*d4726bddSHONG Yifan .conditions
74*d4726bddSHONG Yifan .iter()
75*d4726bddSHONG Yifan .map(|(cfg, target_triples)| {
76*d4726bddSHONG Yifan (
77*d4726bddSHONG Yifan cfg.clone(),
78*d4726bddSHONG Yifan target_triples
79*d4726bddSHONG Yifan .iter()
80*d4726bddSHONG Yifan .map(|target_triple| {
81*d4726bddSHONG Yifan render_platform_constraint_label(
82*d4726bddSHONG Yifan &self.config.platforms_template,
83*d4726bddSHONG Yifan target_triple,
84*d4726bddSHONG Yifan )
85*d4726bddSHONG Yifan })
86*d4726bddSHONG Yifan .collect(),
87*d4726bddSHONG Yifan )
88*d4726bddSHONG Yifan })
89*d4726bddSHONG Yifan .collect()
90*d4726bddSHONG Yifan }
91*d4726bddSHONG Yifan
render_crates_module( &self, context: &Context, platforms: &Platforms, ) -> Result<BTreeMap<PathBuf, String>>92*d4726bddSHONG Yifan fn render_crates_module(
93*d4726bddSHONG Yifan &self,
94*d4726bddSHONG Yifan context: &Context,
95*d4726bddSHONG Yifan platforms: &Platforms,
96*d4726bddSHONG Yifan ) -> Result<BTreeMap<PathBuf, String>> {
97*d4726bddSHONG Yifan let module_label = render_module_label(&self.config.crates_module_template, "defs.bzl")
98*d4726bddSHONG Yifan .context("Failed to resolve string to module file label")?;
99*d4726bddSHONG Yifan let module_build_label =
100*d4726bddSHONG Yifan render_module_label(&self.config.crates_module_template, "BUILD.bazel")
101*d4726bddSHONG Yifan .context("Failed to resolve string to module file label")?;
102*d4726bddSHONG Yifan let module_alias_rules_label =
103*d4726bddSHONG Yifan render_module_label(&self.config.crates_module_template, "alias_rules.bzl")
104*d4726bddSHONG Yifan .context("Failed to resolve string to module file label")?;
105*d4726bddSHONG Yifan
106*d4726bddSHONG Yifan let mut map = BTreeMap::new();
107*d4726bddSHONG Yifan map.insert(
108*d4726bddSHONG Yifan Renderer::label_to_path(&module_label),
109*d4726bddSHONG Yifan self.engine.render_module_bzl(context, platforms)?,
110*d4726bddSHONG Yifan );
111*d4726bddSHONG Yifan map.insert(
112*d4726bddSHONG Yifan Renderer::label_to_path(&module_build_label),
113*d4726bddSHONG Yifan self.render_module_build_file(context)?,
114*d4726bddSHONG Yifan );
115*d4726bddSHONG Yifan map.insert(
116*d4726bddSHONG Yifan Renderer::label_to_path(&module_alias_rules_label),
117*d4726bddSHONG Yifan include_str!(concat!(
118*d4726bddSHONG Yifan env!("CARGO_MANIFEST_DIR"),
119*d4726bddSHONG Yifan "/src/rendering/verbatim/alias_rules.bzl"
120*d4726bddSHONG Yifan ))
121*d4726bddSHONG Yifan .to_owned(),
122*d4726bddSHONG Yifan );
123*d4726bddSHONG Yifan
124*d4726bddSHONG Yifan Ok(map)
125*d4726bddSHONG Yifan }
126*d4726bddSHONG Yifan
render_module_build_file(&self, context: &Context) -> Result<String>127*d4726bddSHONG Yifan fn render_module_build_file(&self, context: &Context) -> Result<String> {
128*d4726bddSHONG Yifan let mut starlark = Vec::new();
129*d4726bddSHONG Yifan
130*d4726bddSHONG Yifan // Banner comment for top of the file.
131*d4726bddSHONG Yifan let header = self.engine.render_header()?;
132*d4726bddSHONG Yifan starlark.push(Starlark::Verbatim(header));
133*d4726bddSHONG Yifan
134*d4726bddSHONG Yifan // Load any `alias_rule`s.
135*d4726bddSHONG Yifan let mut loads: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
136*d4726bddSHONG Yifan for alias_rule in Iterator::chain(
137*d4726bddSHONG Yifan std::iter::once(&self.config.default_alias_rule),
138*d4726bddSHONG Yifan context
139*d4726bddSHONG Yifan .workspace_member_deps()
140*d4726bddSHONG Yifan .iter()
141*d4726bddSHONG Yifan .flat_map(|dep| &context.crates[&dep.id].alias_rule),
142*d4726bddSHONG Yifan ) {
143*d4726bddSHONG Yifan if let Some(bzl) = alias_rule.bzl() {
144*d4726bddSHONG Yifan loads.entry(bzl).or_default().insert(alias_rule.rule());
145*d4726bddSHONG Yifan }
146*d4726bddSHONG Yifan }
147*d4726bddSHONG Yifan for (bzl, items) in loads {
148*d4726bddSHONG Yifan starlark.push(Starlark::Load(Load { bzl, items }))
149*d4726bddSHONG Yifan }
150*d4726bddSHONG Yifan
151*d4726bddSHONG Yifan // Package visibility, exported bzl files.
152*d4726bddSHONG Yifan let package = Package::default_visibility_public(BTreeSet::new());
153*d4726bddSHONG Yifan starlark.push(Starlark::Package(package));
154*d4726bddSHONG Yifan
155*d4726bddSHONG Yifan let mut exports_files = ExportsFiles {
156*d4726bddSHONG Yifan paths: BTreeSet::from(["cargo-bazel.json".to_owned(), "defs.bzl".to_owned()]),
157*d4726bddSHONG Yifan globs: Glob {
158*d4726bddSHONG Yifan allow_empty: true,
159*d4726bddSHONG Yifan include: BTreeSet::from(["*.bazel".to_owned()]),
160*d4726bddSHONG Yifan exclude: BTreeSet::new(),
161*d4726bddSHONG Yifan },
162*d4726bddSHONG Yifan };
163*d4726bddSHONG Yifan if let Some(VendorMode::Remote) = self.config.vendor_mode {
164*d4726bddSHONG Yifan exports_files.paths.insert("crates.bzl".to_owned());
165*d4726bddSHONG Yifan }
166*d4726bddSHONG Yifan starlark.push(Starlark::ExportsFiles(exports_files));
167*d4726bddSHONG Yifan
168*d4726bddSHONG Yifan let filegroup = Filegroup {
169*d4726bddSHONG Yifan name: "srcs".to_owned(),
170*d4726bddSHONG Yifan srcs: Glob {
171*d4726bddSHONG Yifan allow_empty: true,
172*d4726bddSHONG Yifan include: BTreeSet::from(["*.bazel".to_owned(), "*.bzl".to_owned()]),
173*d4726bddSHONG Yifan exclude: BTreeSet::new(),
174*d4726bddSHONG Yifan },
175*d4726bddSHONG Yifan };
176*d4726bddSHONG Yifan starlark.push(Starlark::Filegroup(filegroup));
177*d4726bddSHONG Yifan
178*d4726bddSHONG Yifan // An `alias` for each direct dependency of a workspace member crate.
179*d4726bddSHONG Yifan let mut dependencies = Vec::new();
180*d4726bddSHONG Yifan for dep in context.workspace_member_deps() {
181*d4726bddSHONG Yifan let krate = &context.crates[&dep.id];
182*d4726bddSHONG Yifan let alias_rule = krate
183*d4726bddSHONG Yifan .alias_rule
184*d4726bddSHONG Yifan .as_ref()
185*d4726bddSHONG Yifan .unwrap_or(&self.config.default_alias_rule);
186*d4726bddSHONG Yifan
187*d4726bddSHONG Yifan if let Some(library_target_name) = &krate.library_target_name {
188*d4726bddSHONG Yifan let rename = dep.alias.as_ref().unwrap_or(&krate.name);
189*d4726bddSHONG Yifan dependencies.push(Alias {
190*d4726bddSHONG Yifan rule: alias_rule.rule(),
191*d4726bddSHONG Yifan // If duplicates exist, include version to disambiguate them.
192*d4726bddSHONG Yifan name: if context.has_duplicate_workspace_member_dep(&dep) {
193*d4726bddSHONG Yifan format!("{}-{}", rename, krate.version)
194*d4726bddSHONG Yifan } else {
195*d4726bddSHONG Yifan rename.clone()
196*d4726bddSHONG Yifan },
197*d4726bddSHONG Yifan actual: self.crate_label(
198*d4726bddSHONG Yifan &krate.name,
199*d4726bddSHONG Yifan &krate.version.to_string(),
200*d4726bddSHONG Yifan library_target_name,
201*d4726bddSHONG Yifan ),
202*d4726bddSHONG Yifan tags: BTreeSet::from(["manual".to_owned()]),
203*d4726bddSHONG Yifan });
204*d4726bddSHONG Yifan }
205*d4726bddSHONG Yifan
206*d4726bddSHONG Yifan for (alias, target) in &krate.extra_aliased_targets {
207*d4726bddSHONG Yifan dependencies.push(Alias {
208*d4726bddSHONG Yifan rule: alias_rule.rule(),
209*d4726bddSHONG Yifan name: alias.clone(),
210*d4726bddSHONG Yifan actual: self.crate_label(&krate.name, &krate.version.to_string(), target),
211*d4726bddSHONG Yifan tags: BTreeSet::from(["manual".to_owned()]),
212*d4726bddSHONG Yifan });
213*d4726bddSHONG Yifan }
214*d4726bddSHONG Yifan }
215*d4726bddSHONG Yifan
216*d4726bddSHONG Yifan let duplicates: Vec<_> = dependencies
217*d4726bddSHONG Yifan .iter()
218*d4726bddSHONG Yifan .map(|alias| &alias.name)
219*d4726bddSHONG Yifan .duplicates()
220*d4726bddSHONG Yifan .sorted()
221*d4726bddSHONG Yifan .collect();
222*d4726bddSHONG Yifan
223*d4726bddSHONG Yifan assert!(
224*d4726bddSHONG Yifan duplicates.is_empty(),
225*d4726bddSHONG Yifan "Found duplicate aliases that must be changed (Check your `extra_aliased_targets`): {:#?}",
226*d4726bddSHONG Yifan duplicates
227*d4726bddSHONG Yifan );
228*d4726bddSHONG Yifan
229*d4726bddSHONG Yifan if !dependencies.is_empty() {
230*d4726bddSHONG Yifan let comment = "# Workspace Member Dependencies".to_owned();
231*d4726bddSHONG Yifan starlark.push(Starlark::Verbatim(comment));
232*d4726bddSHONG Yifan starlark.extend(dependencies.into_iter().map(Starlark::Alias));
233*d4726bddSHONG Yifan }
234*d4726bddSHONG Yifan
235*d4726bddSHONG Yifan // An `alias` for each binary dependency.
236*d4726bddSHONG Yifan let mut binaries = Vec::new();
237*d4726bddSHONG Yifan for crate_id in &context.binary_crates {
238*d4726bddSHONG Yifan let krate = &context.crates[crate_id];
239*d4726bddSHONG Yifan for rule in &krate.targets {
240*d4726bddSHONG Yifan if let Rule::Binary(bin) = rule {
241*d4726bddSHONG Yifan binaries.push(Alias {
242*d4726bddSHONG Yifan rule: AliasRule::default().rule(),
243*d4726bddSHONG Yifan // If duplicates exist, include version to disambiguate them.
244*d4726bddSHONG Yifan name: if context.has_duplicate_binary_crate(crate_id) {
245*d4726bddSHONG Yifan format!("{}-{}__{}", krate.name, krate.version, bin.crate_name)
246*d4726bddSHONG Yifan } else {
247*d4726bddSHONG Yifan format!("{}__{}", krate.name, bin.crate_name)
248*d4726bddSHONG Yifan },
249*d4726bddSHONG Yifan actual: self.crate_label(
250*d4726bddSHONG Yifan &krate.name,
251*d4726bddSHONG Yifan &krate.version.to_string(),
252*d4726bddSHONG Yifan &format!("{}__bin", bin.crate_name),
253*d4726bddSHONG Yifan ),
254*d4726bddSHONG Yifan tags: BTreeSet::from(["manual".to_owned()]),
255*d4726bddSHONG Yifan });
256*d4726bddSHONG Yifan }
257*d4726bddSHONG Yifan }
258*d4726bddSHONG Yifan }
259*d4726bddSHONG Yifan if !binaries.is_empty() {
260*d4726bddSHONG Yifan let comment = "# Binaries".to_owned();
261*d4726bddSHONG Yifan starlark.push(Starlark::Verbatim(comment));
262*d4726bddSHONG Yifan starlark.extend(binaries.into_iter().map(Starlark::Alias));
263*d4726bddSHONG Yifan }
264*d4726bddSHONG Yifan
265*d4726bddSHONG Yifan let starlark = starlark::serialize(&starlark)?;
266*d4726bddSHONG Yifan Ok(starlark)
267*d4726bddSHONG Yifan }
268*d4726bddSHONG Yifan
render_build_files( &self, context: &Context, platforms: &Platforms, ) -> Result<BTreeMap<PathBuf, String>>269*d4726bddSHONG Yifan fn render_build_files(
270*d4726bddSHONG Yifan &self,
271*d4726bddSHONG Yifan context: &Context,
272*d4726bddSHONG Yifan platforms: &Platforms,
273*d4726bddSHONG Yifan ) -> Result<BTreeMap<PathBuf, String>> {
274*d4726bddSHONG Yifan let default_splicing_package_id = default_splicing_package_crate_id();
275*d4726bddSHONG Yifan context
276*d4726bddSHONG Yifan .crates
277*d4726bddSHONG Yifan .keys()
278*d4726bddSHONG Yifan // Do not render the default splicing package
279*d4726bddSHONG Yifan .filter(|id| *id != &default_splicing_package_id)
280*d4726bddSHONG Yifan // Do not render local packages
281*d4726bddSHONG Yifan .filter(|id| !context.workspace_members.contains_key(id))
282*d4726bddSHONG Yifan .map(|id| {
283*d4726bddSHONG Yifan let label = match render_build_file_template(
284*d4726bddSHONG Yifan &self.config.build_file_template,
285*d4726bddSHONG Yifan &id.name,
286*d4726bddSHONG Yifan &id.version.to_string(),
287*d4726bddSHONG Yifan ) {
288*d4726bddSHONG Yifan Ok(label) => label,
289*d4726bddSHONG Yifan Err(e) => bail!(e),
290*d4726bddSHONG Yifan };
291*d4726bddSHONG Yifan
292*d4726bddSHONG Yifan let filename = Renderer::label_to_path(&label);
293*d4726bddSHONG Yifan let content = self.render_one_build_file(platforms, &context.crates[id])?;
294*d4726bddSHONG Yifan Ok((filename, content))
295*d4726bddSHONG Yifan })
296*d4726bddSHONG Yifan .collect()
297*d4726bddSHONG Yifan }
298*d4726bddSHONG Yifan
render_one_build_file(&self, platforms: &Platforms, krate: &CrateContext) -> Result<String>299*d4726bddSHONG Yifan fn render_one_build_file(&self, platforms: &Platforms, krate: &CrateContext) -> Result<String> {
300*d4726bddSHONG Yifan let mut starlark = Vec::new();
301*d4726bddSHONG Yifan
302*d4726bddSHONG Yifan // Banner comment for top of the file.
303*d4726bddSHONG Yifan let header = self.engine.render_header()?;
304*d4726bddSHONG Yifan starlark.push(Starlark::Verbatim(header));
305*d4726bddSHONG Yifan
306*d4726bddSHONG Yifan // Loads: map of bzl file to set of items imported from that file. These
307*d4726bddSHONG Yifan // get inserted into `starlark` at the bottom of this function.
308*d4726bddSHONG Yifan let mut loads: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
309*d4726bddSHONG Yifan let mut load = |bzl: &str, item: &str| {
310*d4726bddSHONG Yifan loads
311*d4726bddSHONG Yifan .entry(bzl.to_owned())
312*d4726bddSHONG Yifan .or_default()
313*d4726bddSHONG Yifan .insert(item.to_owned())
314*d4726bddSHONG Yifan };
315*d4726bddSHONG Yifan
316*d4726bddSHONG Yifan let disable_visibility = "# buildifier: disable=bzl-visibility".to_owned();
317*d4726bddSHONG Yifan starlark.push(Starlark::Verbatim(disable_visibility));
318*d4726bddSHONG Yifan starlark.push(Starlark::Load(Load {
319*d4726bddSHONG Yifan bzl: "@rules_rust//crate_universe/private:selects.bzl".to_owned(),
320*d4726bddSHONG Yifan items: BTreeSet::from(["selects".to_owned()]),
321*d4726bddSHONG Yifan }));
322*d4726bddSHONG Yifan
323*d4726bddSHONG Yifan if self.config.generate_rules_license_metadata {
324*d4726bddSHONG Yifan let has_license_ids = !krate.license_ids.is_empty();
325*d4726bddSHONG Yifan let mut package_metadata = BTreeSet::from([Label::Relative {
326*d4726bddSHONG Yifan target: "package_info".to_owned(),
327*d4726bddSHONG Yifan }]);
328*d4726bddSHONG Yifan
329*d4726bddSHONG Yifan starlark.push(Starlark::Load(Load {
330*d4726bddSHONG Yifan bzl: "@rules_license//rules:package_info.bzl".to_owned(),
331*d4726bddSHONG Yifan items: BTreeSet::from(["package_info".to_owned()]),
332*d4726bddSHONG Yifan }));
333*d4726bddSHONG Yifan
334*d4726bddSHONG Yifan if has_license_ids {
335*d4726bddSHONG Yifan starlark.push(Starlark::Load(Load {
336*d4726bddSHONG Yifan bzl: "@rules_license//rules:license.bzl".to_owned(),
337*d4726bddSHONG Yifan items: BTreeSet::from(["license".to_owned()]),
338*d4726bddSHONG Yifan }));
339*d4726bddSHONG Yifan package_metadata.insert(Label::Relative {
340*d4726bddSHONG Yifan target: "license".to_owned(),
341*d4726bddSHONG Yifan });
342*d4726bddSHONG Yifan }
343*d4726bddSHONG Yifan
344*d4726bddSHONG Yifan let package = Package::default_visibility_public(package_metadata);
345*d4726bddSHONG Yifan starlark.push(Starlark::Package(package));
346*d4726bddSHONG Yifan
347*d4726bddSHONG Yifan starlark.push(Starlark::PackageInfo(starlark::PackageInfo {
348*d4726bddSHONG Yifan name: "package_info".to_owned(),
349*d4726bddSHONG Yifan package_name: krate.name.clone(),
350*d4726bddSHONG Yifan package_url: krate.package_url.clone().unwrap_or_default(),
351*d4726bddSHONG Yifan package_version: krate.version.to_string(),
352*d4726bddSHONG Yifan }));
353*d4726bddSHONG Yifan
354*d4726bddSHONG Yifan if has_license_ids {
355*d4726bddSHONG Yifan let mut license_kinds = BTreeSet::new();
356*d4726bddSHONG Yifan
357*d4726bddSHONG Yifan krate.license_ids.clone().into_iter().for_each(|lic| {
358*d4726bddSHONG Yifan license_kinds.insert("@rules_license//licenses/spdx:".to_owned() + &lic);
359*d4726bddSHONG Yifan });
360*d4726bddSHONG Yifan
361*d4726bddSHONG Yifan starlark.push(Starlark::License(starlark::License {
362*d4726bddSHONG Yifan name: "license".to_owned(),
363*d4726bddSHONG Yifan license_kinds,
364*d4726bddSHONG Yifan license_text: krate.license_file.clone().unwrap_or_default(),
365*d4726bddSHONG Yifan }));
366*d4726bddSHONG Yifan }
367*d4726bddSHONG Yifan } else {
368*d4726bddSHONG Yifan // Package visibility.
369*d4726bddSHONG Yifan let package = Package::default_visibility_public(BTreeSet::new());
370*d4726bddSHONG Yifan starlark.push(Starlark::Package(package));
371*d4726bddSHONG Yifan }
372*d4726bddSHONG Yifan
373*d4726bddSHONG Yifan for rule in &krate.targets {
374*d4726bddSHONG Yifan if let Some(override_target) = krate.override_targets.get(rule.override_target_key()) {
375*d4726bddSHONG Yifan starlark.push(Starlark::Alias(Alias {
376*d4726bddSHONG Yifan rule: AliasRule::default().rule(),
377*d4726bddSHONG Yifan name: rule.crate_name().to_owned(),
378*d4726bddSHONG Yifan actual: override_target.clone(),
379*d4726bddSHONG Yifan tags: BTreeSet::from(["manual".to_owned()]),
380*d4726bddSHONG Yifan }));
381*d4726bddSHONG Yifan } else {
382*d4726bddSHONG Yifan match rule {
383*d4726bddSHONG Yifan Rule::BuildScript(target) => {
384*d4726bddSHONG Yifan load("@rules_rust//cargo:defs.bzl", "cargo_build_script");
385*d4726bddSHONG Yifan let cargo_build_script =
386*d4726bddSHONG Yifan self.make_cargo_build_script(platforms, krate, target)?;
387*d4726bddSHONG Yifan starlark.push(Starlark::CargoBuildScript(cargo_build_script));
388*d4726bddSHONG Yifan starlark.push(Starlark::Alias(Alias {
389*d4726bddSHONG Yifan rule: AliasRule::default().rule(),
390*d4726bddSHONG Yifan name: target.crate_name.clone(),
391*d4726bddSHONG Yifan actual: Label::from_str("_bs").unwrap(),
392*d4726bddSHONG Yifan tags: BTreeSet::from(["manual".to_owned()]),
393*d4726bddSHONG Yifan }));
394*d4726bddSHONG Yifan }
395*d4726bddSHONG Yifan Rule::ProcMacro(target) => {
396*d4726bddSHONG Yifan load("@rules_rust//rust:defs.bzl", "rust_proc_macro");
397*d4726bddSHONG Yifan let rust_proc_macro =
398*d4726bddSHONG Yifan self.make_rust_proc_macro(platforms, krate, target)?;
399*d4726bddSHONG Yifan starlark.push(Starlark::RustProcMacro(rust_proc_macro));
400*d4726bddSHONG Yifan }
401*d4726bddSHONG Yifan Rule::Library(target) => {
402*d4726bddSHONG Yifan load("@rules_rust//rust:defs.bzl", "rust_library");
403*d4726bddSHONG Yifan let rust_library = self.make_rust_library(platforms, krate, target)?;
404*d4726bddSHONG Yifan starlark.push(Starlark::RustLibrary(rust_library));
405*d4726bddSHONG Yifan }
406*d4726bddSHONG Yifan Rule::Binary(target) => {
407*d4726bddSHONG Yifan load("@rules_rust//rust:defs.bzl", "rust_binary");
408*d4726bddSHONG Yifan let rust_binary = self.make_rust_binary(platforms, krate, target)?;
409*d4726bddSHONG Yifan starlark.push(Starlark::RustBinary(rust_binary));
410*d4726bddSHONG Yifan }
411*d4726bddSHONG Yifan }
412*d4726bddSHONG Yifan }
413*d4726bddSHONG Yifan }
414*d4726bddSHONG Yifan
415*d4726bddSHONG Yifan if let Some(additive_build_file_content) = &krate.additive_build_file_content {
416*d4726bddSHONG Yifan let comment = "# Additive BUILD file content".to_owned();
417*d4726bddSHONG Yifan starlark.push(Starlark::Verbatim(comment));
418*d4726bddSHONG Yifan starlark.push(Starlark::Verbatim(additive_build_file_content.clone()));
419*d4726bddSHONG Yifan }
420*d4726bddSHONG Yifan
421*d4726bddSHONG Yifan // Insert all the loads immediately after the header banner comment.
422*d4726bddSHONG Yifan let loads = loads
423*d4726bddSHONG Yifan .into_iter()
424*d4726bddSHONG Yifan .map(|(bzl, items)| Starlark::Load(Load { bzl, items }));
425*d4726bddSHONG Yifan starlark.splice(1..1, loads);
426*d4726bddSHONG Yifan
427*d4726bddSHONG Yifan let starlark = starlark::serialize(&starlark)?;
428*d4726bddSHONG Yifan Ok(starlark)
429*d4726bddSHONG Yifan }
430*d4726bddSHONG Yifan
make_cargo_build_script( &self, platforms: &Platforms, krate: &CrateContext, target: &TargetAttributes, ) -> Result<CargoBuildScript>431*d4726bddSHONG Yifan fn make_cargo_build_script(
432*d4726bddSHONG Yifan &self,
433*d4726bddSHONG Yifan platforms: &Platforms,
434*d4726bddSHONG Yifan krate: &CrateContext,
435*d4726bddSHONG Yifan target: &TargetAttributes,
436*d4726bddSHONG Yifan ) -> Result<CargoBuildScript> {
437*d4726bddSHONG Yifan let attrs = krate.build_script_attrs.as_ref();
438*d4726bddSHONG Yifan
439*d4726bddSHONG Yifan Ok(CargoBuildScript {
440*d4726bddSHONG Yifan // Because `cargo_build_script` does some invisible target name
441*d4726bddSHONG Yifan // mutating to determine the package and crate name for a build
442*d4726bddSHONG Yifan // script, the Bazel target name of any build script cannot be the
443*d4726bddSHONG Yifan // Cargo canonical name of "cargo_build_script" without losing out
444*d4726bddSHONG Yifan // on having certain Cargo environment variables set.
445*d4726bddSHONG Yifan //
446*d4726bddSHONG Yifan // Do not change this name to "cargo_build_script".
447*d4726bddSHONG Yifan //
448*d4726bddSHONG Yifan // This is set to a short name to avoid long path name issues on windows.
449*d4726bddSHONG Yifan name: "_bs".to_string(),
450*d4726bddSHONG Yifan aliases: SelectDict::new(self.make_aliases(krate, true, false), platforms),
451*d4726bddSHONG Yifan build_script_env: SelectDict::new(
452*d4726bddSHONG Yifan attrs
453*d4726bddSHONG Yifan .map(|attrs| attrs.build_script_env.clone())
454*d4726bddSHONG Yifan .unwrap_or_default(),
455*d4726bddSHONG Yifan platforms,
456*d4726bddSHONG Yifan ),
457*d4726bddSHONG Yifan compile_data: make_data(
458*d4726bddSHONG Yifan platforms,
459*d4726bddSHONG Yifan Default::default(),
460*d4726bddSHONG Yifan attrs
461*d4726bddSHONG Yifan .map(|attrs| attrs.compile_data.clone())
462*d4726bddSHONG Yifan .unwrap_or_default(),
463*d4726bddSHONG Yifan ),
464*d4726bddSHONG Yifan crate_features: SelectSet::new(krate.common_attrs.crate_features.clone(), platforms),
465*d4726bddSHONG Yifan crate_name: utils::sanitize_module_name(&target.crate_name),
466*d4726bddSHONG Yifan crate_root: target.crate_root.clone(),
467*d4726bddSHONG Yifan data: make_data(
468*d4726bddSHONG Yifan platforms,
469*d4726bddSHONG Yifan attrs
470*d4726bddSHONG Yifan .map(|attrs| attrs.data_glob.clone())
471*d4726bddSHONG Yifan .unwrap_or_default(),
472*d4726bddSHONG Yifan attrs.map(|attrs| attrs.data.clone()).unwrap_or_default(),
473*d4726bddSHONG Yifan ),
474*d4726bddSHONG Yifan deps: SelectSet::new(
475*d4726bddSHONG Yifan self.make_deps(
476*d4726bddSHONG Yifan attrs.map(|attrs| attrs.deps.clone()).unwrap_or_default(),
477*d4726bddSHONG Yifan attrs
478*d4726bddSHONG Yifan .map(|attrs| attrs.extra_deps.clone())
479*d4726bddSHONG Yifan .unwrap_or_default(),
480*d4726bddSHONG Yifan ),
481*d4726bddSHONG Yifan platforms,
482*d4726bddSHONG Yifan ),
483*d4726bddSHONG Yifan link_deps: SelectSet::new(
484*d4726bddSHONG Yifan self.make_deps(
485*d4726bddSHONG Yifan attrs
486*d4726bddSHONG Yifan .map(|attrs| attrs.link_deps.clone())
487*d4726bddSHONG Yifan .unwrap_or_default(),
488*d4726bddSHONG Yifan attrs
489*d4726bddSHONG Yifan .map(|attrs| attrs.extra_link_deps.clone())
490*d4726bddSHONG Yifan .unwrap_or_default(),
491*d4726bddSHONG Yifan ),
492*d4726bddSHONG Yifan platforms,
493*d4726bddSHONG Yifan ),
494*d4726bddSHONG Yifan edition: krate.common_attrs.edition.clone(),
495*d4726bddSHONG Yifan linker_script: krate.common_attrs.linker_script.clone(),
496*d4726bddSHONG Yifan links: attrs.and_then(|attrs| attrs.links.clone()),
497*d4726bddSHONG Yifan pkg_name: Some(krate.name.clone()),
498*d4726bddSHONG Yifan proc_macro_deps: SelectSet::new(
499*d4726bddSHONG Yifan self.make_deps(
500*d4726bddSHONG Yifan attrs
501*d4726bddSHONG Yifan .map(|attrs| attrs.proc_macro_deps.clone())
502*d4726bddSHONG Yifan .unwrap_or_default(),
503*d4726bddSHONG Yifan attrs
504*d4726bddSHONG Yifan .map(|attrs| attrs.extra_proc_macro_deps.clone())
505*d4726bddSHONG Yifan .unwrap_or_default(),
506*d4726bddSHONG Yifan ),
507*d4726bddSHONG Yifan platforms,
508*d4726bddSHONG Yifan ),
509*d4726bddSHONG Yifan rundir: SelectScalar::new(
510*d4726bddSHONG Yifan attrs.map(|attrs| attrs.rundir.clone()).unwrap_or_default(),
511*d4726bddSHONG Yifan platforms,
512*d4726bddSHONG Yifan ),
513*d4726bddSHONG Yifan rustc_env: SelectDict::new(
514*d4726bddSHONG Yifan attrs
515*d4726bddSHONG Yifan .map(|attrs| attrs.rustc_env.clone())
516*d4726bddSHONG Yifan .unwrap_or_default(),
517*d4726bddSHONG Yifan platforms,
518*d4726bddSHONG Yifan ),
519*d4726bddSHONG Yifan rustc_env_files: SelectSet::new(
520*d4726bddSHONG Yifan attrs
521*d4726bddSHONG Yifan .map(|attrs| attrs.rustc_env_files.clone())
522*d4726bddSHONG Yifan .unwrap_or_default(),
523*d4726bddSHONG Yifan platforms,
524*d4726bddSHONG Yifan ),
525*d4726bddSHONG Yifan rustc_flags: SelectList::new(
526*d4726bddSHONG Yifan // In most cases, warnings in 3rd party crates are not
527*d4726bddSHONG Yifan // interesting as they're out of the control of consumers. The
528*d4726bddSHONG Yifan // flag here silences warnings. For more details see:
529*d4726bddSHONG Yifan // https://doc.rust-lang.org/rustc/lints/levels.html
530*d4726bddSHONG Yifan Select::merge(
531*d4726bddSHONG Yifan Select::from_value(Vec::from(["--cap-lints=allow".to_owned()])),
532*d4726bddSHONG Yifan attrs
533*d4726bddSHONG Yifan .map(|attrs| attrs.rustc_flags.clone())
534*d4726bddSHONG Yifan .unwrap_or_default(),
535*d4726bddSHONG Yifan ),
536*d4726bddSHONG Yifan platforms,
537*d4726bddSHONG Yifan ),
538*d4726bddSHONG Yifan srcs: target.srcs.clone(),
539*d4726bddSHONG Yifan tags: {
540*d4726bddSHONG Yifan let mut tags = BTreeSet::from_iter(krate.common_attrs.tags.iter().cloned());
541*d4726bddSHONG Yifan tags.insert("cargo-bazel".to_owned());
542*d4726bddSHONG Yifan tags.insert("manual".to_owned());
543*d4726bddSHONG Yifan tags.insert("noclippy".to_owned());
544*d4726bddSHONG Yifan tags.insert("norustfmt".to_owned());
545*d4726bddSHONG Yifan tags.insert(format!("crate-name={}", krate.name));
546*d4726bddSHONG Yifan tags
547*d4726bddSHONG Yifan },
548*d4726bddSHONG Yifan tools: SelectSet::new(
549*d4726bddSHONG Yifan attrs.map(|attrs| attrs.tools.clone()).unwrap_or_default(),
550*d4726bddSHONG Yifan platforms,
551*d4726bddSHONG Yifan ),
552*d4726bddSHONG Yifan toolchains: attrs.map_or_else(BTreeSet::new, |attrs| attrs.toolchains.clone()),
553*d4726bddSHONG Yifan version: krate.common_attrs.version.clone(),
554*d4726bddSHONG Yifan visibility: BTreeSet::from(["//visibility:private".to_owned()]),
555*d4726bddSHONG Yifan })
556*d4726bddSHONG Yifan }
557*d4726bddSHONG Yifan
make_rust_proc_macro( &self, platforms: &Platforms, krate: &CrateContext, target: &TargetAttributes, ) -> Result<RustProcMacro>558*d4726bddSHONG Yifan fn make_rust_proc_macro(
559*d4726bddSHONG Yifan &self,
560*d4726bddSHONG Yifan platforms: &Platforms,
561*d4726bddSHONG Yifan krate: &CrateContext,
562*d4726bddSHONG Yifan target: &TargetAttributes,
563*d4726bddSHONG Yifan ) -> Result<RustProcMacro> {
564*d4726bddSHONG Yifan Ok(RustProcMacro {
565*d4726bddSHONG Yifan name: target.crate_name.clone(),
566*d4726bddSHONG Yifan deps: SelectSet::new(
567*d4726bddSHONG Yifan self.make_deps(
568*d4726bddSHONG Yifan krate.common_attrs.deps.clone(),
569*d4726bddSHONG Yifan krate.common_attrs.extra_deps.clone(),
570*d4726bddSHONG Yifan ),
571*d4726bddSHONG Yifan platforms,
572*d4726bddSHONG Yifan ),
573*d4726bddSHONG Yifan proc_macro_deps: SelectSet::new(
574*d4726bddSHONG Yifan self.make_deps(
575*d4726bddSHONG Yifan krate.common_attrs.proc_macro_deps.clone(),
576*d4726bddSHONG Yifan krate.common_attrs.extra_proc_macro_deps.clone(),
577*d4726bddSHONG Yifan ),
578*d4726bddSHONG Yifan platforms,
579*d4726bddSHONG Yifan ),
580*d4726bddSHONG Yifan aliases: SelectDict::new(self.make_aliases(krate, false, false), platforms),
581*d4726bddSHONG Yifan common: self.make_common_attrs(platforms, krate, target)?,
582*d4726bddSHONG Yifan })
583*d4726bddSHONG Yifan }
584*d4726bddSHONG Yifan
make_rust_library( &self, platforms: &Platforms, krate: &CrateContext, target: &TargetAttributes, ) -> Result<RustLibrary>585*d4726bddSHONG Yifan fn make_rust_library(
586*d4726bddSHONG Yifan &self,
587*d4726bddSHONG Yifan platforms: &Platforms,
588*d4726bddSHONG Yifan krate: &CrateContext,
589*d4726bddSHONG Yifan target: &TargetAttributes,
590*d4726bddSHONG Yifan ) -> Result<RustLibrary> {
591*d4726bddSHONG Yifan Ok(RustLibrary {
592*d4726bddSHONG Yifan name: target.crate_name.clone(),
593*d4726bddSHONG Yifan deps: SelectSet::new(
594*d4726bddSHONG Yifan self.make_deps(
595*d4726bddSHONG Yifan krate.common_attrs.deps.clone(),
596*d4726bddSHONG Yifan krate.common_attrs.extra_deps.clone(),
597*d4726bddSHONG Yifan ),
598*d4726bddSHONG Yifan platforms,
599*d4726bddSHONG Yifan ),
600*d4726bddSHONG Yifan proc_macro_deps: SelectSet::new(
601*d4726bddSHONG Yifan self.make_deps(
602*d4726bddSHONG Yifan krate.common_attrs.proc_macro_deps.clone(),
603*d4726bddSHONG Yifan krate.common_attrs.extra_proc_macro_deps.clone(),
604*d4726bddSHONG Yifan ),
605*d4726bddSHONG Yifan platforms,
606*d4726bddSHONG Yifan ),
607*d4726bddSHONG Yifan aliases: SelectDict::new(self.make_aliases(krate, false, false), platforms),
608*d4726bddSHONG Yifan common: self.make_common_attrs(platforms, krate, target)?,
609*d4726bddSHONG Yifan disable_pipelining: krate.disable_pipelining,
610*d4726bddSHONG Yifan })
611*d4726bddSHONG Yifan }
612*d4726bddSHONG Yifan
make_rust_binary( &self, platforms: &Platforms, krate: &CrateContext, target: &TargetAttributes, ) -> Result<RustBinary>613*d4726bddSHONG Yifan fn make_rust_binary(
614*d4726bddSHONG Yifan &self,
615*d4726bddSHONG Yifan platforms: &Platforms,
616*d4726bddSHONG Yifan krate: &CrateContext,
617*d4726bddSHONG Yifan target: &TargetAttributes,
618*d4726bddSHONG Yifan ) -> Result<RustBinary> {
619*d4726bddSHONG Yifan Ok(RustBinary {
620*d4726bddSHONG Yifan name: format!("{}__bin", target.crate_name),
621*d4726bddSHONG Yifan deps: {
622*d4726bddSHONG Yifan let mut deps = self.make_deps(
623*d4726bddSHONG Yifan krate.common_attrs.deps.clone(),
624*d4726bddSHONG Yifan krate.common_attrs.extra_deps.clone(),
625*d4726bddSHONG Yifan );
626*d4726bddSHONG Yifan if let Some(library_target_name) = &krate.library_target_name {
627*d4726bddSHONG Yifan deps.insert(
628*d4726bddSHONG Yifan Label::from_str(&format!(":{library_target_name}")).unwrap(),
629*d4726bddSHONG Yifan None,
630*d4726bddSHONG Yifan );
631*d4726bddSHONG Yifan }
632*d4726bddSHONG Yifan SelectSet::new(deps, platforms)
633*d4726bddSHONG Yifan },
634*d4726bddSHONG Yifan proc_macro_deps: SelectSet::new(
635*d4726bddSHONG Yifan self.make_deps(
636*d4726bddSHONG Yifan krate.common_attrs.proc_macro_deps.clone(),
637*d4726bddSHONG Yifan krate.common_attrs.extra_proc_macro_deps.clone(),
638*d4726bddSHONG Yifan ),
639*d4726bddSHONG Yifan platforms,
640*d4726bddSHONG Yifan ),
641*d4726bddSHONG Yifan aliases: SelectDict::new(self.make_aliases(krate, false, false), platforms),
642*d4726bddSHONG Yifan common: self.make_common_attrs(platforms, krate, target)?,
643*d4726bddSHONG Yifan })
644*d4726bddSHONG Yifan }
645*d4726bddSHONG Yifan
make_common_attrs( &self, platforms: &Platforms, krate: &CrateContext, target: &TargetAttributes, ) -> Result<CommonAttrs>646*d4726bddSHONG Yifan fn make_common_attrs(
647*d4726bddSHONG Yifan &self,
648*d4726bddSHONG Yifan platforms: &Platforms,
649*d4726bddSHONG Yifan krate: &CrateContext,
650*d4726bddSHONG Yifan target: &TargetAttributes,
651*d4726bddSHONG Yifan ) -> Result<CommonAttrs> {
652*d4726bddSHONG Yifan Ok(CommonAttrs {
653*d4726bddSHONG Yifan compile_data: make_data(
654*d4726bddSHONG Yifan platforms,
655*d4726bddSHONG Yifan krate.common_attrs.compile_data_glob.clone(),
656*d4726bddSHONG Yifan krate.common_attrs.compile_data.clone(),
657*d4726bddSHONG Yifan ),
658*d4726bddSHONG Yifan crate_features: SelectSet::new(krate.common_attrs.crate_features.clone(), platforms),
659*d4726bddSHONG Yifan crate_root: target.crate_root.clone(),
660*d4726bddSHONG Yifan data: make_data(
661*d4726bddSHONG Yifan platforms,
662*d4726bddSHONG Yifan krate.common_attrs.data_glob.clone(),
663*d4726bddSHONG Yifan krate.common_attrs.data.clone(),
664*d4726bddSHONG Yifan ),
665*d4726bddSHONG Yifan edition: krate.common_attrs.edition.clone(),
666*d4726bddSHONG Yifan linker_script: krate.common_attrs.linker_script.clone(),
667*d4726bddSHONG Yifan rustc_env: SelectDict::new(krate.common_attrs.rustc_env.clone(), platforms),
668*d4726bddSHONG Yifan rustc_env_files: SelectSet::new(krate.common_attrs.rustc_env_files.clone(), platforms),
669*d4726bddSHONG Yifan rustc_flags: SelectList::new(
670*d4726bddSHONG Yifan // In most cases, warnings in 3rd party crates are not
671*d4726bddSHONG Yifan // interesting as they're out of the control of consumers. The
672*d4726bddSHONG Yifan // flag here silences warnings. For more details see:
673*d4726bddSHONG Yifan // https://doc.rust-lang.org/rustc/lints/levels.html
674*d4726bddSHONG Yifan Select::merge(
675*d4726bddSHONG Yifan Select::from_value(Vec::from(["--cap-lints=allow".to_owned()])),
676*d4726bddSHONG Yifan krate.common_attrs.rustc_flags.clone(),
677*d4726bddSHONG Yifan ),
678*d4726bddSHONG Yifan platforms,
679*d4726bddSHONG Yifan ),
680*d4726bddSHONG Yifan srcs: target.srcs.clone(),
681*d4726bddSHONG Yifan tags: {
682*d4726bddSHONG Yifan let mut tags = BTreeSet::from_iter(krate.common_attrs.tags.iter().cloned());
683*d4726bddSHONG Yifan tags.insert("cargo-bazel".to_owned());
684*d4726bddSHONG Yifan tags.insert("manual".to_owned());
685*d4726bddSHONG Yifan tags.insert("noclippy".to_owned());
686*d4726bddSHONG Yifan tags.insert("norustfmt".to_owned());
687*d4726bddSHONG Yifan tags.insert(format!("crate-name={}", krate.name));
688*d4726bddSHONG Yifan tags
689*d4726bddSHONG Yifan },
690*d4726bddSHONG Yifan target_compatible_with: self.config.generate_target_compatible_with.then(|| {
691*d4726bddSHONG Yifan TargetCompatibleWith::new(
692*d4726bddSHONG Yifan self.supported_platform_triples
693*d4726bddSHONG Yifan .iter()
694*d4726bddSHONG Yifan .map(|target_triple| {
695*d4726bddSHONG Yifan render_platform_constraint_label(
696*d4726bddSHONG Yifan &self.config.platforms_template,
697*d4726bddSHONG Yifan target_triple,
698*d4726bddSHONG Yifan )
699*d4726bddSHONG Yifan })
700*d4726bddSHONG Yifan .collect(),
701*d4726bddSHONG Yifan )
702*d4726bddSHONG Yifan }),
703*d4726bddSHONG Yifan version: krate.common_attrs.version.clone(),
704*d4726bddSHONG Yifan })
705*d4726bddSHONG Yifan }
706*d4726bddSHONG Yifan
707*d4726bddSHONG Yifan /// Filter a crate's dependencies to only ones with aliases
make_aliases( &self, krate: &CrateContext, build: bool, include_dev: bool, ) -> Select<BTreeMap<Label, String>>708*d4726bddSHONG Yifan fn make_aliases(
709*d4726bddSHONG Yifan &self,
710*d4726bddSHONG Yifan krate: &CrateContext,
711*d4726bddSHONG Yifan build: bool,
712*d4726bddSHONG Yifan include_dev: bool,
713*d4726bddSHONG Yifan ) -> Select<BTreeMap<Label, String>> {
714*d4726bddSHONG Yifan let mut dependency_selects = Vec::new();
715*d4726bddSHONG Yifan if build {
716*d4726bddSHONG Yifan if let Some(build_script_attrs) = &krate.build_script_attrs {
717*d4726bddSHONG Yifan dependency_selects.push(&build_script_attrs.deps);
718*d4726bddSHONG Yifan dependency_selects.push(&build_script_attrs.proc_macro_deps);
719*d4726bddSHONG Yifan }
720*d4726bddSHONG Yifan } else {
721*d4726bddSHONG Yifan dependency_selects.push(&krate.common_attrs.deps);
722*d4726bddSHONG Yifan dependency_selects.push(&krate.common_attrs.proc_macro_deps);
723*d4726bddSHONG Yifan if include_dev {
724*d4726bddSHONG Yifan dependency_selects.push(&krate.common_attrs.deps_dev);
725*d4726bddSHONG Yifan dependency_selects.push(&krate.common_attrs.proc_macro_deps_dev);
726*d4726bddSHONG Yifan }
727*d4726bddSHONG Yifan }
728*d4726bddSHONG Yifan
729*d4726bddSHONG Yifan let mut aliases: Select<BTreeMap<Label, String>> = Select::default();
730*d4726bddSHONG Yifan for dependency_select in dependency_selects.iter() {
731*d4726bddSHONG Yifan for (configuration, dependency) in dependency_select.items().into_iter() {
732*d4726bddSHONG Yifan if let Some(alias) = &dependency.alias {
733*d4726bddSHONG Yifan let label = self.crate_label(
734*d4726bddSHONG Yifan &dependency.id.name,
735*d4726bddSHONG Yifan &dependency.id.version.to_string(),
736*d4726bddSHONG Yifan &dependency.target,
737*d4726bddSHONG Yifan );
738*d4726bddSHONG Yifan aliases.insert((label, alias.clone()), configuration.clone());
739*d4726bddSHONG Yifan }
740*d4726bddSHONG Yifan }
741*d4726bddSHONG Yifan }
742*d4726bddSHONG Yifan aliases
743*d4726bddSHONG Yifan }
744*d4726bddSHONG Yifan
make_deps( &self, deps: Select<BTreeSet<CrateDependency>>, extra_deps: Select<BTreeSet<Label>>, ) -> Select<BTreeSet<Label>>745*d4726bddSHONG Yifan fn make_deps(
746*d4726bddSHONG Yifan &self,
747*d4726bddSHONG Yifan deps: Select<BTreeSet<CrateDependency>>,
748*d4726bddSHONG Yifan extra_deps: Select<BTreeSet<Label>>,
749*d4726bddSHONG Yifan ) -> Select<BTreeSet<Label>> {
750*d4726bddSHONG Yifan Select::merge(
751*d4726bddSHONG Yifan deps.map(|dep| {
752*d4726bddSHONG Yifan self.crate_label(&dep.id.name, &dep.id.version.to_string(), &dep.target)
753*d4726bddSHONG Yifan }),
754*d4726bddSHONG Yifan extra_deps,
755*d4726bddSHONG Yifan )
756*d4726bddSHONG Yifan }
757*d4726bddSHONG Yifan
render_vendor_support_files(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>>758*d4726bddSHONG Yifan fn render_vendor_support_files(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
759*d4726bddSHONG Yifan let module_label = render_module_label(&self.config.crates_module_template, "crates.bzl")
760*d4726bddSHONG Yifan .context("Failed to resolve string to module file label")?;
761*d4726bddSHONG Yifan
762*d4726bddSHONG Yifan let mut map = BTreeMap::new();
763*d4726bddSHONG Yifan map.insert(
764*d4726bddSHONG Yifan Renderer::label_to_path(&module_label),
765*d4726bddSHONG Yifan self.engine.render_vendor_module_file(context)?,
766*d4726bddSHONG Yifan );
767*d4726bddSHONG Yifan
768*d4726bddSHONG Yifan Ok(map)
769*d4726bddSHONG Yifan }
770*d4726bddSHONG Yifan
label_to_path(label: &Label) -> PathBuf771*d4726bddSHONG Yifan fn label_to_path(label: &Label) -> PathBuf {
772*d4726bddSHONG Yifan match &label.package() {
773*d4726bddSHONG Yifan Some(package) if !package.is_empty() => {
774*d4726bddSHONG Yifan PathBuf::from(format!("{}/{}", package, label.target()))
775*d4726bddSHONG Yifan }
776*d4726bddSHONG Yifan Some(_) | None => PathBuf::from(label.target()),
777*d4726bddSHONG Yifan }
778*d4726bddSHONG Yifan }
779*d4726bddSHONG Yifan
crate_label(&self, name: &str, version: &str, target: &str) -> Label780*d4726bddSHONG Yifan fn crate_label(&self, name: &str, version: &str, target: &str) -> Label {
781*d4726bddSHONG Yifan Label::from_str(&sanitize_repository_name(&render_crate_bazel_label(
782*d4726bddSHONG Yifan &self.config.crate_label_template,
783*d4726bddSHONG Yifan &self.config.repository_name,
784*d4726bddSHONG Yifan name,
785*d4726bddSHONG Yifan version,
786*d4726bddSHONG Yifan target,
787*d4726bddSHONG Yifan )))
788*d4726bddSHONG Yifan .unwrap()
789*d4726bddSHONG Yifan }
790*d4726bddSHONG Yifan }
791*d4726bddSHONG Yifan
792*d4726bddSHONG Yifan /// Write a set of [crate::context::crate_context::CrateContext] to disk.
write_outputs(outputs: BTreeMap<PathBuf, String>, dry_run: bool) -> Result<()>793*d4726bddSHONG Yifan pub(crate) fn write_outputs(outputs: BTreeMap<PathBuf, String>, dry_run: bool) -> Result<()> {
794*d4726bddSHONG Yifan if dry_run {
795*d4726bddSHONG Yifan for (path, content) in outputs {
796*d4726bddSHONG Yifan println!(
797*d4726bddSHONG Yifan "==============================================================================="
798*d4726bddSHONG Yifan );
799*d4726bddSHONG Yifan println!("{}", path.display());
800*d4726bddSHONG Yifan println!(
801*d4726bddSHONG Yifan "==============================================================================="
802*d4726bddSHONG Yifan );
803*d4726bddSHONG Yifan println!("{content}\n");
804*d4726bddSHONG Yifan }
805*d4726bddSHONG Yifan } else {
806*d4726bddSHONG Yifan for (path, content) in outputs {
807*d4726bddSHONG Yifan // Ensure the output directory exists
808*d4726bddSHONG Yifan fs::create_dir_all(
809*d4726bddSHONG Yifan path.parent()
810*d4726bddSHONG Yifan .expect("All file paths should have valid directories"),
811*d4726bddSHONG Yifan )?;
812*d4726bddSHONG Yifan fs::write(&path, content.as_bytes())
813*d4726bddSHONG Yifan .context(format!("Failed to write file to disk: {}", path.display()))?;
814*d4726bddSHONG Yifan }
815*d4726bddSHONG Yifan }
816*d4726bddSHONG Yifan
817*d4726bddSHONG Yifan Ok(())
818*d4726bddSHONG Yifan }
819*d4726bddSHONG Yifan
820*d4726bddSHONG Yifan /// Render the Bazel label of a crate
render_crate_bazel_label( template: &str, repository_name: &str, name: &str, version: &str, target: &str, ) -> String821*d4726bddSHONG Yifan pub(crate) fn render_crate_bazel_label(
822*d4726bddSHONG Yifan template: &str,
823*d4726bddSHONG Yifan repository_name: &str,
824*d4726bddSHONG Yifan name: &str,
825*d4726bddSHONG Yifan version: &str,
826*d4726bddSHONG Yifan target: &str,
827*d4726bddSHONG Yifan ) -> String {
828*d4726bddSHONG Yifan template
829*d4726bddSHONG Yifan .replace("{repository}", repository_name)
830*d4726bddSHONG Yifan .replace("{name}", name)
831*d4726bddSHONG Yifan .replace("{version}", version)
832*d4726bddSHONG Yifan .replace("{target}", target)
833*d4726bddSHONG Yifan }
834*d4726bddSHONG Yifan
835*d4726bddSHONG Yifan /// Render the Bazel label of a crate
render_crate_bazel_repository( template: &str, repository_name: &str, name: &str, version: &str, ) -> String836*d4726bddSHONG Yifan pub(crate) fn render_crate_bazel_repository(
837*d4726bddSHONG Yifan template: &str,
838*d4726bddSHONG Yifan repository_name: &str,
839*d4726bddSHONG Yifan name: &str,
840*d4726bddSHONG Yifan version: &str,
841*d4726bddSHONG Yifan ) -> String {
842*d4726bddSHONG Yifan template
843*d4726bddSHONG Yifan .replace("{repository}", repository_name)
844*d4726bddSHONG Yifan .replace("{name}", name)
845*d4726bddSHONG Yifan .replace("{version}", version)
846*d4726bddSHONG Yifan }
847*d4726bddSHONG Yifan
848*d4726bddSHONG Yifan /// Render the Bazel label of a crate
render_crate_build_file(template: &str, name: &str, version: &str) -> String849*d4726bddSHONG Yifan pub(crate) fn render_crate_build_file(template: &str, name: &str, version: &str) -> String {
850*d4726bddSHONG Yifan template
851*d4726bddSHONG Yifan .replace("{name}", name)
852*d4726bddSHONG Yifan .replace("{version}", version)
853*d4726bddSHONG Yifan }
854*d4726bddSHONG Yifan
855*d4726bddSHONG Yifan /// Render the Bazel label of a vendor module label
render_module_label(template: &str, name: &str) -> Result<Label>856*d4726bddSHONG Yifan pub(crate) fn render_module_label(template: &str, name: &str) -> Result<Label> {
857*d4726bddSHONG Yifan Label::from_str(&template.replace("{file}", name))
858*d4726bddSHONG Yifan }
859*d4726bddSHONG Yifan
860*d4726bddSHONG Yifan /// Render the Bazel label of a platform triple
render_platform_constraint_label(template: &str, target_triple: &TargetTriple) -> String861*d4726bddSHONG Yifan fn render_platform_constraint_label(template: &str, target_triple: &TargetTriple) -> String {
862*d4726bddSHONG Yifan template.replace("{triple}", &target_triple.to_bazel())
863*d4726bddSHONG Yifan }
864*d4726bddSHONG Yifan
render_build_file_template(template: &str, name: &str, version: &str) -> Result<Label>865*d4726bddSHONG Yifan fn render_build_file_template(template: &str, name: &str, version: &str) -> Result<Label> {
866*d4726bddSHONG Yifan Label::from_str(
867*d4726bddSHONG Yifan &template
868*d4726bddSHONG Yifan .replace("{name}", name)
869*d4726bddSHONG Yifan .replace("{version}", version),
870*d4726bddSHONG Yifan )
871*d4726bddSHONG Yifan }
872*d4726bddSHONG Yifan
make_data( platforms: &Platforms, glob: BTreeSet<String>, select: Select<BTreeSet<Label>>, ) -> Data873*d4726bddSHONG Yifan fn make_data(
874*d4726bddSHONG Yifan platforms: &Platforms,
875*d4726bddSHONG Yifan glob: BTreeSet<String>,
876*d4726bddSHONG Yifan select: Select<BTreeSet<Label>>,
877*d4726bddSHONG Yifan ) -> Data {
878*d4726bddSHONG Yifan const COMMON_GLOB_EXCLUDES: &[&str] = &[
879*d4726bddSHONG Yifan "**/* *",
880*d4726bddSHONG Yifan "BUILD.bazel",
881*d4726bddSHONG Yifan "BUILD",
882*d4726bddSHONG Yifan "WORKSPACE.bazel",
883*d4726bddSHONG Yifan "WORKSPACE",
884*d4726bddSHONG Yifan ".tmp_git_root/**/*",
885*d4726bddSHONG Yifan ];
886*d4726bddSHONG Yifan
887*d4726bddSHONG Yifan Data {
888*d4726bddSHONG Yifan glob: Glob {
889*d4726bddSHONG Yifan allow_empty: true,
890*d4726bddSHONG Yifan include: glob,
891*d4726bddSHONG Yifan exclude: COMMON_GLOB_EXCLUDES
892*d4726bddSHONG Yifan .iter()
893*d4726bddSHONG Yifan .map(|&glob| glob.to_owned())
894*d4726bddSHONG Yifan .collect(),
895*d4726bddSHONG Yifan },
896*d4726bddSHONG Yifan select: SelectSet::new(select, platforms),
897*d4726bddSHONG Yifan }
898*d4726bddSHONG Yifan }
899*d4726bddSHONG Yifan
900*d4726bddSHONG Yifan #[cfg(test)]
901*d4726bddSHONG Yifan mod test {
902*d4726bddSHONG Yifan use super::*;
903*d4726bddSHONG Yifan
904*d4726bddSHONG Yifan use indoc::indoc;
905*d4726bddSHONG Yifan
906*d4726bddSHONG Yifan use crate::config::{Config, CrateId};
907*d4726bddSHONG Yifan use crate::context::{BuildScriptAttributes, CommonAttributes};
908*d4726bddSHONG Yifan use crate::metadata::Annotations;
909*d4726bddSHONG Yifan use crate::test;
910*d4726bddSHONG Yifan use crate::utils::normalize_cargo_file_paths;
911*d4726bddSHONG Yifan
912*d4726bddSHONG Yifan const VERSION_ZERO_ONE_ZERO: semver::Version = semver::Version::new(0, 1, 0);
913*d4726bddSHONG Yifan
mock_target_attributes() -> TargetAttributes914*d4726bddSHONG Yifan fn mock_target_attributes() -> TargetAttributes {
915*d4726bddSHONG Yifan TargetAttributes {
916*d4726bddSHONG Yifan crate_name: "mock_crate".to_owned(),
917*d4726bddSHONG Yifan crate_root: Some("src/root.rs".to_owned()),
918*d4726bddSHONG Yifan ..TargetAttributes::default()
919*d4726bddSHONG Yifan }
920*d4726bddSHONG Yifan }
921*d4726bddSHONG Yifan
mock_render_config(vendor_mode: Option<VendorMode>) -> RenderConfig922*d4726bddSHONG Yifan fn mock_render_config(vendor_mode: Option<VendorMode>) -> RenderConfig {
923*d4726bddSHONG Yifan RenderConfig {
924*d4726bddSHONG Yifan repository_name: "test_rendering".to_owned(),
925*d4726bddSHONG Yifan regen_command: "cargo_bazel_regen_command".to_owned(),
926*d4726bddSHONG Yifan vendor_mode,
927*d4726bddSHONG Yifan ..RenderConfig::default()
928*d4726bddSHONG Yifan }
929*d4726bddSHONG Yifan }
930*d4726bddSHONG Yifan
mock_supported_platform_triples() -> BTreeSet<TargetTriple>931*d4726bddSHONG Yifan fn mock_supported_platform_triples() -> BTreeSet<TargetTriple> {
932*d4726bddSHONG Yifan BTreeSet::from([
933*d4726bddSHONG Yifan TargetTriple::from_bazel("aarch64-apple-darwin".to_owned()),
934*d4726bddSHONG Yifan TargetTriple::from_bazel("aarch64-apple-ios".to_owned()),
935*d4726bddSHONG Yifan TargetTriple::from_bazel("aarch64-linux-android".to_owned()),
936*d4726bddSHONG Yifan TargetTriple::from_bazel("aarch64-pc-windows-msvc".to_owned()),
937*d4726bddSHONG Yifan TargetTriple::from_bazel("aarch64-unknown-linux-gnu".to_owned()),
938*d4726bddSHONG Yifan TargetTriple::from_bazel("arm-unknown-linux-gnueabi".to_owned()),
939*d4726bddSHONG Yifan TargetTriple::from_bazel("armv7-unknown-linux-gnueabi".to_owned()),
940*d4726bddSHONG Yifan TargetTriple::from_bazel("i686-apple-darwin".to_owned()),
941*d4726bddSHONG Yifan TargetTriple::from_bazel("i686-linux-android".to_owned()),
942*d4726bddSHONG Yifan TargetTriple::from_bazel("i686-pc-windows-msvc".to_owned()),
943*d4726bddSHONG Yifan TargetTriple::from_bazel("i686-unknown-freebsd".to_owned()),
944*d4726bddSHONG Yifan TargetTriple::from_bazel("i686-unknown-linux-gnu".to_owned()),
945*d4726bddSHONG Yifan TargetTriple::from_bazel("powerpc-unknown-linux-gnu".to_owned()),
946*d4726bddSHONG Yifan TargetTriple::from_bazel("s390x-unknown-linux-gnu".to_owned()),
947*d4726bddSHONG Yifan TargetTriple::from_bazel("wasm32-unknown-unknown".to_owned()),
948*d4726bddSHONG Yifan TargetTriple::from_bazel("wasm32-wasi".to_owned()),
949*d4726bddSHONG Yifan TargetTriple::from_bazel("x86_64-apple-darwin".to_owned()),
950*d4726bddSHONG Yifan TargetTriple::from_bazel("x86_64-apple-ios".to_owned()),
951*d4726bddSHONG Yifan TargetTriple::from_bazel("x86_64-linux-android".to_owned()),
952*d4726bddSHONG Yifan TargetTriple::from_bazel("x86_64-pc-windows-msvc".to_owned()),
953*d4726bddSHONG Yifan TargetTriple::from_bazel("x86_64-unknown-freebsd".to_owned()),
954*d4726bddSHONG Yifan TargetTriple::from_bazel("x86_64-unknown-linux-gnu".to_owned()),
955*d4726bddSHONG Yifan ])
956*d4726bddSHONG Yifan }
957*d4726bddSHONG Yifan
958*d4726bddSHONG Yifan #[test]
render_rust_library()959*d4726bddSHONG Yifan fn render_rust_library() {
960*d4726bddSHONG Yifan let mut context = Context::default();
961*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
962*d4726bddSHONG Yifan context.crates.insert(
963*d4726bddSHONG Yifan crate_id.clone(),
964*d4726bddSHONG Yifan CrateContext {
965*d4726bddSHONG Yifan name: crate_id.name,
966*d4726bddSHONG Yifan version: crate_id.version,
967*d4726bddSHONG Yifan package_url: None,
968*d4726bddSHONG Yifan repository: None,
969*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
970*d4726bddSHONG Yifan library_target_name: None,
971*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
972*d4726bddSHONG Yifan build_script_attrs: None,
973*d4726bddSHONG Yifan license: None,
974*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
975*d4726bddSHONG Yifan license_file: None,
976*d4726bddSHONG Yifan additive_build_file_content: None,
977*d4726bddSHONG Yifan disable_pipelining: false,
978*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
979*d4726bddSHONG Yifan alias_rule: None,
980*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
981*d4726bddSHONG Yifan },
982*d4726bddSHONG Yifan );
983*d4726bddSHONG Yifan
984*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
985*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
986*d4726bddSHONG Yifan
987*d4726bddSHONG Yifan let build_file_content = output
988*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
989*d4726bddSHONG Yifan .unwrap();
990*d4726bddSHONG Yifan
991*d4726bddSHONG Yifan assert!(build_file_content.contains("rust_library("));
992*d4726bddSHONG Yifan assert!(build_file_content.contains("name = \"mock_crate\""));
993*d4726bddSHONG Yifan assert!(build_file_content.contains("\"crate-name=mock_crate\""));
994*d4726bddSHONG Yifan }
995*d4726bddSHONG Yifan
996*d4726bddSHONG Yifan #[test]
test_disable_pipelining()997*d4726bddSHONG Yifan fn test_disable_pipelining() {
998*d4726bddSHONG Yifan let mut context = Context::default();
999*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1000*d4726bddSHONG Yifan context.crates.insert(
1001*d4726bddSHONG Yifan crate_id.clone(),
1002*d4726bddSHONG Yifan CrateContext {
1003*d4726bddSHONG Yifan name: crate_id.name,
1004*d4726bddSHONG Yifan version: crate_id.version,
1005*d4726bddSHONG Yifan package_url: None,
1006*d4726bddSHONG Yifan repository: None,
1007*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1008*d4726bddSHONG Yifan library_target_name: None,
1009*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1010*d4726bddSHONG Yifan build_script_attrs: None,
1011*d4726bddSHONG Yifan license: None,
1012*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1013*d4726bddSHONG Yifan license_file: None,
1014*d4726bddSHONG Yifan additive_build_file_content: None,
1015*d4726bddSHONG Yifan disable_pipelining: true,
1016*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1017*d4726bddSHONG Yifan alias_rule: None,
1018*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1019*d4726bddSHONG Yifan },
1020*d4726bddSHONG Yifan );
1021*d4726bddSHONG Yifan
1022*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1023*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1024*d4726bddSHONG Yifan
1025*d4726bddSHONG Yifan let build_file_content = output
1026*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1027*d4726bddSHONG Yifan .unwrap();
1028*d4726bddSHONG Yifan
1029*d4726bddSHONG Yifan assert!(build_file_content.contains("disable_pipelining = True"));
1030*d4726bddSHONG Yifan }
1031*d4726bddSHONG Yifan
1032*d4726bddSHONG Yifan #[test]
render_cargo_build_script()1033*d4726bddSHONG Yifan fn render_cargo_build_script() {
1034*d4726bddSHONG Yifan let mut context = Context::default();
1035*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1036*d4726bddSHONG Yifan context.crates.insert(
1037*d4726bddSHONG Yifan crate_id.clone(),
1038*d4726bddSHONG Yifan CrateContext {
1039*d4726bddSHONG Yifan name: crate_id.name,
1040*d4726bddSHONG Yifan version: crate_id.version,
1041*d4726bddSHONG Yifan package_url: None,
1042*d4726bddSHONG Yifan repository: None,
1043*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::BuildScript(TargetAttributes {
1044*d4726bddSHONG Yifan crate_name: "build_script_build".to_owned(),
1045*d4726bddSHONG Yifan crate_root: Some("build.rs".to_owned()),
1046*d4726bddSHONG Yifan ..TargetAttributes::default()
1047*d4726bddSHONG Yifan })]),
1048*d4726bddSHONG Yifan // Build script attributes are required.
1049*d4726bddSHONG Yifan library_target_name: None,
1050*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1051*d4726bddSHONG Yifan build_script_attrs: Some(BuildScriptAttributes::default()),
1052*d4726bddSHONG Yifan license: None,
1053*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1054*d4726bddSHONG Yifan license_file: None,
1055*d4726bddSHONG Yifan additive_build_file_content: None,
1056*d4726bddSHONG Yifan disable_pipelining: false,
1057*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1058*d4726bddSHONG Yifan alias_rule: None,
1059*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1060*d4726bddSHONG Yifan },
1061*d4726bddSHONG Yifan );
1062*d4726bddSHONG Yifan
1063*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1064*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1065*d4726bddSHONG Yifan
1066*d4726bddSHONG Yifan let build_file_content = output
1067*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1068*d4726bddSHONG Yifan .unwrap();
1069*d4726bddSHONG Yifan
1070*d4726bddSHONG Yifan assert!(build_file_content.contains("cargo_build_script("));
1071*d4726bddSHONG Yifan assert!(build_file_content.contains("name = \"build_script_build\""));
1072*d4726bddSHONG Yifan assert!(build_file_content.contains("\"crate-name=mock_crate\""));
1073*d4726bddSHONG Yifan
1074*d4726bddSHONG Yifan // Ensure `cargo_build_script` requirements are met
1075*d4726bddSHONG Yifan assert!(build_file_content.contains("name = \"_bs\""));
1076*d4726bddSHONG Yifan }
1077*d4726bddSHONG Yifan
1078*d4726bddSHONG Yifan #[test]
render_proc_macro()1079*d4726bddSHONG Yifan fn render_proc_macro() {
1080*d4726bddSHONG Yifan let mut context = Context::default();
1081*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1082*d4726bddSHONG Yifan context.crates.insert(
1083*d4726bddSHONG Yifan crate_id.clone(),
1084*d4726bddSHONG Yifan CrateContext {
1085*d4726bddSHONG Yifan name: crate_id.name,
1086*d4726bddSHONG Yifan version: crate_id.version,
1087*d4726bddSHONG Yifan package_url: None,
1088*d4726bddSHONG Yifan repository: None,
1089*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::ProcMacro(mock_target_attributes())]),
1090*d4726bddSHONG Yifan library_target_name: None,
1091*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1092*d4726bddSHONG Yifan build_script_attrs: None,
1093*d4726bddSHONG Yifan license: None,
1094*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1095*d4726bddSHONG Yifan license_file: None,
1096*d4726bddSHONG Yifan additive_build_file_content: None,
1097*d4726bddSHONG Yifan disable_pipelining: false,
1098*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1099*d4726bddSHONG Yifan alias_rule: None,
1100*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1101*d4726bddSHONG Yifan },
1102*d4726bddSHONG Yifan );
1103*d4726bddSHONG Yifan
1104*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1105*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1106*d4726bddSHONG Yifan
1107*d4726bddSHONG Yifan let build_file_content = output
1108*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1109*d4726bddSHONG Yifan .unwrap();
1110*d4726bddSHONG Yifan
1111*d4726bddSHONG Yifan assert!(build_file_content.contains("rust_proc_macro("));
1112*d4726bddSHONG Yifan assert!(build_file_content.contains("name = \"mock_crate\""));
1113*d4726bddSHONG Yifan assert!(build_file_content.contains("\"crate-name=mock_crate\""));
1114*d4726bddSHONG Yifan }
1115*d4726bddSHONG Yifan
1116*d4726bddSHONG Yifan #[test]
render_binary()1117*d4726bddSHONG Yifan fn render_binary() {
1118*d4726bddSHONG Yifan let mut context = Context::default();
1119*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1120*d4726bddSHONG Yifan context.crates.insert(
1121*d4726bddSHONG Yifan crate_id.clone(),
1122*d4726bddSHONG Yifan CrateContext {
1123*d4726bddSHONG Yifan name: crate_id.name,
1124*d4726bddSHONG Yifan version: crate_id.version,
1125*d4726bddSHONG Yifan package_url: None,
1126*d4726bddSHONG Yifan repository: None,
1127*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Binary(mock_target_attributes())]),
1128*d4726bddSHONG Yifan library_target_name: None,
1129*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1130*d4726bddSHONG Yifan build_script_attrs: None,
1131*d4726bddSHONG Yifan license: None,
1132*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1133*d4726bddSHONG Yifan license_file: None,
1134*d4726bddSHONG Yifan additive_build_file_content: None,
1135*d4726bddSHONG Yifan disable_pipelining: false,
1136*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1137*d4726bddSHONG Yifan alias_rule: None,
1138*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1139*d4726bddSHONG Yifan },
1140*d4726bddSHONG Yifan );
1141*d4726bddSHONG Yifan
1142*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1143*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1144*d4726bddSHONG Yifan
1145*d4726bddSHONG Yifan let build_file_content = output
1146*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1147*d4726bddSHONG Yifan .unwrap();
1148*d4726bddSHONG Yifan
1149*d4726bddSHONG Yifan assert!(build_file_content.contains("rust_binary("));
1150*d4726bddSHONG Yifan assert!(build_file_content.contains("name = \"mock_crate__bin\""));
1151*d4726bddSHONG Yifan assert!(build_file_content.contains("\"crate-name=mock_crate\""));
1152*d4726bddSHONG Yifan }
1153*d4726bddSHONG Yifan
1154*d4726bddSHONG Yifan #[test]
render_additive_build_contents()1155*d4726bddSHONG Yifan fn render_additive_build_contents() {
1156*d4726bddSHONG Yifan let mut context = Context::default();
1157*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1158*d4726bddSHONG Yifan context.crates.insert(
1159*d4726bddSHONG Yifan crate_id.clone(),
1160*d4726bddSHONG Yifan CrateContext {
1161*d4726bddSHONG Yifan name: crate_id.name,
1162*d4726bddSHONG Yifan version: crate_id.version,
1163*d4726bddSHONG Yifan package_url: None,
1164*d4726bddSHONG Yifan repository: None,
1165*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Binary(mock_target_attributes())]),
1166*d4726bddSHONG Yifan library_target_name: None,
1167*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1168*d4726bddSHONG Yifan build_script_attrs: None,
1169*d4726bddSHONG Yifan license: None,
1170*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1171*d4726bddSHONG Yifan license_file: None,
1172*d4726bddSHONG Yifan additive_build_file_content: Some(
1173*d4726bddSHONG Yifan "# Hello World from additive section!".to_owned(),
1174*d4726bddSHONG Yifan ),
1175*d4726bddSHONG Yifan disable_pipelining: false,
1176*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1177*d4726bddSHONG Yifan alias_rule: None,
1178*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1179*d4726bddSHONG Yifan },
1180*d4726bddSHONG Yifan );
1181*d4726bddSHONG Yifan
1182*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1183*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1184*d4726bddSHONG Yifan
1185*d4726bddSHONG Yifan let build_file_content = output
1186*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1187*d4726bddSHONG Yifan .unwrap();
1188*d4726bddSHONG Yifan
1189*d4726bddSHONG Yifan assert!(build_file_content.contains("# Hello World from additive section!"));
1190*d4726bddSHONG Yifan }
1191*d4726bddSHONG Yifan
1192*d4726bddSHONG Yifan #[test]
render_aliases()1193*d4726bddSHONG Yifan fn render_aliases() {
1194*d4726bddSHONG Yifan let config = Config {
1195*d4726bddSHONG Yifan generate_binaries: true,
1196*d4726bddSHONG Yifan ..Config::default()
1197*d4726bddSHONG Yifan };
1198*d4726bddSHONG Yifan let annotations =
1199*d4726bddSHONG Yifan Annotations::new(test::metadata::alias(), test::lockfile::alias(), config).unwrap();
1200*d4726bddSHONG Yifan let context = Context::new(annotations, false).unwrap();
1201*d4726bddSHONG Yifan
1202*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1203*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1204*d4726bddSHONG Yifan
1205*d4726bddSHONG Yifan let build_file_content = output.get(&PathBuf::from("BUILD.bazel")).unwrap();
1206*d4726bddSHONG Yifan
1207*d4726bddSHONG Yifan assert!(build_file_content.contains(r#"name = "names-0.12.1-dev__names","#));
1208*d4726bddSHONG Yifan assert!(build_file_content.contains(r#"name = "names-0.13.0__names","#));
1209*d4726bddSHONG Yifan }
1210*d4726bddSHONG Yifan
1211*d4726bddSHONG Yifan #[test]
render_crate_repositories()1212*d4726bddSHONG Yifan fn render_crate_repositories() {
1213*d4726bddSHONG Yifan let mut context = Context::default();
1214*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1215*d4726bddSHONG Yifan context.crates.insert(
1216*d4726bddSHONG Yifan crate_id.clone(),
1217*d4726bddSHONG Yifan CrateContext {
1218*d4726bddSHONG Yifan name: crate_id.name,
1219*d4726bddSHONG Yifan version: crate_id.version,
1220*d4726bddSHONG Yifan package_url: None,
1221*d4726bddSHONG Yifan repository: None,
1222*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1223*d4726bddSHONG Yifan library_target_name: None,
1224*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1225*d4726bddSHONG Yifan build_script_attrs: None,
1226*d4726bddSHONG Yifan license: None,
1227*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1228*d4726bddSHONG Yifan license_file: None,
1229*d4726bddSHONG Yifan additive_build_file_content: None,
1230*d4726bddSHONG Yifan disable_pipelining: false,
1231*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1232*d4726bddSHONG Yifan alias_rule: None,
1233*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1234*d4726bddSHONG Yifan },
1235*d4726bddSHONG Yifan );
1236*d4726bddSHONG Yifan
1237*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1238*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1239*d4726bddSHONG Yifan
1240*d4726bddSHONG Yifan let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
1241*d4726bddSHONG Yifan
1242*d4726bddSHONG Yifan assert!(defs_module.contains("def crate_repositories():"));
1243*d4726bddSHONG Yifan }
1244*d4726bddSHONG Yifan
1245*d4726bddSHONG Yifan #[test]
remote_remote_vendor_mode()1246*d4726bddSHONG Yifan fn remote_remote_vendor_mode() {
1247*d4726bddSHONG Yifan let mut context = Context::default();
1248*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1249*d4726bddSHONG Yifan context.crates.insert(
1250*d4726bddSHONG Yifan crate_id.clone(),
1251*d4726bddSHONG Yifan CrateContext {
1252*d4726bddSHONG Yifan name: crate_id.name,
1253*d4726bddSHONG Yifan version: crate_id.version,
1254*d4726bddSHONG Yifan package_url: None,
1255*d4726bddSHONG Yifan repository: None,
1256*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1257*d4726bddSHONG Yifan library_target_name: None,
1258*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1259*d4726bddSHONG Yifan build_script_attrs: None,
1260*d4726bddSHONG Yifan license: None,
1261*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1262*d4726bddSHONG Yifan license_file: None,
1263*d4726bddSHONG Yifan additive_build_file_content: None,
1264*d4726bddSHONG Yifan disable_pipelining: false,
1265*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1266*d4726bddSHONG Yifan alias_rule: None,
1267*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1268*d4726bddSHONG Yifan },
1269*d4726bddSHONG Yifan );
1270*d4726bddSHONG Yifan
1271*d4726bddSHONG Yifan // Enable remote vendor mode
1272*d4726bddSHONG Yifan let renderer = Renderer::new(
1273*d4726bddSHONG Yifan mock_render_config(Some(VendorMode::Remote)),
1274*d4726bddSHONG Yifan mock_supported_platform_triples(),
1275*d4726bddSHONG Yifan );
1276*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1277*d4726bddSHONG Yifan
1278*d4726bddSHONG Yifan let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
1279*d4726bddSHONG Yifan assert!(defs_module.contains("def crate_repositories():"));
1280*d4726bddSHONG Yifan
1281*d4726bddSHONG Yifan let crates_module = output.get(&PathBuf::from("crates.bzl")).unwrap();
1282*d4726bddSHONG Yifan assert!(crates_module.contains("def crate_repositories():"));
1283*d4726bddSHONG Yifan }
1284*d4726bddSHONG Yifan
1285*d4726bddSHONG Yifan #[test]
remote_local_vendor_mode()1286*d4726bddSHONG Yifan fn remote_local_vendor_mode() {
1287*d4726bddSHONG Yifan let mut context = Context::default();
1288*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1289*d4726bddSHONG Yifan context.crates.insert(
1290*d4726bddSHONG Yifan crate_id.clone(),
1291*d4726bddSHONG Yifan CrateContext {
1292*d4726bddSHONG Yifan name: crate_id.name,
1293*d4726bddSHONG Yifan version: crate_id.version,
1294*d4726bddSHONG Yifan package_url: None,
1295*d4726bddSHONG Yifan repository: None,
1296*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1297*d4726bddSHONG Yifan library_target_name: None,
1298*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1299*d4726bddSHONG Yifan build_script_attrs: None,
1300*d4726bddSHONG Yifan license: None,
1301*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1302*d4726bddSHONG Yifan license_file: None,
1303*d4726bddSHONG Yifan additive_build_file_content: None,
1304*d4726bddSHONG Yifan disable_pipelining: false,
1305*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1306*d4726bddSHONG Yifan alias_rule: None,
1307*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1308*d4726bddSHONG Yifan },
1309*d4726bddSHONG Yifan );
1310*d4726bddSHONG Yifan
1311*d4726bddSHONG Yifan // Enable local vendor mode
1312*d4726bddSHONG Yifan let renderer = Renderer::new(
1313*d4726bddSHONG Yifan mock_render_config(Some(VendorMode::Local)),
1314*d4726bddSHONG Yifan mock_supported_platform_triples(),
1315*d4726bddSHONG Yifan );
1316*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1317*d4726bddSHONG Yifan
1318*d4726bddSHONG Yifan // Local vendoring does not produce a `crate_repositories` macro
1319*d4726bddSHONG Yifan let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
1320*d4726bddSHONG Yifan assert!(!defs_module.contains("def crate_repositories():"));
1321*d4726bddSHONG Yifan
1322*d4726bddSHONG Yifan // Local vendoring does not produce a `crates.bzl` file.
1323*d4726bddSHONG Yifan assert!(!output.contains_key(&PathBuf::from("crates.bzl")));
1324*d4726bddSHONG Yifan }
1325*d4726bddSHONG Yifan
1326*d4726bddSHONG Yifan #[test]
duplicate_rustc_flags()1327*d4726bddSHONG Yifan fn duplicate_rustc_flags() {
1328*d4726bddSHONG Yifan let mut context = Context::default();
1329*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1330*d4726bddSHONG Yifan
1331*d4726bddSHONG Yifan let rustc_flags = vec![
1332*d4726bddSHONG Yifan "-l".to_owned(),
1333*d4726bddSHONG Yifan "dylib=ssl".to_owned(),
1334*d4726bddSHONG Yifan "-l".to_owned(),
1335*d4726bddSHONG Yifan "dylib=crypto".to_owned(),
1336*d4726bddSHONG Yifan ];
1337*d4726bddSHONG Yifan
1338*d4726bddSHONG Yifan context.crates.insert(
1339*d4726bddSHONG Yifan crate_id.clone(),
1340*d4726bddSHONG Yifan CrateContext {
1341*d4726bddSHONG Yifan name: crate_id.name,
1342*d4726bddSHONG Yifan version: crate_id.version,
1343*d4726bddSHONG Yifan package_url: None,
1344*d4726bddSHONG Yifan repository: None,
1345*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1346*d4726bddSHONG Yifan library_target_name: None,
1347*d4726bddSHONG Yifan common_attrs: CommonAttributes {
1348*d4726bddSHONG Yifan rustc_flags: Select::from_value(rustc_flags.clone()),
1349*d4726bddSHONG Yifan ..CommonAttributes::default()
1350*d4726bddSHONG Yifan },
1351*d4726bddSHONG Yifan build_script_attrs: None,
1352*d4726bddSHONG Yifan license: None,
1353*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1354*d4726bddSHONG Yifan license_file: None,
1355*d4726bddSHONG Yifan additive_build_file_content: None,
1356*d4726bddSHONG Yifan disable_pipelining: false,
1357*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1358*d4726bddSHONG Yifan alias_rule: None,
1359*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1360*d4726bddSHONG Yifan },
1361*d4726bddSHONG Yifan );
1362*d4726bddSHONG Yifan
1363*d4726bddSHONG Yifan // Enable local vendor mode
1364*d4726bddSHONG Yifan let renderer = Renderer::new(
1365*d4726bddSHONG Yifan mock_render_config(Some(VendorMode::Local)),
1366*d4726bddSHONG Yifan mock_supported_platform_triples(),
1367*d4726bddSHONG Yifan );
1368*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1369*d4726bddSHONG Yifan
1370*d4726bddSHONG Yifan let build_file_content = output
1371*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1372*d4726bddSHONG Yifan .unwrap();
1373*d4726bddSHONG Yifan
1374*d4726bddSHONG Yifan // Strip all spaces from the generated BUILD file and ensure it has the flags
1375*d4726bddSHONG Yifan // represented by `rustc_flags` in the same order.
1376*d4726bddSHONG Yifan assert!(build_file_content.replace(' ', "").contains(
1377*d4726bddSHONG Yifan &rustc_flags
1378*d4726bddSHONG Yifan .iter()
1379*d4726bddSHONG Yifan .map(|s| format!("\"{s}\","))
1380*d4726bddSHONG Yifan .collect::<Vec<String>>()
1381*d4726bddSHONG Yifan .join("\n")
1382*d4726bddSHONG Yifan ));
1383*d4726bddSHONG Yifan }
1384*d4726bddSHONG Yifan
1385*d4726bddSHONG Yifan #[test]
test_render_build_file_deps()1386*d4726bddSHONG Yifan fn test_render_build_file_deps() {
1387*d4726bddSHONG Yifan let config: Config = serde_json::from_value(serde_json::json!({
1388*d4726bddSHONG Yifan "generate_binaries": false,
1389*d4726bddSHONG Yifan "generate_build_scripts": false,
1390*d4726bddSHONG Yifan "rendering": {
1391*d4726bddSHONG Yifan "repository_name": "multi_cfg_dep",
1392*d4726bddSHONG Yifan "regen_command": "bazel test //crate_universe:unit_test",
1393*d4726bddSHONG Yifan },
1394*d4726bddSHONG Yifan "supported_platform_triples": [
1395*d4726bddSHONG Yifan "x86_64-apple-darwin",
1396*d4726bddSHONG Yifan "x86_64-unknown-linux-gnu",
1397*d4726bddSHONG Yifan "aarch64-apple-darwin",
1398*d4726bddSHONG Yifan "aarch64-unknown-linux-gnu",
1399*d4726bddSHONG Yifan ],
1400*d4726bddSHONG Yifan }))
1401*d4726bddSHONG Yifan .unwrap();
1402*d4726bddSHONG Yifan let metadata = test::metadata::multi_cfg_dep();
1403*d4726bddSHONG Yifan let lockfile = test::lockfile::multi_cfg_dep();
1404*d4726bddSHONG Yifan
1405*d4726bddSHONG Yifan let annotations = Annotations::new(metadata, lockfile, config.clone()).unwrap();
1406*d4726bddSHONG Yifan let context = Context::new(annotations, false).unwrap();
1407*d4726bddSHONG Yifan
1408*d4726bddSHONG Yifan let renderer = Renderer::new(config.rendering, config.supported_platform_triples);
1409*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1410*d4726bddSHONG Yifan
1411*d4726bddSHONG Yifan let build_file_content = output
1412*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.cpufeatures-0.2.7.bazel"))
1413*d4726bddSHONG Yifan .unwrap();
1414*d4726bddSHONG Yifan
1415*d4726bddSHONG Yifan // This is unfortunately somewhat brittle. Alas. Ultimately we wish to demonstrate that the
1416*d4726bddSHONG Yifan // original cfg(...) strings are preserved in the `deps` list for ease of debugging.
1417*d4726bddSHONG Yifan let expected = indoc! {r#"
1418*d4726bddSHONG Yifan deps = select({
1419*d4726bddSHONG Yifan "@rules_rust//rust/platform:aarch64-apple-darwin": [
1420*d4726bddSHONG Yifan "@multi_cfg_dep__libc-0.2.117//:libc", # cfg(all(target_arch = "aarch64", target_vendor = "apple"))
1421*d4726bddSHONG Yifan ],
1422*d4726bddSHONG Yifan "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [
1423*d4726bddSHONG Yifan "@multi_cfg_dep__libc-0.2.117//:libc", # cfg(all(target_arch = "aarch64", target_os = "linux"))
1424*d4726bddSHONG Yifan ],
1425*d4726bddSHONG Yifan "//conditions:default": [],
1426*d4726bddSHONG Yifan }),
1427*d4726bddSHONG Yifan "#};
1428*d4726bddSHONG Yifan
1429*d4726bddSHONG Yifan assert!(
1430*d4726bddSHONG Yifan build_file_content.contains(&expected.replace('\n', "\n ")),
1431*d4726bddSHONG Yifan "{}",
1432*d4726bddSHONG Yifan build_file_content,
1433*d4726bddSHONG Yifan );
1434*d4726bddSHONG Yifan }
1435*d4726bddSHONG Yifan
1436*d4726bddSHONG Yifan #[test]
crate_features_by_target()1437*d4726bddSHONG Yifan fn crate_features_by_target() {
1438*d4726bddSHONG Yifan let mut context = Context {
1439*d4726bddSHONG Yifan conditions: mock_supported_platform_triples()
1440*d4726bddSHONG Yifan .iter()
1441*d4726bddSHONG Yifan .map(|platform| (platform.to_bazel(), BTreeSet::from([platform.clone()])))
1442*d4726bddSHONG Yifan .collect(),
1443*d4726bddSHONG Yifan ..Context::default()
1444*d4726bddSHONG Yifan };
1445*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1446*d4726bddSHONG Yifan let mut crate_features: Select<BTreeSet<String>> = Select::default();
1447*d4726bddSHONG Yifan crate_features.insert("foo".to_owned(), Some("aarch64-apple-darwin".to_owned()));
1448*d4726bddSHONG Yifan crate_features.insert("bar".to_owned(), None);
1449*d4726bddSHONG Yifan context.crates.insert(
1450*d4726bddSHONG Yifan crate_id.clone(),
1451*d4726bddSHONG Yifan CrateContext {
1452*d4726bddSHONG Yifan name: crate_id.name,
1453*d4726bddSHONG Yifan version: crate_id.version,
1454*d4726bddSHONG Yifan package_url: None,
1455*d4726bddSHONG Yifan repository: None,
1456*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1457*d4726bddSHONG Yifan library_target_name: None,
1458*d4726bddSHONG Yifan common_attrs: CommonAttributes {
1459*d4726bddSHONG Yifan crate_features,
1460*d4726bddSHONG Yifan ..CommonAttributes::default()
1461*d4726bddSHONG Yifan },
1462*d4726bddSHONG Yifan build_script_attrs: None,
1463*d4726bddSHONG Yifan license: None,
1464*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1465*d4726bddSHONG Yifan license_file: None,
1466*d4726bddSHONG Yifan additive_build_file_content: None,
1467*d4726bddSHONG Yifan disable_pipelining: false,
1468*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1469*d4726bddSHONG Yifan alias_rule: None,
1470*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1471*d4726bddSHONG Yifan },
1472*d4726bddSHONG Yifan );
1473*d4726bddSHONG Yifan
1474*d4726bddSHONG Yifan let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
1475*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1476*d4726bddSHONG Yifan
1477*d4726bddSHONG Yifan let build_file_content = output
1478*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1479*d4726bddSHONG Yifan .unwrap();
1480*d4726bddSHONG Yifan let expected = indoc! {r#"
1481*d4726bddSHONG Yifan crate_features = [
1482*d4726bddSHONG Yifan "bar",
1483*d4726bddSHONG Yifan ] + select({
1484*d4726bddSHONG Yifan "@rules_rust//rust/platform:aarch64-apple-darwin": [
1485*d4726bddSHONG Yifan "foo", # aarch64-apple-darwin
1486*d4726bddSHONG Yifan ],
1487*d4726bddSHONG Yifan "//conditions:default": [],
1488*d4726bddSHONG Yifan }),
1489*d4726bddSHONG Yifan "#};
1490*d4726bddSHONG Yifan assert!(build_file_content
1491*d4726bddSHONG Yifan .replace(' ', "")
1492*d4726bddSHONG Yifan .contains(&expected.replace(' ', "")));
1493*d4726bddSHONG Yifan }
1494*d4726bddSHONG Yifan
1495*d4726bddSHONG Yifan #[test]
crate_package_metadata_without_license_ids()1496*d4726bddSHONG Yifan fn crate_package_metadata_without_license_ids() {
1497*d4726bddSHONG Yifan let mut context = Context::default();
1498*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1499*d4726bddSHONG Yifan context.crates.insert(
1500*d4726bddSHONG Yifan crate_id.clone(),
1501*d4726bddSHONG Yifan CrateContext {
1502*d4726bddSHONG Yifan name: crate_id.name,
1503*d4726bddSHONG Yifan version: crate_id.version,
1504*d4726bddSHONG Yifan package_url: Some("http://www.mock_crate.com/".to_owned()),
1505*d4726bddSHONG Yifan repository: None,
1506*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1507*d4726bddSHONG Yifan library_target_name: None,
1508*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1509*d4726bddSHONG Yifan build_script_attrs: None,
1510*d4726bddSHONG Yifan license: None,
1511*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1512*d4726bddSHONG Yifan license_file: None,
1513*d4726bddSHONG Yifan additive_build_file_content: None,
1514*d4726bddSHONG Yifan disable_pipelining: false,
1515*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1516*d4726bddSHONG Yifan alias_rule: None,
1517*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1518*d4726bddSHONG Yifan },
1519*d4726bddSHONG Yifan );
1520*d4726bddSHONG Yifan
1521*d4726bddSHONG Yifan let mut render_config = mock_render_config(None);
1522*d4726bddSHONG Yifan render_config.generate_rules_license_metadata = true;
1523*d4726bddSHONG Yifan let renderer = Renderer::new(render_config, mock_supported_platform_triples());
1524*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1525*d4726bddSHONG Yifan
1526*d4726bddSHONG Yifan let build_file_content = output
1527*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1528*d4726bddSHONG Yifan .unwrap();
1529*d4726bddSHONG Yifan
1530*d4726bddSHONG Yifan let expected = indoc! {r#"
1531*d4726bddSHONG Yifan package(
1532*d4726bddSHONG Yifan default_package_metadata = [":package_info"],
1533*d4726bddSHONG Yifan default_visibility = ["//visibility:public"],
1534*d4726bddSHONG Yifan )
1535*d4726bddSHONG Yifan
1536*d4726bddSHONG Yifan package_info(
1537*d4726bddSHONG Yifan name = "package_info",
1538*d4726bddSHONG Yifan package_name = "mock_crate",
1539*d4726bddSHONG Yifan package_version = "0.1.0",
1540*d4726bddSHONG Yifan package_url = "http://www.mock_crate.com/",
1541*d4726bddSHONG Yifan )
1542*d4726bddSHONG Yifan "#};
1543*d4726bddSHONG Yifan assert!(build_file_content
1544*d4726bddSHONG Yifan .replace(' ', "")
1545*d4726bddSHONG Yifan .contains(&expected.replace(' ', "")));
1546*d4726bddSHONG Yifan }
1547*d4726bddSHONG Yifan
1548*d4726bddSHONG Yifan #[test]
crate_package_metadata_with_license_ids()1549*d4726bddSHONG Yifan fn crate_package_metadata_with_license_ids() {
1550*d4726bddSHONG Yifan let mut context = Context::default();
1551*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1552*d4726bddSHONG Yifan context.crates.insert(
1553*d4726bddSHONG Yifan crate_id.clone(),
1554*d4726bddSHONG Yifan CrateContext {
1555*d4726bddSHONG Yifan name: crate_id.name,
1556*d4726bddSHONG Yifan version: crate_id.version,
1557*d4726bddSHONG Yifan package_url: Some("http://www.mock_crate.com/".to_owned()),
1558*d4726bddSHONG Yifan license_ids: BTreeSet::from(["Apache-2.0".to_owned(), "MIT".to_owned()]),
1559*d4726bddSHONG Yifan license_file: None,
1560*d4726bddSHONG Yifan additive_build_file_content: None,
1561*d4726bddSHONG Yifan disable_pipelining: false,
1562*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1563*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1564*d4726bddSHONG Yifan library_target_name: None,
1565*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1566*d4726bddSHONG Yifan build_script_attrs: None,
1567*d4726bddSHONG Yifan repository: None,
1568*d4726bddSHONG Yifan license: None,
1569*d4726bddSHONG Yifan alias_rule: None,
1570*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1571*d4726bddSHONG Yifan },
1572*d4726bddSHONG Yifan );
1573*d4726bddSHONG Yifan
1574*d4726bddSHONG Yifan let mut render_config = mock_render_config(None);
1575*d4726bddSHONG Yifan render_config.generate_rules_license_metadata = true;
1576*d4726bddSHONG Yifan let renderer = Renderer::new(render_config, mock_supported_platform_triples());
1577*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1578*d4726bddSHONG Yifan
1579*d4726bddSHONG Yifan let build_file_content = output
1580*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1581*d4726bddSHONG Yifan .unwrap();
1582*d4726bddSHONG Yifan
1583*d4726bddSHONG Yifan let expected = indoc! {r#"
1584*d4726bddSHONG Yifan package(
1585*d4726bddSHONG Yifan default_package_metadata = [
1586*d4726bddSHONG Yifan ":license",
1587*d4726bddSHONG Yifan ":package_info",
1588*d4726bddSHONG Yifan ],
1589*d4726bddSHONG Yifan default_visibility = ["//visibility:public"],
1590*d4726bddSHONG Yifan )
1591*d4726bddSHONG Yifan
1592*d4726bddSHONG Yifan package_info(
1593*d4726bddSHONG Yifan name = "package_info",
1594*d4726bddSHONG Yifan package_name = "mock_crate",
1595*d4726bddSHONG Yifan package_version = "0.1.0",
1596*d4726bddSHONG Yifan package_url = "http://www.mock_crate.com/",
1597*d4726bddSHONG Yifan )
1598*d4726bddSHONG Yifan
1599*d4726bddSHONG Yifan license(
1600*d4726bddSHONG Yifan name = "license",
1601*d4726bddSHONG Yifan license_kinds = [
1602*d4726bddSHONG Yifan "@rules_license//licenses/spdx:Apache-2.0",
1603*d4726bddSHONG Yifan "@rules_license//licenses/spdx:MIT",
1604*d4726bddSHONG Yifan ],
1605*d4726bddSHONG Yifan )
1606*d4726bddSHONG Yifan "#};
1607*d4726bddSHONG Yifan assert!(build_file_content
1608*d4726bddSHONG Yifan .replace(' ', "")
1609*d4726bddSHONG Yifan .contains(&expected.replace(' ', "")));
1610*d4726bddSHONG Yifan }
1611*d4726bddSHONG Yifan
1612*d4726bddSHONG Yifan #[test]
crate_package_metadata_with_license_ids_and_file()1613*d4726bddSHONG Yifan fn crate_package_metadata_with_license_ids_and_file() {
1614*d4726bddSHONG Yifan let mut context = Context::default();
1615*d4726bddSHONG Yifan let crate_id = CrateId::new("mock_crate".to_owned(), VERSION_ZERO_ONE_ZERO);
1616*d4726bddSHONG Yifan context.crates.insert(
1617*d4726bddSHONG Yifan crate_id.clone(),
1618*d4726bddSHONG Yifan CrateContext {
1619*d4726bddSHONG Yifan name: crate_id.name,
1620*d4726bddSHONG Yifan version: crate_id.version,
1621*d4726bddSHONG Yifan package_url: Some("http://www.mock_crate.com/".to_owned()),
1622*d4726bddSHONG Yifan license_ids: BTreeSet::from(["Apache-2.0".to_owned(), "MIT".to_owned()]),
1623*d4726bddSHONG Yifan license_file: Some("LICENSE.txt".to_owned()),
1624*d4726bddSHONG Yifan additive_build_file_content: None,
1625*d4726bddSHONG Yifan disable_pipelining: false,
1626*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1627*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1628*d4726bddSHONG Yifan library_target_name: None,
1629*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1630*d4726bddSHONG Yifan build_script_attrs: None,
1631*d4726bddSHONG Yifan repository: None,
1632*d4726bddSHONG Yifan license: None,
1633*d4726bddSHONG Yifan alias_rule: None,
1634*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1635*d4726bddSHONG Yifan },
1636*d4726bddSHONG Yifan );
1637*d4726bddSHONG Yifan
1638*d4726bddSHONG Yifan let mut render_config = mock_render_config(None);
1639*d4726bddSHONG Yifan render_config.generate_rules_license_metadata = true;
1640*d4726bddSHONG Yifan let renderer = Renderer::new(render_config, mock_supported_platform_triples());
1641*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1642*d4726bddSHONG Yifan
1643*d4726bddSHONG Yifan let build_file_content = output
1644*d4726bddSHONG Yifan .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
1645*d4726bddSHONG Yifan .unwrap();
1646*d4726bddSHONG Yifan
1647*d4726bddSHONG Yifan let expected = indoc! {r#"
1648*d4726bddSHONG Yifan package(
1649*d4726bddSHONG Yifan default_package_metadata = [
1650*d4726bddSHONG Yifan ":license",
1651*d4726bddSHONG Yifan ":package_info",
1652*d4726bddSHONG Yifan ],
1653*d4726bddSHONG Yifan default_visibility = ["//visibility:public"],
1654*d4726bddSHONG Yifan )
1655*d4726bddSHONG Yifan
1656*d4726bddSHONG Yifan package_info(
1657*d4726bddSHONG Yifan name = "package_info",
1658*d4726bddSHONG Yifan package_name = "mock_crate",
1659*d4726bddSHONG Yifan package_version = "0.1.0",
1660*d4726bddSHONG Yifan package_url = "http://www.mock_crate.com/",
1661*d4726bddSHONG Yifan )
1662*d4726bddSHONG Yifan
1663*d4726bddSHONG Yifan license(
1664*d4726bddSHONG Yifan name = "license",
1665*d4726bddSHONG Yifan license_kinds = [
1666*d4726bddSHONG Yifan "@rules_license//licenses/spdx:Apache-2.0",
1667*d4726bddSHONG Yifan "@rules_license//licenses/spdx:MIT",
1668*d4726bddSHONG Yifan ],
1669*d4726bddSHONG Yifan license_text = "LICENSE.txt",
1670*d4726bddSHONG Yifan )
1671*d4726bddSHONG Yifan "#};
1672*d4726bddSHONG Yifan assert!(build_file_content
1673*d4726bddSHONG Yifan .replace(' ', "")
1674*d4726bddSHONG Yifan .contains(&expected.replace(' ', "")));
1675*d4726bddSHONG Yifan }
1676*d4726bddSHONG Yifan
1677*d4726bddSHONG Yifan #[test]
write_outputs_semver_metadata()1678*d4726bddSHONG Yifan fn write_outputs_semver_metadata() {
1679*d4726bddSHONG Yifan let mut context = Context::default();
1680*d4726bddSHONG Yifan // generate crate for libbpf-sys-1.4.0-v1.4.0
1681*d4726bddSHONG Yifan let mut version = semver::Version::new(1, 4, 0);
1682*d4726bddSHONG Yifan version.build = semver::BuildMetadata::new("v1.4.0").unwrap();
1683*d4726bddSHONG Yifan // ensure metadata has a +
1684*d4726bddSHONG Yifan assert!(version.to_string().contains('+'));
1685*d4726bddSHONG Yifan let crate_id = CrateId::new("libbpf-sys".to_owned(), version);
1686*d4726bddSHONG Yifan
1687*d4726bddSHONG Yifan context.crates.insert(
1688*d4726bddSHONG Yifan crate_id.clone(),
1689*d4726bddSHONG Yifan CrateContext {
1690*d4726bddSHONG Yifan name: crate_id.name,
1691*d4726bddSHONG Yifan version: crate_id.version,
1692*d4726bddSHONG Yifan package_url: None,
1693*d4726bddSHONG Yifan repository: None,
1694*d4726bddSHONG Yifan targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
1695*d4726bddSHONG Yifan library_target_name: None,
1696*d4726bddSHONG Yifan common_attrs: CommonAttributes::default(),
1697*d4726bddSHONG Yifan build_script_attrs: None,
1698*d4726bddSHONG Yifan license: None,
1699*d4726bddSHONG Yifan license_ids: BTreeSet::default(),
1700*d4726bddSHONG Yifan license_file: None,
1701*d4726bddSHONG Yifan additive_build_file_content: None,
1702*d4726bddSHONG Yifan disable_pipelining: false,
1703*d4726bddSHONG Yifan extra_aliased_targets: BTreeMap::default(),
1704*d4726bddSHONG Yifan alias_rule: None,
1705*d4726bddSHONG Yifan override_targets: BTreeMap::default(),
1706*d4726bddSHONG Yifan },
1707*d4726bddSHONG Yifan );
1708*d4726bddSHONG Yifan
1709*d4726bddSHONG Yifan let mut config = mock_render_config(Some(VendorMode::Local));
1710*d4726bddSHONG Yifan // change templates so it matches local vendor
1711*d4726bddSHONG Yifan config.build_file_template = "//{name}-{version}:BUILD.bazel".into();
1712*d4726bddSHONG Yifan
1713*d4726bddSHONG Yifan // Enable local vendor mode
1714*d4726bddSHONG Yifan let renderer = Renderer::new(config, mock_supported_platform_triples());
1715*d4726bddSHONG Yifan let output = renderer.render(&context).unwrap();
1716*d4726bddSHONG Yifan eprintln!("output before {:?}", output.keys());
1717*d4726bddSHONG Yifan // Local vendoring does not produce a `crate_repositories` macro
1718*d4726bddSHONG Yifan let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
1719*d4726bddSHONG Yifan assert!(!defs_module.contains("def crate_repositories():"));
1720*d4726bddSHONG Yifan
1721*d4726bddSHONG Yifan // Local vendoring does not produce a `crates.bzl` file.
1722*d4726bddSHONG Yifan assert!(!output.contains_key(&PathBuf::from("crates.bzl")));
1723*d4726bddSHONG Yifan
1724*d4726bddSHONG Yifan // create tempdir to write to
1725*d4726bddSHONG Yifan let outdir = tempfile::tempdir().unwrap();
1726*d4726bddSHONG Yifan
1727*d4726bddSHONG Yifan // create dir to mimic cargo vendor
1728*d4726bddSHONG Yifan let _ = std::fs::create_dir_all(outdir.path().join("libbpf-sys-1.4.0+v1.4.0"));
1729*d4726bddSHONG Yifan
1730*d4726bddSHONG Yifan let normalized_outputs = normalize_cargo_file_paths(output, outdir.path());
1731*d4726bddSHONG Yifan eprintln!(
1732*d4726bddSHONG Yifan "Normalized outputs are {:?}",
1733*d4726bddSHONG Yifan normalized_outputs.clone().into_keys()
1734*d4726bddSHONG Yifan );
1735*d4726bddSHONG Yifan
1736*d4726bddSHONG Yifan write_outputs(normalized_outputs, false).unwrap();
1737*d4726bddSHONG Yifan let expected = outdir.path().join("libbpf-sys-1.4.0-v1.4.0");
1738*d4726bddSHONG Yifan let mut found = false;
1739*d4726bddSHONG Yifan // ensure no files paths have a + sign
1740*d4726bddSHONG Yifan for entry in fs::read_dir(outdir.path()).unwrap() {
1741*d4726bddSHONG Yifan let path_str = entry.as_ref().unwrap().path().to_str().unwrap().to_string();
1742*d4726bddSHONG Yifan if entry.unwrap().path() == expected {
1743*d4726bddSHONG Yifan found = true;
1744*d4726bddSHONG Yifan }
1745*d4726bddSHONG Yifan assert!(!path_str.contains('+'));
1746*d4726bddSHONG Yifan }
1747*d4726bddSHONG Yifan assert!(found);
1748*d4726bddSHONG Yifan }
1749*d4726bddSHONG Yifan }
1750