summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Crane <cranes@google.com>2024-04-11 00:44:54 +0000
committerStephen Crane <cranes@google.com>2024-04-12 22:46:27 +0000
commit06afe25b6c1ea3ea76eadeb7e590a829038d5084 (patch)
tree1ad48719e7bc7d3a51bf9de2aafda7a9c5c96700
parent8471b71856b7b015c89df8b470023aea685f7753 (diff)
downloaddevelopment-06afe25b6c1ea3ea76eadeb7e590a829038d5084.tar.gz
cargo_embargo: Add rules.mk generation
Adds Trusty makefile rules generation support to cargo embargo. This change is just the infrastructure and configuration support for generating these files, the details of generation will be implemented in the next change. Test: m cargo_embargo Test: atest cargo_embargo.test Bug: 307615277 Change-Id: I70ff9f6cfea611d40e1528a958f347538a842b10
-rw-r--r--tools/cargo_embargo/src/config.rs31
-rw-r--r--tools/cargo_embargo/src/main.rs181
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::*;