Skip to content

Commit 29f53c3

Browse files
Support --no-build and --no-binary in uv sync et al (#7100)
## Summary This option already existed, but `--no-binary` always errored. Closes #7099.
1 parent f2309bf commit 29f53c3

File tree

4 files changed

+241
-72
lines changed

4 files changed

+241
-72
lines changed

crates/uv-resolver/src/lock/mod.rs

Lines changed: 124 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use pypi_types::{
3131
redact_git_credentials, HashDigest, ParsedArchiveUrl, ParsedGitUrl, Requirement,
3232
RequirementSource, ResolverMarkerEnvironment,
3333
};
34-
use uv_configuration::ExtrasSpecification;
34+
use uv_configuration::{BuildOptions, ExtrasSpecification};
3535
use uv_distribution::DistributionDatabase;
3636
use uv_fs::{relative_to, PortablePath, PortablePathBuf};
3737
use uv_git::{GitReference, GitSha, RepositoryReference, ResolvedRepositoryReference};
@@ -561,6 +561,7 @@ impl Lock {
561561
tags: &Tags,
562562
extras: &ExtrasSpecification,
563563
dev: &[GroupName],
564+
build_options: &BuildOptions,
564565
) -> Result<Resolution, LockError> {
565566
let mut queue: VecDeque<(&Package, Option<&ExtraName>)> = VecDeque::new();
566567
let mut seen = FxHashSet::default();
@@ -649,7 +650,11 @@ impl Lock {
649650
}
650651
map.insert(
651652
dist.id.name.clone(),
652-
ResolvedDist::Installable(dist.to_dist(project.workspace().install_path(), tags)?),
653+
ResolvedDist::Installable(dist.to_dist(
654+
project.workspace().install_path(),
655+
tags,
656+
build_options,
657+
)?),
653658
);
654659
hashes.insert(dist.id.name.clone(), dist.hashes());
655660
}
@@ -876,6 +881,7 @@ impl Lock {
876881
constraints: &[Requirement],
877882
overrides: &[Requirement],
878883
indexes: Option<&IndexLocations>,
884+
build_options: &BuildOptions,
879885
tags: &Tags,
880886
database: &DistributionDatabase<'_, Context>,
881887
) -> Result<SatisfiesResult<'_>, LockError> {
@@ -1066,7 +1072,7 @@ impl Lock {
10661072
}
10671073

10681074
// Get the metadata for the distribution.
1069-
let dist = package.to_dist(workspace.install_path(), tags)?;
1075+
let dist = package.to_dist(workspace.install_path(), tags, build_options)?;
10701076

10711077
let Ok(archive) = database
10721078
.get_or_build_wheel_metadata(&dist, HashPolicy::None)
@@ -1565,78 +1571,106 @@ impl Package {
15651571
}
15661572

15671573
/// Convert the [`Package`] to a [`Dist`] that can be used in installation.
1568-
fn to_dist(&self, workspace_root: &Path, tags: &Tags) -> Result<Dist, LockError> {
1569-
if let Some(best_wheel_index) = self.find_best_wheel(tags) {
1570-
return match &self.id.source {
1571-
Source::Registry(source) => {
1572-
let wheels = self
1573-
.wheels
1574-
.iter()
1575-
.map(|wheel| wheel.to_registry_dist(source, workspace_root))
1576-
.collect::<Result<_, LockError>>()?;
1577-
let reg_built_dist = RegistryBuiltDist {
1578-
wheels,
1579-
best_wheel_index,
1580-
sdist: None,
1581-
};
1582-
Ok(Dist::Built(BuiltDist::Registry(reg_built_dist)))
1583-
}
1584-
Source::Path(path) => {
1585-
let filename: WheelFilename = self.wheels[best_wheel_index].filename.clone();
1586-
let path_dist = PathBuiltDist {
1587-
filename,
1588-
url: verbatim_url(workspace_root.join(path), &self.id)?,
1589-
install_path: workspace_root.join(path),
1590-
};
1591-
let built_dist = BuiltDist::Path(path_dist);
1592-
Ok(Dist::Built(built_dist))
1593-
}
1594-
Source::Direct(url, direct) => {
1595-
let filename: WheelFilename = self.wheels[best_wheel_index].filename.clone();
1596-
let url = Url::from(ParsedArchiveUrl {
1597-
url: url.to_url(),
1598-
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
1599-
ext: DistExtension::Wheel,
1600-
});
1601-
let direct_dist = DirectUrlBuiltDist {
1602-
filename,
1603-
location: url.clone(),
1604-
url: VerbatimUrl::from_url(url),
1605-
};
1606-
let built_dist = BuiltDist::DirectUrl(direct_dist);
1607-
Ok(Dist::Built(built_dist))
1608-
}
1609-
Source::Git(_, _) => Err(LockErrorKind::InvalidWheelSource {
1610-
id: self.id.clone(),
1611-
source_type: "Git",
1612-
}
1613-
.into()),
1614-
Source::Directory(_) => Err(LockErrorKind::InvalidWheelSource {
1615-
id: self.id.clone(),
1616-
source_type: "directory",
1617-
}
1618-
.into()),
1619-
Source::Editable(_) => Err(LockErrorKind::InvalidWheelSource {
1620-
id: self.id.clone(),
1621-
source_type: "editable",
1622-
}
1623-
.into()),
1624-
Source::Virtual(_) => Err(LockErrorKind::InvalidWheelSource {
1625-
id: self.id.clone(),
1626-
source_type: "virtual",
1627-
}
1628-
.into()),
1574+
fn to_dist(
1575+
&self,
1576+
workspace_root: &Path,
1577+
tags: &Tags,
1578+
build_options: &BuildOptions,
1579+
) -> Result<Dist, LockError> {
1580+
let no_binary = build_options.no_binary_package(&self.id.name);
1581+
let no_build = build_options.no_build_package(&self.id.name);
1582+
1583+
if !no_binary {
1584+
if let Some(best_wheel_index) = self.find_best_wheel(tags) {
1585+
return match &self.id.source {
1586+
Source::Registry(source) => {
1587+
let wheels = self
1588+
.wheels
1589+
.iter()
1590+
.map(|wheel| wheel.to_registry_dist(source, workspace_root))
1591+
.collect::<Result<_, LockError>>()?;
1592+
let reg_built_dist = RegistryBuiltDist {
1593+
wheels,
1594+
best_wheel_index,
1595+
sdist: None,
1596+
};
1597+
Ok(Dist::Built(BuiltDist::Registry(reg_built_dist)))
1598+
}
1599+
Source::Path(path) => {
1600+
let filename: WheelFilename =
1601+
self.wheels[best_wheel_index].filename.clone();
1602+
let path_dist = PathBuiltDist {
1603+
filename,
1604+
url: verbatim_url(workspace_root.join(path), &self.id)?,
1605+
install_path: workspace_root.join(path),
1606+
};
1607+
let built_dist = BuiltDist::Path(path_dist);
1608+
Ok(Dist::Built(built_dist))
1609+
}
1610+
Source::Direct(url, direct) => {
1611+
let filename: WheelFilename =
1612+
self.wheels[best_wheel_index].filename.clone();
1613+
let url = Url::from(ParsedArchiveUrl {
1614+
url: url.to_url(),
1615+
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
1616+
ext: DistExtension::Wheel,
1617+
});
1618+
let direct_dist = DirectUrlBuiltDist {
1619+
filename,
1620+
location: url.clone(),
1621+
url: VerbatimUrl::from_url(url),
1622+
};
1623+
let built_dist = BuiltDist::DirectUrl(direct_dist);
1624+
Ok(Dist::Built(built_dist))
1625+
}
1626+
Source::Git(_, _) => Err(LockErrorKind::InvalidWheelSource {
1627+
id: self.id.clone(),
1628+
source_type: "Git",
1629+
}
1630+
.into()),
1631+
Source::Directory(_) => Err(LockErrorKind::InvalidWheelSource {
1632+
id: self.id.clone(),
1633+
source_type: "directory",
1634+
}
1635+
.into()),
1636+
Source::Editable(_) => Err(LockErrorKind::InvalidWheelSource {
1637+
id: self.id.clone(),
1638+
source_type: "editable",
1639+
}
1640+
.into()),
1641+
Source::Virtual(_) => Err(LockErrorKind::InvalidWheelSource {
1642+
id: self.id.clone(),
1643+
source_type: "virtual",
1644+
}
1645+
.into()),
1646+
};
16291647
};
1630-
};
1648+
}
16311649

1632-
if let Some(sdist) = self.to_source_dist(workspace_root)? {
1633-
return Ok(Dist::Source(sdist));
1650+
if !no_build {
1651+
if let Some(sdist) = self.to_source_dist(workspace_root)? {
1652+
return Ok(Dist::Source(sdist));
1653+
}
16341654
}
16351655

1636-
Err(LockErrorKind::NeitherSourceDistNorWheel {
1637-
id: self.id.clone(),
1656+
match (no_binary, no_build) {
1657+
(true, true) => Err(LockErrorKind::NoBinaryNoBuild {
1658+
id: self.id.clone(),
1659+
}
1660+
.into()),
1661+
(true, false) => Err(LockErrorKind::NoBinary {
1662+
id: self.id.clone(),
1663+
}
1664+
.into()),
1665+
(false, true) => Err(LockErrorKind::NoBuild {
1666+
id: self.id.clone(),
1667+
}
1668+
.into()),
1669+
(false, false) => Err(LockErrorKind::NeitherSourceDistNorWheel {
1670+
id: self.id.clone(),
1671+
}
1672+
.into()),
16381673
}
1639-
.into())
16401674
}
16411675

16421676
/// Convert the source of this [`Package`] to a [`SourceDist`] that can be used in installation.
@@ -3758,6 +3792,26 @@ enum LockErrorKind {
37583792
/// The ID of the distribution that has a missing base.
37593793
id: PackageId,
37603794
},
3795+
/// An error that occurs when a distribution is marked as both `--no-binary` and `--no-build`.
3796+
#[error("distribution {id} can't be installed because it is marked as both `--no-binary` and `--no-build`")]
3797+
NoBinaryNoBuild {
3798+
/// The ID of the distribution.
3799+
id: PackageId,
3800+
},
3801+
/// An error that occurs when a distribution is marked as both `--no-binary`, but no source
3802+
/// distribution is available.
3803+
#[error("distribution {id} can't be installed because it is marked as `--no-binary` but has no source distribution")]
3804+
NoBinary {
3805+
/// The ID of the distribution.
3806+
id: PackageId,
3807+
},
3808+
/// An error that occurs when a distribution is marked as both `--no-build`, but no binary
3809+
/// distribution is available.
3810+
#[error("distribution {id} can't be installed because it is marked as `--no-build` but has no binary distribution")]
3811+
NoBuild {
3812+
/// The ID of the distribution.
3813+
id: PackageId,
3814+
},
37613815
/// An error that occurs when converting between URLs and paths.
37623816
#[error("found dependency `{id}` with no locked distribution")]
37633817
VerbatimUrl {

crates/uv/src/commands/project/lock.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use pypi_types::{Requirement, SupportedEnvironments};
1616
use uv_auth::store_credentials_from_url;
1717
use uv_cache::Cache;
1818
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
19-
use uv_configuration::{Concurrency, Constraints, ExtrasSpecification, Reinstall, Upgrade};
19+
use uv_configuration::{
20+
BuildOptions, Concurrency, Constraints, ExtrasSpecification, Reinstall, Upgrade,
21+
};
2022
use uv_dispatch::BuildDispatch;
2123
use uv_distribution::DistributionDatabase;
2224
use uv_fs::CWD;
@@ -436,6 +438,7 @@ async fn do_lock(
436438
interpreter,
437439
&requires_python,
438440
index_locations,
441+
build_options,
439442
upgrade,
440443
&options,
441444
&database,
@@ -590,6 +593,7 @@ impl ValidatedLock {
590593
interpreter: &Interpreter,
591594
requires_python: &RequiresPython,
592595
index_locations: &IndexLocations,
596+
build_options: &BuildOptions,
593597
upgrade: &Upgrade,
594598
options: &Options,
595599
database: &DistributionDatabase<'_, Context>,
@@ -706,6 +710,7 @@ impl ValidatedLock {
706710
constraints,
707711
overrides,
708712
indexes,
713+
build_options,
709714
interpreter.tags()?,
710715
database,
711716
)

crates/uv/src/commands/project/sync.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ pub(super) async fn do_sync(
219219
let tags = venv.interpreter().tags()?;
220220

221221
// Read the lockfile.
222-
let resolution = lock.to_resolution(target, &markers, tags, extras, &dev)?;
222+
let resolution = lock.to_resolution(target, &markers, tags, extras, &dev, build_options)?;
223223

224224
// Always skip virtual projects, which shouldn't be built or installed.
225225
let resolution = apply_no_virtual_project(resolution);

0 commit comments

Comments
 (0)