Skip to main content

cubecl_environment/persistence/
root.rs

1use etcetera::BaseStrategy;
2
3/// Cache location options.
4#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
5pub enum CacheConfig {
6    /// Stores cache in the current working directory.
7    #[serde(rename = "local")]
8    Local,
9
10    /// Stores cache in the project's `target/environment` directory
11    /// (default).
12    #[default]
13    #[serde(rename = "target")]
14    Target,
15
16    /// Stores cache in the system's user cache directory.
17    #[serde(rename = "global")]
18    Global,
19
20    /// Stores cache under a user-specified directory. The environment
21    /// database file is placed inside it, not at this path.
22    #[serde(rename = "directory")]
23    Directory(std::path::PathBuf),
24}
25
26impl CacheConfig {
27    /// Returns the root directory for the cache.
28    ///
29    /// Every arm degrades rather than fails: none of these look-ups is under
30    /// the application's control, and a cache root that can't be resolved must
31    /// cost a recompute, not abort the process. A root that turns out to be
32    /// unwritable is handled one level down, when the database is opened.
33    pub fn root(&self) -> std::path::PathBuf {
34        match self {
35            // A daemon or test harness whose cwd was deleted has no current
36            // directory at all.
37            Self::Local => std::env::current_dir().unwrap_or_else(|err| {
38                log::warn!("cubecl cache: no current directory ({err}); using the user cache");
39                user_cache_dir()
40            }),
41            Self::Target => {
42                let start = match std::env::current_dir() {
43                    Ok(dir) => dir,
44                    // Same condition `Local` reports: a daemon or test harness
45                    // whose cwd was deleted has no current directory to search
46                    // from.
47                    Err(err) => {
48                        log::warn!(
49                            "cubecl cache: no current directory ({err}); using the user cache"
50                        );
51                        return user_cache_dir();
52                    }
53                };
54
55                // The *outermost* Cargo.toml, not the first one found walking
56                // up: a workspace member has its own manifest, but `target/`
57                // lives at the workspace root, so stopping at the innermost
58                // manifest would scatter caches into `member/target`.
59                let mut root = None;
60                let mut dir = start.as_path();
61                loop {
62                    if let Ok(true) = std::fs::exists(dir.join("Cargo.toml")) {
63                        root = Some(dir);
64                    }
65                    match dir.parent() {
66                        Some(parent) => dir = parent,
67                        None => break,
68                    }
69                }
70
71                match root {
72                    Some(root) => root.join("target").join("environment"),
73                    // No Cargo.toml anywhere above cwd — this is a bundled or
74                    // installed application (Tauri, GUI app, CLI installed via
75                    // cargo install, etc.) running outside a workspace. Joining
76                    // "target" onto the original cwd became `/target` when cwd
77                    // was `/`, which fails on most platforms with EROFS and
78                    // cascaded a directory failure into the whole autotune
79                    // pipeline. Use the platform-appropriate user cache
80                    // directory instead.
81                    None => user_cache_dir(),
82                }
83            }
84            // The cache directory, not the configuration directory: this is
85            // regenerable data, and XDG says so. It also matches the `Target`
86            // fallback right above, which already used `cache_dir`.
87            Self::Global => user_cache_dir(),
88            Self::Directory(path_buf) => path_buf.clone(),
89        }
90    }
91}
92
93/// The user cache directory, or a temporary one when the platform can't name
94/// it — a systemd unit with no `HOME`, a distroless container.
95fn user_cache_dir() -> std::path::PathBuf {
96    match etcetera::choose_base_strategy() {
97        Ok(strategy) => strategy.cache_dir().join("cubecl"),
98        Err(err) => {
99            log::warn!(
100                "cubecl cache: no user cache directory ({err}); \
101                 falling back to the temporary directory"
102            );
103            std::env::temp_dir().join("cubecl")
104        }
105    }
106}