From 3a7aeff86fc541f6f0d495e0bc3fee180aeed9e2 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sat, 2 Aug 2025 15:26:02 -0400 Subject: [PATCH] Respect extra build requires when reading from wheel cache (#15030) ## Summary We weren't including these in the cache key when constructing the install plan. We likely still read them from the cache later, but we may have reported the wrong number of prepares, etc. --- Cargo.lock | 1 + crates/uv-dispatch/src/lib.rs | 3 +- .../src/index/built_wheel_index.rs | 36 ++++++++++++++----- crates/uv-distribution/src/source/mod.rs | 2 +- crates/uv-installer/Cargo.toml | 1 + crates/uv-installer/src/plan.rs | 6 +++- crates/uv-types/src/traits.rs | 3 +- crates/uv/src/commands/pip/operations.rs | 8 +++-- crates/uv/tests/it/sync.rs | 13 +++++++ 9 files changed, 58 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf19b6c4b..08b74888c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5450,6 +5450,7 @@ dependencies = [ "uv-static", "uv-types", "uv-warnings", + "uv-workspace", "walkdir", ] diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 606836a06..0205ca969 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -311,6 +311,7 @@ impl BuildContext for BuildDispatch<'_> { self.index_locations, self.config_settings, self.config_settings_package, + self.extra_build_dependencies(), self.cache(), venv, tags, @@ -460,7 +461,7 @@ impl BuildContext for BuildDispatch<'_> { self.workspace_cache(), config_settings, self.build_isolation, - &self.extra_build_requires.extra_build_dependencies, + self.extra_build_dependencies(), &build_stack, build_kind, self.build_extra_env_vars.clone(), diff --git a/crates/uv-distribution/src/index/built_wheel_index.rs b/crates/uv-distribution/src/index/built_wheel_index.rs index 90ce5deed..d877c2509 100644 --- a/crates/uv-distribution/src/index/built_wheel_index.rs +++ b/crates/uv-distribution/src/index/built_wheel_index.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; + use uv_cache::{Cache, CacheBucket, CacheShard, WheelCache}; use uv_cache_info::CacheInfo; use uv_cache_key::cache_digest; @@ -9,6 +10,7 @@ use uv_distribution_types::{ use uv_normalize::PackageName; use uv_platform_tags::Tags; use uv_types::HashStrategy; +use uv_workspace::pyproject::ExtraBuildDependencies; use crate::Error; use crate::index::cached_wheel::CachedWheel; @@ -22,6 +24,7 @@ pub struct BuiltWheelIndex<'a> { hasher: &'a HashStrategy, config_settings: &'a ConfigSettings, config_settings_package: &'a PackageConfigSettings, + extra_build_dependencies: &'a ExtraBuildDependencies, } impl<'a> BuiltWheelIndex<'a> { @@ -32,6 +35,7 @@ impl<'a> BuiltWheelIndex<'a> { hasher: &'a HashStrategy, config_settings: &'a ConfigSettings, config_settings_package: &'a PackageConfigSettings, + extra_build_dependencies: &'a ExtraBuildDependencies, ) -> Self { Self { cache, @@ -39,6 +43,7 @@ impl<'a> BuiltWheelIndex<'a> { hasher, config_settings, config_settings_package, + extra_build_dependencies, } } @@ -69,10 +74,11 @@ impl<'a> BuiltWheelIndex<'a> { // If there are build settings, we need to scope to a cache shard. let config_settings = self.config_settings_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() { + let extra_build_deps = self.extra_build_dependencies_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { cache_shard } else { - cache_shard.shard(cache_digest(&config_settings)) + cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) }; Ok(self.find(&cache_shard)) @@ -107,10 +113,11 @@ impl<'a> BuiltWheelIndex<'a> { // If there are build settings, we need to scope to a cache shard. let config_settings = self.config_settings_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() { + let extra_build_deps = self.extra_build_dependencies_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { cache_shard } else { - cache_shard.shard(cache_digest(&config_settings)) + cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) }; Ok(self @@ -156,10 +163,11 @@ impl<'a> BuiltWheelIndex<'a> { // If there are build settings, we need to scope to a cache shard. let config_settings = self.config_settings_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() { + let extra_build_deps = self.extra_build_dependencies_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { cache_shard } else { - cache_shard.shard(cache_digest(&config_settings)) + cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) }; Ok(self @@ -183,10 +191,11 @@ impl<'a> BuiltWheelIndex<'a> { // If there are build settings, we need to scope to a cache shard. let config_settings = self.config_settings_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() { + let extra_build_deps = self.extra_build_dependencies_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { cache_shard } else { - cache_shard.shard(cache_digest(&config_settings)) + cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) }; self.find(&cache_shard) @@ -257,4 +266,15 @@ impl<'a> BuiltWheelIndex<'a> { Cow::Borrowed(self.config_settings) } } + + /// Determine the extra build dependencies for the given package name. + fn extra_build_dependencies_for( + &self, + name: &PackageName, + ) -> &[uv_pep508::Requirement] { + self.extra_build_dependencies + .get(name) + .map(Vec::as_slice) + .unwrap_or(&[]) + } } diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 6e1dbb228..d516c6d0d 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -413,7 +413,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { self.build_context .extra_build_dependencies() .get(name) - .map(|v| v.as_slice()) + .map(Vec::as_slice) }) .unwrap_or(&[]) } diff --git a/crates/uv-installer/Cargo.toml b/crates/uv-installer/Cargo.toml index a78dec23b..d0f43e770 100644 --- a/crates/uv-installer/Cargo.toml +++ b/crates/uv-installer/Cargo.toml @@ -35,6 +35,7 @@ uv-redacted = { workspace = true } uv-static = { workspace = true } uv-types = { workspace = true } uv-warnings = { workspace = true } +uv-workspace = { workspace = true } anyhow = { workspace = true } async-channel = { workspace = true } diff --git a/crates/uv-installer/src/plan.rs b/crates/uv-installer/src/plan.rs index 69e10befc..ddabf3386 100644 --- a/crates/uv-installer/src/plan.rs +++ b/crates/uv-installer/src/plan.rs @@ -1,5 +1,6 @@ -use anyhow::{Result, bail}; use std::sync::Arc; + +use anyhow::{Result, bail}; use tracing::{debug, warn}; use uv_cache::{Cache, CacheBucket, WheelCache}; @@ -17,6 +18,7 @@ use uv_platform_tags::Tags; use uv_pypi_types::VerbatimParsedUrl; use uv_python::PythonEnvironment; use uv_types::HashStrategy; +use uv_workspace::pyproject::ExtraBuildDependencies; use crate::SitePackages; use crate::satisfies::RequirementSatisfaction; @@ -53,6 +55,7 @@ impl<'a> Planner<'a> { index_locations: &IndexLocations, config_settings: &ConfigSettings, config_settings_package: &PackageConfigSettings, + extra_build_dependencies: &ExtraBuildDependencies, cache: &Cache, venv: &PythonEnvironment, tags: &Tags, @@ -66,6 +69,7 @@ impl<'a> Planner<'a> { hasher, config_settings, config_settings_package, + extra_build_dependencies, ); let mut cached = vec![]; diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index 68e6583ae..6966c850a 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -19,6 +19,7 @@ use uv_git::GitResolver; use uv_pep508::PackageName; use uv_python::{Interpreter, PythonEnvironment}; use uv_workspace::WorkspaceCache; +use uv_workspace::pyproject::ExtraBuildDependencies; use crate::BuildArena; @@ -104,7 +105,7 @@ pub trait BuildContext { fn workspace_cache(&self) -> &WorkspaceCache; /// Get the extra build dependencies. - fn extra_build_dependencies(&self) -> &uv_workspace::pyproject::ExtraBuildDependencies; + fn extra_build_dependencies(&self) -> &ExtraBuildDependencies; /// Resolve the given requirements into a ready-to-install set of package versions. fn resolve<'a>( diff --git a/crates/uv/src/commands/pip/operations.rs b/crates/uv/src/commands/pip/operations.rs index b5879ecf6..3e940112e 100644 --- a/crates/uv/src/commands/pip/operations.rs +++ b/crates/uv/src/commands/pip/operations.rs @@ -1,12 +1,13 @@ //! Common operations shared across the `pip` API and subcommands. -use anyhow::{Context, anyhow}; -use itertools::Itertools; -use owo_colors::OwoColorize; use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::fmt::Write; use std::path::PathBuf; use std::sync::Arc; + +use anyhow::{Context, anyhow}; +use itertools::Itertools; +use owo_colors::OwoColorize; use tracing::debug; use uv_cache::Cache; @@ -468,6 +469,7 @@ pub(crate) async fn install( index_urls, config_settings, config_settings_package, + build_dispatch.extra_build_dependencies(), cache, venv, tags, diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index b304733d9..cb17c7fff 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -1662,6 +1662,19 @@ fn sync_extra_build_dependencies() -> Result<()> { + child==0.1.0 (from file://[TEMP_DIR]/child) "); + context.venv().arg("--clear").assert().success(); + uv_snapshot!(context.filters(), context.sync(), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: The `extra-build-dependencies` option is experimental and may change without warning. Pass `--preview-features extra-build-dependencies` to disable this warning. + Resolved [N] packages in [TIME] + Installed [N] packages in [TIME] + + child==0.1.0 (from file://[TEMP_DIR]/child) + "); + // Adding `extra-build-dependencies` with the wrong name should fail the build // (the cache is invalidated when extra build dependencies change) pyproject_toml.write_str(indoc! {r#"