1*d4726bddSHONG Yifan //! A module for configuration information
2*d4726bddSHONG Yifan
3*d4726bddSHONG Yifan use std::cmp::Ordering;
4*d4726bddSHONG Yifan use std::collections::{BTreeMap, BTreeSet};
5*d4726bddSHONG Yifan use std::fmt::Formatter;
6*d4726bddSHONG Yifan use std::iter::Sum;
7*d4726bddSHONG Yifan use std::ops::Add;
8*d4726bddSHONG Yifan use std::path::Path;
9*d4726bddSHONG Yifan use std::str::FromStr;
10*d4726bddSHONG Yifan use std::{fmt, fs};
11*d4726bddSHONG Yifan
12*d4726bddSHONG Yifan use anyhow::{Context, Result};
13*d4726bddSHONG Yifan use cargo_lock::package::GitReference;
14*d4726bddSHONG Yifan use cargo_metadata::Package;
15*d4726bddSHONG Yifan use semver::VersionReq;
16*d4726bddSHONG Yifan use serde::de::value::SeqAccessDeserializer;
17*d4726bddSHONG Yifan use serde::de::{Deserializer, SeqAccess, Unexpected, Visitor};
18*d4726bddSHONG Yifan use serde::{Deserialize, Serialize, Serializer};
19*d4726bddSHONG Yifan
20*d4726bddSHONG Yifan use crate::select::{Select, Selectable};
21*d4726bddSHONG Yifan use crate::utils::starlark::Label;
22*d4726bddSHONG Yifan use crate::utils::target_triple::TargetTriple;
23*d4726bddSHONG Yifan
24*d4726bddSHONG Yifan /// Representations of different kinds of crate vendoring into workspaces.
25*d4726bddSHONG Yifan #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
26*d4726bddSHONG Yifan #[serde(rename_all = "lowercase")]
27*d4726bddSHONG Yifan pub(crate) enum VendorMode {
28*d4726bddSHONG Yifan /// Crates having full source being vendored into a workspace
29*d4726bddSHONG Yifan Local,
30*d4726bddSHONG Yifan
31*d4726bddSHONG Yifan /// Crates having only BUILD files with repository rules vendored into a workspace
32*d4726bddSHONG Yifan Remote,
33*d4726bddSHONG Yifan }
34*d4726bddSHONG Yifan
35*d4726bddSHONG Yifan impl std::fmt::Display for VendorMode {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result36*d4726bddSHONG Yifan fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37*d4726bddSHONG Yifan fmt::Display::fmt(
38*d4726bddSHONG Yifan match self {
39*d4726bddSHONG Yifan VendorMode::Local => "local",
40*d4726bddSHONG Yifan VendorMode::Remote => "remote",
41*d4726bddSHONG Yifan },
42*d4726bddSHONG Yifan f,
43*d4726bddSHONG Yifan )
44*d4726bddSHONG Yifan }
45*d4726bddSHONG Yifan }
46*d4726bddSHONG Yifan
47*d4726bddSHONG Yifan #[derive(Debug, Serialize, Deserialize, Clone)]
48*d4726bddSHONG Yifan #[serde(deny_unknown_fields)]
49*d4726bddSHONG Yifan pub(crate) struct RenderConfig {
50*d4726bddSHONG Yifan /// The name of the repository being rendered
51*d4726bddSHONG Yifan pub(crate) repository_name: String,
52*d4726bddSHONG Yifan
53*d4726bddSHONG Yifan /// The pattern to use for BUILD file names.
54*d4726bddSHONG Yifan /// Eg. `//:BUILD.{name}-{version}.bazel`
55*d4726bddSHONG Yifan #[serde(default = "default_build_file_template")]
56*d4726bddSHONG Yifan pub(crate) build_file_template: String,
57*d4726bddSHONG Yifan
58*d4726bddSHONG Yifan /// The pattern to use for a crate target.
59*d4726bddSHONG Yifan /// Eg. `@{repository}__{name}-{version}//:{target}`
60*d4726bddSHONG Yifan #[serde(default = "default_crate_label_template")]
61*d4726bddSHONG Yifan pub(crate) crate_label_template: String,
62*d4726bddSHONG Yifan
63*d4726bddSHONG Yifan /// The pattern to use for the `defs.bzl` and `BUILD.bazel`
64*d4726bddSHONG Yifan /// file names used for the crates module.
65*d4726bddSHONG Yifan /// Eg. `//:{file}`
66*d4726bddSHONG Yifan #[serde(default = "default_crates_module_template")]
67*d4726bddSHONG Yifan pub(crate) crates_module_template: String,
68*d4726bddSHONG Yifan
69*d4726bddSHONG Yifan /// The pattern used for a crate's repository name.
70*d4726bddSHONG Yifan /// Eg. `{repository}__{name}-{version}`
71*d4726bddSHONG Yifan #[serde(default = "default_crate_repository_template")]
72*d4726bddSHONG Yifan pub(crate) crate_repository_template: String,
73*d4726bddSHONG Yifan
74*d4726bddSHONG Yifan /// Default alias rule to use for packages. Can be overridden by annotations.
75*d4726bddSHONG Yifan #[serde(default)]
76*d4726bddSHONG Yifan pub(crate) default_alias_rule: AliasRule,
77*d4726bddSHONG Yifan
78*d4726bddSHONG Yifan /// The default of the `package_name` parameter to use for the module macros like `all_crate_deps`.
79*d4726bddSHONG Yifan /// In general, this should be be unset to allow the macros to do auto-detection in the analysis phase.
80*d4726bddSHONG Yifan pub(crate) default_package_name: Option<String>,
81*d4726bddSHONG Yifan
82*d4726bddSHONG Yifan /// Whether to generate `target_compatible_with` annotations on the generated BUILD files. This
83*d4726bddSHONG Yifan /// catches a `target_triple`being targeted that isn't declared in `supported_platform_triples`.
84*d4726bddSHONG Yifan #[serde(default = "default_generate_target_compatible_with")]
85*d4726bddSHONG Yifan pub(crate) generate_target_compatible_with: bool,
86*d4726bddSHONG Yifan
87*d4726bddSHONG Yifan /// The pattern to use for platform constraints.
88*d4726bddSHONG Yifan /// Eg. `@rules_rust//rust/platform:{triple}`.
89*d4726bddSHONG Yifan #[serde(default = "default_platforms_template")]
90*d4726bddSHONG Yifan pub(crate) platforms_template: String,
91*d4726bddSHONG Yifan
92*d4726bddSHONG Yifan /// The command to use for regenerating generated files.
93*d4726bddSHONG Yifan pub(crate) regen_command: String,
94*d4726bddSHONG Yifan
95*d4726bddSHONG Yifan /// An optional configuration for rendering content to be rendered into repositories.
96*d4726bddSHONG Yifan pub(crate) vendor_mode: Option<VendorMode>,
97*d4726bddSHONG Yifan
98*d4726bddSHONG Yifan /// Whether to generate package metadata
99*d4726bddSHONG Yifan #[serde(default = "default_generate_rules_license_metadata")]
100*d4726bddSHONG Yifan pub(crate) generate_rules_license_metadata: bool,
101*d4726bddSHONG Yifan }
102*d4726bddSHONG Yifan
103*d4726bddSHONG Yifan // Default is manually implemented so that the default values match the default
104*d4726bddSHONG Yifan // values when deserializing, which involves calling the vairous `default_x()`
105*d4726bddSHONG Yifan // functions specified in `#[serde(default = "default_x")]`.
106*d4726bddSHONG Yifan impl Default for RenderConfig {
default() -> Self107*d4726bddSHONG Yifan fn default() -> Self {
108*d4726bddSHONG Yifan RenderConfig {
109*d4726bddSHONG Yifan repository_name: String::default(),
110*d4726bddSHONG Yifan build_file_template: default_build_file_template(),
111*d4726bddSHONG Yifan crate_label_template: default_crate_label_template(),
112*d4726bddSHONG Yifan crates_module_template: default_crates_module_template(),
113*d4726bddSHONG Yifan crate_repository_template: default_crate_repository_template(),
114*d4726bddSHONG Yifan default_alias_rule: AliasRule::default(),
115*d4726bddSHONG Yifan default_package_name: Option::default(),
116*d4726bddSHONG Yifan generate_target_compatible_with: default_generate_target_compatible_with(),
117*d4726bddSHONG Yifan platforms_template: default_platforms_template(),
118*d4726bddSHONG Yifan regen_command: String::default(),
119*d4726bddSHONG Yifan vendor_mode: Option::default(),
120*d4726bddSHONG Yifan generate_rules_license_metadata: default_generate_rules_license_metadata(),
121*d4726bddSHONG Yifan }
122*d4726bddSHONG Yifan }
123*d4726bddSHONG Yifan }
124*d4726bddSHONG Yifan
125*d4726bddSHONG Yifan impl RenderConfig {
are_sources_present(&self) -> bool126*d4726bddSHONG Yifan pub(crate) fn are_sources_present(&self) -> bool {
127*d4726bddSHONG Yifan self.vendor_mode == Some(VendorMode::Local)
128*d4726bddSHONG Yifan }
129*d4726bddSHONG Yifan }
130*d4726bddSHONG Yifan
default_build_file_template() -> String131*d4726bddSHONG Yifan fn default_build_file_template() -> String {
132*d4726bddSHONG Yifan "//:BUILD.{name}-{version}.bazel".to_owned()
133*d4726bddSHONG Yifan }
134*d4726bddSHONG Yifan
default_crates_module_template() -> String135*d4726bddSHONG Yifan fn default_crates_module_template() -> String {
136*d4726bddSHONG Yifan "//:{file}".to_owned()
137*d4726bddSHONG Yifan }
138*d4726bddSHONG Yifan
default_crate_label_template() -> String139*d4726bddSHONG Yifan fn default_crate_label_template() -> String {
140*d4726bddSHONG Yifan "@{repository}__{name}-{version}//:{target}".to_owned()
141*d4726bddSHONG Yifan }
142*d4726bddSHONG Yifan
default_crate_repository_template() -> String143*d4726bddSHONG Yifan fn default_crate_repository_template() -> String {
144*d4726bddSHONG Yifan "{repository}__{name}-{version}".to_owned()
145*d4726bddSHONG Yifan }
146*d4726bddSHONG Yifan
default_platforms_template() -> String147*d4726bddSHONG Yifan fn default_platforms_template() -> String {
148*d4726bddSHONG Yifan "@rules_rust//rust/platform:{triple}".to_owned()
149*d4726bddSHONG Yifan }
150*d4726bddSHONG Yifan
default_generate_target_compatible_with() -> bool151*d4726bddSHONG Yifan fn default_generate_target_compatible_with() -> bool {
152*d4726bddSHONG Yifan true
153*d4726bddSHONG Yifan }
154*d4726bddSHONG Yifan
default_generate_rules_license_metadata() -> bool155*d4726bddSHONG Yifan fn default_generate_rules_license_metadata() -> bool {
156*d4726bddSHONG Yifan false
157*d4726bddSHONG Yifan }
158*d4726bddSHONG Yifan
159*d4726bddSHONG Yifan /// A representation of some Git identifier used to represent the "revision" or "pin" of a checkout.
160*d4726bddSHONG Yifan #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
161*d4726bddSHONG Yifan pub(crate) enum Commitish {
162*d4726bddSHONG Yifan /// From a tag.
163*d4726bddSHONG Yifan Tag(String),
164*d4726bddSHONG Yifan
165*d4726bddSHONG Yifan /// From the HEAD of a branch.
166*d4726bddSHONG Yifan Branch(String),
167*d4726bddSHONG Yifan
168*d4726bddSHONG Yifan /// From a specific revision.
169*d4726bddSHONG Yifan Rev(String),
170*d4726bddSHONG Yifan }
171*d4726bddSHONG Yifan
172*d4726bddSHONG Yifan impl From<GitReference> for Commitish {
from(git_ref: GitReference) -> Self173*d4726bddSHONG Yifan fn from(git_ref: GitReference) -> Self {
174*d4726bddSHONG Yifan match git_ref {
175*d4726bddSHONG Yifan GitReference::Tag(v) => Self::Tag(v),
176*d4726bddSHONG Yifan GitReference::Branch(v) => Self::Branch(v),
177*d4726bddSHONG Yifan GitReference::Rev(v) => Self::Rev(v),
178*d4726bddSHONG Yifan }
179*d4726bddSHONG Yifan }
180*d4726bddSHONG Yifan }
181*d4726bddSHONG Yifan
182*d4726bddSHONG Yifan #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
183*d4726bddSHONG Yifan pub(crate) enum AliasRule {
184*d4726bddSHONG Yifan #[default]
185*d4726bddSHONG Yifan #[serde(rename = "alias")]
186*d4726bddSHONG Yifan Alias,
187*d4726bddSHONG Yifan #[serde(rename = "dbg")]
188*d4726bddSHONG Yifan Dbg,
189*d4726bddSHONG Yifan #[serde(rename = "fastbuild")]
190*d4726bddSHONG Yifan Fastbuild,
191*d4726bddSHONG Yifan #[serde(rename = "opt")]
192*d4726bddSHONG Yifan Opt,
193*d4726bddSHONG Yifan #[serde(untagged)]
194*d4726bddSHONG Yifan Custom { bzl: String, rule: String },
195*d4726bddSHONG Yifan }
196*d4726bddSHONG Yifan
197*d4726bddSHONG Yifan impl AliasRule {
bzl(&self) -> Option<String>198*d4726bddSHONG Yifan pub(crate) fn bzl(&self) -> Option<String> {
199*d4726bddSHONG Yifan match self {
200*d4726bddSHONG Yifan AliasRule::Alias => None,
201*d4726bddSHONG Yifan AliasRule::Dbg | AliasRule::Fastbuild | AliasRule::Opt => {
202*d4726bddSHONG Yifan Some("//:alias_rules.bzl".to_owned())
203*d4726bddSHONG Yifan }
204*d4726bddSHONG Yifan AliasRule::Custom { bzl, .. } => Some(bzl.clone()),
205*d4726bddSHONG Yifan }
206*d4726bddSHONG Yifan }
207*d4726bddSHONG Yifan
rule(&self) -> String208*d4726bddSHONG Yifan pub(crate) fn rule(&self) -> String {
209*d4726bddSHONG Yifan match self {
210*d4726bddSHONG Yifan AliasRule::Alias => "alias".to_owned(),
211*d4726bddSHONG Yifan AliasRule::Dbg => "transition_alias_dbg".to_owned(),
212*d4726bddSHONG Yifan AliasRule::Fastbuild => "transition_alias_fastbuild".to_owned(),
213*d4726bddSHONG Yifan AliasRule::Opt => "transition_alias_opt".to_owned(),
214*d4726bddSHONG Yifan AliasRule::Custom { rule, .. } => rule.clone(),
215*d4726bddSHONG Yifan }
216*d4726bddSHONG Yifan }
217*d4726bddSHONG Yifan }
218*d4726bddSHONG Yifan
219*d4726bddSHONG Yifan #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
220*d4726bddSHONG Yifan pub(crate) struct CrateAnnotations {
221*d4726bddSHONG Yifan /// Which subset of the crate's bins should get produced as `rust_binary` targets.
222*d4726bddSHONG Yifan pub(crate) gen_binaries: Option<GenBinaries>,
223*d4726bddSHONG Yifan
224*d4726bddSHONG Yifan /// Determins whether or not Cargo build scripts should be generated for the current package
225*d4726bddSHONG Yifan pub(crate) gen_build_script: Option<bool>,
226*d4726bddSHONG Yifan
227*d4726bddSHONG Yifan /// Additional data to pass to
228*d4726bddSHONG Yifan /// [deps](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-deps) attribute.
229*d4726bddSHONG Yifan pub(crate) deps: Option<Select<BTreeSet<Label>>>,
230*d4726bddSHONG Yifan
231*d4726bddSHONG Yifan /// Additional data to pass to
232*d4726bddSHONG Yifan /// [proc_macro_deps](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-proc_macro_deps) attribute.
233*d4726bddSHONG Yifan pub(crate) proc_macro_deps: Option<Select<BTreeSet<Label>>>,
234*d4726bddSHONG Yifan
235*d4726bddSHONG Yifan /// Additional data to pass to the target's
236*d4726bddSHONG Yifan /// [crate_features](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-crate_features) attribute.
237*d4726bddSHONG Yifan pub(crate) crate_features: Option<Select<BTreeSet<String>>>,
238*d4726bddSHONG Yifan
239*d4726bddSHONG Yifan /// Additional data to pass to the target's
240*d4726bddSHONG Yifan /// [data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-data) attribute.
241*d4726bddSHONG Yifan pub(crate) data: Option<Select<BTreeSet<Label>>>,
242*d4726bddSHONG Yifan
243*d4726bddSHONG Yifan /// An optional glob pattern to set on the
244*d4726bddSHONG Yifan /// [data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-data) attribute.
245*d4726bddSHONG Yifan pub(crate) data_glob: Option<BTreeSet<String>>,
246*d4726bddSHONG Yifan
247*d4726bddSHONG Yifan /// Additional data to pass to
248*d4726bddSHONG Yifan /// [compile_data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-compile_data) attribute.
249*d4726bddSHONG Yifan pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
250*d4726bddSHONG Yifan
251*d4726bddSHONG Yifan /// An optional glob pattern to set on the
252*d4726bddSHONG Yifan /// [compile_data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-compile_data) attribute.
253*d4726bddSHONG Yifan pub(crate) compile_data_glob: Option<BTreeSet<String>>,
254*d4726bddSHONG Yifan
255*d4726bddSHONG Yifan /// If true, disables pipelining for library targets generated for this crate.
256*d4726bddSHONG Yifan pub(crate) disable_pipelining: bool,
257*d4726bddSHONG Yifan
258*d4726bddSHONG Yifan /// Additional data to pass to the target's
259*d4726bddSHONG Yifan /// [rustc_env](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_env) attribute.
260*d4726bddSHONG Yifan pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
261*d4726bddSHONG Yifan
262*d4726bddSHONG Yifan /// Additional data to pass to the target's
263*d4726bddSHONG Yifan /// [rustc_env_files](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_env_files) attribute.
264*d4726bddSHONG Yifan pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
265*d4726bddSHONG Yifan
266*d4726bddSHONG Yifan /// Additional data to pass to the target's
267*d4726bddSHONG Yifan /// [rustc_flags](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_flags) attribute.
268*d4726bddSHONG Yifan pub(crate) rustc_flags: Option<Select<Vec<String>>>,
269*d4726bddSHONG Yifan
270*d4726bddSHONG Yifan /// Additional dependencies to pass to a build script's
271*d4726bddSHONG Yifan /// [deps](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-deps) attribute.
272*d4726bddSHONG Yifan pub(crate) build_script_deps: Option<Select<BTreeSet<Label>>>,
273*d4726bddSHONG Yifan
274*d4726bddSHONG Yifan /// Additional data to pass to a build script's
275*d4726bddSHONG Yifan /// [proc_macro_deps](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-proc_macro_deps) attribute.
276*d4726bddSHONG Yifan pub(crate) build_script_proc_macro_deps: Option<Select<BTreeSet<Label>>>,
277*d4726bddSHONG Yifan
278*d4726bddSHONG Yifan /// Additional data to pass to a build script's
279*d4726bddSHONG Yifan /// [build_script_data](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-data) attribute.
280*d4726bddSHONG Yifan pub(crate) build_script_data: Option<Select<BTreeSet<Label>>>,
281*d4726bddSHONG Yifan
282*d4726bddSHONG Yifan /// Additional data to pass to a build script's
283*d4726bddSHONG Yifan /// [tools](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-tools) attribute.
284*d4726bddSHONG Yifan pub(crate) build_script_tools: Option<Select<BTreeSet<Label>>>,
285*d4726bddSHONG Yifan
286*d4726bddSHONG Yifan /// An optional glob pattern to set on the
287*d4726bddSHONG Yifan /// [build_script_data](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-build_script_env) attribute.
288*d4726bddSHONG Yifan pub(crate) build_script_data_glob: Option<BTreeSet<String>>,
289*d4726bddSHONG Yifan
290*d4726bddSHONG Yifan /// Additional environment variables to pass to a build script's
291*d4726bddSHONG Yifan /// [build_script_env](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-rustc_env) attribute.
292*d4726bddSHONG Yifan pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
293*d4726bddSHONG Yifan
294*d4726bddSHONG Yifan /// Additional rustc_env flags to pass to a build script's
295*d4726bddSHONG Yifan /// [rustc_env](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-rustc_env) attribute.
296*d4726bddSHONG Yifan pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
297*d4726bddSHONG Yifan
298*d4726bddSHONG Yifan /// Additional labels to pass to a build script's
299*d4726bddSHONG Yifan /// [toolchains](https://bazel.build/reference/be/common-definitions#common-attributes) attribute.
300*d4726bddSHONG Yifan pub(crate) build_script_toolchains: Option<BTreeSet<Label>>,
301*d4726bddSHONG Yifan
302*d4726bddSHONG Yifan /// Directory to run the crate's build script in. If not set, will run in the manifest directory, otherwise a directory relative to the exec root.
303*d4726bddSHONG Yifan pub(crate) build_script_rundir: Option<Select<String>>,
304*d4726bddSHONG Yifan
305*d4726bddSHONG Yifan /// A scratch pad used to write arbitrary text to target BUILD files.
306*d4726bddSHONG Yifan pub(crate) additive_build_file_content: Option<String>,
307*d4726bddSHONG Yifan
308*d4726bddSHONG Yifan /// For git sourced crates, this is a the
309*d4726bddSHONG Yifan /// [git_repository::shallow_since](https://docs.bazel.build/versions/main/repo/git.html#new_git_repository-shallow_since) attribute.
310*d4726bddSHONG Yifan pub(crate) shallow_since: Option<String>,
311*d4726bddSHONG Yifan
312*d4726bddSHONG Yifan /// The `patch_args` attribute of a Bazel repository rule. See
313*d4726bddSHONG Yifan /// [http_archive.patch_args](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_args)
314*d4726bddSHONG Yifan pub(crate) patch_args: Option<Vec<String>>,
315*d4726bddSHONG Yifan
316*d4726bddSHONG Yifan /// The `patch_tool` attribute of a Bazel repository rule. See
317*d4726bddSHONG Yifan /// [http_archive.patch_tool](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_tool)
318*d4726bddSHONG Yifan pub(crate) patch_tool: Option<String>,
319*d4726bddSHONG Yifan
320*d4726bddSHONG Yifan /// The `patches` attribute of a Bazel repository rule. See
321*d4726bddSHONG Yifan /// [http_archive.patches](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patches)
322*d4726bddSHONG Yifan pub(crate) patches: Option<BTreeSet<String>>,
323*d4726bddSHONG Yifan
324*d4726bddSHONG Yifan /// Extra targets the should be aliased during rendering.
325*d4726bddSHONG Yifan pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
326*d4726bddSHONG Yifan
327*d4726bddSHONG Yifan /// Transition rule to use instead of `native.alias()`.
328*d4726bddSHONG Yifan pub(crate) alias_rule: Option<AliasRule>,
329*d4726bddSHONG Yifan
330*d4726bddSHONG Yifan /// The crates to use instead of the generated one.
331*d4726bddSHONG Yifan pub(crate) override_targets: Option<BTreeMap<String, Label>>,
332*d4726bddSHONG Yifan }
333*d4726bddSHONG Yifan
334*d4726bddSHONG Yifan macro_rules! joined_extra_member {
335*d4726bddSHONG Yifan ($lhs:expr, $rhs:expr, $fn_new:expr, $fn_extend:expr) => {
336*d4726bddSHONG Yifan if let Some(lhs) = $lhs {
337*d4726bddSHONG Yifan if let Some(rhs) = $rhs {
338*d4726bddSHONG Yifan let mut new = $fn_new();
339*d4726bddSHONG Yifan $fn_extend(&mut new, lhs);
340*d4726bddSHONG Yifan $fn_extend(&mut new, rhs);
341*d4726bddSHONG Yifan Some(new)
342*d4726bddSHONG Yifan } else {
343*d4726bddSHONG Yifan Some(lhs)
344*d4726bddSHONG Yifan }
345*d4726bddSHONG Yifan } else if $rhs.is_some() {
346*d4726bddSHONG Yifan $rhs
347*d4726bddSHONG Yifan } else {
348*d4726bddSHONG Yifan None
349*d4726bddSHONG Yifan }
350*d4726bddSHONG Yifan };
351*d4726bddSHONG Yifan }
352*d4726bddSHONG Yifan
353*d4726bddSHONG Yifan impl Add for CrateAnnotations {
354*d4726bddSHONG Yifan type Output = CrateAnnotations;
355*d4726bddSHONG Yifan
add(self, rhs: Self) -> Self::Output356*d4726bddSHONG Yifan fn add(self, rhs: Self) -> Self::Output {
357*d4726bddSHONG Yifan fn select_merge<T>(lhs: Option<Select<T>>, rhs: Option<Select<T>>) -> Option<Select<T>>
358*d4726bddSHONG Yifan where
359*d4726bddSHONG Yifan T: Selectable,
360*d4726bddSHONG Yifan {
361*d4726bddSHONG Yifan match (lhs, rhs) {
362*d4726bddSHONG Yifan (Some(lhs), Some(rhs)) => Some(Select::merge(lhs, rhs)),
363*d4726bddSHONG Yifan (Some(lhs), None) => Some(lhs),
364*d4726bddSHONG Yifan (None, Some(rhs)) => Some(rhs),
365*d4726bddSHONG Yifan (None, None) => None,
366*d4726bddSHONG Yifan }
367*d4726bddSHONG Yifan }
368*d4726bddSHONG Yifan
369*d4726bddSHONG Yifan let concat_string = |lhs: &mut String, rhs: String| {
370*d4726bddSHONG Yifan *lhs = format!("{lhs}{rhs}");
371*d4726bddSHONG Yifan };
372*d4726bddSHONG Yifan
373*d4726bddSHONG Yifan #[rustfmt::skip]
374*d4726bddSHONG Yifan let output = CrateAnnotations {
375*d4726bddSHONG Yifan gen_binaries: self.gen_binaries.or(rhs.gen_binaries),
376*d4726bddSHONG Yifan gen_build_script: self.gen_build_script.or(rhs.gen_build_script),
377*d4726bddSHONG Yifan deps: select_merge(self.deps, rhs.deps),
378*d4726bddSHONG Yifan proc_macro_deps: select_merge(self.proc_macro_deps, rhs.proc_macro_deps),
379*d4726bddSHONG Yifan crate_features: select_merge(self.crate_features, rhs.crate_features),
380*d4726bddSHONG Yifan data: select_merge(self.data, rhs.data),
381*d4726bddSHONG Yifan data_glob: joined_extra_member!(self.data_glob, rhs.data_glob, BTreeSet::new, BTreeSet::extend),
382*d4726bddSHONG Yifan disable_pipelining: self.disable_pipelining || rhs.disable_pipelining,
383*d4726bddSHONG Yifan compile_data: select_merge(self.compile_data, rhs.compile_data),
384*d4726bddSHONG Yifan compile_data_glob: joined_extra_member!(self.compile_data_glob, rhs.compile_data_glob, BTreeSet::new, BTreeSet::extend),
385*d4726bddSHONG Yifan rustc_env: select_merge(self.rustc_env, rhs.rustc_env),
386*d4726bddSHONG Yifan rustc_env_files: select_merge(self.rustc_env_files, rhs.rustc_env_files),
387*d4726bddSHONG Yifan rustc_flags: select_merge(self.rustc_flags, rhs.rustc_flags),
388*d4726bddSHONG Yifan build_script_deps: select_merge(self.build_script_deps, rhs.build_script_deps),
389*d4726bddSHONG Yifan build_script_proc_macro_deps: select_merge(self.build_script_proc_macro_deps, rhs.build_script_proc_macro_deps),
390*d4726bddSHONG Yifan build_script_data: select_merge(self.build_script_data, rhs.build_script_data),
391*d4726bddSHONG Yifan build_script_tools: select_merge(self.build_script_tools, rhs.build_script_tools),
392*d4726bddSHONG Yifan build_script_data_glob: joined_extra_member!(self.build_script_data_glob, rhs.build_script_data_glob, BTreeSet::new, BTreeSet::extend),
393*d4726bddSHONG Yifan build_script_env: select_merge(self.build_script_env, rhs.build_script_env),
394*d4726bddSHONG Yifan build_script_rustc_env: select_merge(self.build_script_rustc_env, rhs.build_script_rustc_env),
395*d4726bddSHONG Yifan build_script_toolchains: joined_extra_member!(self.build_script_toolchains, rhs.build_script_toolchains, BTreeSet::new, BTreeSet::extend),
396*d4726bddSHONG Yifan build_script_rundir: self.build_script_rundir.or(rhs.build_script_rundir),
397*d4726bddSHONG Yifan additive_build_file_content: joined_extra_member!(self.additive_build_file_content, rhs.additive_build_file_content, String::new, concat_string),
398*d4726bddSHONG Yifan shallow_since: self.shallow_since.or(rhs.shallow_since),
399*d4726bddSHONG Yifan patch_args: joined_extra_member!(self.patch_args, rhs.patch_args, Vec::new, Vec::extend),
400*d4726bddSHONG Yifan patch_tool: self.patch_tool.or(rhs.patch_tool),
401*d4726bddSHONG Yifan patches: joined_extra_member!(self.patches, rhs.patches, BTreeSet::new, BTreeSet::extend),
402*d4726bddSHONG Yifan extra_aliased_targets: joined_extra_member!(self.extra_aliased_targets, rhs.extra_aliased_targets, BTreeMap::new, BTreeMap::extend),
403*d4726bddSHONG Yifan alias_rule: self.alias_rule.or(rhs.alias_rule),
404*d4726bddSHONG Yifan override_targets: self.override_targets.or(rhs.override_targets),
405*d4726bddSHONG Yifan };
406*d4726bddSHONG Yifan
407*d4726bddSHONG Yifan output
408*d4726bddSHONG Yifan }
409*d4726bddSHONG Yifan }
410*d4726bddSHONG Yifan
411*d4726bddSHONG Yifan impl Sum for CrateAnnotations {
sum<I: Iterator<Item = Self>>(iter: I) -> Self412*d4726bddSHONG Yifan fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
413*d4726bddSHONG Yifan iter.fold(CrateAnnotations::default(), |a, b| a + b)
414*d4726bddSHONG Yifan }
415*d4726bddSHONG Yifan }
416*d4726bddSHONG Yifan
417*d4726bddSHONG Yifan /// A subset of `crate.annotation` that we allow packages to define in their
418*d4726bddSHONG Yifan /// free-form Cargo.toml metadata.
419*d4726bddSHONG Yifan ///
420*d4726bddSHONG Yifan /// ```toml
421*d4726bddSHONG Yifan /// [package.metadata.bazel]
422*d4726bddSHONG Yifan /// additive_build_file_contents = """
423*d4726bddSHONG Yifan /// ...
424*d4726bddSHONG Yifan /// """
425*d4726bddSHONG Yifan /// data = ["font.woff2"]
426*d4726bddSHONG Yifan /// extra_aliased_targets = { ... }
427*d4726bddSHONG Yifan /// gen_build_script = false
428*d4726bddSHONG Yifan /// ```
429*d4726bddSHONG Yifan ///
430*d4726bddSHONG Yifan /// These are considered default values which apply if the Bazel workspace does
431*d4726bddSHONG Yifan /// not specify a different value for the same annotation in their
432*d4726bddSHONG Yifan /// crates_repository attributes.
433*d4726bddSHONG Yifan #[derive(Debug, Deserialize)]
434*d4726bddSHONG Yifan pub(crate) struct AnnotationsProvidedByPackage {
435*d4726bddSHONG Yifan pub(crate) gen_build_script: Option<bool>,
436*d4726bddSHONG Yifan pub(crate) data: Option<Select<BTreeSet<Label>>>,
437*d4726bddSHONG Yifan pub(crate) data_glob: Option<BTreeSet<String>>,
438*d4726bddSHONG Yifan pub(crate) deps: Option<Select<BTreeSet<Label>>>,
439*d4726bddSHONG Yifan pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
440*d4726bddSHONG Yifan pub(crate) compile_data_glob: Option<BTreeSet<String>>,
441*d4726bddSHONG Yifan pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
442*d4726bddSHONG Yifan pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
443*d4726bddSHONG Yifan pub(crate) rustc_flags: Option<Select<Vec<String>>>,
444*d4726bddSHONG Yifan pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
445*d4726bddSHONG Yifan pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
446*d4726bddSHONG Yifan pub(crate) build_script_rundir: Option<Select<String>>,
447*d4726bddSHONG Yifan pub(crate) additive_build_file_content: Option<String>,
448*d4726bddSHONG Yifan pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
449*d4726bddSHONG Yifan }
450*d4726bddSHONG Yifan
451*d4726bddSHONG Yifan impl CrateAnnotations {
apply_defaults_from_package_metadata( &mut self, pkg_metadata: &serde_json::Value, )452*d4726bddSHONG Yifan pub(crate) fn apply_defaults_from_package_metadata(
453*d4726bddSHONG Yifan &mut self,
454*d4726bddSHONG Yifan pkg_metadata: &serde_json::Value,
455*d4726bddSHONG Yifan ) {
456*d4726bddSHONG Yifan #[deny(unused_variables)]
457*d4726bddSHONG Yifan let AnnotationsProvidedByPackage {
458*d4726bddSHONG Yifan gen_build_script,
459*d4726bddSHONG Yifan data,
460*d4726bddSHONG Yifan data_glob,
461*d4726bddSHONG Yifan deps,
462*d4726bddSHONG Yifan compile_data,
463*d4726bddSHONG Yifan compile_data_glob,
464*d4726bddSHONG Yifan rustc_env,
465*d4726bddSHONG Yifan rustc_env_files,
466*d4726bddSHONG Yifan rustc_flags,
467*d4726bddSHONG Yifan build_script_env,
468*d4726bddSHONG Yifan build_script_rustc_env,
469*d4726bddSHONG Yifan build_script_rundir,
470*d4726bddSHONG Yifan additive_build_file_content,
471*d4726bddSHONG Yifan extra_aliased_targets,
472*d4726bddSHONG Yifan } = match AnnotationsProvidedByPackage::deserialize(&pkg_metadata["bazel"]) {
473*d4726bddSHONG Yifan Ok(annotations) => annotations,
474*d4726bddSHONG Yifan // Ignore bad annotations. The set of supported annotations evolves
475*d4726bddSHONG Yifan // over time across different versions of crate_universe, and we
476*d4726bddSHONG Yifan // don't want a library to be impossible to import into Bazel for
477*d4726bddSHONG Yifan // having old or broken annotations. The Bazel workspace can specify
478*d4726bddSHONG Yifan // its own correct annotations.
479*d4726bddSHONG Yifan Err(_) => return,
480*d4726bddSHONG Yifan };
481*d4726bddSHONG Yifan
482*d4726bddSHONG Yifan fn default<T>(workspace_value: &mut Option<T>, default_value: Option<T>) {
483*d4726bddSHONG Yifan if workspace_value.is_none() {
484*d4726bddSHONG Yifan *workspace_value = default_value;
485*d4726bddSHONG Yifan }
486*d4726bddSHONG Yifan }
487*d4726bddSHONG Yifan
488*d4726bddSHONG Yifan default(&mut self.gen_build_script, gen_build_script);
489*d4726bddSHONG Yifan default(&mut self.gen_build_script, gen_build_script);
490*d4726bddSHONG Yifan default(&mut self.data, data);
491*d4726bddSHONG Yifan default(&mut self.data_glob, data_glob);
492*d4726bddSHONG Yifan default(&mut self.deps, deps);
493*d4726bddSHONG Yifan default(&mut self.compile_data, compile_data);
494*d4726bddSHONG Yifan default(&mut self.compile_data_glob, compile_data_glob);
495*d4726bddSHONG Yifan default(&mut self.rustc_env, rustc_env);
496*d4726bddSHONG Yifan default(&mut self.rustc_env_files, rustc_env_files);
497*d4726bddSHONG Yifan default(&mut self.rustc_flags, rustc_flags);
498*d4726bddSHONG Yifan default(&mut self.build_script_env, build_script_env);
499*d4726bddSHONG Yifan default(&mut self.build_script_rustc_env, build_script_rustc_env);
500*d4726bddSHONG Yifan default(&mut self.build_script_rundir, build_script_rundir);
501*d4726bddSHONG Yifan default(
502*d4726bddSHONG Yifan &mut self.additive_build_file_content,
503*d4726bddSHONG Yifan additive_build_file_content,
504*d4726bddSHONG Yifan );
505*d4726bddSHONG Yifan default(&mut self.extra_aliased_targets, extra_aliased_targets);
506*d4726bddSHONG Yifan }
507*d4726bddSHONG Yifan }
508*d4726bddSHONG Yifan
509*d4726bddSHONG Yifan /// A unique identifier for Crates
510*d4726bddSHONG Yifan #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
511*d4726bddSHONG Yifan pub struct CrateId {
512*d4726bddSHONG Yifan /// The name of the crate
513*d4726bddSHONG Yifan pub name: String,
514*d4726bddSHONG Yifan
515*d4726bddSHONG Yifan /// The crate's semantic version
516*d4726bddSHONG Yifan pub version: semver::Version,
517*d4726bddSHONG Yifan }
518*d4726bddSHONG Yifan
519*d4726bddSHONG Yifan impl CrateId {
520*d4726bddSHONG Yifan /// Construct a new [CrateId]
new(name: String, version: semver::Version) -> Self521*d4726bddSHONG Yifan pub(crate) fn new(name: String, version: semver::Version) -> Self {
522*d4726bddSHONG Yifan Self { name, version }
523*d4726bddSHONG Yifan }
524*d4726bddSHONG Yifan }
525*d4726bddSHONG Yifan
526*d4726bddSHONG Yifan impl From<&Package> for CrateId {
from(package: &Package) -> Self527*d4726bddSHONG Yifan fn from(package: &Package) -> Self {
528*d4726bddSHONG Yifan Self {
529*d4726bddSHONG Yifan name: package.name.clone(),
530*d4726bddSHONG Yifan version: package.version.clone(),
531*d4726bddSHONG Yifan }
532*d4726bddSHONG Yifan }
533*d4726bddSHONG Yifan }
534*d4726bddSHONG Yifan
535*d4726bddSHONG Yifan impl Serialize for CrateId {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,536*d4726bddSHONG Yifan fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
537*d4726bddSHONG Yifan where
538*d4726bddSHONG Yifan S: Serializer,
539*d4726bddSHONG Yifan {
540*d4726bddSHONG Yifan serializer.serialize_str(&format!("{} {}", self.name, self.version))
541*d4726bddSHONG Yifan }
542*d4726bddSHONG Yifan }
543*d4726bddSHONG Yifan
544*d4726bddSHONG Yifan struct CrateIdVisitor;
545*d4726bddSHONG Yifan impl<'de> Visitor<'de> for CrateIdVisitor {
546*d4726bddSHONG Yifan type Value = CrateId;
547*d4726bddSHONG Yifan
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result548*d4726bddSHONG Yifan fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
549*d4726bddSHONG Yifan formatter.write_str("Expected string value of `{name} {version}`.")
550*d4726bddSHONG Yifan }
551*d4726bddSHONG Yifan
visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error,552*d4726bddSHONG Yifan fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
553*d4726bddSHONG Yifan where
554*d4726bddSHONG Yifan E: serde::de::Error,
555*d4726bddSHONG Yifan {
556*d4726bddSHONG Yifan let (name, version_str) = v.rsplit_once(' ').ok_or_else(|| {
557*d4726bddSHONG Yifan E::custom(format!(
558*d4726bddSHONG Yifan "Expected string value of `{{name}} {{version}}`. Got '{v}'"
559*d4726bddSHONG Yifan ))
560*d4726bddSHONG Yifan })?;
561*d4726bddSHONG Yifan let version = semver::Version::parse(version_str).map_err(|err| {
562*d4726bddSHONG Yifan E::custom(format!(
563*d4726bddSHONG Yifan "Couldn't parse {version_str} as a semver::Version: {err}"
564*d4726bddSHONG Yifan ))
565*d4726bddSHONG Yifan })?;
566*d4726bddSHONG Yifan Ok(CrateId {
567*d4726bddSHONG Yifan name: name.to_string(),
568*d4726bddSHONG Yifan version,
569*d4726bddSHONG Yifan })
570*d4726bddSHONG Yifan }
571*d4726bddSHONG Yifan }
572*d4726bddSHONG Yifan
573*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for CrateId {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>,574*d4726bddSHONG Yifan fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
575*d4726bddSHONG Yifan where
576*d4726bddSHONG Yifan D: serde::Deserializer<'de>,
577*d4726bddSHONG Yifan {
578*d4726bddSHONG Yifan deserializer.deserialize_str(CrateIdVisitor)
579*d4726bddSHONG Yifan }
580*d4726bddSHONG Yifan }
581*d4726bddSHONG Yifan
582*d4726bddSHONG Yifan impl std::fmt::Display for CrateId {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result583*d4726bddSHONG Yifan fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
584*d4726bddSHONG Yifan fmt::Display::fmt(&format!("{} {}", self.name, self.version), f)
585*d4726bddSHONG Yifan }
586*d4726bddSHONG Yifan }
587*d4726bddSHONG Yifan
588*d4726bddSHONG Yifan #[derive(Debug, Hash, Clone, PartialEq, Eq)]
589*d4726bddSHONG Yifan pub(crate) enum GenBinaries {
590*d4726bddSHONG Yifan All,
591*d4726bddSHONG Yifan Some(BTreeSet<String>),
592*d4726bddSHONG Yifan }
593*d4726bddSHONG Yifan
594*d4726bddSHONG Yifan impl Default for GenBinaries {
default() -> Self595*d4726bddSHONG Yifan fn default() -> Self {
596*d4726bddSHONG Yifan GenBinaries::Some(BTreeSet::new())
597*d4726bddSHONG Yifan }
598*d4726bddSHONG Yifan }
599*d4726bddSHONG Yifan
600*d4726bddSHONG Yifan impl Serialize for GenBinaries {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,601*d4726bddSHONG Yifan fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
602*d4726bddSHONG Yifan where
603*d4726bddSHONG Yifan S: Serializer,
604*d4726bddSHONG Yifan {
605*d4726bddSHONG Yifan match self {
606*d4726bddSHONG Yifan GenBinaries::All => serializer.serialize_bool(true),
607*d4726bddSHONG Yifan GenBinaries::Some(set) if set.is_empty() => serializer.serialize_bool(false),
608*d4726bddSHONG Yifan GenBinaries::Some(set) => serializer.collect_seq(set),
609*d4726bddSHONG Yifan }
610*d4726bddSHONG Yifan }
611*d4726bddSHONG Yifan }
612*d4726bddSHONG Yifan
613*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for GenBinaries {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,614*d4726bddSHONG Yifan fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
615*d4726bddSHONG Yifan where
616*d4726bddSHONG Yifan D: Deserializer<'de>,
617*d4726bddSHONG Yifan {
618*d4726bddSHONG Yifan deserializer.deserialize_any(GenBinariesVisitor)
619*d4726bddSHONG Yifan }
620*d4726bddSHONG Yifan }
621*d4726bddSHONG Yifan
622*d4726bddSHONG Yifan struct GenBinariesVisitor;
623*d4726bddSHONG Yifan impl<'de> Visitor<'de> for GenBinariesVisitor {
624*d4726bddSHONG Yifan type Value = GenBinaries;
625*d4726bddSHONG Yifan
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result626*d4726bddSHONG Yifan fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
627*d4726bddSHONG Yifan formatter.write_str("boolean, or array of bin names")
628*d4726bddSHONG Yifan }
629*d4726bddSHONG Yifan
visit_bool<E>(self, gen_binaries: bool) -> Result<Self::Value, E>630*d4726bddSHONG Yifan fn visit_bool<E>(self, gen_binaries: bool) -> Result<Self::Value, E> {
631*d4726bddSHONG Yifan if gen_binaries {
632*d4726bddSHONG Yifan Ok(GenBinaries::All)
633*d4726bddSHONG Yifan } else {
634*d4726bddSHONG Yifan Ok(GenBinaries::Some(BTreeSet::new()))
635*d4726bddSHONG Yifan }
636*d4726bddSHONG Yifan }
637*d4726bddSHONG Yifan
visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error> where A: SeqAccess<'de>,638*d4726bddSHONG Yifan fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
639*d4726bddSHONG Yifan where
640*d4726bddSHONG Yifan A: SeqAccess<'de>,
641*d4726bddSHONG Yifan {
642*d4726bddSHONG Yifan BTreeSet::deserialize(SeqAccessDeserializer::new(seq)).map(GenBinaries::Some)
643*d4726bddSHONG Yifan }
644*d4726bddSHONG Yifan }
645*d4726bddSHONG Yifan
646*d4726bddSHONG Yifan /// Workspace specific settings to control how targets are generated
647*d4726bddSHONG Yifan #[derive(Debug, Default, Serialize, Deserialize, Clone)]
648*d4726bddSHONG Yifan #[serde(deny_unknown_fields)]
649*d4726bddSHONG Yifan pub(crate) struct Config {
650*d4726bddSHONG Yifan /// Whether to generate `rust_binary` targets for all bins by default
651*d4726bddSHONG Yifan pub(crate) generate_binaries: bool,
652*d4726bddSHONG Yifan
653*d4726bddSHONG Yifan /// Whether or not to generate Cargo build scripts by default
654*d4726bddSHONG Yifan pub(crate) generate_build_scripts: bool,
655*d4726bddSHONG Yifan
656*d4726bddSHONG Yifan /// Additional settings to apply to generated crates
657*d4726bddSHONG Yifan #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
658*d4726bddSHONG Yifan pub(crate) annotations: BTreeMap<CrateNameAndVersionReq, CrateAnnotations>,
659*d4726bddSHONG Yifan
660*d4726bddSHONG Yifan /// Settings used to determine various render info
661*d4726bddSHONG Yifan pub(crate) rendering: RenderConfig,
662*d4726bddSHONG Yifan
663*d4726bddSHONG Yifan /// The contents of a Cargo configuration file
664*d4726bddSHONG Yifan pub(crate) cargo_config: Option<toml::Value>,
665*d4726bddSHONG Yifan
666*d4726bddSHONG Yifan /// A set of platform triples to use in generated select statements
667*d4726bddSHONG Yifan #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
668*d4726bddSHONG Yifan pub(crate) supported_platform_triples: BTreeSet<TargetTriple>,
669*d4726bddSHONG Yifan }
670*d4726bddSHONG Yifan
671*d4726bddSHONG Yifan impl Config {
try_from_path<T: AsRef<Path>>(path: T) -> Result<Self>672*d4726bddSHONG Yifan pub(crate) fn try_from_path<T: AsRef<Path>>(path: T) -> Result<Self> {
673*d4726bddSHONG Yifan let data = fs::read_to_string(path)?;
674*d4726bddSHONG Yifan Ok(serde_json::from_str(&data)?)
675*d4726bddSHONG Yifan }
676*d4726bddSHONG Yifan }
677*d4726bddSHONG Yifan
678*d4726bddSHONG Yifan #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
679*d4726bddSHONG Yifan pub struct CrateNameAndVersionReq {
680*d4726bddSHONG Yifan /// The name of the crate
681*d4726bddSHONG Yifan pub name: String,
682*d4726bddSHONG Yifan
683*d4726bddSHONG Yifan version_req_string: VersionReqString,
684*d4726bddSHONG Yifan }
685*d4726bddSHONG Yifan
686*d4726bddSHONG Yifan impl Serialize for CrateNameAndVersionReq {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,687*d4726bddSHONG Yifan fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
688*d4726bddSHONG Yifan where
689*d4726bddSHONG Yifan S: Serializer,
690*d4726bddSHONG Yifan {
691*d4726bddSHONG Yifan serializer.serialize_str(&format!(
692*d4726bddSHONG Yifan "{} {}",
693*d4726bddSHONG Yifan self.name, self.version_req_string.original
694*d4726bddSHONG Yifan ))
695*d4726bddSHONG Yifan }
696*d4726bddSHONG Yifan }
697*d4726bddSHONG Yifan
698*d4726bddSHONG Yifan struct CrateNameAndVersionReqVisitor;
699*d4726bddSHONG Yifan impl<'de> Visitor<'de> for CrateNameAndVersionReqVisitor {
700*d4726bddSHONG Yifan type Value = CrateNameAndVersionReq;
701*d4726bddSHONG Yifan
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result702*d4726bddSHONG Yifan fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
703*d4726bddSHONG Yifan formatter.write_str("Expected string value of `{name} {version}`.")
704*d4726bddSHONG Yifan }
705*d4726bddSHONG Yifan
visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error,706*d4726bddSHONG Yifan fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
707*d4726bddSHONG Yifan where
708*d4726bddSHONG Yifan E: serde::de::Error,
709*d4726bddSHONG Yifan {
710*d4726bddSHONG Yifan let (name, version) = v.rsplit_once(' ').ok_or_else(|| {
711*d4726bddSHONG Yifan E::custom(format!(
712*d4726bddSHONG Yifan "Expected string value of `{{name}} {{version}}`. Got '{v}'"
713*d4726bddSHONG Yifan ))
714*d4726bddSHONG Yifan })?;
715*d4726bddSHONG Yifan version
716*d4726bddSHONG Yifan .parse()
717*d4726bddSHONG Yifan .map(|version| CrateNameAndVersionReq {
718*d4726bddSHONG Yifan name: name.to_string(),
719*d4726bddSHONG Yifan version_req_string: version,
720*d4726bddSHONG Yifan })
721*d4726bddSHONG Yifan .map_err(|err| E::custom(err.to_string()))
722*d4726bddSHONG Yifan }
723*d4726bddSHONG Yifan }
724*d4726bddSHONG Yifan
725*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for CrateNameAndVersionReq {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>,726*d4726bddSHONG Yifan fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
727*d4726bddSHONG Yifan where
728*d4726bddSHONG Yifan D: serde::Deserializer<'de>,
729*d4726bddSHONG Yifan {
730*d4726bddSHONG Yifan deserializer.deserialize_str(CrateNameAndVersionReqVisitor)
731*d4726bddSHONG Yifan }
732*d4726bddSHONG Yifan }
733*d4726bddSHONG Yifan
734*d4726bddSHONG Yifan /// A version requirement (i.e. a semver::VersionReq) which preserves the original string it was parsed from.
735*d4726bddSHONG Yifan /// This means that you can report back to the user whether they wrote `1` or `1.0.0` or `^1.0.0` or `>=1,<2`,
736*d4726bddSHONG Yifan /// and support exact round-trip serialization and deserialization.
737*d4726bddSHONG Yifan #[derive(Clone, Debug)]
738*d4726bddSHONG Yifan pub struct VersionReqString {
739*d4726bddSHONG Yifan original: String,
740*d4726bddSHONG Yifan
741*d4726bddSHONG Yifan parsed: VersionReq,
742*d4726bddSHONG Yifan }
743*d4726bddSHONG Yifan
744*d4726bddSHONG Yifan impl FromStr for VersionReqString {
745*d4726bddSHONG Yifan type Err = anyhow::Error;
746*d4726bddSHONG Yifan
from_str(original: &str) -> Result<Self, Self::Err>747*d4726bddSHONG Yifan fn from_str(original: &str) -> Result<Self, Self::Err> {
748*d4726bddSHONG Yifan let parsed = VersionReq::parse(original)
749*d4726bddSHONG Yifan .context("VersionReqString must be a valid semver requirement")?;
750*d4726bddSHONG Yifan Ok(VersionReqString {
751*d4726bddSHONG Yifan original: original.to_owned(),
752*d4726bddSHONG Yifan parsed,
753*d4726bddSHONG Yifan })
754*d4726bddSHONG Yifan }
755*d4726bddSHONG Yifan }
756*d4726bddSHONG Yifan
757*d4726bddSHONG Yifan impl PartialEq for VersionReqString {
eq(&self, other: &Self) -> bool758*d4726bddSHONG Yifan fn eq(&self, other: &Self) -> bool {
759*d4726bddSHONG Yifan self.original == other.original
760*d4726bddSHONG Yifan }
761*d4726bddSHONG Yifan }
762*d4726bddSHONG Yifan
763*d4726bddSHONG Yifan impl Eq for VersionReqString {}
764*d4726bddSHONG Yifan
765*d4726bddSHONG Yifan impl PartialOrd for VersionReqString {
partial_cmp(&self, other: &Self) -> Option<Ordering>766*d4726bddSHONG Yifan fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
767*d4726bddSHONG Yifan Some(self.cmp(other))
768*d4726bddSHONG Yifan }
769*d4726bddSHONG Yifan }
770*d4726bddSHONG Yifan
771*d4726bddSHONG Yifan impl Ord for VersionReqString {
cmp(&self, other: &Self) -> Ordering772*d4726bddSHONG Yifan fn cmp(&self, other: &Self) -> Ordering {
773*d4726bddSHONG Yifan Ord::cmp(&self.original, &other.original)
774*d4726bddSHONG Yifan }
775*d4726bddSHONG Yifan }
776*d4726bddSHONG Yifan
777*d4726bddSHONG Yifan impl Serialize for VersionReqString {
serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> where S: Serializer,778*d4726bddSHONG Yifan fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
779*d4726bddSHONG Yifan where
780*d4726bddSHONG Yifan S: Serializer,
781*d4726bddSHONG Yifan {
782*d4726bddSHONG Yifan serializer.serialize_str(&self.original)
783*d4726bddSHONG Yifan }
784*d4726bddSHONG Yifan }
785*d4726bddSHONG Yifan
786*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for VersionReqString {
deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> where D: Deserializer<'de>,787*d4726bddSHONG Yifan fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
788*d4726bddSHONG Yifan where
789*d4726bddSHONG Yifan D: Deserializer<'de>,
790*d4726bddSHONG Yifan {
791*d4726bddSHONG Yifan struct StringVisitor;
792*d4726bddSHONG Yifan
793*d4726bddSHONG Yifan impl<'de> Visitor<'de> for StringVisitor {
794*d4726bddSHONG Yifan type Value = String;
795*d4726bddSHONG Yifan
796*d4726bddSHONG Yifan fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
797*d4726bddSHONG Yifan formatter.write_str("string of a semver requirement")
798*d4726bddSHONG Yifan }
799*d4726bddSHONG Yifan }
800*d4726bddSHONG Yifan
801*d4726bddSHONG Yifan let original = deserializer.deserialize_str(StringVisitor)?;
802*d4726bddSHONG Yifan let parsed = VersionReq::parse(&original).map_err(|_| {
803*d4726bddSHONG Yifan serde::de::Error::invalid_value(
804*d4726bddSHONG Yifan Unexpected::Str(&original),
805*d4726bddSHONG Yifan &"a valid semver requirement",
806*d4726bddSHONG Yifan )
807*d4726bddSHONG Yifan })?;
808*d4726bddSHONG Yifan Ok(VersionReqString { original, parsed })
809*d4726bddSHONG Yifan }
810*d4726bddSHONG Yifan }
811*d4726bddSHONG Yifan
812*d4726bddSHONG Yifan impl CrateNameAndVersionReq {
813*d4726bddSHONG Yifan #[cfg(test)]
new(name: String, version_req_string: VersionReqString) -> CrateNameAndVersionReq814*d4726bddSHONG Yifan pub fn new(name: String, version_req_string: VersionReqString) -> CrateNameAndVersionReq {
815*d4726bddSHONG Yifan CrateNameAndVersionReq {
816*d4726bddSHONG Yifan name,
817*d4726bddSHONG Yifan version_req_string,
818*d4726bddSHONG Yifan }
819*d4726bddSHONG Yifan }
820*d4726bddSHONG Yifan
821*d4726bddSHONG Yifan /// Compares a [CrateNameAndVersionReq] against a [cargo_metadata::Package].
matches(&self, package: &Package) -> bool822*d4726bddSHONG Yifan pub fn matches(&self, package: &Package) -> bool {
823*d4726bddSHONG Yifan // If the package name does not match, it's obviously
824*d4726bddSHONG Yifan // not the right package
825*d4726bddSHONG Yifan if self.name != "*" && self.name != package.name {
826*d4726bddSHONG Yifan return false;
827*d4726bddSHONG Yifan }
828*d4726bddSHONG Yifan
829*d4726bddSHONG Yifan // First see if the package version matches exactly
830*d4726bddSHONG Yifan if package.version.to_string() == self.version_req_string.original {
831*d4726bddSHONG Yifan return true;
832*d4726bddSHONG Yifan }
833*d4726bddSHONG Yifan
834*d4726bddSHONG Yifan // If the version provided is the wildcard "*", it matches. Do not
835*d4726bddSHONG Yifan // delegate to the semver crate in this case because semver does not
836*d4726bddSHONG Yifan // consider "*" to match prerelease packages. That's expected behavior
837*d4726bddSHONG Yifan // in the context of declaring package dependencies, but not in the
838*d4726bddSHONG Yifan // context of declaring which versions of preselected packages an
839*d4726bddSHONG Yifan // annotation applies to.
840*d4726bddSHONG Yifan if self.version_req_string.original == "*" {
841*d4726bddSHONG Yifan return true;
842*d4726bddSHONG Yifan }
843*d4726bddSHONG Yifan
844*d4726bddSHONG Yifan // Next, check to see if the version provided is a semver req and
845*d4726bddSHONG Yifan // check if the package matches the condition
846*d4726bddSHONG Yifan self.version_req_string.parsed.matches(&package.version)
847*d4726bddSHONG Yifan }
848*d4726bddSHONG Yifan }
849*d4726bddSHONG Yifan
850*d4726bddSHONG Yifan #[cfg(test)]
851*d4726bddSHONG Yifan mod test {
852*d4726bddSHONG Yifan use super::*;
853*d4726bddSHONG Yifan
854*d4726bddSHONG Yifan use crate::test::*;
855*d4726bddSHONG Yifan
856*d4726bddSHONG Yifan #[test]
test_crate_id_serde()857*d4726bddSHONG Yifan fn test_crate_id_serde() {
858*d4726bddSHONG Yifan let id: CrateId = serde_json::from_str("\"crate 0.1.0\"").unwrap();
859*d4726bddSHONG Yifan assert_eq!(
860*d4726bddSHONG Yifan id,
861*d4726bddSHONG Yifan CrateId::new("crate".to_owned(), semver::Version::new(0, 1, 0))
862*d4726bddSHONG Yifan );
863*d4726bddSHONG Yifan assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
864*d4726bddSHONG Yifan }
865*d4726bddSHONG Yifan
866*d4726bddSHONG Yifan #[test]
test_crate_id_matches()867*d4726bddSHONG Yifan fn test_crate_id_matches() {
868*d4726bddSHONG Yifan let mut package = mock_cargo_metadata_package();
869*d4726bddSHONG Yifan let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "0.1.0".parse().unwrap());
870*d4726bddSHONG Yifan
871*d4726bddSHONG Yifan package.version = cargo_metadata::semver::Version::new(0, 1, 0);
872*d4726bddSHONG Yifan assert!(id.matches(&package));
873*d4726bddSHONG Yifan
874*d4726bddSHONG Yifan package.version = cargo_metadata::semver::Version::new(1, 0, 0);
875*d4726bddSHONG Yifan assert!(!id.matches(&package));
876*d4726bddSHONG Yifan }
877*d4726bddSHONG Yifan
878*d4726bddSHONG Yifan #[test]
test_crate_name_and_version_req_serde()879*d4726bddSHONG Yifan fn test_crate_name_and_version_req_serde() {
880*d4726bddSHONG Yifan let id: CrateNameAndVersionReq = serde_json::from_str("\"crate 0.1.0\"").unwrap();
881*d4726bddSHONG Yifan assert_eq!(
882*d4726bddSHONG Yifan id,
883*d4726bddSHONG Yifan CrateNameAndVersionReq::new(
884*d4726bddSHONG Yifan "crate".to_owned(),
885*d4726bddSHONG Yifan VersionReqString::from_str("0.1.0").unwrap()
886*d4726bddSHONG Yifan )
887*d4726bddSHONG Yifan );
888*d4726bddSHONG Yifan assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
889*d4726bddSHONG Yifan }
890*d4726bddSHONG Yifan
891*d4726bddSHONG Yifan #[test]
test_crate_name_and_version_req_serde_semver()892*d4726bddSHONG Yifan fn test_crate_name_and_version_req_serde_semver() {
893*d4726bddSHONG Yifan let id: CrateNameAndVersionReq = serde_json::from_str("\"crate *\"").unwrap();
894*d4726bddSHONG Yifan assert_eq!(
895*d4726bddSHONG Yifan id,
896*d4726bddSHONG Yifan CrateNameAndVersionReq::new(
897*d4726bddSHONG Yifan "crate".to_owned(),
898*d4726bddSHONG Yifan VersionReqString::from_str("*").unwrap()
899*d4726bddSHONG Yifan )
900*d4726bddSHONG Yifan );
901*d4726bddSHONG Yifan assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate *\"");
902*d4726bddSHONG Yifan }
903*d4726bddSHONG Yifan
904*d4726bddSHONG Yifan #[test]
test_crate_name_and_version_req_semver_matches()905*d4726bddSHONG Yifan fn test_crate_name_and_version_req_semver_matches() {
906*d4726bddSHONG Yifan let mut package = mock_cargo_metadata_package();
907*d4726bddSHONG Yifan package.version = cargo_metadata::semver::Version::new(1, 0, 0);
908*d4726bddSHONG Yifan let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "*".parse().unwrap());
909*d4726bddSHONG Yifan assert!(id.matches(&package));
910*d4726bddSHONG Yifan
911*d4726bddSHONG Yifan let mut prerelease = mock_cargo_metadata_package();
912*d4726bddSHONG Yifan prerelease.version = cargo_metadata::semver::Version::parse("1.0.0-pre.0").unwrap();
913*d4726bddSHONG Yifan assert!(id.matches(&prerelease));
914*d4726bddSHONG Yifan
915*d4726bddSHONG Yifan let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "<1".parse().unwrap());
916*d4726bddSHONG Yifan assert!(!id.matches(&package));
917*d4726bddSHONG Yifan }
918*d4726bddSHONG Yifan
919*d4726bddSHONG Yifan #[test]
deserialize_config()920*d4726bddSHONG Yifan fn deserialize_config() {
921*d4726bddSHONG Yifan let runfiles = runfiles::Runfiles::create().unwrap();
922*d4726bddSHONG Yifan let path = runfiles::rlocation!(
923*d4726bddSHONG Yifan runfiles,
924*d4726bddSHONG Yifan "rules_rust/crate_universe/test_data/serialized_configs/config.json"
925*d4726bddSHONG Yifan );
926*d4726bddSHONG Yifan
927*d4726bddSHONG Yifan let content = std::fs::read_to_string(path).unwrap();
928*d4726bddSHONG Yifan
929*d4726bddSHONG Yifan let config: Config = serde_json::from_str(&content).unwrap();
930*d4726bddSHONG Yifan
931*d4726bddSHONG Yifan // Annotations
932*d4726bddSHONG Yifan let annotation = config
933*d4726bddSHONG Yifan .annotations
934*d4726bddSHONG Yifan .get(&CrateNameAndVersionReq::new(
935*d4726bddSHONG Yifan "rand".to_owned(),
936*d4726bddSHONG Yifan "0.8.5".parse().unwrap(),
937*d4726bddSHONG Yifan ))
938*d4726bddSHONG Yifan .unwrap();
939*d4726bddSHONG Yifan assert_eq!(
940*d4726bddSHONG Yifan annotation.crate_features,
941*d4726bddSHONG Yifan Some(Select::from_value(BTreeSet::from(["small_rng".to_owned()])))
942*d4726bddSHONG Yifan );
943*d4726bddSHONG Yifan
944*d4726bddSHONG Yifan // Global settings
945*d4726bddSHONG Yifan assert!(config.cargo_config.is_none());
946*d4726bddSHONG Yifan assert!(!config.generate_binaries);
947*d4726bddSHONG Yifan assert!(!config.generate_build_scripts);
948*d4726bddSHONG Yifan
949*d4726bddSHONG Yifan // Render Config
950*d4726bddSHONG Yifan assert_eq!(
951*d4726bddSHONG Yifan config.rendering.platforms_template,
952*d4726bddSHONG Yifan "//custom/platform:{triple}"
953*d4726bddSHONG Yifan );
954*d4726bddSHONG Yifan }
955*d4726bddSHONG Yifan }
956