Skip to content
Draft
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
42 changes: 32 additions & 10 deletions src/cargo/ops/cargo_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ impl<'gctx> InstallablePackage<'gctx> {
self.gctx.shell().status("Installing", &self.pkg)?;

let dst = self.root.join("bin").into_path_unlocked();
let cwd = self.gctx.cwd();
let dst_abs_root = if dst.is_absolute() {
paths::normalize_path(dst.as_path())
} else {
paths::normalize_path(&cwd.join(&dst))
};

let mut td_opt = None;
let mut needs_cleanup = false;
Expand Down Expand Up @@ -458,13 +464,18 @@ impl<'gctx> InstallablePackage<'gctx> {
// Move the temporary copies into `dst` starting with new binaries.
for bin in to_install.iter() {
let src = staging_dir.path().join(bin);
let dst = dst.join(bin);
self.gctx.shell().status("Installing", dst.display())?;
let dst_rel = dst.join(bin);
let dst_abs = dst_abs_root.join(bin);
self.gctx.shell().status("Installing", dst_abs.display())?;
if !dry_run {
fs::rename(&src, &dst).with_context(|| {
format!("failed to move `{}` to `{}`", src.display(), dst.display())
fs::rename(&src, &dst_rel).with_context(|| {
format!(
"failed to move `{}` to `{}`",
src.display(),
dst_abs.display()
)
})?;
installed.bins.push(dst);
installed.bins.push(dst_rel);
successful_bins.insert(bin.to_string());
}
}
Expand All @@ -475,11 +486,16 @@ impl<'gctx> InstallablePackage<'gctx> {
let mut try_install = || -> CargoResult<()> {
for &bin in to_replace.iter() {
let src = staging_dir.path().join(bin);
let dst = dst.join(bin);
self.gctx.shell().status("Replacing", dst.display())?;
let dst_rel = dst.join(bin);
let dst_abs = dst_abs_root.join(bin);
self.gctx.shell().status("Replacing", dst_abs.display())?;
if !dry_run {
fs::rename(&src, &dst).with_context(|| {
format!("failed to move `{}` to `{}`", src.display(), dst.display())
fs::rename(&src, &dst_rel).with_context(|| {
format!(
"failed to move `{}` to `{}`",
src.display(),
dst_abs.display()
)
})?;
successful_bins.insert(bin.to_string());
}
Expand Down Expand Up @@ -777,14 +793,20 @@ pub fn install(
if installed_anything {
// Print a warning that if this directory isn't in PATH that they won't be
// able to run these commands.
let cwd = gctx.cwd();
let dst_abs = if dst.is_absolute() {
paths::normalize_path(dst.as_path())
} else {
paths::normalize_path(&cwd.join(&dst))
};
let path = gctx.get_env_os("PATH").unwrap_or_default();
let dst_in_path = env::split_paths(&path).any(|path| path == dst);

if !dst_in_path {
gctx.shell().warn(&format!(
"be sure to add `{}` to your PATH to be \
able to run the installed binaries",
dst.display()
dst_abs.display()
))?;
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/cargo/ops/common_for_install_and_uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::util::GlobalContext;
use crate::util::cache_lock::CacheLockMode;
use crate::util::context::ConfigRelativePath;
use crate::util::errors::CargoResult;
use crate::util::{FileLock, Filesystem};

Expand Down Expand Up @@ -545,11 +546,14 @@ impl InstallInfo {

/// Determines the root directory where installation is done.
pub fn resolve_root(flag: Option<&str>, gctx: &GlobalContext) -> CargoResult<Filesystem> {
let config_root = gctx.get_path("install.root")?;
let config_root = gctx
.get::<Option<ConfigRelativePath>>("install.root")?
.map(|p| p.resolve_program(gctx));

Ok(flag
.map(PathBuf::from)
.or_else(|| gctx.get_env_os("CARGO_INSTALL_ROOT").map(PathBuf::from))
.or_else(move || config_root.map(|v| v.val))
.or_else(|| config_root)
.map(Filesystem::new)
.unwrap_or_else(|| gctx.home().clone()))
}
Expand Down
73 changes: 73 additions & 0 deletions tests/testsuite/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,79 @@ fn install_location_precedence() {
assert_has_installed_exe(&t4, "foo");
}

#[cargo_test]
fn relative_install_location_without_trailing_slash() {
let p = project().file("src/main.rs", "fn main() {}").build();

let root = paths::root();
let root_t1 = root.join("t1");
let p_path = p.root().to_path_buf();
let project_t1 = p_path.join("t1");

fs::create_dir(root.join(".cargo")).unwrap();
fs::write(
root.join(".cargo/config.toml"),
r#"
[install]
root = "t1"
"#,
)
.unwrap();

let mut cmd = cargo_process("install --path .");
cmd.cwd(p.root());
cmd.with_stderr_data(str![[r#"
[INSTALLING] foo v0.0.1 ([ROOT]/foo)
[COMPILING] foo v0.0.1 ([ROOT]/foo)
[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s
[INSTALLING] [ROOT]/foo/t1/bin/foo[EXE]
[INSTALLED] package `foo v0.0.1 ([ROOT]/foo)` (executable `foo[EXE]`)
[WARNING] be sure to add `[ROOT]/foo/t1/bin` to your PATH to be able to run the installed binaries

"#]])
.run();

// NOTE: the install location is relative to the CWD, not the config file
assert_has_not_installed_exe(&root_t1, "foo");
assert_has_installed_exe(&project_t1, "foo");
}

#[cargo_test]
fn relative_install_location_with_trailing_slash() {
let p = project().file("src/main.rs", "fn main() {}").build();

let root = paths::root();
let root_t1 = root.join("t1");
let p_path = p.root().to_path_buf();
let project_t1 = p_path.join("t1");

fs::create_dir(root.join(".cargo")).unwrap();
fs::write(
root.join(".cargo/config.toml"),
r#"
[install]
root = "t1/"
"#,
)
.unwrap();

let mut cmd = cargo_process("install --path .");
cmd.cwd(p.root());
cmd.with_stderr_data(str![[r#"
[INSTALLING] foo v0.0.1 ([ROOT]/foo)
[COMPILING] foo v0.0.1 ([ROOT]/foo)
[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s
[INSTALLING] [ROOT]/t1/bin/foo[EXE]
[INSTALLED] package `foo v0.0.1 ([ROOT]/foo)` (executable `foo[EXE]`)
[WARNING] be sure to add `[ROOT]/t1/bin` to your PATH to be able to run the installed binaries

"#]])
.run();

assert_has_installed_exe(&root_t1, "foo");
assert_has_not_installed_exe(&project_t1, "foo");
}

#[cargo_test]
fn install_path() {
let p = project().file("src/main.rs", "fn main() {}").build();
Expand Down