Fix error with git repo discovery and symlinks.

This commit is contained in:
Eric Huss 2020-04-30 13:45:58 -07:00
parent ba832aca75
commit 5bd74c41c4
3 changed files with 74 additions and 8 deletions

View file

@ -197,13 +197,18 @@ impl<'cfg> PathSource<'cfg> {
repo.path().display()
)
})?;
let repo_relative_path = root.strip_prefix(repo_root).chain_err(|| {
format!(
"expected git repo {} to be parent of package {}",
repo.path().display(),
root.display()
)
})?;
let repo_relative_path = match paths::strip_prefix_canonical(root, repo_root) {
Ok(p) => p,
Err(e) => {
log::warn!(
"cannot determine if path `{:?}` is in git repo `{:?}`: {:?}",
root,
repo_root,
e
);
return Ok(None);
}
};
let manifest_path = repo_relative_path.join("Cargo.toml");
if index.get_path(&manifest_path, 0).is_some() {
return Ok(Some(self.list_files_git(pkg, &repo, filter)?));

View file

@ -411,3 +411,25 @@ pub fn set_file_time_no_err<P: AsRef<Path>>(path: P, time: FileTime) {
),
}
}
/// Strips `base` from `path`.
///
/// This canonicalizes both paths before stripping. This is useful if the
/// paths are obtained in different ways, and one or the other may or may not
/// have been normalized in some way.
pub fn strip_prefix_canonical<P: AsRef<Path>>(
path: P,
base: P,
) -> Result<PathBuf, std::path::StripPrefixError> {
// Not all filesystems support canonicalize. Just ignore if it doesn't work.
let safe_canonicalize = |path: &Path| match path.canonicalize() {
Ok(p) => p,
Err(e) => {
log::warn!("cannot canonicalize {:?}: {:?}", path, e);
path.to_path_buf()
}
};
let canon_path = safe_canonicalize(path.as_ref());
let canon_base = safe_canonicalize(base.as_ref());
canon_path.strip_prefix(canon_base).map(|p| p.to_path_buf())
}

View file

@ -10,7 +10,9 @@ use cargo_test_support::install::{
};
use cargo_test_support::paths;
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_manifest, cargo_process, project, NO_SUCH_FILE_ERR_MSG};
use cargo_test_support::{
basic_manifest, cargo_process, project, symlink_supported, t, NO_SUCH_FILE_ERR_MSG,
};
fn pkg(name: &str, vers: &str) {
Package::new(name, vers)
@ -1458,3 +1460,40 @@ fn git_install_reads_workspace_manifest() {
.with_stderr_contains(" invalid type: integer `3`[..]")
.run();
}
#[cargo_test]
fn install_git_with_symlink_home() {
// Ensure that `cargo install` with a git repo is OK when CARGO_HOME is a
// symlink, and uses an build script.
if !symlink_supported() {
return;
}
let p = git::new("foo", |p| {
p.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
.file("src/main.rs", "fn main() {}")
// This triggers discover_git_and_list_files for detecting changed files.
.file("build.rs", "fn main() {}")
});
#[cfg(unix)]
use std::os::unix::fs::symlink;
#[cfg(windows)]
use std::os::windows::fs::symlink_dir as symlink;
let actual = paths::root().join("actual-home");
t!(std::fs::create_dir(&actual));
t!(symlink(&actual, paths::home().join(".cargo")));
cargo_process("install --git")
.arg(p.url().to_string())
.with_stderr(
"\
[UPDATING] git repository [..]
[INSTALLING] foo v1.0.0 [..]
[COMPILING] foo v1.0.0 [..]
[FINISHED] [..]
[INSTALLING] [..]home/.cargo/bin/foo[..]
[INSTALLED] package `foo [..]
[WARNING] be sure to add [..]
",
)
.run();
}