diff options
author | Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> | 2024-04-16 01:54:58 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-04-16 01:54:58 +0000 |
commit | 957b2b6912f82bebfcd71ac732138c3000af7f12 (patch) | |
tree | 99212b04b37b7d961fe9382c2f649ed0c62d6eb1 | |
parent | af4bdc3b82fca69ee97941b3533e9e176dbf7919 (diff) | |
parent | b05d4a2f35622df7ca8a71a0f8ec116b0de0305b (diff) | |
download | development-957b2b6912f82bebfcd71ac732138c3000af7f12.tar.gz |
Merge changes I4fa301cf,I70ff9f6c into main
* changes:
cargo_embargo: Implement rules.mk generation
cargo_embargo: Add rules.mk generation
-rw-r--r-- | tools/cargo_embargo/src/config.rs | 31 | ||||
-rw-r--r-- | tools/cargo_embargo/src/main.rs | 307 |
2 files changed, 294 insertions, 44 deletions
diff --git a/tools/cargo_embargo/src/config.rs b/tools/cargo_embargo/src/config.rs index 519f69eac..e73feb039 100644 --- a/tools/cargo_embargo/src/config.rs +++ b/tools/cargo_embargo/src/config.rs @@ -325,6 +325,12 @@ pub struct VariantConfig { /// from the cargo metadata. #[serde(default = "default_true", skip_serializing_if = "is_true")] pub run_cargo: bool, + /// Generate an Android.bp build file for this variant if true. + #[serde(default = "default_true", skip_serializing_if = "is_true")] + pub generate_androidbp: bool, + /// Generate a rules.mk build file for this variant if true. + #[serde(default, skip_serializing_if = "is_false")] + pub generate_rulesmk: bool, } impl Default for VariantConfig { @@ -350,6 +356,8 @@ impl Default for VariantConfig { module_blocklist: Default::default(), module_visibility: Default::default(), run_cargo: true, + generate_androidbp: true, + generate_rulesmk: false, } } } @@ -365,11 +373,14 @@ pub struct PackageConfig { /// Patch file to apply after Android.bp is generated. #[serde(skip_serializing_if = "Option::is_none")] pub patch: Option<PathBuf>, + /// Patch file to apply after rules.mk is generated. + #[serde(skip_serializing_if = "Option::is_none")] + pub rulesmk_patch: Option<PathBuf>, } impl PackageConfig { /// Names of all the fields on `PackageConfig`. - const FIELD_NAMES: [&'static str; 2] = ["add_toplevel_block", "patch"]; + const FIELD_NAMES: [&'static str; 3] = ["add_toplevel_block", "patch", "rulesmk_patch"]; } /// Options that apply to everything in a package (i.e. everything associated with a particular @@ -460,11 +471,16 @@ mod tests { "add_toplevel_block": "block.bp", "device_supported": false, "force_rlib": true + }, + "rulesmk": { + "rulesmk_patch": "patches/rules.mk.patch" } }, "variants": [ {}, { + "generate_androidbp": false, + "generate_rulesmk": true, "tests": false, "features": ["feature"], "vendor_available": false, @@ -488,6 +504,8 @@ mod tests { Config { variants: vec![ VariantConfig { + generate_androidbp: true, + generate_rulesmk: false, tests: true, features: None, vendor_available: true, @@ -501,12 +519,15 @@ mod tests { ..Default::default() }, ), + ("rulesmk".to_string(), PackageVariantConfig { ..Default::default() }), ] .into_iter() .collect(), ..Default::default() }, VariantConfig { + generate_androidbp: false, + generate_rulesmk: true, tests: false, features: Some(vec!["feature".to_string()]), vendor_available: false, @@ -521,6 +542,7 @@ mod tests { ..Default::default() }, ), + ("rulesmk".to_string(), PackageVariantConfig { ..Default::default() }), ( "variant_package".to_string(), PackageVariantConfig { @@ -549,6 +571,13 @@ mod tests { ..Default::default() }, ), + ( + "rulesmk".to_string(), + PackageConfig { + rulesmk_patch: Some("patches/rules.mk.patch".into()), + ..Default::default() + }, + ), ] .into_iter() .collect(), diff --git a/tools/cargo_embargo/src/main.rs b/tools/cargo_embargo/src/main.rs index 024791222..9794b38e0 100644 --- a/tools/cargo_embargo/src/main.rs +++ b/tools/cargo_embargo/src/main.rs @@ -34,6 +34,7 @@ use crate::config::Config; use crate::config::PackageConfig; use crate::config::PackageVariantConfig; use crate::config::VariantConfig; +use anyhow::anyhow; use anyhow::bail; use anyhow::Context; use anyhow::Result; @@ -81,18 +82,38 @@ pub static RENAME_MAP: Lazy<BTreeMap<&str, &str>> = Lazy::new(|| { .collect() }); +/// This map tracks Rust crates that have special rules.mk modules that were not +/// generated automatically by this script. Examples include compiler builtins +/// and other foundational libraries. It also tracks the location of rules.mk +/// build files for crates that are not under external/rust/crates. +pub static RULESMK_RENAME_MAP: Lazy<BTreeMap<&str, &str>> = Lazy::new(|| { + [ + ("liballoc", "trusty/user/base/lib/liballoc-rust"), + ("libcompiler_builtins", "trusty/user/base/lib/libcompiler_builtins-rust"), + ("libcore", "trusty/user/base/lib/libcore-rust"), + ("libhashbrown", "trusty/user/base/lib/libhashbrown-rust"), + ("libpanic_abort", "trusty/user/base/lib/libpanic_abort-rust"), + ("libstd", "trusty/user/base/lib/libstd-rust"), + ("libstd_detect", "trusty/user/base/lib/libstd_detect-rust"), + ("libunwind", "trusty/user/base/lib/libunwind-rust"), + ] + .into_iter() + .collect() +}); + /// Given a proposed module name, returns `None` if it is blocked by the given config, or /// else apply any name overrides and returns the name to use. fn override_module_name( module_name: &str, blocklist: &[String], module_name_overrides: &BTreeMap<String, String>, + rename_map: &BTreeMap<&str, &str>, ) -> Option<String> { if blocklist.iter().any(|blocked_name| blocked_name == module_name) { None } else if let Some(overridden_name) = module_name_overrides.get(module_name) { Some(overridden_name.to_string()) - } else if let Some(renamed) = RENAME_MAP.get(module_name) { + } else if let Some(renamed) = rename_map.get(module_name) { Some(renamed.to_string()) } else { Some(module_name.to_string()) @@ -330,7 +351,7 @@ fn run_embargo(args: &Args, config_filename: &Path) -> Result<()> { } } - write_all_bp(&cfg, crates, &package_out_files) + write_all_build_files(&cfg, crates, &package_out_files) } /// Input is indexed by variant, then all crates for that variant. @@ -351,7 +372,7 @@ fn group_by_package(crates: Vec<Vec<Crate>>) -> BTreeMap<PathBuf, Vec<Vec<Crate> module_by_package } -fn write_all_bp( +fn write_all_build_files( cfg: &Config, crates: Vec<Vec<Crate>>, package_out_files: &BTreeMap<String, Vec<Vec<PathBuf>>>, @@ -361,10 +382,10 @@ fn write_all_bp( let num_variants = cfg.variants.len(); let empty_package_out_files = vec![vec![]; num_variants]; - // Write an Android.bp file per package. + // Write a build file per package. for (package_dir, crates) in module_by_package { let package_name = &crates.iter().flatten().next().unwrap().package_name; - write_android_bp( + write_build_files( cfg, package_name, package_dir, @@ -491,10 +512,36 @@ fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { Ok(CargoOutput { cargo_metadata, cargo_out }) } -/// Create the Android.bp file for `package_dir`. +/// Read and return license and other header lines from a build file. +/// +/// Skips initial comment lines, then returns all lines before the first line +/// starting with `rust_`, `genrule {`, or `LOCAL_DIR`. +/// +/// If `path` could not be read, return a placeholder license TODO line. +fn read_license_header(path: &Path) -> Result<String> { + // Keep the old license header. + match std::fs::read_to_string(path) { + Ok(s) => Ok(s + .lines() + .skip_while(|l| l.starts_with("//") || l.starts_with('#')) + .take_while(|l| { + !l.starts_with("rust_") + && !l.starts_with("genrule {") + && !l.starts_with("LOCAL_DIR") + }) + .collect::<Vec<&str>>() + .join("\n")), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + Ok("// DO NOT SUBMIT: Add license before submitting.\n".to_string()) + } + Err(e) => Err(anyhow!("error when reading {path:?}: {e}")), + } +} + +/// Create the build file for `package_dir`. /// /// `crates` and `out_files` are both indexed by variant. -fn write_android_bp( +fn write_build_files( cfg: &Config, package_name: &str, package_dir: PathBuf, @@ -502,23 +549,9 @@ fn write_android_bp( out_files: &[Vec<PathBuf>], ) -> Result<()> { assert_eq!(crates.len(), out_files.len()); - let bp_path = package_dir.join("Android.bp"); - - // Keep the old license header. - let license_section = match std::fs::read_to_string(&bp_path) { - Ok(s) => s - .lines() - .skip_while(|l| l.starts_with("//")) - .take_while(|l| !l.starts_with("rust_") && !l.starts_with("genrule {")) - .collect::<Vec<&str>>() - .join("\n"), - Err(e) if e.kind() == std::io::ErrorKind::NotFound => { - "// DO NOT SUBMIT: Add license before submitting.\n".to_string() - } - Err(e) => bail!("error when reading {bp_path:?}: {e}"), - }; let mut bp_contents = String::new(); + let mut mk_contents = String::new(); for (variant_index, variant_config) in cfg.variants.iter().enumerate() { let variant_crates = &crates[variant_index]; let def = PackageVariantConfig::default(); @@ -538,13 +571,24 @@ fn write_android_bp( } } - bp_contents += &generate_android_bp( - variant_config, - package_cfg, - package_name, - variant_crates, - &out_files[variant_index], - )?; + if variant_config.generate_androidbp { + bp_contents += &generate_android_bp( + variant_config, + package_cfg, + package_name, + variant_crates, + &out_files[variant_index], + )?; + } + if variant_config.generate_rulesmk { + mk_contents += &generate_rules_mk( + variant_config, + package_cfg, + package_name, + variant_crates, + &out_files[variant_index], + )?; + } } let def = PackageConfig::default(); @@ -555,14 +599,29 @@ fn write_android_bp( bp_contents += "\n"; } if !bp_contents.is_empty() { + let output_path = package_dir.join("Android.bp"); let bp_contents = "// This file is generated by cargo_embargo.\n".to_owned() + "// Do not modify this file after the first \"rust_*\" or \"genrule\" module\n" + "// because the changes will be overridden on upgrade.\n" + "// Content before the first \"rust_*\" or \"genrule\" module is preserved.\n\n" - + license_section.trim() + + read_license_header(&output_path)?.trim() + "\n" + &bp_contents; - write_format_android_bp(&bp_path, &bp_contents, package_cfg.patch.as_deref())?; + write_format_android_bp(&output_path, &bp_contents, package_cfg.patch.as_deref())?; + } + if !mk_contents.is_empty() { + let output_path = package_dir.join("rules.mk"); + let mk_contents = "# This file is generated by cargo_embargo.\n".to_owned() + + "# Do not modify this file after the LOCAL_DIR line\n" + + "# because the changes will be overridden on upgrade.\n" + + "# Content before the first line starting with LOCAL_DIR is preserved.\n" + + read_license_header(&output_path)?.trim() + + "\n" + + &mk_contents; + File::create(&output_path)?.write_all(mk_contents.as_bytes())?; + if let Some(patch) = package_cfg.rulesmk_patch.as_deref() { + apply_patch_file(&output_path, patch)?; + } } Ok(()) @@ -592,6 +651,7 @@ fn generate_android_bp( &format!("copy_{}_build_out", package_name), &cfg.module_blocklist, &cfg.module_name_overrides, + &RENAME_MAP, ) { m.props.set("name", module_name.clone()); m.props.set("srcs", vec!["out/*"]); @@ -632,6 +692,65 @@ fn generate_android_bp( Ok(bp_contents) } +/// Generates and returns a Trusty rules.mk file for the given set of crates. +fn generate_rules_mk( + cfg: &VariantConfig, + package_cfg: &PackageVariantConfig, + package_name: &str, + crates: &[Crate], + out_files: &[PathBuf], +) -> Result<String> { + let out_files = if package_cfg.copy_out && !out_files.is_empty() { + out_files.iter().map(|f| f.file_name().unwrap().to_str().unwrap().to_string()).collect() + } else { + vec![] + }; + + let crates: Vec<_> = crates + .iter() + .filter(|c| { + if c.types.contains(&CrateType::Bin) { + eprintln!("WARNING: skipped generation of rules.mk for binary crate: {}", c.name); + false + } else if c.types.iter().any(|t| t.is_test()) { + // Test build file generation is not yet implemented + eprintln!("WARNING: skipped generation of rules.mk for test crate: {}", c.name); + false + } else { + true + } + }) + .collect(); + let [crate_] = &crates[..] else { + bail!( + "Expected exactly one library crate for package {package_name} when generating \ + rules.mk, found: {crates:?}" + ); + }; + crate_to_rulesmk(crate_, cfg, package_cfg, &out_files).with_context(|| { + format!( + "failed to generate rules.mk for crate \"{}\" with package name \"{}\"", + crate_.name, crate_.package_name + ) + }) +} + +/// Apply patch from `patch_path` to file `output_path`. +/// +/// Warns but still returns ok if the patch did not cleanly apply, +fn apply_patch_file(output_path: &Path, patch_path: &Path) -> Result<()> { + let patch_output = Command::new("patch") + .arg("-s") + .arg(output_path) + .arg(patch_path) + .output() + .context("Running patch")?; + if !patch_output.status.success() { + eprintln!("WARNING: failed to apply patch {:?}", patch_path); + } + Ok(()) +} + /// Writes the given contents to the given `Android.bp` file, formats it with `bpfmt`, and applies /// the patch if there is one. fn write_format_android_bp( @@ -652,15 +771,7 @@ fn write_format_android_bp( } if let Some(patch_path) = patch_path { - let patch_output = Command::new("patch") - .arg("-s") - .arg(bp_path) - .arg(patch_path) - .output() - .context("Running patch")?; - if !patch_output.status.success() { - eprintln!("WARNING: failed to apply patch {:?}", patch_path); - } + apply_patch_file(bp_path, patch_path)?; // Re-run bpfmt after the patch so let bpfmt_output = Command::new("bpfmt") .arg("-w") @@ -733,9 +844,12 @@ fn crate_to_bp_modules( }; let mut m = BpModule::new(module_type.clone()); - let Some(module_name) = - override_module_name(&module_name, &cfg.module_blocklist, &cfg.module_name_overrides) - else { + let Some(module_name) = override_module_name( + &module_name, + &cfg.module_blocklist, + &cfg.module_name_overrides, + &RENAME_MAP, + ) else { continue; }; if matches!( @@ -830,6 +944,7 @@ fn crate_to_bp_modules( &module_name, &package_cfg.dep_blocklist, &cfg.module_name_overrides, + &RENAME_MAP, ) { result.push(module_name); } @@ -908,6 +1023,112 @@ fn crate_to_bp_modules( Ok(modules) } +/// Convert a `Crate` into a rules.mk file. +/// +/// If messy business logic is necessary, prefer putting it here. +fn crate_to_rulesmk( + crate_: &Crate, + cfg: &VariantConfig, + package_cfg: &PackageVariantConfig, + out_files: &[String], +) -> Result<String> { + let mut contents = String::new(); + + contents += "LOCAL_DIR := $(GET_LOCAL_DIR)\n"; + contents += "MODULE := $(LOCAL_DIR)\n"; + contents += &format!("MODULE_CRATE_NAME := {}\n", crate_.name); + + if !crate_.types.is_empty() { + contents += "MODULE_RUST_CRATE_TYPES :="; + for crate_type in &crate_.types { + contents += match crate_type { + CrateType::Lib => " rlib", + CrateType::StaticLib => " staticlib", + CrateType::ProcMacro => " proc-macro", + _ => bail!("Cannot generate rules.mk for crate type {crate_type:?}"), + }; + } + contents += "\n"; + } + + contents += &format!("MODULE_SRCS := $(LOCAL_DIR)/{}\n", crate_.main_src.display()); + + if !out_files.is_empty() { + contents += &format!("OUT_FILES := {}\n", out_files.join(" ")); + contents += "BUILD_OUT_FILES := $(addprefix $(call TOBUILDDIR,$(MODULE))/,$(OUT_FILES))\n"; + contents += "$(BUILD_OUT_FILES): $(call TOBUILDDIR,$(MODULE))/% : $(MODULE)/out/%\n"; + contents += "\t@echo copying $^ to $@\n"; + contents += "\t@$(MKDIR)\n"; + contents += "\t@cp $^ $@\n\n"; + contents += "MODULE_RUST_ENV += OUT_DIR=$(call TOBUILDDIR,$(MODULE))\n\n"; + contents += "MODULE_SRCDEPS += $(BUILD_OUT_FILES)\n\n"; + contents += "OUT_FILES :=\n"; + contents += "BUILD_OUT_FILES :=\n"; + contents += "\n"; + } + + // crate dependencies without lib- prefix + let mut library_deps: Vec<_> = crate_.externs.iter().map(|dep| dep.lib_name.clone()).collect(); + if package_cfg.no_std { + contents += "MODULE_ADD_IMPLICIT_DEPS := false\n"; + library_deps.push("compiler_builtins".to_string()); + library_deps.push("core".to_string()); + if package_cfg.alloc { + library_deps.push("alloc".to_string()); + } + } + + contents += &format!("MODULE_RUST_EDITION := {}\n", crate_.edition); + + let mut flags = Vec::new(); + if !crate_.cap_lints.is_empty() { + flags.push(crate_.cap_lints.clone()); + } + flags.extend(crate_.codegens.iter().map(|codegen| format!("-C {}", codegen))); + flags.extend(crate_.features.iter().map(|feat| format!("--cfg 'feature=\"{feat}\"'"))); + flags.extend( + crate_ + .cfgs + .iter() + .filter(|crate_cfg| !cfg.cfg_blocklist.contains(crate_cfg)) + .chain(cfg.extra_cfg.iter()) + .map(|cfg| format!("--cfg '{cfg}'")), + ); + if !flags.is_empty() { + contents += "MODULE_RUSTFLAGS += \\\n\t"; + contents += &flags.join(" \\\n\t"); + contents += "\n\n"; + } + + let mut library_deps: Vec<String> = library_deps + .into_iter() + .flat_map(|dep| { + override_module_name( + &format!("lib{dep}"), + &package_cfg.dep_blocklist, + &cfg.module_name_overrides, + &RULESMK_RENAME_MAP, + ) + }) + .map(|dep| { + // Rewrite dependency name to module path for Trusty build system + if let Some(dep) = dep.strip_prefix("lib") { + format!("external/rust/crates/{dep}") + } else { + dep + } + }) + .collect(); + library_deps.sort(); + library_deps.dedup(); + contents += "MODULE_LIBRARY_DEPS := \\\n\t"; + contents += &library_deps.join(" \\\n\t"); + contents += "\n\n"; + + contents += "include make/library.mk\n"; + Ok(contents) +} + #[cfg(test)] mod tests { use super::*; |