Skip to content

Commit 674150e

Browse files
committed
Move hardlink_or_copy to a common location so it can be reused.
1 parent aca3274 commit 674150e

File tree

2 files changed

+63
-52
lines changed

2 files changed

+63
-52
lines changed

src/cargo/core/compiler/mod.rs

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@ use std::env;
1919
use std::ffi::{OsStr, OsString};
2020
use std::fs::{self, File};
2121
use std::io::Write;
22-
use std::path::{Path, PathBuf};
22+
use std::path::PathBuf;
2323
use std::sync::Arc;
2424

2525
use failure::Error;
2626
use lazycell::LazyCell;
2727
use log::debug;
28-
use same_file::is_same_file;
2928
use serde::Serialize;
3029

3130
pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
@@ -443,12 +442,12 @@ fn link_targets<'a, 'cfg>(
443442
}
444443
};
445444
destinations.push(dst.clone());
446-
hardlink_or_copy(src, dst)?;
445+
paths::link_or_copy(src, dst)?;
447446
if let Some(ref path) = output.export_path {
448447
let export_dir = export_dir.as_ref().unwrap();
449448
paths::create_dir_all(export_dir)?;
450449

451-
hardlink_or_copy(src, path)?;
450+
paths::link_or_copy(src, path)?;
452451
}
453452
}
454453

@@ -477,54 +476,6 @@ fn link_targets<'a, 'cfg>(
477476
}))
478477
}
479478

480-
/// Hardlink (file) or symlink (dir) src to dst if possible, otherwise copy it.
481-
fn hardlink_or_copy(src: &Path, dst: &Path) -> CargoResult<()> {
482-
debug!("linking {} to {}", src.display(), dst.display());
483-
if is_same_file(src, dst).unwrap_or(false) {
484-
return Ok(());
485-
}
486-
487-
// NB: we can't use dst.exists(), as if dst is a broken symlink,
488-
// dst.exists() will return false. This is problematic, as we still need to
489-
// unlink dst in this case. symlink_metadata(dst).is_ok() will tell us
490-
// whether dst exists *without* following symlinks, which is what we want.
491-
if fs::symlink_metadata(dst).is_ok() {
492-
paths::remove_file(&dst)?;
493-
}
494-
495-
let link_result = if src.is_dir() {
496-
#[cfg(target_os = "redox")]
497-
use std::os::redox::fs::symlink;
498-
#[cfg(unix)]
499-
use std::os::unix::fs::symlink;
500-
#[cfg(windows)]
501-
use std::os::windows::fs::symlink_dir as symlink;
502-
503-
let dst_dir = dst.parent().unwrap();
504-
let src = if src.starts_with(dst_dir) {
505-
src.strip_prefix(dst_dir).unwrap()
506-
} else {
507-
src
508-
};
509-
symlink(src, dst)
510-
} else {
511-
fs::hard_link(src, dst)
512-
};
513-
link_result
514-
.or_else(|err| {
515-
debug!("link failed {}. falling back to fs::copy", err);
516-
fs::copy(src, dst).map(|_| ())
517-
})
518-
.chain_err(|| {
519-
format!(
520-
"failed to link or copy `{}` to `{}`",
521-
src.display(),
522-
dst.display()
523-
)
524-
})?;
525-
Ok(())
526-
}
527-
528479
// For all plugin dependencies, add their -L paths (now calculated and present
529480
// in `build_script_outputs`) to the dynamic library load path for the command
530481
// to execute.

src/cargo/util/paths.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,63 @@ fn set_not_readonly(p: &Path) -> io::Result<bool> {
340340
fs::set_permissions(p, perms)?;
341341
Ok(true)
342342
}
343+
344+
/// Hardlink (file) or symlink (dir) src to dst if possible, otherwise copy it.
345+
///
346+
/// If the destination already exists, it is removed before linking.
347+
pub fn link_or_copy(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> CargoResult<()> {
348+
let src = src.as_ref();
349+
let dst = dst.as_ref();
350+
_link_or_copy(src, dst)
351+
}
352+
353+
fn _link_or_copy(src: &Path, dst: &Path) -> CargoResult<()> {
354+
log::debug!("linking {} to {}", src.display(), dst.display());
355+
if same_file::is_same_file(src, dst).unwrap_or(false) {
356+
return Ok(());
357+
}
358+
359+
// NB: we can't use dst.exists(), as if dst is a broken symlink,
360+
// dst.exists() will return false. This is problematic, as we still need to
361+
// unlink dst in this case. symlink_metadata(dst).is_ok() will tell us
362+
// whether dst exists *without* following symlinks, which is what we want.
363+
if fs::symlink_metadata(dst).is_ok() {
364+
remove_file(&dst)?;
365+
}
366+
367+
let link_result = if src.is_dir() {
368+
#[cfg(target_os = "redox")]
369+
use std::os::redox::fs::symlink;
370+
#[cfg(unix)]
371+
use std::os::unix::fs::symlink;
372+
#[cfg(windows)]
373+
// FIXME: This should probably panic or have a copy fallback. Symlinks
374+
// are not supported in all windows environments. Currently symlinking
375+
// is only used for .dSYM directories on macos, but this shouldn't be
376+
// accidentally relied upon.
377+
use std::os::windows::fs::symlink_dir as symlink;
378+
379+
let dst_dir = dst.parent().unwrap();
380+
let src = if src.starts_with(dst_dir) {
381+
src.strip_prefix(dst_dir).unwrap()
382+
} else {
383+
src
384+
};
385+
symlink(src, dst)
386+
} else {
387+
fs::hard_link(src, dst)
388+
};
389+
link_result
390+
.or_else(|err| {
391+
log::debug!("link failed {}. falling back to fs::copy", err);
392+
fs::copy(src, dst).map(|_| ())
393+
})
394+
.chain_err(|| {
395+
format!(
396+
"failed to link or copy `{}` to `{}`",
397+
src.display(),
398+
dst.display()
399+
)
400+
})?;
401+
Ok(())
402+
}

0 commit comments

Comments
 (0)