cuenv_workspaces/materializer/
cargo_deps.rs

1//! Materializer for Cargo dependencies.
2
3use super::Materializer;
4use crate::core::types::{LockfileEntry, PackageManager, Workspace};
5use crate::error::{Error, Result};
6use std::path::Path;
7
8#[cfg(unix)]
9use std::os::unix::fs::symlink;
10#[cfg(windows)]
11use std::os::windows::fs::symlink_dir as symlink;
12
13/// Materializer for Cargo projects.
14pub struct CargoMaterializer;
15
16impl Materializer for CargoMaterializer {
17    fn materialize(
18        &self,
19        workspace: &Workspace,
20        _entries: &[LockfileEntry],
21        target_dir: &Path,
22    ) -> Result<()> {
23        if workspace.manager != PackageManager::Cargo {
24            return Ok(());
25        }
26
27        // Symlink the target directory to share build artifacts
28        // This allows reusing incremental compilation results
29        let workspace_target = workspace.root.join("target");
30        let env_target = target_dir.join("target");
31
32        if !workspace_target.exists() {
33            std::fs::create_dir_all(&workspace_target).map_err(|e| Error::Io {
34                source: e,
35                path: Some(workspace_target.clone()),
36                operation: "create workspace target directory".to_string(),
37            })?;
38        }
39
40        if env_target.exists() {
41            // If it exists (e.g. from previous run or created by inputs), remove it
42            // to allow symlinking the shared target directory.
43            if env_target.is_symlink() || env_target.is_file() {
44                std::fs::remove_file(&env_target).map_err(|e| Error::Io {
45                    source: e,
46                    path: Some(env_target.clone()),
47                    operation: "removing existing target symlink/file".to_string(),
48                })?;
49            } else {
50                std::fs::remove_dir_all(&env_target).map_err(|e| Error::Io {
51                    source: e,
52                    path: Some(env_target.clone()),
53                    operation: "removing existing target directory".to_string(),
54                })?;
55            }
56        }
57
58        // We assume target_dir is the root of the hermetic environment.
59        // Cargo expects 'target' at the root usually.
60
61        // Note: Symlinking 'target' might cause locking issues if multiple tasks run in parallel
62        // and try to write to the same shared target.
63        // However, Cargo handles concurrent builds relatively well with file locking.
64        // But different tasks might need different profiles or features.
65        // Ideally, we should use CARGO_TARGET_DIR env var instead of symlinking,
66        // but symlinking works if we want it to appear local.
67
68        if let Err(e) = symlink(&workspace_target, &env_target) {
69            return Err(Error::Io {
70                source: e,
71                path: Some(env_target),
72                operation: "symlink target dir".to_string(),
73            });
74        }
75
76        Ok(())
77    }
78}