Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark temporary directories as excluded from macOS Spotlight #8686

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 80 additions & 5 deletions src/cargo/core/compiler/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
use crate::core::compiler::CompileTarget;
use crate::core::Workspace;
use crate::util::paths;
use crate::util::{CargoResult, FileLock};
use crate::util::{CargoResult, CargoResultExt, FileLock};
use std::path::{Path, PathBuf};

/// Contains the paths of all target output locations.
Expand All @@ -125,6 +125,8 @@ pub struct Layout {
examples: PathBuf,
/// The directory for rustdoc output: `$root/doc`
doc: PathBuf,
/// Root in system's temporary directory
temp_root: Option<PathBuf>,
/// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
/// struct is `drop`ped.
_lock: FileLock,
Expand Down Expand Up @@ -170,6 +172,7 @@ impl Layout {
fingerprint: dest.join(".fingerprint"),
examples: dest.join("examples"),
doc: root.join("doc"),
temp_root: Self::temp_root_path(&root),
Copy link

@soc soc Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is a bit confusing, because cache and temp are usually different things and people people have different conceptions about them.

root,
dest,
_lock: lock,
Expand All @@ -178,15 +181,87 @@ impl Layout {

/// Makes sure all directories stored in the Layout exist on the filesystem.
pub fn prepare(&mut self) -> CargoResult<()> {
paths::create_dir_all(&self.deps)?;
paths::create_dir_all(&self.incremental)?;
paths::create_dir_all(&self.fingerprint)?;
let temp_root = self.temp_root.as_deref();
if let Some(temp_root) = temp_root {
paths::create_dir_all(temp_root)?;
}
Self::create_dir_or_symlink_to_temp(temp_root, &self.deps)?;
Self::create_dir_or_symlink_to_temp(temp_root, &self.incremental)?;
Self::create_dir_or_symlink_to_temp(temp_root, &self.fingerprint)?;
Self::create_dir_or_symlink_to_temp(temp_root, &self.build)?;
paths::create_dir_all(&self.examples)?;
paths::create_dir_all(&self.build)?;

Ok(())
}

/// Create a path for a subdirectory in system's temporary directory
/// that is specifically for the root path.
#[cfg(unix)]
fn temp_root_path(target_root: &Path) -> Option<PathBuf> {
let temp_root = paths::persistent_temp_path()?;

// Each target dir gets its own temp subdir based on a hash of the path
// with some printable chars for friendlier paths
let root_bytes = paths::path2bytes(target_root).ok()?;
let mut dir_name = String::with_capacity(64 + 17);
for ch in root_bytes[root_bytes.len().saturating_sub(64)..]
.iter()
.skip(1) // initial slash
.copied()
{
dir_name.push(if ch.is_ascii_alphanumeric() {
ch as char
} else {
'-'
});
}
dir_name.push('-');
dir_name.push_str(&crate::util::short_hash(&root_bytes));

Some(temp_root.join(dir_name))
}

/// On non-Unix it's not safe to assume that symlinks are reliable and efficient,
/// so symlinking to temp won't be used.
#[cfg(not(unix))]
fn temp_root_path(_root: &Path) -> Option<PathBuf> {
None
}

/// Symlink `path` to inside of `temp_root`, or create the `path` as dir as a fallback
#[cfg(unix)]
fn create_dir_or_symlink_to_temp(temp_root: Option<&Path>, path: &Path) -> CargoResult<()> {
// Don't change existing target subdirectories (this also verifies that the symlink is valid)
if path.exists() {
return Ok(());
}
// Clean up broken symlinks (OK to ignore failures, subsequent operations will report a failure)
let _ = std::fs::remove_file(path);

if let Some(temp_root) = temp_root {
let file_name = path.file_name().expect("/ isn't allowed");
let temp_dest = temp_root.join(file_name);
paths::create_dir_all(&temp_dest)?;
std::os::unix::fs::symlink(&temp_dest, path).chain_err(|| {
format!(
"failed to symlink `{}` to `{}`",
path.display(),
temp_dest.display()
)
})?;
Ok(())
} else {
paths::create_dir_all(path)
}
}

/// On non-Unix it's not safe to assume that symlinks are reliable and efficient,
/// so symlinking to temp won't be used.
#[cfg(not(unix))]
fn create_dir_or_symlink_to_temp(_temp_root: &Path, path: &Path) -> CargoResult<()> {
paths::create_dir_all(path)
}

/// Fetch the destination path for final artifacts (`/…/target/debug`).
pub fn dest(&self) -> &Path {
&self.dest
Expand Down
33 changes: 33 additions & 0 deletions src/cargo/util/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,3 +547,36 @@ fn exclude_from_time_machine(path: &Path) {
// Errors are ignored, since it's an optional feature and failure
// doesn't prevent Cargo from working
}

/// Absolute path to an on-disk temporary directory.
/// It may be system-wide or user-specific depending on system conventions.
/// Used in layout.rs, only needed on Unix systems.
#[cfg(unix)]
pub fn persistent_temp_path() -> Option<PathBuf> {
#[cfg(target_os = "macos")]
{
// "Library/Caches" should be obtained via NSFileManager's
// URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask
// However, cocoa-foundation doesn't have bindings for it yet.
// This path has remained stable for two decades,
// so hardcode it for simplicity (dirs crate does the same).
Some(home::home_dir()?.join("Library/Caches/Cargo"))
}
#[cfg(not(target_os = "macos"))]
{
// XDG standard
if let Some(path) = env::var_os("XDG_CACHE_HOME") {
let path = PathBuf::from(path);
if path.is_absolute() {
return Some(path.join("cargo"));
}
}
// FHS standard
// This is not using /tmp, because /tmp could be using ramfs.
let path = Path::new("/var/tmp");
if path.exists() {
return Some(path.join("cargo"));
}
None
Copy link

@soc soc Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand this logic – is there any reason why it ignores the "important" part of XDG and instead does something completely different altogether in the "env not set" case?

More generally, reimplementing the directory-selection logic seems to be a straight-forward route to bikeshed city (as demonstrated in the RFC).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What "important" part do you mean?

I am afraid that touching XDG is going to attract bikeshed fiesta. I'm tempted to remove it for that reason, and go straight for /var/tmp instead.

Copy link

@soc soc Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What "important" part do you mean?

XDG defines default directories if the env var is not set. The env var is probably not set in 99% of the cases, because people are fine with the defaults.

Only "being XDG" when the env var is set ... if I wanted to troll people, that's how I would do it. ;-)

I am afraid that touching XDG is going to attract bikeshed fiesta.

Did you consider simply using a library?

I'm tempted to remove it for that reason, and go straight for /var/tmp instead.

That's a cheap way out for this special case, but I'm not sure you want the next person following this precedent when they try to fix the issue for config/data files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've considered using a library, but it this area is full of abandoned projects and half-abandoned forks, so I didn't see any suitable crate.

I think the best option would be to use a cargo-specific variable/setting for customizing this dir.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've considered using a library, but it this area is full of abandoned projects and half-abandoned forks, so I didn't see any suitable crate.

I think no crate can help you if your intention is to invent your own non-standard scheme anyway.

I think the best option would be to use a cargo-specific variable/setting for customizing this dir.

That's of course technically possible, but is does nothing to resolve rust-lang/rfcs#1615 which is about sane defaults.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think no crate can help you if your intention is to invent your own non-standard scheme anyway.

Jabs like that are unhelpful. Please don't do that.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a nice day.

}
}