From 11494def272e812bff68bd9779ed80eb7ae8b06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 20 Jun 2024 08:54:29 +0200 Subject: [PATCH 1/4] Import Android.bp for unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Import idmap2's Android.bp from AOSP. Signed-off-by: Mårten Kongstad --- tests/data/Android.bp | 375 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 tests/data/Android.bp diff --git a/tests/data/Android.bp b/tests/data/Android.bp new file mode 100644 index 0000000..6e51f00 --- /dev/null +++ b/tests/data/Android.bp @@ -0,0 +1,375 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_defaults { + name: "idmap2_defaults", + cpp_std: "gnu++2b", + tidy: true, + tidy_checks: [ + "modernize-*", + "-modernize-avoid-c-arrays", + "-modernize-use-nodiscard", + "-modernize-use-trailing-return-type", + "android-*", + "misc-*", + "-misc-const-correctness", + "readability-*", + "-readability-identifier-length", + "-readability-implicit-bool-conversion", + ], + tidy_checks_as_errors: [ + "modernize-*", + "-modernize-avoid-c-arrays", + "-modernize-concat-nested-namespaces", + "-modernize-pass-by-value", + "-modernize-replace-disallow-copy-and-assign-macro", + "-modernize-return-braced-init-list", + "-modernize-use-default-member-init", + "-modernize-use-equals-default", + "-modernize-use-emplace", + "-modernize-use-nodiscard", + "-modernize-use-override", + "-modernize-use-trailing-return-type", + "-modernize-use-using", + "android-*", + "misc-*", + "-misc-non-private-member-variables-in-classes", + "readability-*", + "-readability-braces-around-statements", + "-readability-const-return-type", + "-readability-convert-member-functions-to-static", + "-readability-duplicate-include", + "-readability-implicit-bool-conversion", + "-readability-else-after-return", + "-readability-named-parameter", + "-readability-redundant-access-specifiers", + "-readability-uppercase-literal-suffix", + ], +} + +cc_library { + name: "libidmap2", + defaults: [ + "idmap2_defaults", + ], + host_supported: true, + srcs: [ + "libidmap2/**/*.cpp", + "self_targeting/*.cpp", + ], + export_include_dirs: ["include"], + target: { + android: { + static: { + enabled: false, + }, + static_libs: [ + "libidmap2_policies", + "libidmap2_protos", + "libpng", + ], + shared_libs: [ + "libandroidfw", + "libbase", + "libcutils", + "libprotobuf-cpp-lite", + "libutils", + "libz", + "libziparchive", + ], + }, + host: { + shared: { + enabled: false, + }, + static_libs: [ + "libandroidfw", + "libbase", + "libcutils", + "libidmap2_policies", + "libidmap2_protos", + "libpng", + "libprotobuf-cpp-lite", + "libutils", + "libz", + "libziparchive", + ], + }, + }, +} + +cc_library { + name: "libidmap2_protos", + srcs: [ + "libidmap2/proto/*.proto", + ], + host_supported: true, + tidy: false, + proto: { + type: "lite", + export_proto_headers: true, + }, +} + +cc_library_static { + name: "libidmap2_policies", + defaults: [ + "idmap2_defaults", + ], + host_supported: true, + export_include_dirs: ["libidmap2_policies/include"], + target: { + windows: { + enabled: true, + }, + android: { + shared_libs: [ + "libandroidfw", + ], + }, + host: { + static_libs: [ + "libandroidfw", + ], + }, + }, +} + +cc_test { + name: "idmap2_tests", + defaults: [ + "idmap2_defaults", + ], + tidy_checks: [ + "-readability-magic-numbers", + ], + host_supported: true, + test_suites: ["general-tests"], + srcs: [ + "tests/BinaryStreamVisitorTests.cpp", + "tests/CommandLineOptionsTests.cpp", + "tests/FabricatedOverlayTests.cpp", + "tests/FileUtilsTests.cpp", + "tests/Idmap2BinaryTests.cpp", + "tests/IdmapTests.cpp", + "tests/Main.cpp", + "tests/PoliciesTests.cpp", + "tests/PrettyPrintVisitorTests.cpp", + "tests/RawPrintVisitorTests.cpp", + "tests/ResourceMappingTests.cpp", + "tests/ResourceUtilsTests.cpp", + "tests/ResultTests.cpp", + "tests/XmlParserTests.cpp", + ], + required: [ + "idmap2", + ], + static_libs: [ + "libgmock", + "libidmap2_protos", + "libpng", + ], + target: { + android: { + shared_libs: [ + "libandroidfw", + "libbase", + "libidmap2", + "liblog", + "libprotobuf-cpp-lite", + "libutils", + "libz", + "libz", + "libziparchive", + ], + static_libs: [ + "libidmap2_policies", + ], + }, + host: { + static_libs: [ + "libandroidfw", + "libbase", + "libcutils", + "libidmap2", + "libidmap2_policies", + "liblog", + "libprotobuf-cpp-lite", + "libutils", + "libziparchive", + ], + shared_libs: [ + "libz", + ], + data: [ + ":libz", + ":idmap2", + ], + }, + }, + data: [ + "tests/data/**/*.apk", + "tests/data/**/*.png", + ], + compile_multilib: "first", + test_options: { + unit_test: true, + }, +} + +cc_binary { + name: "idmap2", + defaults: [ + "idmap2_defaults", + ], + host_supported: true, + srcs: [ + "idmap2/CommandUtils.cpp", + "idmap2/Create.cpp", + "idmap2/CreateMultiple.cpp", + "idmap2/Dump.cpp", + "idmap2/Lookup.cpp", + "idmap2/Main.cpp", + ], + static_libs: [ + "libidmap2_protos", + ], + target: { + android: { + shared_libs: [ + "libandroidfw", + "libbase", + "libcutils", + "libidmap2", + "libpng", + "libprotobuf-cpp-lite", + "libutils", + "libz", + "libziparchive", + ], + static_libs: [ + "libidmap2_policies", + ], + }, + host: { + static_libs: [ + "libandroidfw", + "libbase", + "libcutils", + "libidmap2", + "libidmap2_policies", + "liblog", + "libpng", + "libprotobuf-cpp-lite", + "libutils", + "libziparchive", + ], + shared_libs: [ + "libz", + ], + }, + }, + +} + +cc_binary { + name: "idmap2d", + defaults: [ + "idmap2_defaults", + ], + host_supported: false, + srcs: [ + "idmap2d/Idmap2Service.cpp", + "idmap2d/Main.cpp", + ], + shared_libs: [ + "libandroidfw", + "libbase", + "libbinder", + "libcutils", + "libidmap2", + "libprotobuf-cpp-lite", + "libutils", + "libziparchive", + ], + static_libs: [ + "libc++fs", + "libidmap2_policies", + "libidmap2_protos", + "libidmap2daidl", + ], + init_rc: ["idmap2d/idmap2d.rc"], +} + +cc_library_static { + name: "libidmap2daidl", + srcs: [ + ":idmap2_aidl", + ":idmap2_core_aidl", + ], + header_libs: [ + "libbinder_headers", + ], + shared_libs: [ + "libbase", + ], + aidl: { + export_aidl_headers: true, + local_include_dirs: [ + "idmap2d/aidl/core", + "idmap2d/aidl/services/", + ], + }, +} + +filegroup { + name: "idmap2_core_aidl", + srcs: [ + "idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl", + "idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl", + "idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl", + ], + path: "idmap2d/aidl/core/", +} + +filegroup { + name: "idmap2_aidl", + srcs: [ + "idmap2d/aidl/services/android/os/IIdmap2.aidl", + ], + path: "idmap2d/aidl/services/", +} + +aidl_interface { + name: "overlayable_policy_aidl", + unstable: true, + srcs: [":overlayable_policy_aidl_files"], +} + +filegroup { + name: "overlayable_policy_aidl_files", + srcs: [ + "idmap2d/aidl/services/android/os/OverlayablePolicy.aidl", + ], + path: "idmap2d/aidl/services/", +} From 1347969f5274b90b4e028a0116b3b62967e99c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 20 Jun 2024 09:22:03 +0200 Subject: [PATCH 2/4] deps: regex (1.10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regular expressions will be used to grep for the module definition in Android.bp files. Signed-off-by: Mårten Kongstad --- Cargo.lock | 31 ++++++++++++++++++++++--------- Cargo.toml | 1 + 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1152b47..c533a3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -20,6 +20,7 @@ dependencies = [ "clap", "memmap", "predicates", + "regex", "serde", "serde_json", ] @@ -84,7 +85,7 @@ checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "lazy_static", "memchr", - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -167,9 +168,9 @@ checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" [[package]] name = "memchr" -version = "2.4.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap" @@ -246,12 +247,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", + "regex-automata 0.4.7", "regex-syntax", ] @@ -261,11 +263,22 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ryu" diff --git a/Cargo.toml b/Cargo.toml index 58cd365..d0ad268 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" anyhow = "1.0" clap = "2.33" memmap = "0.7" +regex = "1.10" serde_json = "1.0" [dependencies.serde] From e4486994ba4c7e2c80fc21dca4a52e10e7539e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 20 Jun 2024 10:11:31 +0200 Subject: [PATCH 3/4] Add method to grep for module definition in Android.bp file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support finding a module definition given an Android.bp file and the module's name. The logic relies on regular expressions, which assumes more about the input than a real parser would do. Support for a parser can be added at a later date, if needed. Signed-off-by: Mårten Kongstad --- src/blueprint.rs | 32 ++++++++++++++++++++++++++++++++ src/main.rs | 1 + 2 files changed, 33 insertions(+) create mode 100644 src/blueprint.rs diff --git a/src/blueprint.rs b/src/blueprint.rs new file mode 100644 index 0000000..fd5becd --- /dev/null +++ b/src/blueprint.rs @@ -0,0 +1,32 @@ +use anyhow::Result; +use regex::Regex; + +#[allow(dead_code)] +pub fn find_module_source<'h>(haystack: &'h str, name: &str) -> Result> { + let regex_module = Regex::new(r"(?ms)[ \t]*[_a-zA-Z0-9]+\s*\{.*?^\}")?; + let regex_name = Regex::new(&format!(r#"(?m)^\s*name:\s*"{}""#, name))?; + for cap in regex_module.captures_iter(haystack) { + let match_ = cap.get(0).unwrap(); + if regex_name.is_match(match_.as_str()) { + return Ok(Some(&haystack[match_.range()])); + } + } + + Ok(None) +} + +#[cfg(test)] +mod tests { + use super::*; + + const BLUEPRINT: &str = include_str!("../tests/data/Android.bp"); + + #[test] + fn test_find_module_source() { + assert!(find_module_source("", "").unwrap().is_none()); + assert!(find_module_source(BLUEPRINT, "none").unwrap().is_none()); + let source = find_module_source(BLUEPRINT, "idmap2").unwrap().unwrap(); + assert!(source.starts_with("cc_binary {\n name: \"idmap2\",\n")); + assert!(source.ends_with("},\n\n}")); + } +} diff --git a/src/main.rs b/src/main.rs index 23ff6a8..725a75e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use anyhow::{anyhow, bail, Result}; +mod blueprint; mod modinfo; use modinfo::ModuleInfo; From 91f3e355d20099b1e47427b9b6f7aebeaf507427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 20 Jun 2024 10:37:37 +0200 Subject: [PATCH 4/4] Add `source` subcommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new subcommand, `source `, to print the Android.bp definition of a module. The new command requires knowledge of the top of the Android tree. Add a new global parameter to explicitly set this, and default to ANDROID_BUILD_TOP. Signed-off-by: Mårten Kongstad --- src/main.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++-- tests/cli.rs | 13 ++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 725a75e..498dded 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ use clap::{App, AppSettings, Arg}; use memmap::MmapOptions; use std::env; -use std::fs::File; +use std::fs::{self, File}; use std::path::PathBuf; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, ensure, Context, Result}; mod blueprint; mod modinfo; @@ -14,6 +14,7 @@ use modinfo::ModuleInfo; #[derive(Debug)] struct Arguments { module_info_path: PathBuf, + android_top_path: PathBuf, command: Command, } @@ -21,6 +22,7 @@ struct Arguments { enum Command { List, Show(String, Option), + Source(String), } const MODULE_FIELDS: [&str; 9] = [ @@ -47,6 +49,14 @@ fn parse_args() -> Result { .value_name("FILE") .takes_value(true), ) + .arg( + Arg::with_name("android-top") + .help("Path to top of Android tree") + .long_help("Path to the top of the Android tree; defaults to `$ANDROID_BUILD_TOP`.") + .long("android-top") + .value_name("DIR") + .takes_value(true), + ) .subcommand(App::new("list").about("Prints the names of all modules")) .subcommand( App::new("show") @@ -58,6 +68,13 @@ fn parse_args() -> Result { .help("Name of field to show") .possible_values(&MODULE_FIELDS)), ) + .subcommand( + App::new("source") + .about("Prints the Android.bp definition of module") + .arg(Arg::with_name("NAME") + .help("Name of module to show") + .required(true)) + ) .get_matches(); let module_info_path = if matches.is_present("module-info") { @@ -70,6 +87,14 @@ fn parse_args() -> Result { path }; + let android_top_path = if matches.is_present("android-top") { + matches.value_of("android-top").unwrap().into() + } else { + env::var("ANDROID_BUILD_TOP") + .map_err(|_| anyhow!("ANDROID_BUILD_TOP not set"))? + .into() + }; + let command = match &matches.subcommand() { ("list", _) => Command::List, ("show", Some(args)) => Command::Show( @@ -78,11 +103,17 @@ fn parse_args() -> Result { .to_string(), args.value_of("FIELD").map(|s| s.to_string()), ), + ("source", Some(args)) => Command::Source( + args.value_of("NAME") + .expect("value guaranteed by clap") + .to_string(), + ), (_, _) => unreachable!(), }; Ok(Arguments { module_info_path, + android_top_path, command, }) } @@ -131,6 +162,27 @@ fn main() -> Result<()> { println!("{:#?}", module); } } + Command::Source(name) => { + let module = modinfo + .find(&name) + .ok_or_else(|| anyhow!("{}: module not found", name))??; + ensure!( + module.path.len() == 1, + "{}: module does not have exactly one path: {:?}", + name, + module.path + ); + let blueprint_path = format!( + "{}/{}/Android.bp", + args.android_top_path.display(), + module.path[0] + ); + let blueprint_contents = fs::read_to_string(&blueprint_path) + .with_context(|| format!("could not read file {}", blueprint_path))?; + let module_source = blueprint::find_module_source(&blueprint_contents, &name)? + .ok_or_else(|| anyhow!("{}: module source not found", name))?; + println!("{}", module_source); + } } Ok(()) diff --git a/tests/cli.rs b/tests/cli.rs index 00318bc..e43d073 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -28,6 +28,8 @@ mod integration { Command::cargo_bin("amodinfo")? .arg("--module-info") .arg("tests/data/module-info.json") + .arg("--android-top") + .arg("/dev/null") .arg("list") .assert() .success() @@ -42,6 +44,8 @@ mod integration { Command::cargo_bin("amodinfo")? .arg("--module-info") .arg("tests/data/module-info.json") + .arg("--android-top") + .arg("/dev/null") .arg("show") .assert() .failure(); @@ -50,6 +54,8 @@ mod integration { Command::cargo_bin("amodinfo")? .arg("--module-info") .arg("tests/data/module-info.json") + .arg("--android-top") + .arg("/dev/null") .arg("show") .arg("does-not-exist") .assert() @@ -59,6 +65,8 @@ mod integration { Command::cargo_bin("amodinfo")? .arg("--module-info") .arg("tests/data/module-info.json") + .arg("--android-top") + .arg("/dev/null") .arg("show") .arg("idmap2") .assert() @@ -74,6 +82,8 @@ mod integration { Command::cargo_bin("amodinfo")? .arg("--module-info") .arg("tests/data/module-info.json") + .arg("--android-top") + .arg("/dev/null") .arg("show") .arg("idmap2") .arg("path") @@ -86,6 +96,8 @@ mod integration { Command::cargo_bin("amodinfo")? .arg("--module-info") .arg("tests/data/module-info.json") + .arg("--android-top") + .arg("/dev/null") .arg("show") .arg("idmap2") .arg("foo") @@ -99,6 +111,7 @@ mod integration { fn implicit_module_info_path() -> Result<(), Box> { Command::cargo_bin("amodinfo")? .env("ANDROID_PRODUCT_OUT", "tests/data") + .env("ANDROID_BUILD_TOP", "/dev/null") .arg("show") .arg("idmap2") .assert()