1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use std::io;
use std::path::{Path, PathBuf};
use uv_static::EnvVars;
use crate::Cache;
use clap::Parser;
use tracing::{debug, warn};
#[derive(Parser, Debug, Clone)]
#[command(next_help_heading = "Cache options")]
pub struct CacheArgs {
/// Avoid reading from or writing to the cache, instead using a temporary directory for the
/// duration of the operation.
#[arg(
global = true,
long,
short,
alias = "no-cache-dir",
env = EnvVars::UV_NO_CACHE,
value_parser = clap::builder::BoolishValueParser::new(),
)]
pub no_cache: bool,
/// Path to the cache directory.
///
/// Defaults to `$XDG_CACHE_HOME/uv` or `$HOME/.cache/uv` on macOS and Linux, and
/// `%LOCALAPPDATA%\uv\cache` on Windows.
///
/// To view the location of the cache directory, run `uv cache dir`.
#[arg(global = true, long, env = EnvVars::UV_CACHE_DIR)]
pub cache_dir: Option<PathBuf>,
}
impl Cache {
/// Prefer, in order:
///
/// 1. A temporary cache directory, if the user requested `--no-cache`.
/// 2. The specific cache directory specified by the user via `--cache-dir` or `UV_CACHE_DIR`.
/// 3. The system-appropriate cache directory.
/// 4. A `.uv_cache` directory in the current working directory.
///
/// Returns an absolute cache dir.
pub fn from_settings(no_cache: bool, cache_dir: Option<PathBuf>) -> Result<Self, io::Error> {
if no_cache {
Self::temp()
} else if let Some(cache_dir) = cache_dir {
Ok(Self::from_path(cache_dir))
} else if let Some(cache_dir) = uv_dirs::legacy_user_cache_dir().filter(|dir| dir.exists())
{
// If the user has an existing directory at (e.g.) `/Users/user/Library/Caches/uv`,
// respect it for backwards compatibility. Otherwise, prefer the XDG strategy, even on
// macOS.
Ok(Self::from_path(cache_dir))
} else if let Some(cache_dir) = uv_dirs::user_cache_dir() {
if cfg!(windows) {
// On Windows, we append `cache` to the LocalAppData directory, i.e., prefer
// `C:\Users\User\AppData\Local\uv\cache` over `C:\Users\User\AppData\Local\uv`.
//
// Unfortunately, v0.3.0 and v0.3.1 used the latter, so we need to migrate the cache
// for those users.
let destination = cache_dir.join("cache");
let source = cache_dir;
if let Err(err) = migrate_windows_cache(&source, &destination) {
warn!(
"Failed to migrate cache from `{}` to `{}`: {err}",
source.display(),
destination.display()
);
}
Ok(Self::from_path(destination))
} else {
Ok(Self::from_path(cache_dir))
}
} else {
Ok(Self::from_path(".uv_cache"))
}
}
}
impl TryFrom<CacheArgs> for Cache {
type Error = io::Error;
fn try_from(value: CacheArgs) -> Result<Self, Self::Error> {
Self::from_settings(value.no_cache, value.cache_dir)
}
}
/// Migrate the Windows cache from `C:\Users\User\AppData\Local\uv` to `C:\Users\User\AppData\Local\uv\cache`.
fn migrate_windows_cache(source: &Path, destination: &Path) -> Result<(), io::Error> {
// The list of expected cache buckets in v0.3.0.
for directory in [
"built-wheels-v3",
"flat-index-v0",
"git-v0",
"interpreter-v2",
"simple-v12",
"wheels-v1",
"archive-v0",
"builds-v0",
"environments-v1",
] {
let source = source.join(directory);
let destination = destination.join(directory);
// Migrate the cache bucket.
if source.exists() {
debug!(
"Migrating cache bucket from {} to {}",
source.display(),
destination.display()
);
if let Some(parent) = destination.parent() {
fs_err::create_dir_all(parent)?;
}
fs_err::rename(&source, &destination)?;
}
}
// The list of expected cache files in v0.3.0.
for file in [".gitignore", "CACHEDIR.TAG"] {
let source = source.join(file);
let destination = destination.join(file);
// Migrate the cache file.
if source.exists() {
debug!(
"Migrating cache file from {} to {}",
source.display(),
destination.display()
);
if let Some(parent) = destination.parent() {
fs_err::create_dir_all(parent)?;
}
fs_err::rename(&source, &destination)?;
}
}
Ok(())
}