Skip to content

Enhanced configuration discovery for cargo install and cargo uninstall commands #11775

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

Closed
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
8 changes: 4 additions & 4 deletions crates/cargo-util/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,10 @@ pub struct PathAncestors<'a> {

impl<'a> PathAncestors<'a> {
fn new(path: &'a Path, stop_root_at: Option<&Path>) -> PathAncestors<'a> {
let stop_at = env::var("__CARGO_TEST_ROOT")
.ok()
.map(PathBuf::from)
.or_else(|| stop_root_at.map(|p| p.to_path_buf()));
let stop_at = stop_root_at
.map(|p| p.to_path_buf())
.or_else(|| env::var("__CARGO_TEST_ROOT").ok().map(PathBuf::from));

PathAncestors {
current: Some(path),
//HACK: avoid reading `~/.cargo/config` when testing Cargo itself.
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
if let Some(path) = &path {
config.reload_rooted_at(path)?;
} else {
// TODO: Consider calling set_search_stop_path(home).
config.set_search_stop_path(config.home().clone().into_path_unlocked());
config.reload_rooted_at(config.home().clone().into_path_unlocked())?;
}

Expand Down
4 changes: 4 additions & 0 deletions src/bin/cargo/commands/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub fn cli() -> Command {
}

pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
// Ignore local configuration, same as `cargo install` does
config.set_search_stop_path(config.home().clone().into_path_unlocked());
config.reload_rooted_at(config.home().clone().into_path_unlocked())?;

let root = args.get_one::<String>("root").map(String::as_str);

if args.is_present_with_zero_values("package") {
Expand Down
4 changes: 1 addition & 3 deletions src/cargo/util/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,7 @@ impl Config {
/// Sets the path where ancestor config file searching will stop. The
/// given path is included, but its ancestors are not.
pub fn set_search_stop_path<P: Into<PathBuf>>(&mut self, path: P) {
let path = path.into();
debug_assert!(self.cwd.starts_with(&path));
self.search_stop_path = Some(path);
self.search_stop_path = Some(path.into());
}

/// Reloads on-disk configuration values, starting at the given path and
Expand Down
3 changes: 2 additions & 1 deletion src/doc/man/cargo-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ available.

This command operates on system or user level, not project level.
This means that the local [configuration discovery] is ignored.
Instead, the configuration discovery begins at `$CARGO_HOME/config.toml`.
Instead, this command will only consider the global configuration from the
`$CARGO_HOME/config.toml` file.
If the package is installed with `--path $PATH`, the local configuration
will be used, beginning discovery at `$PATH/.cargo/config.toml`.

Expand Down
9 changes: 9 additions & 0 deletions src/doc/man/cargo-uninstall.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ By default all binaries are removed for a crate but the `--bin` and

{{> description-install-root }}

### Configuration Discovery

This command operates on system or user level, not on project level.
This means that the local [configuration discovery] is ignored.
Instead, this command will only consider the global configuration from the
`$CARGO_HOME/config.toml` file.

[configuration discovery]: ../reference/config.html#hierarchical-structure

## OPTIONS

### Install Options
Expand Down
8 changes: 4 additions & 4 deletions src/doc/man/generated_txt/cargo-install.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ DESCRIPTION
This command operates on system or user level, not project level. This
means that the local configuration discovery
<https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure>
is ignored. Instead, the configuration discovery begins at
$CARGO_HOME/config.toml. If the package is installed with --path $PATH,
the local configuration will be used, beginning discovery at
$PATH/.cargo/config.toml.
is ignored. Instead, this command will only consider the global
configuration from the $CARGO_HOME/config.toml file. If the package is
installed with --path $PATH, the local configuration will be used,
beginning discovery at $PATH/.cargo/config.toml.

OPTIONS
Install Options
Expand Down
7 changes: 7 additions & 0 deletions src/doc/man/generated_txt/cargo-uninstall.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ DESCRIPTION

o $HOME/.cargo

Configuration Discovery
This command operates on system or user level, not on project level.
This means that the local configuration discovery
<https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure>
is ignored. Instead, this command will only consider the global
configuration from the $CARGO_HOME/config.toml file.

OPTIONS
Install Options
-p, --package spec…
Expand Down
3 changes: 2 additions & 1 deletion src/doc/src/commands/cargo-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ available.

This command operates on system or user level, not project level.
This means that the local [configuration discovery] is ignored.
Instead, the configuration discovery begins at `$CARGO_HOME/config.toml`.
Instead, this command will only consider the global configuration from the
`$CARGO_HOME/config.toml` file.
If the package is installed with `--path $PATH`, the local configuration
will be used, beginning discovery at `$PATH/.cargo/config.toml`.

Expand Down
9 changes: 9 additions & 0 deletions src/doc/src/commands/cargo-uninstall.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ The installation root is determined, in order of precedence:
- `$HOME/.cargo`


### Configuration Discovery

This command operates on system or user level, not on project level.
This means that the local [configuration discovery] is ignored.
Instead, this command will only consider the global configuration from the
`$CARGO_HOME/config.toml` file.

[configuration discovery]: ../reference/config.html#hierarchical-structure

## OPTIONS

### Install Options
Expand Down
3 changes: 2 additions & 1 deletion src/etc/man/cargo-install.1
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ available.
.SS "Configuration Discovery"
This command operates on system or user level, not project level.
This means that the local \fIconfiguration discovery\fR <https://doc.rust\-lang.org/cargo/reference/config.html#hierarchical\-structure> is ignored.
Instead, the configuration discovery begins at \fB$CARGO_HOME/config.toml\fR\&.
Instead, this command will only consider the global configuration from the
\fB$CARGO_HOME/config.toml\fR file.
If the package is installed with \fB\-\-path $PATH\fR, the local configuration
will be used, beginning discovery at \fB$PATH/.cargo/config.toml\fR\&.
.SH "OPTIONS"
Expand Down
5 changes: 5 additions & 0 deletions src/etc/man/cargo-uninstall.1
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ The installation root is determined, in order of precedence:
.RS 4
\h'-04'\(bu\h'+02'\fB$HOME/.cargo\fR
.RE
.SS "Configuration Discovery"
This command operates on system or user level, not on project level.
This means that the local \fIconfiguration discovery\fR <https://doc.rust\-lang.org/cargo/reference/config.html#hierarchical\-structure> is ignored.
Instead, this command will only consider the global configuration from the
\fB$CARGO_HOME/config.toml\fR file.
.SH "OPTIONS"
.SS "Install Options"
.sp
Expand Down
15 changes: 9 additions & 6 deletions tests/testsuite/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@ use serde::Serialize;

use cargo_test_support::cargo_process;
use cargo_test_support::git;
use cargo_test_support::install::cargo_home;
use cargo_test_support::paths;
use cargo_test_support::registry::{cksum, Package};
use cargo_test_support::{basic_manifest, project, t, ProjectBuilder};

fn setup() {
let root = paths::root();
t!(fs::create_dir(&root.join(".cargo")));
t!(fs::create_dir_all(cargo_home()));
t!(fs::write(
root.join(".cargo/config"),
r#"
cargo_home().join("config"),
&format!(
"
[source.crates-io]
replace-with = 'my-awesome-local-registry'

[source.my-awesome-local-registry]
directory = 'index'
"#
directory = '{}/index'
",
paths::root().display(),
)
));
}

Expand Down
59 changes: 51 additions & 8 deletions tests/testsuite/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,12 @@ fn install_location_precedence() {
assert_has_installed_exe(&t1, "foo");
assert_has_not_installed_exe(&t2, "foo");

cargo_process("uninstall foo --root")
.arg(&t1)
.env("CARGO_INSTALL_ROOT", &t2)
.run();
assert_has_not_installed_exe(&t1, "foo");

println!("install CARGO_INSTALL_ROOT");

cargo_process("install foo")
Expand All @@ -415,18 +421,22 @@ fn install_location_precedence() {
assert_has_installed_exe(&t2, "foo");
assert_has_not_installed_exe(&t3, "foo");

println!("install install.root");

cargo_process("install foo").run();
assert_has_installed_exe(&t3, "foo");
assert_has_not_installed_exe(&t4, "foo");

fs::remove_file(root.join(".cargo/config")).unwrap();
cargo_process("uninstall foo")
.env("CARGO_INSTALL_ROOT", &t2)
.run();
assert_has_not_installed_exe(&t2, "foo");

println!("install cargo home");
// The `install.root` config value in `root/.cargo/config` is ignored by
// `cargo install` and `cargo uninstall`, because they only consider the
// global configuration file `$CARGO_HOME/config`.
println!("install install.root");

cargo_process("install foo").run();
assert_has_installed_exe(&t4, "foo");
assert_has_not_installed_exe(&t3, "foo");

cargo_process("uninstall foo").run();
assert_has_not_installed_exe(&t4, "foo");
}

#[cargo_test]
Expand Down Expand Up @@ -1837,6 +1847,39 @@ fn install_path_config() {
.run();
}

#[cargo_test]
fn uninstall_should_ignore_local_config() {
let project = project()
.file(
".cargo/config",
r#"
[install]
root = './'
"#,
)
.file("src/main.rs", "fn main() {}")
.build();

// Will install `foo` binary to `/path/to/foo/bin/foo`
cargo_process("install --path foo").run();

assert_has_installed_exe(project.root(), "foo");

// Won't find the `foo` executable, because `cargo uninstall`
// ignores the config resolution chain of the current work directory
cargo_process("uninstall foo")
.cwd("foo")
.with_status(101)
.with_stderr_contains(
"[..]error: package ID specification `foo` did not match any packages[..]",
)
.run();

cargo_process("uninstall foo --root foo").run();

assert_has_not_installed_exe(project.root(), "foo");
}

#[cargo_test]
fn install_version_req() {
// Try using a few versionreq styles.
Expand Down