xref: /aosp_15_r20/external/bazelbuild-rules_rust/crate_universe/src/context/platforms.rs (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1 use std::collections::{BTreeMap, BTreeSet};
2 
3 use anyhow::{anyhow, Context, Result};
4 use cfg_expr::targets::{get_builtin_target_by_triple, TargetInfo};
5 use cfg_expr::{Expression, Predicate};
6 
7 use crate::context::CrateContext;
8 use crate::utils::target_triple::TargetTriple;
9 
10 /// Walk through all dependencies in a [CrateContext] list for all configuration specific
11 /// dependencies to produce a mapping of configurations/Cargo target_triples to compatible
12 /// Bazel target_triples.  Also adds mappings for all known target_triples.
resolve_cfg_platforms( crates: Vec<&CrateContext>, supported_platform_triples: &BTreeSet<TargetTriple>, ) -> Result<BTreeMap<String, BTreeSet<TargetTriple>>>13 pub(crate) fn resolve_cfg_platforms(
14     crates: Vec<&CrateContext>,
15     supported_platform_triples: &BTreeSet<TargetTriple>,
16 ) -> Result<BTreeMap<String, BTreeSet<TargetTriple>>> {
17     // Collect all unique configurations from all dependencies into a single set
18     let configurations: BTreeSet<String> = crates
19         .iter()
20         .flat_map(|ctx| {
21             let attr = &ctx.common_attrs;
22             let mut configurations = BTreeSet::new();
23 
24             configurations.extend(attr.deps.configurations());
25             configurations.extend(attr.deps_dev.configurations());
26             configurations.extend(attr.proc_macro_deps.configurations());
27             configurations.extend(attr.proc_macro_deps_dev.configurations());
28 
29             // Chain the build dependencies if some are defined
30             if let Some(attr) = &ctx.build_script_attrs {
31                 configurations.extend(attr.deps.configurations());
32                 configurations.extend(attr.proc_macro_deps.configurations());
33             }
34 
35             configurations
36         })
37         .collect();
38 
39     // Generate target information for each triple string
40     let target_infos = supported_platform_triples
41         .iter()
42         .map(
43             |target_triple| match get_builtin_target_by_triple(&target_triple.to_cargo()) {
44                 Some(info) => Ok((target_triple, info)),
45                 None => Err(anyhow!(
46                     "Invalid platform triple in supported platforms: {}",
47                     target_triple
48                 )),
49             },
50         )
51         .collect::<Result<BTreeMap<&TargetTriple, &'static TargetInfo>>>()?;
52 
53     // `cfg-expr` does not understand configurations that are simply platform triples
54     // (`x86_64-unknown-linux-gnu` vs `cfg(target = "x86_64-unkonwn-linux-gnu")`). So
55     // in order to parse configurations, the text is renamed for the check but the
56     // original is retained for comaptibility with the manifest.
57     let rename = |cfg: &str| -> String { format!("cfg(target = \"{cfg}\")") };
58     let original_cfgs: BTreeMap<String, String> = configurations
59         .iter()
60         .filter(|cfg| !cfg.starts_with("cfg("))
61         .map(|cfg| (rename(cfg), cfg.clone()))
62         .collect();
63 
64     let mut conditions = configurations
65         .into_iter()
66         // `cfg-expr` requires that the expressions be actual `cfg` expressions. Any time
67         // there's a target triple (which is a valid constraint), convert it to a cfg expression.
68         .map(|cfg| match cfg.starts_with("cfg(") {
69             true => cfg,
70             false => rename(&cfg),
71         })
72         // Check the current configuration with against each supported triple
73         .map(|cfg| {
74             let expression =
75                 Expression::parse(&cfg).context(format!("Failed to parse expression: '{cfg}'"))?;
76 
77             let triples = target_infos
78                 .iter()
79                 .filter(|(_, target_info)| {
80                     expression.eval(|p| match p {
81                         Predicate::Target(tp) => tp.matches(**target_info),
82                         Predicate::KeyValue { key, val } => {
83                             *key == "target" && val == &target_info.triple.as_str()
84                         }
85                         // For now there is no other kind of matching
86                         _ => false,
87                     })
88                 })
89                 .map(|(triple, _)| (*triple).clone())
90                 .collect();
91 
92             // Map any renamed configurations back to their original IDs
93             let cfg = match original_cfgs.get(&cfg) {
94                 Some(orig) => orig.clone(),
95                 None => cfg,
96             };
97 
98             Ok((cfg, triples))
99         })
100         .collect::<Result<BTreeMap<String, BTreeSet<TargetTriple>>>>()?;
101     // Insert identity relationships.
102     for target_triple in supported_platform_triples.iter() {
103         conditions
104             .entry(target_triple.to_bazel())
105             .or_default()
106             .insert(target_triple.clone());
107     }
108     Ok(conditions)
109 }
110 
111 #[cfg(test)]
112 mod test {
113     use crate::config::CrateId;
114     use crate::context::crate_context::CrateDependency;
115     use crate::context::CommonAttributes;
116     use crate::select::Select;
117 
118     use super::*;
119 
120     const VERSION_ZERO_ONE_ZERO: semver::Version = semver::Version::new(0, 1, 0);
121 
supported_platform_triples() -> BTreeSet<TargetTriple>122     fn supported_platform_triples() -> BTreeSet<TargetTriple> {
123         BTreeSet::from([
124             TargetTriple::from_bazel("aarch64-apple-darwin".to_owned()),
125             TargetTriple::from_bazel("i686-apple-darwin".to_owned()),
126             TargetTriple::from_bazel("x86_64-unknown-linux-gnu".to_owned()),
127         ])
128     }
129 
130     #[test]
resolve_no_targeted()131     fn resolve_no_targeted() {
132         let mut deps: Select<BTreeSet<CrateDependency>> = Select::default();
133         deps.insert(
134             CrateDependency {
135                 id: CrateId::new("mock_crate_b".to_owned(), VERSION_ZERO_ONE_ZERO),
136                 target: "mock_crate_b".to_owned(),
137                 alias: None,
138             },
139             None,
140         );
141 
142         let context = CrateContext {
143             name: "mock_crate_a".to_owned(),
144             version: VERSION_ZERO_ONE_ZERO,
145             package_url: None,
146             repository: None,
147             targets: BTreeSet::default(),
148             library_target_name: None,
149             common_attrs: CommonAttributes {
150                 deps,
151                 ..CommonAttributes::default()
152             },
153             build_script_attrs: None,
154             license: None,
155             license_ids: BTreeSet::default(),
156             license_file: None,
157             additive_build_file_content: None,
158             disable_pipelining: false,
159             extra_aliased_targets: BTreeMap::default(),
160             alias_rule: None,
161             override_targets: BTreeMap::default(),
162         };
163 
164         let configurations =
165             resolve_cfg_platforms(vec![&context], &supported_platform_triples()).unwrap();
166 
167         assert_eq!(
168             configurations,
169             BTreeMap::from([
170                 // All known triples.
171                 (
172                     "aarch64-apple-darwin".to_owned(),
173                     BTreeSet::from([TargetTriple::from_bazel("aarch64-apple-darwin".to_owned())]),
174                 ),
175                 (
176                     "i686-apple-darwin".to_owned(),
177                     BTreeSet::from([TargetTriple::from_bazel("i686-apple-darwin".to_owned())]),
178                 ),
179                 (
180                     "x86_64-unknown-linux-gnu".to_owned(),
181                     BTreeSet::from([TargetTriple::from_bazel(
182                         "x86_64-unknown-linux-gnu".to_owned()
183                     )]),
184                 ),
185             ])
186         )
187     }
188 
mock_resolve_context(configuration: String) -> CrateContext189     fn mock_resolve_context(configuration: String) -> CrateContext {
190         let mut deps: Select<BTreeSet<CrateDependency>> = Select::default();
191         deps.insert(
192             CrateDependency {
193                 id: CrateId::new("mock_crate_b".to_owned(), VERSION_ZERO_ONE_ZERO),
194                 target: "mock_crate_b".to_owned(),
195                 alias: None,
196             },
197             Some(configuration),
198         );
199 
200         CrateContext {
201             name: "mock_crate_a".to_owned(),
202             version: VERSION_ZERO_ONE_ZERO,
203             package_url: None,
204             repository: None,
205             targets: BTreeSet::default(),
206             library_target_name: None,
207             common_attrs: CommonAttributes {
208                 deps,
209                 ..CommonAttributes::default()
210             },
211             build_script_attrs: None,
212             license: None,
213             license_ids: BTreeSet::default(),
214             license_file: None,
215             additive_build_file_content: None,
216             disable_pipelining: false,
217             extra_aliased_targets: BTreeMap::default(),
218             alias_rule: None,
219             override_targets: BTreeMap::default(),
220         }
221     }
222 
223     #[test]
resolve_targeted()224     fn resolve_targeted() {
225         let data = BTreeMap::from([
226             (
227                 r#"cfg(target = "x86_64-unknown-linux-gnu")"#.to_owned(),
228                 BTreeSet::from([TargetTriple::from_bazel(
229                     "x86_64-unknown-linux-gnu".to_owned(),
230                 )]),
231             ),
232             (
233                 r#"cfg(any(target_os = "macos", target_os = "ios"))"#.to_owned(),
234                 BTreeSet::from([
235                     TargetTriple::from_bazel("aarch64-apple-darwin".to_owned()),
236                     TargetTriple::from_bazel("i686-apple-darwin".to_owned()),
237                 ]),
238             ),
239         ]);
240 
241         data.into_iter().for_each(|(configuration, expectation)| {
242             let context = mock_resolve_context(configuration.clone());
243 
244             let configurations =
245                 resolve_cfg_platforms(vec![&context], &supported_platform_triples()).unwrap();
246 
247             assert_eq!(
248                 configurations,
249                 BTreeMap::from([
250                     (configuration, expectation),
251                     // All known triples.
252                     (
253                         "aarch64-apple-darwin".to_owned(),
254                         BTreeSet::from([TargetTriple::from_bazel(
255                             "aarch64-apple-darwin".to_owned()
256                         )]),
257                     ),
258                     (
259                         "i686-apple-darwin".to_owned(),
260                         BTreeSet::from([TargetTriple::from_bazel("i686-apple-darwin".to_owned())]),
261                     ),
262                     (
263                         "x86_64-unknown-linux-gnu".to_owned(),
264                         BTreeSet::from([TargetTriple::from_bazel(
265                             "x86_64-unknown-linux-gnu".to_owned()
266                         )]),
267                     ),
268                 ])
269             );
270         })
271     }
272 
273     #[test]
resolve_platforms()274     fn resolve_platforms() {
275         let configuration = r#"x86_64-unknown-linux-gnu"#.to_owned();
276         let mut deps: Select<BTreeSet<CrateDependency>> = Select::default();
277         deps.insert(
278             CrateDependency {
279                 id: CrateId::new("mock_crate_b".to_owned(), VERSION_ZERO_ONE_ZERO),
280                 target: "mock_crate_b".to_owned(),
281                 alias: None,
282             },
283             Some(configuration.clone()),
284         );
285 
286         let context = CrateContext {
287             name: "mock_crate_a".to_owned(),
288             version: VERSION_ZERO_ONE_ZERO,
289             package_url: None,
290             repository: None,
291             targets: BTreeSet::default(),
292             library_target_name: None,
293             common_attrs: CommonAttributes {
294                 deps,
295                 ..CommonAttributes::default()
296             },
297             build_script_attrs: None,
298             license: None,
299             license_ids: BTreeSet::default(),
300             license_file: None,
301             additive_build_file_content: None,
302             disable_pipelining: false,
303             extra_aliased_targets: BTreeMap::default(),
304             alias_rule: None,
305             override_targets: BTreeMap::default(),
306         };
307 
308         let configurations =
309             resolve_cfg_platforms(vec![&context], &supported_platform_triples()).unwrap();
310 
311         assert_eq!(
312             configurations,
313             BTreeMap::from([
314                 (
315                     configuration,
316                     BTreeSet::from([TargetTriple::from_bazel(
317                         "x86_64-unknown-linux-gnu".to_owned()
318                     )])
319                 ),
320                 // All known triples.
321                 (
322                     "aarch64-apple-darwin".to_owned(),
323                     BTreeSet::from([TargetTriple::from_bazel("aarch64-apple-darwin".to_owned())]),
324                 ),
325                 (
326                     "i686-apple-darwin".to_owned(),
327                     BTreeSet::from([TargetTriple::from_bazel("i686-apple-darwin".to_owned())]),
328                 ),
329                 (
330                     "x86_64-unknown-linux-gnu".to_owned(),
331                     BTreeSet::from([TargetTriple::from_bazel(
332                         "x86_64-unknown-linux-gnu".to_owned()
333                     )]),
334                 ),
335             ])
336         );
337     }
338 
339     #[test]
resolve_unsupported_targeted()340     fn resolve_unsupported_targeted() {
341         let configuration = r#"cfg(target = "x86_64-unknown-unknown")"#.to_owned();
342         let mut deps: Select<BTreeSet<CrateDependency>> = Select::default();
343         deps.insert(
344             CrateDependency {
345                 id: CrateId::new("mock_crate_b".to_owned(), VERSION_ZERO_ONE_ZERO),
346                 target: "mock_crate_b".to_owned(),
347                 alias: None,
348             },
349             Some(configuration.clone()),
350         );
351 
352         let context = CrateContext {
353             name: "mock_crate_a".to_owned(),
354             version: VERSION_ZERO_ONE_ZERO,
355             package_url: None,
356             repository: None,
357             targets: BTreeSet::default(),
358             library_target_name: None,
359             common_attrs: CommonAttributes {
360                 deps,
361                 ..CommonAttributes::default()
362             },
363             build_script_attrs: None,
364             license: None,
365             license_ids: BTreeSet::default(),
366             license_file: None,
367             additive_build_file_content: None,
368             disable_pipelining: false,
369             extra_aliased_targets: BTreeMap::default(),
370             alias_rule: None,
371             override_targets: BTreeMap::default(),
372         };
373 
374         let configurations =
375             resolve_cfg_platforms(vec![&context], &supported_platform_triples()).unwrap();
376 
377         assert_eq!(
378             configurations,
379             BTreeMap::from([
380                 (configuration, BTreeSet::new()),
381                 // All known triples.
382                 (
383                     "aarch64-apple-darwin".to_owned(),
384                     BTreeSet::from([TargetTriple::from_bazel("aarch64-apple-darwin".to_owned())]),
385                 ),
386                 (
387                     "i686-apple-darwin".to_owned(),
388                     BTreeSet::from([TargetTriple::from_bazel("i686-apple-darwin".to_owned())]),
389                 ),
390                 (
391                     "x86_64-unknown-linux-gnu".to_owned(),
392                     BTreeSet::from([TargetTriple::from_bazel(
393                         "x86_64-unknown-linux-gnu".to_owned()
394                     )]),
395                 ),
396             ])
397         );
398     }
399 }
400