Skip to content
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
18 changes: 18 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4102,6 +4102,15 @@ pub struct ToolRunArgs {
#[arg(long, short, alias = "constraint", env = EnvVars::UV_CONSTRAINT, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub constraints: 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, alias = "build-constraint", env = EnvVars::UV_BUILD_CONSTRAINT, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub build_constraints: Vec<Maybe<PathBuf>>,

/// Override versions using the given requirements files.
///
/// Overrides files are `requirements.txt`-like files that force a specific version of a
Expand Down Expand Up @@ -4212,6 +4221,15 @@ pub struct ToolInstallArgs {
#[arg(long, alias = "override", env = EnvVars::UV_OVERRIDE, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub overrides: 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, alias = "build-constraint", env = EnvVars::UV_BUILD_CONSTRAINT, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub build_constraints: Vec<Maybe<PathBuf>>,

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

Expand Down
35 changes: 35 additions & 0 deletions crates/uv-tool/src/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub struct Tool {
constraints: Vec<Requirement>,
/// The overrides requested by the user during installation.
overrides: Vec<Requirement>,
/// The build constraints requested by the user during installation.
build_constraints: Vec<Requirement>,
/// The Python requested by the user during installation.
python: Option<String>,
/// A mapping of entry point names to their metadata.
Expand All @@ -28,13 +30,16 @@ pub struct Tool {
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "kebab-case")]
struct ToolWire {
#[serde(default)]
requirements: Vec<RequirementWire>,
#[serde(default)]
constraints: Vec<Requirement>,
#[serde(default)]
overrides: Vec<Requirement>,
#[serde(default)]
build_constraint_dependencies: Vec<Requirement>,
python: Option<String>,
entrypoints: Vec<ToolEntrypoint>,
#[serde(default)]
Expand All @@ -61,6 +66,7 @@ impl From<Tool> for ToolWire {
.collect(),
constraints: tool.constraints,
overrides: tool.overrides,
build_constraint_dependencies: tool.build_constraints,
python: tool.python,
entrypoints: tool.entrypoints,
options: tool.options,
Expand All @@ -83,6 +89,7 @@ impl TryFrom<ToolWire> for Tool {
.collect(),
constraints: tool.constraints,
overrides: tool.overrides,
build_constraints: tool.build_constraint_dependencies,
python: tool.python,
entrypoints: tool.entrypoints,
options: tool.options,
Expand Down Expand Up @@ -156,6 +163,7 @@ impl Tool {
requirements: Vec<Requirement>,
constraints: Vec<Requirement>,
overrides: Vec<Requirement>,
build_constraints: Vec<Requirement>,
python: Option<String>,
entrypoints: impl Iterator<Item = ToolEntrypoint>,
options: ToolOptions,
Expand All @@ -166,6 +174,7 @@ impl Tool {
requirements,
constraints,
overrides,
build_constraints,
python,
entrypoints,
options,
Expand Down Expand Up @@ -248,6 +257,28 @@ impl Tool {
});
}

if !self.build_constraints.is_empty() {
table.insert("build-constraint-dependencies", {
let build_constraints = self
.build_constraints
.iter()
.map(|r#build_constraint| {
serde::Serialize::serialize(
&r#build_constraint,
toml_edit::ser::ValueSerializer::new(),
)
})
.collect::<Result<Vec<_>, _>>()?;

let build_constraints = match build_constraints.as_slice() {
[] => Array::new(),
[r#build_constraint] => Array::from_iter([r#build_constraint]),
build_constraints => each_element_on_its_line_array(build_constraints.iter()),
};
value(build_constraints)
});
}

if let Some(ref python) = self.python {
table.insert("python", value(python));
}
Expand Down Expand Up @@ -292,6 +323,10 @@ impl Tool {
&self.overrides
}

pub fn build_constraints(&self) -> &[Requirement] {
&self.build_constraints
}

pub fn python(&self) -> &Option<String> {
&self.python
}
Expand Down
4 changes: 2 additions & 2 deletions crates/uv/src/commands/build_frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,8 @@ async fn build_package(

let build_constraints = Constraints::from_requirements(
build_constraints
.iter()
.map(|constraint| constraint.requirement.clone()),
.into_iter()
.map(|constraint| constraint.requirement),
);

// Initialize the registry client.
Expand Down
3 changes: 1 addition & 2 deletions crates/uv/src/commands/pip/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,7 @@ pub(crate) async fn pip_compile(
let build_constraints: Vec<NameRequirementSpecification> =
operations::read_constraints(build_constraints, &client_builder)
.await?
.iter()
.cloned()
.into_iter()
.chain(
build_constraints_from_workspace
.into_iter()
Expand Down
3 changes: 1 addition & 2 deletions crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,7 @@ pub(crate) async fn pip_install(
let build_constraints: Vec<NameRequirementSpecification> =
operations::read_constraints(build_constraints, &client_builder)
.await?
.iter()
.cloned()
.into_iter()
.chain(
build_constraints_from_workspace
.iter()
Expand Down
4 changes: 3 additions & 1 deletion crates/uv/src/commands/project/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use tracing::debug;

use uv_cache::{Cache, CacheBucket};
use uv_cache_key::{cache_digest, hash_digest};
use uv_configuration::{Concurrency, PreviewMode};
use uv_configuration::{Concurrency, Constraints, PreviewMode};
use uv_distribution_types::{Name, Resolution};
use uv_python::{Interpreter, PythonEnvironment};

Expand All @@ -28,6 +28,7 @@ impl CachedEnvironment {
/// Get or create an [`CachedEnvironment`] based on a given set of requirements.
pub(crate) async fn from_spec(
spec: EnvironmentSpecification<'_>,
build_constraints: Constraints,
interpreter: &Interpreter,
settings: &ResolverInstallerSettings,
network_settings: &NetworkSettings,
Expand Down Expand Up @@ -99,6 +100,7 @@ impl CachedEnvironment {
venv,
&resolution,
Modifications::Exact,
build_constraints,
settings.into(),
network_settings,
state,
Expand Down
4 changes: 2 additions & 2 deletions crates/uv/src/commands/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,7 @@ pub(crate) async fn sync_environment(
venv: PythonEnvironment,
resolution: &Resolution,
modifications: Modifications,
build_constraints: Constraints,
settings: InstallerSettingsRef<'_>,
network_settings: &NetworkSettings,
state: &PlatformState,
Expand Down Expand Up @@ -1891,7 +1892,6 @@ pub(crate) async fn sync_environment(

// TODO(charlie): These are all default values. We should consider whether we want to make them
// optional on the downstream APIs.
let build_constraints = Constraints::default();
let build_hasher = HashStrategy::default();
let dry_run = DryRun::default();
let hasher = HashStrategy::default();
Expand Down Expand Up @@ -1982,6 +1982,7 @@ pub(crate) async fn update_environment(
venv: PythonEnvironment,
spec: RequirementsSpecification,
modifications: Modifications,
build_constraints: Constraints,
settings: &ResolverInstallerSettings,
network_settings: &NetworkSettings,
state: &SharedState,
Expand Down Expand Up @@ -2113,7 +2114,6 @@ pub(crate) async fn update_environment(

// TODO(charlie): These are all default values. We should consider whether we want to make them
// optional on the downstream APIs.
let build_constraints = Constraints::default();
let build_hasher = HashStrategy::default();
let extras = ExtrasSpecification::default();
let groups = BTreeMap::new();
Expand Down
25 changes: 23 additions & 2 deletions crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ use uv_cache::Cache;
use uv_cli::ExternalCommand;
use uv_client::BaseClientBuilder;
use uv_configuration::{
Concurrency, DependencyGroups, DryRun, EditableMode, ExtrasSpecification, InstallOptions,
PreviewMode,
Concurrency, Constraints, DependencyGroups, DryRun, EditableMode, ExtrasSpecification,
InstallOptions, PreviewMode,
};
use uv_distribution_types::Requirement;
use uv_fs::which::is_executable;
use uv_fs::{PythonExt, Simplified};
use uv_installer::{SatisfiesResult, SitePackages};
Expand Down Expand Up @@ -351,10 +352,28 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
.await?
.into_environment()?;

let build_constraints = script
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't planning on doing this, but seemed like an easy fix..

.metadata()
.tool
.as_ref()
.and_then(|tool| {
tool.uv
.as_ref()
.and_then(|uv| uv.build_constraint_dependencies.as_ref())
})
.map(|constraints| {
Constraints::from_requirements(
constraints
.iter()
.map(|constraint| Requirement::from(constraint.clone())),
)
});

match update_environment(
environment,
spec,
modifications,
build_constraints.unwrap_or_default(),
&settings,
&network_settings,
&sync_state,
Expand Down Expand Up @@ -880,6 +899,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
lock.as_ref()
.map(|(lock, install_path)| (lock, install_path.as_ref())),
),
// TODO(bluefact): Respect build constraints for `uv run --with` dependencies.
Constraints::default(),
&base_interpreter,
&settings,
&network_settings,
Expand Down
25 changes: 23 additions & 2 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use uv_auth::UrlAuthPolicies;
use uv_cache::Cache;
use uv_client::{FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
Concurrency, DependencyGroups, DependencyGroupsWithDefaults, DryRun, EditableMode,
Concurrency, Constraints, DependencyGroups, DependencyGroupsWithDefaults, DryRun, EditableMode,
ExtrasSpecification, HashCheckingMode, InstallOptions, PreviewMode,
};
use uv_dispatch::BuildDispatch;
use uv_distribution_types::{
DirectorySourceDist, Dist, Index, Resolution, ResolvedDist, SourceDist,
DirectorySourceDist, Dist, Index, Requirement, Resolution, ResolvedDist, SourceDist,
};
use uv_fs::Simplified;
use uv_installer::SitePackages;
Expand Down Expand Up @@ -274,12 +274,33 @@ pub(crate) async fn sync(
));
}

// Parse the requirements from the script.
let spec = script_specification(Pep723ItemRef::Script(script), &settings.resolver)?
.unwrap_or_default();

// Parse the build constraints from the script.
let build_constraints = script
.metadata
.tool
.as_ref()
.and_then(|tool| {
tool.uv
.as_ref()
.and_then(|uv| uv.build_constraint_dependencies.as_ref())
})
.map(|constraints| {
Constraints::from_requirements(
constraints
.iter()
.map(|constraint| Requirement::from(constraint.clone())),
)
});

match update_environment(
Deref::deref(&environment).clone(),
spec,
modifications,
build_constraints.unwrap_or_default(),
&settings,
&network_settings,
&PlatformState::default(),
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/tool/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ pub(crate) fn install_executables(
requirements: Vec<Requirement>,
constraints: Vec<Requirement>,
overrides: Vec<Requirement>,
build_constraints: Vec<Requirement>,
printer: Printer,
) -> anyhow::Result<ExitStatus> {
let site_packages = SitePackages::from_environment(environment)?;
Expand Down Expand Up @@ -289,6 +290,7 @@ pub(crate) fn install_executables(
requirements,
constraints,
overrides,
build_constraints,
python,
target_entry_points
.into_iter()
Expand Down
Loading
Loading