This commit is contained in:
Aria Desires 2025-07-05 18:58:32 +03:00 committed by GitHub
commit 9b0954ec07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 487 additions and 17 deletions

View file

@ -103,7 +103,7 @@ mod resolver {
ResolverEnvironment, ResolverOutput,
};
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
use uv_workspace::WorkspaceCache;
use uv_workspace::{WorkspaceCache, pyproject::ExtraBuildDependencies};
static MARKERS: LazyLock<MarkerEnvironment> = LazyLock::new(|| {
MarkerEnvironment::try_from(MarkerEnvironmentBuilder {
@ -141,6 +141,7 @@ mod resolver {
universal: bool,
) -> Result<ResolverOutput> {
let build_isolation = BuildIsolation::default();
let extra_build_dependencies = ExtraBuildDependencies::default();
let build_options = BuildOptions::default();
let concurrency = Concurrency::default();
let config_settings = ConfigSettings::default();
@ -185,6 +186,7 @@ mod resolver {
IndexStrategy::default(),
&config_settings,
build_isolation,
&extra_build_dependencies,
LinkMode::default(),
&build_options,
&hashes,

View file

@ -42,6 +42,7 @@ use uv_static::EnvVars;
use uv_types::{AnyErrorBuild, BuildContext, BuildIsolation, BuildStack, SourceBuildTrait};
use uv_warnings::warn_user_once;
use uv_workspace::WorkspaceCache;
use uv_workspace::pyproject::ExtraBuildDependencies;
pub use crate::error::{Error, MissingHeaderCause};
@ -281,6 +282,7 @@ impl SourceBuild {
workspace_cache: &WorkspaceCache,
config_settings: ConfigSettings,
build_isolation: BuildIsolation<'_>,
extra_build_dependencies: &ExtraBuildDependencies,
build_stack: &BuildStack,
build_kind: BuildKind,
mut environment_variables: FxHashMap<OsString, OsString>,
@ -297,7 +299,6 @@ impl SourceBuild {
};
let default_backend: Pep517Backend = DEFAULT_BACKEND.clone();
// Check if we have a PEP 517 build backend.
let (pep517_backend, project) = Self::extract_pep517_backend(
&source_tree,
@ -305,6 +306,7 @@ impl SourceBuild {
fallback_package_name,
locations,
source_strategy,
extra_build_dependencies,
workspace_cache,
&default_backend,
)
@ -506,6 +508,7 @@ impl SourceBuild {
package_name: Option<&PackageName>,
locations: &IndexLocations,
source_strategy: SourceStrategy,
extra_build_dependencies: &ExtraBuildDependencies,
workspace_cache: &WorkspaceCache,
default_backend: &Pep517Backend,
) -> Result<(Pep517Backend, Option<Project>), Box<Error>> {
@ -517,17 +520,24 @@ impl SourceBuild {
let pyproject_toml: PyProjectToml =
PyProjectToml::deserialize(pyproject_toml.into_deserializer())
.map_err(Error::InvalidPyprojectTomlSchema)?;
let name = pyproject_toml
.project
.as_ref()
.map(|project| &project.name)
.or(package_name);
let extra_build_dependencies = name
.as_ref()
.and_then(|name| extra_build_dependencies.get(name).cloned())
.unwrap_or_default();
let backend = if let Some(mut build_system) = pyproject_toml.build_system {
// Apply extra-build-dependencies if there are any
build_system.requires.extend(extra_build_dependencies);
let backend = if let Some(build_system) = pyproject_toml.build_system {
// If necessary, lower the requirements.
let requirements = match source_strategy {
SourceStrategy::Enabled => {
if let Some(name) = pyproject_toml
.project
.as_ref()
.map(|project| &project.name)
.or(package_name)
{
if let Some(name) = name {
let build_requires = uv_pypi_types::BuildRequires {
name: Some(name.clone()),
requires_dist: build_system.requires,
@ -606,7 +616,13 @@ impl SourceBuild {
);
}
}
default_backend.clone()
let mut backend = default_backend.clone();
// Apply extra_build_dependencies
// TODO(Gankra): should Sources/Indexes be applied on this path?
backend
.requirements
.extend(extra_build_dependencies.into_iter().map(Requirement::from));
backend
};
Ok((backend, pyproject_toml.project))
}
@ -617,12 +633,21 @@ impl SourceBuild {
source_tree.to_path_buf(),
)));
}
// If no `pyproject.toml` is present, by default, proceed with a PEP 517 build using
// the default backend, to match `build`. `pip` uses `setup.py` directly in this
// case, but plans to make PEP 517 builds the default in the future.
// See: https://github.com/pypa/pip/issues/9175.
Ok((default_backend.clone(), None))
let mut backend = default_backend.clone();
// Apply extra_build_dependencies
// TODO(Gankra): should Sources/Indexes be applied on this path?
let extra_build_dependencies = package_name
.as_ref()
.and_then(|name| extra_build_dependencies.get(name).cloned())
.unwrap_or_default();
backend
.requirements
.extend(extra_build_dependencies.into_iter().map(Requirement::from));
Ok((backend, None))
}
Err(err) => Err(Box::new(err.into())),
}

View file

@ -323,6 +323,7 @@ pub fn resolver_options(
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
no_build_isolation_package: Some(no_build_isolation_package),
extra_build_dependencies: None,
exclude_newer,
link_mode,
no_build: flag(no_build, build, "build"),
@ -434,6 +435,7 @@ pub fn resolver_installer_options(
} else {
Some(no_build_isolation_package)
},
extra_build_dependencies: None,
exclude_newer,
link_mode,
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),

View file

@ -40,6 +40,7 @@ use uv_types::{
HashStrategy, InFlight,
};
use uv_workspace::WorkspaceCache;
use uv_workspace::pyproject::ExtraBuildDependencies;
#[derive(Debug, Error)]
pub enum BuildDispatchError {
@ -88,6 +89,7 @@ pub struct BuildDispatch<'a> {
shared_state: SharedState,
dependency_metadata: &'a DependencyMetadata,
build_isolation: BuildIsolation<'a>,
extra_build_dependencies: &'a ExtraBuildDependencies,
link_mode: uv_install_wheel::LinkMode,
build_options: &'a BuildOptions,
config_settings: &'a ConfigSettings,
@ -114,6 +116,7 @@ impl<'a> BuildDispatch<'a> {
index_strategy: IndexStrategy,
config_settings: &'a ConfigSettings,
build_isolation: BuildIsolation<'a>,
extra_build_dependencies: &'a ExtraBuildDependencies,
link_mode: uv_install_wheel::LinkMode,
build_options: &'a BuildOptions,
hasher: &'a HashStrategy,
@ -135,6 +138,7 @@ impl<'a> BuildDispatch<'a> {
index_strategy,
config_settings,
build_isolation,
extra_build_dependencies,
link_mode,
build_options,
hasher,
@ -433,6 +437,7 @@ impl BuildContext for BuildDispatch<'_> {
self.workspace_cache(),
self.config_settings.clone(),
self.build_isolation,
self.extra_build_dependencies,
&build_stack,
build_kind,
self.build_extra_env_vars.clone(),

View file

@ -1,5 +1,5 @@
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::{collections::BTreeMap, num::NonZeroUsize};
use url::Url;
@ -120,6 +120,21 @@ impl<T> Combine for Option<Vec<T>> {
}
}
impl<K: Ord, T> Combine for Option<BTreeMap<K, Vec<T>>> {
/// Combine two maps of vecs by combining their vecs
fn combine(self, other: Option<BTreeMap<K, Vec<T>>>) -> Option<BTreeMap<K, Vec<T>>> {
match (self, other) {
(Some(mut a), Some(b)) => {
for (key, value) in b {
a.entry(key).or_default().extend(value);
}
Some(a)
}
(a, b) => a.or(b),
}
}
}
impl Combine for Option<ConfigSettings> {
/// Combine two maps by merging the map in `self` with the map in `other`, if they're both
/// `Some`.

View file

@ -20,7 +20,7 @@ use uv_redacted::DisplaySafeUrl;
use uv_resolver::{AnnotationStyle, ExcludeNewer, ForkStrategy, PrereleaseMode, ResolutionMode};
use uv_static::EnvVars;
use uv_torch::TorchMode;
use uv_workspace::pyproject_mut::AddBoundsKind;
use uv_workspace::{pyproject::ExtraBuildDependencies, pyproject_mut::AddBoundsKind};
/// A `pyproject.toml` with an (optional) `[tool.uv]` section.
#[allow(dead_code)]
@ -371,6 +371,7 @@ pub struct ResolverOptions {
pub no_binary_package: Option<Vec<PackageName>>,
pub no_build_isolation: Option<bool>,
pub no_build_isolation_package: Option<Vec<PackageName>>,
pub extra_build_dependencies: Option<ExtraBuildDependencies>,
pub no_sources: Option<bool>,
}
@ -611,6 +612,20 @@ pub struct ResolverInstallerOptions {
"#
)]
pub no_build_isolation_package: Option<Vec<PackageName>>,
/// Additional build dependencies for dependencies.
///
/// This is intended for enabling more packages to be built with
/// build-isolation, by adding dependencies that they ambiently
/// assume to exist (`setuptools` and `pip` being common).
#[option(
default = "[]",
value_type = "dict",
example = r#"
[extra-build-dependencies]
pytest = ["setuptools"]
"#
)]
pub extra_build_dependencies: Option<ExtraBuildDependencies>,
/// 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.,
@ -1107,6 +1122,20 @@ pub struct PipOptions {
"#
)]
pub no_build_isolation_package: Option<Vec<PackageName>>,
/// Additional build dependencies for dependencies.
///
/// This is intended for enabling more packages to be built with
/// build-isolation, by adding dependencies that they ambiently
/// assume to exist (`setuptools` and `pip` being common).
#[option(
default = "[]",
value_type = "dict",
example = r#"
[extra-build-dependencies]
pytest = ["setuptools"]
"#
)]
pub extra_build_dependencies: Option<ExtraBuildDependencies>,
/// Validate the Python environment, to detect packages with missing dependencies and other
/// issues.
#[option(
@ -1661,6 +1690,7 @@ impl From<ResolverInstallerOptions> for ResolverOptions {
no_binary_package: value.no_binary_package,
no_build_isolation: value.no_build_isolation,
no_build_isolation_package: value.no_build_isolation_package,
extra_build_dependencies: value.extra_build_dependencies,
no_sources: value.no_sources,
}
}
@ -1716,6 +1746,7 @@ pub struct ToolOptions {
pub config_settings: Option<ConfigSettings>,
pub no_build_isolation: Option<bool>,
pub no_build_isolation_package: Option<Vec<PackageName>>,
pub extra_build_dependencies: Option<ExtraBuildDependencies>,
pub exclude_newer: Option<ExcludeNewer>,
pub link_mode: Option<LinkMode>,
pub compile_bytecode: Option<bool>,
@ -1743,6 +1774,7 @@ impl From<ResolverInstallerOptions> for ToolOptions {
config_settings: value.config_settings,
no_build_isolation: value.no_build_isolation,
no_build_isolation_package: value.no_build_isolation_package,
extra_build_dependencies: value.extra_build_dependencies,
exclude_newer: value.exclude_newer,
link_mode: value.link_mode,
compile_bytecode: value.compile_bytecode,
@ -1772,6 +1804,7 @@ impl From<ToolOptions> for ResolverInstallerOptions {
config_settings: value.config_settings,
no_build_isolation: value.no_build_isolation,
no_build_isolation_package: value.no_build_isolation_package,
extra_build_dependencies: value.extra_build_dependencies,
exclude_newer: value.exclude_newer,
link_mode: value.link_mode,
compile_bytecode: value.compile_bytecode,
@ -1824,6 +1857,7 @@ pub struct OptionsWire {
config_settings: Option<ConfigSettings>,
no_build_isolation: Option<bool>,
no_build_isolation_package: Option<Vec<PackageName>>,
extra_build_dependencies: Option<ExtraBuildDependencies>,
exclude_newer: Option<ExcludeNewer>,
link_mode: Option<LinkMode>,
compile_bytecode: Option<bool>,
@ -1940,6 +1974,7 @@ impl From<OptionsWire> for Options {
sources,
default_groups,
dependency_groups,
extra_build_dependencies,
dev_dependencies,
managed,
package,
@ -1979,6 +2014,7 @@ impl From<OptionsWire> for Options {
config_settings,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
exclude_newer,
link_mode,
compile_bytecode,

View file

@ -374,6 +374,21 @@ pub struct ToolUv {
)]
pub dependency_groups: Option<ToolUvDependencyGroups>,
/// Additional build dependencies for dependencies.
///
/// This is intended for enabling more packages to be built with
/// build-isolation, by adding dependencies that they ambiently
/// assume to exist (`setuptools` and `pip` being common).
#[option(
default = "[]",
value_type = "dict",
example = r#"
[tool.uv.extra-build-dependencies]
pytest = ["pip"]
"#
)]
pub extra_build_dependencies: Option<ToolUvExtraBuildDependencies>,
/// The project's development dependencies.
///
/// Development dependencies will be installed by default in `uv run` and `uv sync`, but will
@ -745,6 +760,70 @@ pub struct DependencyGroupSettings {
pub requires_python: Option<VersionSpecifiers>,
}
pub type ExtraBuildDependencies =
BTreeMap<PackageName, Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>;
#[derive(Default, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(Serialize))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ToolUvExtraBuildDependencies(ExtraBuildDependencies);
impl ToolUvExtraBuildDependencies {
/// Returns the underlying `BTreeMap` of group names to settings.
pub fn inner(&self) -> &ExtraBuildDependencies {
&self.0
}
/// Convert the [`ToolUvExtraBuildDependencies`] into its inner `BTreeMap`.
#[must_use]
pub fn into_inner(self) -> ExtraBuildDependencies {
self.0
}
}
/// Ensure that all keys in the TOML table are unique.
impl<'de> serde::de::Deserialize<'de> for ToolUvExtraBuildDependencies {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct DependenciesVisitor;
impl<'de> serde::de::Visitor<'de> for DependenciesVisitor {
type Value = ToolUvExtraBuildDependencies;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a map with unique keys")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: serde::de::MapAccess<'de>,
{
let mut groups = BTreeMap::new();
while let Some((key, value)) = access
.next_entry::<PackageName, Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>()?
{
match groups.entry(key) {
std::collections::btree_map::Entry::Occupied(entry) => {
return Err(serde::de::Error::custom(format!(
"duplicate extra-build-dependencies for `{}`",
entry.key()
)));
}
std::collections::btree_map::Entry::Vacant(entry) => {
entry.insert(value);
}
}
}
Ok(ToolUvExtraBuildDependencies(groups))
}
}
deserializer.deserialize_map(DependenciesVisitor)
}
}
#[derive(Deserialize, OptionsMetadata, Default, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(Serialize))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]

View file

@ -1868,6 +1868,7 @@ mod tests {
"package": null,
"default-groups": null,
"dependency-groups": null,
"extra-build-dependencies": null,
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
@ -1964,6 +1965,7 @@ mod tests {
"package": null,
"default-groups": null,
"dependency-groups": null,
"extra-build-dependencies": null,
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
@ -2175,6 +2177,7 @@ mod tests {
"package": null,
"default-groups": null,
"dependency-groups": null,
"extra-build-dependencies": null,
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
@ -2283,6 +2286,7 @@ mod tests {
"package": null,
"default-groups": null,
"dependency-groups": null,
"extra-build-dependencies": null,
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
@ -2404,6 +2408,7 @@ mod tests {
"package": null,
"default-groups": null,
"dependency-groups": null,
"extra-build-dependencies": null,
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
@ -2499,6 +2504,7 @@ mod tests {
"package": null,
"default-groups": null,
"dependency-groups": null,
"extra-build-dependencies": null,
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,

View file

@ -38,6 +38,7 @@ use uv_requirements::RequirementsSource;
use uv_resolver::{ExcludeNewer, FlatIndex};
use uv_settings::PythonInstallMirrors;
use uv_types::{AnyErrorBuild, BuildContext, BuildIsolation, BuildStack, HashStrategy};
use uv_workspace::pyproject::ExtraBuildDependencies;
use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache, WorkspaceError};
use crate::commands::ExitStatus;
@ -199,6 +200,7 @@ async fn build_impl(
config_setting,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
exclude_newer,
link_mode,
upgrade: _,
@ -344,6 +346,7 @@ async fn build_impl(
build_constraints,
*no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
*index_strategy,
*keyring_provider,
*exclude_newer,
@ -421,6 +424,7 @@ async fn build_package(
build_constraints: &[RequirementsSource],
no_build_isolation: bool,
no_build_isolation_package: &[PackageName],
extra_build_dependencies: &ExtraBuildDependencies,
index_strategy: IndexStrategy,
keyring_provider: KeyringProviderType,
exclude_newer: Option<ExcludeNewer>,
@ -568,6 +572,7 @@ async fn build_package(
index_strategy,
config_setting,
build_isolation,
extra_build_dependencies,
link_mode,
build_options,
&hasher,

View file

@ -44,6 +44,7 @@ use uv_torch::{TorchMode, TorchStrategy};
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
use uv_warnings::warn_user;
use uv_workspace::WorkspaceCache;
use uv_workspace::pyproject::ExtraBuildDependencies;
use crate::commands::pip::loggers::DefaultResolveLogger;
use crate::commands::pip::{operations, resolution_environment};
@ -92,6 +93,7 @@ pub(crate) async fn pip_compile(
config_settings: ConfigSettings,
no_build_isolation: bool,
no_build_isolation_package: Vec<PackageName>,
extra_build_dependencies: &ExtraBuildDependencies,
build_options: BuildOptions,
mut python_version: Option<PythonVersion>,
python_platform: Option<TargetTriple>,
@ -477,6 +479,7 @@ pub(crate) async fn pip_compile(
index_strategy,
&config_settings,
build_isolation,
extra_build_dependencies,
link_mode,
&build_options,
&build_hashes,

View file

@ -38,6 +38,7 @@ use uv_torch::{TorchMode, TorchStrategy};
use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user;
use uv_workspace::WorkspaceCache;
use uv_workspace::pyproject::ExtraBuildDependencies;
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
use crate::commands::pip::operations::Modifications;
@ -77,6 +78,7 @@ pub(crate) async fn pip_install(
config_settings: &ConfigSettings,
no_build_isolation: bool,
no_build_isolation_package: Vec<PackageName>,
extra_build_dependencies: &ExtraBuildDependencies,
build_options: BuildOptions,
modifications: Modifications,
python_version: Option<PythonVersion>,
@ -422,6 +424,7 @@ pub(crate) async fn pip_install(
index_strategy,
config_settings,
build_isolation,
extra_build_dependencies,
link_mode,
&build_options,
&build_hasher,

View file

@ -32,6 +32,7 @@ use uv_torch::{TorchMode, TorchStrategy};
use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user;
use uv_workspace::WorkspaceCache;
use uv_workspace::pyproject::ExtraBuildDependencies;
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
use crate::commands::pip::operations::Modifications;
@ -62,6 +63,7 @@ pub(crate) async fn pip_sync(
config_settings: &ConfigSettings,
no_build_isolation: bool,
no_build_isolation_package: Vec<PackageName>,
extra_build_dependencies: &ExtraBuildDependencies,
build_options: BuildOptions,
python_version: Option<PythonVersion>,
python_platform: Option<TargetTriple>,
@ -355,6 +357,7 @@ pub(crate) async fn pip_sync(
index_strategy,
config_settings,
build_isolation,
extra_build_dependencies,
link_mode,
&build_options,
&build_hasher,

View file

@ -434,6 +434,7 @@ pub(crate) async fn add(
settings.resolver.index_strategy,
&settings.resolver.config_setting,
build_isolation,
&settings.resolver.extra_build_dependencies,
settings.resolver.link_mode,
&settings.resolver.build_options,
&build_hasher,

View file

@ -426,6 +426,7 @@ async fn do_lock(
config_setting,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
exclude_newer,
link_mode,
upgrade,
@ -665,6 +666,7 @@ async fn do_lock(
*index_strategy,
config_setting,
build_isolation,
extra_build_dependencies,
*link_mode,
build_options,
&build_hasher,

View file

@ -1646,6 +1646,7 @@ pub(crate) async fn resolve_names(
link_mode,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
prerelease: _,
resolution: _,
sources,
@ -1704,6 +1705,7 @@ pub(crate) async fn resolve_names(
*index_strategy,
config_setting,
build_isolation,
extra_build_dependencies,
*link_mode,
build_options,
&build_hasher,
@ -1795,6 +1797,7 @@ pub(crate) async fn resolve_environment(
config_setting,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
exclude_newer,
link_mode,
upgrade: _,
@ -1909,6 +1912,7 @@ pub(crate) async fn resolve_environment(
*index_strategy,
config_setting,
build_isolation,
extra_build_dependencies,
*link_mode,
build_options,
&build_hasher,
@ -1975,6 +1979,7 @@ pub(crate) async fn sync_environment(
config_setting,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
exclude_newer,
link_mode,
compile_bytecode,
@ -2044,6 +2049,7 @@ pub(crate) async fn sync_environment(
index_strategy,
config_setting,
build_isolation,
extra_build_dependencies,
link_mode,
build_options,
&build_hasher,
@ -2137,6 +2143,7 @@ pub(crate) async fn update_environment(
link_mode,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
prerelease,
resolution,
sources,
@ -2264,6 +2271,7 @@ pub(crate) async fn update_environment(
*index_strategy,
config_setting,
build_isolation,
extra_build_dependencies,
*link_mode,
build_options,
&build_hasher,

View file

@ -610,6 +610,7 @@ pub(super) async fn do_sync(
config_setting,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
exclude_newer,
link_mode,
compile_bytecode,
@ -744,6 +745,7 @@ pub(super) async fn do_sync(
index_strategy,
config_setting,
build_isolation,
extra_build_dependencies,
link_mode,
build_options,
&build_hasher,

View file

@ -202,6 +202,7 @@ pub(crate) async fn tree(
config_setting: _,
no_build_isolation: _,
no_build_isolation_package: _,
extra_build_dependencies: _,
exclude_newer: _,
link_mode: _,
upgrade: _,

View file

@ -30,6 +30,7 @@ use uv_settings::PythonInstallMirrors;
use uv_shell::{Shell, shlex_posix, shlex_windows};
use uv_types::{AnyErrorBuild, BuildContext, BuildIsolation, BuildStack, HashStrategy};
use uv_warnings::warn_user;
use uv_workspace::pyproject::ExtraBuildDependencies;
use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceCache, WorkspaceError};
use crate::commands::ExitStatus;
@ -343,7 +344,7 @@ async fn venv_impl(
// Do not allow builds
let build_options = BuildOptions::new(NoBinary::None, NoBuild::All);
let extra_build_dependencies = ExtraBuildDependencies::default();
// Prep the build context.
let build_dispatch = BuildDispatch::new(
&client,
@ -357,6 +358,7 @@ async fn venv_impl(
index_strategy,
&config_settings,
BuildIsolation::Isolated,
&extra_build_dependencies,
link_mode,
&build_options,
&build_hasher,

View file

@ -520,6 +520,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.settings.config_setting,
args.settings.no_build_isolation,
args.settings.no_build_isolation_package,
&args.settings.extra_build_dependencies,
args.settings.build_options,
args.settings.python_version,
args.settings.python_platform,
@ -590,6 +591,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&args.settings.config_setting,
args.settings.no_build_isolation,
args.settings.no_build_isolation_package,
&args.settings.extra_build_dependencies,
args.settings.build_options,
args.settings.python_version,
args.settings.python_platform,
@ -741,6 +743,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&args.settings.config_setting,
args.settings.no_build_isolation,
args.settings.no_build_isolation_package,
&args.settings.extra_build_dependencies,
args.settings.build_options,
args.modifications,
args.settings.python_version,

View file

@ -44,7 +44,7 @@ use uv_settings::{
use uv_static::EnvVars;
use uv_torch::TorchMode;
use uv_warnings::warn_user_once;
use uv_workspace::pyproject::DependencyType;
use uv_workspace::pyproject::{DependencyType, ExtraBuildDependencies};
use uv_workspace::pyproject_mut::AddBoundsKind;
use crate::commands::ToolRunCommand;
@ -2664,6 +2664,7 @@ pub(crate) struct InstallerSettingsRef<'a> {
pub(crate) config_setting: &'a ConfigSettings,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: &'a [PackageName],
pub(crate) extra_build_dependencies: &'a ExtraBuildDependencies,
pub(crate) exclude_newer: Option<ExcludeNewer>,
pub(crate) link_mode: LinkMode,
pub(crate) compile_bytecode: bool,
@ -2689,6 +2690,7 @@ pub(crate) struct ResolverSettings {
pub(crate) link_mode: LinkMode,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: Vec<PackageName>,
pub(crate) extra_build_dependencies: ExtraBuildDependencies,
pub(crate) prerelease: PrereleaseMode,
pub(crate) resolution: ResolutionMode,
pub(crate) sources: SourceStrategy,
@ -2740,6 +2742,7 @@ impl From<ResolverOptions> for ResolverSettings {
config_setting: value.config_settings.unwrap_or_default(),
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(),
exclude_newer: value.exclude_newer,
link_mode: value.link_mode.unwrap_or_default(),
sources: SourceStrategy::from_args(value.no_sources.unwrap_or_default()),
@ -2828,6 +2831,7 @@ impl From<ResolverInstallerOptions> for ResolverInstallerSettings {
link_mode: value.link_mode.unwrap_or_default(),
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(),
prerelease: value.prerelease.unwrap_or_default(),
resolution: value.resolution.unwrap_or_default(),
sources: SourceStrategy::from_args(value.no_sources.unwrap_or_default()),
@ -2870,6 +2874,7 @@ pub(crate) struct PipSettings {
pub(crate) torch_backend: Option<TorchMode>,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: Vec<PackageName>,
pub(crate) extra_build_dependencies: ExtraBuildDependencies,
pub(crate) build_options: BuildOptions,
pub(crate) allow_empty_requirements: bool,
pub(crate) strict: bool,
@ -2936,6 +2941,7 @@ impl PipSettings {
only_binary,
no_build_isolation,
no_build_isolation_package,
extra_build_dependencies,
strict,
extra,
all_extras,
@ -2992,6 +2998,7 @@ impl PipSettings {
config_settings: top_level_config_settings,
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,
exclude_newer: top_level_exclude_newer,
link_mode: top_level_link_mode,
compile_bytecode: top_level_compile_bytecode,
@ -3025,6 +3032,8 @@ impl PipSettings {
let no_build_isolation = no_build_isolation.combine(top_level_no_build_isolation);
let no_build_isolation_package =
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 exclude_newer = exclude_newer.combine(top_level_exclude_newer);
let link_mode = link_mode.combine(top_level_link_mode);
let compile_bytecode = compile_bytecode.combine(top_level_compile_bytecode);
@ -3120,6 +3129,10 @@ impl PipSettings {
.no_build_isolation_package
.combine(no_build_isolation_package)
.unwrap_or_default(),
extra_build_dependencies: args
.extra_build_dependencies
.combine(extra_build_dependencies)
.unwrap_or_default(),
config_setting: args
.config_settings
.combine(config_settings)
@ -3219,6 +3232,7 @@ impl<'a> From<&'a ResolverInstallerSettings> for InstallerSettingsRef<'a> {
config_setting: &settings.resolver.config_setting,
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,
exclude_newer: settings.resolver.exclude_newer,
link_mode: settings.resolver.link_mode,
compile_bytecode: settings.compile_bytecode,

View file

@ -179,6 +179,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -360,6 +361,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -542,6 +544,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -756,6 +759,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -906,6 +910,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1099,6 +1104,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1339,6 +1345,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1588,6 +1595,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1794,6 +1802,7 @@ fn resolve_find_links() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1966,6 +1975,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2196,6 +2206,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2409,6 +2420,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2580,6 +2592,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2735,6 +2748,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2890,6 +2904,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -3047,6 +3062,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -3195,6 +3211,7 @@ fn resolve_tool() -> anyhow::Result<()> {
config_settings: None,
no_build_isolation: None,
no_build_isolation_package: None,
extra_build_dependencies: None,
exclude_newer: None,
link_mode: Some(
Clone,
@ -3234,6 +3251,7 @@ fn resolve_tool() -> anyhow::Result<()> {
link_mode: Clone,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
prerelease: IfNecessaryOrExplicit,
resolution: LowestDirect,
sources: Enabled,
@ -3388,6 +3406,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -3604,6 +3623,7 @@ fn resolve_both() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -3910,6 +3930,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -3987,7 +4008,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`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `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`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `exclude-newer`, `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`
"
);
@ -4159,6 +4180,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -4317,6 +4339,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -4494,6 +4517,7 @@ fn allow_insecure_host() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -4730,6 +4754,7 @@ fn index_priority() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -4945,6 +4970,7 @@ fn index_priority() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -5166,6 +5192,7 @@ fn index_priority() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -5382,6 +5409,7 @@ fn index_priority() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -5605,6 +5633,7 @@ fn index_priority() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -5821,6 +5850,7 @@ fn index_priority() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -5983,6 +6013,7 @@ fn verify_hashes() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -6131,6 +6162,7 @@ fn verify_hashes() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -6277,6 +6309,7 @@ fn verify_hashes() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -6425,6 +6458,7 @@ fn verify_hashes() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -6571,6 +6605,7 @@ fn verify_hashes() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -6718,6 +6753,7 @@ fn verify_hashes() -> anyhow::Result<()> {
torch_backend: None,
no_build_isolation: false,
no_build_isolation_package: [],
extra_build_dependencies: {},
build_options: BuildOptions {
no_binary: None,
no_build: None,

View file

@ -1295,6 +1295,106 @@ fn sync_build_isolation_extra() -> Result<()> {
Ok(())
}
/// Use dedicated extra groups to install dependencies for `--no-build-isolation-package`.
#[test]
fn sync_build_isolation_fail() -> Result<()> {
let context = TestContext::new("3.12").with_filtered_counts();
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["fasttext==0.9.2"]
[build-system]
requires = ["setuptools >= 40.9.0"]
build-backend = "setuptools.build_meta"
"#,
)?;
let filters = std::iter::once((r"exit code: 1", "exit status: 1"))
.chain(context.filters())
.collect::<Vec<_>>();
// Running `uv sync` should fail due to missing build-dependencies
uv_snapshot!(&filters, context.sync(), @r#"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× Failed to build `fasttext==0.9.2`
The build backend returned an error
Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1)
[stderr]
[CACHE_DIR]/builds-v0/[TMP]/python: No module named pip
Traceback (most recent call last):
File "<string>", line 38, in __init__
ModuleNotFoundError: No module named 'pybind11'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<string>", line 14, in <module>
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 325, in get_requires_for_build_wheel
return self._get_build_requires(config_settings, requirements=['wheel'])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 295, in _get_build_requires
self.run_setup()
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 487, in run_setup
super().run_setup(setup_script=setup_script)
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 311, in run_setup
exec(code, locals())
File "<string>", line 72, in <module>
File "<string>", line 41, in __init__
RuntimeError: pybind11 install failed.
hint: This usually indicates a problem with the package or the build environment.
help: `fasttext` (v0.9.2) was included because `myproject` (v0.1.0) depends on `fasttext==0.9.2`
"#);
// Adding extra-build-dependencies should solve the issue
pyproject_toml.write_str(
r#"
[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["fasttext==0.9.2"]
[build-system]
requires = ["setuptools >= 40.9.0"]
build-backend = "setuptools.build_meta"
[tool.uv.extra-build-dependencies]
fasttext = ["setuptools", "wheel", "pybind11"]
"#,
)?;
uv_snapshot!(&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]
+ fasttext==0.9.2
+ myproject==0.1.0 (from file://[TEMP_DIR]/)
+ numpy==1.26.4
+ pybind11==2.11.1
+ setuptools==69.2.0
");
assert!(context.temp_dir.child("uv.lock").exists());
Ok(())
}
/// Avoid using incompatible versions for build dependencies that are also part of the resolved
/// environment. This is a very subtle issue, but: when locking, we don't enforce platform
/// compatibility. So, if we reuse the resolver state to install, and the install itself has to

View file

@ -202,6 +202,28 @@ environments = ["sys_platform == 'darwin'"]
---
### [`extra-build-dependencies`](#extra-build-dependencies) {: #extra-build-dependencies }
Additional build dependencies for dependencies.
This is intended for enabling more packages to be built with
build-isolation, by adding dependencies that they ambiently
assume to exist (`setuptools` and `pip` being common).
**Default value**: `[]`
**Type**: `dict`
**Example usage**:
```toml title="pyproject.toml"
[tool.uv.extra-build-dependencies]
pytest = ["pip"]
```
---
### [`index`](#index) {: #index }
The indexes to use when resolving dependencies.
@ -1070,6 +1092,36 @@ behave consistently across timezones.
---
### [`extra-build-dependencies`](#extra-build-dependencies) {: #extra-build-dependencies }
Additional build dependencies for dependencies.
This is intended for enabling more packages to be built with
build-isolation, by adding dependencies that they ambiently
assume to exist (`setuptools` and `pip` being common).
**Default value**: `[]`
**Type**: `dict`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv]
[extra-build-dependencies]
pytest = ["setuptools"]
```
=== "uv.toml"
```toml
[extra-build-dependencies]
pytest = ["setuptools"]
```
---
### [`extra-index-url`](#extra-index-url) {: #extra-index-url }
Extra URLs of package indexes to use, in addition to `--index-url`.
@ -2504,6 +2556,38 @@ Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
---
#### [`extra-build-dependencies`](#pip_extra-build-dependencies) {: #pip_extra-build-dependencies }
<span id="extra-build-dependencies"></span>
Additional build dependencies for dependencies.
This is intended for enabling more packages to be built with
build-isolation, by adding dependencies that they ambiently
assume to exist (`setuptools` and `pip` being common).
**Default value**: `[]`
**Type**: `dict`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv.pip]
[extra-build-dependencies]
pytest = ["setuptools"]
```
=== "uv.toml"
```toml
[pip]
[extra-build-dependencies]
pytest = ["setuptools"]
```
---
#### [`extra-index-url`](#pip_extra-index-url) {: #pip_extra-index-url }
<span id="extra-index-url"></span>

33
uv.schema.json generated
View file

@ -203,6 +203,17 @@
}
]
},
"extra-build-dependencies": {
"description": "Additional build dependencies for dependencies.\n\nThis is intended for enabling more packages to be built with build-isolation, by adding dependencies that they ambiently assume to exist (`setuptools` and `pip` being common).",
"anyOf": [
{
"$ref": "#/definitions/ToolUvExtraBuildDependencies"
},
{
"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": [
@ -1206,6 +1217,19 @@
"$ref": "#/definitions/ExtraName"
}
},
"extra-build-dependencies": {
"description": "Additional build dependencies for dependencies.\n\nThis is intended for enabling more packages to be built with build-isolation, by adding dependencies that they ambiently assume to exist (`setuptools` and `pip` being common).",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/definitions/Requirement"
}
}
},
"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": [
@ -2234,6 +2258,15 @@
"$ref": "#/definitions/DependencyGroupSettings"
}
},
"ToolUvExtraBuildDependencies": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/definitions/Requirement"
}
}
},
"ToolUvSources": {
"type": "object",
"additionalProperties": {