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
42 changes: 41 additions & 1 deletion crates/uv-distribution-types/src/requirement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,23 @@ impl Requirement {
self.source.is_editable()
}

/// Convert the requirement to a [`Requirement`] relative to the given path.
/// Convert to a [`Requirement`] with a relative path based on the given root.
pub fn relative_to(self, path: &Path) -> Result<Self, io::Error> {
Ok(Self {
source: self.source.relative_to(path)?,
..self
})
}

/// Convert to a [`Requirement`] with an absolute path based on the given root.
#[must_use]
pub fn to_absolute(self, path: &Path) -> Self {
Self {
source: self.source.to_absolute(path),
..self
}
}

/// Return the hashes of the requirement, as specified in the URL fragment.
pub fn hashes(&self) -> Option<Hashes> {
let RequirementSource::Url { ref url, .. } = self.source else {
Expand Down Expand Up @@ -593,6 +602,37 @@ impl RequirementSource {
}),
}
}

/// Convert the source to a [`RequirementSource`] with an absolute path based on the given root.
#[must_use]
pub fn to_absolute(self, root: &Path) -> Self {
match self {
RequirementSource::Registry { .. }
| RequirementSource::Url { .. }
| RequirementSource::Git { .. } => self,
RequirementSource::Path {
install_path,
ext,
url,
} => Self::Path {
install_path: uv_fs::normalize_path_buf(root.join(install_path)).into_boxed_path(),
ext,
url,
},
RequirementSource::Directory {
install_path,
editable,
r#virtual,
url,
..
} => Self::Directory {
install_path: uv_fs::normalize_path_buf(root.join(install_path)).into_boxed_path(),
editable,
r#virtual,
url,
},
}
}
}

