mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Use files instead of junctions on Windows (#11269)
Instead of using junctions, we can just write files that contain (as the file contents) the target path. This requires a little more finesse in that, as readers, we need to know where to expect these. But it also means we get to avoid junctions, which have led to a variety of confusing behaviors. Further, `replace_symlink` should now be on atomic on Windows. Closes #11263.
This commit is contained in:
parent
59c65c3e77
commit
4d5041dc00
14 changed files with 351 additions and 59 deletions
|
@ -1,12 +1,7 @@
|
|||
use uv_cache::{ArchiveId, Cache};
|
||||
use uv_cache::{ArchiveId, Cache, ARCHIVE_VERSION};
|
||||
use uv_distribution_types::Hashed;
|
||||
use uv_pypi_types::HashDigest;
|
||||
|
||||
/// The version of the archive bucket.
|
||||
///
|
||||
/// Must be kept in-sync with the version in [`uv_cache::CacheBucket::to_str`].
|
||||
const ARCHIVE_VERSION: u8 = 0;
|
||||
|
||||
/// An archive (unzipped wheel) that exists in the local cache.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Archive {
|
||||
|
|
|
@ -371,7 +371,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
|||
|
||||
// If the wheel was unzipped previously, respect it. Source distributions are
|
||||
// cached under a unique revision ID, so unzipped directories are never stale.
|
||||
match built_wheel.target.canonicalize() {
|
||||
match self.build_context.cache().resolve_link(&built_wheel.target) {
|
||||
Ok(archive) => {
|
||||
return Ok(LocalWheel {
|
||||
dist: Dist::Source(dist.clone()),
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
use crate::index::cached_wheel::CachedWheel;
|
||||
use crate::source::{HttpRevisionPointer, LocalRevisionPointer, HTTP_REVISION, LOCAL_REVISION};
|
||||
use crate::Error;
|
||||
use uv_cache::{Cache, CacheBucket, CacheShard, WheelCache};
|
||||
use uv_cache_info::CacheInfo;
|
||||
use uv_cache_key::cache_digest;
|
||||
|
@ -8,10 +5,13 @@ use uv_configuration::ConfigSettings;
|
|||
use uv_distribution_types::{
|
||||
DirectUrlSourceDist, DirectorySourceDist, GitSourceDist, Hashed, PathSourceDist,
|
||||
};
|
||||
use uv_fs::symlinks;
|
||||
use uv_platform_tags::Tags;
|
||||
use uv_types::HashStrategy;
|
||||
|
||||
use crate::index::cached_wheel::CachedWheel;
|
||||
use crate::source::{HttpRevisionPointer, LocalRevisionPointer, HTTP_REVISION, LOCAL_REVISION};
|
||||
use crate::Error;
|
||||
|
||||
/// A local index of built distributions for a specific source distribution.
|
||||
#[derive(Debug)]
|
||||
pub struct BuiltWheelIndex<'a> {
|
||||
|
@ -203,8 +203,16 @@ impl<'a> BuiltWheelIndex<'a> {
|
|||
let mut candidate: Option<CachedWheel> = None;
|
||||
|
||||
// Unzipped wheels are stored as symlinks into the archive directory.
|
||||
for subdir in symlinks(shard) {
|
||||
match CachedWheel::from_built_source(&subdir) {
|
||||
for wheel_dir in uv_fs::entries(shard) {
|
||||
// Ignore any `.lock` files.
|
||||
if wheel_dir
|
||||
.extension()
|
||||
.is_some_and(|ext| ext.eq_ignore_ascii_case("lock"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match CachedWheel::from_built_source(&wheel_dir, self.cache) {
|
||||
None => {}
|
||||
Some(dist_info) => {
|
||||
// Pick the wheel with the highest priority
|
||||
|
|
|
@ -26,7 +26,7 @@ pub struct CachedWheel {
|
|||
|
||||
impl CachedWheel {
|
||||
/// Try to parse a distribution from a cached directory name (like `typing-extensions-4.8.0-py3-none-any`).
|
||||
pub fn from_built_source(path: impl AsRef<Path>) -> Option<Self> {
|
||||
pub fn from_built_source(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> {
|
||||
let path = path.as_ref();
|
||||
|
||||
// Determine the wheel filename.
|
||||
|
@ -34,7 +34,7 @@ impl CachedWheel {
|
|||
let filename = WheelFilename::from_stem(filename).ok()?;
|
||||
|
||||
// Convert to a cached wheel.
|
||||
let archive = path.canonicalize().ok()?;
|
||||
let archive = cache.resolve_link(path).ok()?;
|
||||
let entry = CacheEntry::from_path(archive);
|
||||
let hashes = Vec::new();
|
||||
let cache_info = CacheInfo::default();
|
||||
|
|
|
@ -6,7 +6,7 @@ use uv_cache::{Cache, CacheBucket, WheelCache};
|
|||
use uv_cache_key::cache_digest;
|
||||
use uv_configuration::ConfigSettings;
|
||||
use uv_distribution_types::{CachedRegistryDist, Hashed, Index, IndexLocations, IndexUrl};
|
||||
use uv_fs::{directories, files, symlinks};
|
||||
use uv_fs::{directories, files};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_platform_tags::Tags;
|
||||
use uv_types::HashStrategy;
|
||||
|
@ -205,8 +205,16 @@ impl<'a> RegistryWheelIndex<'a> {
|
|||
cache_shard.shard(cache_digest(build_configuration))
|
||||
};
|
||||
|
||||
for wheel_dir in symlinks(cache_shard) {
|
||||
if let Some(wheel) = CachedWheel::from_built_source(wheel_dir) {
|
||||
for wheel_dir in uv_fs::entries(cache_shard) {
|
||||
// Ignore any `.lock` files.
|
||||
if wheel_dir
|
||||
.extension()
|
||||
.is_some_and(|ext| ext.eq_ignore_ascii_case("lock"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(wheel) = CachedWheel::from_built_source(wheel_dir, cache) {
|
||||
if wheel.filename.compatibility(tags).is_compatible() {
|
||||
// Enforce hash-checking based on the source distribution.
|
||||
if revision.satisfies(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue