1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Code for reading configuration json files, usually called `cargo_embargo.json`.
16 //!
17 //! A single configuration file may cover several Rust packages under its directory tree, and may
18 //! have multiple "variants". A variant is a particular configuration for building a set of
19 //! packages, such as what feature flags to enable. Multiple variants are most often used to build
20 //! both a std variant of a library and a no_std variant. Each variant generates one or more modules
21 //! for each package, usually distinguished by a suffix.
22 //!
23 //! The [`Config`] struct has a map of `PackageConfig`s keyed by package name (for options that
24 //! apply across all variants of a package), and a vector of `VariantConfig`s. There must be at
25 //! least one variant for the configuration to generate any output. Each `VariantConfig` has the
26 //! options that apply to that variant across all packages, and then a map of
27 //! `PackageVariantConfig`s for options specific to a particular package of the variant.
28
29 use anyhow::{bail, Context, Result};
30 use serde::{Deserialize, Serialize};
31 use serde_json::{Map, Value};
32 use std::collections::BTreeMap;
33 use std::path::{Path, PathBuf};
34
default_apex_available() -> Vec<String>35 fn default_apex_available() -> Vec<String> {
36 vec!["//apex_available:platform".to_string(), "//apex_available:anyapex".to_string()]
37 }
38
is_default_apex_available(apex_available: &[String]) -> bool39 fn is_default_apex_available(apex_available: &[String]) -> bool {
40 apex_available == default_apex_available()
41 }
42
default_true() -> bool43 fn default_true() -> bool {
44 true
45 }
46
is_true(value: &bool) -> bool47 fn is_true(value: &bool) -> bool {
48 *value
49 }
50
is_false(value: &bool) -> bool51 fn is_false(value: &bool) -> bool {
52 !*value
53 }
54
55 /// Options that apply to everything.
56 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
57 #[serde(deny_unknown_fields)]
58 pub struct Config {
59 pub variants: Vec<VariantConfig>,
60 /// Package specific config options across all variants.
61 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
62 pub package: BTreeMap<String, PackageConfig>,
63 }
64
65 /// Inserts entries from `defaults` into `variant` if neither it nor `ignored_fields` contain
66 /// matching keys.
add_defaults_to_variant( variant: &mut Map<String, Value>, defaults: &Map<String, Value>, ignored_fields: &[&str], )67 fn add_defaults_to_variant(
68 variant: &mut Map<String, Value>,
69 defaults: &Map<String, Value>,
70 ignored_fields: &[&str],
71 ) {
72 for (key, value) in defaults {
73 if !ignored_fields.contains(&key.as_str()) && !variant.contains_key(key) {
74 variant.insert(key.to_owned(), value.to_owned());
75 }
76 }
77 }
78
79 impl Config {
80 /// Names of all fields in [`Config`] other than `variants` (which is treated specially).
81 const FIELD_NAMES: [&'static str; 1] = ["package"];
82
83 /// Parses an instance of this config from the given JSON file.
from_file(filename: &Path) -> Result<Self>84 pub fn from_file(filename: &Path) -> Result<Self> {
85 let json_string = std::fs::read_to_string(filename)
86 .with_context(|| format!("failed to read file: {:?}", filename))?;
87 Self::from_json_str(&json_string)
88 }
89
90 /// Parses an instance of this config from a string of JSON.
from_json_str(json_str: &str) -> Result<Self>91 pub fn from_json_str(json_str: &str) -> Result<Self> {
92 // Ignore comments.
93 let json_str: String =
94 json_str.lines().filter(|l| !l.trim_start().starts_with("//")).collect();
95 // First parse into untyped map.
96 let mut config: Map<String, Value> =
97 serde_json::from_str(&json_str).context("failed to parse config")?;
98
99 // Flatten variants. First, get the variants from the config file.
100 let mut variants = match config.remove("variants") {
101 Some(Value::Array(v)) => v,
102 Some(_) => bail!("Failed to parse config: variants is not an array"),
103 None => {
104 // There are no variants, so just put everything into a single variant.
105 vec![Value::Object(Map::new())]
106 }
107 };
108 // Set default values in variants from top-level config.
109 for variant in &mut variants {
110 let variant = variant
111 .as_object_mut()
112 .context("Failed to parse config: variant is not an object")?;
113 add_defaults_to_variant(variant, &config, &Config::FIELD_NAMES);
114
115 if let Some(packages) = config.get("package") {
116 // Copy package entries across.
117 let variant_packages = variant
118 .entry("package")
119 .or_insert_with(|| Map::new().into())
120 .as_object_mut()
121 .context("Failed to parse config: variant package is not an object")?;
122 for (package_name, package_config) in packages
123 .as_object()
124 .context("Failed to parse config: package is not an object")?
125 {
126 let variant_package = variant_packages
127 .entry(package_name)
128 .or_insert_with(|| Map::new().into())
129 .as_object_mut()
130 .context(
131 "Failed to parse config: variant package config is not an object",
132 )?;
133 add_defaults_to_variant(
134 variant_package,
135 package_config
136 .as_object()
137 .context("Failed to parse config: package is not an object")?,
138 &PackageConfig::FIELD_NAMES,
139 );
140 }
141 }
142 }
143 // Remove other entries from the top-level config, and put variants back.
144 config.retain(|key, _| Self::FIELD_NAMES.contains(&key.as_str()));
145 if let Some(package) = config.get_mut("package") {
146 for value in package
147 .as_object_mut()
148 .context("Failed to parse config: package is not an object")?
149 .values_mut()
150 {
151 let package_config = value
152 .as_object_mut()
153 .context("Failed to parse config: package is not an object")?;
154 package_config.retain(|key, _| PackageConfig::FIELD_NAMES.contains(&key.as_str()))
155 }
156 }
157 config.insert("variants".to_string(), Value::Array(variants));
158
159 // Parse into `Config` struct.
160 serde_json::from_value(Value::Object(config)).context("failed to parse config")
161 }
162
163 /// Serializes an instance of this config to a string of pretty-printed JSON.
to_json_string(&self) -> Result<String>164 pub fn to_json_string(&self) -> Result<String> {
165 // First convert to an untyped map.
166 let Value::Object(mut config) = serde_json::to_value(self)? else {
167 panic!("Config wasn't a map.");
168 };
169
170 // Factor out common options which are set for all variants.
171 let Value::Array(mut variants) = config.remove("variants").unwrap() else {
172 panic!("variants wasn't an array.")
173 };
174 let mut packages = if let Some(Value::Object(packages)) = config.remove("package") {
175 packages
176 } else {
177 Map::new()
178 };
179 for (key, value) in variants[0].as_object().unwrap() {
180 if key == "package" {
181 for (package_name, package_config) in value.as_object().unwrap() {
182 for (package_key, package_value) in package_config.as_object().unwrap() {
183 // Check whether all other variants have the same entry for the same package.
184 if variants[1..variants.len()].iter().all(|variant| {
185 if let Some(Value::Object(variant_packages)) =
186 variant.as_object().unwrap().get("package")
187 {
188 if let Some(Value::Object(variant_package_config)) =
189 variant_packages.get(package_name)
190 {
191 return variant_package_config.get(package_key)
192 == Some(package_value);
193 }
194 }
195 false
196 }) {
197 packages
198 .entry(package_name)
199 .or_insert_with(|| Map::new().into())
200 .as_object_mut()
201 .unwrap()
202 .insert(package_key.to_owned(), package_value.to_owned());
203 }
204 }
205 }
206 } else {
207 // Check whether all the other variants have the same entry.
208 if variants[1..variants.len()]
209 .iter()
210 .all(|variant| variant.as_object().unwrap().get(key) == Some(value))
211 {
212 // Add it to the top-level config.
213 config.insert(key.to_owned(), value.to_owned());
214 }
215 }
216 }
217 // Remove factored out common options from all variants.
218 for key in config.keys() {
219 for variant in &mut variants {
220 variant.as_object_mut().unwrap().remove(key);
221 }
222 }
223 // Likewise, remove package options factored out from variants.
224 for (package_name, package_config) in &packages {
225 for package_key in package_config.as_object().unwrap().keys() {
226 for variant in &mut variants {
227 if let Some(Value::Object(variant_packages)) = variant.get_mut("package") {
228 if let Some(Value::Object(variant_package_config)) =
229 variant_packages.get_mut(package_name)
230 {
231 variant_package_config.remove(package_key);
232 }
233 }
234 }
235 }
236 }
237 // Remove any variant packages which are now empty.
238 for variant in &mut variants {
239 if let Some(Value::Object(variant_packages)) = variant.get_mut("package") {
240 variant_packages
241 .retain(|_, package_config| !package_config.as_object().unwrap().is_empty());
242 if variant_packages.is_empty() {
243 variant.as_object_mut().unwrap().remove("package");
244 }
245 }
246 }
247 // Put packages and variants back into the top-level config.
248 if variants.len() > 1 || !variants[0].as_object().unwrap().is_empty() {
249 config.insert("variants".to_string(), Value::Array(variants));
250 }
251 if !packages.is_empty() {
252 config.insert("package".to_string(), Value::Object(packages));
253 }
254
255 // Serialise the map into a JSON string.
256 serde_json::to_string_pretty(&config).context("failed to serialize config")
257 }
258 }
259
260 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
261 #[serde(deny_unknown_fields)]
262 pub struct VariantConfig {
263 /// Whether to output `rust_test` modules.
264 #[serde(default, skip_serializing_if = "is_false")]
265 pub tests: bool,
266 /// Set of features to enable. If not set, uses the default crate features.
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub features: Option<Vec<String>>,
269 /// Whether to build with `--workspace`.
270 #[serde(default, skip_serializing_if = "is_false")]
271 pub workspace: bool,
272 /// When workspace is enabled, list of `--exclude` crates.
273 #[serde(default, skip_serializing_if = "Vec::is_empty")]
274 pub workspace_excludes: Vec<String>,
275 /// Value to use for every generated module's `defaults` field.
276 #[serde(skip_serializing_if = "Option::is_none")]
277 pub global_defaults: Option<String>,
278 /// Value to use for every generated library module's `apex_available` field.
279 #[serde(default = "default_apex_available", skip_serializing_if = "is_default_apex_available")]
280 pub apex_available: Vec<String>,
281 /// Value to use for every generated library module's `native_bridge_supported` field.
282 #[serde(default, skip_serializing_if = "is_false")]
283 pub native_bridge_supported: bool,
284 /// Value to use for every generated library module's `product_available` field.
285 #[serde(default = "default_true", skip_serializing_if = "is_true")]
286 pub product_available: bool,
287 /// Value to use for every generated library module's `ramdisk_available` field.
288 #[serde(default, skip_serializing_if = "is_false")]
289 pub ramdisk_available: bool,
290 /// Value to use for every generated library module's `recovery_available` field.
291 #[serde(default, skip_serializing_if = "is_false")]
292 pub recovery_available: bool,
293 /// Value to use for every generated library module's `vendor_available` field.
294 #[serde(default = "default_true", skip_serializing_if = "is_true")]
295 pub vendor_available: bool,
296 /// Value to use for every generated library module's `vendor_ramdisk_available` field.
297 #[serde(default, skip_serializing_if = "is_false")]
298 pub vendor_ramdisk_available: bool,
299 /// Minimum SDK version.
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub min_sdk_version: Option<String>,
302 /// Map of renames for modules. For example, if a "libfoo" would be generated and there is an
303 /// entry ("libfoo", "libbar"), the generated module will be called "libbar" instead.
304 ///
305 /// Also, affects references to dependencies (e.g. in a "static_libs" list), even those outside
306 /// the project being processed.
307 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
308 pub module_name_overrides: BTreeMap<String, String>,
309 /// Package specific config options.
310 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
311 pub package: BTreeMap<String, PackageVariantConfig>,
312 /// `cfg` flags in this list will not be included.
313 #[serde(default, skip_serializing_if = "Vec::is_empty")]
314 pub cfg_blocklist: Vec<String>,
315 /// Extra `cfg` flags to enable in output modules.
316 #[serde(default, skip_serializing_if = "Vec::is_empty")]
317 pub extra_cfg: Vec<String>,
318 /// Modules in this list will not be generated.
319 #[serde(default, skip_serializing_if = "Vec::is_empty")]
320 pub module_blocklist: Vec<String>,
321 /// Modules name => Soong "visibility" property.
322 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
323 pub module_visibility: BTreeMap<String, Vec<String>>,
324 /// Whether to run the cargo build and parse its output, rather than just figuring things out
325 /// from the cargo metadata.
326 #[serde(default = "default_true", skip_serializing_if = "is_true")]
327 pub run_cargo: bool,
328 /// Generate an Android.bp build file for this variant if true.
329 #[serde(default = "default_true", skip_serializing_if = "is_true")]
330 pub generate_androidbp: bool,
331 /// Generate a rules.mk build file for this variant if true.
332 #[serde(default, skip_serializing_if = "is_false")]
333 pub generate_rulesmk: bool,
334 }
335
336 impl Default for VariantConfig {
default() -> Self337 fn default() -> Self {
338 Self {
339 tests: false,
340 features: Default::default(),
341 workspace: false,
342 workspace_excludes: Default::default(),
343 global_defaults: None,
344 apex_available: default_apex_available(),
345 native_bridge_supported: false,
346 product_available: true,
347 ramdisk_available: false,
348 recovery_available: false,
349 vendor_available: true,
350 vendor_ramdisk_available: false,
351 min_sdk_version: None,
352 module_name_overrides: Default::default(),
353 package: Default::default(),
354 cfg_blocklist: Default::default(),
355 extra_cfg: Default::default(),
356 module_blocklist: Default::default(),
357 module_visibility: Default::default(),
358 run_cargo: true,
359 generate_androidbp: true,
360 generate_rulesmk: false,
361 }
362 }
363 }
364
365 /// Options that apply to everything in a package (i.e. everything associated with a particular
366 /// Cargo.toml file), for all variants.
367 #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
368 #[serde(deny_unknown_fields)]
369 pub struct PackageConfig {
370 /// File with content to append to the end of the generated Android.bp.
371 #[serde(skip_serializing_if = "Option::is_none")]
372 pub add_toplevel_block: Option<PathBuf>,
373 /// Patch file to apply after Android.bp is generated.
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub patch: Option<PathBuf>,
376 /// Patch file to apply after rules.mk is generated.
377 #[serde(skip_serializing_if = "Option::is_none")]
378 pub rulesmk_patch: Option<PathBuf>,
379 }
380
381 impl PackageConfig {
382 /// Names of all the fields on `PackageConfig`.
383 const FIELD_NAMES: [&'static str; 3] = ["add_toplevel_block", "patch", "rulesmk_patch"];
384 }
385
386 /// Options that apply to everything in a package (i.e. everything associated with a particular
387 /// Cargo.toml file), for a particular variant.
388 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
389 #[serde(deny_unknown_fields)]
390 pub struct PackageVariantConfig {
391 /// Link against `alloc`. Only valid if `no_std` is also true.
392 #[serde(default, skip_serializing_if = "is_false")]
393 pub alloc: bool,
394 /// Whether to compile for device. Defaults to true.
395 #[serde(default = "default_true", skip_serializing_if = "is_true")]
396 pub device_supported: bool,
397 /// Whether to compile for host. Defaults to true.
398 #[serde(default = "default_true", skip_serializing_if = "is_true")]
399 pub host_supported: bool,
400 /// Add a `compile_multilib: "first"` property to host modules.
401 #[serde(default, skip_serializing_if = "is_false")]
402 pub host_first_multilib: bool,
403 /// Generate "rust_library_rlib" instead of "rust_library".
404 #[serde(default, skip_serializing_if = "is_false")]
405 pub force_rlib: bool,
406 /// Whether to disable "unit_test" for "rust_test" modules.
407 // TODO: Should probably be a list of modules or crates. A package might have a mix of unit and
408 // integration tests.
409 #[serde(default, skip_serializing_if = "is_false")]
410 pub no_presubmit: bool,
411 /// File with content to append to the end of each generated module.
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub add_module_block: Option<PathBuf>,
414 /// Modules in this list will not be added as dependencies of generated modules.
415 #[serde(default, skip_serializing_if = "Vec::is_empty")]
416 pub dep_blocklist: Vec<String>,
417 /// Don't link against `std`, only `core`.
418 #[serde(default, skip_serializing_if = "is_false")]
419 pub no_std: bool,
420 /// Copy build.rs output to ./out/* and add a genrule to copy ./out/* to genrule output.
421 /// For crates with code pattern:
422 /// include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))
423 #[serde(default, skip_serializing_if = "is_false")]
424 pub copy_out: bool,
425 /// Add the given files to the given tests' `data` property. The key is the test source filename
426 /// relative to the crate root.
427 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
428 pub test_data: BTreeMap<String, Vec<String>>,
429 /// Static libraries in this list will instead be added as whole_static_libs.
430 #[serde(default, skip_serializing_if = "Vec::is_empty")]
431 pub whole_static_libs: Vec<String>,
432 /// Directories with headers to export for C usage.
433 #[serde(default, skip_serializing_if = "Vec::is_empty")]
434 pub exported_c_header_dir: Vec<PathBuf>,
435 }
436
437 impl Default for PackageVariantConfig {
default() -> Self438 fn default() -> Self {
439 Self {
440 alloc: false,
441 device_supported: true,
442 host_supported: true,
443 host_first_multilib: false,
444 force_rlib: false,
445 no_presubmit: false,
446 add_module_block: None,
447 dep_blocklist: Default::default(),
448 no_std: false,
449 copy_out: false,
450 test_data: Default::default(),
451 whole_static_libs: Default::default(),
452 exported_c_header_dir: Default::default(),
453 }
454 }
455 }
456
457 #[cfg(test)]
458 mod tests {
459 use super::*;
460
461 #[test]
variant_config()462 fn variant_config() {
463 let config = Config::from_json_str(
464 r#"{
465 "tests": true,
466 "package": {
467 "argh": {
468 "patch": "patches/Android.bp.patch"
469 },
470 "another": {
471 "add_toplevel_block": "block.bp",
472 "device_supported": false,
473 "force_rlib": true
474 },
475 "rulesmk": {
476 "rulesmk_patch": "patches/rules.mk.patch"
477 }
478 },
479 "variants": [
480 {},
481 {
482 "generate_androidbp": false,
483 "generate_rulesmk": true,
484 "tests": false,
485 "features": ["feature"],
486 "vendor_available": false,
487 "package": {
488 "another": {
489 "alloc": false,
490 "force_rlib": false
491 },
492 "variant_package": {
493 "add_module_block": "variant_module_block.bp"
494 }
495 }
496 }
497 ]
498 }"#,
499 )
500 .unwrap();
501
502 assert_eq!(
503 config,
504 Config {
505 variants: vec![
506 VariantConfig {
507 generate_androidbp: true,
508 generate_rulesmk: false,
509 tests: true,
510 features: None,
511 vendor_available: true,
512 package: [
513 ("argh".to_string(), PackageVariantConfig { ..Default::default() }),
514 (
515 "another".to_string(),
516 PackageVariantConfig {
517 device_supported: false,
518 force_rlib: true,
519 ..Default::default()
520 },
521 ),
522 ("rulesmk".to_string(), PackageVariantConfig { ..Default::default() }),
523 ]
524 .into_iter()
525 .collect(),
526 ..Default::default()
527 },
528 VariantConfig {
529 generate_androidbp: false,
530 generate_rulesmk: true,
531 tests: false,
532 features: Some(vec!["feature".to_string()]),
533 vendor_available: false,
534 package: [
535 ("argh".to_string(), PackageVariantConfig { ..Default::default() }),
536 (
537 "another".to_string(),
538 PackageVariantConfig {
539 alloc: false,
540 device_supported: false,
541 force_rlib: false,
542 ..Default::default()
543 },
544 ),
545 ("rulesmk".to_string(), PackageVariantConfig { ..Default::default() }),
546 (
547 "variant_package".to_string(),
548 PackageVariantConfig {
549 add_module_block: Some("variant_module_block.bp".into()),
550 ..Default::default()
551 },
552 ),
553 ]
554 .into_iter()
555 .collect(),
556 ..Default::default()
557 },
558 ],
559 package: [
560 (
561 "argh".to_string(),
562 PackageConfig {
563 patch: Some("patches/Android.bp.patch".into()),
564 ..Default::default()
565 },
566 ),
567 (
568 "another".to_string(),
569 PackageConfig {
570 add_toplevel_block: Some("block.bp".into()),
571 ..Default::default()
572 },
573 ),
574 (
575 "rulesmk".to_string(),
576 PackageConfig {
577 rulesmk_patch: Some("patches/rules.mk.patch".into()),
578 ..Default::default()
579 },
580 ),
581 ]
582 .into_iter()
583 .collect(),
584 }
585 );
586 }
587
588 /// Tests that variant configuration options are factored out to the top level where possible.
589 #[test]
factor_variants()590 fn factor_variants() {
591 let config = Config {
592 variants: vec![
593 VariantConfig {
594 features: Some(vec![]),
595 tests: true,
596 vendor_available: false,
597 package: [(
598 "argh".to_string(),
599 PackageVariantConfig {
600 dep_blocklist: vec!["bad_dep".to_string()],
601 ..Default::default()
602 },
603 )]
604 .into_iter()
605 .collect(),
606 ..Default::default()
607 },
608 VariantConfig {
609 features: Some(vec![]),
610 tests: true,
611 product_available: false,
612 module_name_overrides: [("argh".to_string(), "argh_nostd".to_string())]
613 .into_iter()
614 .collect(),
615 vendor_available: false,
616 package: [(
617 "argh".to_string(),
618 PackageVariantConfig {
619 dep_blocklist: vec!["bad_dep".to_string()],
620 no_std: true,
621 ..Default::default()
622 },
623 )]
624 .into_iter()
625 .collect(),
626 ..Default::default()
627 },
628 ],
629 package: [(
630 "argh".to_string(),
631 PackageConfig { add_toplevel_block: Some("block.bp".into()), ..Default::default() },
632 )]
633 .into_iter()
634 .collect(),
635 };
636
637 assert_eq!(
638 config.to_json_string().unwrap(),
639 r#"{
640 "features": [],
641 "package": {
642 "argh": {
643 "add_toplevel_block": "block.bp",
644 "dep_blocklist": [
645 "bad_dep"
646 ]
647 }
648 },
649 "tests": true,
650 "variants": [
651 {},
652 {
653 "module_name_overrides": {
654 "argh": "argh_nostd"
655 },
656 "package": {
657 "argh": {
658 "no_std": true
659 }
660 },
661 "product_available": false
662 }
663 ],
664 "vendor_available": false
665 }"#
666 );
667 }
668
669 #[test]
factor_trivial_variant()670 fn factor_trivial_variant() {
671 let config = Config {
672 variants: vec![VariantConfig {
673 tests: true,
674 package: [("argh".to_string(), Default::default())].into_iter().collect(),
675 ..Default::default()
676 }],
677 package: Default::default(),
678 };
679
680 assert_eq!(
681 config.to_json_string().unwrap(),
682 r#"{
683 "tests": true
684 }"#
685 );
686 }
687 }
688