summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/npm/managed/resolvers/local.rs18
-rw-r--r--cli/util/fs.rs68
2 files changed, 72 insertions, 14 deletions
diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs
index 055fdfb23..5362d2f61 100644
--- a/cli/npm/managed/resolvers/local.rs
+++ b/cli/npm/managed/resolvers/local.rs
@@ -17,6 +17,7 @@ use crate::cache::CACHE_PERM;
use crate::npm::cache_dir::mixed_case_package_name_decode;
use crate::util::fs::atomic_write_file;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
+use crate::util::fs::clone_dir_recursive;
use crate::util::fs::symlink_dir;
use crate::util::fs::LaxSingleProcessFsFlag;
use crate::util::progress_bar::ProgressBar;
@@ -44,8 +45,6 @@ use serde::Deserialize;
use serde::Serialize;
use crate::npm::cache_dir::mixed_case_package_name_encode;
-use crate::util::fs::copy_dir_recursive;
-use crate::util::fs::hard_link_dir_recursive;
use super::super::super::common::types_package_name;
use super::super::cache::NpmCache;
@@ -331,16 +330,9 @@ async fn sync_resolution_with_fs(
let sub_node_modules = folder_path.join("node_modules");
let package_path =
join_package_name(&sub_node_modules, &package.id.nv.name);
- fs::create_dir_all(&package_path)
- .with_context(|| format!("Creating '{}'", folder_path.display()))?;
let cache_folder =
cache.package_folder_for_name_and_version(&package.id.nv);
- if hard_link_dir_recursive(&cache_folder, &package_path).is_err() {
- // Fallback to copying the directory.
- //
- // Also handles EXDEV when when trying to hard link across volumes.
- copy_dir_recursive(&cache_folder, &package_path)?;
- }
+ clone_dir_recursive(&cache_folder, &package_path)?;
// write out a file that indicates this folder has been initialized
fs::write(initialized_file, "")?;
@@ -373,9 +365,7 @@ async fn sync_resolution_with_fs(
let sub_node_modules = destination_path.join("node_modules");
let package_path =
join_package_name(&sub_node_modules, &package.id.nv.name);
- fs::create_dir_all(&package_path).with_context(|| {
- format!("Creating '{}'", destination_path.display())
- })?;
+
let source_path = join_package_name(
&deno_local_registry_dir
.join(get_package_folder_id_folder_name(
@@ -384,7 +374,7 @@ async fn sync_resolution_with_fs(
.join("node_modules"),
&package.id.nv.name,
);
- hard_link_dir_recursive(&source_path, &package_path)?;
+ clone_dir_recursive(&source_path, &package_path)?;
// write out a file that indicates this folder has been initialized
fs::write(initialized_file, "")?;
}
diff --git a/cli/util/fs.rs b/cli/util/fs.rs
index fdc7855e6..9bdb1d014 100644
--- a/cli/util/fs.rs
+++ b/cli/util/fs.rs
@@ -492,6 +492,74 @@ pub async fn remove_dir_all_if_exists(path: &Path) -> std::io::Result<()> {
}
}
+mod clone_dir_imp {
+
+ #[cfg(target_vendor = "apple")]
+ mod apple {
+ use super::super::copy_dir_recursive;
+ use deno_core::error::AnyError;
+ use std::os::unix::ffi::OsStrExt;
+ use std::path::Path;
+ fn clonefile(from: &Path, to: &Path) -> std::io::Result<()> {
+ let from = std::ffi::CString::new(from.as_os_str().as_bytes())?;
+ let to = std::ffi::CString::new(to.as_os_str().as_bytes())?;
+ // SAFETY: `from` and `to` are valid C strings.
+ let ret = unsafe { libc::clonefile(from.as_ptr(), to.as_ptr(), 0) };
+ if ret != 0 {
+ return Err(std::io::Error::last_os_error());
+ }
+ Ok(())
+ }
+
+ pub fn clone_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> {
+ if let Some(parent) = to.parent() {
+ std::fs::create_dir_all(parent)?;
+ }
+ // Try to clone the whole directory
+ if let Err(err) = clonefile(from, to) {
+ if err.kind() != std::io::ErrorKind::AlreadyExists {
+ log::warn!(
+ "Failed to clone dir {:?} to {:?} via clonefile: {}",
+ from,
+ to,
+ err
+ );
+ }
+ // clonefile won't overwrite existing files, so if the dir exists
+ // we need to handle it recursively.
+ copy_dir_recursive(from, to)?;
+ }
+
+ Ok(())
+ }
+ }
+
+ #[cfg(target_vendor = "apple")]
+ pub(super) use apple::clone_dir_recursive;
+
+ #[cfg(not(target_vendor = "apple"))]
+ pub(super) fn clone_dir_recursive(
+ from: &std::path::Path,
+ to: &std::path::Path,
+ ) -> Result<(), deno_core::error::AnyError> {
+ if let Err(e) = super::hard_link_dir_recursive(from, to) {
+ log::debug!("Failed to hard link dir {:?} to {:?}: {}", from, to, e);
+ super::copy_dir_recursive(from, to)?;
+ }
+
+ Ok(())
+ }
+}
+
+/// Clones a directory to another directory. The exact method
+/// is not guaranteed - it may be a hardlink, copy, or other platform-specific
+/// operation.
+///
+/// Note: Does not handle symlinks.
+pub fn clone_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> {
+ clone_dir_imp::clone_dir_recursive(from, to)
+}
+
/// Copies a directory to another directory.
///
/// Note: Does not handle symlinks.