diff --git a/README.md b/README.md index 1318a7b8b975..0dc5e0c4a251 100644 --- a/README.md +++ b/README.md @@ -508,6 +508,8 @@ uv accepts the following command-line arguments as environment variables: file as the constraints file. Uses space-separated list of files. - `UV_LINK_MODE`: Equivalent to the `--link-mode` command-line argument. If set, uv will use this as a link mode. +- `UV_NO_BUILD_ISOLATION`: Equivalent to the `--no-build-isolation` command-line argument. If set, + uv will skip isolation when building source distributions. In each case, the corresponding command-line argument takes precedence over an environment variable. diff --git a/crates/uv/src/cli.rs b/crates/uv/src/cli.rs index a202829f38c9..f4deaddeb33f 100644 --- a/crates/uv/src/cli.rs +++ b/crates/uv/src/cli.rs @@ -500,7 +500,12 @@ pub(crate) struct PipCompileArgs { /// Disable isolation when building source distributions. /// /// Assumes that build dependencies specified by PEP 518 are already installed. - #[arg(long, overrides_with("build_isolation"))] + #[arg( + long, + env = "UV_NO_BUILD_ISOLATION", + value_parser = clap::builder::BoolishValueParser::new(), + overrides_with("build_isolation") + )] pub(crate) no_build_isolation: bool, #[arg(long, overrides_with("no_build_isolation"), hide = true)] @@ -801,7 +806,13 @@ pub(crate) struct PipSyncArgs { /// Disable isolation when building source distributions. /// /// Assumes that build dependencies specified by PEP 518 are already installed. - #[arg(long, overrides_with("build_isolation"))] + + #[arg( + long, + env = "UV_NO_BUILD_ISOLATION", + value_parser = clap::builder::BoolishValueParser::new(), + overrides_with("build_isolation") + )] pub(crate) no_build_isolation: bool, #[arg(long, overrides_with("no_build_isolation"), hide = true)] @@ -1176,7 +1187,12 @@ pub(crate) struct PipInstallArgs { /// Disable isolation when building source distributions. /// /// Assumes that build dependencies specified by PEP 518 are already installed. - #[arg(long, overrides_with("build_isolation"))] + #[arg( + long, + env = "UV_NO_BUILD_ISOLATION", + value_parser = clap::builder::BoolishValueParser::new(), + overrides_with("build_isolation") + )] pub(crate) no_build_isolation: bool, #[arg(long, overrides_with("no_build_isolation"), hide = true)] diff --git a/crates/uv/tests/pip_install.rs b/crates/uv/tests/pip_install.rs index 7edba6f97b24..43e1f75f8c3b 100644 --- a/crates/uv/tests/pip_install.rs +++ b/crates/uv/tests/pip_install.rs @@ -2676,6 +2676,77 @@ fn no_build_isolation() -> Result<()> { Ok(()) } +/// Ensure that `UV_NO_BUILD_ISOLATION` env var does the same as the `--no-build-isolation` flag +#[test] +fn respect_no_build_isolation_env_var() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz")?; + + // We expect the build to fail, because `setuptools` is not installed. + let filters = std::iter::once((r"exit code: 1", "exit status: 1")) + .chain(context.filters()) + .collect::>(); + uv_snapshot!(filters, context.install() + .arg("-r") + .arg("requirements.in") + .env("UV_NO_BUILD_ISOLATION", "yes"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Failed to download and build: `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` + Caused by: Failed to build: `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` + Caused by: Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` with exit status: 1 + --- stdout: + + --- stderr: + Traceback (most recent call last): + File "", line 8, in + ModuleNotFoundError: No module named 'setuptools' + --- + "### + ); + + // Install `setuptools` and `wheel`. + uv_snapshot!(context.install() + .arg("setuptools") + .arg("wheel"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 2 packages in [TIME] + Downloaded 2 packages in [TIME] + Installed 2 packages in [TIME] + + setuptools==69.2.0 + + wheel==0.43.0 + "###); + + // We expect the build to succeed, since `setuptools` is now installed. + uv_snapshot!(context.install() + .arg("-r") + .arg("requirements.in") + .env("UV_NO_BUILD_ISOLATION", "yes"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 3 packages in [TIME] + Downloaded 3 packages in [TIME] + Installed 3 packages in [TIME] + + anyio==0.0.0 (from https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz) + + idna==3.6 + + sniffio==1.3.1 + "### + ); + + Ok(()) +} + /// This tests that `uv` can read UTF-16LE encoded requirements.txt files. /// /// Ref: