diff --git a/tools/crate-updater/src/main.rs b/tools/crate-updater/src/main.rs index 107788a118..0b2afc1a10 100644 --- a/tools/crate-updater/src/main.rs +++ b/tools/crate-updater/src/main.rs @@ -13,11 +13,9 @@ // limitations under the License. use std::{ - collections::BTreeSet, path::{Path, PathBuf}, process::{Command, ExitStatus, Output}, str::from_utf8, - sync::LazyLock, }; use anyhow::{bail, Result}; @@ -268,36 +266,6 @@ fn try_update( Ok(()) } -#[rustfmt::skip] -static DENYLIST: LazyLock> = LazyLock::new(|| { - BTreeSet::from([ - // Paired crates that need to be updated together - "async-stream", - "async-stream-impl", - "clap", - "clap_builder", - "linkme", - "linkme-impl", - "pin-project", - "pin-project-internal", - "protobuf-support", - "protobuf", - "ptr_meta", - "ptr_meta_derive", - "regex", - "regex-automata", - "regex-syntax", - "serde_derive", - "serde", - "thiserror-impl", - "thiserror", - "tikv-jemalloc-sys", - "tikv-jemallocator", - "zerocopy-derive", - "zerocopy", - ]) -}); - fn main() -> Result<()> { let args = Cli::parse(); if !args.android_root.is_absolute() { @@ -325,10 +293,6 @@ fn main() -> Result<()> { for suggestion in get_suggestions(&monorepo_path)? { let crate_name = suggestion.name.as_str(); let version = suggestion.version.as_str(); - if DENYLIST.contains(crate_name) { - println!("Skipping {crate_name} (on deny list)"); - continue; - } if updates_tried.contains(crate_name, version) { println!("Skipping {crate_name} (already attempted recently)"); continue; diff --git a/tools/external_crates/crate_config/src/lib.rs b/tools/external_crates/crate_config/src/lib.rs index b6c978b6cc..4e8e0b75ae 100644 --- a/tools/external_crates/crate_config/src/lib.rs +++ b/tools/external_crates/crate_config/src/lib.rs @@ -23,6 +23,9 @@ use serde::Deserialize; pub struct CrateConfig { #[serde(default, skip_serializing_if = "Vec::is_empty")] deletions: Vec, + + #[serde(default, skip_serializing_if = "Vec::is_empty")] + update_with: Vec, } #[allow(missing_docs)] @@ -52,6 +55,10 @@ impl CrateConfig { pub fn deletions(&self) -> impl Iterator { self.deletions.iter().map(|d| d.as_str()) } + /// Get an iterator of crates that also need to be updated at the same time as this crate. + pub fn update_with(&self) -> impl Iterator { + self.update_with.iter().map(|d| d.as_str()) + } } #[cfg(test)] diff --git a/tools/external_crates/crate_tool/src/managed_crate.rs b/tools/external_crates/crate_tool/src/managed_crate.rs index c63d08980e..68bb4a0be2 100644 --- a/tools/external_crates/crate_tool/src/managed_crate.rs +++ b/tools/external_crates/crate_tool/src/managed_crate.rs @@ -237,11 +237,11 @@ impl ManagedCrate { } pub fn fix_test_mapping(&self) -> Result<()> { let mut tm = TestMapping::read(self.android_crate_path().clone())?; - println!("{}", self.name()); let mut changed = tm.fix_import_paths(); changed |= tm.add_new_tests_to_postsubmit()?; changed |= tm.remove_unknown_tests()?; if changed { + println!("Updating TEST_MAPPING for {}", self.name()); tm.write()?; } Ok(()) @@ -443,6 +443,7 @@ impl ManagedCrate { writeback |= true; } if writeback { + println!("Updating METADATA for {}", staged.name()); metadata.write()?; } } diff --git a/tools/external_crates/crate_tool/src/managed_repo.rs b/tools/external_crates/crate_tool/src/managed_repo.rs index 7b259c5312..0057ae4f40 100644 --- a/tools/external_crates/crate_tool/src/managed_repo.rs +++ b/tools/external_crates/crate_tool/src/managed_repo.rs @@ -29,10 +29,10 @@ use glob::glob; use google_metadata::GoogleMetadata; use itertools::Itertools; use license_checker::find_licenses; -use name_and_version::{NameAndVersionMap, NameAndVersionRef, NamedAndVersioned}; +use name_and_version::{NameAndVersion, NameAndVersionMap, NameAndVersionRef, NamedAndVersioned}; use repo_config::RepoConfig; use rooted_path::RootedPath; -use semver::Version; +use semver::{Version, VersionReq}; use serde::Serialize; use spdx::Licensee; @@ -907,12 +907,55 @@ impl ManagedRepo { Ok(()) } pub fn update(&self, crate_name: impl AsRef, version: impl AsRef) -> Result<()> { - let pseudo_crate = self.pseudo_crate(); + let crate_name = crate_name.as_ref(); let version = Version::parse(version.as_ref())?; - let nv = NameAndVersionRef::new(crate_name.as_ref(), &version); - pseudo_crate.remove(&crate_name)?; - pseudo_crate.cargo_add(&nv)?; - self.regenerate([&crate_name].iter(), true)?; + + let pseudo_crate = self.pseudo_crate(); + let managed_crate = self.managed_crate_for(crate_name)?; + let mut crate_updates = vec![NameAndVersion::new(crate_name.to_string(), version.clone())]; + + let cio_crate = self.crates_io.get_crate(crate_name)?; + let cio_crate_version = cio_crate + .get_version(&version) + .ok_or(anyhow!("Could not find {crate_name} {version} on crates.io"))?; + + for dependent_crate_name in managed_crate.config().update_with() { + let dep = cio_crate_version + .dependencies() + .iter() + .find(|dep| dep.crate_name() == dependent_crate_name) + .ok_or(anyhow!( + "Could not find crate {dependent_crate_name} as a dependency of {crate_name}" + ))?; + let req = VersionReq::parse(dep.requirement())?; + let dep_cio_crate = self.crates_io.get_crate(dependent_crate_name)?; + let version = dep_cio_crate + .safe_versions() + .find(|v| { + if let Ok(parsed_version) = Version::parse(v.version()) { + req.matches(&parsed_version) + } else { + false + } + }) + .ok_or(anyhow!( + "Failed to find a version of {dependent_crate_name} that satisfies {}", + dep.requirement() + ))?; + println!("Also updating {dependent_crate_name} to {}", version.version()); + crate_updates.push(NameAndVersion::new( + dependent_crate_name.to_string(), + Version::parse(version.version())?, + )); + } + + for nv in &crate_updates { + pseudo_crate.remove(nv.name())?; + } + for nv in &crate_updates { + pseudo_crate.cargo_add(nv)?; + } + self.regenerate(crate_updates.iter().map(|nv| nv.name()), true)?; Ok(()) } pub fn init(&self) -> Result<()> {