impl Display for RequirementSource {
Expand Down
74 changes: 68 additions & 6 deletions crates/uv-resolver/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use tracing::debug;
use url::Url;

use uv_cache_key::RepositoryUrl;
use uv_configuration::BuildOptions;
use uv_configuration::{BuildOptions, Constraints};
use uv_distribution::{DistributionDatabase, FlatRequiresDist};
use uv_distribution_filename::{
BuildTag, DistExtension, ExtensionError, SourceDistExtension, WheelFilename,
Expand Down Expand Up @@ -674,6 +674,17 @@ impl Lock {
&self.manifest.dependency_groups
}

/// Returns the build constraints that were used to generate this lock.
pub fn build_constraints(&self, root: &Path) -> Constraints {
Constraints::from_requirements(
self.manifest
.build_constraints
.iter()
.cloned()
.map(|requirement| requirement.to_absolute(root)),
)
}

/// Return the workspace root used to generate this lock.
pub fn root(&self) -> Option<&Package> {
self.packages.iter().find(|package| {
Expand Down Expand Up @@ -931,6 +942,26 @@ impl Lock {
manifest_table.insert("overrides", value(overrides));
}

if !self.manifest.build_constraints.is_empty() {
let build_constraints = self
.manifest
.build_constraints
.iter()
.map(|requirement| {
serde::Serialize::serialize(
&requirement,
toml_edit::ser::ValueSerializer::new(),
)
})
.collect::<Result<Vec<_>, _>>()?;
let build_constraints = match build_constraints.as_slice() {
[] => Array::new(),
[requirement] => Array::from_iter([requirement]),
build_constraints => each_element_on_its_line_array(build_constraints.iter()),
};
manifest_table.insert("build-constraints", value(build_constraints));
}

if !self.manifest.dependency_groups.is_empty() {
let mut dependency_groups = Table::new();
for (extra, requirements) in &self.manifest.dependency_groups {
Expand Down Expand Up @@ -1190,6 +1221,7 @@ impl Lock {
requirements: &[Requirement],
constraints: &[Requirement],
overrides: &[Requirement],
build_constraints: &[Requirement],
dependency_groups: &BTreeMap<GroupName, Vec<Requirement>>,
dependency_metadata: &DependencyMetadata,
indexes: Option<&IndexLocations>,
Expand Down Expand Up @@ -1281,6 +1313,27 @@ impl Lock {
}
}

// Validate that the lockfile was generated with the same build constraints.
{
let expected: BTreeSet<_> = build_constraints
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
let actual: BTreeSet<_> = self
.manifest
.build_constraints
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
if expected != actual {
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to invalidate the lockfile, would the resolution change with different build dependencies? (And if so, do we need to invalidate the cache of previous build attempts?)

Copy link
Member Author

Choose a reason for hiding this comment

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

It's only because we write the build constraints to the lockfile, and I think it's nice to ensure that we encode them.

return Ok(SatisfiesResult::MismatchedBuildConstraints(
expected, actual,
));
}
}

// Validate that the lockfile was generated with the dependency groups.
{
let expected: BTreeMap<GroupName, BTreeSet<Requirement>> = dependency_groups
Expand Down Expand Up @@ -1687,6 +1740,8 @@ pub enum SatisfiesResult<'lock> {
MismatchedConstraints(BTreeSet<Requirement>, BTreeSet<Requirement>),
/// The lockfile uses a different set of overrides.
MismatchedOverrides(BTreeSet<Requirement>, BTreeSet<Requirement>),
/// The lockfile uses a different set of build constraints.
MismatchedBuildConstraints(BTreeSet<Requirement>, BTreeSet<Requirement>),
/// The lockfile uses a different set of dependency groups.
MismatchedDependencyGroups(
BTreeMap<GroupName, BTreeSet<Requirement>>,
Expand Down Expand Up @@ -1767,6 +1822,9 @@ pub struct ResolverManifest {
/// The overrides provided to the resolver.
#[serde(default)]
overrides: BTreeSet<Requirement>,
/// The build constraints provided to the resolver.
#[serde(default)]
build_constraints: BTreeSet<Requirement>,
/// The static metadata provided to the resolver.
#[serde(default)]
dependency_metadata: BTreeSet<StaticMetadata>,
Expand All @@ -1780,6 +1838,7 @@ impl ResolverManifest {
requirements: impl IntoIterator<Item = Requirement>,
constraints: impl IntoIterator<Item = Requirement>,
overrides: impl IntoIterator<Item = Requirement>,
build_constraints: impl IntoIterator<Item = Requirement>,
dependency_groups: impl IntoIterator<Item = (GroupName, Vec<Requirement>)>,
dependency_metadata: impl IntoIterator<Item = StaticMetadata>,
) -> Self {
Expand All @@ -1788,6 +1847,7 @@ impl ResolverManifest {
requirements: requirements.into_iter().collect(),
constraints: constraints.into_iter().collect(),
overrides: overrides.into_iter().collect(),
build_constraints: build_constraints.into_iter().collect(),
dependency_groups: dependency_groups
.into_iter()
.map(|(group, requirements)| (group, requirements.into_iter().collect()))
Expand Down Expand Up @@ -1815,6 +1875,11 @@ impl ResolverManifest {
.into_iter()
.map(|requirement| requirement.relative_to(root))
.collect::<Result<BTreeSet<_>, _>>()?,
build_constraints: self
.build_constraints
.into_iter()
.map(|requirement| requirement.relative_to(root))
.collect::<Result<BTreeSet<_>, _>>()?,
dependency_groups: self
.dependency_groups
.into_iter()
Expand Down Expand Up @@ -2377,11 +2442,8 @@ impl Package {
url.set_query(None);

// Reconstruct the `GitUrl` from the `GitSource`.
let git_url = uv_git_types::GitUrl::from_commit(
url,
GitReference::from(git.kind.clone()),
git.precise,
)?;
let git_url =
GitUrl::from_commit(url, GitReference::from(git.kind.clone()), git.precise)?;

// Reconstruct the PEP 508-compatible URL from the `GitSource`.
let url = Url::from(ParsedGitUrl {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
build_constraints: {},
dependency_metadata: {},
},
},
Expand Down
6 changes: 5 additions & 1 deletion crates/uv/src/commands/project/install_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::str::FromStr;
use itertools::Either;
use rustc_hash::FxHashSet;

use uv_configuration::{DependencyGroupsWithDefaults, ExtrasSpecification};
use uv_configuration::{Constraints, DependencyGroupsWithDefaults, ExtrasSpecification};
use uv_distribution_types::Index;
use uv_normalize::PackageName;
use uv_pypi_types::{DependencyGroupSpecifier, LenientRequirement, VerbatimParsedUrl};
Expand Down Expand Up @@ -236,6 +236,10 @@ impl<'lock> InstallTarget<'lock> {
}
}

pub(crate) fn build_constraints(&self) -> Constraints {
self.lock().build_constraints(self.install_path())
}

/// Validate the extras requested by the [`ExtrasSpecification`].
#[allow(clippy::result_large_err)]
pub(crate) fn validate_extras(self, extras: &ExtrasSpecification) -> Result<(), ProjectError> {
Expand Down
15 changes: 12 additions & 3 deletions crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,6 @@ 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_hasher = HashStrategy::default();
Expand All @@ -647,7 +645,7 @@ async fn do_lock(
let build_dispatch = BuildDispatch::new(
&client,
cache,
build_constraints,
Constraints::from_requirements(build_constraints.iter().cloned()),
interpreter,
index_locations,
&flat_index,
Expand Down Expand Up @@ -679,6 +677,7 @@ async fn do_lock(
&dependency_groups,
&constraints,
&overrides,
&build_constraints,
&conflicts,
environments,
required_environments,
Expand Down Expand Up @@ -837,6 +836,7 @@ async fn do_lock(
requirements,
constraints,
overrides,
build_constraints,
dependency_groups,
dependency_metadata.values().cloned(),
)
Expand Down Expand Up @@ -889,6 +889,7 @@ impl ValidatedLock {
dependency_groups: &BTreeMap<GroupName, Vec<Requirement>>,
constraints: &[Requirement],
overrides: &[Requirement],
build_constraints: &[Requirement],
conflicts: &Conflicts,
environments: Option<&SupportedEnvironments>,
required_environments: Option<&SupportedEnvironments>,
Expand Down Expand Up @@ -1066,6 +1067,7 @@ impl ValidatedLock {
requirements,
constraints,
overrides,
build_constraints,
dependency_groups,
dependency_metadata,
indexes,
Expand Down Expand Up @@ -1144,6 +1146,13 @@ impl ValidatedLock {
);
Ok(Self::Preferable(lock))
}
SatisfiesResult::MismatchedBuildConstraints(expected, actual) => {
debug!(
"Ignoring existing lockfile due to mismatched build constraints:\n Requested: {:?}\n Existing: {:?}",
expected, actual
);
Ok(Self::Preferable(lock))
}
SatisfiesResult::MismatchedDependencyGroups(expected, actual) => {
debug!(
"Ignoring existing lockfile due to mismatched dependency groups:\n Requested: {:?}\n Existing: {:?}",
Expand Down
Loading
Loading