diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index df14e3ae94e7..4d7733e63a32 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -479,18 +479,19 @@ pub struct ToolUv { )] pub constraint_dependencies: Option>>, - /// Constrains to build dependencies using the given requirements files when building source - /// distributions. + /// Constraints to apply when solving build dependencies. /// - /// Including a package as a constraint will _not_ trigger installation of the package on its - /// own; instead, the package must be requested elsewhere in the project's first-party or - /// transitive dependencies. + /// Build constraints are used to restrict the versions of build dependencies that are selected + /// when building a package during resolution or installation. + /// + /// Including a package as a constraint will _not_ trigger installation of the package during + /// a build; instead, the package must be requested elsewhere in the project's build dependency + /// graph. /// /// !!! note /// In `uv lock`, `uv sync`, and `uv run`, uv will only read `build-constraint-dependencies` from /// the `pyproject.toml` at the workspace root, and will ignore any declarations in other /// workspace members or `uv.toml` files. - /// #[cfg_attr( feature = "schemars", schemars( @@ -502,8 +503,8 @@ pub struct ToolUv { default = "[]", value_type = "list[str]", example = r#" - # Ensure that the `setuptools 60.0.0` is used to build any packages with a build dependency - on `setuptools`. + # Ensure that the setuptools v60.0.0 is used whenever a package has a build dependency + # on setuptools. build-constraint-dependencies = ["setuptools==60.0.0"] "# )] diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 0c2b49b47532..ef4811c9ccac 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -1897,10 +1897,8 @@ fn lock_project_with_build_constraints() -> Result<()> { requires-python = ">=3.12" dependencies = ["anyio==3.7.0"] + # This should be ignored because none of the dependencies requires `setuptools` [tool.uv] - constraint-dependencies = ["idna<3.4"] - - # This should be ignored because none of the dependencies requires 'setuptools' build-constraint-dependencies = ["setuptools==1"] "#, )?; @@ -1934,11 +1932,10 @@ fn lock_project_with_build_constraints() -> Result<()> { Prepared 3 packages in [TIME] Installed 3 packages in [TIME] + anyio==3.7.0 - + idna==3.3 + + idna==3.6 + sniffio==1.3.1 "###); - // let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( r#" @@ -1948,6 +1945,7 @@ fn lock_project_with_build_constraints() -> Result<()> { requires-python = ">=3.8" dependencies = ["requests==1.2"] + # This should fail the operation, since `requests` depends on `setuptools` at build time. [tool.uv] build-constraint-dependencies = ["setuptools==1"] "#, @@ -1972,7 +1970,7 @@ fn lock_project_with_build_constraints() -> Result<()> { /// Lock a project with `uv.tool.build-constraint-dependencies` that reference `tool.uv.sources`. #[test] fn lock_project_with_build_constraint_sources() -> Result<()> { - let context = TestContext::new("3.12"); + let context = TestContext::new("3.9"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1980,14 +1978,14 @@ fn lock_project_with_build_constraint_sources() -> Result<()> { [project] name = "project" version = "0.1.0" - requires-python = ">=3.12" - dependencies = ["anyio==3.7.0"] + requires-python = ">=3.8" + dependencies = ["requests==1.2"] [tool.uv] - constraint-dependencies = ["idna<3.4"] + build-constraint-dependencies = ["setuptools==75.8.0"] [tool.uv.sources] - idna = { url = "https://files.pythonhosted.org/packages/d7/77/ff688d1504cdc4db2a938e2b7b9adee5dd52e34efbd2431051efc9984de9/idna-3.2-py3-none-any.whl" } + setuptools = { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl" } "#, )?; @@ -1997,7 +1995,7 @@ fn lock_project_with_build_constraint_sources() -> Result<()> { ----- stdout ----- ----- stderr ----- - Resolved 4 packages in [TIME] + Resolved 2 packages in [TIME] "###); // Re-run with `--locked`. diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index b0f95bd95adf..7e3ac854aa5b 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -7837,7 +7837,7 @@ fn compatible_build_constraint_in_pyproject_toml() -> Result<()> { pyproject_toml.write_str( r#"[tool.uv] build-constraint-dependencies = [ - "setuptools>=40", + "setuptools==40.8.0", ] "#, )?; @@ -7864,7 +7864,7 @@ build-constraint-dependencies = [ fn incompatible_build_constraint_merged_with_pyproject_toml() -> Result<()> { let context = TestContext::new("3.8"); - // incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt + // Incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt. let constraints_txt = context.temp_dir.child("build_constraints.txt"); constraints_txt.write_str("setuptools>=40")?; let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -7892,7 +7892,7 @@ build-constraint-dependencies = [ "### ); - // compatible setuptools version in pyproject.toml, incompatible in build_constraints.txt + // Compatible setuptools version in pyproject.toml, incompatible in build_constraints.txt. let constraints_txt = context.temp_dir.child("build_constraints.txt"); constraints_txt.write_str("setuptools==1")?; let pyproject_toml = context.temp_dir.child("pyproject.toml"); diff --git a/docs/pip/compile.md b/docs/pip/compile.md index 490f191ab344..6451a2ae1024 100644 --- a/docs/pip/compile.md +++ b/docs/pip/compile.md @@ -135,9 +135,24 @@ when building runtime dependencies. Build constraint files are `requirements.txt`-like files that only control the _version_ of a build-time requirement. However, including a package in a build constraints file will _not_ trigger -its installation; instead, constraints apply only when the package is required as a direct or -transitive build-time dependency. Build constraints can be used to add bounds to dependencies that -are not explicitly declared as build-time dependencies of the current project. +its installation at build time; instead, constraints apply only when the package is required as a +direct or transitive build-time dependency. Build constraints can be used to add bounds to +dependencies that are not explicitly declared as build-time dependencies of the current project. + +For example, if a package defines its build dependencies as follows: + +```toml title="pyproject.toml" +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" +``` + +Build constraints could be used to ensure that a specific version of `setuptools` is used for every +package in the workspace: + +```python title="build-constraints.txt" +setuptools==75.0.0 +``` uv will also read `build-constraint-dependencies` from the `pyproject.toml` at the workspace root, and append them to those specified in the build constraints file. diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 6c4f62a25ae2..677eb2dc81ee 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -1,12 +1,14 @@ ## Project metadata ### [`build-constraint-dependencies`](#build-constraint-dependencies) {: #build-constraint-dependencies } -Constrains to build dependencies using the given requirements files when building source -distributions. +Constraints to apply when solving build dependencies. -Including a package as a constraint will _not_ trigger installation of the package on its -own; instead, the package must be requested elsewhere in the project's first-party or -transitive dependencies. +Build constraints are used to restrict the versions of build dependencies that are selected +when building a package during resolution or installation. + +Including a package as a constraint will _not_ trigger installation of the package during +a build; instead, the package must be requested elsewhere in the project's build dependency +graph. !!! note In `uv lock`, `uv sync`, and `uv run`, uv will only read `build-constraint-dependencies` from @@ -21,8 +23,8 @@ transitive dependencies. ```toml title="pyproject.toml" [tool.uv] -# Ensure that the `setuptools 60.0.0` is used to build any packages with a build dependency -on `setuptools`. +# Ensure that the setuptools v60.0.0 is used whenever a package has a build dependency +# on setuptools. build-constraint-dependencies = ["setuptools==60.0.0"] ``` diff --git a/docs/reference/troubleshooting/build-failures.md b/docs/reference/troubleshooting/build-failures.md index dbda2df5ddcb..6cfa34754c72 100644 --- a/docs/reference/troubleshooting/build-failures.md +++ b/docs/reference/troubleshooting/build-failures.md @@ -288,27 +288,27 @@ uv will avoid using an old version of `apache-beam`. Constraints can also be defined for indirect dependencies using `constraints.txt` files or the [`constraint-dependencies`](../settings.md#constraint-dependencies) setting. -### Old Version of a Build Dependency is Used +### Old Version of a build dependency is used If a package fails to build because `uv` selects an incompatible or outdated version of a build-time dependency, you can enforce constraints specifically for build dependencies. The -[`build-constraint-dependencies`](../settings.md#build-constraint-dependencies) setting or a -`build-constraints.txt` file can help ensure that `uv` selects an appropriate version of build -requirements. +[`build-constraint-dependencies`](../settings.md#build-constraint-dependencies) setting (or an +analogous `build-constraints.txt` file) can be used to ensure that `uv` selects an appropriate +version of a given build requirements. For example, the issue described in -[uv#5551](https://github.com/astral-sh/uv/issues/5551#issuecomment-2256055975) can be addressed by -specifying a build constraint that excludes `setuptools` version `72.0.0`, which causes a build -failure for certain packages. This can be done as follows: +[#5551](https://github.com/astral-sh/uv/issues/5551#issuecomment-2256055975) could be addressed by +specifying a build constraint that excludes `setuptools` version `72.0.0`: ```toml title="pyproject.toml" [tool.uv] -# Prevent setuptools version 72.0.0 from being used as a build dependency +# Prevent setuptools version 72.0.0 from being used as a build dependency. build-constraint-dependencies = ["setuptools!=72.0.0"] ``` -This ensures that any package requiring `setuptools` during the build process will avoid using the -problematic version, preventing build failures caused by incompatible build dependencies. +The build constraint will thus ensure that any package requiring `setuptools` during the build +process will avoid using the problematic version, preventing build failures caused by incompatible +build dependencies. ### Package is only needed for an unused platform