Skip to content

Commit b172d53

Browse files
committed
Use system temp directory on unix
1 parent ca648bd commit b172d53

File tree

1 file changed

+80
-5
lines changed

1 file changed

+80
-5
lines changed

src/cargo/core/compiler/layout.rs

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
use crate::core::compiler::CompileTarget;
102102
use crate::core::Workspace;
103103
use crate::util::paths;
104-
use crate::util::{CargoResult, FileLock};
104+
use crate::util::{CargoResult, CargoResultExt, FileLock};
105105
use std::path::{Path, PathBuf};
106106

107107
/// Contains the paths of all target output locations.
@@ -125,6 +125,8 @@ pub struct Layout {
125125
examples: PathBuf,
126126
/// The directory for rustdoc output: `$root/doc`
127127
doc: PathBuf,
128+
/// Root in system's temporary directory
129+
temp_root: Option<PathBuf>,
128130
/// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
129131
/// struct is `drop`ped.
130132
_lock: FileLock,
@@ -170,6 +172,7 @@ impl Layout {
170172
fingerprint: dest.join(".fingerprint"),
171173
examples: dest.join("examples"),
172174
doc: root.join("doc"),
175+
temp_root: Self::temp_root_path(&root),
173176
root,
174177
dest,
175178
_lock: lock,
@@ -178,15 +181,87 @@ impl Layout {
178181

179182
/// Makes sure all directories stored in the Layout exist on the filesystem.
180183
pub fn prepare(&mut self) -> CargoResult<()> {
181-
paths::create_dir_all(&self.deps)?;
182-
paths::create_dir_all(&self.incremental)?;
183-
paths::create_dir_all(&self.fingerprint)?;
184+
let temp_root = self.temp_root.as_deref();
185+
if let Some(temp_root) = temp_root {
186+
paths::create_dir_all(temp_root)?;
187+
}
188+
Self::create_dir_or_symlink_to_temp(temp_root, &self.deps)?;
189+
Self::create_dir_or_symlink_to_temp(temp_root, &self.incremental)?;
190+
Self::create_dir_or_symlink_to_temp(temp_root, &self.fingerprint)?;
191+
Self::create_dir_or_symlink_to_temp(temp_root, &self.build)?;
184192
paths::create_dir_all(&self.examples)?;
185-
paths::create_dir_all(&self.build)?;
186193

187194
Ok(())
188195
}
189196

197+
/// Create a path for a subdirectory in system's temporary directory
198+
/// that is specifically for the root path.
199+
#[cfg(unix)]
200+
fn temp_root_path(target_root: &Path) -> Option<PathBuf> {
201+
let temp_root = paths::persistent_temp_path()?;
202+
203+
// Each target dir gets its own temp subdir based on a hash of the path
204+
// with some printable chars for friendlier paths
205+
let root_bytes = paths::path2bytes(target_root).ok()?;
206+
let mut dir_name = String::with_capacity(64 + 17);
207+
for ch in root_bytes[root_bytes.len().saturating_sub(64)..]
208+
.iter()
209+
.skip(1) // initial slash
210+
.copied()
211+
{
212+
dir_name.push(if ch.is_ascii_alphanumeric() {
213+
ch as char
214+
} else {
215+
'-'
216+
});
217+
}
218+
dir_name.push('-');
219+
dir_name.push_str(&crate::util::short_hash(&root_bytes));
220+
221+
Some(temp_root.join(dir_name))
222+
}
223+
224+
/// On non-Unix it's not safe to assume that symlinks are reliable and efficient,
225+
/// so symlinking to temp won't be used.
226+
#[cfg(not(unix))]
227+
fn temp_root_path(_root: &Path) -> Option<PathBuf> {
228+
None
229+
}
230+
231+
/// Symlink `path` to inside of `temp_root`, or create the `path` as dir as a fallback
232+
#[cfg(unix)]
233+
fn create_dir_or_symlink_to_temp(temp_root: Option<&Path>, path: &Path) -> CargoResult<()> {
234+
// Don't change existing target subdirectories (this also verifies that the symlink is valid)
235+
if path.exists() {
236+
return Ok(());
237+
}
238+
// Clean up broken symlinks (OK to ignore failures, subsequent operations will report a failure)
239+
let _ = std::fs::remove_file(path);
240+
241+
if let Some(temp_root) = temp_root {
242+
let file_name = path.file_name().expect("/ isn't allowed");
243+
let temp_dest = temp_root.join(file_name);
244+
paths::create_dir_all(&temp_dest)?;
245+
std::os::unix::fs::symlink(&temp_dest, path).chain_err(|| {
246+
format!(
247+
"failed to symlink `{}` to `{}`",
248+
path.display(),
249+
temp_dest.display()
250+
)
251+
})?;
252+
Ok(())
253+
} else {
254+
paths::create_dir_all(path)
255+
}
256+
}
257+
258+
/// On non-Unix it's not safe to assume that symlinks are reliable and efficient,
259+
/// so symlinking to temp won't be used.
260+
#[cfg(not(unix))]
261+
fn create_dir_or_symlink_to_temp(_temp_root: &Path, path: &Path) -> CargoResult<()> {
262+
paths::create_dir_all(path)
263+
}
264+
190265
/// Fetch the destination path for final artifacts (`/…/target/debug`).
191266
pub fn dest(&self) -> &Path {
192267
&self.dest

0 commit comments

Comments
 (0)