diff --git a/crates/uv-distribution/src/index/registry_wheel_index.rs b/crates/uv-distribution/src/index/registry_wheel_index.rs index a3530da1b..728adaecc 100644 --- a/crates/uv-distribution/src/index/registry_wheel_index.rs +++ b/crates/uv-distribution/src/index/registry_wheel_index.rs @@ -3,6 +3,8 @@ use std::collections::hash_map::Entry; use rustc_hash::{FxHashMap, FxHashSet}; use uv_cache::{Cache, CacheBucket, WheelCache}; +use uv_cache_key::cache_digest; +use uv_configuration::ConfigSettings; use uv_distribution_types::{CachedRegistryDist, Hashed, Index, IndexLocations, IndexUrl}; use uv_fs::{directories, files, symlinks}; use uv_normalize::PackageName; @@ -31,6 +33,7 @@ pub struct RegistryWheelIndex<'a> { index_locations: &'a IndexLocations, hasher: &'a HashStrategy, index: FxHashMap<&'a PackageName, Vec>>, + build_configuration: &'a ConfigSettings, } impl<'a> RegistryWheelIndex<'a> { @@ -40,12 +43,14 @@ impl<'a> RegistryWheelIndex<'a> { tags: &'a Tags, index_locations: &'a IndexLocations, hasher: &'a HashStrategy, + build_configuration: &'a ConfigSettings, ) -> Self { Self { cache, tags, index_locations, hasher, + build_configuration, index: FxHashMap::default(), } } @@ -67,6 +72,7 @@ impl<'a> RegistryWheelIndex<'a> { self.tags, self.index_locations, self.hasher, + self.build_configuration, )), }; versions @@ -79,6 +85,7 @@ impl<'a> RegistryWheelIndex<'a> { tags: &Tags, index_locations: &'index IndexLocations, hasher: &HashStrategy, + build_configuration: &ConfigSettings, ) -> Vec> { let mut entries = vec![]; @@ -162,9 +169,8 @@ impl<'a> RegistryWheelIndex<'a> { WheelCache::Index(index.url()).wheel_dir(package.to_string()), ); - // For registry wheels, the cache structure is: `///`. + // For registry source distributions, the cache structure is: `///`. for shard in directories(&cache_shard) { - // Read the existing metadata from the cache, if it exists. let cache_shard = cache_shard.shard(shard); // Read the revision from the cache. @@ -190,7 +196,16 @@ impl<'a> RegistryWheelIndex<'a> { }; if let Some(revision) = revision { - for wheel_dir in symlinks(cache_shard.join(revision.id())) { + let cache_shard = cache_shard.shard(revision.id()); + + // If there are build settings, we need to scope to a cache shard. + let cache_shard = if build_configuration.is_empty() { + cache_shard + } else { + cache_shard.shard(cache_digest(build_configuration)) + }; + + for wheel_dir in symlinks(cache_shard) { if let Some(wheel) = CachedWheel::from_built_source(wheel_dir) { if wheel.filename.compatibility(tags).is_compatible() { // Enforce hash-checking based on the source distribution. diff --git a/crates/uv-installer/src/plan.rs b/crates/uv-installer/src/plan.rs index 09b4af611..a8b477f2f 100644 --- a/crates/uv-installer/src/plan.rs +++ b/crates/uv-installer/src/plan.rs @@ -56,7 +56,8 @@ impl<'a> Planner<'a> { tags: &Tags, ) -> Result { // Index all the already-downloaded wheels in the cache. - let mut registry_index = RegistryWheelIndex::new(cache, tags, index_locations, hasher); + let mut registry_index = + RegistryWheelIndex::new(cache, tags, index_locations, hasher, config_settings); let built_index = BuiltWheelIndex::new(cache, tags, hasher, config_settings); let mut cached = vec![]; @@ -248,7 +249,7 @@ impl<'a> Planner<'a> { } Some(&entry.dist) }) { - debug!("Requirement already cached: {distribution}"); + debug!("Registry requirement already cached: {distribution}"); cached.push(CachedDist::Registry(distribution.clone())); continue; } diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index 14e7dee50..cf596cb17 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -3251,7 +3251,89 @@ fn launcher_with_symlink() -> Result<()> { } #[test] -fn config_settings() { +fn config_settings_registry() { + let context = TestContext::new("3.12"); + + // Install with a `-C` flag. In this case, the flag has no impact on the build, but uv should + // respect it anyway. + uv_snapshot!(context.filters(), context.pip_install() + .arg("iniconfig") + .arg("--no-binary") + .arg("iniconfig") + .arg("-C=global-option=build_ext"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + iniconfig==2.0.0 + "### + ); + + // Uninstall the package. + uv_snapshot!(context.filters(), context.pip_uninstall() + .arg("iniconfig"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Uninstalled 1 package in [TIME] + - iniconfig==2.0.0 + "###); + + // Re-install the package, with the same flag. We should read from the cache. + uv_snapshot!(context.filters(), context.pip_install() + .arg("iniconfig") + .arg("--no-binary") + .arg("iniconfig") + .arg("-C=global-option=build_ext"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Installed 1 package in [TIME] + + iniconfig==2.0.0 + "### + ); + + // Uninstall the package. + uv_snapshot!(context.filters(), context.pip_uninstall() + .arg("iniconfig"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Uninstalled 1 package in [TIME] + - iniconfig==2.0.0 + "###); + + // Re-install the package, without the flag. We should build it from source. + uv_snapshot!(context.filters(), context.pip_install() + .arg("iniconfig") + .arg("--no-binary") + .arg("iniconfig"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + iniconfig==2.0.0 + "### + ); +} + +#[test] +fn config_settings_path() { let context = TestContext::new("3.12"); // Install the editable package.