From f07308823e877690a00141441b07fd0f7dfe9805 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 24 Jun 2024 15:25:01 +0300 Subject: [PATCH] Add `--emit-build-options` flag to `uv pip compile` interface (#4463) ## Summary Closes https://github.com/astral-sh/uv/issues/4420. --- PIP_COMPATIBILITY.md | 4 ++ crates/requirements-txt/src/lib.rs | 2 +- crates/uv-cli/src/lib.rs | 7 +++ crates/uv-configuration/src/build_options.rs | 14 +++++- crates/uv-dispatch/src/lib.rs | 2 +- .../src/distribution_database.rs | 6 ++- crates/uv-distribution/src/source/mod.rs | 6 ++- crates/uv-settings/src/settings.rs | 1 + crates/uv/src/commands/pip/compile.rs | 44 ++++++++++++++++--- crates/uv/src/main.rs | 1 + crates/uv/src/settings.rs | 9 ++++ crates/uv/tests/pip_compile.rs | 43 ++++++++++++++++++ crates/uv/tests/show_settings.rs | 16 +++++++ uv.schema.json | 6 +++ 14 files changed, 148 insertions(+), 13 deletions(-) diff --git a/PIP_COMPATIBILITY.md b/PIP_COMPATIBILITY.md index 7a52f51f3e59..0eab394047f9 100644 --- a/PIP_COMPATIBILITY.md +++ b/PIP_COMPATIBILITY.md @@ -385,3 +385,7 @@ By default, uv does not write any index URLs to the output file, while `pip-comp `--index-url` or `--extra-index-url` that does not match the default (PyPI). To include index URLs in the output file, pass the `--emit-index-url` flag to `uv pip compile`. Unlike `pip-compile`, uv will include all index URLs when `--emit-index-url` is passed, including the default index URL. + +By default, uv does not write any `--no-build` or `--only-binary` options to the output file, unlike +`pip-compile`. To include these options in the output file, pass the `--emit-build-options` flag to +`uv pip compile`. diff --git a/crates/requirements-txt/src/lib.rs b/crates/requirements-txt/src/lib.rs index 1aa9e3fa270c..46b9dec1b0a9 100644 --- a/crates/requirements-txt/src/lib.rs +++ b/crates/requirements-txt/src/lib.rs @@ -89,7 +89,7 @@ enum RequirementsTxtStatement { NoIndex, /// `--no-binary` NoBinary(NoBinary), - /// `only-binary` + /// `--only-binary` OnlyBinary(NoBuild), } diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 9ceedc88c518..9918e66f8b6a 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -577,6 +577,13 @@ pub struct PipCompileArgs { #[arg(long, overrides_with("emit_find_links"), hide = true)] pub no_emit_find_links: bool, + /// Include `--no-binary` and `--only-binary` entries in the generated output file. + #[arg(long, overrides_with("no_emit_build_options"))] + pub emit_build_options: bool, + + #[arg(long, overrides_with("emit_build_options"), hide = true)] + pub no_emit_build_options: bool, + /// Whether to emit a marker string indicating when it is known that the /// resulting set of pinned dependencies is valid. /// diff --git a/crates/uv-configuration/src/build_options.rs b/crates/uv-configuration/src/build_options.rs index 0079440da807..67049981934d 100644 --- a/crates/uv-configuration/src/build_options.rs +++ b/crates/uv-configuration/src/build_options.rs @@ -78,14 +78,14 @@ impl BuildOptions { } } - pub fn no_build(&self, package_name: Option<&PackageName>) -> bool { + pub fn no_build_requirement(&self, package_name: Option<&PackageName>) -> bool { match package_name { Some(name) => self.no_build_package(name), None => self.no_build_all(), } } - pub fn no_binary(&self, package_name: Option<&PackageName>) -> bool { + pub fn no_binary_requirement(&self, package_name: Option<&PackageName>) -> bool { match package_name { Some(name) => self.no_binary_package(name), None => self.no_binary_all(), @@ -99,6 +99,16 @@ impl BuildOptions { pub fn no_binary_all(&self) -> bool { matches!(self.no_binary, NoBinary::All) } + + /// Return the [`NoBuild`] strategy to use. + pub fn no_build(&self) -> &NoBuild { + &self.no_build + } + + /// Return the [`NoBinary`] strategy to use. + pub fn no_binary(&self) -> &NoBinary { + &self.no_binary + } } #[derive(Debug, Default, Clone, PartialEq, Eq)] diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index ea48ab7b68d5..412902008e8e 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -308,7 +308,7 @@ impl<'a> BuildContext for BuildDispatch<'a> { // unless all builds are disabled. if self .build_options - .no_build(dist.map(distribution_types::Name::name)) + .no_build_requirement(dist.map(distribution_types::Name::name)) // We always allow editable builds && !matches!(build_kind, BuildKind::Editable) { diff --git a/crates/uv-distribution/src/distribution_database.rs b/crates/uv-distribution/src/distribution_database.rs index 0a4c184c174a..9b097a3848f3 100644 --- a/crates/uv-distribution/src/distribution_database.rs +++ b/crates/uv-distribution/src/distribution_database.rs @@ -413,7 +413,11 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> { hashes: HashPolicy<'_>, ) -> Result { // Optimization: Skip source dist download when we must not build them anyway. - if self.build_context.build_options().no_build(source.name()) { + if self + .build_context + .build_options() + .no_build_requirement(source.name()) + { if source.is_editable() { debug!("Allowing build for editable source distribution: {source}"); } else { diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 21adfec150da..783873781940 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -1390,7 +1390,11 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { debug!("Building: {source}"); // Guard against build of source distributions when disabled. - if self.build_context.build_options().no_build(source.name()) { + if self + .build_context + .build_options() + .no_build_requirement(source.name()) + { if source.is_editable() { debug!("Allowing build for editable source distribution: {source}"); } else { diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 21c8ecb70776..b467230cf258 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -180,6 +180,7 @@ pub struct PipOptions { pub no_emit_package: Option>, pub emit_index_url: Option, pub emit_find_links: Option, + pub emit_build_options: Option, pub emit_marker_expression: Option, pub emit_index_annotation: Option, pub annotation_style: Option, diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 0fb98d6f18e7..5a758241f9a6 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -21,7 +21,7 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, ExtrasSpecification, IndexStrategy, - Overrides, PreviewMode, SetupPyStrategy, Upgrade, + NoBinary, NoBuild, Overrides, PreviewMode, SetupPyStrategy, Upgrade, }; use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::BuildDispatch; @@ -71,6 +71,7 @@ pub(crate) async fn pip_compile( custom_compile_command: Option, include_index_url: bool, include_find_links: bool, + include_build_options: bool, include_marker_expression: bool, include_index_annotation: bool, index_locations: IndexLocations, @@ -539,18 +540,17 @@ pub(crate) async fn pip_compile( writeln!(writer, "{}", format!("# {relevant_markers}").green())?; } - // Write the index locations to the output channel. - let mut wrote_index = false; + let mut wrote_preamble = false; // If necessary, include the `--index-url` and `--extra-index-url` locations. if include_index_url { if let Some(index) = index_locations.index() { writeln!(writer, "--index-url {}", index.verbatim())?; - wrote_index = true; + wrote_preamble = true; } for extra_index in index_locations.extra_index() { writeln!(writer, "--extra-index-url {}", extra_index.verbatim())?; - wrote_index = true; + wrote_preamble = true; } } @@ -558,12 +558,42 @@ pub(crate) async fn pip_compile( if include_find_links { for flat_index in index_locations.flat_index() { writeln!(writer, "--find-links {flat_index}")?; - wrote_index = true; + wrote_preamble = true; + } + } + + // If necessary, include the `--no-binary` and `--only-binary` options. + if include_build_options { + match build_options.no_binary() { + NoBinary::None => {} + NoBinary::All => { + writeln!(writer, "--no-binary :all:")?; + wrote_preamble = true; + } + NoBinary::Packages(packages) => { + for package in packages { + writeln!(writer, "--no-binary {package}")?; + wrote_preamble = true; + } + } + } + match build_options.no_build() { + NoBuild::None => {} + NoBuild::All => { + writeln!(writer, "--only-binary :all:")?; + wrote_preamble = true; + } + NoBuild::Packages(packages) => { + for package in packages { + writeln!(writer, "--only-binary {package}")?; + wrote_preamble = true; + } + } } } // If we wrote an index, add a newline to separate it from the requirements - if wrote_index { + if wrote_preamble { writeln!(writer)?; } diff --git a/crates/uv/src/main.rs b/crates/uv/src/main.rs index 5683d07a2cb0..f358926e788b 100644 --- a/crates/uv/src/main.rs +++ b/crates/uv/src/main.rs @@ -268,6 +268,7 @@ async fn run() -> Result { args.settings.custom_compile_command, args.settings.emit_index_url, args.settings.emit_find_links, + args.settings.emit_build_options, args.settings.emit_marker_expression, args.settings.emit_index_annotation, args.settings.index_locations, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 0b866546ba38..e8a56eb09182 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -528,6 +528,8 @@ impl PipCompileSettings { no_emit_index_url, emit_find_links, no_emit_find_links, + emit_build_options, + no_emit_build_options, emit_marker_expression, no_emit_marker_expression, emit_index_annotation, @@ -581,6 +583,7 @@ impl PipCompileSettings { no_emit_package, emit_index_url: flag(emit_index_url, no_emit_index_url), emit_find_links: flag(emit_find_links, no_emit_find_links), + emit_build_options: flag(emit_build_options, no_emit_build_options), emit_marker_expression: flag(emit_marker_expression, no_emit_marker_expression), emit_index_annotation: flag(emit_index_annotation, no_emit_index_annotation), annotation_style, @@ -1406,6 +1409,7 @@ pub(crate) struct PipSettings { pub(crate) no_emit_package: Vec, pub(crate) emit_index_url: bool, pub(crate) emit_find_links: bool, + pub(crate) emit_build_options: bool, pub(crate) emit_marker_expression: bool, pub(crate) emit_index_annotation: bool, pub(crate) annotation_style: AnnotationStyle, @@ -1460,6 +1464,7 @@ impl PipSettings { no_emit_package, emit_index_url, emit_find_links, + emit_build_options, emit_marker_expression, emit_index_annotation, annotation_style, @@ -1595,6 +1600,10 @@ impl PipSettings { .emit_find_links .combine(emit_find_links) .unwrap_or_default(), + emit_build_options: args + .emit_build_options + .combine(emit_build_options) + .unwrap_or_default(), emit_marker_expression: args .emit_marker_expression .combine(emit_marker_expression) diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index be2c91dad404..b35d921eacbb 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -5116,6 +5116,49 @@ fn emit_find_links() -> Result<()> { Ok(()) } +/// Emit the `--no-binary` and `--only-binary` options. +#[test] +fn emit_build_options() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("black==23.10.1")?; + + uv_snapshot!(context.compile() + .arg("requirements.in") + .arg("--emit-build-options") + .arg("--only-binary") + .arg("black") + .arg("--no-binary") + .arg(":all:"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in --emit-build-options --only-binary black --no-binary :all: + --no-binary :all: + --only-binary black + + black==23.10.1 + # via -r requirements.in + click==8.1.7 + # via black + mypy-extensions==1.0.0 + # via black + packaging==24.0 + # via black + pathspec==0.12.1 + # via black + platformdirs==4.2.0 + # via black + + ----- stderr ----- + Resolved 6 packages in [TIME] + "### + ); + + Ok(()) +} + /// Respect the `--no-index` flag in a `requirements.txt` file. #[test] fn no_index_requirements_txt() -> Result<()> { diff --git a/crates/uv/tests/show_settings.rs b/crates/uv/tests/show_settings.rs index 251c3080ed55..ed41c7d0072f 100644 --- a/crates/uv/tests/show_settings.rs +++ b/crates/uv/tests/show_settings.rs @@ -147,6 +147,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -274,6 +275,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -402,6 +404,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -562,6 +565,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -668,6 +672,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -806,6 +811,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -981,6 +987,7 @@ fn resolve_index_url() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -1155,6 +1162,7 @@ fn resolve_index_url() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -1302,6 +1310,7 @@ fn resolve_find_links() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -1430,6 +1439,7 @@ fn resolve_top_level() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -1596,6 +1606,7 @@ fn resolve_top_level() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -1745,6 +1756,7 @@ fn resolve_top_level() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -1873,6 +1885,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -1984,6 +1997,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -2095,6 +2109,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, @@ -2208,6 +2223,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { no_emit_package: [], emit_index_url: false, emit_find_links: false, + emit_build_options: false, emit_marker_expression: false, emit_index_annotation: false, annotation_style: Split, diff --git a/uv.schema.json b/uv.schema.json index 02e2f2f9257b..f21591817df6 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -493,6 +493,12 @@ "null" ] }, + "emit-build-options": { + "type": [ + "boolean", + "null" + ] + }, "emit-find-links": { "type": [ "boolean",