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, in
33    /// [`Database::open_at`](crate::persistence::Database::open_at).
34    pub fn root(&self) -> std::path::PathBuf {
35        match self {
36            // A daemon or test harness whose cwd was deleted has no current
37            // directory at all.
38            Self::Local => std::env::current_dir().unwrap_or_else(|err| {
39                log::warn!("cubecl cache: no current directory ({err}); using the user cache");
40                user_cache_dir()
41            }),
42            Self::Target => {
43                let start = match std::env::current_dir() {
44                    Ok(dir) => dir,
45                    // Same condition `Local` reports: a daemon or test harness
46                    // whose cwd was deleted has no current directory to search
47                    // from.
48                    Err(err) => {
49                        log::warn!(
50                            "cubecl cache: no current directory ({err}); using the user cache"
51                        );
52                        return user_cache_dir();
53                    }
54                };
55
56                // The *outermost* Cargo.toml, not the first one found walking
57                // up: a workspace member has its own manifest, but `target/`
58                // lives at the workspace root, so stopping at the innermost
59                // manifest would scatter caches into `member/target`.
60                let mut root = None;
61                let mut dir = start.as_path();
62                loop {
63                    if let Ok(true) = std::fs::exists(dir.join("Cargo.toml")) {
64                        root = Some(dir);
65                    }
66                    match dir.parent() {
67                        Some(parent) => dir = parent,
68                        None => break,
69                    }
70                }
71
72                match root {
73                    Some(root) => root.join("target").join("environment"),
74                    // No Cargo.toml anywhere above cwd — this is a bundled or
75                    // installed application (Tauri, GUI app, CLI installed via
76                    // cargo install, etc.) running outside a workspace. Joining
77                    // "target" onto the original cwd became `/target` when cwd
78                    // was `/`, which fails on most platforms with EROFS and
79                    // cascaded a directory failure into the whole autotune
80                    // pipeline. Use the platform-appropriate user cache
81                    // directory instead.
82                    None => user_cache_dir(),
83                }
84            }
85            // The cache directory, not the configuration directory: this is
86            // regenerable data, and XDG says so. It also matches the `Target`
87            // fallback right above, which already used `cache_dir`.
88            Self::Global => user_cache_dir(),
89            Self::Directory(path_buf) => path_buf.clone(),
90        }
91    }
92}
93
94/// The user cache directory, or a temporary one when the platform can't name
95/// it — a systemd unit with no `HOME`, a distroless container.
96fn user_cache_dir() -> std::path::PathBuf {
97    match etcetera::choose_base_strategy() {
98        Ok(strategy) => strategy.cache_dir().join("cubecl"),
99        Err(err) => {
100            log::warn!(
101                "cubecl cache: no user cache directory ({err}); \
102                 falling back to the temporary directory"
103            );
104            std::env::temp_dir().join("cubecl")
105        }
106    }
107}