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
use etcetera::BaseStrategy;
/// Cache location options.
#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum CacheConfig {
/// Stores cache in the current working directory.
#[serde(rename = "local")]
Local,
/// Stores cache in the project's `target/environment` directory
/// (default).
#[default]
#[serde(rename = "target")]
Target,
/// Stores cache in the system's user cache directory.
#[serde(rename = "global")]
Global,
/// Stores cache under a user-specified directory. The environment
/// database file is placed inside it, not at this path.
#[serde(rename = "directory")]
Directory(std::path::PathBuf),
}
impl CacheConfig {
/// Returns the root directory for the cache.
///
/// Every arm degrades rather than fails: none of these look-ups is under
/// the application's control, and a cache root that can't be resolved must
/// cost a recompute, not abort the process. A root that turns out to be
/// unwritable is handled one level down, in
/// [`Database::open_at`](crate::persistence::Database::open_at).
pub fn root(&self) -> std::path::PathBuf {
match self {
// A daemon or test harness whose cwd was deleted has no current
// directory at all.
Self::Local => std::env::current_dir().unwrap_or_else(|err| {
log::warn!("cubecl cache: no current directory ({err}); using the user cache");
user_cache_dir()
}),
Self::Target => {
let start = match std::env::current_dir() {
Ok(dir) => dir,
// Same condition `Local` reports: a daemon or test harness
// whose cwd was deleted has no current directory to search
// from.
Err(err) => {
log::warn!(
"cubecl cache: no current directory ({err}); using the user cache"
);
return user_cache_dir();
}
};
// The *outermost* Cargo.toml, not the first one found walking
// up: a workspace member has its own manifest, but `target/`
// lives at the workspace root, so stopping at the innermost
// manifest would scatter caches into `member/target`.
let mut root = None;
let mut dir = start.as_path();
loop {
if let Ok(true) = std::fs::exists(dir.join("Cargo.toml")) {
root = Some(dir);
}
match dir.parent() {
Some(parent) => dir = parent,
None => break,
}
}
match root {
Some(root) => root.join("target").join("environment"),
// No Cargo.toml anywhere above cwd — this is a bundled or
// installed application (Tauri, GUI app, CLI installed via
// cargo install, etc.) running outside a workspace. Joining
// "target" onto the original cwd became `/target` when cwd
// was `/`, which fails on most platforms with EROFS and
// cascaded a directory failure into the whole autotune
// pipeline. Use the platform-appropriate user cache
// directory instead.
None => user_cache_dir(),
}
}
// The cache directory, not the configuration directory: this is
// regenerable data, and XDG says so. It also matches the `Target`
// fallback right above, which already used `cache_dir`.
Self::Global => user_cache_dir(),
Self::Directory(path_buf) => path_buf.clone(),
}
}
}
/// The user cache directory, or a temporary one when the platform can't name
/// it — a systemd unit with no `HOME`, a distroless container.
fn user_cache_dir() -> std::path::PathBuf {
match etcetera::choose_base_strategy() {
Ok(strategy) => strategy.cache_dir().join("cubecl"),
Err(err) => {
log::warn!(
"cubecl cache: no user cache directory ({err}); \
falling back to the temporary directory"
);
std::env::temp_dir().join("cubecl")
}
}
}