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

Add tool.uv.build-constraint-dependencies to pyproject.toml #11585

Merged
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
1 change: 1 addition & 0 deletions crates/uv-scripts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ pub struct ToolUv {
pub top_level: ResolverInstallerOptions,
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub build_constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub sources: Option<BTreeMap<PackageName, Sources>>,
}

Expand Down
6 changes: 6 additions & 0 deletions crates/uv-settings/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ pub struct Options {
#[cfg_attr(feature = "schemars", schemars(skip))]
pub constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,

#[cfg_attr(feature = "schemars", schemars(skip))]
pub build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,

#[cfg_attr(feature = "schemars", schemars(skip))]
pub environments: Option<SupportedEnvironments>,

Expand Down Expand Up @@ -1792,6 +1795,7 @@ pub struct OptionsWire {
// They're respected in both `pyproject.toml` and `uv.toml` files.
override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
environments: Option<SupportedEnvironments>,
required_environments: Option<SupportedEnvironments>,

Expand Down Expand Up @@ -1858,6 +1862,7 @@ impl From<OptionsWire> for Options {
cache_keys,
override_dependencies,
constraint_dependencies,
build_constraint_dependencies,
environments,
required_environments,
conflicts,
Expand Down Expand Up @@ -1922,6 +1927,7 @@ impl From<OptionsWire> for Options {
cache_keys,
override_dependencies,
constraint_dependencies,
build_constraint_dependencies,
environments,
required_environments,
install_mirrors: PythonInstallMirrors::resolve(
Expand Down
31 changes: 31 additions & 0 deletions crates/uv-workspace/src/pyproject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,37 @@ pub struct ToolUv {
)]
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,

/// Constraints to apply when solving build dependencies.
///
/// Build constraints are used to restrict the versions of build dependencies that are selected
/// when building a package during resolution or installation.
///
/// Including a package as a constraint will _not_ trigger installation of the package during
/// a build; instead, the package must be requested elsewhere in the project's build dependency
/// graph.
///
/// !!! note
/// In `uv lock`, `uv sync`, and `uv run`, uv will only read `build-constraint-dependencies` from
/// the `pyproject.toml` at the workspace root, and will ignore any declarations in other
/// workspace members or `uv.toml` files.
#[cfg_attr(
feature = "schemars",
schemars(
with = "Option<Vec<String>>",
description = "PEP 508-style requirements, e.g., `ruff==0.5.0`, or `ruff @ https://...`."
)
)]
#[option(
default = "[]",
value_type = "list[str]",
example = r#"
# Ensure that the setuptools v60.0.0 is used whenever a package has a build dependency
# on setuptools.
build-constraint-dependencies = ["setuptools==60.0.0"]
"#
)]
pub build_constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,

/// A list of supported environments against which to resolve dependencies.
///
/// By default, uv will resolve for all possible environments during a `uv lock` operation.
Expand Down
20 changes: 20 additions & 0 deletions crates/uv-workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,20 @@ impl Workspace {
constraints.clone()
}

/// Returns the set of build constraints for the workspace.
pub fn build_constraints(&self) -> Vec<uv_pep508::Requirement<VerbatimParsedUrl>> {
let Some(build_constraints) = self
.pyproject_toml
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.build_constraint_dependencies.as_ref())
else {
return vec![];
};
build_constraints.clone()
}

