mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-02 04:48:18 +00:00
Support build constraints (#5639)
## Summary Partially resolves #5561. Haven't added overrides support yet but I can add it tomorrow if the current approach for constraints is ok. ## Test Plan `cargo test` Manually checked trace logs after changing the constraints.
This commit is contained in:
parent
c558d70690
commit
ff9f3dede1
25 changed files with 360 additions and 4 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -541,6 +541,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.
|
||||
|
|
@ -838,6 +847,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,
|
||||
|
||||
|
|
@ -1111,6 +1129,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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -77,6 +79,7 @@ impl<'a> BuildDispatch<'a> {
|
|||
Self {
|
||||
client,
|
||||
cache,
|
||||
constraints: Constraints::from_requirements(constraints.iter().cloned()),
|
||||
interpreter,
|
||||
index_locations,
|
||||
flat_index,
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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() {
|
||||
|
|
@ -304,6 +309,7 @@ pub(crate) async fn pip_compile(
|
|||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
&build_constraints,
|
||||
&interpreter,
|
||||
&index_locations,
|
||||
&flat_index,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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()
|
||||
|
|
@ -294,6 +299,7 @@ pub(crate) async fn pip_install(
|
|||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
&build_constraints,
|
||||
interpreter,
|
||||
&index_locations,
|
||||
&flat_index,
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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();
|
||||
|
|
@ -240,6 +245,7 @@ pub(crate) async fn pip_sync(
|
|||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
&build_constraints,
|
||||
interpreter,
|
||||
&index_locations,
|
||||
&flat_index,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -403,10 +403,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,
|
||||
|
|
@ -479,10 +482,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,
|
||||
|
|
|
|||
|
|
@ -405,10 +405,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,
|
||||
|
|
@ -525,10 +528,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,
|
||||
|
|
@ -638,10 +644,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,
|
||||
|
|
@ -799,10 +808,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,
|
||||
|
|
|
|||
|
|
@ -209,10 +209,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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -236,11 +236,17 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
|||
.into_iter()
|
||||
.map(RequirementsSource::from_overrides_txt)
|
||||
.collect::<Vec<_>>();
|
||||
let build_constraints = args
|
||||
.build_constraint
|
||||
.into_iter()
|
||||
.map(RequirementsSource::from_constraints_txt)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
commands::pip_compile(
|
||||
&requirements,
|
||||
&constraints,
|
||||
&overrides,
|
||||
&build_constraints,
|
||||
args.constraints_from_workspace,
|
||||
args.overrides_from_workspace,
|
||||
args.settings.extras,
|
||||
|
|
@ -316,10 +322,16 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
|||
.into_iter()
|
||||
.map(RequirementsSource::from_constraints_txt)
|
||||
.collect::<Vec<_>>();
|
||||
let build_constraints = args
|
||||
.build_constraint
|
||||
.into_iter()
|
||||
.map(RequirementsSource::from_constraints_txt)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
commands::pip_sync(
|
||||
&requirements,
|
||||
&constraints,
|
||||
&build_constraints,
|
||||
args.settings.reinstall,
|
||||
args.settings.link_mode,
|
||||
args.settings.compile_bytecode,
|
||||
|
|
@ -392,10 +404,17 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
|||
.map(RequirementsSource::from_overrides_txt)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let build_constraints = args
|
||||
.build_constraint
|
||||
.into_iter()
|
||||
.map(RequirementsSource::from_overrides_txt)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
commands::pip_install(
|
||||
&requirements,
|
||||
&constraints,
|
||||
&overrides,
|
||||
&build_constraints,
|
||||
args.constraints_from_workspace,
|
||||
args.overrides_from_workspace,
|
||||
&args.settings.extras,
|
||||
|
|
|
|||
|
|
@ -814,6 +814,7 @@ pub(crate) struct PipCompileSettings {
|
|||
pub(crate) r#override: Vec<PathBuf>,
|
||||
pub(crate) constraints_from_workspace: Vec<Requirement>,
|
||||
pub(crate) overrides_from_workspace: Vec<Requirement>,
|
||||
pub(crate) build_constraint: Vec<PathBuf>,
|
||||
pub(crate) refresh: Refresh,
|
||||
pub(crate) settings: PipSettings,
|
||||
}
|
||||
|
|
@ -828,6 +829,7 @@ impl PipCompileSettings {
|
|||
extra,
|
||||
all_extras,
|
||||
no_all_extras,
|
||||
build_constraint,
|
||||
refresh,
|
||||
no_deps,
|
||||
deps,
|
||||
|
|
@ -908,6 +910,10 @@ impl PipCompileSettings {
|
|||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
.collect(),
|
||||
build_constraint: build_constraint
|
||||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
.collect(),
|
||||
r#override: r#override
|
||||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
|
|
@ -961,6 +967,7 @@ impl PipCompileSettings {
|
|||
pub(crate) struct PipSyncSettings {
|
||||
pub(crate) src_file: Vec<PathBuf>,
|
||||
pub(crate) constraint: Vec<PathBuf>,
|
||||
pub(crate) build_constraint: Vec<PathBuf>,
|
||||
pub(crate) dry_run: bool,
|
||||
pub(crate) refresh: Refresh,
|
||||
pub(crate) settings: PipSettings,
|
||||
|
|
@ -972,6 +979,7 @@ impl PipSyncSettings {
|
|||
let PipSyncArgs {
|
||||
src_file,
|
||||
constraint,
|
||||
build_constraint,
|
||||
installer,
|
||||
refresh,
|
||||
require_hashes,
|
||||
|
|
@ -1009,6 +1017,10 @@ impl PipSyncSettings {
|
|||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
.collect(),
|
||||
build_constraint: build_constraint
|
||||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
.collect(),
|
||||
dry_run,
|
||||
refresh: Refresh::from(refresh),
|
||||
settings: PipSettings::combine(
|
||||
|
|
@ -1052,6 +1064,7 @@ pub(crate) struct PipInstallSettings {
|
|||
pub(crate) editable: Vec<String>,
|
||||
pub(crate) constraint: Vec<PathBuf>,
|
||||
pub(crate) r#override: Vec<PathBuf>,
|
||||
pub(crate) build_constraint: Vec<PathBuf>,
|
||||
pub(crate) dry_run: bool,
|
||||
pub(crate) constraints_from_workspace: Vec<Requirement>,
|
||||
pub(crate) overrides_from_workspace: Vec<Requirement>,
|
||||
|
|
@ -1071,6 +1084,7 @@ impl PipInstallSettings {
|
|||
extra,
|
||||
all_extras,
|
||||
no_all_extras,
|
||||
build_constraint,
|
||||
refresh,
|
||||
no_deps,
|
||||
deps,
|
||||
|
|
@ -1142,6 +1156,10 @@ impl PipInstallSettings {
|
|||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
.collect(),
|
||||
build_constraint: build_constraint
|
||||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
.collect(),
|
||||
dry_run,
|
||||
constraints_from_workspace,
|
||||
overrides_from_workspace,
|
||||
|
|
|
|||
|
|
@ -413,10 +413,10 @@ impl TestContext {
|
|||
}
|
||||
|
||||
/// Create a `uv help` command with options shared across scenarios.
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn help(&self) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
command.arg("help");
|
||||
self.add_shared_args(&mut command);
|
||||
command
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -627,7 +627,7 @@ fn help_unknown_subsubcommand() {
|
|||
fn help_with_global_option() {
|
||||
let context = TestContext::new_with_versions(&[]);
|
||||
|
||||
uv_snapshot!(context.filters(), context.help().arg("--cache-dir").arg("/dev/null"), @r###"
|
||||
uv_snapshot!(context.filters(), context.help().arg("--no-cache"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
|
|||
|
|
@ -11508,3 +11508,63 @@ fn ignore_invalid_constraint() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include a `build_constraints.txt` file with an incompatible constraint.
|
||||
#[test]
|
||||
fn incompatible_build_constraint() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str("requests==1.2")?;
|
||||
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools==1")?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("requirements.txt")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to download and build `requests==1.2.0`
|
||||
Caused by: Failed to build: `requests==1.2.0`
|
||||
Caused by: Failed to install requirements from setup.py build (resolve)
|
||||
Caused by: No solution found when resolving: setuptools>=40.8.0
|
||||
Caused by: Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that the requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include a `build_constraints.txt` file with a compatible constraint.
|
||||
#[test]
|
||||
fn compatible_build_constraint() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str("requests==1.2")?;
|
||||
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=40")?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("requirements.txt")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] requirements.txt --build-constraint build_constraints.txt
|
||||
requests==1.2.0
|
||||
# via -r requirements.txt
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6244,3 +6244,58 @@ fn install_relocatable() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include a `build_constraints.txt` file with an incompatible constraint.
|
||||
#[test]
|
||||
fn incompatible_build_constraint() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools==1")?;
|
||||
|
||||
uv_snapshot!(context.pip_install()
|
||||
.arg("requests==1.2")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to download and build `requests==1.2.0`
|
||||
Caused by: Failed to build: `requests==1.2.0`
|
||||
Caused by: Failed to install requirements from setup.py build (resolve)
|
||||
Caused by: No solution found when resolving: setuptools>=40.8.0
|
||||
Caused by: Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that the requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include a `build_constraints.txt` file with a compatible constraint.
|
||||
#[test]
|
||||
fn compatible_build_constraint() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=40")?;
|
||||
|
||||
uv_snapshot!(context.pip_install()
|
||||
.arg("requests==1.2")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ requests==1.2.0
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5349,3 +5349,62 @@ fn preserve_markers() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include a `build_constraints.txt` file with an incompatible constraint.
|
||||
#[test]
|
||||
fn incompatible_build_constraint() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str("requests==1.2")?;
|
||||
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools==1")?;
|
||||
|
||||
uv_snapshot!(context.pip_sync()
|
||||
.arg("requirements.txt")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to download and build `requests==1.2.0`
|
||||
Caused by: Failed to build: `requests==1.2.0`
|
||||
Caused by: Failed to install requirements from setup.py build (resolve)
|
||||
Caused by: No solution found when resolving: setuptools>=40.8.0
|
||||
Caused by: Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that the requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include a `build_constraints.txt` file with a compatible constraint.
|
||||
#[test]
|
||||
fn compatible_build_constraint() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str("requests==1.2")?;
|
||||
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=40")?;
|
||||
|
||||
uv_snapshot!(context.pip_sync()
|
||||
.arg("requirements.txt")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ requests==1.2.0
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -207,6 +208,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -341,6 +343,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -507,6 +510,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -642,6 +646,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -763,6 +768,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -921,6 +927,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -1079,6 +1086,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -1282,6 +1290,7 @@ fn resolve_find_links() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -1439,6 +1448,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -1566,6 +1576,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -1721,6 +1732,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -1900,6 +1912,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -2017,6 +2030,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -2134,6 +2148,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -2253,6 +2268,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -2397,6 +2413,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
@ -2542,6 +2559,7 @@ fn resolve_both() -> anyhow::Result<()> {
|
|||
override: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraint: [],
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
SystemTime {
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ uv pip compile [OPTIONS] <SRC_FILE>...
|
|||
|
||||
<p>While constraints are <em>additive</em>, in that they’re combined with the requirements of the constituent packages, overrides are <em>absolute</em>, in that they completely replace the requirements of the constituent packages.</p>
|
||||
|
||||
</dd><dt><code>--build-constraint</code>, <code>-b</code> <i>build-constraint</i></dt><dd><p>Constrain build dependencies using the given requirements files when building source distributions.</p>
|
||||
|
||||
<p>Constraints files are <code>requirements.txt</code>-like files that only control the <em>version</em> of a requirement that’s installed. However, including a package in a constraints file will <em>not</em> trigger the installation of that package.</p>
|
||||
|
||||
</dd><dt><code>--extra</code> <i>extra</i></dt><dd><p>Include optional dependencies from the extra group name; may be provided more than once.</p>
|
||||
|
||||
<p>Only applies to <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
|
|
@ -254,6 +258,10 @@ uv pip sync [OPTIONS] <SRC_FILE>...
|
|||
|
||||
<p>This is equivalent to pip’s <code>--constraint</code> option.</p>
|
||||
|
||||
</dd><dt><code>--build-constraint</code>, <code>-b</code> <i>build-constraint</i></dt><dd><p>Constrain build dependencies using the given requirements files when building source distributions.</p>
|
||||
|
||||
<p>Constraints files are <code>requirements.txt</code>-like files that only control the <em>version</em> of a requirement that’s installed. However, including a package in a constraints file will <em>not</em> trigger the installation of that package.</p>
|
||||
|
||||
</dd><dt><code>--index-url</code>, <code>-i</code> <i>index-url</i></dt><dd><p>The URL of the Python package index (by default: <https://pypi.org/simple>).</p>
|
||||
|
||||
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
|
||||
|
|
@ -390,6 +398,10 @@ uv pip install [OPTIONS] <PACKAGE|--requirement <REQUIREMENT>|--editable <EDITAB
|
|||
|
||||
<p>While constraints are <em>additive</em>, in that they’re combined with the requirements of the constituent packages, overrides are <em>absolute</em>, in that they completely replace the requirements of the constituent packages.</p>
|
||||
|
||||
</dd><dt><code>--build-constraint</code>, <code>-b</code> <i>build-constraint</i></dt><dd><p>Constrain build dependencies using the given requirements files when building source distributions.</p>
|
||||
|
||||
<p>Constraints files are <code>requirements.txt</code>-like files that only control the <em>version</em> of a requirement that’s installed. However, including a package in a constraints file will <em>not</em> trigger the installation of that package.</p>
|
||||
|
||||
</dd><dt><code>--extra</code> <i>extra</i></dt><dd><p>Include optional dependencies from the extra group name; may be provided more than once.</p>
|
||||
|
||||
<p>Only applies to <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue