Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support build constraints #5639

Merged
merged 7 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions PIP_COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,19 @@ Specifically, uv does not support installing new `.egg-info`- or `.egg-link`-sty
but will respect any such existing distributions during resolution, list them with `uv pip list` and
`uv pip freeze`, and uninstall them with `uv pip uninstall`.

## Build constraints

When constraints are provided via `--constraint` (or `UV_CONSTRAINT`), uv will _not_ apply the
constraints when resolving build dependencies (i.e., to build a source distribution). Instead,
build constraints should be provided via the dedicated `--build-constraint` (or `UV_BUILD_CONSTRAINT`)
setting.

pip, meanwhile, applies constraints to build dependencies when specified via `PIP_CONSTRAINT`, but
not when provided via `--constraint` on the command line.

For example, to ensure that `setuptools 60.0.0` is used to build any packages with a build
dependency on `setuptools`, use `--build-constraint`, rather than `--constraint`.

## `pip compile` defaults

There are a few small but notable differences in the default behaviors of `pip compile` and
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,8 @@ uv accepts the following command-line arguments as environment variables:
uv will require that all dependencies have a hash specified in the requirements file.
- `UV_CONSTRAINT`: Equivalent to the `--constraint` command-line argument. If set, uv will use this
file as the constraints file. Uses space-separated list of files.
- `UV_BUILD_CONSTRAINT`: Equivalent to the `--build-constraint` command-line argument. If set, uv
will use this file as constraints for any source distribution builds. Uses space-separated list of files.
- `UV_OVERRIDE`: Equivalent to the `--override` command-line argument. If set, uv will use this
file as the overrides file. Uses space-separated list of files.
- `UV_LINK_MODE`: Equivalent to the `--link-mode` command-line argument. If set, uv will use this
Expand Down
2 changes: 2 additions & 0 deletions crates/bench/benches/uv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,12 @@ mod resolver {
let python_requirement = PythonRequirement::from_interpreter(interpreter);

let options = OptionsBuilder::new().exclude_newer(exclude_newer).build();
let build_constraints = [];

let build_context = BuildDispatch::new(
client,
&cache,
&build_constraints,
interpreter,
&index_locations,
&flat_index,
Expand Down
27 changes: 27 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,15 @@ pub struct PipCompileArgs {
#[arg(long, env = "UV_OVERRIDE", value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub r#override: Vec<Maybe<PathBuf>>,

/// Constrain build dependencies using the given requirements files when building source
/// distributions.
///
/// Constraints files are `requirements.txt`-like files that only control the _version_ of a
/// requirement that's installed. However, including a package in a constraints file will _not_
/// trigger the installation of that package.
#[arg(long, short, env = "UV_BUILD_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub build_constraint: Vec<Maybe<PathBuf>>,

/// Include optional dependencies from the extra group name; may be provided more than once.
///
/// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
Expand Down Expand Up @@ -845,6 +854,15 @@ pub struct PipSyncArgs {
#[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub constraint: Vec<Maybe<PathBuf>>,

/// Constrain build dependencies using the given requirements files when building source
/// distributions.
///
/// Constraints files are `requirements.txt`-like files that only control the _version_ of a
/// requirement that's installed. However, including a package in a constraints file will _not_
/// trigger the installation of that package.
#[arg(long, short, env = "UV_BUILD_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub build_constraint: Vec<Maybe<PathBuf>>,

#[command(flatten)]
pub installer: InstallerArgs,

Expand Down Expand Up @@ -1118,6 +1136,15 @@ pub struct PipInstallArgs {
#[arg(long, env = "UV_OVERRIDE", value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub r#override: Vec<Maybe<PathBuf>>,

/// Constrain build dependencies using the given requirements files when building source
/// distributions.
///
/// Constraints files are `requirements.txt`-like files that only control the _version_ of a
/// requirement that's installed. However, including a package in a constraints file will _not_
/// trigger the installation of that package.
#[arg(long, short, env = "UV_BUILD_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub build_constraint: Vec<Maybe<PathBuf>>,

/// Include optional dependencies from the extra group name; may be provided more than once.
///
/// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
Expand Down
2 changes: 2 additions & 0 deletions crates/uv-dev/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
&cache,
)?;
let build_options = BuildOptions::default();
let build_constraints = [];

let build_dispatch = BuildDispatch::new(
&client,
&cache,
&build_constraints,
python.interpreter(),
&index_urls,
&flat_index,
Expand Down
8 changes: 6 additions & 2 deletions crates/uv-dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use uv_build::{SourceBuild, SourceBuildContext};
use uv_cache::Cache;
use uv_client::RegistryClient;
use uv_configuration::{
BuildKind, BuildOptions, ConfigSettings, IndexStrategy, Reinstall, SetupPyStrategy,
BuildKind, BuildOptions, ConfigSettings, Constraints, IndexStrategy, Reinstall, SetupPyStrategy,
};
use uv_configuration::{Concurrency, PreviewMode};
use uv_distribution::DistributionDatabase;
Expand All @@ -35,6 +35,7 @@ use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrateg
pub struct BuildDispatch<'a> {
client: &'a RegistryClient,
cache: &'a Cache,
constraints: Constraints,
interpreter: &'a Interpreter,
index_locations: &'a IndexLocations,
index_strategy: IndexStrategy,
Expand All @@ -58,6 +59,7 @@ impl<'a> BuildDispatch<'a> {
pub fn new(
client: &'a RegistryClient,
cache: &'a Cache,
constraints: &'a [Requirement],
interpreter: &'a Interpreter,
index_locations: &'a IndexLocations,
flat_index: &'a FlatIndex,
Expand All @@ -77,6 +79,7 @@ impl<'a> BuildDispatch<'a> {
Self {
client,
cache,
constraints: Constraints::from_requirements(constraints.iter().cloned()),
interpreter,
index_locations,
flat_index,
Expand Down Expand Up @@ -140,8 +143,9 @@ impl<'a> BuildContext for BuildDispatch<'a> {
let python_requirement = PythonRequirement::from_interpreter(self.interpreter);
let markers = self.interpreter.markers();
let tags = self.interpreter.tags()?;

let resolver = Resolver::new(
Manifest::simple(requirements.to_vec()),
Manifest::simple(requirements.to_vec()).with_constraints(self.constraints.clone()),
OptionsBuilder::new()
.exclude_newer(self.exclude_newer)
.index_strategy(self.index_strategy)
Expand Down
6 changes: 6 additions & 0 deletions crates/uv-resolver/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ impl Manifest {
}
}

#[must_use]
pub fn with_constraints(mut self, constraints: Constraints) -> Self {
self.constraints = constraints;
self
}

/// Return an iterator over all requirements, constraints, and overrides, in priority order,
/// such that requirements come first, followed by constraints, followed by overrides.
///
Expand Down
6 changes: 6 additions & 0 deletions crates/uv/src/commands/pip/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub(crate) async fn pip_compile(
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
build_constraints: &[RequirementsSource],
constraints_from_workspace: Vec<Requirement>,
overrides_from_workspace: Vec<Requirement>,
extras: ExtrasSpecification,
Expand Down Expand Up @@ -143,6 +144,10 @@ pub(crate) async fn pip_compile(
)
.collect();

// Read build constraints.
let build_constraints =
operations::read_constraints(build_constraints, &client_builder).await?;

// If all the metadata could be statically resolved, validate that every extra was used. If we
// need to resolve metadata via PEP 517, we don't know which extras are used until much later.
if source_trees.is_empty() {
Expand Down Expand Up @@ -304,6 +309,7 @@ pub(crate) async fn pip_compile(
let build_dispatch = BuildDispatch::new(
&client,
&cache,
&build_constraints,
&interpreter,
&index_locations,
&flat_index,
Expand Down
6 changes: 6 additions & 0 deletions crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub(crate) async fn pip_install(
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
build_constraints: &[RequirementsSource],
constraints_from_workspace: Vec<Requirement>,
overrides_from_workspace: Vec<Requirement>,
extras: &ExtrasSpecification,
Expand Down Expand Up @@ -105,6 +106,10 @@ pub(crate) async fn pip_install(
)
.await?;

// Read build constraints.
let build_constraints =
operations::read_constraints(build_constraints, &client_builder).await?;

let constraints: Vec<Requirement> = constraints
.iter()
.cloned()
Expand Down Expand Up @@ -294,6 +299,7 @@ pub(crate) async fn pip_install(
let build_dispatch = BuildDispatch::new(
&client,
&cache,
&build_constraints,
interpreter,
&index_locations,
&flat_index,
Expand Down
12 changes: 12 additions & 0 deletions crates/uv/src/commands/pip/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ pub(crate) async fn read_requirements(
.await?)
}

/// Resolve a set of constraints.
pub(crate) async fn read_constraints(
constraints: &[RequirementsSource],
client_builder: &BaseClientBuilder<'_>,
) -> Result<Vec<Requirement>, Error> {
Ok(
RequirementsSpecification::from_sources(&[], constraints, &[], client_builder)
.await?
.constraints,
)
}

/// Resolve a set of requirements, similar to running `pip compile`.
pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
requirements: Vec<UnresolvedRequirementSpecification>,
Expand Down
6 changes: 6 additions & 0 deletions crates/uv/src/commands/pip/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::printer::Printer;
pub(crate) async fn pip_sync(
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
build_constraints: &[RequirementsSource],
reinstall: Reinstall,
link_mode: LinkMode,
compile: bool,
Expand Down Expand Up @@ -103,6 +104,10 @@ pub(crate) async fn pip_sync(
)
.await?;

// Read build constraints.
let build_constraints =
operations::read_constraints(build_constraints, &client_builder).await?;

// Validate that the requirements are non-empty.
if !allow_empty_requirements {
let num_requirements = requirements.len() + source_trees.len();
Expand Down Expand Up @@ -240,6 +245,7 @@ pub(crate) async fn pip_sync(
let build_dispatch = BuildDispatch::new(
&client,
&cache,
&build_constraints,
interpreter,
&index_locations,
&flat_index,
Expand Down
4 changes: 4 additions & 0 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,14 @@ pub(crate) async fn add(
FlatIndex::from_entries(entries, Some(&tags), &hasher, &settings.build_options)
};

// TODO: read locked build constraints
let build_constraints = [];

// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
venv.interpreter(),
&settings.index_locations,
&flat_index,
Expand Down
6 changes: 6 additions & 0 deletions crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,13 @@ async fn do_lock(
// Prefill the index with the lockfile metadata.
let index = lock.to_index(workspace.install_path(), upgrade)?;

// TODO: read locked build constraints
let build_constraints = [];
// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
interpreter,
index_locations,
&flat_index,
Expand Down Expand Up @@ -480,10 +483,13 @@ async fn do_lock(
None => {
debug!("Starting clean resolution");

// TODO: read locked build constraints
let build_constraints = [];
// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
interpreter,
index_locations,
&flat_index,
Expand Down
13 changes: 13 additions & 0 deletions crates/uv/src/commands/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,13 @@ pub(crate) async fn resolve_names(
let setup_py = SetupPyStrategy::default();
let flat_index = FlatIndex::default();

// TODO: read locked build constraints
let build_constraints = [];
// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
interpreter,
index_locations,
&flat_index,
Expand Down Expand Up @@ -491,10 +494,13 @@ pub(crate) async fn resolve_environment<'a>(
FlatIndex::from_entries(entries, Some(tags), &hasher, build_options)
};

// TODO: read locked build constraints
let build_constraints = [];
// Create a build dispatch.
let resolve_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
interpreter,
index_locations,
&flat_index,
Expand Down Expand Up @@ -604,10 +610,13 @@ pub(crate) async fn sync_environment(
FlatIndex::from_entries(entries, Some(tags), &hasher, build_options)
};

// TODO: read locked build constraints
let build_constraints = [];
// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
interpreter,
index_locations,
&flat_index,
Expand Down Expand Up @@ -765,10 +774,14 @@ pub(crate) async fn update_environment(
FlatIndex::from_entries(entries, Some(tags), &hasher, build_options)
};

// TODO: read locked build constraints
let build_constraints = [];

// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
interpreter,
index_locations,
&flat_index,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,13 @@ pub(super) async fn do_sync(
FlatIndex::from_entries(entries, Some(tags), &hasher, build_options)
};

// TODO: read locked build constraints
let build_constraints = [];
// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
venv.interpreter(),
index_locations,
&flat_index,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/commands/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,13 @@ async fn venv_impl(
// Do not allow builds
let build_options = BuildOptions::new(NoBinary::None, NoBuild::All);

let build_constraints = [];

// Prep the build context.
let build_dispatch = BuildDispatch::new(
&client,
cache,
&build_constraints,
interpreter,
index_locations,
&flat_index,
Expand Down
Loading
Loading