diff options
-rw-r--r-- | tools/cargo_embargo/src/config.rs | 31 | ||||
-rw-r--r-- | tools/cargo_embargo/src/main.rs | 181 |
2 files changed, 172 insertions, 40 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 bf55ac217..c45a78237 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; @@ -331,7 +332,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. @@ -352,7 +353,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>>>, @@ -362,10 +363,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, @@ -499,10 +500,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, @@ -510,23 +537,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(); @@ -546,13 +559,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(); @@ -563,14 +587,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(()) @@ -640,6 +679,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 extra_srcs = 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, &extra_srcs).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( @@ -660,15 +758,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") @@ -916,6 +1006,19 @@ 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, + _extra_srcs: &[String], +) -> Result<String> { + // TODO: Implement rules generation + Ok(String::new()) +} + #[cfg(test)] mod tests { use super::*; |