/// The path to the workspace root, the directory containing the top level `pyproject.toml` with
/// the `uv.tool.workspace`, or the `pyproject.toml` in an implicit single workspace project.
pub fn install_path(&self) -> &PathBuf {
Expand Down Expand Up @@ -1725,6 +1739,7 @@ mod tests {
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
"required-environments": null,
"conflicts": null
Expand Down Expand Up @@ -1818,6 +1833,7 @@ mod tests {
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
"required-environments": null,
"conflicts": null
Expand Down Expand Up @@ -2025,6 +2041,7 @@ mod tests {
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
"required-environments": null,
"conflicts": null
Expand Down Expand Up @@ -2130,6 +2147,7 @@ mod tests {
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
"required-environments": null,
"conflicts": null
Expand Down Expand Up @@ -2248,6 +2266,7 @@ mod tests {
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
"required-environments": null,
"conflicts": null
Expand Down Expand Up @@ -2340,6 +2359,7 @@ mod tests {
"dev-dependencies": null,
"override-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
"required-environments": null,
"conflicts": null
Expand Down
14 changes: 12 additions & 2 deletions crates/uv/src/commands/pip/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub(crate) async fn pip_compile(
build_constraints: &[RequirementsSource],
constraints_from_workspace: Vec<Requirement>,
overrides_from_workspace: Vec<Requirement>,
build_constraints_from_workspace: Vec<Requirement>,
environments: SupportedEnvironments,
extras: ExtrasSpecification,
groups: DevGroupsSpecification,
Expand Down Expand Up @@ -184,8 +185,17 @@ pub(crate) async fn pip_compile(
.collect();

// Read build constraints.
let build_constraints =
operations::read_constraints(build_constraints, &client_builder).await?;
let build_constraints: Vec<NameRequirementSpecification> =
operations::read_constraints(build_constraints, &client_builder)
.await?
.iter()
.cloned()
.chain(
build_constraints_from_workspace
.into_iter()
.map(NameRequirementSpecification::from),
)
.collect();

// 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.
Expand Down
19 changes: 15 additions & 4 deletions crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub(crate) async fn pip_install(
build_constraints: &[RequirementsSource],
constraints_from_workspace: Vec<Requirement>,
overrides_from_workspace: Vec<Requirement>,
build_constraints_from_workspace: Vec<Requirement>,
extras: &ExtrasSpecification,
groups: &DevGroupsSpecification,
resolution_mode: ResolutionMode,
Expand Down Expand Up @@ -123,10 +124,6 @@ pub(crate) async fn pip_install(
)
.await?;

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

let constraints: Vec<NameRequirementSpecification> = constraints
.iter()
.cloned()
Expand All @@ -147,6 +144,20 @@ pub(crate) async fn pip_install(
)
.collect();

// Read build constraints.
let build_constraints: Vec<NameRequirementSpecification> =
operations::read_constraints(build_constraints, &client_builder)
.await?
.iter()
.cloned()
.chain(
build_constraints_from_workspace
.iter()
.cloned()
.map(NameRequirementSpecification::from),
)
.collect();

// Detect the current Python interpreter.
let environment = if target.is_some() || prefix.is_some() {
let installation = PythonInstallation::find(
Expand Down
5 changes: 4 additions & 1 deletion crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,15 @@ async fn do_lock(
let requirements = target.requirements();
let overrides = target.overrides();
let constraints = target.constraints();
let build_constraints = target.build_constraints();
let dependency_groups = target.dependency_groups()?;
let source_trees = vec![];

// If necessary, lower the overrides and constraints.
let requirements = target.lower(requirements, index_locations, sources)?;
let overrides = target.lower(overrides, index_locations, sources)?;
let constraints = target.lower(constraints, index_locations, sources)?;
let build_constraints = target.lower(build_constraints, index_locations, sources)?;
let dependency_groups = dependency_groups
.into_iter()
.map(|(name, requirements)| {
Expand Down Expand Up @@ -556,9 +558,10 @@ async fn do_lock(
.build();
let hasher = HashStrategy::Generate(HashGeneration::Url);

let build_constraints = Constraints::from_requirements(build_constraints.iter().cloned());

// 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 = DevGroupsSpecification::default();
Expand Down
17 changes: 17 additions & 0 deletions crates/uv/src/commands/project/lock_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,23 @@ impl<'lock> LockTarget<'lock> {
}
}

/// Returns the set of build constraints for the [`LockTarget`].
pub(crate) fn build_constraints(self) -> Vec<uv_pep508::Requirement<VerbatimParsedUrl>> {
match self {
Self::Workspace(workspace) => workspace.build_constraints(),
Self::Script(script) => script
.metadata
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.build_constraint_dependencies.as_ref())
.into_iter()
.flatten()
.cloned()
.collect(),
}
}

/// Return the dependency groups that are attached to the target directly, as opposed to being
/// attached to any members within the target.
pub(crate) fn dependency_groups(
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&build_constraints,
args.constraints_from_workspace,
args.overrides_from_workspace,
args.build_constraints_from_workspace,
args.environments,
args.settings.extras,
args.settings.groups,
Expand Down Expand Up @@ -561,6 +562,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&build_constraints,
args.constraints_from_workspace,
args.overrides_from_workspace,
args.build_constraints_from_workspace,
&args.settings.extras,
&args.settings.groups,
args.settings.resolution,
Expand Down
32 changes: 32 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,7 @@ pub(crate) struct PipCompileSettings {
pub(crate) build_constraints: Vec<PathBuf>,
pub(crate) constraints_from_workspace: Vec<Requirement>,
pub(crate) overrides_from_workspace: Vec<Requirement>,
pub(crate) build_constraints_from_workspace: Vec<Requirement>,
pub(crate) environments: SupportedEnvironments,
pub(crate) refresh: Refresh,
pub(crate) settings: PipSettings,
Expand Down Expand Up @@ -1626,6 +1627,20 @@ impl PipCompileSettings {
Vec::new()
};

let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
configuration
.build_constraint_dependencies
.clone()
.unwrap_or_default()
.into_iter()
.map(|requirement| {
Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
})
.collect()
} else {
Vec::new()
};

let environments = if let Some(configuration) = &filesystem {
configuration.environments.clone().unwrap_or_default()
} else {
Expand All @@ -1648,6 +1663,7 @@ impl PipCompileSettings {
.collect(),
constraints_from_workspace,
overrides_from_workspace,
build_constraints_from_workspace,
environments,
refresh: Refresh::from(refresh),
settings: PipSettings::combine(
Expand Down Expand Up @@ -1783,6 +1799,7 @@ pub(crate) struct PipInstallSettings {
pub(crate) dry_run: DryRun,
pub(crate) constraints_from_workspace: Vec<Requirement>,
pub(crate) overrides_from_workspace: Vec<Requirement>,
pub(crate) build_constraints_from_workspace: Vec<Requirement>,
pub(crate) modifications: Modifications,
pub(crate) refresh: Refresh,
pub(crate) settings: PipSettings,
Expand Down Expand Up @@ -1858,6 +1875,20 @@ impl PipInstallSettings {
Vec::new()
};

let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
configuration
.build_constraint_dependencies
.clone()
.unwrap_or_default()
.into_iter()
.map(|requirement| {
Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
})
.collect()
} else {
Vec::new()
};

Self {
package,
requirements,
Expand All @@ -1877,6 +1908,7 @@ impl PipInstallSettings {
dry_run: DryRun::from_args(dry_run),
constraints_from_workspace,
overrides_from_workspace,
build_constraints_from_workspace,
modifications: if flag(exact, inexact).unwrap_or(false) {
Modifications::Exact
} else {
Expand Down
Loading
Loading