diff options
45 files changed, 5896 insertions, 83 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index d38a96172..e032c4df7 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,6 +1,9 @@ # Per-project `repo upload` hook settings. # https://android.googlesource.com/platform/tools/repohooks +[Builtin Hooks] +rustfmt = true + [Options] ignore_merged_commits = true @@ -11,4 +14,4 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPL ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/ktfmt_includes.txt ${PREUPLOAD_FILES} -ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
\ No newline at end of file +ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES} diff --git a/build/Android.bp b/build/Android.bp index 9c3d0e899..f54346ff1 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -214,6 +214,9 @@ android_sdk_repo_host { linux: { strip_files: ["lib64/*.so"], }, + linux_bionic: { + enabled: false, + }, darwin: { strip_files: ["lib64/*.dylib"], }, diff --git a/build/Android.mk b/build/Android.mk index be7aa4e63..b05bcd5ad 100644 --- a/build/Android.mk +++ b/build/Android.mk @@ -123,6 +123,10 @@ $(call dist-for-goals,sdk,$(full_target):system-data/annotations.zip) full_target := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/api_versions_module_lib_generated-api-versions.xml $(call dist-for-goals,sdk,$(full_target):module-lib-data/api-versions.xml) +# ======= Lint module-lib API XML (complete API) =========== +full_target := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/api_versions_module_lib_complete_generated-api-versions.xml +$(call dist-for-goals,sdk,$(full_target):module-lib-data/api-versions-complete.xml) + # ======= Lint module-lib Annotations zip (complete API) =========== full_target := $(call intermediates-dir-for,ETC,sdk-annotations-module-lib.zip)/sdk-annotations-module-lib.zip $(call dist-for-goals,sdk,$(full_target):module-lib-data/annotations.zip) @@ -131,6 +135,10 @@ $(call dist-for-goals,sdk,$(full_target):module-lib-data/annotations.zip) full_target := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/api_versions_system_server_generated-api-versions.xml $(call dist-for-goals,sdk,$(full_target):system-server-data/api-versions.xml) +# ======= Lint system-server API XML (complete API) =========== +full_target := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/api_versions_system_server_complete_generated-api-versions.xml +$(call dist-for-goals,sdk,$(full_target):system-server-data/api-versions-complete.xml) + # ======= Lint system-server Annotations zip (complete API) =========== full_target := $(call intermediates-dir-for,ETC,sdk-annotations-system-server.zip)/sdk-annotations-system-server.zip $(call dist-for-goals,sdk,$(full_target):system-server-data/annotations.zip) diff --git a/scripts/symbol.py b/scripts/symbol.py index f4c239535..64242eab8 100755 --- a/scripts/symbol.py +++ b/scripts/symbol.py @@ -20,6 +20,7 @@ The information can include symbol names, offsets, and source locations. """ import atexit +import json import glob import os import platform @@ -292,7 +293,7 @@ def CallLlvmSymbolizerForSet(lib, unique_addrs): return None cmd = [ToolPath("llvm-symbolizer"), "--functions", "--inlines", - "--demangle", "--obj=" + symbols, "--output-style=GNU"] + "--demangle", "--obj=" + symbols, "--output-style=JSON"] child = _PIPE_ADDR2LINE_CACHE.GetProcess(cmd) for addr in addrs: @@ -300,20 +301,12 @@ def CallLlvmSymbolizerForSet(lib, unique_addrs): child.stdin.write("0x%s\n" % addr) child.stdin.flush() records = [] - first = True - while True: - symbol = child.stdout.readline().strip() - if not symbol: - break - location = child.stdout.readline().strip() - records.append((symbol, location)) - if first: - # Write a blank line as a sentinel so we know when to stop - # reading inlines from the output. - # The blank line will cause llvm-symbolizer to emit a blank line. - child.stdin.write("\n") - child.stdin.flush() - first = False + json_result = json.loads(child.stdout.readline().strip()) + for symbol in json_result["Symbol"]: + function_name = symbol["FunctionName"] + # GNU style location: file_name:line_num + location = ("%s:%s" % (symbol["FileName"], symbol["Line"])) + records.append((function_name, location)) except IOError as e: # Remove the / in front of the library name to match other output. records = [(None, lib[1:] + " ***Error: " + str(e))] diff --git a/sdk/OWNERS b/sdk/OWNERS index e0c1e7d22..a05cf75ac 100644 --- a/sdk/OWNERS +++ b/sdk/OWNERS @@ -1,2 +1,2 @@ per-file plat_tools_source.prop_template=set noparent -per-file plat_tools_source.prop_template=enh@google.com,shaju@google.com +per-file plat_tools_source.prop_template=enh@google.com,sanglardf@google.com diff --git a/tools/cargo_embargo/Android.bp b/tools/cargo_embargo/Android.bp index 2e6eab4c4..3ef899193 100644 --- a/tools/cargo_embargo/Android.bp +++ b/tools/cargo_embargo/Android.bp @@ -19,7 +19,7 @@ package { rust_defaults { name: "cargo_embargo.defaults", - srcs: ["src/main.rs"], + crate_root: "src/main.rs", // Disable LTO for faster builds. Don't need the performance here. flags: ["-C lto=off"], rustlibs: [ diff --git a/tools/cargo_embargo/src/bp.rs b/tools/cargo_embargo/src/bp.rs index cc186c46b..7744ae74f 100644 --- a/tools/cargo_embargo/src/bp.rs +++ b/tools/cargo_embargo/src/bp.rs @@ -111,6 +111,7 @@ impl BpProperties { "crate_name", "cargo_env_compat", "cargo_pkg_version", + "crate_root", "srcs", "test_suites", "auto_gen_config", diff --git a/tools/cargo_embargo/src/cargo/cargo_out.rs b/tools/cargo_embargo/src/cargo/cargo_out.rs index 01ca55c95..fb2f00f64 100644 --- a/tools/cargo_embargo/src/cargo/cargo_out.rs +++ b/tools/cargo_embargo/src/cargo/cargo_out.rs @@ -410,9 +410,9 @@ impl Crate { manifest_path, ) })?; - out.package_name = package_metadata.name.clone(); + out.package_name.clone_from(&package_metadata.name); out.version = Some(package_metadata.version.clone()); - out.edition = package_metadata.edition.clone(); + out.edition.clone_from(&package_metadata.edition); let output_filename = out.name.clone() + &extra_filename; if let Some(test_contents) = tests.get(&output_filename).and_then(|m| m.get(&out.main_src)) diff --git a/tools/cargo_embargo/src/cargo/metadata.rs b/tools/cargo_embargo/src/cargo/metadata.rs index 2958a0117..ea4c95983 100644 --- a/tools/cargo_embargo/src/cargo/metadata.rs +++ b/tools/cargo_embargo/src/cargo/metadata.rs @@ -24,12 +24,12 @@ use std::path::{Path, PathBuf}; /// `cfg` strings for dependencies which should be considered enabled. It would be better to parse /// them properly, but this is good enough in practice so far. const ENABLED_CFGS: [&str; 6] = [ - r#"cfg(unix)"#, - r#"cfg(not(windows))"#, - r#"cfg(any(unix, target_os = "wasi"))"#, - r#"cfg(not(all(target_family = "wasm", target_os = "unknown")))"#, - r#"cfg(not(target_family = "wasm"))"#, - r#"cfg(any(target_os = "linux", target_os = "android"))"#, + r#"unix"#, + r#"not(windows)"#, + r#"any(unix, target_os = "wasi")"#, + r#"not(all(target_family = "wasm", target_os = "unknown"))"#, + r#"not(target_family = "wasm")"#, + r#"any(target_os = "linux", target_os = "android")"#, ]; /// `cargo metadata` output. @@ -39,7 +39,7 @@ pub struct WorkspaceMetadata { pub workspace_members: Vec<String>, } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] pub struct PackageMetadata { pub name: String, pub version: String, @@ -62,17 +62,21 @@ pub struct DependencyMetadata { impl DependencyMetadata { /// Returns whether the dependency should be included when the given features are enabled. - fn enabled(&self, features: &[String]) -> bool { + fn enabled(&self, features: &[String], cfgs: &[String]) -> bool { if let Some(target) = &self.target { - if !ENABLED_CFGS.contains(&target.as_str()) { - return false; + if target.starts_with("cfg(") && target.ends_with(')') { + let target_cfg = &target[4..target.len() - 1]; + if !ENABLED_CFGS.contains(&target_cfg) && !cfgs.contains(&target_cfg.to_string()) { + return false; + } } } - !self.optional || features.contains(&format!("dep:{}", self.name)) + let name = self.rename.as_ref().unwrap_or(&self.name); + !self.optional || features.contains(&format!("dep:{}", name)) } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] #[allow(dead_code)] pub struct TargetMetadata { pub crate_types: Vec<CrateType>, @@ -103,12 +107,13 @@ pub enum TargetKind { pub fn parse_cargo_metadata_str(cargo_metadata: &str, cfg: &VariantConfig) -> Result<Vec<Crate>> { let metadata = serde_json::from_str(cargo_metadata).context("failed to parse cargo metadata")?; - parse_cargo_metadata(&metadata, &cfg.features, cfg.tests) + parse_cargo_metadata(&metadata, &cfg.features, &cfg.extra_cfg, cfg.tests) } fn parse_cargo_metadata( metadata: &WorkspaceMetadata, features: &Option<Vec<String>>, + cfgs: &[String], include_tests: bool, ) -> Result<Vec<Crate>> { let mut crates = Vec::new(); @@ -171,9 +176,11 @@ fn parse_cargo_metadata( package, &metadata.packages, &features, + cfgs, &target_kinds, false, )?, + cfgs: cfgs.to_owned(), ..Default::default() }); } @@ -193,9 +200,11 @@ fn parse_cargo_metadata( package, &metadata.packages, &features, + cfgs, &target_kinds, true, )?, + cfgs: cfgs.to_owned(), ..Default::default() }); } @@ -208,6 +217,7 @@ fn get_externs( package: &PackageMetadata, packages: &[PackageMetadata], features: &[String], + cfgs: &[String], target_kinds: &[TargetKind], test: bool, ) -> Result<Vec<Extern>> { @@ -216,7 +226,7 @@ fn get_externs( .iter() .filter_map(|dependency| { // Kind is None for normal dependencies, as opposed to dev dependencies. - if dependency.enabled(features) + if dependency.enabled(features, cfgs) && dependency.kind.as_deref() != Some("build") && (dependency.kind.is_none() || test) { @@ -440,6 +450,221 @@ mod tests { } #[test] + fn get_externs_cfg() { + let package = PackageMetadata { + name: "test_package".to_string(), + dependencies: vec![ + DependencyMetadata { + name: "alwayslib".to_string(), + kind: None, + optional: false, + target: None, + rename: None, + }, + DependencyMetadata { + name: "unixlib".to_string(), + kind: None, + optional: false, + target: Some("cfg(unix)".to_string()), + rename: None, + }, + DependencyMetadata { + name: "windowslib".to_string(), + kind: None, + optional: false, + target: Some("cfg(windows)".to_string()), + rename: None, + }, + ], + features: [].into_iter().collect(), + targets: vec![], + ..Default::default() + }; + let packages = vec![ + package.clone(), + PackageMetadata { + name: "alwayslib".to_string(), + targets: vec![TargetMetadata { + name: "alwayslib".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + PackageMetadata { + name: "unixlib".to_string(), + targets: vec![TargetMetadata { + name: "unixlib".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + PackageMetadata { + name: "windowslib".to_string(), + targets: vec![TargetMetadata { + name: "windowslib".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + ]; + assert_eq!( + get_externs(&package, &packages, &[], &[], &[], false).unwrap(), + vec![ + Extern { + name: "alwayslib".to_string(), + lib_name: "alwayslib".to_string(), + extern_type: ExternType::Rust + }, + Extern { + name: "unixlib".to_string(), + lib_name: "unixlib".to_string(), + extern_type: ExternType::Rust + }, + ] + ); + } + + #[test] + fn get_externs_extra_cfg() { + let package = PackageMetadata { + name: "test_package".to_string(), + dependencies: vec![ + DependencyMetadata { + name: "foolib".to_string(), + kind: None, + optional: false, + target: Some("cfg(foo)".to_string()), + rename: None, + }, + DependencyMetadata { + name: "barlib".to_string(), + kind: None, + optional: false, + target: Some("cfg(bar)".to_string()), + rename: None, + }, + ], + features: [].into_iter().collect(), + targets: vec![], + ..Default::default() + }; + let packages = vec![ + package.clone(), + PackageMetadata { + name: "foolib".to_string(), + targets: vec![TargetMetadata { + name: "foolib".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + PackageMetadata { + name: "barlib".to_string(), + targets: vec![TargetMetadata { + name: "barlib".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + ]; + assert_eq!( + get_externs(&package, &packages, &[], &["foo".to_string()], &[], false).unwrap(), + vec![Extern { + name: "foolib".to_string(), + lib_name: "foolib".to_string(), + extern_type: ExternType::Rust + },] + ); + } + + #[test] + fn get_externs_rename() { + let package = PackageMetadata { + name: "test_package".to_string(), + dependencies: vec![ + DependencyMetadata { + name: "foo".to_string(), + kind: None, + optional: false, + target: None, + rename: Some("foo2".to_string()), + }, + DependencyMetadata { + name: "bar".to_string(), + kind: None, + optional: true, + target: None, + rename: None, + }, + DependencyMetadata { + name: "bar".to_string(), + kind: None, + optional: true, + target: None, + rename: Some("baz".to_string()), + }, + ], + ..Default::default() + }; + let packages = vec![ + package.clone(), + PackageMetadata { + name: "foo".to_string(), + targets: vec![TargetMetadata { + name: "foo".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + PackageMetadata { + name: "bar".to_string(), + targets: vec![TargetMetadata { + name: "bar".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + ]; + assert_eq!( + get_externs(&package, &packages, &["dep:bar".to_string()], &[], &[], false).unwrap(), + vec![ + Extern { + name: "bar".to_string(), + lib_name: "bar".to_string(), + extern_type: ExternType::Rust + }, + Extern { + name: "foo2".to_string(), + lib_name: "foo".to_string(), + extern_type: ExternType::Rust + }, + ] + ); + assert_eq!( + get_externs(&package, &packages, &["dep:baz".to_string()], &[], &[], false).unwrap(), + vec![ + Extern { + name: "baz".to_string(), + lib_name: "bar".to_string(), + extern_type: ExternType::Rust + }, + Extern { + name: "foo2".to_string(), + lib_name: "foo".to_string(), + extern_type: ExternType::Rust + }, + ] + ); + } + + #[test] fn parse_metadata() { /// Remove anything before "external/rust/crates/" from the /// `package_dir` field. This makes the test robust since you diff --git a/tools/cargo_embargo/src/main.rs b/tools/cargo_embargo/src/main.rs index 9c67eba0e..753f059e1 100644 --- a/tools/cargo_embargo/src/main.rs +++ b/tools/cargo_embargo/src/main.rs @@ -296,7 +296,9 @@ fn make_crates(args: &Args, cfg: &VariantConfig) -> Result<Vec<Crate>> { } } else { let cargo_output = generate_cargo_out(cfg).context("generate_cargo_out failed")?; - write(cargo_out_path, &cargo_output.cargo_out)?; + if cfg.run_cargo { + write(cargo_out_path, &cargo_output.cargo_out)?; + } write(cargo_metadata_path, &cargo_output.cargo_metadata)?; cargo_output }; @@ -477,9 +479,23 @@ fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { let mut cargo_out = String::new(); if cfg.run_cargo { + let envs = if cfg.extra_cfg.is_empty() { + vec![] + } else { + vec![( + "RUSTFLAGS", + cfg.extra_cfg + .iter() + .map(|cfg_flag| format!("--cfg {}", cfg_flag)) + .collect::<Vec<_>>() + .join(" "), + )] + }; + // cargo build cargo_out += &run_cargo( Command::new("cargo") + .envs(envs.clone()) .args(["build", "--target", default_target]) .args(verbose_args) .args(target_dir_args) @@ -491,6 +507,7 @@ fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { // cargo build --tests cargo_out += &run_cargo( Command::new("cargo") + .envs(envs.clone()) .args(["build", "--target", default_target, "--tests"]) .args(verbose_args) .args(target_dir_args) @@ -500,6 +517,7 @@ fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { // cargo test -- --list cargo_out += &run_cargo( Command::new("cargo") + .envs(envs) .args(["test", "--target", default_target]) .args(target_dir_args) .args(&workspace_args) @@ -898,9 +916,8 @@ fn crate_to_bp_modules( } } - let mut srcs = vec![crate_.main_src.to_string_lossy().to_string()]; - srcs.extend(extra_srcs.iter().cloned()); - m.props.set("srcs", srcs); + m.props.set("crate_root", crate_.main_src.clone()); + m.props.set_if_nonempty("srcs", extra_srcs.to_owned()); m.props.set("edition", crate_.edition.clone()); m.props.set_if_nonempty("features", crate_.features.clone()); @@ -911,7 +928,6 @@ fn crate_to_bp_modules( .clone() .into_iter() .filter(|crate_cfg| !cfg.cfg_blocklist.contains(crate_cfg)) - .chain(cfg.extra_cfg.clone().into_iter()) .collect(), ); @@ -1091,7 +1107,6 @@ fn crate_to_rulesmk( .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() { @@ -1269,7 +1284,7 @@ mod tests { ("host_supported".to_string(), BpValue::Bool(true)), ("name".to_string(), BpValue::String("libname".to_string())), ("product_available".to_string(), BpValue::Bool(true)), - ("srcs".to_string(), BpValue::List(vec![BpValue::String("".to_string())])), + ("crate_root".to_string(), BpValue::String("".to_string())), ("vendor_available".to_string(), BpValue::Bool(true)), ] .into_iter() @@ -1312,7 +1327,7 @@ mod tests { ("host_supported".to_string(), BpValue::Bool(true)), ("name".to_string(), BpValue::String("libash_rust".to_string())), ("product_available".to_string(), BpValue::Bool(true)), - ("srcs".to_string(), BpValue::List(vec![BpValue::String("".to_string())])), + ("crate_root".to_string(), BpValue::String("".to_string())), ("vendor_available".to_string(), BpValue::Bool(true)), ] .into_iter() diff --git a/tools/cargo_embargo/testdata/aho-corasick/expected_Android.bp b/tools/cargo_embargo/testdata/aho-corasick/expected_Android.bp index 7eeebf833..f16d7a414 100644 --- a/tools/cargo_embargo/testdata/aho-corasick/expected_Android.bp +++ b/tools/cargo_embargo/testdata/aho-corasick/expected_Android.bp @@ -4,7 +4,7 @@ host_supported: true, crate_name: "aho_corasick", cargo_env_compat: true, cargo_pkg_version: "0.7.20", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", test_suites: ["general-tests"], auto_gen_config: true, test_options: { @@ -21,7 +21,7 @@ host_supported: true, crate_name: "aho_corasick", cargo_env_compat: true, cargo_pkg_version: "0.7.20", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", edition: "2018", features: ["default", "std"], rustlibs: ["libmemchr"], diff --git a/tools/cargo_embargo/testdata/async-trait/expected_Android.bp b/tools/cargo_embargo/testdata/async-trait/expected_Android.bp index 60db1f4e6..88433ac32 100644 --- a/tools/cargo_embargo/testdata/async-trait/expected_Android.bp +++ b/tools/cargo_embargo/testdata/async-trait/expected_Android.bp @@ -3,7 +3,7 @@ name: "libasync_trait", crate_name: "async_trait", cargo_env_compat: true, cargo_pkg_version: "0.1.74", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", edition: "2021", rustlibs: ["libproc_macro2", "libquote", "libsyn"], product_available: true, diff --git a/tools/cargo_embargo/testdata/either/expected_Android.bp b/tools/cargo_embargo/testdata/either/expected_Android.bp index c74ec8f3b..cf58a2c00 100644 --- a/tools/cargo_embargo/testdata/either/expected_Android.bp +++ b/tools/cargo_embargo/testdata/either/expected_Android.bp @@ -4,7 +4,7 @@ host_supported: true, crate_name: "either", cargo_env_compat: true, cargo_pkg_version: "1.9.0", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", test_suites: ["general-tests"], auto_gen_config: true, test_options: { @@ -21,7 +21,7 @@ host_supported: true, crate_name: "either", cargo_env_compat: true, cargo_pkg_version: "1.9.0", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", edition: "2018", features: ["default", "use_std"], apex_available: ["//apex_available:platform", "//apex_available:anyapex"], diff --git a/tools/cargo_embargo/testdata/plotters/expected_Android.bp b/tools/cargo_embargo/testdata/plotters/expected_Android.bp index 5cc185899..4a2c394f7 100644 --- a/tools/cargo_embargo/testdata/plotters/expected_Android.bp +++ b/tools/cargo_embargo/testdata/plotters/expected_Android.bp @@ -4,7 +4,7 @@ host_supported: true, crate_name: "plotters", cargo_env_compat: true, cargo_pkg_version: "0.3.5", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", edition: "2018", features: ["area_series", "line_series", "plotters-svg", "svg_backend"], rustlibs: ["libnum_traits", "libplotters_backend", "libplotters_svg"], diff --git a/tools/cargo_embargo/testdata/rustc-demangle-capi/expected_Android.bp b/tools/cargo_embargo/testdata/rustc-demangle-capi/expected_Android.bp index b2f3169cc..9c633e4a3 100644 --- a/tools/cargo_embargo/testdata/rustc-demangle-capi/expected_Android.bp +++ b/tools/cargo_embargo/testdata/rustc-demangle-capi/expected_Android.bp @@ -4,7 +4,7 @@ host_supported: true, crate_name: "rustc_demangle", cargo_env_compat: true, cargo_pkg_version: "0.1.0", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", edition: "2015", rustlibs: ["librustc_demangle"], include_dirs: ["include"], @@ -24,7 +24,7 @@ host_supported: true, crate_name: "rustc_demangle", cargo_env_compat: true, cargo_pkg_version: "0.1.0", -srcs: ["src/lib.rs"], +crate_root: "src/lib.rs", test_suites: ["general-tests"], auto_gen_config: true, test_options: { diff --git a/tools/external_crates/.gitignore b/tools/external_crates/.gitignore new file mode 100644 index 000000000..2f7896d1d --- /dev/null +++ b/tools/external_crates/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/tools/external_crates/Cargo.lock b/tools/external_crates/Cargo.lock new file mode 100644 index 000000000..62fafcc9b --- /dev/null +++ b/tools/external_crates/Cargo.lock @@ -0,0 +1,3045 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "btoi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" +dependencies = [ + "num-traits", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" + +[[package]] +name = "cargo" +version = "0.73.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a6fe1f5394d14b81d2f3f605832a3ce35ed0bf120bc7ef437ce27fd4929c6a" +dependencies = [ + "anyhow", + "base64", + "bytesize", + "cargo-platform", + "cargo-util", + "clap", + "crates-io", + "curl", + "curl-sys", + "env_logger", + "filetime", + "flate2", + "fwdansi", + "git2", + "git2-curl", + "gix", + "gix-features", + "glob", + "hex", + "hmac", + "home", + "http-auth", + "humantime", + "ignore", + "im-rc", + "indexmap 1.9.3", + "itertools 0.10.5", + "jobserver", + "lazycell", + "libc", + "libgit2-sys", + "log", + "memchr", + "opener", + "os_info", + "pasetors", + "pathdiff", + "pulldown-cmark", + "rand", + "rustfix", + "semver", + "serde", + "serde-value", + "serde_ignored", + "serde_json", + "sha1", + "shell-escape", + "strip-ansi-escapes", + "syn", + "tar", + "tempfile", + "termcolor", + "time", + "toml", + "toml_edit", + "unicode-width", + "unicode-xid", + "url", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-util" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2d9a9a8d3e0b61b1110c49ab8f6ed7a76ce4f2b1d53ae48a83152d3d5e8f5b" +dependencies = [ + "anyhow", + "core-foundation", + "filetime", + "hex", + "ignore", + "jobserver", + "libc", + "miow", + "same-file", + "sha2", + "shell-escape", + "tempfile", + "tracing", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "cc" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "clru" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "copy_dir" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "543d1dd138ef086e2ff05e3a48cf9da045da2033d16f8538fd76b86cd49b2ca3" +dependencies = [ + "walkdir", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crate_health" +version = "0.1.0" +dependencies = [ + "anyhow", + "cargo", + "clap", + "copy_dir", + "crate_health_proc_macros", + "glob", + "itertools 0.11.0", + "num_cpus", + "semver", + "serde", + "serde_json", + "tempfile", + "thiserror", + "threadpool", + "tinytemplate", + "walkdir", + "whoami", +] + +[[package]] +name = "crate_health_proc_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "crates-io" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876aa69b4afca5f2eb5e23daa3445930faf829bcb67075a20ffa884f11f8c57c" +dependencies = [ + "anyhow", + "curl", + "percent-encoding", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ct-codecs" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" + +[[package]] +name = "curl" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.72+curl-8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "windows-sys 0.52.0", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519-compact" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" +dependencies = [ + "getrandom", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "faster-hex" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" +dependencies = [ + "serde", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fwdansi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208" +dependencies = [ + "memchr", + "termcolor", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "git2" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044" +dependencies = [ + "bitflags 1.3.2", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "git2-curl" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f8b7432b72928cff76f69e59ed5327f94a52763731e71274960dee72fe5f8c" +dependencies = [ + "curl", + "git2", + "log", + "url", +] + +[[package]] +name = "gix" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf2a03ec66ee24d1b2bae3ab718f8d14f141613810cb7ff6756f7db667f1cd82" +dependencies = [ + "gix-actor", + "gix-attributes", + "gix-commitgraph", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-discover", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-mailmap", + "gix-negotiate", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-prompt", + "gix-protocol", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-sec", + "gix-tempfile", + "gix-transport", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "log", + "once_cell", + "prodash", + "signal-hook", + "smallvec", + "thiserror", + "unicode-normalization", +] + +[[package]] +name = "gix-actor" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe73f9f6be1afbf1bd5be919a9636fa560e2f14d42262a934423ed6760cd838" +dependencies = [ + "bstr", + "btoi", + "gix-date", + "itoa", + "nom", + "thiserror", +] + +[[package]] +name = "gix-attributes" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b79590ac382f80d87e06416f5fcac6fee5d83dcb152a00ed0bdbaa988acc31" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "kstring", + "log", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-chunk" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c8751169961ba7640b513c3b24af61aa962c967aaf04116734975cd5af0c52" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-command" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-commitgraph" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8490ae1b3d55c47e6a71d247c082304a2f79f8d0332c1a2f5693d42a2021a09" +dependencies = [ + "bstr", + "gix-chunk", + "gix-features", + "gix-hash", + "memmap2", + "thiserror", +] + +[[package]] +name = "gix-config" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f310120ae1ba8f0ca52fb22876ce9bad5b15c8ffb3eb7302e4b64a3b9f681c" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "log", + "memchr", + "nom", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-config-value" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e874f41437441c02991dcea76990b9058fadfc54b02ab4dd06ab2218af43897" +dependencies = [ + "bitflags 2.5.0", + "bstr", + "gix-path", + "libc", + "thiserror", +] + +[[package]] +name = "gix-credentials" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f89fea8acd28f5ef8fa5042146f1637afd4d834bc8f13439d8fd1e5aca0d65" +dependencies = [ + "bstr", + "gix-command", + "gix-config-value", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-url", + "thiserror", +] + +[[package]] +name = "gix-date" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc164145670e9130a60a21670d9b6f0f4f8de04e5dd256c51fa5a0340c625902" +dependencies = [ + "bstr", + "itoa", + "thiserror", + "time", +] + +[[package]] +name = "gix-diff" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9029ad0083cc286a4bd2f5b3bf66bb66398abc26f2731a2824cd5edfc41a0e33" +dependencies = [ + "gix-hash", + "gix-object", + "imara-diff", + "thiserror", +] + +[[package]] +name = "gix-discover" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba9c6c0d1f2b2efe65581de73de4305004612d49c83773e783202a7ef204f46" +dependencies = [ + "bstr", + "dunce", + "gix-hash", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror", +] + +[[package]] +name = "gix-features" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8c493409bf6060d408eec9bbdd1b12ea351266b50012e2a522f75dfc7b8314" +dependencies = [ + "bytes", + "crc32fast", + "crossbeam-channel", + "flate2", + "gix-hash", + "libc", + "once_cell", + "parking_lot", + "prodash", + "sha1_smol", + "thiserror", + "walkdir", +] + +[[package]] +name = "gix-fs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30da8997008adb87f94e15beb7ee229f8a48e97af585a584bfee4a5a1880aab5" +dependencies = [ + "gix-features", +] + +[[package]] +name = "gix-glob" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0ade1e80ab1f079703d1824e1daf73009096386aa7fd2f0477f6e4ac0a558e" +dependencies = [ + "bitflags 2.5.0", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b422ff2ad9a0628baaad6da468cf05385bf3f5ab495ad5a33cce99b9f41092f" +dependencies = [ + "hex", + "thiserror", +] + +[[package]] +name = "gix-hashtable" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385f4ce6ecf3692d313ca3aa9bd3b3d8490de53368d6d94bedff3af8b6d9c58d" +dependencies = [ + "gix-hash", + "hashbrown 0.14.3", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6f7f101a0ccce808dbf7008ba131dede94e20257e7bde7a44cbb2f8c775625" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "unicode-bom", +] + +[[package]] +name = "gix-index" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ba958fabfb11263fa042c35690d48a6c7be4e9277e2c7e24ff263b3fe7b82" +dependencies = [ + "bitflags 2.5.0", + "bstr", + "btoi", + "filetime", + "gix-bitmap", + "gix-features", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "itoa", + "memmap2", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-lock" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ec5d5e6f07316d3553aa7425e3ecd935ec29882556021fe1696297a448af8d2" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-mailmap" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4653701922c920e009f1bc4309feaff14882ade017770788f9a150928da3fa6a" +dependencies = [ + "bstr", + "gix-actor", + "thiserror", +] + +[[package]] +name = "gix-negotiate" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945c3ef1e912e44a5f405fc9e924edf42000566a1b257ed52cb1293300f6f08c" +dependencies = [ + "bitflags 2.5.0", + "gix-commitgraph", + "gix-hash", + "gix-object", + "gix-revision", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-object" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8926c8f51c44dec3e709cb5dbc93deb9e8d4064c43c9efc54c158dcdfe8446c7" +dependencies = [ + "bstr", + "btoi", + "gix-actor", + "gix-features", + "gix-hash", + "gix-validate", + "hex", + "itoa", + "nom", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-odb" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b234d806278eeac2f907c8b5a105c4ba537230c1a9d9236d822bf0db291f8f3" +dependencies = [ + "arc-swap", + "gix-features", + "gix-hash", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "parking_lot", + "tempfile", + "thiserror", +] + +[[package]] +name = "gix-pack" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d2a14cb3156037eedb17d6cb7209b7180522b8949b21fd0fe3184c0a1d0af88" +dependencies = [ + "clru", + "gix-chunk", + "gix-diff", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-traverse", + "memmap2", + "parking_lot", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-packetline" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab" +dependencies = [ + "bstr", + "faster-hex", + "thiserror", +] + +[[package]] +name = "gix-path" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18609c8cbec8508ea97c64938c33cd305b75dfc04a78d0c3b78b8b3fd618a77c" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror", +] + +[[package]] +name = "gix-prompt" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c22decaf4a063ccae2b2108820c8630c01bd6756656df3fe464b32b8958a5ea" +dependencies = [ + "gix-command", + "gix-config-value", + "parking_lot", + "rustix", + "thiserror", +] + +[[package]] +name = "gix-protocol" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92a17058b45c461f0847528c5fb6ee6e76115e026979eb2d2202f98ee94f6c24" +dependencies = [ + "bstr", + "btoi", + "gix-credentials", + "gix-features", + "gix-hash", + "gix-transport", + "maybe-async", + "nom", + "thiserror", +] + +[[package]] +name = "gix-quote" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbff4f9b9ea3fa7a25a70ee62f545143abef624ac6aa5884344e70c8b0a1d9ff" +dependencies = [ + "bstr", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-ref" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebdd999256f4ce8a5eefa89999879c159c263f3493a951d62aa5ce42c0397e1c" +dependencies = [ + "gix-actor", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-validate", + "memmap2", + "nom", + "thiserror", +] + +[[package]] +name = "gix-refspec" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72bfd622abc86dd8ad1ec51b9eb77b4f1a766b94e3a1b87cf4a022c5b5570cf4" +dependencies = [ + "bstr", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-revision" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5044f56cd7a487ce9b034cbe0252ae0b6b47ff56ca3dabd79bc30214d0932cd7" +dependencies = [ + "bstr", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "thiserror", +] + +[[package]] +name = "gix-revwalk" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2623ba8747914f151f5e12b65adac576ab459dbed5f50a36c7a3e9cbf2d3ca" +dependencies = [ + "gix-commitgraph", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-sec" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9615cbd6b456898aeb942cd75e5810c382fbfc48dbbff2fa23ebd2d33dcbe9c7" +dependencies = [ + "bitflags 2.5.0", + "gix-path", + "libc", + "windows", +] + +[[package]] +name = "gix-tempfile" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3785cb010e9dc5c446dfbf02bc1119fc17d3a48a27c029efcb3a3c32953eb10" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "signal-hook", + "signal-hook-registry", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b838b2db8f62c9447d483a4c28d251b67fee32741a82cb4d35e9eb4e9fdc5ab" + +[[package]] +name = "gix-transport" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a39ffed9a9078ed700605e064b15d7c6ae50aa65e7faa36ca6919e8081df15" +dependencies = [ + "base64", + "bstr", + "curl", + "gix-command", + "gix-credentials", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "thiserror", +] + +[[package]] +name = "gix-traverse" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0842e984cb4bf26339dc559f3a1b8bf8cdb83547799b2b096822a59f87f33d9" +dependencies = [ + "gix-hash", + "gix-hashtable", + "gix-object", + "thiserror", +] + +[[package]] +name = "gix-url" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1663df25ac42047a2547618d2a6979a26f478073f6306997429235d2cd4c863" +dependencies = [ + "bstr", + "gix-features", + "gix-path", + "home", + "thiserror", + "url", +] + +[[package]] +name = "gix-utils" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066432d4c277f9877f091279a597ea5331f68ca410efc874f0bdfb1cd348f92" +dependencies = [ + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba9b3737b2cef3dcd014633485f0034b0f1a931ee54aeb7d8f87f177f3c89040" +dependencies = [ + "bstr", + "thiserror", +] + +[[package]] +name = "gix-worktree" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d388ad962e8854402734a7387af8790f6bdbc8d05349052dab16ca4a0def50f6" +dependencies = [ + "bstr", + "filetime", + "gix-attributes", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "io-close", + "thiserror", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http-auth" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643c9bbf6a4ea8a656d6b4cd53d34f79e3f841ad5203c1a55fb7d761923bc255" +dependencies = [ + "memchr", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "im-rc" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "imara-diff" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8" +dependencies = [ + "ahash", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "io-close" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libgit2-sys" +version = "0.15.2+1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libnghttp2-sys" +version = "0.1.9+1.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "miow" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opener" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" +dependencies = [ + "bstr", + "winapi", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "orion" +version = "0.17.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abdb10181903c8c4b016ba45d6d6d5af1a1e2a461aa4763a83b87f5df4695e5" +dependencies = [ + "fiat-crypto", + "subtle", + "zeroize", +] + +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pasetors" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b36d47c66f2230dd1b7143d9afb2b4891879020210eddf2ccb624e529b96dba" +dependencies = [ + "ct-codecs", + "ed25519-compact", + "getrandom", + "orion", + "p384", + "rand_core", + "regex", + "serde", + "serde_json", + "sha2", + "subtle", + "time", + "zeroize", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prodash" +version = "25.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d67eb4220992a4a052a4bb03cf776e493ecb1a3a36bab551804153d63486af7" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +dependencies = [ + "bitflags 2.5.0", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustfix" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481" +dependencies = [ + "anyhow", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_ignored" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e319a36d1b52126a0d608f24e93b2d81297091818cd70625fcf50a15d84ddf" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strip-ansi-escapes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +dependencies = [ + "vte", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/tools/external_crates/Cargo.toml b/tools/external_crates/Cargo.toml new file mode 100644 index 000000000..f0b017094 --- /dev/null +++ b/tools/external_crates/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "crate_health", + "crate_health_proc_macros", +] +resolver = "2" diff --git a/tools/external_crates/OWNERS b/tools/external_crates/OWNERS new file mode 100644 index 000000000..2d111b4f5 --- /dev/null +++ b/tools/external_crates/OWNERS @@ -0,0 +1,2 @@ +jamesfarrell@google.com +srhines@google.com diff --git a/tools/external_crates/crate_health/Cargo.toml b/tools/external_crates/crate_health/Cargo.toml new file mode 100644 index 000000000..3e02f3648 --- /dev/null +++ b/tools/external_crates/crate_health/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "crate_health" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +cargo = "0.73" +clap = { version = "4.4.6", features = ["derive"] } +copy_dir = "0.1" +glob = "0.3" +itertools = "0.11" +num_cpus = "1" +semver = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" +threadpool = "1" +tinytemplate = "1.2" +walkdir = "2" +whoami = "1" +crate_health_proc_macros = { path = "../crate_health_proc_macros" } + +[dev-dependencies] +tempfile = "3" diff --git a/tools/external_crates/crate_health/src/android_bp.rs b/tools/external_crates/crate_health/src/android_bp.rs new file mode 100644 index 000000000..a6a0c3aa1 --- /dev/null +++ b/tools/external_crates/crate_health/src/android_bp.rs @@ -0,0 +1,139 @@ +// Copyright (C) 2023 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. + +use std::{ + collections::BTreeMap, + env, + path::Path, + process::{Command, Output}, + str::from_utf8, + sync::mpsc::channel, +}; + +use anyhow::{anyhow, Context, Result}; +use threadpool::ThreadPool; + +use crate::{Crate, NameAndVersion, NameAndVersionMap, NamedAndVersioned}; + +pub fn generate_android_bps<'a, T: Iterator<Item = &'a Crate>>( + crates: T, +) -> Result<BTreeMap<NameAndVersion, (Output, Output)>> { + let pool = ThreadPool::new(std::cmp::max(num_cpus::get(), 32)); + let (tx, rx) = channel(); + + let mut num_crates = 0; + for krate in crates { + num_crates += 1; + let tx = tx.clone(); + let crate_name = krate.name().to_string(); + let crate_version = krate.version().clone(); + let repo_root = krate.root().to_path_buf(); + let android_bp_path = krate.android_bp(); + let test_path = krate.staging_path(); + pool.execute(move || { + println!("Generating Android.bp for {} {}", crate_name, crate_version); + tx.send(( + crate_name, + crate_version, + generate_android_bp(&repo_root, &android_bp_path, &test_path), + )) + .expect("Failed to send"); + }); + } + let mut results = BTreeMap::new(); + for (crate_name, crate_version, result) in rx.iter().take(num_crates) { + results.insert_or_error(NameAndVersion::new(crate_name, crate_version), result?)?; + } + Ok(results) +} + +pub(crate) fn generate_android_bp( + repo_root: &impl AsRef<Path>, + android_bp_path: &impl AsRef<Path>, + staging_path: &impl AsRef<Path>, +) -> Result<(Output, Output)> { + let generate_android_bp_output = run_cargo_embargo(repo_root, staging_path)?; + if !generate_android_bp_output.status.success() { + println!( + "cargo_embargo failed for {}\nstdout:\n{}\nstderr:\n{}", + android_bp_path.as_ref().display(), + from_utf8(&generate_android_bp_output.stdout)?, + from_utf8(&generate_android_bp_output.stderr)? + ); + } + let diff_output = + diff(&android_bp_path.as_ref(), &staging_path.as_ref().join("Android.bp"), repo_root) + .context("Failed to diff Android.bp".to_string())?; + Ok((generate_android_bp_output, diff_output)) +} + +fn run_cargo_embargo( + repo_root: &impl AsRef<Path>, + staging_path: &impl AsRef<Path>, +) -> Result<Output> { + // Make sure we can find bpfmt. + let host_bin = repo_root.as_ref().join("out/host/linux-x86/bin"); + let new_path = match env::var_os("PATH") { + Some(p) => { + let mut paths = vec![host_bin]; + paths.extend(env::split_paths(&p)); + env::join_paths(paths)? + } + None => host_bin.as_os_str().into(), + }; + + let staging_path_absolute = repo_root.as_ref().join(staging_path); + let mut cmd = Command::new(repo_root.as_ref().join("out/host/linux-x86/bin/cargo_embargo")); + cmd.args(["generate", "cargo_embargo.json"]) + .env("PATH", new_path) + .env("ANDROID_BUILD_TOP", repo_root.as_ref()) + .current_dir(&staging_path_absolute) + .output() + .context(format!("Failed to execute {:?}", cmd.get_program())) +} + +fn diff(a: &impl AsRef<Path>, b: &impl AsRef<Path>, root: &impl AsRef<Path>) -> Result<Output> { + Ok(Command::new("diff") + .args([ + "-u", + "-w", + "-B", + "-I", + "// has rustc warnings", + "-I", + "This file is generated by", + "-I", + "cargo_pkg_version:", + ]) + .arg(a.as_ref()) + .arg(b.as_ref()) + .current_dir(root) + .output()?) +} + +pub fn build_cargo_embargo(repo_root: &impl AsRef<Path>) -> Result<()> { + let output = Command::new("/usr/bin/bash") + .args(["-c", "source build/envsetup.sh && lunch aosp_cf_x86_64_phone-trunk_staging-userdebug && m cargo_embargo bpfmt"]) + .current_dir(repo_root) + .output() + .context("Failed to build cargo embargo and bpfmt")?; + if !output.status.success() { + return Err(anyhow!( + "Failed to build cargo embargo and bpfmt.\nstdout:\n{}\nstderr:\n{}", + from_utf8(&output.stdout)?, + from_utf8(&output.stderr)? + )); + } + Ok(()) +} diff --git a/tools/external_crates/crate_health/src/bin/health_report.rs b/tools/external_crates/crate_health/src/bin/health_report.rs new file mode 100644 index 000000000..6b083e686 --- /dev/null +++ b/tools/external_crates/crate_health/src/bin/health_report.rs @@ -0,0 +1,52 @@ +// Copyright (C) 2023 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. + +use std::path::PathBuf; + +use anyhow::Result; +use clap::Parser; +use crate_health::{ + build_cargo_embargo, default_output_dir, default_repo_root, CrateCollection, NameAndVersionMap, + ReportEngine, +}; + +/// Generate a health report for crates in external/rust/crates +#[derive(Parser, Debug)] +#[command(about, long_about = None)] +struct Args { + /// Path to the AOSP repo. Defaults to current working directory. + #[arg(long, default_value_os_t=default_repo_root().unwrap_or(PathBuf::from(".")))] + repo_root: PathBuf, + + /// Path the health report will be written to. + #[arg(long, default_value_os_t=default_output_dir("crate-health-report.html"))] + output_path: PathBuf, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + build_cargo_embargo(&args.repo_root)?; + + let mut cc = CrateCollection::new(args.repo_root); + cc.add_from(&"external/rust/crates", None::<&&str>)?; + cc.map_field_mut().retain(|_nv, krate| krate.is_crates_io()); + + cc.stage_crates()?; + cc.generate_android_bps()?; + + let re = ReportEngine::new()?; + + Ok(re.health_report(&cc, &args.output_path)?) +} diff --git a/tools/external_crates/crate_health/src/bin/migration_report.rs b/tools/external_crates/crate_health/src/bin/migration_report.rs new file mode 100644 index 000000000..ca3edaa43 --- /dev/null +++ b/tools/external_crates/crate_health/src/bin/migration_report.rs @@ -0,0 +1,47 @@ +// Copyright (C) 2023 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. + +use std::path::PathBuf; + +use anyhow::Result; +use clap::Parser; +use crate_health::{ + build_cargo_embargo, default_output_dir, default_repo_root, migrate, ReportEngine, +}; + +/// Generate a health report for crates in external/rust/crates +#[derive(Parser, Debug)] +#[command(about, long_about = None)] +struct Args { + /// Path to the AOSP repo. Defaults to current working directory. + #[arg(long, default_value_os_t=default_repo_root().unwrap_or(PathBuf::from(".")))] + repo_root: PathBuf, + + /// Path the health report will be written to. + #[arg(long, default_value_os_t=default_output_dir("crate-migration-report.html"))] + output_path: PathBuf, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + build_cargo_embargo(&args.repo_root)?; + + let migration = + migrate(args.repo_root, &"external/rust/crates", &"out/rust-crate-migration-report")?; + + let re = ReportEngine::new()?; + + Ok(re.migration_report(&migration, &args.output_path)?) +} diff --git a/tools/external_crates/crate_health/src/crate_collection.rs b/tools/external_crates/crate_health/src/crate_collection.rs new file mode 100644 index 000000000..03dc4fe22 --- /dev/null +++ b/tools/external_crates/crate_health/src/crate_collection.rs @@ -0,0 +1,93 @@ +// Copyright (C) 2023 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. + +use crate_health_proc_macros::NameAndVersionMap; + +use std::{ + collections::HashSet, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, Result}; +use semver::Version; +use walkdir::WalkDir; + +use crate::{ + android_bp::generate_android_bps, Crate, CrateError, NameAndVersion, NameAndVersionMap, + NamedAndVersioned, +}; + +use std::collections::BTreeMap; + +#[derive(NameAndVersionMap)] +pub struct CrateCollection { + crates: BTreeMap<NameAndVersion, Crate>, + repo_root: PathBuf, +} + +impl CrateCollection { + pub fn new<P: Into<PathBuf>>(repo_root: P) -> CrateCollection { + CrateCollection { crates: BTreeMap::new(), repo_root: repo_root.into() } + } + pub fn add_from( + &mut self, + path: &impl AsRef<Path>, + pseudo_crate: Option<&impl AsRef<Path>>, + ) -> Result<()> { + for entry_or_err in WalkDir::new(self.repo_root.join(path)) { + let entry = entry_or_err?; + if entry.file_name() == "Cargo.toml" { + match Crate::from( + &entry.path(), + &self.repo_root.as_path(), + pseudo_crate.map(|p| p.as_ref()), + ) { + Ok(krate) => self.crates.insert_or_error( + NameAndVersion::new(krate.name().to_string(), krate.version().clone()), + krate, + )?, + Err(e) => match e.downcast_ref() { + Some(CrateError::VirtualCrate(_)) => (), + _ => return Err(e), + }, + }; + } + } + Ok(()) + } + pub fn repo_root(&self) -> &Path { + self.repo_root.as_path() + } + pub fn print(&self) -> Result<()> { + for krate in self.crates.values() { + krate.print()? + } + Ok(()) + } + pub fn stage_crates(&self) -> Result<()> { + for krate in self.crates.values() { + krate.stage_crate()? + } + Ok(()) + } + pub fn generate_android_bps(&mut self) -> Result<()> { + for (nv, output) in generate_android_bps(self.crates.values())?.into_iter() { + self.crates + .get_mut(&nv) + .ok_or(anyhow!("Failed to get crate {} {}", nv.name(), nv.version()))? + .set_generate_android_bp_output(output.0, output.1); + } + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/crate_type.rs b/tools/external_crates/crate_health/src/crate_type.rs new file mode 100644 index 000000000..7f02edd19 --- /dev/null +++ b/tools/external_crates/crate_health/src/crate_type.rs @@ -0,0 +1,392 @@ +// Copyright (C) 2023 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. + +use std::{ + fs::{copy, read_dir, remove_dir_all}, + path::{Path, PathBuf}, + process::{Command, Output}, + str::from_utf8, +}; + +use anyhow::{anyhow, Context, Result}; +use cargo::{ + core::{Manifest, SourceId}, + util::toml::read_manifest, + Config, +}; +use copy_dir::copy_dir; +use semver::Version; + +use crate::{ + ensure_exists_and_empty, name_and_version::IsUpgradableTo, CrateError, NameAndVersionRef, + NamedAndVersioned, +}; + +#[derive(Debug)] +pub struct Crate { + manifest: Manifest, + + // root is absolute. All other paths are relative to it. + root: PathBuf, + relpath: PathBuf, + pseudo_crate: Option<PathBuf>, + + // compatible_dest_version: Option<Version>, + patch_output: Vec<Output>, + generate_android_bp_output: Option<Output>, + android_bp_diff: Option<Output>, +} + +impl NamedAndVersioned for Crate { + fn name(&self) -> &str { + self.manifest.name().as_str() + } + fn version(&self) -> &Version { + self.manifest.version() + } + fn key<'k>(&'k self) -> NameAndVersionRef<'k> { + NameAndVersionRef::new(self.name(), self.version()) + } +} + +impl IsUpgradableTo for Crate {} + +impl Crate { + pub fn new<P: Into<PathBuf>, Q: Into<PathBuf>, R: Into<PathBuf>>( + manifest: Manifest, + root: P, + relpath: Q, + pseudo_crate: Option<R>, + ) -> Crate { + Crate { + manifest, + root: root.into(), + relpath: relpath.into(), + pseudo_crate: pseudo_crate.map(|p| p.into()), + // compatible_dest_version: None, + patch_output: Vec::new(), + generate_android_bp_output: None, + android_bp_diff: None, + } + } + pub fn from<P: Into<PathBuf>, Q: Into<PathBuf>>( + cargo_toml: &impl AsRef<Path>, + root: P, + pseudo_crate: Option<Q>, + ) -> Result<Crate> { + let root: PathBuf = root.into(); + let manifest_dir = cargo_toml.as_ref().parent().ok_or(anyhow!( + "Failed to get parent directory of manifest at {}", + cargo_toml.as_ref().display() + ))?; + let relpath = manifest_dir.strip_prefix(&root)?.to_path_buf(); + let source_id = SourceId::for_path(manifest_dir)?; + let (manifest, _nested) = + read_manifest(cargo_toml.as_ref(), source_id, &Config::default()?)?; + match manifest { + cargo::core::EitherManifest::Real(r) => Ok(Crate::new(r, root, relpath, pseudo_crate)), + cargo::core::EitherManifest::Virtual(_) => { + Err(anyhow!(CrateError::VirtualCrate(cargo_toml.as_ref().to_path_buf()))) + } + } + } + + pub fn root(&self) -> &Path { + self.root.as_path() + } + pub fn relpath(&self) -> &Path { + &self.relpath.as_path() + } + pub fn path(&self) -> PathBuf { + self.root.join(&self.relpath) + } + pub fn android_bp(&self) -> PathBuf { + if let Some(d) = self.customization_dir() { + d.join("Android.bp.disabled") + } else { + self.path().join("Android.bp") + } + } + pub fn cargo_embargo_json(&self) -> PathBuf { + if let Some(d) = self.customization_dir() { + d.join("cargo_embargo.json") + } else { + self.path().join("cargo_embargo.json") + } + } + pub fn staging_path(&self) -> PathBuf { + Path::new("out/rust-crate-temporary-build").join(self.staging_dir_name()) + } + pub fn customization_dir(&self) -> Option<PathBuf> { + self.pseudo_crate.as_ref().map(|pseudo_crate| { + pseudo_crate.join("android/customizations").join(self.staging_dir_name()) + }) + } + pub fn patch_dir(&self) -> Option<PathBuf> { + self.customization_dir().map(|p| p.join("patches")) + } + pub fn staging_dir_name(&self) -> String { + if let Some(dirname) = self.relpath.file_name().and_then(|x| x.to_str()) { + if dirname == self.name() { + return dirname.to_string(); + } + } + format!("{}-{}", self.name(), self.version().to_string()) + } + + pub fn aosp_url(&self) -> Option<String> { + if self.relpath.starts_with("external/rust/crates") { + if self.relpath.ends_with(self.name()) { + Some(format!( + "https://android.googlesource.com/platform/{}/+/refs/heads/main", + self.relpath().display() + )) + } else if self.relpath.parent()?.ends_with(self.name()) { + Some(format!( + "https://android.googlesource.com/platform/{}/+/refs/heads/main/{}", + self.relpath().parent()?.display(), + self.relpath().file_name()?.to_str()? + )) + } else { + None + } + } else { + None + } + } + pub fn crates_io_url(&self) -> String { + format!("https://crates.io/crates/{}", self.name()) + } + + pub fn is_vendored(&self) -> bool { + self.pseudo_crate.is_some() + } + pub fn is_crates_io(&self) -> bool { + const NOT_CRATES_IO: &'static [&'static str] = &[ + "external/rust/beto-rust/", // Google crates + "external/rust/pica/", // Google crate + "external/rust/crates/webpki/third-party/", // Internal/example code + "external/rust/cxx/third-party/", // Internal/example code + "external/rust/cxx/demo/", // Internal/example code + ]; + !NOT_CRATES_IO.iter().any(|prefix| self.relpath.starts_with(prefix)) + } + pub fn is_migration_denied(&self) -> bool { + const MIGRATION_DENYLIST: &'static [&'static str] = &[ + "external/rust/crates/openssl/", // It's complicated. + "external/rust/cxx/", // It's REALLY complicated. + ]; + MIGRATION_DENYLIST.iter().any(|prefix| self.relpath.starts_with(prefix)) + } + pub fn is_android_bp_healthy(&self) -> bool { + !self.is_migration_denied() + && self.android_bp().exists() + && self.cargo_embargo_json().exists() + && self.generate_android_bp_success() + && self.android_bp_unchanged() + } + pub fn patch_success(&self) -> bool { + self.patch_output.iter().all(|output| output.status.success()) + } + pub fn generate_android_bp_success(&self) -> bool { + self.generate_android_bp_output.as_ref().is_some_and(|output| output.status.success()) + } + pub fn android_bp_unchanged(&self) -> bool { + self.android_bp_diff.as_ref().is_some_and(|output| output.status.success()) + } + + pub fn print(&self) -> Result<()> { + println!("{} {} {}", self.name(), self.version(), self.relpath.display()); + if let Some(output) = &self.generate_android_bp_output { + println!("generate Android.bp exit status: {}", output.status); + println!("{}", from_utf8(&output.stdout)?); + println!("{}", from_utf8(&output.stderr)?); + } + if let Some(output) = &self.android_bp_diff { + println!("diff exit status: {}", output.status); + println!("{}", from_utf8(&output.stdout)?); + println!("{}", from_utf8(&output.stderr)?); + } + Ok(()) + } + + // Make a clean copy of the crate in out/ + pub fn stage_crate(&self) -> Result<()> { + let staging_path_absolute = self.root().join(self.staging_path()); + ensure_exists_and_empty(&staging_path_absolute)?; + remove_dir_all(&staging_path_absolute) + .context(format!("Failed to remove {}", staging_path_absolute.display()))?; + copy_dir(self.path(), &staging_path_absolute).context(format!( + "Failed to copy {} to {}", + self.path().display(), + staging_path_absolute.display() + ))?; + if staging_path_absolute.join(".git").is_dir() { + remove_dir_all(staging_path_absolute.join(".git")) + .with_context(|| "Failed to remove .git".to_string())?; + } + self.copy_customizations() + } + pub fn copy_customizations(&self) -> Result<()> { + if let Some(customization_dir) = self.customization_dir() { + let customization_dir_absolute = self.root().join(customization_dir); + let staging_path_absolute = self.root().join(self.staging_path()); + for entry in read_dir(&customization_dir_absolute) + .context(format!("Failed to read_dir {}", customization_dir_absolute.display()))? + { + let entry = entry?; + let entry_path = entry.path(); + let mut filename = entry.file_name().to_os_string(); + if entry_path.is_dir() { + copy_dir(&entry_path, staging_path_absolute.join(&filename)).context( + format!( + "Failed to copy {} to {}", + entry_path.display(), + staging_path_absolute.display() + ), + )?; + } else { + if let Some(extension) = entry_path.extension() { + if extension == "disabled" { + let mut new_filename = entry_path.clone(); + new_filename.set_extension(""); + filename = new_filename + .file_name() + .context(format!( + "Failed to get file name for {}", + new_filename.display() + ))? + .to_os_string(); + } + } + copy(&entry_path, staging_path_absolute.join(filename)).context(format!( + "Failed to copy {} to {}", + entry_path.display(), + staging_path_absolute.display() + ))?; + } + } + } + Ok(()) + } + + pub fn apply_patches(&mut self) -> Result<()> { + if let Some(patch_dir) = self.patch_dir() { + let patch_dir_absolute = self.root().join(patch_dir); + if patch_dir_absolute.exists() { + println!("Patching {}", self.path().display()); + for entry in read_dir(&patch_dir_absolute) + .context(format!("Failed to read_dir {}", patch_dir_absolute.display()))? + { + let entry = entry?; + if entry.file_name() == "Android.bp.patch" + || entry.file_name() == "Android.bp.diff" + { + continue; + } + let entry_path = entry.path(); + println!(" applying {}", entry_path.display()); + let output = Command::new("patch") + .args(["-p1", "-l"]) + .arg(&entry_path) + .current_dir(self.root().join(self.staging_path())) + .output()?; + if !output.status.success() { + println!( + "Failed to apply {}\nstdout:\n{}\nstderr:\n:{}", + entry_path.display(), + from_utf8(&output.stdout)?, + from_utf8(&output.stderr)? + ); + } + self.patch_output.push(output); + } + } + } + Ok(()) + } + + pub fn android_bp_diff(&self) -> Option<&Output> { + self.android_bp_diff.as_ref() + } + pub fn generate_android_bp_output(&self) -> Option<&Output> { + self.generate_android_bp_output.as_ref() + } + pub fn set_generate_android_bp_output(&mut self, c2a_output: Output, diff_output: Output) { + self.generate_android_bp_output.replace(c2a_output); + self.android_bp_diff.replace(diff_output); + } + pub fn set_patch_output(&mut self, patch_output: Vec<Output>) { + self.patch_output = patch_output; + } +} + +pub trait Migratable { + fn is_migration_eligible(&self) -> bool; + fn is_migratable(&self) -> bool; +} + +impl Migratable for Crate { + fn is_migration_eligible(&self) -> bool { + self.is_crates_io() + && !self.is_migration_denied() + && self.android_bp().exists() + && self.cargo_embargo_json().exists() + } + fn is_migratable(&self) -> bool { + self.patch_success() && self.generate_android_bp_success() && self.android_bp_unchanged() + } +} + +#[cfg(test)] +mod tests { + use std::fs::{create_dir, write}; + + use super::*; + use anyhow::anyhow; + use tempfile::tempdir; + + fn write_test_manifest(temp_crate_dir: &Path, name: &str, version: &str) -> Result<PathBuf> { + let cargo_toml: PathBuf = [temp_crate_dir, &Path::new("Cargo.toml")].iter().collect(); + write( + cargo_toml.as_path(), + format!("[package]\nname = \"{}\"\nversion = \"{}\"\n", name, version), + )?; + let lib_rs: PathBuf = [temp_crate_dir, &Path::new("src/lib.rs")].iter().collect(); + create_dir(lib_rs.parent().ok_or(anyhow!("Failed to get parent"))?)?; + write(lib_rs.as_path(), "// foo")?; + Ok(cargo_toml) + } + + #[test] + fn test_from_and_properties() -> Result<()> { + let temp_crate_dir = tempdir()?; + let cargo_toml = write_test_manifest(temp_crate_dir.path(), "foo", "1.2.0")?; + let krate = Crate::from(&cargo_toml, &"/", None::<&&str>)?; + assert_eq!(krate.name(), "foo"); + assert_eq!(krate.version().to_string(), "1.2.0"); + assert!(krate.is_crates_io()); + assert_eq!(krate.android_bp(), temp_crate_dir.path().join("Android.bp")); + assert_eq!(krate.cargo_embargo_json(), temp_crate_dir.path().join("cargo_embargo.json")); + Ok(()) + } + + #[test] + fn test_from_error() -> Result<()> { + let temp_crate_dir = tempdir()?; + let cargo_toml = write_test_manifest(temp_crate_dir.path(), "foo", "1.2.0")?; + assert!(Crate::from(&cargo_toml, &"/blah", None::<&&str>).is_err()); + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/lib.rs b/tools/external_crates/crate_health/src/lib.rs new file mode 100644 index 000000000..b2b8f57a4 --- /dev/null +++ b/tools/external_crates/crate_health/src/lib.rs @@ -0,0 +1,92 @@ +// Copyright (C) 2023 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. + +use std::env::current_dir; +use std::fs::{create_dir_all, remove_dir_all}; +use std::path::{Path, PathBuf}; + +use anyhow::{anyhow, Context, Result}; +use semver::Version; +use thiserror::Error; + +pub use self::crate_type::{Crate, Migratable}; +mod crate_type; + +pub use self::crate_collection::CrateCollection; +mod crate_collection; + +pub use self::reports::{ReportEngine, SizeReport, Table}; +mod reports; + +pub use self::migration::migrate; +mod migration; + +pub use self::pseudo_crate::write_pseudo_crate; +mod pseudo_crate; + +pub use self::version_match::{CompatibleVersionPair, VersionMatch, VersionPair}; +mod version_match; + +pub use self::android_bp::{build_cargo_embargo, generate_android_bps}; +mod android_bp; + +pub use self::name_and_version::{ + IsUpgradableTo, NameAndVersion, NameAndVersionRef, NamedAndVersioned, +}; +mod name_and_version; + +#[cfg(test)] +pub use self::name_and_version_map::try_name_version_map_from_iter; +pub use self::name_and_version_map::{ + crates_with_multiple_versions, crates_with_single_version, most_recent_version, + NameAndVersionMap, +}; +mod name_and_version_map; + +#[derive(Error, Debug)] +pub enum CrateError { + #[error("Virtual crate: {0}")] + VirtualCrate(PathBuf), + + #[error("Duplicate crate version: {0} {1}")] + DuplicateCrateVersion(String, Version), +} + +pub fn default_repo_root() -> Result<PathBuf> { + let cwd = current_dir().context("Could not get current working directory")?; + for cur in cwd.ancestors() { + for e in cur.read_dir()? { + if e?.file_name() == ".repo" { + return Ok(cur.to_path_buf()); + } + } + } + Err(anyhow!(".repo directory not found in any ancestor of {}", cwd.display())) +} + +pub fn default_output_dir(filename: &str) -> PathBuf { + PathBuf::from("/google/data/rw/users") + .join(&whoami::username()[..2]) + .join(whoami::username()) + .join("www") + .join(filename) +} + +pub fn ensure_exists_and_empty(dir: &impl AsRef<Path>) -> Result<()> { + let dir = dir.as_ref(); + if dir.exists() { + remove_dir_all(&dir).context(format!("Failed to remove {}", dir.display()))?; + } + create_dir_all(&dir).context(format!("Failed to create {}", dir.display())) +} diff --git a/tools/external_crates/crate_health/src/migration.rs b/tools/external_crates/crate_health/src/migration.rs new file mode 100644 index 000000000..0555cd463 --- /dev/null +++ b/tools/external_crates/crate_health/src/migration.rs @@ -0,0 +1,105 @@ +// Copyright (C) 2023 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. + +use std::{ + fs::copy, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, Context, Result}; +use copy_dir::copy_dir; +use glob::glob; + +use crate::{ + ensure_exists_and_empty, most_recent_version, write_pseudo_crate, CompatibleVersionPair, Crate, + CrateCollection, Migratable, NameAndVersionMap, VersionMatch, +}; + +static CUSTOMIZATIONS: &'static [&'static str] = + &["*.bp", "cargo_embargo.json", "patches", "METADATA"]; + +impl<'a> CompatibleVersionPair<'a, Crate> { + pub fn copy_customizations(&self) -> Result<()> { + let dest_dir_absolute = self.dest.root().join( + self.dest + .customization_dir() + .ok_or(anyhow!("No customization dir for {}", self.dest.path().display()))?, + ); + ensure_exists_and_empty(&dest_dir_absolute)?; + for pattern in CUSTOMIZATIONS { + let full_pattern = self.source.path().join(pattern); + for entry in glob( + full_pattern + .to_str() + .ok_or(anyhow!("Failed to convert path {} to str", full_pattern.display()))?, + )? { + let entry = entry?; + let mut filename = entry + .file_name() + .context(format!("Failed to get file name for {}", entry.display()))? + .to_os_string(); + if entry.is_dir() { + copy_dir(&entry, dest_dir_absolute.join(filename)).context(format!( + "Failed to copy {} to {}", + entry.display(), + dest_dir_absolute.display() + ))?; + } else { + if let Some(extension) = entry.extension() { + if extension == "bp" { + filename.push(".disabled"); + } + } + copy(&entry, dest_dir_absolute.join(filename)).context(format!( + "Failed to copy {} to {}", + entry.display(), + dest_dir_absolute.display() + ))?; + } + } + } + Ok(()) + } +} + +pub fn migrate<P: Into<PathBuf>>( + repo_root: P, + source_dir: &impl AsRef<Path>, + pseudo_crate_dir: &impl AsRef<Path>, +) -> Result<VersionMatch<CrateCollection>> { + let mut source = CrateCollection::new(repo_root); + source.add_from(source_dir, None::<&&str>)?; + source.map_field_mut().retain(|_nv, krate| krate.is_crates_io()); + + let pseudo_crate_dir_absolute = source.repo_root().join(pseudo_crate_dir); + write_pseudo_crate( + &pseudo_crate_dir_absolute, + source + .filter_versions(&most_recent_version) + .filter(|(_nv, krate)| krate.is_migration_eligible()) + .map(|(_nv, krate)| krate), + )?; + + let mut dest = CrateCollection::new(source.repo_root()); + dest.add_from(&pseudo_crate_dir.as_ref().join("android/vendor"), Some(pseudo_crate_dir))?; + + let mut version_match = VersionMatch::new(source, dest)?; + + version_match.copy_customizations()?; + version_match.stage_crates()?; + version_match.apply_patches()?; + version_match.generate_android_bps()?; + + Ok(version_match) +} diff --git a/tools/external_crates/crate_health/src/name_and_version.rs b/tools/external_crates/crate_health/src/name_and_version.rs new file mode 100644 index 000000000..90dc24319 --- /dev/null +++ b/tools/external_crates/crate_health/src/name_and_version.rs @@ -0,0 +1,189 @@ +// Copyright (C) 2023 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. + +use std::{ + borrow::Borrow, + cmp::Ordering, + hash::{Hash, Hasher}, +}; + +#[cfg(test)] +use anyhow::Result; + +use semver::{BuildMetadata, Prerelease, Version, VersionReq}; + +static MIN_VERSION: Version = + Version { major: 0, minor: 0, patch: 0, pre: Prerelease::EMPTY, build: BuildMetadata::EMPTY }; + +pub trait NamedAndVersioned { + fn name(&self) -> &str; + fn version(&self) -> &Version; + fn key<'a>(&'a self) -> NameAndVersionRef<'a>; +} + +#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] +pub struct NameAndVersion { + name: String, + version: Version, +} + +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct NameAndVersionRef<'a> { + name: &'a str, + version: &'a Version, +} + +impl NameAndVersion { + pub fn new(name: String, version: Version) -> Self { + NameAndVersion { name, version } + } + pub fn from(nv: &impl NamedAndVersioned) -> Self { + NameAndVersion { name: nv.name().to_string(), version: nv.version().clone() } + } + pub fn min_version(name: String) -> Self { + NameAndVersion { name, version: MIN_VERSION.clone() } + } + #[cfg(test)] + pub fn try_from_str(name: &str, version: &str) -> Result<Self> { + Ok(NameAndVersion::new(name.to_string(), Version::parse(version)?)) + } +} + +impl NamedAndVersioned for NameAndVersion { + fn name(&self) -> &str { + self.name.as_str() + } + + fn version(&self) -> &Version { + &self.version + } + fn key<'k>(&'k self) -> NameAndVersionRef<'k> { + NameAndVersionRef::new(self.name(), self.version()) + } +} + +impl<'a> NameAndVersionRef<'a> { + pub fn new(name: &'a str, version: &'a Version) -> Self { + NameAndVersionRef { name, version } + } +} + +impl<'a> NamedAndVersioned for NameAndVersionRef<'a> { + fn name(&self) -> &str { + self.name + } + fn version(&self) -> &Version { + self.version + } + fn key<'k>(&'k self) -> NameAndVersionRef<'k> { + *self + } +} + +impl<'a> Borrow<dyn NamedAndVersioned + 'a> for NameAndVersion { + fn borrow(&self) -> &(dyn NamedAndVersioned + 'a) { + self + } +} + +impl<'a> PartialEq for (dyn NamedAndVersioned + 'a) { + fn eq(&self, other: &Self) -> bool { + self.key().eq(&other.key()) + } +} + +impl<'a> Eq for (dyn NamedAndVersioned + 'a) {} + +impl<'a> PartialOrd for (dyn NamedAndVersioned + 'a) { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.key().partial_cmp(&other.key()) + } +} + +impl<'a> Ord for (dyn NamedAndVersioned + 'a) { + fn cmp(&self, other: &Self) -> Ordering { + self.key().cmp(&other.key()) + } +} + +impl<'a> Hash for (dyn NamedAndVersioned + 'a) { + fn hash<H: Hasher>(&self, state: &mut H) { + self.key().hash(state) + } +} + +pub trait IsUpgradableTo: NamedAndVersioned { + fn is_upgradable_to(&self, other: &impl NamedAndVersioned) -> bool { + self.name() == other.name() + && VersionReq::parse(&self.version().to_string()) + .is_ok_and(|req| req.matches(other.version())) + } +} + +impl<'a> IsUpgradableTo for NameAndVersion {} +impl<'a> IsUpgradableTo for NameAndVersionRef<'a> {} + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::Result; + + #[test] + fn test_name_version_ref() -> Result<()> { + let version = Version::parse("2.3.4")?; + let compat1 = Version::parse("2.3.5")?; + let compat2 = Version::parse("2.4.0")?; + let incompat = Version::parse("3.0.0")?; + let older = Version::parse("2.3.3")?; + let nvp = NameAndVersionRef::new("foo", &version); + assert_eq!(nvp.name(), "foo"); + assert_eq!(nvp.version().to_string(), "2.3.4"); + assert!(nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat1)), "Patch update"); + assert!( + nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat2)), + "Minor version update" + ); + assert!( + !nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &incompat)), + "Incompatible (major version) update" + ); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &older)), "Downgrade"); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("bar", &compat1)), "Different name"); + Ok(()) + } + + #[test] + fn test_name_and_version() -> Result<()> { + let version = Version::parse("2.3.4")?; + let compat1 = Version::parse("2.3.5")?; + let compat2 = Version::parse("2.4.0")?; + let incompat = Version::parse("3.0.0")?; + let older = Version::parse("2.3.3")?; + let nvp = NameAndVersion::new("foo".to_string(), version); + assert_eq!(nvp.name(), "foo"); + assert_eq!(nvp.version().to_string(), "2.3.4"); + assert!(nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat1)), "Patch update"); + assert!( + nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat2)), + "Minor version update" + ); + assert!( + !nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &incompat)), + "Incompatible (major version) update" + ); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &older)), "Downgrade"); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("bar", &compat1)), "Different name"); + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/name_and_version_map.rs b/tools/external_crates/crate_health/src/name_and_version_map.rs new file mode 100644 index 000000000..f35d40415 --- /dev/null +++ b/tools/external_crates/crate_health/src/name_and_version_map.rs @@ -0,0 +1,289 @@ +// Copyright (C) 2023 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. + +use std::collections::{BTreeMap, HashSet}; + +use anyhow::Result; +use itertools::Itertools; +use semver::Version; + +use crate::{CrateError, IsUpgradableTo, NameAndVersion, NamedAndVersioned}; + +pub trait NameAndVersionMap { + type Value; + + fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>; + fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>; + + fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError>; + fn num_crates(&self) -> usize; + fn contains_name(&self, name: &str) -> bool { + self.get_versions(name).next().is_some() + } + fn get_versions<'a, 'b>( + &'a self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>; + fn get_versions_mut<'a, 'b>( + &'a mut self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>; + fn get_version_upgradable_from<T: NamedAndVersioned + IsUpgradableTo>( + &self, + other: &T, + ) -> Option<&NameAndVersion> { + let mut best_version = None; + for (nv, _val) in self.get_versions(other.name()) { + if other.is_upgradable_to(nv) { + best_version.replace(nv); + } + } + best_version + } + fn filter_versions< + 'a: 'b, + 'b, + F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version> + + 'a, + >( + &'a self, + f: F, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>; +} + +impl<ValueType> NameAndVersionMap for BTreeMap<NameAndVersion, ValueType> { + type Value = ValueType; + + fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value> { + self + } + + fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value> { + self + } + + fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError> { + if self.contains_key(&key) { + Err(CrateError::DuplicateCrateVersion(key.name().to_string(), key.version().clone())) + } else { + self.insert(key, val); + Ok(()) + } + } + + fn num_crates(&self) -> usize { + let mut seen = ::std::collections::HashSet::new(); + for nv in self.keys() { + seen.insert(nv.name().to_string()); + } + seen.len() + } + + fn get_versions<'a, 'b>( + &'a self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> { + let owned_name = name.to_string(); + Box::new( + self.range(std::ops::RangeFrom { + start: NameAndVersion::min_version(name.to_string()), + }) + .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }), + ) + } + + fn get_versions_mut<'a, 'b>( + &'a mut self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a> { + let owned_name = name.to_string(); + Box::new( + self.range_mut(std::ops::RangeFrom { + start: NameAndVersion::min_version(name.to_string()), + }) + .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }), + ) + } + + fn filter_versions< + 'a: 'b, + 'b, + F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version> + + 'a, + >( + &'a self, + f: F, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> { + let mut kept_keys: HashSet<NameAndVersion> = HashSet::new(); + for (key, mut group) in self.iter().group_by(|item| item.0.name()).into_iter() { + kept_keys.extend( + f(&mut group).into_iter().map(move |v| NameAndVersion::new(key.to_string(), v)), + ); + } + Box::new(self.iter().filter(move |(nv, _krate)| kept_keys.contains(*nv))) + } +} + +pub fn crates_with_single_version<'a, ValueType>( + versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, +) -> HashSet<Version> { + let mut vset = HashSet::new(); + versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count(); + if vset.len() != 1 { + vset.clear() + } + vset +} + +pub fn crates_with_multiple_versions<'a, ValueType>( + versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, +) -> HashSet<Version> { + let mut vset = HashSet::new(); + versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count(); + if vset.len() == 1 { + vset.clear() + } + vset +} + +pub fn most_recent_version<'a, ValueType>( + versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, +) -> HashSet<Version> { + let mut vset = HashSet::new(); + if let Some((nv, _crate)) = versions.into_iter().last() { + vset.insert(nv.version().clone()); + } + vset +} + +#[cfg(test)] +pub fn try_name_version_map_from_iter<'a, ValueType>( + nvs: impl IntoIterator<Item = (&'a str, &'a str, ValueType)>, +) -> Result<BTreeMap<NameAndVersion, ValueType>> { + let mut test_map = BTreeMap::new(); + for (name, version, val) in nvs { + test_map.insert_or_error(NameAndVersion::try_from_str(name, version)?, val)?; + } + Ok(test_map) +} + +#[cfg(test)] +mod tests { + use crate::{NameAndVersion, NameAndVersionRef}; + + use super::*; + use anyhow::Result; + use itertools::assert_equal; + + #[test] + fn test_name_and_version_map_empty() -> Result<()> { + let mut test_map: BTreeMap<NameAndVersion, String> = BTreeMap::new(); + let v = Version::parse("1.2.3")?; + let nvp = NameAndVersionRef::new("foo", &v); + // let nvp = NameAndVersion::try_from_str("foo", "1.2.3")?; + assert_eq!(test_map.num_crates(), 0); + assert!(!test_map.contains_key(&nvp as &dyn NamedAndVersioned)); + assert!(!test_map.contains_name("foo")); + assert!(test_map.get(&nvp as &dyn NamedAndVersioned).is_none()); + assert!(test_map.get_mut(&nvp as &dyn NamedAndVersioned).is_none()); + Ok(()) + } + + #[test] + fn test_name_and_version_map_nonempty() -> Result<()> { + let mut test_map = try_name_version_map_from_iter([ + ("foo", "1.2.3", "foo v1".to_string()), + ("foo", "2.3.4", "foo v2".to_string()), + ("bar", "1.0.0", "bar".to_string()), + ])?; + + let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?; + let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?; + let bar = NameAndVersion::try_from_str("bar", "1.0.0")?; + let wrong_name = NameAndVersion::try_from_str("baz", "1.2.3")?; + let wrong_version = NameAndVersion::try_from_str("foo", "1.0.0")?; + + assert_eq!(test_map.num_crates(), 2); + + assert!(test_map.contains_key(&foo1)); + assert!(test_map.contains_key(&foo2)); + assert!(test_map.contains_key(&bar)); + assert!(!test_map.contains_key(&wrong_name)); + assert!(!test_map.contains_key(&wrong_version)); + + assert!(test_map.contains_name("foo")); + assert!(test_map.contains_name("bar")); + assert!(!test_map.contains_name("baz")); + + assert_eq!(test_map.get(&foo1), Some(&"foo v1".to_string())); + assert_eq!(test_map.get(&foo2), Some(&"foo v2".to_string())); + assert_eq!(test_map.get(&bar), Some(&"bar".to_string())); + assert!(test_map.get(&wrong_name).is_none()); + assert!(test_map.get(&wrong_version).is_none()); + + assert_eq!(test_map.get_mut(&foo1), Some(&mut "foo v1".to_string())); + assert_eq!(test_map.get_mut(&foo2), Some(&mut "foo v2".to_string())); + assert_eq!(test_map.get_mut(&bar), Some(&mut "bar".to_string())); + assert!(test_map.get_mut(&wrong_name).is_none()); + assert!(test_map.get_mut(&wrong_version).is_none()); + + assert_eq!( + test_map.get_version_upgradable_from(&NameAndVersion::try_from_str("foo", "1.2.2")?), + Some(&foo1) + ); + + // TOOD: Iter + assert_equal(test_map.keys(), [&bar, &foo1, &foo2]); + + assert_equal(test_map.values(), ["bar", "foo v1", "foo v2"]); + assert_equal(test_map.values_mut(), ["bar", "foo v1", "foo v2"]); + + assert_equal( + test_map.iter().filter(|(_nv, x)| x.starts_with("foo")).map(|(_nv, val)| val), + ["foo v1", "foo v2"], + ); + + test_map.retain(|_nv, x| x.starts_with("foo")); + assert_equal(test_map.values(), ["foo v1", "foo v2"]); + + Ok(()) + } + + #[test] + fn test_filter_versions() -> Result<()> { + let test_map = try_name_version_map_from_iter([ + ("foo", "1.2.3", ()), + ("foo", "2.3.4", ()), + ("bar", "1.0.0", ()), + ])?; + let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?; + let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?; + let bar = NameAndVersion::try_from_str("bar", "1.0.0")?; + + assert_equal( + test_map.filter_versions(crates_with_single_version).map(|(nv, _)| nv), + [&bar], + ); + assert_equal( + test_map.filter_versions(crates_with_multiple_versions).map(|(nv, _)| nv), + [&foo1, &foo2], + ); + assert_equal( + test_map.filter_versions(most_recent_version).map(|(nv, _)| nv), + [&bar, &foo2], + ); + + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/pseudo_crate.rs b/tools/external_crates/crate_health/src/pseudo_crate.rs new file mode 100644 index 000000000..25b45bf0f --- /dev/null +++ b/tools/external_crates/crate_health/src/pseudo_crate.rs @@ -0,0 +1,91 @@ +// Copyright (C) 2023 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. + +use std::{ + fs::{create_dir, write}, + path::Path, + process::Command, + str::from_utf8, +}; + +use anyhow::{anyhow, Context, Result}; +use serde::Serialize; +use tinytemplate::TinyTemplate; + +use crate::{ensure_exists_and_empty, NamedAndVersioned}; + +static CARGO_TOML_TEMPLATE: &'static str = include_str!("templates/Cargo.toml.template"); + +#[derive(Serialize)] +struct Dep { + name: String, + version: String, +} + +#[derive(Serialize)] +struct CargoToml { + deps: Vec<Dep>, +} + +pub fn write_pseudo_crate<'a>( + dest_absolute: &impl AsRef<Path>, + crates: impl Iterator<Item = &'a (impl NamedAndVersioned + 'a)>, +) -> Result<()> { + let dest_absolute = dest_absolute.as_ref(); + ensure_exists_and_empty(&dest_absolute)?; + + let mut deps = Vec::new(); + for krate in crates { + // Special cases: + // * libsqlite3-sys is a sub-crate of rusqlite + // * remove_dir_all has a version not known by crates.io (b/313489216) + if krate.name() != "libsqlite3-sys" { + deps.push(Dep { + name: krate.name().to_string(), + version: if krate.name() == "remove_dir_all" + && krate.version().to_string() == "0.7.1" + { + "0.7.0".to_string() + } else { + krate.version().to_string() + }, + }); + } + } + + let mut tt = TinyTemplate::new(); + tt.add_template("cargo_toml", CARGO_TOML_TEMPLATE)?; + let cargo_toml = dest_absolute.join("Cargo.toml"); + write(&cargo_toml, tt.render("cargo_toml", &CargoToml { deps })?)?; + + create_dir(dest_absolute.join("src")).context("Failed to create src dir")?; + write(dest_absolute.join("src/lib.rs"), "// Nothing").context("Failed to create src/lib.rs")?; + + let vendor_output = Command::new("cargo") + .args(["vendor", "android/vendor"]) + .current_dir(dest_absolute) + .output()?; + if !vendor_output.status.success() { + return Err(anyhow!( + "cargo vendor failed with exit code {}\nstdout:\n{}\nstderr:\n{}", + vendor_output.status, + from_utf8(&vendor_output.stdout)?, + from_utf8(&vendor_output.stderr)? + )); + } + + // TODO: Run "cargo deny" + + Ok(()) +} diff --git a/tools/external_crates/crate_health/src/reports.rs b/tools/external_crates/crate_health/src/reports.rs new file mode 100644 index 000000000..c70b5676b --- /dev/null +++ b/tools/external_crates/crate_health/src/reports.rs @@ -0,0 +1,326 @@ +// Copyright (C) 2023 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. + +use std::{fmt::Display, fs::write, path::Path, str::from_utf8}; + +use crate::{ + crates_with_multiple_versions, crates_with_single_version, Crate, CrateCollection, + NameAndVersionMap, NamedAndVersioned, VersionMatch, VersionPair, +}; + +use anyhow::Result; +use serde::Serialize; +use tinytemplate::TinyTemplate; + +static SIZE_REPORT_TEMPLATE: &'static str = include_str!("templates/size_report.html.template"); +static TABLE_TEMPLATE: &'static str = include_str!("templates/table.html.template"); + +static CRATE_HEALTH_REPORT_TEMPLATE: &'static str = + include_str!("templates/crate_health.html.template"); +static MIGRATION_REPORT_TEMPLATE: &'static str = + include_str!("templates/migration_report.html.template"); + +pub struct ReportEngine<'template> { + tt: TinyTemplate<'template>, +} + +fn len_formatter(value: &serde_json::Value, out: &mut String) -> tinytemplate::error::Result<()> { + match value { + serde_json::Value::Array(a) => { + out.push_str(&format!("{}", a.len())); + Ok(()) + } + _ => Err(tinytemplate::error::Error::GenericError { + msg: "Can only use length formatter on an array".to_string(), + }), + } +} + +fn linkify(text: &dyn Display, url: &dyn Display) -> String { + format!("<a href=\"{}\">{}</a>", url, text) +} + +impl<'template> ReportEngine<'template> { + pub fn new() -> Result<ReportEngine<'template>> { + let mut tt = TinyTemplate::new(); + tt.add_template("size_report", SIZE_REPORT_TEMPLATE)?; + tt.add_template("table", TABLE_TEMPLATE)?; + tt.add_template("crate_health", CRATE_HEALTH_REPORT_TEMPLATE)?; + tt.add_template("migration", MIGRATION_REPORT_TEMPLATE)?; + tt.add_formatter("len", len_formatter); + Ok(ReportEngine { tt }) + } + pub fn size_report(&self, cc: &CrateCollection) -> Result<String> { + let num_crates = cc.num_crates(); + let crates_with_single_version = cc.filter_versions(&crates_with_single_version).count(); + Ok(self.tt.render( + "size_report", + &SizeReport { + num_crates, + crates_with_single_version, + crates_with_multiple_versions: num_crates - crates_with_single_version, + num_dirs: cc.map_field().len(), + }, + )?) + } + pub fn table<'a>(&self, crates: impl Iterator<Item = &'a Crate>) -> Result<String> { + let mut table = Table::new(&[&"Crate", &"Version", &"Path"]); + for krate in crates { + table.add_row(&[ + &linkify(&krate.name(), &krate.crates_io_url()), + &krate.version().to_string(), + &krate.aosp_url().map_or(format!("{}", krate.relpath().display()), |url| { + linkify(&krate.relpath().display(), &url) + }), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn health_table<'a>(&self, crates: impl Iterator<Item = &'a Crate>) -> Result<String> { + let mut table = Table::new(&[ + &"Crate", + &"Version", + &"Path", + &"Has Android.bp", + &"Generate Android.bp succeeds", + &"Android.bp unchanged", + &"Has cargo_embargo.json", + &"On migration denylist", + ]); + table.set_vertical_headers(); + for krate in crates { + table.add_row(&[ + &linkify(&krate.name(), &krate.crates_io_url()), + &krate.version().to_string(), + &krate.aosp_url().map_or(format!("{}", krate.relpath().display()), |url| { + linkify(&krate.relpath().display(), &url) + }), + &prefer_yes(krate.android_bp().exists()), + &prefer_yes_or_summarize( + krate.generate_android_bp_success(), + krate + .generate_android_bp_output() + .map_or("Error".to_string(), |o| { + format!( + "STDOUT:\n{}\n\nSTDERR:\n{}", + from_utf8(&o.stdout).unwrap_or("Error"), + from_utf8(&o.stderr).unwrap_or("Error") + ) + }) + .as_str(), + ), + &prefer_yes_or_summarize( + krate.android_bp_unchanged(), + krate + .android_bp_diff() + .map_or("Error", |o| from_utf8(&o.stdout).unwrap_or("Error")), + ), + &prefer_yes(krate.cargo_embargo_json().exists()), + &prefer_no(krate.is_migration_denied()), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn migration_ineligible_table<'a>( + &self, + crates: impl Iterator<Item = &'a Crate>, + ) -> Result<String> { + let mut table = Table::new(&[ + &"Crate", + &"Version", + &"Path", + &"In crates.io", + &"Denylisted", + &"Has Android.bp", + &"Has cargo_embargo.json", + ]); + table.set_vertical_headers(); + for krate in crates { + table.add_row(&[ + &linkify(&krate.name(), &krate.crates_io_url()), + &krate.version().to_string(), + &krate.aosp_url().map_or(format!("{}", krate.relpath().display()), |url| { + linkify(&krate.relpath().display(), &url) + }), + &prefer_yes(krate.is_crates_io()), + &prefer_no(krate.is_migration_denied()), + &prefer_yes(krate.android_bp().exists()), + &prefer_yes(krate.cargo_embargo_json().exists()), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn migration_eligible_table<'a>( + &self, + crate_pairs: impl Iterator<Item = VersionPair<'a, Crate>>, + ) -> Result<String> { + let mut table = Table::new(&[ + &"Crate", + &"Version", + &"Path", + &"Compatible version", + &"Patch succeeds", + &"Generate Android.bp succeeds", + &"Android.bp unchanged", + ]); + table.set_vertical_headers(); + for crate_pair in crate_pairs { + let source = crate_pair.source; + let maybe_dest = crate_pair.dest; + table.add_row(&[ + &linkify(&source.name(), &source.crates_io_url()), + &source.version().to_string(), + &source.aosp_url().map_or(format!("{}", source.relpath().display()), |url| { + linkify(&source.relpath().display(), &url) + }), + maybe_dest.map_or(&"None", |dest| { + if dest.version() != source.version() { + dest.version() + } else { + &"" + } + }), + &prefer_yes(!maybe_dest.is_some_and(|dest| !dest.patch_success())), + &prefer_yes_or_summarize( + !maybe_dest.is_some_and(|dest| !dest.generate_android_bp_success()), + maybe_dest + .map_or("Error".to_string(), |dest| { + dest.generate_android_bp_output().map_or("Error".to_string(), |o| { + format!( + "STDOUT:\n{}\n\nSTDERR:\n{}", + from_utf8(&o.stdout).unwrap_or("Error"), + from_utf8(&o.stderr).unwrap_or("Error") + ) + }) + }) + .as_str(), + ), + &prefer_yes_or_summarize( + !maybe_dest.is_some_and(|dest| !dest.android_bp_unchanged()), + maybe_dest.map_or("Error", |dest| { + dest.android_bp_diff() + .map_or("Error", |o| from_utf8(&o.stdout).unwrap_or("Error")) + }), + ), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn health_report( + &self, + cc: &CrateCollection, + output_path: &impl AsRef<Path>, + ) -> Result<()> { + let chr = CrateHealthReport { + crate_count: self.size_report(cc)?, + crate_multiversion: self.table( + cc.filter_versions(&crates_with_multiple_versions).map(|(_nv, krate)| krate), + )?, + healthy: self.table( + cc.map_field() + .iter() + .filter(|(_nv, krate)| krate.is_android_bp_healthy()) + .map(|(_nv, krate)| krate), + )?, + unhealthy: self.health_table( + cc.map_field() + .iter() + .filter(|(_nv, krate)| !krate.is_android_bp_healthy()) + .map(|(_nv, krate)| krate), + )?, + }; + Ok(write(output_path, self.tt.render("crate_health", &chr)?)?) + } + pub fn migration_report( + &self, + m: &VersionMatch<CrateCollection>, + output_path: &impl AsRef<Path>, + ) -> Result<()> { + let mr = MigrationReport { + migratable: self.table(m.migratable().map(|pair| pair.source))?, + eligible: self.migration_eligible_table(m.eligible_but_not_migratable())?, + ineligible: self.migration_ineligible_table(m.ineligible())?, + superfluous: self.table(m.superfluous().map(|(_nv, krate)| krate))?, + }; + Ok(write(output_path, self.tt.render("migration", &mr)?)?) + } +} + +pub fn prefer_yes(p: bool) -> &'static str { + if p { + "" + } else { + "No" + } +} +pub fn prefer_yes_or_summarize(p: bool, details: &str) -> String { + if p { + "".to_string() + } else { + format!("<details><summary>No</summary><pre>{}</pre></details>", details) + } +} +pub fn prefer_no(p: bool) -> &'static str { + if p { + "Yes" + } else { + "" + } +} + +#[derive(Serialize)] +pub struct SizeReport { + num_crates: usize, + crates_with_single_version: usize, + crates_with_multiple_versions: usize, + num_dirs: usize, +} + +#[derive(Serialize)] +pub struct Table { + header: Vec<String>, + rows: Vec<Vec<String>>, + vertical: bool, +} + +impl Table { + pub fn new(header: &[&dyn Display]) -> Table { + Table { + header: header.iter().map(|cell| format!("{}", cell)).collect::<Vec<_>>(), + rows: Vec::new(), + vertical: false, + } + } + pub fn add_row(&mut self, row: &[&dyn Display]) { + self.rows.push(row.iter().map(|cell| format!("{}", cell)).collect::<Vec<_>>()); + } + pub fn set_vertical_headers(&mut self) { + self.vertical = true; + } +} + +#[derive(Serialize)] +pub struct CrateHealthReport { + crate_count: String, + crate_multiversion: String, + healthy: String, + unhealthy: String, +} +#[derive(Serialize)] +pub struct MigrationReport { + migratable: String, + eligible: String, + ineligible: String, + superfluous: String, +} diff --git a/tools/external_crates/crate_health/src/templates/Cargo.toml.template b/tools/external_crates/crate_health/src/templates/Cargo.toml.template new file mode 100644 index 000000000..52a6c5d78 --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/Cargo.toml.template @@ -0,0 +1,10 @@ +[package] +name = "new-rust-crates" +version = "0.1.0" +edition = "2021" +publish = false +license = "Apache-2.0" + +[dependencies] +{{ for crate in deps }}{crate.name} = "{crate.version}" +{{ endfor }} diff --git a/tools/external_crates/crate_health/src/templates/crate_health.html.template b/tools/external_crates/crate_health/src/templates/crate_health.html.template new file mode 100644 index 000000000..759fa4c1e --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/crate_health.html.template @@ -0,0 +1,33 @@ +<html> +<head> +<title>Crate health</title> +<style> +table, th, td \{ + border: 1px solid black; + border-collapse: collapse; + padding: 3px; +} +td \{ + vertical-align: top; +} +.vertical \{ + writing-mode: vertical-lr; +} +</style> +</head> +<body> +<h1>Crates in external/rust</h1> +{crate_count | unescaped} +<h1>Crates with multiple versions</h1> +{crate_multiversion | unescaped} +<h1>Healthy crates in external/rust</h1> +<ul> +<li>Has cargo_embargo.json</li> +<li>cargo_embargo runs successfully</li> +<li>The resulting Android.bp is unchanged</li> +</ul> +{healthy | unescaped } +<h1>Unhealthy crates in external/rust</h1> +{unhealthy | unescaped} +</body> +</html>
\ No newline at end of file diff --git a/tools/external_crates/crate_health/src/templates/migration_report.html.template b/tools/external_crates/crate_health/src/templates/migration_report.html.template new file mode 100644 index 000000000..02bae1489 --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/migration_report.html.template @@ -0,0 +1,53 @@ +<html> +<head> +<title>Crate health</title> +<style> +table, th, td \{ + border: 1px solid black; + border-collapse: collapse; + padding: 3px; +} +td \{ + vertical-align: top; +} +.vertical \{ + writing-mode: vertical-lr; +} +</style> +</head> +<body> + +<h1>Migratable crates</h1> +<p>Crates that can be safely migrated: +<ul> +<li>migration-eligible as defined below</li> +<li>Has a vendored crate with a compatible version</li> +<li>Patches apply successfully</li> +<li>cargo_embargo succeeds on the vendored version.</li> +<li>No significant diffs in the resulting Android.bp</li> +</ul> +</p> +{migratable | unescaped} + +<h1>Migration-eligible crates</h1> +<p>Crates that are eligible for migration, but can't yet be migrated: +<ul> +<li>It is in crates.io</li> +<li>It is not denylisted</li> +<li>It has an Android.bp</li> +<li>It has a cargo_embargo.json</li> +</ul> +</p> +{eligible | unescaped} + +<h1>Ineligible crates</h1> +<p>Crates that are not eligible for migration</p> +</p> +{ineligible | unescaped} + +<h1>Superfluous vendored crates</h1> +<p>Vendored crates that we don't know anything about</p> +{superfluous | unescaped} + +</body> +</html>
\ No newline at end of file diff --git a/tools/external_crates/crate_health/src/templates/size_report.html.template b/tools/external_crates/crate_health/src/templates/size_report.html.template new file mode 100644 index 000000000..23f3aec8e --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/size_report.html.template @@ -0,0 +1,6 @@ +<ul> + <li>{num_crates} crates</li> + <li>{crates_with_single_version} have a single version</li> + <li>{crates_with_multiple_versions} have multiple versions</li> + <li>{num_dirs} total crate directories</li> +</ul> diff --git a/tools/external_crates/crate_health/src/templates/table.html.template b/tools/external_crates/crate_health/src/templates/table.html.template new file mode 100644 index 000000000..b36da4761 --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/table.html.template @@ -0,0 +1,10 @@ +<p>{rows | len} crate directories</p> +<table> +<tr>{{ for cell in header}} + <th{{ if vertical }} class="vertical"{{ endif }}>{cell}</th>{{ endfor }} +</tr> +{{ for row in rows }} +<tr>{{ for cell in row }} + <td>{cell | unescaped}</td>{{ endfor }} +</tr>{{ endfor }} +</table>
\ No newline at end of file diff --git a/tools/external_crates/crate_health/src/version_match.rs b/tools/external_crates/crate_health/src/version_match.rs new file mode 100644 index 000000000..6670302d0 --- /dev/null +++ b/tools/external_crates/crate_health/src/version_match.rs @@ -0,0 +1,417 @@ +// Copyright (C) 2023 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. + +use std::collections::BTreeMap; + +use anyhow::{anyhow, Result}; + +use crate::{ + generate_android_bps, CrateCollection, Migratable, NameAndVersion, NameAndVersionMap, + NamedAndVersioned, +}; + +#[derive(Debug)] +pub struct VersionPair<'a, T> { + pub source: &'a T, + pub dest: Option<&'a T>, +} + +#[derive(Debug)] +pub struct CompatibleVersionPair<'a, T> { + pub source: &'a T, + pub dest: &'a T, +} + +impl<'a, T> VersionPair<'a, T> { + pub fn to_compatible(self) -> Option<CompatibleVersionPair<'a, T>> { + self.dest.map(|dest| CompatibleVersionPair { source: self.source, dest }) + } +} + +pub struct VersionMatch<CollectionType: NameAndVersionMap> { + source: CollectionType, + dest: CollectionType, + compatibility: BTreeMap<NameAndVersion, Option<NameAndVersion>>, +} + +impl<CollectionType: NameAndVersionMap> VersionMatch<CollectionType> { + pub fn new(source: CollectionType, dest: CollectionType) -> Result<Self> { + let mut vm = VersionMatch { source, dest, compatibility: BTreeMap::new() }; + + for nv in vm.dest.map_field().keys() { + vm.compatibility.insert_or_error(nv.to_owned(), None)?; + } + + for nv in vm.source.map_field().keys() { + let compatibility = if let Some(dest_nv) = vm.dest.get_version_upgradable_from(nv) { + vm.compatibility.map_field_mut().remove(dest_nv).ok_or(anyhow!( + "Destination crate version {} {} expected but not found", + dest_nv.name(), + dest_nv.version() + ))?; + Some(dest_nv.clone()) + } else { + None + }; + vm.compatibility.insert_or_error(nv.to_owned(), compatibility)?; + } + + Ok(vm) + } + pub fn is_superfluous(&self, dest: &dyn NamedAndVersioned) -> bool { + self.dest.map_field().contains_key(dest) + && self.compatibility.get(dest).is_some_and(|compatibility| compatibility.is_none()) + } + pub fn get_compatible_version( + &self, + source: &dyn NamedAndVersioned, + ) -> Option<&NameAndVersion> { + self.compatibility.get(source).and_then(|compatibility| compatibility.as_ref()) + } + pub fn get_compatible_item( + &self, + source: &dyn NamedAndVersioned, + ) -> Option<&CollectionType::Value> { + self.get_compatible_version(source).map(|nv| self.dest.map_field().get(nv).unwrap()) + } + pub fn get_compatible_item_mut( + &mut self, + source: &dyn NamedAndVersioned, + ) -> Option<&mut CollectionType::Value> { + let nv = self.get_compatible_version(source)?.clone(); + self.dest.map_field_mut().get_mut(&nv) + } + + pub fn superfluous(&self) -> impl Iterator<Item = (&NameAndVersion, &CollectionType::Value)> { + self.dest.map_field().iter().filter(|(nv, _val)| { + self.compatibility.get(*nv).is_some_and(|compatibility| compatibility.is_none()) + }) + } + pub fn pairs<'a>(&'a self) -> impl Iterator<Item = VersionPair<'a, CollectionType::Value>> { + self.source + .map_field() + .iter() + .map(|(nv, source)| VersionPair { source, dest: self.get_compatible_item(nv) }) + } + pub fn compatible_pairs<'a>( + &'a self, + ) -> impl Iterator<Item = CompatibleVersionPair<'a, CollectionType::Value>> { + self.pairs().into_iter().filter_map( + |pair: VersionPair<'_, <CollectionType as NameAndVersionMap>::Value>| { + pair.to_compatible() + }, + ) + } + pub fn print(&self) { + for (nv, compatibility) in self.compatibility.iter() { + match compatibility { + Some(dest) => { + println!("{} old {} -> new {}", nv.name(), nv.version(), dest.version()) + } + None => { + if self.dest.contains_name(nv.name()) { + println!("{} {} -> NO MATCHING VERSION", nv.name(), nv.version()) + } else { + println!("{} {} -> NOT FOUND IN NEW", nv.name(), nv.version()) + } + } + } + } + for (nv, _) in self.superfluous() { + println!("{} {} -> NOT FOUND IN OLD", nv.name(), nv.version()); + } + } +} + +impl<CollectionType: NameAndVersionMap> VersionMatch<CollectionType> +where + CollectionType::Value: Migratable, +{ + pub fn ineligible(&self) -> impl Iterator<Item = &CollectionType::Value> { + self.source.map_field().values().filter(|val| !val.is_migration_eligible()) + } + pub fn eligible_but_not_migratable<'a>( + &'a self, + ) -> impl Iterator<Item = VersionPair<'a, CollectionType::Value>> { + self.pairs().filter(|pair| { + pair.source.is_migration_eligible() + && !pair.dest.is_some_and(|dest| dest.is_migratable()) + }) + } + pub fn compatible_and_eligible<'a>( + &'a self, + ) -> impl Iterator<Item = CompatibleVersionPair<'a, CollectionType::Value>> { + self.compatible_pairs().filter(|crate_pair| crate_pair.source.is_migration_eligible()) + } + pub fn migratable<'a>( + &'a self, + ) -> impl Iterator<Item = CompatibleVersionPair<'a, CollectionType::Value>> { + self.compatible_pairs() + .filter(|pair| pair.source.is_migration_eligible() && pair.dest.is_migratable()) + } +} + +impl VersionMatch<CrateCollection> { + pub fn copy_customizations(&self) -> Result<()> { + println!("Copy customizations"); + for pair in self.compatible_and_eligible() { + pair.copy_customizations()?; + } + Ok(()) + } + pub fn stage_crates(&mut self) -> Result<()> { + println!("Stage crates"); + for pair in self.compatible_and_eligible() { + pair.dest.stage_crate()?; + } + Ok(()) + } + pub fn apply_patches(&mut self) -> Result<()> { + println!("Apply patches"); + + let (s, d, c) = (&self.source, &mut self.dest, &self.compatibility); + for (source_key, source_crate) in s.map_field() { + if source_crate.is_migration_eligible() { + if let Some(dest_crate) = c.get(source_key).and_then(|compatibility| { + compatibility.as_ref().and_then(|dest_key| d.map_field_mut().get_mut(dest_key)) + }) { + dest_crate.apply_patches()? + } + } + } + Ok(()) + } + pub fn generate_android_bps(&mut self) -> Result<()> { + println!("Generate Android.bp"); + let results = generate_android_bps(self.compatible_and_eligible().map(|pair| pair.dest))?; + for (nv, output) in results.into_iter() { + self.dest + .map_field_mut() + .get_mut(&nv) + .ok_or(anyhow!("Failed to get crate {} {}", nv.name(), nv.version()))? + .set_generate_android_bp_output(output.0, output.1); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::try_name_version_map_from_iter; + + use super::*; + use anyhow::Result; + use itertools::assert_equal; + use std::collections::BTreeMap; + + #[test] + fn test_version_map() -> Result<()> { + let source = try_name_version_map_from_iter([ + ("equal", "2.3.4", "equal src".to_string()), + ("compatible", "1.2.3", "compatible src".to_string()), + ("incompatible", "1.1.1", "incompatible src".to_string()), + ("downgrade", "2.2.2", "downgrade src".to_string()), + ("missing", "1.0.0", "missing src".to_string()), + ])?; + let dest = try_name_version_map_from_iter([ + ("equal", "2.3.4", "equal dest".to_string()), + ("compatible", "1.2.4", "compatible dest".to_string()), + ("incompatible", "2.0.0", "incompatible dest".to_string()), + ("downgrade", "2.2.1", "downgrade dest".to_string()), + ("superfluous", "1.0.0", "superfluous dest".to_string()), + ])?; + + let equal = NameAndVersion::try_from_str("equal", "2.3.4")?; + let compatible_old = NameAndVersion::try_from_str("compatible", "1.2.3")?; + let incompatible_old = NameAndVersion::try_from_str("incompatible", "1.1.1")?; + let downgrade_old = NameAndVersion::try_from_str("downgrade", "2.2.2")?; + let missing = NameAndVersion::try_from_str("missing", "1.0.0")?; + + let compatible_new = NameAndVersion::try_from_str("compatible", "1.2.4")?; + let incompatible_new = NameAndVersion::try_from_str("incompatible", "2.0.0")?; + let downgrade_new = NameAndVersion::try_from_str("downgrade", "2.2.1")?; + let superfluous = NameAndVersion::try_from_str("superfluous", "1.0.0")?; + + let mut version_match = VersionMatch::new(source, dest)?; + assert_eq!( + version_match.compatibility, + BTreeMap::from([ + (downgrade_new.clone(), None), + (downgrade_old.clone(), None), + (equal.clone(), Some(equal.clone())), + (compatible_old.clone(), Some(compatible_new.clone())), + (incompatible_old.clone(), None), + (incompatible_new.clone(), None), + (missing.clone(), None), + (superfluous.clone(), None), + ]) + ); + + // assert!(version_match.has_compatible(&equal)); + assert_eq!(version_match.get_compatible_version(&equal), Some(&equal)); + assert_eq!(version_match.get_compatible_item(&equal), Some(&"equal dest".to_string())); + assert_eq!( + version_match.get_compatible_item_mut(&equal), + Some(&mut "equal dest".to_string()) + ); + assert!(!version_match.is_superfluous(&equal)); + + // assert!(version_match.has_compatible(&compatible_old)); + assert_eq!(version_match.get_compatible_version(&compatible_old), Some(&compatible_new)); + assert_eq!( + version_match.get_compatible_item(&compatible_old), + Some(&"compatible dest".to_string()) + ); + assert_eq!( + version_match.get_compatible_item_mut(&compatible_old), + Some(&mut "compatible dest".to_string()) + ); + assert!(!version_match.is_superfluous(&compatible_old)); + assert!(!version_match.is_superfluous(&compatible_new)); + + // assert!(!version_match.has_compatible(&incompatible_old)); + assert!(version_match.get_compatible_version(&incompatible_old).is_none()); + assert!(version_match.get_compatible_item(&incompatible_old).is_none()); + assert!(version_match.get_compatible_item_mut(&incompatible_old).is_none()); + assert!(!version_match.is_superfluous(&incompatible_old)); + assert!(version_match.is_superfluous(&incompatible_new)); + + // assert!(!version_match.has_compatible(&downgrade_old)); + assert!(version_match.get_compatible_version(&downgrade_old).is_none()); + assert!(version_match.get_compatible_item(&downgrade_old).is_none()); + assert!(version_match.get_compatible_item_mut(&downgrade_old).is_none()); + assert!(!version_match.is_superfluous(&downgrade_old)); + assert!(version_match.is_superfluous(&downgrade_new)); + + // assert!(!version_match.has_compatible(&missing)); + assert!(version_match.get_compatible_version(&missing).is_none()); + assert!(version_match.get_compatible_item(&missing).is_none()); + assert!(version_match.get_compatible_item_mut(&missing).is_none()); + assert!(!version_match.is_superfluous(&missing)); + + // assert!(!version_match.has_compatible(&superfluous)); + assert!(version_match.get_compatible_version(&superfluous).is_none()); + assert!(version_match.get_compatible_item(&superfluous).is_none()); + assert!(version_match.get_compatible_item_mut(&superfluous).is_none()); + assert!(version_match.is_superfluous(&superfluous)); + + assert_equal( + version_match.superfluous().map(|(nv, _dest)| nv), + [&downgrade_new, &incompatible_new, &superfluous], + ); + + assert_equal( + version_match.pairs().map(|x| x.source), + ["compatible src", "downgrade src", "equal src", "incompatible src", "missing src"], + ); + assert_equal( + version_match.pairs().map(|x| x.dest), + [ + Some(&"compatible dest".to_string()), + None, + Some(&"equal dest".to_string()), + None, + None, + ], + ); + + assert_equal( + version_match.compatible_pairs().map(|x| x.source), + ["compatible src", "equal src"], + ); + assert_equal( + version_match.compatible_pairs().map(|x| x.dest), + ["compatible dest", "equal dest"], + ); + + Ok(()) + } + + #[derive(Debug, PartialEq, Eq)] + struct FakeMigratable { + name: String, + source: bool, + eligible: bool, + migratable: bool, + } + + impl FakeMigratable { + pub fn source(name: &str, eligible: bool) -> FakeMigratable { + FakeMigratable { name: name.to_string(), source: true, eligible, migratable: false } + } + pub fn dest(migratable: bool) -> FakeMigratable { + FakeMigratable { name: "".to_string(), source: false, eligible: false, migratable } + } + } + + impl Migratable for FakeMigratable { + fn is_migration_eligible(&self) -> bool { + if !self.source { + unreachable!("Checking if dest is migration-eligible"); + } + self.eligible + } + + fn is_migratable(&self) -> bool { + if self.source { + unreachable!("Checking if source is migratable"); + } + self.migratable + } + } + + #[test] + fn test_migratability() -> Result<()> { + let source = try_name_version_map_from_iter([ + ("ineligible", "1.2.3", FakeMigratable::source("ineligible", false)), + ( + "eligible incompatible", + "1.2.3", + FakeMigratable::source("eligible incompatible", true), + ), + ("eligible compatible", "1.2.3", FakeMigratable::source("eligible compatible", true)), + ("migratable", "1.2.3", FakeMigratable::source("migratable", true)), + ( + "migratable incompatible", + "1.2.3", + FakeMigratable::source("migratable incompatible", true), + ), + ])?; + let dest = try_name_version_map_from_iter([ + ("ineligible", "1.2.3", FakeMigratable::dest(true)), + ("eligible incompatible", "2.0.0", FakeMigratable::dest(true)), + ("eligible compatible", "1.2.3", FakeMigratable::dest(false)), + ("migratable", "1.2.3", FakeMigratable::dest(true)), + ("migratable incompatible", "2.0.0", FakeMigratable::dest(true)), + ])?; + + let version_match = VersionMatch::new(source, dest)?; + + assert_equal(version_match.ineligible().map(|m| m.name.as_str()), ["ineligible"]); + assert_equal( + version_match.eligible_but_not_migratable().map(|pair| pair.source.name.as_str()), + ["eligible compatible", "eligible incompatible", "migratable incompatible"], + ); + assert_equal( + version_match.compatible_and_eligible().map(|pair| pair.source.name.as_str()), + ["eligible compatible", "migratable"], + ); + assert_equal( + version_match.migratable().map(|pair| pair.source.name.as_str()), + ["migratable"], + ); + + Ok(()) + } +} diff --git a/tools/external_crates/crate_health_proc_macros/Cargo.toml b/tools/external_crates/crate_health_proc_macros/Cargo.toml new file mode 100644 index 000000000..a62e929d6 --- /dev/null +++ b/tools/external_crates/crate_health_proc_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "crate_health_proc_macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +syn = "2.0" +quote = "1.0" diff --git a/tools/external_crates/crate_health_proc_macros/src/lib.rs b/tools/external_crates/crate_health_proc_macros/src/lib.rs new file mode 100644 index 000000000..a53dd5d65 --- /dev/null +++ b/tools/external_crates/crate_health_proc_macros/src/lib.rs @@ -0,0 +1,124 @@ +// Copyright (C) 2023 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. + +use syn::{parse_macro_input, DeriveInput, Error}; + +#[proc_macro_derive(NameAndVersionMap)] +pub fn derive_name_and_version_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + name_and_version_map::expand(input).unwrap_or_else(Error::into_compile_error).into() +} + +mod name_and_version_map { + use proc_macro2::TokenStream; + use quote::quote; + use syn::{ + Data, DataStruct, DeriveInput, Error, Field, GenericArgument, PathArguments, Result, Type, + }; + + pub(crate) fn expand(input: DeriveInput) -> Result<TokenStream> { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let mapfield = get_map_field(get_struct(&input)?)?; + let mapfield_name = mapfield + .ident + .as_ref() + .ok_or(Error::new_spanned(mapfield, "mapfield ident is none"))?; + let (_, value_type) = get_map_type(&mapfield.ty)?; + + let expanded = quote! { + #[automatically_derived] + impl #impl_generics NameAndVersionMap for #name #ty_generics #where_clause { + type Value = #value_type; + + fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value> { + self.#mapfield_name.map_field() + } + + fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value> { + self.#mapfield_name.map_field_mut() + } + + fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError> { + self.#mapfield_name.insert_or_error(key, val) + } + + fn num_crates(&self) -> usize { + self.#mapfield_name.num_crates() + } + + fn get_versions<'a, 'b>(&'a self, name: &'b str) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> { + self.#mapfield_name.get_versions(name) + } + + fn get_versions_mut<'a, 'b>(&'a mut self, name: &'b str) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a> { + self.#mapfield_name.get_versions_mut(name) + } + + fn filter_versions<'a: 'b, 'b, F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>, + ) -> HashSet<Version> + 'a>( + &'a self, + f: F, + ) -> Box<dyn Iterator<Item =(&'a NameAndVersion, &'a Self::Value)> + 'a> { + self.#mapfield_name.filter_versions(f) + } + } + }; + + Ok(TokenStream::from(expanded)) + } + + fn get_struct(input: &DeriveInput) -> Result<&DataStruct> { + match &input.data { + Data::Struct(strukt) => Ok(strukt), + _ => Err(Error::new_spanned(input, "Not a struct")), + } + } + + fn get_map_field(strukt: &DataStruct) -> Result<&Field> { + for field in &strukt.fields { + if let Ok((key_type, _value_type)) = get_map_type(&field.ty) { + if let syn::Type::Path(path) = &key_type { + if path.path.segments.len() == 1 + && path.path.segments[0].ident == "NameAndVersion" + { + return Ok(field); + } + } + } + } + return Err(Error::new_spanned(strukt.struct_token, "No field of type NameAndVersionMap")); + } + + fn get_map_type(typ: &Type) -> Result<(&Type, &Type)> { + if let syn::Type::Path(path) = &typ { + if path.path.segments.len() == 1 && path.path.segments[0].ident == "BTreeMap" { + if let PathArguments::AngleBracketed(args) = &path.path.segments[0].arguments { + if args.args.len() == 2 { + return Ok((get_type(&args.args[0])?, get_type(&args.args[1])?)); + } + } + } + } + Err(Error::new_spanned(typ, "Must be BTreeMap")) + } + + fn get_type(arg: &GenericArgument) -> Result<&Type> { + if let GenericArgument::Type(typ) = arg { + return Ok(typ); + } + Err(Error::new_spanned(arg, "Could not extract argument type")) + } +} diff --git a/tools/ndk/OWNERS b/tools/ndk/OWNERS index 7310b199a..14a30711c 100644 --- a/tools/ndk/OWNERS +++ b/tools/ndk/OWNERS @@ -1,4 +1,3 @@ danalbert@google.com enh@google.com -hhb@google.com rprichard@google.com diff --git a/tools/repo_pull/gerrit.py b/tools/repo_pull/gerrit.py index c93461d25..601c3ff5c 100755 --- a/tools/repo_pull/gerrit.py +++ b/tools/repo_pull/gerrit.py @@ -608,7 +608,7 @@ def _parse_args(): parser = argparse.ArgumentParser() add_common_parse_args(parser) parser.add_argument('--format', default='json', - choices=['json', 'oneline'], + choices=['json', 'oneline', 'porcelain'], help='Print format') return parser.parse_args() @@ -635,16 +635,22 @@ def main(): if args.format == 'json': json.dump(change_lists, sys.stdout, indent=4, separators=(', ', ': ')) print() # Print the end-of-line - elif args.format == 'oneline': + else: + if args.format == 'oneline': + format_str = ('{i:<8} {number:<16} {status:<20} ' + '{change_id:<60} {project:<120} ' + '{subject}') + else: + format_str = ('{i}\t{number}\t{status}\t' + '{change_id}\t{project}\t{subject}') + for i, change in enumerate(change_lists): - print('{i:<8} {number:<16} {status:<20} ' \ - '{change_id:<60} {project:<120} ' \ - '{subject}'.format(i=i, - project=change['project'], - change_id=change['change_id'], - status=change['status'], - number=change['_number'], - subject=change['subject'])) + print(format_str.format(i=i, + project=change['project'], + change_id=change['change_id'], + status=change['status'], + number=change['_number'], + subject=change['subject'])) if __name__ == '__main__': diff --git a/tools/winscope/OWNERS b/tools/winscope/OWNERS index 9cb826f69..820c48a4c 100644 --- a/tools/winscope/OWNERS +++ b/tools/winscope/OWNERS @@ -3,3 +3,4 @@ pablogamito@google.com keanmariotti@google.com jjaggi@google.com roosa@google.com +priyankaspatel@google.com diff --git a/treble/compare_bp_system_image.sh b/treble/compare_bp_system_image.sh new file mode 100755 index 000000000..7e8a736bd --- /dev/null +++ b/treble/compare_bp_system_image.sh @@ -0,0 +1,8 @@ +#!/bin/bash +echo "Note: Should run 'lunch aosp_cf_x86_64_only_phone-trunk_staging-userdebug && m aosp_cf_system_x86_64 && m' before running this script" +bp_base_path=$ANDROID_BUILD_TOP/out/soong/.intermediates/device/google/cuttlefish/system_image/aosp_cf_system_x86_64/android_common +latest_hash=$(ls $bp_base_path -t | head -1) +bp_path=$bp_base_path/$latest_hash/root +echo $OUT +echo $bp_path +compare_images -t $OUT $bp_path -s system -i diff --git a/vndk/tools/header-checker/README.md b/vndk/tools/header-checker/README.md index 846614411..9ac45950b 100644 --- a/vndk/tools/header-checker/README.md +++ b/vndk/tools/header-checker/README.md @@ -229,8 +229,8 @@ $ python3 utils/create_reference_dumps.py -l libfoo \ --ref-dump-dir /path/to/abi-dumps ``` -You may specify `-products` if you don't want to create the ABI dumps for -all architectures. For example, with `-products aosp_arm`, the command creates +You may specify `--product` if you don't want to create the ABI dumps for +all architectures. For example, with `--product aosp_arm`, the command creates dumps for 32-bit arm only. #### Configure Cross-Version ABI Check diff --git a/vndk/tools/header-checker/utils/create_reference_dumps.py b/vndk/tools/header-checker/utils/create_reference_dumps.py index 9efb7cee0..98110ddcb 100755 --- a/vndk/tools/header-checker/utils/create_reference_dumps.py +++ b/vndk/tools/header-checker/utils/create_reference_dumps.py @@ -177,14 +177,15 @@ def _parse_args(): """Parse the command line arguments.""" parser = argparse.ArgumentParser() - parser.add_argument('--version', help=argparse.SUPPRESS) parser.add_argument('--no-make-lib', action='store_true', help='skip building dumps while creating references') - parser.add_argument('-libs', action='append', + parser.add_argument('--lib', '-libs', action='append', + dest='libs', metavar='LIB', help='libs to create references for') - parser.add_argument('-products', action='append', + parser.add_argument('--product', '-products', action='append', + dest='products', metavar='PRODUCT', help='products to create references for') - parser.add_argument('-release', + parser.add_argument('--release', '-release', help='release configuration to create references for. ' 'e.g., trunk_staging, next.') parser.add_argument('--build-variant', default='userdebug', @@ -192,37 +193,26 @@ def _parse_args(): parser.add_argument('--lib-variant', action='append', dest='include_tags', default=[], choices=KNOWN_TAGS, help='library variant to create references for.') - parser.add_argument('--compress', action='store_true', - help=argparse.SUPPRESS) - parser.add_argument('-ref-dump-dir', + parser.add_argument('--ref-dump-dir', '-ref-dump-dir', help='directory to copy reference abi dumps into') - args = parser.parse_args() - if args.version is not None: - parser.error('--version is deprecated. Please specify the version in ' - 'the reference dump directory path. e.g., ' - '-ref-dump-dir prebuilts/abi-dumps/platform/current/64') - - if args.compress: - parser.error("Compressed reference dumps are deprecated.") - if args.libs: if any(lib_name.endswith(SOURCE_ABI_DUMP_EXT_END) or lib_name.endswith(SO_EXT) for lib_name in args.libs): - parser.error('-libs should be followed by a base name without ' + parser.error('--lib should be followed by a base name without ' 'file extension.') if NON_AOSP_TAGS.intersection(args.include_tags) and not args.libs: - parser.error('-libs must be given if --lib-variant is any of ' + + parser.error('--lib must be given if --lib-variant is any of ' + str(NON_AOSP_TAGS)) if args.ref_dump_dir and not args.libs: - parser.error('-libs must be given if -ref-dump-dir is given.') + parser.error('--lib must be given if --ref-dump-dir is given.') if args.ref_dump_dir and len(args.include_tags) != 1: print('WARNING: Exactly one --lib-variant should be specified if ' - '-ref-dump-dir is given.') + '--ref-dump-dir is given.') if args.products is None: # If `args.products` is unspecified, generate reference ABI dumps for |