mirror of
https://github.com/rust-lang/cargo
synced 2024-10-31 07:46:57 +00:00
2625 lines
72 KiB
Rust
2625 lines
72 KiB
Rust
//! Tests for the `cargo install` command.
|
|
|
|
use std::fs::{self, OpenOptions};
|
|
use std::io::prelude::*;
|
|
use std::path::Path;
|
|
use std::thread;
|
|
|
|
use cargo_test_support::compare;
|
|
use cargo_test_support::cross_compile;
|
|
use cargo_test_support::git;
|
|
use cargo_test_support::registry::{self, registry_path, Package};
|
|
use cargo_test_support::{
|
|
basic_manifest, cargo_process, no_such_file_err_msg, project, project_in, symlink_supported, t,
|
|
};
|
|
use cargo_util::{ProcessBuilder, ProcessError};
|
|
|
|
use cargo_test_support::install::{
|
|
assert_has_installed_exe, assert_has_not_installed_exe, cargo_home, exe,
|
|
};
|
|
use cargo_test_support::paths::{self, CargoPathExt};
|
|
use std::env;
|
|
use std::path::PathBuf;
|
|
|
|
fn pkg(name: &str, vers: &str) {
|
|
Package::new(name, vers)
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"src/main.rs",
|
|
&format!("extern crate {}; fn main() {{}}", name),
|
|
)
|
|
.publish();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn simple() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install foo")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry [..])
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
|
|
cargo_process("uninstall foo")
|
|
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_the_same_version_twice() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install foo foo")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry [..])
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn toolchain() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install +nightly")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] invalid character `+` in package name: `+nightly`
|
|
Use `cargo +nightly install` if you meant to use the `nightly` toolchain.",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn url() {
|
|
pkg("foo", "0.0.1");
|
|
cargo_process("install https://github.com/bar/foo")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] invalid package name: `https://github.com/bar/foo`
|
|
Use `cargo install --git https://github.com/bar/foo` if you meant to install from a git repository.")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn simple_with_message_format() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install foo --message-format=json")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry [..])
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.with_json(
|
|
r#"
|
|
{
|
|
"reason": "compiler-artifact",
|
|
"package_id": "registry+https://[..]#foo@0.0.1",
|
|
"manifest_path": "[..]",
|
|
"target": {
|
|
"kind": [
|
|
"lib"
|
|
],
|
|
"crate_types": [
|
|
"lib"
|
|
],
|
|
"name": "foo",
|
|
"src_path": "[..]/foo-0.0.1/src/lib.rs",
|
|
"edition": "2015",
|
|
"doc": true,
|
|
"doctest": true,
|
|
"test": true
|
|
},
|
|
"profile": "{...}",
|
|
"features": [],
|
|
"filenames": "{...}",
|
|
"executable": null,
|
|
"fresh": false
|
|
}
|
|
|
|
{
|
|
"reason": "compiler-artifact",
|
|
"package_id": "registry+https://[..]#foo@0.0.1",
|
|
"manifest_path": "[..]",
|
|
"target": {
|
|
"kind": [
|
|
"bin"
|
|
],
|
|
"crate_types": [
|
|
"bin"
|
|
],
|
|
"name": "foo",
|
|
"src_path": "[..]/foo-0.0.1/src/main.rs",
|
|
"edition": "2015",
|
|
"doc": true,
|
|
"doctest": false,
|
|
"test": true
|
|
},
|
|
"profile": "{...}",
|
|
"features": [],
|
|
"filenames": "{...}",
|
|
"executable": "[..]",
|
|
"fresh": false
|
|
}
|
|
|
|
{"reason":"build-finished","success":true}
|
|
"#,
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn with_index() {
|
|
let registry = registry::init();
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install foo --index")
|
|
.arg(registry.index_url().as_str())
|
|
.with_stderr(&format!(
|
|
"\
|
|
[UPDATING] `{reg}` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry `{reg}`)
|
|
[INSTALLING] foo v0.0.1 (registry `{reg}`)
|
|
[COMPILING] foo v0.0.1 (registry `{reg}`)
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1 (registry `{reg}`)` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
reg = registry_path().to_str().unwrap()
|
|
))
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
|
|
cargo_process("uninstall foo")
|
|
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_pkgs() {
|
|
pkg("foo", "0.0.1");
|
|
pkg("bar", "0.0.2");
|
|
|
|
cargo_process("install foo bar baz")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`)
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] bar v0.0.2 (registry `dummy-registry`)
|
|
[ERROR] could not find `baz` in registry `[..]` with version `*`
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
|
|
[INSTALLING] bar v0.0.2
|
|
[COMPILING] bar v0.0.2
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/bar[EXE]
|
|
[INSTALLED] package `bar v0.0.2` (executable `bar[EXE]`)
|
|
[SUMMARY] Successfully installed foo, bar! Failed to install baz (see error(s) above).
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
[ERROR] some crates failed to install
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
assert_has_installed_exe(cargo_home(), "bar");
|
|
|
|
cargo_process("uninstall foo bar")
|
|
.with_stderr(
|
|
"\
|
|
[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[REMOVING] [CWD]/home/.cargo/bin/bar[EXE]
|
|
[SUMMARY] Successfully uninstalled foo, bar!
|
|
",
|
|
)
|
|
.run();
|
|
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
assert_has_not_installed_exe(cargo_home(), "bar");
|
|
}
|
|
|
|
fn path() -> Vec<PathBuf> {
|
|
env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect()
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_pkgs_path_set() {
|
|
// confirm partial failure results in 101 status code and does not have the
|
|
// '[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries'
|
|
// even if CARGO_HOME/bin is in the PATH
|
|
pkg("foo", "0.0.1");
|
|
pkg("bar", "0.0.2");
|
|
|
|
// add CARGO_HOME/bin to path
|
|
let mut path = path();
|
|
path.push(cargo_home().join("bin"));
|
|
let new_path = env::join_paths(path).unwrap();
|
|
cargo_process("install foo bar baz")
|
|
.env("PATH", new_path)
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`)
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] bar v0.0.2 (registry `dummy-registry`)
|
|
[ERROR] could not find `baz` in registry `[..]` with version `*`
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
|
|
[INSTALLING] bar v0.0.2
|
|
[COMPILING] bar v0.0.2
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/bar[EXE]
|
|
[INSTALLED] package `bar v0.0.2` (executable `bar[EXE]`)
|
|
[SUMMARY] Successfully installed foo, bar! Failed to install baz (see error(s) above).
|
|
[ERROR] some crates failed to install
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
assert_has_installed_exe(cargo_home(), "bar");
|
|
|
|
cargo_process("uninstall foo bar")
|
|
.with_stderr(
|
|
"\
|
|
[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[REMOVING] [CWD]/home/.cargo/bin/bar[EXE]
|
|
[SUMMARY] Successfully uninstalled foo, bar!
|
|
",
|
|
)
|
|
.run();
|
|
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
assert_has_not_installed_exe(cargo_home(), "bar");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn pick_max_version() {
|
|
pkg("foo", "0.1.0");
|
|
pkg("foo", "0.2.0");
|
|
pkg("foo", "0.2.1");
|
|
pkg("foo", "0.2.1-pre.1");
|
|
pkg("foo", "0.3.0-pre.2");
|
|
|
|
cargo_process("install foo")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.2.1 (registry [..])
|
|
[INSTALLING] foo v0.2.1
|
|
[COMPILING] foo v0.2.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.2.1` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn installs_beta_version_by_explicit_name_from_git() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.3.0-beta.1"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.arg("foo")
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn missing() {
|
|
pkg("foo", "0.0.1");
|
|
cargo_process("install bar")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] [..] index
|
|
[ERROR] could not find `bar` in registry `[..]` with version `*`
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn missing_current_working_directory() {
|
|
cargo_process("install .")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"error: To install the binaries for the package in current working \
|
|
directory use `cargo install --path .`. \n\
|
|
Use `cargo build` if you want to simply build the package.",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn bad_version() {
|
|
pkg("foo", "0.0.1");
|
|
cargo_process("install foo --version=0.2.0")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] [..] index
|
|
[ERROR] could not find `foo` in registry `[..]` with version `=0.2.0`
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn bad_paths() {
|
|
cargo_process("install")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] `[CWD]` is not a crate root; specify a crate to install [..]")
|
|
.run();
|
|
|
|
cargo_process("install --path .")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] `[CWD]` does not contain a Cargo.toml file[..]")
|
|
.run();
|
|
|
|
let toml = paths::root().join("Cargo.toml");
|
|
fs::write(toml, "").unwrap();
|
|
cargo_process("install --path Cargo.toml")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] `[CWD]/Cargo.toml` is not a directory[..]")
|
|
.run();
|
|
|
|
cargo_process("install --path .")
|
|
.with_status(101)
|
|
.with_stderr_contains("[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_location_precedence() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
let root = paths::root();
|
|
let t1 = root.join("t1");
|
|
let t2 = root.join("t2");
|
|
let t3 = root.join("t3");
|
|
let t4 = cargo_home();
|
|
|
|
fs::create_dir(root.join(".cargo")).unwrap();
|
|
fs::write(
|
|
root.join(".cargo/config.toml"),
|
|
&format!(
|
|
"[install]
|
|
root = '{}'
|
|
",
|
|
t3.display()
|
|
),
|
|
)
|
|
.unwrap();
|
|
|
|
println!("install --root");
|
|
|
|
cargo_process("install foo --root")
|
|
.arg(&t1)
|
|
.env("CARGO_INSTALL_ROOT", &t2)
|
|
.run();
|
|
assert_has_installed_exe(&t1, "foo");
|
|
assert_has_not_installed_exe(&t2, "foo");
|
|
|
|
println!("install CARGO_INSTALL_ROOT");
|
|
|
|
cargo_process("install foo")
|
|
.env("CARGO_INSTALL_ROOT", &t2)
|
|
.run();
|
|
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.toml")).unwrap();
|
|
|
|
println!("install cargo home");
|
|
|
|
cargo_process("install foo").run();
|
|
assert_has_installed_exe(&t4, "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_path() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
|
|
cargo_process("install --path").arg(p.root()).run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
// path-style installs force a reinstall
|
|
p.cargo("install --path .")
|
|
.with_stderr(
|
|
"\
|
|
[INSTALLING] foo v0.0.1 [..]
|
|
[FINISHED] release [..]
|
|
[REPLACING] [..]/.cargo/bin/foo[EXE]
|
|
[REPLACED] package `foo v0.0.1 [..]` with `foo v0.0.1 [..]` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_target_dir() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
|
|
p.cargo("install --target-dir td_test")
|
|
.with_stderr(
|
|
"\
|
|
[WARNING] Using `cargo install` [..]
|
|
[INSTALLING] foo v0.0.1 [..]
|
|
[COMPILING] foo v0.0.1 [..]
|
|
[FINISHED] release [..]
|
|
[INSTALLING] [..]foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1 [..]foo[..]` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add [..]
|
|
",
|
|
)
|
|
.run();
|
|
|
|
let mut path = p.root();
|
|
path.push("td_test");
|
|
assert!(path.exists());
|
|
|
|
#[cfg(not(windows))]
|
|
path.push("release/foo");
|
|
#[cfg(windows)]
|
|
path.push("release/foo.exe");
|
|
assert!(path.exists());
|
|
}
|
|
|
|
#[cargo_test]
|
|
#[cfg(target_os = "linux")]
|
|
fn install_path_with_lowercase_cargo_toml() {
|
|
let toml = paths::root().join("cargo.toml");
|
|
fs::write(toml, "").unwrap();
|
|
|
|
cargo_process("install --path .")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] `[CWD]` does not contain a Cargo.toml file, \
|
|
but found cargo.toml please try to rename it to Cargo.toml. --path must point to a directory containing a Cargo.toml file.
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_relative_path_outside_current_ws() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[workspace]
|
|
members = ["baz"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"baz/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "baz"
|
|
version = "0.1.0"
|
|
authors = []
|
|
edition = "2021"
|
|
|
|
[dependencies]
|
|
foo = "1"
|
|
"#,
|
|
)
|
|
.file("baz/src/lib.rs", "")
|
|
.build();
|
|
|
|
let _bin_project = project_in("bar")
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("install --path ../bar/foo")
|
|
.with_stderr(&format!(
|
|
"\
|
|
[INSTALLING] foo v0.0.1 ([..]/bar/foo)
|
|
[COMPILING] foo v0.0.1 ([..]/bar/foo)
|
|
[FINISHED] release [..]
|
|
[INSTALLING] {home}/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1 ([..]/bar/foo)` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add [..]
|
|
",
|
|
home = cargo_home().display(),
|
|
))
|
|
.run();
|
|
|
|
// Validate the workspace error message to display available targets.
|
|
p.cargo("install --path ../bar/foo --bin")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] \"--bin\" takes one argument.
|
|
Available binaries:
|
|
foo
|
|
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_packages_containing_binaries() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("a/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("a/src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
let git_url = p.url().to_string();
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.with_status(101)
|
|
.with_stderr(format!(
|
|
"\
|
|
[UPDATING] git repository [..]
|
|
[ERROR] multiple packages with binaries found: bar, foo. \
|
|
When installing a git repository, cargo will always search the entire repo for any Cargo.toml.
|
|
Please specify a package, e.g. `cargo install --git {git_url} bar`.
|
|
"
|
|
))
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_packages_matching_example() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/lib.rs", "")
|
|
.file("examples/ex1.rs", "fn main() {}")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("bar/src/lib.rs", "")
|
|
.file("bar/examples/ex1.rs", "fn main() {}")
|
|
.build();
|
|
|
|
let git_url = p.url().to_string();
|
|
cargo_process("install --example ex1 --git")
|
|
.arg(p.url().to_string())
|
|
.with_status(101)
|
|
.with_stderr(format!(
|
|
"\
|
|
[UPDATING] git repository [..]
|
|
[ERROR] multiple packages with examples found: bar, foo. \
|
|
When installing a git repository, cargo will always search the entire repo for any Cargo.toml.
|
|
Please specify a package, e.g. `cargo install --git {git_url} bar`."
|
|
))
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_binaries_deep_select_uses_package_name() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0"))
|
|
.file("bar/baz/src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.arg("baz")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_binaries_in_selected_package_installs_all() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/lib.rs", "")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("bar/src/bin/bin1.rs", "fn main() {}")
|
|
.file("bar/src/bin/bin2.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.arg("bar")
|
|
.run();
|
|
|
|
let cargo_home = cargo_home();
|
|
assert_has_installed_exe(&cargo_home, "bin1");
|
|
assert_has_installed_exe(&cargo_home, "bin2");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_binaries_in_selected_package_with_bin_option_installs_only_one() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/lib.rs", "")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("bar/src/bin/bin1.rs", "fn main() {}")
|
|
.file("bar/src/bin/bin2.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --bin bin1 --git")
|
|
.arg(p.url().to_string())
|
|
.arg("bar")
|
|
.run();
|
|
|
|
let cargo_home = cargo_home();
|
|
assert_has_installed_exe(&cargo_home, "bin1");
|
|
assert_has_not_installed_exe(&cargo_home, "bin2");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_crates_select() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("a/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("a/src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.arg("foo")
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
assert_has_not_installed_exe(cargo_home(), "bar");
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.arg("bar")
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "bar");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_crates_git_all() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["bin1", "bin2"]
|
|
"#,
|
|
)
|
|
.file("bin1/Cargo.toml", &basic_manifest("bin1", "0.1.0"))
|
|
.file("bin2/Cargo.toml", &basic_manifest("bin2", "0.1.0"))
|
|
.file(
|
|
"bin1/src/main.rs",
|
|
r#"fn main() { println!("Hello, world!"); }"#,
|
|
)
|
|
.file(
|
|
"bin2/src/main.rs",
|
|
r#"fn main() { println!("Hello, world!"); }"#,
|
|
)
|
|
.build();
|
|
|
|
cargo_process(&format!("install --git {} bin1 bin2", p.url().to_string())).run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_crates_auto_binaries() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
bar = { path = "a" }
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "extern crate bar; fn main() {}")
|
|
.file("a/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("a/src/lib.rs", "")
|
|
.build();
|
|
|
|
cargo_process("install --path").arg(p.root()).run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multiple_crates_auto_examples() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
bar = { path = "a" }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "extern crate bar;")
|
|
.file(
|
|
"examples/foo.rs",
|
|
"
|
|
extern crate bar;
|
|
extern crate foo;
|
|
fn main() {}
|
|
",
|
|
)
|
|
.file("a/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("a/src/lib.rs", "")
|
|
.build();
|
|
|
|
cargo_process("install --path")
|
|
.arg(p.root())
|
|
.arg("--example=foo")
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn no_binaries_or_examples() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
bar = { path = "a" }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file("a/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("a/src/lib.rs", "")
|
|
.build();
|
|
|
|
cargo_process("install --path")
|
|
.arg(p.root())
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] no packages found with binaries or examples")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn no_binaries() {
|
|
let p = project()
|
|
.file("src/lib.rs", "")
|
|
.file("examples/foo.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --path")
|
|
.arg(p.root())
|
|
.arg("foo")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] there is nothing to install in `foo v0.0.1 ([..])`, because it has no binaries[..]
|
|
[..]
|
|
To use a library crate, add it as a dependency to a Cargo project with `cargo add`.",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn examples() {
|
|
let p = project()
|
|
.file("src/lib.rs", "")
|
|
.file("examples/foo.rs", "extern crate foo; fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --path")
|
|
.arg(p.root())
|
|
.arg("--example=foo")
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_force() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
|
|
cargo_process("install --path").arg(p.root()).run();
|
|
|
|
let p = project()
|
|
.at("foo2")
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.2.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --force --path")
|
|
.arg(p.root())
|
|
.with_stderr(
|
|
"\
|
|
[INSTALLING] foo v0.2.0 ([..])
|
|
[COMPILING] foo v0.2.0 ([..])
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[REPLACING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[REPLACED] package `foo v0.0.1 ([..]/foo)` with `foo v0.2.0 ([..]/foo2)` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
|
|
cargo_process("install --list")
|
|
.with_stdout(
|
|
"\
|
|
foo v0.2.0 ([..]):
|
|
foo[..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_force_partial_overlap() {
|
|
let p = project()
|
|
.file("src/bin/foo-bin1.rs", "fn main() {}")
|
|
.file("src/bin/foo-bin2.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --path").arg(p.root()).run();
|
|
|
|
let p = project()
|
|
.at("foo2")
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.2.0"))
|
|
.file("src/bin/foo-bin2.rs", "fn main() {}")
|
|
.file("src/bin/foo-bin3.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --force --path")
|
|
.arg(p.root())
|
|
.with_stderr(
|
|
"\
|
|
[INSTALLING] foo v0.2.0 ([..])
|
|
[COMPILING] foo v0.2.0 ([..])
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo-bin3[EXE]
|
|
[REPLACING] [CWD]/home/.cargo/bin/foo-bin2[EXE]
|
|
[REMOVING] executable `[..]/bin/foo-bin1[EXE]` from previous version foo v0.0.1 [..]
|
|
[INSTALLED] package `foo v0.2.0 ([..]/foo2)` (executable `foo-bin3[EXE]`)
|
|
[REPLACED] package `foo v0.0.1 ([..]/foo)` with `foo v0.2.0 ([..]/foo2)` (executable `foo-bin2[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
|
|
cargo_process("install --list")
|
|
.with_stdout(
|
|
"\
|
|
foo v0.2.0 ([..]):
|
|
foo-bin2[..]
|
|
foo-bin3[..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_force_bin() {
|
|
let p = project()
|
|
.file("src/bin/foo-bin1.rs", "fn main() {}")
|
|
.file("src/bin/foo-bin2.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --path").arg(p.root()).run();
|
|
|
|
let p = project()
|
|
.at("foo2")
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.2.0"))
|
|
.file("src/bin/foo-bin1.rs", "fn main() {}")
|
|
.file("src/bin/foo-bin2.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --force --bin foo-bin2 --path")
|
|
.arg(p.root())
|
|
.with_stderr(
|
|
"\
|
|
[INSTALLING] foo v0.2.0 ([..])
|
|
[COMPILING] foo v0.2.0 ([..])
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[REPLACING] [CWD]/home/.cargo/bin/foo-bin2[EXE]
|
|
[REPLACED] package `foo v0.0.1 ([..]/foo)` with `foo v0.2.0 ([..]/foo2)` (executable `foo-bin2[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
|
|
cargo_process("install --list")
|
|
.with_stdout(
|
|
"\
|
|
foo v0.0.1 ([..]):
|
|
foo-bin1[..]
|
|
foo v0.2.0 ([..]):
|
|
foo-bin2[..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn compile_failure() {
|
|
let p = project().file("src/main.rs", "").build();
|
|
|
|
cargo_process("install --path")
|
|
.arg(p.root())
|
|
.with_status(101)
|
|
.with_stderr_contains(
|
|
"\
|
|
[ERROR] could not compile `foo` (bin \"foo\") due to 1 previous error
|
|
[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be \
|
|
found at `[..]target`.\nTo reuse those artifacts with a future compilation, \
|
|
set the environment variable `CARGO_TARGET_DIR` to that path.
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn git_repo() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
// Use `--locked` to test that we don't even try to write a lock file.
|
|
cargo_process("install --locked --git")
|
|
.arg(p.url().to_string())
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] git repository `[..]`
|
|
[WARNING] no Cargo.lock file published in foo v0.1.0 ([..])
|
|
[INSTALLING] foo v0.1.0 ([..])
|
|
[COMPILING] foo v0.1.0 ([..])
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.1.0 ([..]/foo#[..])` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
#[cfg(target_os = "linux")]
|
|
fn git_repo_with_lowercase_cargo_toml() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] git repository [..]
|
|
[ERROR] Could not find Cargo.toml in `[..]`, but found cargo.toml please try to rename it to Cargo.toml
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn list() {
|
|
pkg("foo", "0.0.1");
|
|
pkg("bar", "0.2.1");
|
|
pkg("bar", "0.2.2");
|
|
|
|
cargo_process("install --list").with_stdout("").run();
|
|
|
|
cargo_process("install bar --version =0.2.1").run();
|
|
cargo_process("install foo").run();
|
|
cargo_process("install --list")
|
|
.with_stdout(
|
|
"\
|
|
bar v0.2.1:
|
|
bar[..]
|
|
foo v0.0.1:
|
|
foo[..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn list_error() {
|
|
pkg("foo", "0.0.1");
|
|
cargo_process("install foo").run();
|
|
cargo_process("install --list")
|
|
.with_stdout(
|
|
"\
|
|
foo v0.0.1:
|
|
foo[..]
|
|
",
|
|
)
|
|
.run();
|
|
let mut worldfile_path = cargo_home();
|
|
worldfile_path.push(".crates.toml");
|
|
let mut worldfile = OpenOptions::new()
|
|
.write(true)
|
|
.open(worldfile_path)
|
|
.expect(".crates.toml should be there");
|
|
worldfile.write_all(b"\x00").unwrap();
|
|
drop(worldfile);
|
|
cargo_process("install --list --verbose")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] failed to parse crate metadata at `[..]`
|
|
|
|
Caused by:
|
|
invalid TOML found for metadata
|
|
|
|
Caused by:
|
|
TOML parse error at line 1, column 1
|
|
|
|
|
1 | [..]
|
|
| ^
|
|
invalid key
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_pkg_does_not_exist() {
|
|
cargo_process("uninstall foo")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] package ID specification `foo` did not match any packages")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_bin_does_not_exist() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install foo").run();
|
|
cargo_process("uninstall foo --bin=bar")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] binary `bar[..]` not installed as part of `foo v0.0.1`")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_piecemeal() {
|
|
let p = project()
|
|
.file("src/bin/foo.rs", "fn main() {}")
|
|
.file("src/bin/bar.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --path").arg(p.root()).run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
assert_has_installed_exe(cargo_home(), "bar");
|
|
|
|
cargo_process("uninstall foo --bin=bar")
|
|
.with_stderr("[REMOVING] [..]bar[..]")
|
|
.run();
|
|
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
assert_has_not_installed_exe(cargo_home(), "bar");
|
|
|
|
cargo_process("uninstall foo --bin=foo")
|
|
.with_stderr("[REMOVING] [..]foo[..]")
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
|
|
cargo_process("uninstall foo")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] package ID specification `foo` did not match any packages")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn subcommand_works_out_of_the_box() {
|
|
Package::new("cargo-foo", "1.0.0")
|
|
.file("src/main.rs", r#"fn main() { println!("bar"); }"#)
|
|
.publish();
|
|
cargo_process("install cargo-foo").run();
|
|
cargo_process("foo").with_stdout("bar\n").run();
|
|
cargo_process("--list")
|
|
.with_stdout_contains(" foo\n")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn installs_from_cwd_by_default() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
|
|
p.cargo("install")
|
|
.with_stderr_contains(
|
|
"warning: Using `cargo install` to install the binaries from the \
|
|
package in current working directory is deprecated, \
|
|
use `cargo install --path .` instead. \
|
|
Use `cargo build` if you want to simply build the package.",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn installs_from_cwd_with_2018_warnings() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
edition = "2018"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("install")
|
|
.with_status(101)
|
|
.with_stderr_contains(
|
|
"error: Using `cargo install` to install the binaries from the \
|
|
package in current working directory is no longer supported, \
|
|
use `cargo install --path .` instead. \
|
|
Use `cargo build` if you want to simply build the package.",
|
|
)
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_cwd() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
p.cargo("install --path .")
|
|
.with_stderr(&format!(
|
|
"\
|
|
[INSTALLING] foo v0.0.1 ([CWD])
|
|
[COMPILING] foo v0.0.1 ([CWD])
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] {home}/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1 ([..]/foo)` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `{home}/bin` to your PATH to be able to run the installed binaries",
|
|
home = cargo_home().display(),
|
|
))
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
|
|
p.cargo("uninstall")
|
|
.with_stdout("")
|
|
.with_stderr(&format!(
|
|
"[REMOVING] {home}/bin/foo[EXE]",
|
|
home = cargo_home().display()
|
|
))
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_cwd_not_installed() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
p.cargo("uninstall")
|
|
.with_status(101)
|
|
.with_stdout("")
|
|
.with_stderr("error: package `foo v0.0.1 ([CWD])` is not installed")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_cwd_no_project() {
|
|
cargo_process("uninstall")
|
|
.with_status(101)
|
|
.with_stdout("")
|
|
.with_stderr(format!(
|
|
"\
|
|
[ERROR] failed to read `[CWD]/Cargo.toml`
|
|
|
|
Caused by:
|
|
{err_msg}",
|
|
err_msg = no_such_file_err_msg(),
|
|
))
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn do_not_rebuilds_on_local_install() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
|
|
p.cargo("build --release").run();
|
|
cargo_process("install --path")
|
|
.arg(p.root())
|
|
.with_stderr(
|
|
"\
|
|
[INSTALLING] [..]
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [..]
|
|
[INSTALLED] package `foo v0.0.1 ([..]/foo)` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
|
|
assert!(p.build_dir().exists());
|
|
assert!(p.release_bin("foo").exists());
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn reports_unsuccessful_subcommand_result() {
|
|
Package::new("cargo-fail", "1.0.0")
|
|
.file("src/main.rs", "fn main() { panic!(); }")
|
|
.publish();
|
|
cargo_process("install cargo-fail").run();
|
|
cargo_process("--list")
|
|
.with_stdout_contains(" fail\n")
|
|
.run();
|
|
cargo_process("fail")
|
|
.with_status(101)
|
|
.with_stderr_contains("thread '[..]' panicked at [..]src/main.rs:1:[..]")
|
|
.with_stderr_contains("[..]explicit panic[..]")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn git_with_lockfile() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
bar = { path = "bar" }
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("bar/src/lib.rs", "fn main() {}")
|
|
.file(
|
|
"Cargo.lock",
|
|
r#"
|
|
[[package]]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
dependencies = [ "bar 0.1.0" ]
|
|
|
|
[[package]]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn q_silences_warnings() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
|
|
cargo_process("install -q --path")
|
|
.arg(p.root())
|
|
.with_stderr("")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn readonly_dir() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
let root = paths::root();
|
|
let dir = &root.join("readonly");
|
|
fs::create_dir(root.join("readonly")).unwrap();
|
|
let mut perms = fs::metadata(dir).unwrap().permissions();
|
|
perms.set_readonly(true);
|
|
fs::set_permissions(dir, perms).unwrap();
|
|
|
|
cargo_process("install foo").cwd(dir).run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn use_path_workspace() {
|
|
Package::new("foo", "1.0.0").publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[workspace]
|
|
members = ["baz"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"baz/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "baz"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
foo = "1"
|
|
"#,
|
|
)
|
|
.file("baz/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("build").run();
|
|
let lock = p.read_lockfile();
|
|
p.cargo("install").run();
|
|
let lock2 = p.read_lockfile();
|
|
assert_eq!(lock, lock2, "different lockfiles");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn path_install_workspace_root_despite_default_members() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "ws-root"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[workspace]
|
|
members = ["ws-member"]
|
|
default-members = ["ws-member"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"ws-member/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "ws-member"
|
|
version = "0.1.0"
|
|
authors = []
|
|
"#,
|
|
)
|
|
.file("ws-member/src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("install --path")
|
|
.arg(p.root())
|
|
.arg("ws-root")
|
|
.with_stderr_contains(
|
|
"[INSTALLED] package `ws-root v0.1.0 ([..])` (executable `ws-root[EXE]`)",
|
|
)
|
|
// Particularly avoid "Installed package `ws-root v0.1.0 ([..]])` (executable `ws-member`)":
|
|
.with_stderr_does_not_contain("ws-member")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn git_install_workspace_root_despite_default_members() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "ws-root"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[workspace]
|
|
members = ["ws-member"]
|
|
default-members = ["ws-member"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"ws-member/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "ws-member"
|
|
version = "0.1.0"
|
|
authors = []
|
|
"#,
|
|
)
|
|
.file("ws-member/src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.arg("ws-root")
|
|
.with_stderr_contains(
|
|
"[INSTALLED] package `ws-root v0.1.0 ([..])` (executable `ws-root[EXE]`)",
|
|
)
|
|
// Particularly avoid "Installed package `ws-root v0.1.0 ([..]])` (executable `ws-member`)":
|
|
.with_stderr_does_not_contain("ws-member")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn dev_dependencies_no_check() {
|
|
Package::new("foo", "1.0.0").publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dev-dependencies]
|
|
baz = "1.0.0"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("build")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..] no matching package named `baz` found")
|
|
.run();
|
|
p.cargo("install").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn dev_dependencies_lock_file_untouched() {
|
|
Package::new("foo", "1.0.0").publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dev-dependencies]
|
|
bar = { path = "a" }
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("a/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("a/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("build").run();
|
|
let lock = p.read_lockfile();
|
|
p.cargo("install").run();
|
|
let lock2 = p.read_lockfile();
|
|
assert!(lock == lock2, "different lockfiles");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_target_native() {
|
|
pkg("foo", "0.1.0");
|
|
|
|
cargo_process("install foo --target")
|
|
.arg(cargo_test_support::rustc_host())
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_target_foreign() {
|
|
if cross_compile::disabled() {
|
|
return;
|
|
}
|
|
|
|
pkg("foo", "0.1.0");
|
|
|
|
cargo_process("install foo --target")
|
|
.arg(cross_compile::alternate())
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn vers_precise() {
|
|
pkg("foo", "0.1.1");
|
|
pkg("foo", "0.1.2");
|
|
|
|
cargo_process("install foo --vers 0.1.1")
|
|
.with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn version_precise() {
|
|
pkg("foo", "0.1.1");
|
|
pkg("foo", "0.1.2");
|
|
|
|
cargo_process("install foo --version 0.1.1")
|
|
.with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn inline_version_precise() {
|
|
pkg("foo", "0.1.1");
|
|
pkg("foo", "0.1.2");
|
|
|
|
cargo_process("install foo@0.1.1")
|
|
.with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn inline_version_multiple() {
|
|
pkg("foo", "0.1.0");
|
|
pkg("foo", "0.1.1");
|
|
pkg("foo", "0.1.2");
|
|
pkg("bar", "0.2.0");
|
|
pkg("bar", "0.2.1");
|
|
pkg("bar", "0.2.2");
|
|
|
|
cargo_process("install foo@0.1.1 bar@0.2.1")
|
|
.with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])")
|
|
.with_stderr_contains("[DOWNLOADED] bar v0.2.1 (registry [..])")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn inline_version_without_name() {
|
|
pkg("foo", "0.1.1");
|
|
pkg("foo", "0.1.2");
|
|
|
|
cargo_process("install @0.1.1")
|
|
.with_status(1)
|
|
.with_stderr(
|
|
"error: invalid value '@0.1.1' for '[CRATE[@<VER>]]...': missing crate name before '@'
|
|
|
|
For more information, try '--help'.
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn inline_and_explicit_version() {
|
|
pkg("foo", "0.1.1");
|
|
pkg("foo", "0.1.2");
|
|
|
|
cargo_process("install foo@0.1.1 --version 0.1.1")
|
|
.with_status(101)
|
|
.with_stderr("error: cannot specify both `@<VERSION>` and `--version <VERSION>`")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn not_both_vers_and_version() {
|
|
pkg("foo", "0.1.1");
|
|
pkg("foo", "0.1.2");
|
|
|
|
cargo_process("install foo --version 0.1.1 --vers 0.1.2")
|
|
.with_status(1)
|
|
.with_stderr_contains(
|
|
"\
|
|
[ERROR] the argument '--version <VERSION>' cannot be used multiple times
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn test_install_git_cannot_be_a_base_url() {
|
|
cargo_process("install --git github.com:rust-lang/rustfmt.git")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] invalid url `github.com:rust-lang/rustfmt.git`: cannot-be-a-base-URLs are not supported",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_multiple_and_specifying_bin() {
|
|
cargo_process("uninstall foo bar --bin baz")
|
|
.with_status(101)
|
|
.with_stderr("\
|
|
[ERROR] A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant.")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_with_empty_package_option() {
|
|
cargo_process("uninstall -p")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] \"--package <SPEC>\" requires a SPEC format value.
|
|
Run `cargo help pkgid` for more information about SPEC format.
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_multiple_and_some_pkg_does_not_exist() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install foo").run();
|
|
|
|
cargo_process("uninstall foo bar")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
error: package ID specification `bar` did not match any packages
|
|
[SUMMARY] Successfully uninstalled foo! Failed to uninstall bar (see error(s) above).
|
|
error: some packages failed to uninstall
|
|
",
|
|
)
|
|
.run();
|
|
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
assert_has_not_installed_exe(cargo_home(), "bar");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn custom_target_dir_for_git_source() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.run();
|
|
assert!(!paths::root().join("target/release").is_dir());
|
|
|
|
cargo_process("install --force --git")
|
|
.arg(p.url().to_string())
|
|
.env("CARGO_TARGET_DIR", "target")
|
|
.run();
|
|
assert!(paths::root().join("target/release").is_dir());
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_respects_lock_file() {
|
|
// `cargo install` now requires --locked to use a Cargo.lock.
|
|
Package::new("bar", "0.1.0").publish();
|
|
Package::new("bar", "0.1.1")
|
|
.file("src/lib.rs", "not rust")
|
|
.publish();
|
|
Package::new("foo", "0.1.0")
|
|
.dep("bar", "0.1")
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"src/main.rs",
|
|
"extern crate foo; extern crate bar; fn main() {}",
|
|
)
|
|
.file(
|
|
"Cargo.lock",
|
|
r#"
|
|
[[package]]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
|
[[package]]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
dependencies = [
|
|
"bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
]
|
|
"#,
|
|
)
|
|
.publish();
|
|
|
|
cargo_process("install foo")
|
|
.with_stderr_contains("[..]not rust[..]")
|
|
.with_status(101)
|
|
.run();
|
|
cargo_process("install --locked foo").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_path_respects_lock_file() {
|
|
// --path version of install_path_respects_lock_file, --locked is required
|
|
// to use Cargo.lock.
|
|
Package::new("bar", "0.1.0").publish();
|
|
Package::new("bar", "0.1.1")
|
|
.file("src/lib.rs", "not rust")
|
|
.publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bar = "0.1"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "extern crate bar; fn main() {}")
|
|
.file(
|
|
"Cargo.lock",
|
|
r#"
|
|
[[package]]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
|
[[package]]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
dependencies = [
|
|
"bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
]
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("install --path .")
|
|
.with_stderr_contains("[..]not rust[..]")
|
|
.with_status(101)
|
|
.run();
|
|
p.cargo("install --path . --locked").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn lock_file_path_deps_ok() {
|
|
Package::new("bar", "0.1.0").publish();
|
|
|
|
Package::new("foo", "0.1.0")
|
|
.dep("bar", "0.1")
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"src/main.rs",
|
|
"extern crate foo; extern crate bar; fn main() {}",
|
|
)
|
|
.file(
|
|
"Cargo.lock",
|
|
r#"
|
|
[[package]]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
|
|
[[package]]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
dependencies = [
|
|
"bar 0.1.0",
|
|
]
|
|
"#,
|
|
)
|
|
.publish();
|
|
|
|
cargo_process("install foo").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_empty_argument() {
|
|
// Bug 5229
|
|
cargo_process("install")
|
|
.arg("")
|
|
.with_status(1)
|
|
.with_stderr_contains(
|
|
"[ERROR] invalid value '' for '[CRATE[@<VER>]]...': crate name is empty",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn git_repo_replace() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
let repo = git2::Repository::open(&p.root()).unwrap();
|
|
let old_rev = repo.revparse_single("HEAD").unwrap().id();
|
|
cargo_process("install --git")
|
|
.arg(p.url().to_string())
|
|
.run();
|
|
git::commit(&repo);
|
|
let new_rev = repo.revparse_single("HEAD").unwrap().id();
|
|
let mut path = paths::home();
|
|
path.push(".cargo/.crates.toml");
|
|
|
|
assert_ne!(old_rev, new_rev);
|
|
assert!(fs::read_to_string(path.clone())
|
|
.unwrap()
|
|
.contains(&format!("{}", old_rev)));
|
|
cargo_process("install --force --git")
|
|
.arg(p.url().to_string())
|
|
.run();
|
|
assert!(fs::read_to_string(path)
|
|
.unwrap()
|
|
.contains(&format!("{}", new_rev)));
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn workspace_uses_workspace_target_dir() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[workspace]
|
|
|
|
[dependencies]
|
|
bar = { path = 'bar' }
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
|
|
.file("bar/src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("build --release").cwd("bar").run();
|
|
cargo_process("install --path")
|
|
.arg(p.root().join("bar"))
|
|
.with_stderr(
|
|
"[INSTALLING] [..]
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [..]
|
|
[INSTALLED] package `bar v0.1.0 ([..]/bar)` (executable `bar[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_ignores_local_cargo_config() {
|
|
pkg("bar", "0.0.1");
|
|
|
|
let p = project()
|
|
.file(
|
|
".cargo/config.toml",
|
|
r#"
|
|
[build]
|
|
target = "non-existing-target"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("install bar").run();
|
|
assert_has_installed_exe(cargo_home(), "bar");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_ignores_unstable_table_in_local_cargo_config() {
|
|
pkg("bar", "0.0.1");
|
|
|
|
let p = project()
|
|
.file(
|
|
".cargo/config.toml",
|
|
r#"
|
|
[unstable]
|
|
build-std = ["core"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("install bar")
|
|
.masquerade_as_nightly_cargo(&["build-std"])
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "bar");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_global_cargo_config() {
|
|
pkg("bar", "0.0.1");
|
|
|
|
let config = cargo_home().join("config");
|
|
let mut toml = fs::read_to_string(&config).unwrap_or_default();
|
|
|
|
toml.push_str(
|
|
r#"
|
|
[build]
|
|
target = 'nonexistent'
|
|
"#,
|
|
);
|
|
fs::write(&config, toml).unwrap();
|
|
|
|
cargo_process("install bar")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]--target nonexistent[..]")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_path_config() {
|
|
project()
|
|
.file(
|
|
".cargo/config.toml",
|
|
r#"
|
|
[build]
|
|
target = 'nonexistent'
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
cargo_process("install --path foo")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]--target nonexistent[..]")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_version_req() {
|
|
// Try using a few versionreq styles.
|
|
pkg("foo", "0.0.3");
|
|
pkg("foo", "1.0.4");
|
|
pkg("foo", "1.0.5");
|
|
cargo_process("install foo --version=*")
|
|
.with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]")
|
|
.with_stderr_contains("[INSTALLING] foo v1.0.5")
|
|
.run();
|
|
cargo_process("uninstall foo").run();
|
|
cargo_process("install foo --version=^1.0")
|
|
.with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]")
|
|
.with_stderr_contains("[INSTALLING] foo v1.0.5")
|
|
.run();
|
|
cargo_process("uninstall foo").run();
|
|
cargo_process("install foo --version=0.0.*")
|
|
.with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]")
|
|
.with_stderr_contains("[INSTALLING] foo v0.0.3")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn git_install_reads_workspace_manifest() {
|
|
let p = git::repo(&paths::root().join("foo"))
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["bin1"]
|
|
|
|
[profile.release]
|
|
incremental = 3
|
|
"#,
|
|
)
|
|
.file("bin1/Cargo.toml", &basic_manifest("bin1", "0.1.0"))
|
|
.file(
|
|
"bin1/src/main.rs",
|
|
r#"fn main() { println!("Hello, world!"); }"#,
|
|
)
|
|
.build();
|
|
|
|
cargo_process(&format!("install --git {}", p.url().to_string()))
|
|
.with_status(101)
|
|
.with_stderr_contains("error: 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();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_yanked_cargo_package() {
|
|
Package::new("baz", "0.0.1").yanked(true).publish();
|
|
cargo_process("install baz --version 0.0.1")
|
|
.with_status(101)
|
|
.with_stderr_contains(
|
|
"\
|
|
[ERROR] cannot install package `baz`, it has been yanked from registry `crates-io`
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_cargo_package_in_a_patched_workspace() {
|
|
pkg("foo", "0.1.0");
|
|
pkg("fizz", "1.0.0");
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[workspace]
|
|
members = ["baz"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"baz/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "baz"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
fizz = "1"
|
|
|
|
[patch.crates-io]
|
|
fizz = { version = "=1.0.0" }
|
|
"#,
|
|
)
|
|
.file("baz/src/lib.rs", "")
|
|
.build();
|
|
|
|
let stderr = "\
|
|
[WARNING] patch for the non root package will be ignored, specify patch at the workspace root:
|
|
package: [..]/foo/baz/Cargo.toml
|
|
workspace: [..]/foo/Cargo.toml
|
|
";
|
|
p.cargo("check").with_stderr_contains(&stderr).run();
|
|
|
|
// A crate installation must not emit any message from a workspace under
|
|
// current working directory.
|
|
// See https://github.com/rust-lang/cargo/issues/8619
|
|
p.cargo("install foo")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.1.0 (registry [..])
|
|
[INSTALLING] foo v0.1.0
|
|
[COMPILING] foo v0.1.0
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [..]foo[EXE]
|
|
[INSTALLED] package `foo v0.1.0` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn locked_install_without_published_lockfile() {
|
|
Package::new("foo", "0.1.0")
|
|
.file("src/main.rs", "//! Some docs\nfn main() {}")
|
|
.publish();
|
|
|
|
cargo_process("install foo --locked")
|
|
.with_stderr_contains("[WARNING] no Cargo.lock file published in foo v0.1.0")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_semver_metadata() {
|
|
// Check trying to install a package that uses semver metadata.
|
|
// This uses alt registry because the bug this is exercising doesn't
|
|
// trigger with a replaced source.
|
|
registry::alt_init();
|
|
Package::new("foo", "1.0.0+abc")
|
|
.alternative(true)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.publish();
|
|
|
|
cargo_process("install foo --registry alternative --version 1.0.0+abc").run();
|
|
cargo_process("install foo --registry alternative")
|
|
.with_stderr("\
|
|
[UPDATING] `alternative` index
|
|
[IGNORED] package `foo v1.0.0+abc (registry `alternative`)` is already installed, use --force to override
|
|
[WARNING] be sure to add [..]
|
|
")
|
|
.run();
|
|
// "Updating" is not displayed here due to the --version fast-path.
|
|
cargo_process("install foo --registry alternative --version 1.0.0+abc")
|
|
.with_stderr("\
|
|
[IGNORED] package `foo v1.0.0+abc (registry `alternative`)` is already installed, use --force to override
|
|
[WARNING] be sure to add [..]
|
|
")
|
|
.run();
|
|
cargo_process("install foo --registry alternative --version 1.0.0 --force")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `alternative` index
|
|
[INSTALLING] foo v1.0.0+abc (registry `alternative`)
|
|
[COMPILING] foo v1.0.0+abc (registry `alternative`)
|
|
[FINISHED] [..]
|
|
[REPLACING] [ROOT]/home/.cargo/bin/foo[EXE]
|
|
[REPLACED] package [..]
|
|
[WARNING] be sure to add [..]
|
|
",
|
|
)
|
|
.run();
|
|
// Check that from a fresh cache will work without metadata, too.
|
|
paths::home().join(".cargo/registry").rm_rf();
|
|
paths::home().join(".cargo/bin").rm_rf();
|
|
cargo_process("install foo --registry alternative --version 1.0.0")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `alternative` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v1.0.0+abc (registry `alternative`)
|
|
[INSTALLING] foo v1.0.0+abc (registry `alternative`)
|
|
[COMPILING] foo v1.0.0+abc (registry `alternative`)
|
|
[FINISHED] [..]
|
|
[INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v1.0.0+abc (registry `alternative`)` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn no_auto_fix_note() {
|
|
Package::new("auto_fix", "0.0.1")
|
|
.file("src/lib.rs", "use std::io;")
|
|
.file(
|
|
"src/main.rs",
|
|
&format!("extern crate {}; use std::io; fn main() {{}}", "auto_fix"),
|
|
)
|
|
.publish();
|
|
|
|
// This should not contain a suggestion to run `cargo fix`
|
|
//
|
|
// This is checked by matching the full output as `with_stderr_does_not_contain`
|
|
// can be brittle
|
|
cargo_process("install auto_fix")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] auto_fix v0.0.1 (registry [..])
|
|
[INSTALLING] auto_fix v0.0.1
|
|
[COMPILING] auto_fix v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/auto_fix[EXE]
|
|
[INSTALLED] package `auto_fix v0.0.1` (executable `auto_fix[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "auto_fix");
|
|
|
|
cargo_process("uninstall auto_fix")
|
|
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/auto_fix[EXE]")
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "auto_fix");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn failed_install_retains_temp_directory() {
|
|
// Verifies that the temporary directory persists after a build failure.
|
|
Package::new("foo", "0.0.1")
|
|
.file("src/main.rs", "x")
|
|
.publish();
|
|
let err = cargo_process("install foo").exec_with_output().unwrap_err();
|
|
let err = err.downcast::<ProcessError>().unwrap();
|
|
let stderr = String::from_utf8(err.stderr.unwrap()).unwrap();
|
|
compare::match_contains(
|
|
"\
|
|
[UPDATING] `dummy-registry` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`)
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
",
|
|
&stderr,
|
|
None,
|
|
)
|
|
.unwrap();
|
|
compare::match_contains(
|
|
"error: failed to compile `foo v0.0.1`, intermediate artifacts can be found at \
|
|
`[..]`.\nTo reuse those artifacts with a future compilation, set the environment \
|
|
variable `CARGO_TARGET_DIR` to that path.",
|
|
&stderr,
|
|
None,
|
|
)
|
|
.unwrap();
|
|
|
|
// Find the path in the output.
|
|
let stderr = stderr.split_once("found at `").unwrap().1;
|
|
let end = stderr.find('.').unwrap() - 1;
|
|
let path = Path::new(&stderr[..end]);
|
|
assert!(path.exists());
|
|
assert!(path.join("release/deps").exists());
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn sparse_install() {
|
|
// Checks for an issue where uninstalling something corrupted
|
|
// the SourceIds of sparse registries.
|
|
// See https://github.com/rust-lang/cargo/issues/11751
|
|
let _registry = registry::RegistryBuilder::new().http_index().build();
|
|
|
|
pkg("foo", "0.0.1");
|
|
pkg("bar", "0.0.1");
|
|
|
|
cargo_process("install foo --registry dummy-registry")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `dummy-registry` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`)
|
|
[INSTALLING] foo v0.0.1 (registry `dummy-registry`)
|
|
[UPDATING] `dummy-registry` index
|
|
[COMPILING] foo v0.0.1 (registry `dummy-registry`)
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1 (registry `dummy-registry`)` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
let assert_v1 = |expected| {
|
|
let v1 = fs::read_to_string(paths::home().join(".cargo/.crates.toml")).unwrap();
|
|
compare::assert_match_exact(expected, &v1);
|
|
};
|
|
assert_v1(
|
|
r#"[v1]
|
|
"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"]
|
|
"#,
|
|
);
|
|
cargo_process("install bar").run();
|
|
assert_has_installed_exe(cargo_home(), "bar");
|
|
assert_v1(
|
|
r#"[v1]
|
|
"bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["bar[EXE]"]
|
|
"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"]
|
|
"#,
|
|
);
|
|
|
|
cargo_process("uninstall bar")
|
|
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/bar[EXE]")
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "bar");
|
|
assert_v1(
|
|
r#"[v1]
|
|
"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"]
|
|
"#,
|
|
);
|
|
cargo_process("uninstall foo")
|
|
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
|
|
.run();
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
assert_v1(
|
|
r#"[v1]
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn self_referential() {
|
|
// Some packages build-dep on prior versions of themselves.
|
|
Package::new("foo", "0.0.1")
|
|
.file("src/lib.rs", "fn hello() {}")
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("build.rs", "fn main() {}")
|
|
.publish();
|
|
Package::new("foo", "0.0.2")
|
|
.file("src/lib.rs", "fn hello() {}")
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("build.rs", "fn main() {}")
|
|
.build_dep("foo", "0.0.1")
|
|
.publish();
|
|
|
|
cargo_process("install foo")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.2 (registry [..])
|
|
[INSTALLING] foo v0.0.2
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry [..])
|
|
[COMPILING] foo v0.0.1
|
|
[COMPILING] foo v0.0.2
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.2` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn ambiguous_registry_vs_local_package() {
|
|
// Correctly install 'foo' from a local package, even if that package also
|
|
// depends on a registry dependency named 'foo'.
|
|
Package::new("foo", "0.0.1")
|
|
.file("src/lib.rs", "fn hello() {}")
|
|
.publish();
|
|
|
|
let p = project()
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
edition = "2021"
|
|
|
|
[dependencies]
|
|
foo = "0.0.1"
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
cargo_process("install --path")
|
|
.arg(p.root())
|
|
.with_stderr(
|
|
"\
|
|
[INSTALLING] foo v0.1.0 ([..])
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry [..])
|
|
[COMPILING] foo v0.0.1
|
|
[COMPILING] foo v0.1.0 ([..])
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.1.0 ([..])` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_with_redundant_default_mode() {
|
|
pkg("foo", "0.0.1");
|
|
|
|
cargo_process("install foo --release")
|
|
.with_stderr(
|
|
"\
|
|
error: unexpected argument '--release' found
|
|
|
|
tip: `--release` is the default for `cargo install`; instead `--debug` is supported
|
|
|
|
Usage: cargo[EXE] install [OPTIONS] [CRATE[@<VER>]]...
|
|
|
|
For more information, try '--help'.
|
|
",
|
|
)
|
|
.with_status(1)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn install_incompat_msrv() {
|
|
Package::new("foo", "0.1.0")
|
|
.file("src/main.rs", "fn main() {}")
|
|
.rust_version("1.30")
|
|
.publish();
|
|
Package::new("foo", "0.2.0")
|
|
.file("src/main.rs", "fn main() {}")
|
|
.rust_version("1.9876.0")
|
|
.publish();
|
|
|
|
cargo_process("install foo")
|
|
.with_stderr("\
|
|
[UPDATING] `dummy-registry` index
|
|
[ERROR] cannot install package `foo 0.2.0`, it requires rustc 1.9876.0 or newer, while the currently active rustc version is [..]
|
|
`foo 0.1.0` supports rustc 1.30
|
|
")
|
|
.with_status(101).run();
|
|
}
|
|
|
|
fn assert_tracker_noexistence(key: &str) {
|
|
let v1_data: toml::Value =
|
|
toml::from_str(&fs::read_to_string(cargo_home().join(".crates.toml")).unwrap()).unwrap();
|
|
let v2_data: serde_json::Value =
|
|
serde_json::from_str(&fs::read_to_string(cargo_home().join(".crates2.json")).unwrap())
|
|
.unwrap();
|
|
|
|
assert!(v1_data["v1"].get(key).is_none());
|
|
assert!(v2_data["installs"][key].is_null());
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn uninstall_running_binary() {
|
|
use std::io::Write;
|
|
|
|
Package::new("foo", "0.0.1")
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
use std::net::TcpStream;
|
|
use std::env::var;
|
|
use std::io::Read;
|
|
fn main() {
|
|
for i in 0..2 {
|
|
TcpStream::connect(&var("__ADDR__").unwrap()[..])
|
|
.unwrap()
|
|
.read_to_end(&mut Vec::new())
|
|
.unwrap();
|
|
}
|
|
}
|
|
"#,
|
|
)
|
|
.publish();
|
|
|
|
cargo_process("install foo")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] foo v0.0.1 (registry [..])
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
assert_has_installed_exe(cargo_home(), "foo");
|
|
|
|
let foo_bin = cargo_home().join("bin").join(exe("foo"));
|
|
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
|
|
let addr = l.local_addr().unwrap().to_string();
|
|
let t = thread::spawn(move || {
|
|
ProcessBuilder::new(foo_bin)
|
|
.env("__ADDR__", addr)
|
|
.exec()
|
|
.unwrap();
|
|
});
|
|
let key = "foo 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)";
|
|
|
|
#[cfg(windows)]
|
|
{
|
|
// Ensure foo is running before the first `cargo uninstall` call
|
|
l.accept().unwrap().0.write_all(&[1]).unwrap();
|
|
cargo_process("uninstall foo")
|
|
.with_status(101)
|
|
.with_stderr_contains("[ERROR] failed to remove file `[CWD]/home/.cargo/bin/foo[EXE]`")
|
|
.run();
|
|
// Ensure foo is stopped before the second `cargo uninstall` call
|
|
l.accept().unwrap().0.write_all(&[1]).unwrap();
|
|
t.join().unwrap();
|
|
cargo_process("uninstall foo")
|
|
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
|
|
.run();
|
|
};
|
|
|
|
#[cfg(not(windows))]
|
|
{
|
|
// Ensure foo is running before the first `cargo uninstall` call
|
|
l.accept().unwrap().0.write_all(&[1]).unwrap();
|
|
cargo_process("uninstall foo")
|
|
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
|
|
.run();
|
|
l.accept().unwrap().0.write_all(&[1]).unwrap();
|
|
t.join().unwrap();
|
|
};
|
|
|
|
assert_has_not_installed_exe(cargo_home(), "foo");
|
|
assert_tracker_noexistence(key);
|
|
|
|
cargo_process("install foo")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] `[..]` index
|
|
[INSTALLING] foo v0.0.1
|
|
[COMPILING] foo v0.0.1
|
|
[FINISHED] release [optimized] target(s) in [..]
|
|
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
|
|
[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
|
|
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
|
|
",
|
|
)
|
|
.run();
|
|
}
|