diff --git a/crates/uv-bench/benches/uv.rs b/crates/uv-bench/benches/uv.rs index 7657a3826..9809a6814 100644 --- a/crates/uv-bench/benches/uv.rs +++ b/crates/uv-bench/benches/uv.rs @@ -1,6 +1,6 @@ +use std::hint::black_box; use std::str::FromStr; -use std::hint::black_box; use uv_bench::criterion::{Criterion, criterion_group, criterion_main, measurement::WallTime}; use uv_cache::Cache; use uv_client::RegistryClientBuilder; @@ -92,7 +92,7 @@ mod resolver { use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::DistributionDatabase; use uv_distribution_types::{ - DependencyMetadata, ExtraBuildRequires, IndexLocations, RequiresPython, + DependencyMetadata, ExtraBuildRequires, ExtraBuildVariables, IndexLocations, RequiresPython, }; use uv_install_wheel::LinkMode; use uv_pep440::Version; @@ -144,6 +144,7 @@ mod resolver { ) -> Result { let build_isolation = BuildIsolation::default(); let extra_build_requires = ExtraBuildRequires::default(); + let extra_build_variables = ExtraBuildVariables::default(); let build_options = BuildOptions::default(); let concurrency = Concurrency::default(); let config_settings = ConfigSettings::default(); @@ -193,6 +194,7 @@ mod resolver { &config_settings_package, build_isolation, &extra_build_requires, + &extra_build_variables, LinkMode::default(), &build_options, &hashes, diff --git a/crates/uv-cli/src/options.rs b/crates/uv-cli/src/options.rs index 79ca96a7c..e8aab9945 100644 --- a/crates/uv-cli/src/options.rs +++ b/crates/uv-cli/src/options.rs @@ -355,6 +355,7 @@ pub fn resolver_options( no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"), no_build_isolation_package: Some(no_build_isolation_package), extra_build_dependencies: None, + extra_build_variables: None, exclude_newer: ExcludeNewer::from_args( exclude_newer, exclude_newer_package.unwrap_or_default(), @@ -477,6 +478,7 @@ pub fn resolver_installer_options( Some(no_build_isolation_package) }, extra_build_dependencies: None, + extra_build_variables: None, exclude_newer, exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter), link_mode, diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index c70e18ecc..12c116910 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -24,9 +24,9 @@ use uv_configuration::{BuildOutput, Concurrency}; use uv_distribution::DistributionDatabase; use uv_distribution_filename::DistFilename; use uv_distribution_types::{ - CachedDist, DependencyMetadata, ExtraBuildRequires, Identifier, IndexCapabilities, - IndexLocations, IsBuildBackendError, Name, Requirement, Resolution, SourceDist, - VersionOrUrlRef, + CachedDist, DependencyMetadata, ExtraBuildRequires, ExtraBuildVariables, Identifier, + IndexCapabilities, IndexLocations, IsBuildBackendError, Name, Requirement, Resolution, + SourceDist, VersionOrUrlRef, }; use uv_git::GitResolver; use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages}; @@ -90,6 +90,7 @@ pub struct BuildDispatch<'a> { dependency_metadata: &'a DependencyMetadata, build_isolation: BuildIsolation<'a>, extra_build_requires: &'a ExtraBuildRequires, + extra_build_variables: &'a ExtraBuildVariables, link_mode: uv_install_wheel::LinkMode, build_options: &'a BuildOptions, config_settings: &'a ConfigSettings, @@ -119,6 +120,7 @@ impl<'a> BuildDispatch<'a> { config_settings_package: &'a PackageConfigSettings, build_isolation: BuildIsolation<'a>, extra_build_requires: &'a ExtraBuildRequires, + extra_build_variables: &'a ExtraBuildVariables, link_mode: uv_install_wheel::LinkMode, build_options: &'a BuildOptions, hasher: &'a HashStrategy, @@ -142,6 +144,7 @@ impl<'a> BuildDispatch<'a> { config_settings_package, build_isolation, extra_build_requires, + extra_build_variables, link_mode, build_options, hasher, @@ -227,6 +230,10 @@ impl BuildContext for BuildDispatch<'_> { self.extra_build_requires } + fn extra_build_variables(&self) -> &ExtraBuildVariables { + self.extra_build_variables + } + async fn resolve<'data>( &'data self, requirements: &'data [Requirement], @@ -312,6 +319,7 @@ impl BuildContext for BuildDispatch<'_> { self.config_settings, self.config_settings_package, self.extra_build_requires(), + self.extra_build_variables, self.cache(), venv, tags, @@ -446,6 +454,18 @@ impl BuildContext for BuildDispatch<'_> { self.config_settings.clone() }; + // Get package-specific environment variables if available. + let mut environment_variables = self.build_extra_env_vars.clone(); + if let Some(name) = dist_name { + if let Some(package_vars) = self.extra_build_variables.get(name) { + environment_variables.extend( + package_vars + .iter() + .map(|(key, value)| (OsString::from(key), OsString::from(value))), + ); + } + } + let builder = SourceBuild::setup( source, subdirectory, @@ -464,7 +484,7 @@ impl BuildContext for BuildDispatch<'_> { self.extra_build_requires(), &build_stack, build_kind, - self.build_extra_env_vars.clone(), + environment_variables, build_output, self.concurrency.builds, self.preview, diff --git a/crates/uv-distribution-types/src/build_requires.rs b/crates/uv-distribution-types/src/build_requires.rs index e799e0fee..10067cc88 100644 --- a/crates/uv-distribution-types/src/build_requires.rs +++ b/crates/uv-distribution-types/src/build_requires.rs @@ -1,5 +1,7 @@ use std::collections::BTreeMap; +use serde::{Deserialize, Serialize}; + use uv_cache_key::{CacheKey, CacheKeyHasher}; use uv_normalize::PackageName; @@ -104,3 +106,52 @@ impl ExtraBuildRequires { .collect::>() } } + +/// A map of extra build variables, from variable name to value. +pub type BuildVariables = BTreeMap; + +/// Extra environment variables to set during builds, on a per-package basis. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct ExtraBuildVariables(BTreeMap); + +impl std::ops::Deref for ExtraBuildVariables { + type Target = BTreeMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for ExtraBuildVariables { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntoIterator for ExtraBuildVariables { + type Item = (PackageName, BuildVariables); + type IntoIter = std::collections::btree_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl FromIterator<(PackageName, BuildVariables)> for ExtraBuildVariables { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl CacheKey for ExtraBuildVariables { + fn cache_key(&self, state: &mut CacheKeyHasher) { + for (package, vars) in &self.0 { + package.as_str().cache_key(state); + for (key, value) in vars { + key.cache_key(state); + value.cache_key(state); + } + } + } +} diff --git a/crates/uv-distribution/src/index/built_wheel_index.rs b/crates/uv-distribution/src/index/built_wheel_index.rs index c24baeab2..aed0819ca 100644 --- a/crates/uv-distribution/src/index/built_wheel_index.rs +++ b/crates/uv-distribution/src/index/built_wheel_index.rs @@ -5,8 +5,8 @@ use uv_cache_info::CacheInfo; use uv_cache_key::cache_digest; use uv_configuration::{ConfigSettings, PackageConfigSettings}; use uv_distribution_types::{ - DirectUrlSourceDist, DirectorySourceDist, ExtraBuildRequirement, ExtraBuildRequires, - GitSourceDist, Hashed, PathSourceDist, + BuildVariables, DirectUrlSourceDist, DirectorySourceDist, ExtraBuildRequirement, + ExtraBuildRequires, ExtraBuildVariables, GitSourceDist, Hashed, PathSourceDist, }; use uv_normalize::PackageName; use uv_platform_tags::Tags; @@ -25,6 +25,7 @@ pub struct BuiltWheelIndex<'a> { config_settings: &'a ConfigSettings, config_settings_package: &'a PackageConfigSettings, extra_build_requires: &'a ExtraBuildRequires, + extra_build_variables: &'a ExtraBuildVariables, } impl<'a> BuiltWheelIndex<'a> { @@ -36,6 +37,7 @@ impl<'a> BuiltWheelIndex<'a> { config_settings: &'a ConfigSettings, config_settings_package: &'a PackageConfigSettings, extra_build_requires: &'a ExtraBuildRequires, + extra_build_variables: &'a ExtraBuildVariables, ) -> Self { Self { cache, @@ -44,6 +46,7 @@ impl<'a> BuiltWheelIndex<'a> { config_settings, config_settings_package, extra_build_requires, + extra_build_variables, } } @@ -75,10 +78,18 @@ 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 extra_build_deps = self.extra_build_requires_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_vars = self.extra_build_variables_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_vars.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_vars, + ))) }; Ok(self.find(&cache_shard)) @@ -114,10 +125,18 @@ 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 extra_build_deps = self.extra_build_requires_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_vars = self.extra_build_variables_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_vars.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_vars, + ))) }; Ok(self @@ -164,10 +183,18 @@ 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 extra_build_deps = self.extra_build_requires_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_vars = self.extra_build_variables_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_vars.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_vars, + ))) }; Ok(self @@ -192,10 +219,18 @@ 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 extra_build_deps = self.extra_build_requires_for(&source_dist.name); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_vars = self.extra_build_variables_for(&source_dist.name); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_vars.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_vars, + ))) }; self.find(&cache_shard) @@ -274,4 +309,9 @@ impl<'a> BuiltWheelIndex<'a> { .map(Vec::as_slice) .unwrap_or(&[]) } + + /// Determine the extra build variables for the given package name. + fn extra_build_variables_for(&self, name: &PackageName) -> Option<&BuildVariables> { + self.extra_build_variables.get(name) + } } diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 07ea80fa8..2e0e6c224 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -32,8 +32,8 @@ use uv_client::{ use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, SourceStrategy}; use uv_distribution_filename::{SourceDistExtension, WheelFilename}; use uv_distribution_types::{ - BuildableSource, DirectorySourceUrl, ExtraBuildRequirement, GitSourceUrl, HashPolicy, Hashed, - IndexUrl, PathSourceUrl, SourceDist, SourceUrl, + BuildVariables, BuildableSource, DirectorySourceUrl, ExtraBuildRequirement, GitSourceUrl, + HashPolicy, Hashed, IndexUrl, PathSourceUrl, SourceDist, SourceUrl, }; use uv_extract::hash::Hasher; use uv_fs::{rename_with_retry, write_atomic}; @@ -415,6 +415,11 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { .unwrap_or(&[]) } + /// Determine the extra build variables for the given package name. + fn extra_build_variables_for(&self, name: Option<&PackageName>) -> Option<&BuildVariables> { + name.and_then(|name| self.build_context.extra_build_variables().get(name)) + } + /// Build a source distribution from a remote URL. async fn url<'data>( &self, @@ -452,10 +457,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; // If the cache contains a compatible wheel, return it. @@ -665,10 +678,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; let task = self @@ -843,10 +864,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; // If the cache contains a compatible wheel, return it. @@ -1006,10 +1035,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; // Otherwise, we need to build a wheel. @@ -1149,10 +1186,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; // If the cache contains a compatible wheel, return it. @@ -1338,10 +1383,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; // Otherwise, we need to build a wheel. @@ -1544,10 +1597,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; // If the cache contains a compatible wheel, return it. @@ -1848,10 +1909,18 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // 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 extra_build_deps = self.extra_build_dependencies_for(source.name()); - let cache_shard = if config_settings.is_empty() && extra_build_deps.is_empty() { + let extra_build_variables = self.extra_build_variables_for(source.name()); + let cache_shard = if config_settings.is_empty() + && extra_build_deps.is_empty() + && extra_build_variables.is_none() + { cache_shard } else { - cache_shard.shard(cache_digest(&(&config_settings, extra_build_deps))) + cache_shard.shard(cache_digest(&( + &config_settings, + extra_build_deps, + extra_build_variables, + ))) }; // Otherwise, we need to build a wheel. diff --git a/crates/uv-installer/src/plan.rs b/crates/uv-installer/src/plan.rs index ab88010e4..0591ea015 100644 --- a/crates/uv-installer/src/plan.rs +++ b/crates/uv-installer/src/plan.rs @@ -12,8 +12,9 @@ use uv_distribution::{ }; use uv_distribution_filename::WheelFilename; use uv_distribution_types::{ - BuiltDist, CachedDirectUrlDist, CachedDist, Dist, Error, ExtraBuildRequires, Hashed, - IndexLocations, InstalledDist, Name, RequirementSource, Resolution, ResolvedDist, SourceDist, + BuiltDist, CachedDirectUrlDist, CachedDist, Dist, Error, ExtraBuildRequires, + ExtraBuildVariables, Hashed, IndexLocations, InstalledDist, Name, RequirementSource, + Resolution, ResolvedDist, SourceDist, }; use uv_fs::Simplified; use uv_platform_tags::{IncompatibleTag, TagCompatibility, Tags}; @@ -57,6 +58,7 @@ impl<'a> Planner<'a> { config_settings: &ConfigSettings, config_settings_package: &PackageConfigSettings, extra_build_requires: &ExtraBuildRequires, + extra_build_variables: &ExtraBuildVariables, cache: &Cache, venv: &PythonEnvironment, tags: &Tags, @@ -71,6 +73,7 @@ impl<'a> Planner<'a> { config_settings, config_settings_package, extra_build_requires, + extra_build_variables, ); let mut cached = vec![]; diff --git a/crates/uv-settings/src/combine.rs b/crates/uv-settings/src/combine.rs index 6c08cbc61..15aedec33 100644 --- a/crates/uv-settings/src/combine.rs +++ b/crates/uv-settings/src/combine.rs @@ -7,7 +7,9 @@ use uv_configuration::{ ConfigSettings, ExportFormat, IndexStrategy, KeyringProviderType, PackageConfigSettings, RequiredVersion, TargetTriple, TrustedPublishing, }; -use uv_distribution_types::{Index, IndexUrl, PipExtraIndex, PipFindLinks, PipIndex}; +use uv_distribution_types::{ + ExtraBuildVariables, Index, IndexUrl, PipExtraIndex, PipFindLinks, PipIndex, +}; use uv_install_wheel::LinkMode; use uv_pypi_types::{SchemaConflicts, SupportedEnvironments}; use uv_python::{PythonDownloads, PythonPreference, PythonVersion}; @@ -235,3 +237,32 @@ impl Combine for Option { } } } + +impl Combine for ExtraBuildVariables { + fn combine(mut self, other: Self) -> Self { + for (key, value) in other { + match self.entry(key) { + std::collections::btree_map::Entry::Occupied(mut entry) => { + // Combine the maps, with self taking precedence + let existing = entry.get_mut(); + for (k, v) in value { + existing.entry(k).or_insert(v); + } + } + std::collections::btree_map::Entry::Vacant(entry) => { + entry.insert(value); + } + } + } + self + } +} + +impl Combine for Option { + fn combine(self, other: Self) -> Self { + match (self, other) { + (Some(a), Some(b)) => Some(a.combine(b)), + (a, b) => a.or(b), + } + } +} diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index 4ca8a5af8..4eda772ad 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -318,6 +318,7 @@ fn warn_uv_toml_masked_fields(options: &Options) { no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, exclude_newer, exclude_newer_package, link_mode, @@ -449,6 +450,9 @@ fn warn_uv_toml_masked_fields(options: &Options) { if extra_build_dependencies.is_some() { masked_fields.push("extra-build-dependencies"); } + if extra_build_variables.is_some() { + masked_fields.push("extra-build-variables"); + } if exclude_newer.is_some() { masked_fields.push("exclude-newer"); } diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index cf4bb18fe..5eaa7e2eb 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -8,11 +8,11 @@ use uv_configuration::{ PackageNameSpecifier, RequiredVersion, TargetTriple, TrustedHost, TrustedPublishing, }; use uv_distribution_types::{ - Index, IndexUrl, IndexUrlError, PipExtraIndex, PipFindLinks, PipIndex, StaticMetadata, + ExtraBuildVariables, Index, IndexUrl, IndexUrlError, PipExtraIndex, PipFindLinks, PipIndex, + StaticMetadata, }; use uv_install_wheel::LinkMode; use uv_macros::{CombineOptions, OptionsMetadata}; - use uv_normalize::{ExtraName, PackageName, PipGroupName}; use uv_pep508::Requirement; use uv_pypi_types::{SupportedEnvironments, VerbatimParsedUrl}; @@ -377,6 +377,7 @@ pub struct ResolverOptions { pub no_build_isolation: Option, pub no_build_isolation_package: Option>, pub extra_build_dependencies: Option, + pub extra_build_variables: Option, pub no_sources: Option, } @@ -643,6 +644,19 @@ pub struct ResolverInstallerOptions { "# )] pub extra_build_dependencies: Option, + /// Extra environment variables to set when building certain packages. + /// + /// Environment variables will be added to the environment when building the + /// specified packages. + #[option( + default = r#"{}"#, + value_type = r#"dict[str, dict[str, str]]"#, + example = r#" + [tool.uv.extra-build-variables] + flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } + "# + )] + pub extra_build_variables: Option, /// Limit candidate packages to those that were uploaded prior to a given point in time. /// /// Accepts a superset of [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) (e.g., @@ -1164,6 +1178,19 @@ pub struct PipOptions { "# )] pub extra_build_dependencies: Option, + /// Extra environment variables to set when building certain packages. + /// + /// Environment variables will be added to the environment when building the + /// specified packages. + #[option( + default = r#"{}"#, + value_type = r#"dict[str, dict[str, str]]"#, + example = r#" + [extra-build-variables] + flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } + "# + )] + pub extra_build_variables: Option, /// Validate the Python environment, to detect packages with missing dependencies and other /// issues. #[option( @@ -1749,6 +1776,7 @@ impl From for ResolverOptions { no_build_isolation: value.no_build_isolation, no_build_isolation_package: value.no_build_isolation_package, extra_build_dependencies: value.extra_build_dependencies, + extra_build_variables: value.extra_build_variables, no_sources: value.no_sources, } } @@ -1815,6 +1843,7 @@ pub struct ToolOptions { pub no_build_isolation: Option, pub no_build_isolation_package: Option>, pub extra_build_dependencies: Option, + pub extra_build_variables: Option, pub exclude_newer: Option, pub exclude_newer_package: Option, pub link_mode: Option, @@ -1845,6 +1874,7 @@ impl From for ToolOptions { no_build_isolation: value.no_build_isolation, no_build_isolation_package: value.no_build_isolation_package, extra_build_dependencies: value.extra_build_dependencies, + extra_build_variables: value.extra_build_variables, exclude_newer: value.exclude_newer, exclude_newer_package: value.exclude_newer_package, link_mode: value.link_mode, @@ -1877,6 +1907,7 @@ impl From for ResolverInstallerOptions { no_build_isolation: value.no_build_isolation, no_build_isolation_package: value.no_build_isolation_package, extra_build_dependencies: value.extra_build_dependencies, + extra_build_variables: value.extra_build_variables, exclude_newer: value.exclude_newer, exclude_newer_package: value.exclude_newer_package, link_mode: value.link_mode, @@ -1932,6 +1963,7 @@ pub struct OptionsWire { no_build_isolation: Option, no_build_isolation_package: Option>, extra_build_dependencies: Option, + extra_build_variables: Option, exclude_newer: Option, exclude_newer_package: Option, link_mode: Option, @@ -2052,6 +2084,7 @@ impl From for Options { default_groups, dependency_groups, extra_build_dependencies, + extra_build_variables, dev_dependencies, managed, package, @@ -2093,6 +2126,7 @@ impl From for Options { no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, exclude_newer, exclude_newer_package, link_mode, diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index f6e51d929..87bf76d6e 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -12,8 +12,9 @@ use uv_configuration::{ }; use uv_distribution_filename::DistFilename; use uv_distribution_types::{ - CachedDist, DependencyMetadata, DistributionId, ExtraBuildRequires, IndexCapabilities, - IndexLocations, InstalledDist, IsBuildBackendError, Requirement, Resolution, SourceDist, + CachedDist, DependencyMetadata, DistributionId, ExtraBuildRequires, ExtraBuildVariables, + IndexCapabilities, IndexLocations, InstalledDist, IsBuildBackendError, Requirement, Resolution, + SourceDist, }; use uv_git::GitResolver; use uv_pep508::PackageName; @@ -106,6 +107,9 @@ pub trait BuildContext { /// Get the extra build requirements. fn extra_build_requires(&self) -> &ExtraBuildRequires; + /// Get the extra build variables. + fn extra_build_variables(&self) -> &ExtraBuildVariables; + /// Resolve the given requirements into a ready-to-install set of package versions. fn resolve<'a>( &'a self, diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index 176c2b10e..3bc7322fe 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -20,8 +20,9 @@ use rustc_hash::{FxBuildHasher, FxHashSet}; use serde::de::{IntoDeserializer, SeqAccess}; use serde::{Deserialize, Deserializer, Serialize}; use thiserror::Error; + use uv_build_backend::BuildBackendSettings; -use uv_distribution_types::{Index, IndexName, RequirementSource}; +use uv_distribution_types::{ExtraBuildVariables, Index, IndexName, RequirementSource}; use uv_fs::{PortablePathBuf, relative_to}; use uv_git_types::GitReference; use uv_macros::OptionsMetadata; @@ -442,6 +443,20 @@ pub struct ToolUv { )] pub extra_build_dependencies: Option, + /// Extra environment variables to set when building certain packages. + /// + /// Environment variables will be added to the environment when building the + /// specified packages. + #[option( + default = r#"{}"#, + value_type = r#"dict[str, dict[str, str]]"#, + example = r#" + [tool.uv.extra-build-variables] + flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } + "# + )] + pub extra_build_variables: Option, + /// The project's development dependencies. /// /// Development dependencies will be installed by default in `uv run` and `uv sync`, but will diff --git a/crates/uv-workspace/src/workspace.rs b/crates/uv-workspace/src/workspace.rs index 63b489b73..62aa9ca1c 100644 --- a/crates/uv-workspace/src/workspace.rs +++ b/crates/uv-workspace/src/workspace.rs @@ -1971,6 +1971,7 @@ mod tests { "default-groups": null, "dependency-groups": null, "extra-build-dependencies": null, + "extra-build-variables": null, "dev-dependencies": null, "override-dependencies": null, "constraint-dependencies": null, @@ -2072,6 +2073,7 @@ mod tests { "default-groups": null, "dependency-groups": null, "extra-build-dependencies": null, + "extra-build-variables": null, "dev-dependencies": null, "override-dependencies": null, "constraint-dependencies": null, @@ -2286,6 +2288,7 @@ mod tests { "default-groups": null, "dependency-groups": null, "extra-build-dependencies": null, + "extra-build-variables": null, "dev-dependencies": null, "override-dependencies": null, "constraint-dependencies": null, @@ -2396,6 +2399,7 @@ mod tests { "default-groups": null, "dependency-groups": null, "extra-build-dependencies": null, + "extra-build-variables": null, "dev-dependencies": null, "override-dependencies": null, "constraint-dependencies": null, @@ -2519,6 +2523,7 @@ mod tests { "default-groups": null, "dependency-groups": null, "extra-build-dependencies": null, + "extra-build-variables": null, "dev-dependencies": null, "override-dependencies": null, "constraint-dependencies": null, @@ -2616,6 +2621,7 @@ mod tests { "default-groups": null, "dependency-groups": null, "extra-build-dependencies": null, + "extra-build-variables": null, "dev-dependencies": null, "override-dependencies": null, "constraint-dependencies": null, diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 726295592..62d00a226 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -24,7 +24,7 @@ use uv_distribution_filename::{ DistFilename, SourceDistExtension, SourceDistFilename, WheelFilename, }; use uv_distribution_types::{ - DependencyMetadata, Index, IndexLocations, RequiresPython, SourceDist, + DependencyMetadata, ExtraBuildVariables, Index, IndexLocations, RequiresPython, SourceDist, }; use uv_fs::{Simplified, relative_to}; use uv_install_wheel::LinkMode; @@ -203,6 +203,7 @@ async fn build_impl( no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, exclude_newer, link_mode, upgrade: _, @@ -350,6 +351,7 @@ async fn build_impl( *no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, *index_strategy, *keyring_provider, exclude_newer.clone(), @@ -429,6 +431,7 @@ async fn build_package( no_build_isolation: bool, no_build_isolation_package: &[PackageName], extra_build_dependencies: &ExtraBuildDependencies, + extra_build_variables: &ExtraBuildVariables, index_strategy: IndexStrategy, keyring_provider: KeyringProviderType, exclude_newer: ExcludeNewer, @@ -583,6 +586,7 @@ async fn build_package( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, build_options, &hasher, diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 8ea5446d3..56261cbe7 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -21,8 +21,9 @@ use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::LoweredExtraBuildDependencies; use uv_distribution_types::{ - DependencyMetadata, HashGeneration, Index, IndexLocations, NameRequirementSpecification, - Origin, Requirement, RequiresPython, UnresolvedRequirementSpecification, Verbatim, + DependencyMetadata, ExtraBuildVariables, HashGeneration, Index, IndexLocations, + NameRequirementSpecification, Origin, Requirement, RequiresPython, + UnresolvedRequirementSpecification, Verbatim, }; use uv_fs::{CWD, Simplified}; use uv_git::ResolvedRepositoryReference; @@ -98,6 +99,7 @@ pub(crate) async fn pip_compile( no_build_isolation: bool, no_build_isolation_package: Vec, extra_build_dependencies: &ExtraBuildDependencies, + extra_build_variables: &ExtraBuildVariables, build_options: BuildOptions, mut python_version: Option, python_platform: Option, @@ -502,6 +504,7 @@ pub(crate) async fn pip_compile( &config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, &build_options, &build_hashes, diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index da61a0087..ee3542268 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -17,8 +17,8 @@ use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::LoweredExtraBuildDependencies; use uv_distribution_types::{ - DependencyMetadata, Index, IndexLocations, NameRequirementSpecification, Origin, Requirement, - Resolution, UnresolvedRequirementSpecification, + DependencyMetadata, ExtraBuildVariables, Index, IndexLocations, NameRequirementSpecification, + Origin, Requirement, Resolution, UnresolvedRequirementSpecification, }; use uv_fs::Simplified; use uv_install_wheel::LinkMode; @@ -81,6 +81,7 @@ pub(crate) async fn pip_install( no_build_isolation: bool, no_build_isolation_package: Vec, extra_build_dependencies: &ExtraBuildDependencies, + extra_build_variables: &ExtraBuildVariables, build_options: BuildOptions, modifications: Modifications, python_version: Option, @@ -445,6 +446,7 @@ pub(crate) async fn pip_install( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, &build_options, &build_hasher, @@ -577,6 +579,7 @@ pub(crate) async fn pip_install( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, &build_options, &hasher, diff --git a/crates/uv/src/commands/pip/operations.rs b/crates/uv/src/commands/pip/operations.rs index 680eca490..710c7f6f6 100644 --- a/crates/uv/src/commands/pip/operations.rs +++ b/crates/uv/src/commands/pip/operations.rs @@ -465,6 +465,7 @@ pub(crate) async fn install( build_dispatch.config_settings(), build_dispatch.config_settings_package(), build_dispatch.extra_build_requires(), + build_dispatch.extra_build_variables(), cache, venv, tags, diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 1b534dd96..35cc485f1 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -15,7 +15,9 @@ use uv_configuration::{ use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::LoweredExtraBuildDependencies; -use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, Origin, Resolution}; +use uv_distribution_types::{ + DependencyMetadata, ExtraBuildVariables, Index, IndexLocations, Origin, Resolution, +}; use uv_fs::Simplified; use uv_install_wheel::LinkMode; use uv_installer::SitePackages; @@ -70,6 +72,7 @@ pub(crate) async fn pip_sync( no_build_isolation: bool, no_build_isolation_package: Vec, extra_build_dependencies: &ExtraBuildDependencies, + extra_build_variables: &ExtraBuildVariables, build_options: BuildOptions, python_version: Option, python_platform: Option, @@ -380,6 +383,7 @@ pub(crate) async fn pip_sync( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, &build_options, &build_hasher, @@ -514,6 +518,7 @@ pub(crate) async fn pip_sync( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, &build_options, &build_hasher, diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 25589dd72..34d927d54 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -452,6 +452,7 @@ pub(crate) async fn add( .into_inner(); // Create a build dispatch. + let extra_build_variables = settings.resolver.extra_build_variables.clone(); let build_dispatch = BuildDispatch::new( &client, cache, @@ -466,6 +467,7 @@ pub(crate) async fn add( &settings.resolver.config_settings_package, build_isolation, &extra_build_requires, + &extra_build_variables, settings.resolver.link_mode, &settings.resolver.build_options, &build_hasher, diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 29fd3b949..0259b76d2 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -436,6 +436,7 @@ async fn do_lock( no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, exclude_newer, link_mode, upgrade, @@ -706,6 +707,7 @@ async fn do_lock( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, *link_mode, build_options, &build_hasher, diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index abe57f37a..15da8c933 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -1700,6 +1700,7 @@ pub(crate) async fn resolve_names( no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, prerelease: _, resolution: _, sources, @@ -1767,6 +1768,7 @@ pub(crate) async fn resolve_names( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, *link_mode, build_options, &build_hasher, @@ -1860,6 +1862,7 @@ pub(crate) async fn resolve_environment( no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, exclude_newer, link_mode, upgrade: _, @@ -1982,6 +1985,7 @@ pub(crate) async fn resolve_environment( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, *link_mode, build_options, &build_hasher, @@ -2050,6 +2054,7 @@ pub(crate) async fn sync_environment( no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, exclude_newer, link_mode, compile_bytecode, @@ -2127,6 +2132,7 @@ pub(crate) async fn sync_environment( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, build_options, &build_hasher, @@ -2221,6 +2227,7 @@ pub(crate) async fn update_environment( no_build_isolation, no_build_isolation_package, extra_build_dependencies: _, + extra_build_variables, prerelease, resolution, sources, @@ -2351,6 +2358,7 @@ pub(crate) async fn update_environment( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, *link_mode, build_options, &build_hasher, diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index 44d9dfe82..416736bf1 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -586,6 +586,7 @@ pub(super) async fn do_sync( no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, exclude_newer, link_mode, compile_bytecode, @@ -631,6 +632,7 @@ pub(super) async fn do_sync( no_build_isolation, no_build_isolation_package: no_build_isolation_package.to_vec(), extra_build_dependencies: extra_build_dependencies.clone(), + extra_build_variables: extra_build_variables.clone(), prerelease: PrereleaseMode::default(), resolution: ResolutionMode::default(), sources, @@ -773,6 +775,7 @@ pub(super) async fn do_sync( config_settings_package, build_isolation, &extra_build_requires, + extra_build_variables, link_mode, build_options, &build_hasher, diff --git a/crates/uv/src/commands/project/tree.rs b/crates/uv/src/commands/project/tree.rs index 1f8f46a3d..2fff48e46 100644 --- a/crates/uv/src/commands/project/tree.rs +++ b/crates/uv/src/commands/project/tree.rs @@ -204,6 +204,7 @@ pub(crate) async fn tree( no_build_isolation: _, no_build_isolation_package: _, extra_build_dependencies: _, + extra_build_variables: _, exclude_newer: _, link_mode: _, upgrade: _, diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 48b63182a..da6a7acab 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -267,6 +267,7 @@ pub(crate) async fn venv( // Do not allow builds let build_options = BuildOptions::new(NoBinary::None, NoBuild::All); let extra_build_requires = ExtraBuildRequires::default(); + let extra_build_variables = uv_distribution_types::ExtraBuildVariables::default(); // Prep the build context. let build_dispatch = BuildDispatch::new( &client, @@ -282,6 +283,7 @@ pub(crate) async fn venv( &config_settings_package, BuildIsolation::Isolated, &extra_build_requires, + &extra_build_variables, link_mode, &build_options, &build_hasher, diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index c12923123..3a2d5c129 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -517,6 +517,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.no_build_isolation, args.settings.no_build_isolation_package, &args.settings.extra_build_dependencies, + &args.settings.extra_build_variables, args.settings.build_options, args.settings.python_version, args.settings.python_platform, @@ -595,6 +596,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.no_build_isolation, args.settings.no_build_isolation_package, &args.settings.extra_build_dependencies, + &args.settings.extra_build_variables, args.settings.build_options, args.settings.python_version, args.settings.python_platform, @@ -738,6 +740,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.no_build_isolation, args.settings.no_build_isolation_package, &args.settings.extra_build_dependencies, + &args.settings.extra_build_variables, args.settings.build_options, args.modifications, args.settings.python_version, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index e048f1c95..ad26eeaa9 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -27,7 +27,9 @@ use uv_configuration::{ Reinstall, RequiredVersion, SourceStrategy, TargetTriple, TrustedHost, TrustedPublishing, Upgrade, VersionControlSystem, }; -use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, IndexUrl, Requirement}; +use uv_distribution_types::{ + DependencyMetadata, ExtraBuildVariables, Index, IndexLocations, IndexUrl, Requirement, +}; use uv_install_wheel::LinkMode; use uv_normalize::{PackageName, PipGroupName}; use uv_pep508::{ExtraName, MarkerTree, RequirementOrigin}; @@ -2721,6 +2723,7 @@ pub(crate) struct InstallerSettingsRef<'a> { pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: &'a [PackageName], pub(crate) extra_build_dependencies: &'a ExtraBuildDependencies, + pub(crate) extra_build_variables: &'a ExtraBuildVariables, pub(crate) exclude_newer: ExcludeNewer, pub(crate) link_mode: LinkMode, pub(crate) compile_bytecode: bool, @@ -2748,6 +2751,7 @@ pub(crate) struct ResolverSettings { pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: Vec, pub(crate) extra_build_dependencies: ExtraBuildDependencies, + pub(crate) extra_build_variables: ExtraBuildVariables, pub(crate) prerelease: PrereleaseMode, pub(crate) resolution: ResolutionMode, pub(crate) sources: SourceStrategy, @@ -2801,6 +2805,7 @@ impl From for ResolverSettings { no_build_isolation: value.no_build_isolation.unwrap_or_default(), no_build_isolation_package: value.no_build_isolation_package.unwrap_or_default(), extra_build_dependencies: value.extra_build_dependencies.unwrap_or_default(), + extra_build_variables: value.extra_build_variables.unwrap_or_default(), exclude_newer: value.exclude_newer, link_mode: value.link_mode.unwrap_or_default(), sources: SourceStrategy::from_args(value.no_sources.unwrap_or_default()), @@ -2899,6 +2904,7 @@ impl From for ResolverInstallerSettings { no_build_isolation: value.no_build_isolation.unwrap_or_default(), no_build_isolation_package: value.no_build_isolation_package.unwrap_or_default(), extra_build_dependencies: value.extra_build_dependencies.unwrap_or_default(), + extra_build_variables: value.extra_build_variables.unwrap_or_default(), prerelease: value.prerelease.unwrap_or_default(), resolution: value.resolution.unwrap_or_default(), sources: SourceStrategy::from_args(value.no_sources.unwrap_or_default()), @@ -2942,6 +2948,7 @@ pub(crate) struct PipSettings { pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: Vec, pub(crate) extra_build_dependencies: ExtraBuildDependencies, + pub(crate) extra_build_variables: ExtraBuildVariables, pub(crate) build_options: BuildOptions, pub(crate) allow_empty_requirements: bool, pub(crate) strict: bool, @@ -3010,6 +3017,7 @@ impl PipSettings { no_build_isolation, no_build_isolation_package, extra_build_dependencies, + extra_build_variables, strict, extra, all_extras, @@ -3070,6 +3078,7 @@ impl PipSettings { no_build_isolation: top_level_no_build_isolation, no_build_isolation_package: top_level_no_build_isolation_package, extra_build_dependencies: top_level_extra_build_dependencies, + extra_build_variables: top_level_extra_build_variables, exclude_newer: top_level_exclude_newer, link_mode: top_level_link_mode, compile_bytecode: top_level_compile_bytecode, @@ -3108,6 +3117,7 @@ impl PipSettings { no_build_isolation_package.combine(top_level_no_build_isolation_package); let extra_build_dependencies = extra_build_dependencies.combine(top_level_extra_build_dependencies); + let extra_build_variables = extra_build_variables.combine(top_level_extra_build_variables); let exclude_newer = args .exclude_newer .combine(exclude_newer) @@ -3215,6 +3225,10 @@ impl PipSettings { .extra_build_dependencies .combine(extra_build_dependencies) .unwrap_or_default(), + extra_build_variables: args + .extra_build_variables + .combine(extra_build_variables) + .unwrap_or_default(), config_setting: args .config_settings .combine(config_settings) @@ -3323,6 +3337,7 @@ impl<'a> From<&'a ResolverInstallerSettings> for InstallerSettingsRef<'a> { no_build_isolation: settings.resolver.no_build_isolation, no_build_isolation_package: &settings.resolver.no_build_isolation_package, extra_build_dependencies: &settings.resolver.extra_build_dependencies, + extra_build_variables: &settings.resolver.extra_build_variables, exclude_newer: settings.resolver.exclude_newer.clone(), link_mode: settings.resolver.link_mode, compile_bytecode: settings.compile_bytecode, diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index ff9c4383f..f4232e0a5 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -187,6 +187,9 @@ fn resolve_uv_toml() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -384,6 +387,9 @@ fn resolve_uv_toml() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -582,6 +588,9 @@ fn resolve_uv_toml() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -812,6 +821,9 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -977,6 +989,9 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -1186,6 +1201,9 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -1443,6 +1461,9 @@ fn resolve_index_url() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -1710,6 +1731,9 @@ fn resolve_index_url() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -1932,6 +1956,9 @@ fn resolve_find_links() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -2119,6 +2146,9 @@ fn resolve_top_level() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -2366,6 +2396,9 @@ fn resolve_top_level() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -2596,6 +2629,9 @@ fn resolve_top_level() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -2782,6 +2818,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -2952,6 +2991,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -3122,6 +3164,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -3294,6 +3339,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -3457,6 +3505,7 @@ fn resolve_tool() -> anyhow::Result<()> { no_build_isolation: None, no_build_isolation_package: None, extra_build_dependencies: None, + extra_build_variables: None, exclude_newer: None, exclude_newer_package: None, link_mode: Some( @@ -3508,6 +3557,9 @@ fn resolve_tool() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), prerelease: IfNecessaryOrExplicit, resolution: LowestDirect, sources: Enabled, @@ -3669,6 +3721,9 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -3907,6 +3962,9 @@ fn resolve_both() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -4149,6 +4207,9 @@ fn resolve_both_special_fields() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -4470,6 +4531,9 @@ fn resolve_config_file() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -4555,7 +4619,7 @@ fn resolve_config_file() -> anyhow::Result<()> { | 1 | [project] | ^^^^^^^ - unknown field `project`, expected one of `required-version`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `config-settings-package`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `exclude-newer`, `exclude-newer-package`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend` + unknown field `project`, expected one of `required-version`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `config-settings-package`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `extra-build-variables`, `exclude-newer`, `exclude-newer-package`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend` " ); @@ -4733,6 +4797,9 @@ fn resolve_skip_empty() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -4906,6 +4973,9 @@ fn resolve_skip_empty() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -5098,6 +5168,9 @@ fn allow_insecure_host() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -5351,6 +5424,9 @@ fn index_priority() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -5583,6 +5659,9 @@ fn index_priority() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -5821,6 +5900,9 @@ fn index_priority() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -6054,6 +6136,9 @@ fn index_priority() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -6294,6 +6379,9 @@ fn index_priority() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -6527,6 +6615,9 @@ fn index_priority() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -6704,6 +6795,9 @@ fn verify_hashes() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -6867,6 +6961,9 @@ fn verify_hashes() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -7028,6 +7125,9 @@ fn verify_hashes() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -7191,6 +7291,9 @@ fn verify_hashes() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -7352,6 +7455,9 @@ fn verify_hashes() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -7514,6 +7620,9 @@ fn verify_hashes() -> anyhow::Result<()> { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), build_options: BuildOptions { no_binary: None, no_build: None, @@ -7685,6 +7794,9 @@ fn preview_features() { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), prerelease: IfNecessaryOrExplicit, resolution: Highest, sources: Enabled, @@ -7795,6 +7907,9 @@ fn preview_features() { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), prerelease: IfNecessaryOrExplicit, resolution: Highest, sources: Enabled, @@ -7905,6 +8020,9 @@ fn preview_features() { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), prerelease: IfNecessaryOrExplicit, resolution: Highest, sources: Enabled, @@ -8015,6 +8133,9 @@ fn preview_features() { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), prerelease: IfNecessaryOrExplicit, resolution: Highest, sources: Enabled, @@ -8125,6 +8246,9 @@ fn preview_features() { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), prerelease: IfNecessaryOrExplicit, resolution: Highest, sources: Enabled, @@ -8237,6 +8361,9 @@ fn preview_features() { extra_build_dependencies: ExtraBuildDependencies( {}, ), + extra_build_variables: ExtraBuildVariables( + {}, + ), prerelease: IfNecessaryOrExplicit, resolution: Highest, sources: Enabled, diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index 277704197..b2ff3a16a 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -12766,3 +12766,141 @@ fn sync_build_dependencies_respect_locked_versions() -> Result<()> { Ok(()) } + +#[test] +fn sync_extra_build_variables() -> Result<()> { + let context = TestContext::new("3.12").with_filtered_counts(); + + // Create a build backend that asserts that `EXPECTED_ANYIO_VERSION` matches the installed version of `anyio`. + let build_backend = context.temp_dir.child("build_backend.py"); + build_backend.write_str(indoc! {r#" + import os + import sys + from hatchling.build import * + + expected_version = os.environ.get("EXPECTED_ANYIO_VERSION", "") + if not expected_version: + print("`EXPECTED_ANYIO_VERSION` not set", file=sys.stderr) + sys.exit(1) + + try: + import anyio + except ModuleNotFoundError: + print("Missing `anyio` module", file=sys.stderr) + sys.exit(1) + + from importlib.metadata import version + anyio_version = version("anyio") + + if not anyio_version.startswith(expected_version): + print(f"Expected `anyio` version {expected_version} but got {anyio_version}", file=sys.stderr) + sys.exit(1) + + print(f"Found expected `anyio` version {anyio_version}", file=sys.stderr) + "#})?; + + // Create a project that will resolve to a non-latest version of `anyio` + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str(indoc! {r#" + [project] + name = "parent" + version = "0.1.0" + requires-python = ">=3.9" + + [build-system] + requires = ["hatchling", "anyio"] + backend-path = ["."] + build-backend = "build_backend" + "#})?; + context.temp_dir.child("src/parent/__init__.py").touch()?; + + uv_snapshot!(context.filters(), context.lock(), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved [N] packages in [TIME] + "); + + // Ensure our build backend is checking the version correctly. + uv_snapshot!(context.filters(), context.sync().env("EXPECTED_ANYIO_VERSION", "3.0"), @r" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + Resolved [N] packages in [TIME] + × Failed to build `parent @ file://[TEMP_DIR]/` + ├─▶ The build backend returned an error + ╰─▶ Call to `build_backend.build_editable` failed (exit status: 1) + + [stderr] + Expected `anyio` version 3.0 but got 4.3.0 + + hint: This usually indicates a problem with the package or the build environment. + "); + + // Set the variable in TOML (to an incorrect value). + pyproject_toml.write_str(indoc! {r#" + [project] + name = "parent" + version = "0.1.0" + requires-python = ">=3.9" + + [build-system] + requires = ["hatchling", "anyio"] + backend-path = ["."] + build-backend = "build_backend" + + [tool.uv.extra-build-variables] + parent = { EXPECTED_ANYIO_VERSION = "3.0" } + "#})?; + + uv_snapshot!(context.filters(), context.sync(), @r" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + Resolved [N] packages in [TIME] + × Failed to build `parent @ file://[TEMP_DIR]/` + ├─▶ The build backend returned an error + ╰─▶ Call to `build_backend.build_editable` failed (exit status: 1) + + [stderr] + Expected `anyio` version 3.0 but got 4.3.0 + + hint: This usually indicates a problem with the package or the build environment. + "); + + // Set the variable in TOML (to a correct value). + pyproject_toml.write_str(indoc! {r#" + [project] + name = "parent" + version = "0.1.0" + requires-python = ">=3.9" + + [build-system] + requires = ["hatchling", "anyio"] + backend-path = ["."] + build-backend = "build_backend" + + [tool.uv.extra-build-variables] + parent = { EXPECTED_ANYIO_VERSION = "4.3.0" } + "#})?; + + uv_snapshot!(context.filters(), context.sync(), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved [N] packages in [TIME] + Prepared [N] packages in [TIME] + Installed [N] packages in [TIME] + + parent==0.1.0 (from file://[TEMP_DIR]/) + "); + + Ok(()) +} diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 8062f227d..bbd1126bb 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -224,6 +224,27 @@ pytest = ["pip"] --- +### [`extra-build-variables`](#extra-build-variables) {: #extra-build-variables } + +Extra environment variables to set when building certain packages. + +Environment variables will be added to the environment when building the +specified packages. + +**Default value**: `{}` + +**Type**: `dict[str, dict[str, str]]` + +**Example usage**: + +```toml title="pyproject.toml" + +[tool.uv.extra-build-variables] +flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } +``` + +--- + ### [`index`](#index) {: #index } The indexes to use when resolving dependencies. @@ -1179,6 +1200,34 @@ additional packages. This is useful for packages that assume the presence of pac --- +### [`extra-build-variables`](#extra-build-variables) {: #extra-build-variables } + +Extra environment variables to set when building certain packages. + +Environment variables will be added to the environment when building the +specified packages. + +**Default value**: `{}` + +**Type**: `dict[str, dict[str, str]]` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv.extra-build-variables] + flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } + ``` +=== "uv.toml" + + ```toml + [tool.uv.extra-build-variables] + flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } + ``` + +--- + ### [`extra-index-url`](#extra-index-url) {: #extra-index-url } Extra URLs of package indexes to use, in addition to `--index-url`. @@ -2700,6 +2749,37 @@ additional packages. This is useful for packages that assume the presence of pac --- +#### [`extra-build-variables`](#pip_extra-build-variables) {: #pip_extra-build-variables } + + +Extra environment variables to set when building certain packages. + +Environment variables will be added to the environment when building the +specified packages. + +**Default value**: `{}` + +**Type**: `dict[str, dict[str, str]]` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv.pip] + [extra-build-variables] + flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } + ``` +=== "uv.toml" + + ```toml + [pip] + [extra-build-variables] + flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } + ``` + +--- + #### [`extra-index-url`](#pip_extra-index-url) {: #pip_extra-index-url } diff --git a/uv.schema.json b/uv.schema.json index d0d4051d3..f3cb65fae 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -236,6 +236,17 @@ } ] }, + "extra-build-variables": { + "description": "Extra environment variables to set when building certain packages.\n\nEnvironment variables will be added to the environment when building the\nspecified packages.", + "anyOf": [ + { + "$ref": "#/definitions/ExtraBuildVariables" + }, + { + "type": "null" + } + ] + }, "extra-index-url": { "description": "Extra URLs of package indexes to use, in addition to `--index-url`.\n\nAccepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)\n(the simple repository API), or a local directory laid out in the same format.\n\nAll indexes provided via this flag take priority over the index specified by\n[`index_url`](#index-url) or [`index`](#index) with `default = true`. When multiple indexes\nare provided, earlier values take priority.\n\nTo control uv's resolution strategy when multiple indexes are present, see\n[`index_strategy`](#index-strategy).\n\n(Deprecated: use `index` instead.)", "type": [ @@ -915,6 +926,16 @@ } ] }, + "ExtraBuildVariables": { + "description": "Extra environment variables to set during builds, on a per-package basis.", + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "ExtraName": { "description": "The normalized name of an extra dependency.\n\nConverts the name to lowercase and collapses runs of `-`, `_`, and `.` down to a single `-`.\nFor example, `---`, `.`, and `__` are all converted to a single `-`.\n\nSee:\n- \n- ", "type": "string" @@ -1368,6 +1389,17 @@ } ] }, + "extra-build-variables": { + "description": "Extra environment variables to set when building certain packages.\n\nEnvironment variables will be added to the environment when building the\nspecified packages.", + "anyOf": [ + { + "$ref": "#/definitions/ExtraBuildVariables" + }, + { + "type": "null" + } + ] + }, "extra-index-url": { "description": "Extra URLs of package indexes to use, in addition to `--index-url`.\n\nAccepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)\n(the simple repository API), or a local directory laid out in the same format.\n\nAll indexes provided via this flag take priority over the index specified by\n[`index_url`](#index-url). When multiple indexes are provided, earlier values take priority.\n\nTo control uv's resolution strategy when multiple indexes are present, see\n[`index_strategy`](#index-strategy).", "type": [