mirror of https://github.com/astral-sh/uv
Use in cache shards
This commit is contained in:
parent
6fb8618410
commit
8b5d9314ef
|
|
@ -5514,6 +5514,7 @@ dependencies = [
|
|||
"tracing-test",
|
||||
"unicode-width 0.2.1",
|
||||
"url",
|
||||
"uv-cache-key",
|
||||
"uv-fs",
|
||||
"uv-normalize",
|
||||
"uv-pep440",
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ mod resolver {
|
|||
ResolverEnvironment, ResolverOutput,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
||||
use uv_workspace::{WorkspaceCache, pyproject::ExtraBuildDependencies};
|
||||
use uv_workspace::WorkspaceCache;
|
||||
|
||||
static MARKERS: LazyLock<MarkerEnvironment> = LazyLock::new(|| {
|
||||
MarkerEnvironment::try_from(MarkerEnvironmentBuilder {
|
||||
|
|
@ -141,7 +141,9 @@ mod resolver {
|
|||
universal: bool,
|
||||
) -> Result<ResolverOutput> {
|
||||
let build_isolation = BuildIsolation::default();
|
||||
let extra_build_dependencies = ExtraBuildDependencies::default();
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires {
|
||||
extra_build_dependencies: uv_workspace::pyproject::ExtraBuildDependencies::default(),
|
||||
};
|
||||
let build_options = BuildOptions::default();
|
||||
let concurrency = Concurrency::default();
|
||||
let config_settings = ConfigSettings::default();
|
||||
|
|
@ -188,7 +190,7 @@ mod resolver {
|
|||
&config_settings,
|
||||
&config_settings_package,
|
||||
build_isolation,
|
||||
&extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
LinkMode::default(),
|
||||
&build_options,
|
||||
&hashes,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ use uv_types::{
|
|||
HashStrategy, InFlight,
|
||||
};
|
||||
use uv_workspace::WorkspaceCache;
|
||||
use uv_workspace::pyproject::ExtraBuildDependencies;
|
||||
use uv_distribution::ExtraBuildRequires;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BuildDispatchError {
|
||||
|
|
@ -89,7 +89,7 @@ pub struct BuildDispatch<'a> {
|
|||
shared_state: SharedState,
|
||||
dependency_metadata: &'a DependencyMetadata,
|
||||
build_isolation: BuildIsolation<'a>,
|
||||
extra_build_dependencies: &'a ExtraBuildDependencies,
|
||||
extra_build_requires: &'a ExtraBuildRequires,
|
||||
link_mode: uv_install_wheel::LinkMode,
|
||||
build_options: &'a BuildOptions,
|
||||
config_settings: &'a ConfigSettings,
|
||||
|
|
@ -118,7 +118,7 @@ impl<'a> BuildDispatch<'a> {
|
|||
config_settings: &'a ConfigSettings,
|
||||
config_settings_package: &'a PackageConfigSettings,
|
||||
build_isolation: BuildIsolation<'a>,
|
||||
extra_build_dependencies: &'a ExtraBuildDependencies,
|
||||
extra_build_requires: &'a ExtraBuildRequires,
|
||||
link_mode: uv_install_wheel::LinkMode,
|
||||
build_options: &'a BuildOptions,
|
||||
hasher: &'a HashStrategy,
|
||||
|
|
@ -141,7 +141,7 @@ impl<'a> BuildDispatch<'a> {
|
|||
config_settings,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
extra_build_requires,
|
||||
link_mode,
|
||||
build_options,
|
||||
hasher,
|
||||
|
|
@ -223,6 +223,10 @@ impl BuildContext for BuildDispatch<'_> {
|
|||
&self.workspace_cache
|
||||
}
|
||||
|
||||
fn extra_build_dependencies(&self) -> &uv_workspace::pyproject::ExtraBuildDependencies {
|
||||
&self.extra_build_requires.extra_build_dependencies
|
||||
}
|
||||
|
||||
async fn resolve<'data>(
|
||||
&'data self,
|
||||
requirements: &'data [Requirement],
|
||||
|
|
@ -456,7 +460,7 @@ impl BuildContext for BuildDispatch<'_> {
|
|||
self.workspace_cache(),
|
||||
config_settings,
|
||||
self.build_isolation,
|
||||
self.extra_build_dependencies,
|
||||
&self.extra_build_requires.extra_build_dependencies,
|
||||
&build_stack,
|
||||
build_kind,
|
||||
self.build_extra_env_vars.clone(),
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ pub use download::LocalWheel;
|
|||
pub use error::Error;
|
||||
pub use index::{BuiltWheelIndex, RegistryWheelIndex};
|
||||
pub use metadata::{
|
||||
ArchiveMetadata, BuildRequires, FlatRequiresDist, LoweredRequirement, LoweringError, Metadata,
|
||||
MetadataError, RequiresDist, SourcedDependencyGroups,
|
||||
ArchiveMetadata, BuildRequires, ExtraBuildRequires, FlatRequiresDist, LoweredRequirement,
|
||||
LoweringError, Metadata, MetadataError, RequiresDist, SourcedDependencyGroups,
|
||||
};
|
||||
pub use reporter::Reporter;
|
||||
pub use source::prune;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ use std::path::Path;
|
|||
use uv_configuration::SourceStrategy;
|
||||
use uv_distribution_types::{IndexLocations, Requirement};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_workspace::pyproject::ToolUvSources;
|
||||
use uv_pypi_types::VerbatimParsedUrl;
|
||||
use uv_workspace::pyproject::{ExtraBuildDependencies, ToolUvSources};
|
||||
use uv_workspace::{
|
||||
DiscoveryOptions, MemberDiscovery, ProjectWorkspace, Workspace, WorkspaceCache,
|
||||
};
|
||||
|
|
@ -203,3 +204,91 @@ impl BuildRequires {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowered extra build dependencies with source resolution applied.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtraBuildRequires {
|
||||
pub extra_build_dependencies: ExtraBuildDependencies,
|
||||
}
|
||||
|
||||
impl ExtraBuildRequires {
|
||||
/// Lower extra build dependencies from a workspace, applying source resolution.
|
||||
pub fn from_workspace(
|
||||
extra_build_dependencies: ExtraBuildDependencies,
|
||||
workspace: &Workspace,
|
||||
index_locations: &IndexLocations,
|
||||
source_strategy: SourceStrategy,
|
||||
) -> Result<Self, MetadataError> {
|
||||
match source_strategy {
|
||||
SourceStrategy::Enabled => {
|
||||
// Collect project sources and indexes
|
||||
let project_indexes = workspace
|
||||
.pyproject_toml()
|
||||
.tool
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.uv.as_ref())
|
||||
.and_then(|uv| uv.index.as_deref())
|
||||
.unwrap_or(&[]);
|
||||
|
||||
let empty_sources = BTreeMap::default();
|
||||
let project_sources = workspace
|
||||
.pyproject_toml()
|
||||
.tool
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.uv.as_ref())
|
||||
.and_then(|uv| uv.sources.as_ref())
|
||||
.map(ToolUvSources::inner)
|
||||
.unwrap_or(&empty_sources);
|
||||
|
||||
// Lower each package's extra build dependencies
|
||||
let mut result = ExtraBuildDependencies::default();
|
||||
for (package_name, requirements) in extra_build_dependencies {
|
||||
let lowered: Vec<uv_pep508::Requirement<VerbatimParsedUrl>> = requirements
|
||||
.into_iter()
|
||||
.flat_map(|requirement| {
|
||||
let requirement_name = requirement.name.clone();
|
||||
let extra = requirement.marker.top_level_extra_name();
|
||||
let group = None;
|
||||
LoweredRequirement::from_requirement(
|
||||
requirement,
|
||||
None,
|
||||
workspace.install_path(),
|
||||
project_sources,
|
||||
project_indexes,
|
||||
extra.as_deref(),
|
||||
group,
|
||||
index_locations,
|
||||
workspace,
|
||||
None,
|
||||
)
|
||||
.map(move |requirement| match requirement {
|
||||
Ok(requirement) => Ok(requirement.into_inner().into()),
|
||||
Err(err) => Err(MetadataError::LoweringError(
|
||||
requirement_name.clone(),
|
||||
Box::new(err),
|
||||
)),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
result.insert(package_name, lowered);
|
||||
}
|
||||
Ok(Self {
|
||||
extra_build_dependencies: result,
|
||||
})
|
||||
}
|
||||
SourceStrategy::Disabled => {
|
||||
// Without source resolution, just return the dependencies as-is
|
||||
Ok(Self {
|
||||
extra_build_dependencies,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from pre-lowered dependencies (for non-workspace contexts).
|
||||
pub fn from_lowered(extra_build_dependencies: ExtraBuildDependencies) -> Self {
|
||||
Self {
|
||||
extra_build_dependencies,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use uv_pypi_types::{HashDigests, ResolutionMetadata};
|
|||
use uv_workspace::dependency_groups::DependencyGroupError;
|
||||
use uv_workspace::{WorkspaceCache, WorkspaceError};
|
||||
|
||||
pub use crate::metadata::build_requires::BuildRequires;
|
||||
pub use crate::metadata::build_requires::{BuildRequires, ExtraBuildRequires};
|
||||
pub use crate::metadata::dependency_groups::SourcedDependencyGroups;
|
||||
pub use crate::metadata::lowering::LoweredRequirement;
|
||||
pub use crate::metadata::lowering::LoweringError;
|
||||
|
|
|
|||
|
|
@ -390,6 +390,20 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Determine the extra build dependencies for the given package name.
|
||||
fn extra_build_dependencies_for(
|
||||
&self,
|
||||
name: Option<&PackageName>,
|
||||
) -> &[uv_pep508::Requirement<uv_pypi_types::VerbatimParsedUrl>] {
|
||||
name.and_then(|name| {
|
||||
self.build_context
|
||||
.extra_build_dependencies()
|
||||
.get(name)
|
||||
.map(|v| v.as_slice())
|
||||
})
|
||||
.unwrap_or(&[])
|
||||
}
|
||||
|
||||
/// Build a source distribution from a remote URL.
|
||||
async fn url<'data>(
|
||||
&self,
|
||||
|
|
@ -423,12 +437,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
let cache_shard = cache_shard.shard(revision.id());
|
||||
let source_dist_entry = cache_shard.entry(SOURCE);
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
|
|
@ -596,12 +611,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// Otherwise, we either need to build the metadata.
|
||||
|
|
@ -795,12 +811,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
let cache_shard = cache_shard.shard(revision.id());
|
||||
let source_entry = cache_shard.entry(SOURCE);
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
|
|
@ -957,12 +974,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
});
|
||||
}
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// Otherwise, we need to build a wheel.
|
||||
|
|
@ -1099,12 +1117,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
// freshness, since entries have to be fresher than the revision itself.
|
||||
let cache_shard = cache_shard.shard(revision.id());
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
|
|
@ -1287,12 +1306,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
));
|
||||
}
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// Otherwise, we need to build a wheel.
|
||||
|
|
@ -1492,12 +1512,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
// Acquire the advisory lock.
|
||||
let _lock = cache_shard.lock().await.map_err(Error::CacheWrite)?;
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
|
|
@ -1795,12 +1816,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
));
|
||||
}
|
||||
|
||||
// If there are build settings, we need to scope to a cache shard.
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let cache_shard = if config_settings.is_empty() {
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.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)))
|
||||
};
|
||||
|
||||
// Otherwise, we need to build a wheel.
|
||||
|
|
|
|||
|
|
@ -114,6 +114,24 @@ impl Operator {
|
|||
pub fn is_star(self) -> bool {
|
||||
matches!(self, Self::EqualStar | Self::NotEqualStar)
|
||||
}
|
||||
|
||||
/// Returns the string representation of this operator.
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Equal => "==",
|
||||
// Beware, this doesn't print the star
|
||||
Self::EqualStar => "==",
|
||||
#[allow(deprecated)]
|
||||
Self::ExactEqual => "===",
|
||||
Self::NotEqual => "!=",
|
||||
Self::NotEqualStar => "!=",
|
||||
Self::TildeEqual => "~=",
|
||||
Self::LessThan => "<",
|
||||
Self::LessThanEqual => "<=",
|
||||
Self::GreaterThan => ">",
|
||||
Self::GreaterThanEqual => ">=",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Operator {
|
||||
|
|
@ -150,21 +168,7 @@ impl FromStr for Operator {
|
|||
impl std::fmt::Display for Operator {
|
||||
/// Note the `EqualStar` is also `==`.
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let operator = match self {
|
||||
Self::Equal => "==",
|
||||
// Beware, this doesn't print the star
|
||||
Self::EqualStar => "==",
|
||||
#[allow(deprecated)]
|
||||
Self::ExactEqual => "===",
|
||||
Self::NotEqual => "!=",
|
||||
Self::NotEqualStar => "!=",
|
||||
Self::TildeEqual => "~=",
|
||||
Self::LessThan => "<",
|
||||
Self::LessThanEqual => "<=",
|
||||
Self::GreaterThan => ">",
|
||||
Self::GreaterThanEqual => ">=",
|
||||
};
|
||||
|
||||
let operator = self.as_str();
|
||||
write!(f, "{operator}")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,11 @@ impl VersionSpecifiers {
|
|||
Self(Box::new([]))
|
||||
}
|
||||
|
||||
/// The number of specifiers.
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Whether all specifiers match the given version.
|
||||
pub fn contains(&self, version: &Version) -> bool {
|
||||
self.iter().all(|specifier| specifier.contains(version))
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ doctest = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
uv-cache-key = { workspace = true }
|
||||
uv-fs = { workspace = true }
|
||||
uv-normalize = { workspace = true }
|
||||
uv-pep440 = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use std::str::FromStr;
|
|||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
use uv_cache_key::{CacheKey, CacheKeyHasher};
|
||||
|
||||
use cursor::Cursor;
|
||||
pub use marker::{
|
||||
|
|
@ -251,6 +252,52 @@ impl<T: Pep508Url> Serialize for Requirement<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Pep508Url> CacheKey for Requirement<T>
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||
self.name.as_str().cache_key(state);
|
||||
|
||||
self.extras.len().cache_key(state);
|
||||
for extra in &self.extras {
|
||||
extra.as_str().cache_key(state);
|
||||
}
|
||||
|
||||
// TODO(zanieb): We inline cache key handling for the child types here, but we could
|
||||
// move the implementations to the children. The intent here was to limit the scope of
|
||||
// types exposing the `CacheKey` trait for now.
|
||||
if let Some(version_or_url) = &self.version_or_url {
|
||||
1u8.cache_key(state);
|
||||
match version_or_url {
|
||||
VersionOrUrl::VersionSpecifier(spec) => {
|
||||
0u8.cache_key(state);
|
||||
spec.len().cache_key(state);
|
||||
for specifier in spec.iter() {
|
||||
specifier.operator().as_str().cache_key(state);
|
||||
specifier.version().to_string().cache_key(state);
|
||||
}
|
||||
}
|
||||
VersionOrUrl::Url(url) => {
|
||||
1u8.cache_key(state);
|
||||
url.to_string().cache_key(state);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
0u8.cache_key(state);
|
||||
}
|
||||
|
||||
if let Some(marker) = self.marker.contents() {
|
||||
1u8.cache_key(state);
|
||||
marker.to_string().cache_key(state);
|
||||
} else {
|
||||
0u8.cache_key(state);
|
||||
}
|
||||
|
||||
// `origin` is intentionally omitted
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Pep508Url> Requirement<T> {
|
||||
/// Returns whether the markers apply for the given environment
|
||||
pub fn evaluate_markers(&self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ pub trait BuildContext {
|
|||
/// Workspace discovery caching.
|
||||
fn workspace_cache(&self) -> &WorkspaceCache;
|
||||
|
||||
/// Get the extra build dependencies.
|
||||
fn extra_build_dependencies(&self) -> &uv_workspace::pyproject::ExtraBuildDependencies;
|
||||
|
||||
/// Resolve the given requirements into a ready-to-install set of package versions.
|
||||
fn resolve<'a>(
|
||||
&'a self,
|
||||
|
|
|
|||
|
|
@ -564,6 +564,9 @@ async fn build_package(
|
|||
let workspace_cache = WorkspaceCache::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -577,7 +580,7 @@ async fn build_package(
|
|||
config_setting,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
link_mode,
|
||||
build_options,
|
||||
&hasher,
|
||||
|
|
|
|||
|
|
@ -476,6 +476,9 @@ pub(crate) async fn pip_compile(
|
|||
.map(|constraint| constraint.requirement.clone()),
|
||||
);
|
||||
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
|
|
@ -489,7 +492,7 @@ pub(crate) async fn pip_compile(
|
|||
&config_settings,
|
||||
&config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
link_mode,
|
||||
&build_options,
|
||||
&build_hashes,
|
||||
|
|
|
|||
|
|
@ -421,6 +421,9 @@ pub(crate) async fn pip_install(
|
|||
let state = SharedState::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
|
|
@ -434,7 +437,7 @@ pub(crate) async fn pip_install(
|
|||
config_settings,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
link_mode,
|
||||
&build_options,
|
||||
&build_hasher,
|
||||
|
|
|
|||
|
|
@ -354,6 +354,9 @@ pub(crate) async fn pip_sync(
|
|||
let state = SharedState::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
|
|
@ -367,7 +370,7 @@ pub(crate) async fn pip_sync(
|
|||
config_settings,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
link_mode,
|
||||
&build_options,
|
||||
&build_hasher,
|
||||
|
|
|
|||
|
|
@ -431,6 +431,18 @@ pub(crate) async fn add(
|
|||
};
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = if let AddTarget::Project(project, _) = &target {
|
||||
uv_distribution::ExtraBuildRequires::from_workspace(
|
||||
settings.resolver.extra_build_dependencies.clone(),
|
||||
project.workspace(),
|
||||
&settings.resolver.index_locations,
|
||||
settings.resolver.sources,
|
||||
)?
|
||||
} else {
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
settings.resolver.extra_build_dependencies.clone(),
|
||||
)
|
||||
};
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -444,7 +456,7 @@ pub(crate) async fn add(
|
|||
&settings.resolver.config_setting,
|
||||
&settings.resolver.config_settings_package,
|
||||
build_isolation,
|
||||
&settings.resolver.extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
settings.resolver.link_mode,
|
||||
&settings.resolver.build_options,
|
||||
&build_hasher,
|
||||
|
|
|
|||
|
|
@ -671,6 +671,17 @@ async fn do_lock(
|
|||
};
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = match &target {
|
||||
LockTarget::Workspace(workspace) => uv_distribution::ExtraBuildRequires::from_workspace(
|
||||
extra_build_dependencies.clone(),
|
||||
workspace,
|
||||
index_locations,
|
||||
*sources,
|
||||
)?,
|
||||
LockTarget::Script(_) => uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
),
|
||||
};
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -684,7 +695,7 @@ async fn do_lock(
|
|||
config_setting,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
*link_mode,
|
||||
build_options,
|
||||
&build_hasher,
|
||||
|
|
|
|||
|
|
@ -1733,6 +1733,9 @@ pub(crate) async fn resolve_names(
|
|||
let build_hasher = HashStrategy::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -1746,7 +1749,7 @@ pub(crate) async fn resolve_names(
|
|||
config_setting,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
*link_mode,
|
||||
build_options,
|
||||
&build_hasher,
|
||||
|
|
@ -1943,6 +1946,9 @@ pub(crate) async fn resolve_environment(
|
|||
let workspace_cache = WorkspaceCache::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let resolve_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -1956,7 +1962,7 @@ pub(crate) async fn resolve_environment(
|
|||
config_setting,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
*link_mode,
|
||||
build_options,
|
||||
&build_hasher,
|
||||
|
|
@ -2083,6 +2089,9 @@ pub(crate) async fn sync_environment(
|
|||
};
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -2096,7 +2105,7 @@ pub(crate) async fn sync_environment(
|
|||
config_setting,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
link_mode,
|
||||
build_options,
|
||||
&build_hasher,
|
||||
|
|
@ -2309,6 +2318,9 @@ pub(crate) async fn update_environment(
|
|||
};
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -2322,7 +2334,7 @@ pub(crate) async fn update_environment(
|
|||
config_setting,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
*link_mode,
|
||||
build_options,
|
||||
&build_hasher,
|
||||
|
|
|
|||
|
|
@ -18,24 +18,23 @@ use uv_configuration::{
|
|||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_distribution_types::{
|
||||
DirectorySourceDist, Dist, Index, IndexLocations, Requirement, Resolution, ResolvedDist,
|
||||
DirectorySourceDist, Dist, Index, Requirement, Resolution, ResolvedDist,
|
||||
SourceDist,
|
||||
};
|
||||
use uv_fs::{PortablePathBuf, Simplified};
|
||||
use uv_installer::SitePackages;
|
||||
use uv_normalize::{DefaultExtras, DefaultGroups, PackageName};
|
||||
use uv_pep508::{MarkerTree, VersionOrUrl};
|
||||
use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl, ParsedUrl, VerbatimParsedUrl};
|
||||
use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl, ParsedUrl};
|
||||
use uv_python::{PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest};
|
||||
use uv_resolver::{FlatIndex, Installable, Lock};
|
||||
use uv_scripts::{Pep723ItemRef, Pep723Script};
|
||||
use uv_settings::PythonInstallMirrors;
|
||||
use uv_types::{BuildIsolation, HashStrategy};
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
use uv_workspace::pyproject::Source;
|
||||
use uv_workspace::pyproject::{ExtraBuildDependencies, Source};
|
||||
use uv_workspace::{
|
||||
DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache,
|
||||
pyproject::ExtraBuildDependencies,
|
||||
};
|
||||
|
||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
|
||||
|
|
@ -547,60 +546,6 @@ impl Deref for SyncEnvironment {
|
|||
}
|
||||
}
|
||||
|
||||
/// Lower extra build dependencies using workspace sources.
|
||||
///
|
||||
/// This ensures that extra build dependencies respect source configurations
|
||||
/// from the project's `tool.uv.sources` table.
|
||||
#[allow(clippy::result_large_err)]
|
||||
fn lower_extra_build_dependencies(
|
||||
extra_build_dependencies: &ExtraBuildDependencies,
|
||||
workspace: &Workspace,
|
||||
index_locations: &IndexLocations,
|
||||
) -> Result<ExtraBuildDependencies, ProjectError> {
|
||||
use std::collections::BTreeMap;
|
||||
use uv_configuration::SourceStrategy;
|
||||
|
||||
let mut lowered_dependencies = BTreeMap::new();
|
||||
|
||||
for (package_name, requirements) in extra_build_dependencies {
|
||||
// Use BuildRequires to lower the requirements
|
||||
let metadata = uv_distribution::BuildRequires::from_workspace(
|
||||
uv_pypi_types::BuildRequires {
|
||||
name: Some(package_name.clone()),
|
||||
requires_dist: requirements.clone(),
|
||||
},
|
||||
workspace,
|
||||
index_locations,
|
||||
SourceStrategy::Enabled,
|
||||
)?;
|
||||
|
||||
// Extract the lowered requirements and convert them
|
||||
let lowered_requirements: Vec<uv_pep508::Requirement<VerbatimParsedUrl>> =
|
||||
metadata.requires_dist.into_iter().map(Into::into).collect();
|
||||
lowered_dependencies.insert(package_name.clone(), lowered_requirements);
|
||||
}
|
||||
|
||||
Ok(ExtraBuildDependencies::from(lowered_dependencies))
|
||||
}
|
||||
|
||||
/// Lower extra build dependencies using script sources.
|
||||
///
|
||||
/// This ensures that extra build dependencies respect source configurations
|
||||
/// from the script's metadata.
|
||||
fn lower_extra_build_dependencies_for_script(
|
||||
extra_build_dependencies: &ExtraBuildDependencies,
|
||||
_script: &Pep723Script,
|
||||
_index_locations: &IndexLocations,
|
||||
) -> ExtraBuildDependencies {
|
||||
// Scripts don't have extra build dependencies per se, but we still need to handle
|
||||
// the case for consistency. Since scripts don't define extra build dependencies
|
||||
// for other packages, we just return the dependencies as-is.
|
||||
//
|
||||
// If in the future scripts support defining extra build dependencies for packages
|
||||
// they depend on, we would need to implement proper lowering here using the
|
||||
// script's sources.
|
||||
extra_build_dependencies.clone()
|
||||
}
|
||||
|
||||
/// Sync a lockfile with an environment.
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
|
|
@ -650,20 +595,24 @@ pub(super) async fn do_sync(
|
|||
);
|
||||
}
|
||||
|
||||
// Lower extra build dependencies to apply source configurations
|
||||
let extra_build_dependencies = match &target {
|
||||
// Lower the extra build dependencies with source resolution
|
||||
let extra_build_requires = match &target {
|
||||
InstallTarget::Workspace { workspace, .. }
|
||||
| InstallTarget::Project { workspace, .. }
|
||||
| InstallTarget::NonProjectWorkspace { workspace, .. } => {
|
||||
lower_extra_build_dependencies(extra_build_dependencies, workspace, index_locations)?
|
||||
uv_distribution::ExtraBuildRequires::from_workspace(
|
||||
extra_build_dependencies.clone(),
|
||||
workspace,
|
||||
index_locations,
|
||||
sources,
|
||||
)?
|
||||
}
|
||||
InstallTarget::Script { script, .. } => lower_extra_build_dependencies_for_script(
|
||||
extra_build_dependencies,
|
||||
script,
|
||||
index_locations,
|
||||
),
|
||||
InstallTarget::Script { .. } => uv_distribution::ExtraBuildRequires {
|
||||
extra_build_dependencies: ExtraBuildDependencies::default(),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
let client_builder = BaseClientBuilder::new()
|
||||
.retries_from_env()?
|
||||
.connectivity(network_settings.connectivity)
|
||||
|
|
@ -792,7 +741,7 @@ pub(super) async fn do_sync(
|
|||
config_setting,
|
||||
config_settings_package,
|
||||
build_isolation,
|
||||
&extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
link_mode,
|
||||
build_options,
|
||||
&build_hasher,
|
||||
|
|
|
|||
|
|
@ -275,7 +275,9 @@ pub(crate) async fn venv(
|
|||
|
||||
// Do not allow builds
|
||||
let build_options = BuildOptions::new(NoBinary::None, NoBuild::All);
|
||||
let extra_build_dependencies = ExtraBuildDependencies::default();
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
ExtraBuildDependencies::default(),
|
||||
);
|
||||
// Prep the build context.
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
|
|
@ -290,7 +292,7 @@ pub(crate) async fn venv(
|
|||
&config_settings,
|
||||
&config_settings_package,
|
||||
BuildIsolation::Isolated,
|
||||
&extra_build_dependencies,
|
||||
&extra_build_requires,
|
||||
link_mode,
|
||||
&build_options,
|
||||
&build_hasher,
|
||||
|
|
|
|||
|
|
@ -1573,8 +1573,9 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
child = { path = "child" }
|
||||
"#})?;
|
||||
|
||||
context.venv().arg("--clear").assert().success();
|
||||
// Running `uv sync` should fail due to missing build-dependencies
|
||||
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").arg("--refresh"), @r"
|
||||
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
|
@ -1607,7 +1608,8 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
child = ["anyio"]
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").arg("--refresh"), @r"
|
||||
context.venv().arg("--clear").assert().success();
|
||||
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -1620,7 +1622,8 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
+ child==0.1.0 (from file://[TEMP_DIR]/child)
|
||||
");
|
||||
|
||||
// Adding `extra-build-dependencies` with the wrong name should not
|
||||
// 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#"
|
||||
[project]
|
||||
name = "parent"
|
||||
|
|
@ -1635,7 +1638,8 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
wrong_name = ["anyio"]
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").arg("--refresh"), @r"
|
||||
context.venv().arg("--clear").assert().success();
|
||||
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
|
@ -1703,7 +1707,8 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
"#})?;
|
||||
|
||||
// Confirm that `bad_child` fails if anyio is provided
|
||||
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").arg("--refresh"), @r"
|
||||
context.venv().arg("--clear").assert().success();
|
||||
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
|
@ -1738,7 +1743,8 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
child = ["anyio"]
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").arg("--refresh"), @r"
|
||||
context.venv().arg("--clear").assert().success();
|
||||
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -1747,10 +1753,9 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
warning: The `extra-build-dependencies` option is experimental and may change without warning. Pass `--preview` to disable this warning.
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
+ bad-child==0.1.0 (from file://[TEMP_DIR]/bad_child)
|
||||
~ child==0.1.0 (from file://[TEMP_DIR]/child)
|
||||
+ child==0.1.0 (from file://[TEMP_DIR]/child)
|
||||
");
|
||||
|
||||
Ok(())
|
||||
|
|
@ -1815,7 +1820,7 @@ fn sync_extra_build_dependencies_sources() -> Result<()> {
|
|||
})?;
|
||||
|
||||
// Running `uv sync` should succeed, as `anyio` is provided as a source
|
||||
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").arg("--refresh"), @r"
|
||||
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -1828,6 +1833,9 @@ fn sync_extra_build_dependencies_sources() -> Result<()> {
|
|||
+ child==0.1.0 (from file://[TEMP_DIR]/child)
|
||||
");
|
||||
|
||||
// TODO(zanieb): We want to test with `--no-sources` too but unfortunately that's not easy
|
||||
// because it'll disable the `child` path source too!
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue