Auto merge of #7560 - ehuss:stabilize-install-upgrade, r=alexcrichton

Stabilize install-upgrade.

Tracking issue: #6797

This stabilizes the install-upgrade feature, which causes `cargo install` to reinstall a package if it appears to be out of date, or exit cleanly if it is up-to-date.

There are no changes from `-Zinstall-upgrade`. See [the old unstable docs](6a7f505a18/src/doc/src/reference/unstable.md (install-upgrade)) for a refresher on the details of what it does.

This also stabilizes the following changes:
- `--version` no longer allows an implicit version requirement like `1.2`.  It must be either contain all 3 components (like `1.2.3`) or use a requirement operator (like `^1.2`).  This has been a warning for a very long time, and is now changed to a hard error.
- Added `--no-track` to disable install tracking.

**Motivation**

I just personally prefer this behavior, and it has been requested a few times in the past. I've been using it locally, and haven't run into any issues. If this goes into 1.41, then it will release on Jan 30, about 10 months since it was added in #6798.

**Concerns**

Regarding some of the concerns I had:

- Is it tracking the correct set of information?

  I'm satisfied with the current set. It also tracks, but does not use, the version of rustc and the version specified in the `--version` flag, in case we ever want to use that in the future. It is also designed to be backwards and forwards compatible, so more information can be added in the future. I think the current set strikes a good balance of tracking the really important things, without causing unnecessary rebuilds.

- Method to upgrade all outdated packages?

  This can always be added as a new flag or command in the future, and shouldn't block stabilization.

- Should `--no-track` be kept? Does it work correctly?

  I kept it. It's not too hard to support, and nobody said anything (other than maybe using a less confusing name).

- Should this be the default? Should there be a way to use the old behavior?

  I like it as the default, and don't see a real need for the old behavior. I think we could always bring back the old behavior with a flag in the future, but I would like to avoid it for simplicity. There is also the workaround of `which foo || cargo install foo`.

Closes #6797.
Closes #6727.
Closes #6485.
Closes #2082.
This commit is contained in:
bors 2019-11-21 15:59:06 +00:00
commit fb4415090f
11 changed files with 322 additions and 378 deletions

View File

@ -34,7 +34,6 @@ Available unstable (nightly-only) flags:
-Z no-index-update -- Do not update the registry, avoids a network request for benchmarking
-Z unstable-options -- Allow the usage of unstable options such as --registry
-Z config-profile -- Read profiles from .cargo/config files
-Z install-upgrade -- `cargo install` will upgrade instead of failing
-Z timings -- Display concurrency information
-Z doctest-xcompile -- Compile and run doctests for non-host target using runner config

View File

@ -46,10 +46,7 @@ pub fn cli() -> App {
))
.arg_jobs()
.arg(opt("force", "Force overwriting existing crates or binaries").short("f"))
.arg(opt(
"no-track",
"Do not save tracking information (unstable)",
))
.arg(opt("no-track", "Do not save tracking information"))
.arg_features()
.arg_profile("Install artifacts with the specified profile")
.arg(opt("debug", "Build in debug mode instead of release mode"))
@ -90,13 +87,9 @@ crate has multiple binaries, the `--bin` argument can selectively install only
one of them, and if you'd rather install examples the `--example` argument can
be used as well.
By default cargo will refuse to overwrite existing binaries. The `--force` flag
enables overwriting existing binaries. Thus you can reinstall a crate with
`cargo install --force <crate>`.
Omitting the <crate> specification entirely will install the crate in the
current directory. This behaviour is deprecated, and it no longer works in the
Rust 2018 edition. Use the more explicit `install --path .` instead.
If the package is already installed, Cargo will reinstall it if the installed
version does not appear to be up-to-date. Installing with `--path` will always
build and install, unless there are conflicting binaries from another package.
If the source is crates.io or `--git` then by default the crate will be built
in a temporary target directory. To avoid this, the target directory can be
@ -159,13 +152,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let version = args.value_of("version");
let root = args.value_of("root");
if args.is_present("no-track") && !config.cli_unstable().install_upgrade {
return Err(failure::format_err!(
"`--no-track` flag is unstable, pass `-Z install-upgrade` to enable it"
)
.into());
};
if args.is_present("list") {
ops::install_list(root, config)?;
} else {

View File

@ -334,7 +334,6 @@ pub struct CliUnstable {
pub config_profile: bool,
pub dual_proc_macros: bool,
pub mtime_on_use: bool,
pub install_upgrade: bool,
pub named_profiles: bool,
pub binary_dep_depinfo: bool,
pub build_std: Option<Vec<String>>,
@ -400,7 +399,6 @@ impl CliUnstable {
"dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
// can also be set in .cargo/config or with and ENV
"mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
"install-upgrade" => self.install_upgrade = parse_empty(k, v)?,
"named-profiles" => self.named_profiles = parse_empty(k, v)?,
"binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
"build-std" => {

View File

@ -18,33 +18,31 @@ use crate::util::{FileLock, Filesystem};
/// On-disk tracking for which package installed which binary.
///
/// v1 is an older style, v2 is a new (experimental) style that tracks more
/// information. The new style is only enabled with the `-Z install-upgrade`
/// flag (which sets the `unstable_upgrade` flag). v1 is still considered the
/// source of truth. When v2 is used, it will sync with any changes with v1,
/// and will continue to update v1.
/// v1 is an older style, v2 is a new style that tracks more information, and
/// is both backwards and forwards compatible. Cargo keeps both files in sync,
/// updating both v1 and v2 at the same time. Additionally, if it detects
/// changes in v1 that are not in v2 (such as when an older version of Cargo
/// is used), it will automatically propagate those changes to v2.
///
/// This maintains a filesystem lock, preventing other instances of Cargo from
/// modifying at the same time. Drop the value to unlock.
///
/// If/when v2 is stabilized, it is intended that v1 is retained for a while
/// during a longish transition period, and then v1 can be removed.
/// It is intended that v1 should be retained for a while during a longish
/// transition period, and then v1 can be removed.
pub struct InstallTracker {
v1: CrateListingV1,
v2: CrateListingV2,
v1_lock: FileLock,
v2_lock: Option<FileLock>,
unstable_upgrade: bool,
v2_lock: FileLock,
}
/// Tracking information for the set of installed packages.
///
/// This v2 format is unstable and requires the `-Z unstable-upgrade` option
/// to enable.
#[derive(Default, Deserialize, Serialize)]
struct CrateListingV2 {
/// Map of every installed package.
installs: BTreeMap<PackageId, InstallInfo>,
/// Forwards compatibility.
/// Forwards compatibility. Unknown keys from future versions of Cargo
/// will be stored here and retained when the file is saved.
#[serde(flatten)]
other: BTreeMap<String, serde_json::Value>,
}
@ -56,7 +54,7 @@ struct CrateListingV2 {
/// determine if it needs to be rebuilt/reinstalled. If nothing has changed,
/// then Cargo will inform the user that it is "up to date".
///
/// This is only used for the (unstable) v2 format.
/// This is only used for the v2 format.
#[derive(Debug, Deserialize, Serialize)]
struct InstallInfo {
/// Version requested via `--version`.
@ -87,19 +85,15 @@ struct InstallInfo {
/// Tracking information for the set of installed packages.
#[derive(Default, Deserialize, Serialize)]
pub struct CrateListingV1 {
/// Map of installed package id to the set of binary names for that package.
v1: BTreeMap<PackageId, BTreeSet<String>>,
}
impl InstallTracker {
/// Create an InstallTracker from information on disk.
pub fn load(config: &Config, root: &Filesystem) -> CargoResult<InstallTracker> {
let unstable_upgrade = config.cli_unstable().install_upgrade;
let v1_lock = root.open_rw(Path::new(".crates.toml"), config, "crate metadata")?;
let v2_lock = if unstable_upgrade {
Some(root.open_rw(Path::new(".crates2.json"), config, "crate metadata")?)
} else {
None
};
let v2_lock = root.open_rw(Path::new(".crates2.json"), config, "crate metadata")?;
let v1 = (|| -> CargoResult<_> {
let mut contents = String::new();
@ -119,26 +113,21 @@ impl InstallTracker {
})?;
let v2 = (|| -> CargoResult<_> {
match &v2_lock {
Some(lock) => {
let mut contents = String::new();
lock.file().read_to_string(&mut contents)?;
let mut v2 = if contents.is_empty() {
CrateListingV2::default()
} else {
serde_json::from_str(&contents)
.chain_err(|| format_err!("invalid JSON found for metadata"))?
};
v2.sync_v1(&v1)?;
Ok(v2)
}
None => Ok(CrateListingV2::default()),
}
let mut contents = String::new();
v2_lock.file().read_to_string(&mut contents)?;
let mut v2 = if contents.is_empty() {
CrateListingV2::default()
} else {
serde_json::from_str(&contents)
.chain_err(|| format_err!("invalid JSON found for metadata"))?
};
v2.sync_v1(&v1)?;
Ok(v2)
})()
.chain_err(|| {
format_err!(
"failed to parse crate metadata at `{}`",
v2_lock.as_ref().unwrap().path().to_string_lossy()
v2_lock.path().to_string_lossy()
)
})?;
@ -147,7 +136,6 @@ impl InstallTracker {
v2,
v1_lock,
v2_lock,
unstable_upgrade,
})
}
@ -204,7 +192,7 @@ impl InstallTracker {
// If both sets are the same length, that means all duplicates come
// from packages with the same name.
if self.unstable_upgrade && matching_duplicates.len() == duplicates.len() {
if matching_duplicates.len() == duplicates.len() {
// Determine if it is dirty or fresh.
let source_id = pkg.package_id().source_id();
if source_id.is_path() {
@ -265,11 +253,8 @@ impl InstallTracker {
.filter_map(|name| {
if !dst.join(&name).exists() {
None
} else if self.unstable_upgrade {
let p = self.v2.package_for_bin(name);
Some((name.clone(), p))
} else {
let p = self.v1.package_for_bin(name);
let p = self.v2.package_for_bin(name);
Some((name.clone(), p))
}
})
@ -286,10 +271,8 @@ impl InstallTracker {
target: &str,
rustc: &str,
) {
if self.unstable_upgrade {
self.v2
.mark_installed(package, bins, version_req, opts, target, rustc)
}
self.v2
.mark_installed(package, bins, version_req, opts, target, rustc);
self.v1.mark_installed(package, bins);
}
@ -302,14 +285,12 @@ impl InstallTracker {
)
})?;
if self.unstable_upgrade {
self.v2.save(self.v2_lock.as_ref().unwrap()).chain_err(|| {
format_err!(
"failed to write crate metadata at `{}`",
self.v2_lock.as_ref().unwrap().path().to_string_lossy()
)
})?;
}
self.v2.save(&self.v2_lock).chain_err(|| {
format_err!(
"failed to write crate metadata at `{}`",
self.v2_lock.path().to_string_lossy()
)
})?;
Ok(())
}
@ -329,20 +310,11 @@ impl InstallTracker {
/// Remove a package from the tracker.
pub fn remove(&mut self, pkg_id: PackageId, bins: &BTreeSet<String>) {
self.v1.remove(pkg_id, bins);
if self.unstable_upgrade {
self.v2.remove(pkg_id, bins);
}
self.v2.remove(pkg_id, bins);
}
}
impl CrateListingV1 {
fn package_for_bin(&self, bin_name: &str) -> Option<PackageId> {
self.v1
.iter()
.find(|(_, bins)| bins.contains(bin_name))
.map(|(pkg_id, _)| *pkg_id)
}
fn mark_installed(&mut self, pkg: &Package, bins: &BTreeSet<String>) {
// Remove bins from any other packages.
for other_bins in self.v1.values_mut() {
@ -600,24 +572,11 @@ where
match v.to_semver() {
Ok(v) => Some(format!("={}", v)),
Err(e) => {
let mut msg = if config.cli_unstable().install_upgrade {
format!(
"the `--vers` provided, `{}`, is \
not a valid semver version: {}\n",
v, e
)
} else {
format!(
"the `--vers` provided, `{}`, is \
not a valid semver version\n\n\
historically Cargo treated this \
as a semver version requirement \
accidentally\nand will continue \
to do so, but this behavior \
will be removed eventually",
v
)
};
let mut msg = format!(
"the `--vers` provided, `{}`, is \
not a valid semver version: {}\n",
v, e
);
// If it is not a valid version but it is a valid version
// requirement, add a note to the warning
@ -628,12 +587,7 @@ where
v
));
}
if config.cli_unstable().install_upgrade {
bail!(msg);
} else {
config.shell().warn(&msg)?;
}
Some(v.to_string())
bail!(msg);
}
}
}

View File

@ -37,6 +37,20 @@ crate has multiple binaries, the `--bin` argument can selectively install only
one of them, and if you'd rather install examples the `--example` argument can
be used as well.
If the package is already installed, Cargo will reinstall it if the installed
version does not appear to be up-to-date. If any of the following values
change, then Cargo will reinstall the package:
- The package version and source.
- The set of binary names installed.
- The chosen features.
- The release mode (`--debug`).
- The target (`--target`).
Installing with `--path` will always build and install, unless there are
conflicting binaries from another package. The `--force` flag may be used to
force Cargo to always reinstall the package.
If the source is crates.io or `--git` then by default the crate will be built
in a temporary target directory. To avoid this, the target directory can be
specified by setting the `CARGO_TARGET_DIR` environment variable to a relative
@ -63,7 +77,13 @@ available.
*--vers* _VERSION_::
*--version* _VERSION_::
Specify a version to install.
Specify a version to install. This may be a
linkcargo:reference/specifying-dependencies.md[version requirement], like
`~1.2`, to have Cargo select the newest version from the given
requirement. If the version does not have a requirement operator (such as
`^` or `~`), then it must be in the form _MAJOR.MINOR.PATCH_, and will
install exactly that version; it is *not* treated as a caret requirement
like Cargo dependencies are.
*--git* _URL_::
Git URL to install the specified crate from.
@ -85,9 +105,18 @@ available.
*-f*::
*--force*::
Force overwriting existing crates or binaries. This can be used to
reinstall or upgrade a crate.
Force overwriting existing crates or binaries. This can be used if a
package has installed a binary with the same name as another package. This
is also useful if something has changed on the system that you want to
rebuild with, such as a newer version of `rustc`.
*--no-track*::
By default, Cargo keeps track of the installed packages with a metadata
file stored in the installation root directory. This flag tells Cargo not
to use or create that file. With this flag, Cargo will refuse to overwrite
any existing files unless the `--force` flag is used. This also disables
Cargo's ability to protect against multiple concurrent invocations of
Cargo installing at the same time.
*--bin* _NAME_...::
Install only the specified binary.
@ -137,13 +166,17 @@ include::section-exit-status.adoc[]
== EXAMPLES
. Install a package from crates.io:
. Install or upgrade a package from crates.io:
cargo install ripgrep
. Reinstall or upgrade a package:
. Install or reinstall the package in the current directory:
cargo install ripgrep --force
cargo install --path .
. View the list of installed packages:
cargo install --list
== SEE ALSO
man:cargo[1], man:cargo-uninstall[1], man:cargo-search[1], man:cargo-publish[1]

View File

@ -60,6 +60,35 @@ one of them, and if you&#8217;d rather install examples the <code>--example</cod
be used as well.</p>
</div>
<div class="paragraph">
<p>If the package is already installed, Cargo will reinstall it if the installed
version does not appear to be up-to-date. If any of the following values
change, then Cargo will reinstall the package:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The package version and source.</p>
</li>
<li>
<p>The set of binary names installed.</p>
</li>
<li>
<p>The chosen features.</p>
</li>
<li>
<p>The release mode (<code>--debug</code>).</p>
</li>
<li>
<p>The target (<code>--target</code>).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Installing with <code>--path</code> will always build and install, unless there are
conflicting binaries from another package. The <code>--force</code> flag may be used to
force Cargo to always reinstall the package.</p>
</div>
<div class="paragraph">
<p>If the source is crates.io or <code>--git</code> then by default the crate will be built
in a temporary target directory. To avoid this, the target directory can be
specified by setting the <code>CARGO_TARGET_DIR</code> environment variable to a relative
@ -93,7 +122,13 @@ available.</p>
<dt class="hdlist1"><strong>--vers</strong> <em>VERSION</em></dt>
<dt class="hdlist1"><strong>--version</strong> <em>VERSION</em></dt>
<dd>
<p>Specify a version to install.</p>
<p>Specify a version to install. This may be a
<a href="../reference/specifying-dependencies.md">version requirement</a>, like
<code>~1.2</code>, to have Cargo select the newest version from the given
requirement. If the version does not have a requirement operator (such as
<code>^</code> or <code>~</code>), then it must be in the form <em>MAJOR.MINOR.PATCH</em>, and will
install exactly that version; it is <strong>not</strong> treated as a caret requirement
like Cargo dependencies are.</p>
</dd>
<dt class="hdlist1"><strong>--git</strong> <em>URL</em></dt>
<dd>
@ -122,8 +157,19 @@ available.</p>
<dt class="hdlist1"><strong>-f</strong></dt>
<dt class="hdlist1"><strong>--force</strong></dt>
<dd>
<p>Force overwriting existing crates or binaries. This can be used to
reinstall or upgrade a crate.</p>
<p>Force overwriting existing crates or binaries. This can be used if a
package has installed a binary with the same name as another package. This
is also useful if something has changed on the system that you want to
rebuild with, such as a newer version of <code>rustc</code>.</p>
</dd>
<dt class="hdlist1"><strong>--no-track</strong></dt>
<dd>
<p>By default, Cargo keeps track of the installed packages with a metadata
file stored in the installation root directory. This flag tells Cargo not
to use or create that file. With this flag, Cargo will refuse to overwrite
any existing files unless the <code>--force</code> flag is used. This also disables
Cargo&#8217;s ability to protect against multiple concurrent invocations of
Cargo installing at the same time.</p>
</dd>
<dt class="hdlist1"><strong>--bin</strong> <em>NAME</em>&#8230;&#8203;</dt>
<dd>
@ -346,7 +392,7 @@ details on environment variables that Cargo reads.</p>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Install a package from crates.io:</p>
<p>Install or upgrade a package from crates.io:</p>
<div class="literalblock">
<div class="content">
<pre>cargo install ripgrep</pre>
@ -354,10 +400,18 @@ details on environment variables that Cargo reads.</p>
</div>
</li>
<li>
<p>Reinstall or upgrade a package:</p>
<p>Install or reinstall the package in the current directory:</p>
<div class="literalblock">
<div class="content">
<pre>cargo install ripgrep --force</pre>
<pre>cargo install --path .</pre>
</div>
</div>
</li>
<li>
<p>View the list of installed packages:</p>
<div class="literalblock">
<div class="content">
<pre>cargo install --list</pre>
</div>
</div>
</li>

View File

@ -280,38 +280,6 @@ extra-info = "qwerty"
Metabuild packages should have a public function called `metabuild` that
performs the same actions as a regular `build.rs` script would perform.
### install-upgrade
* Tracking Issue: [#6797](https://github.com/rust-lang/cargo/issues/6797)
The `install-upgrade` feature changes the behavior of `cargo install` so that
it will reinstall a package if it is not "up-to-date". If it is "up-to-date",
it will do nothing and exit with success instead of failing. Example:
```
cargo +nightly install foo -Z install-upgrade
```
Cargo tracks some information to determine if a package is "up-to-date",
including:
- The package version and source.
- The set of binary names installed.
- The chosen features.
- The release mode (`--debug`).
- The target (`--target`).
If any of these values change, then Cargo will reinstall the package.
Installation will still fail if a different package installs a binary of the
same name. `--force` may be used to unconditionally reinstall the package.
Installing with `--path` will always build and install, unless there are
conflicting binaries from another package.
Additionally, a new flag `--no-track` is available to prevent `cargo install`
from writing tracking information in `$CARGO_HOME` about which packages are
installed.
### public-dependency
* Tracking Issue: [#44663](https://github.com/rust-lang/rust/issues/44663)

View File

@ -2,12 +2,12 @@
.\" Title: cargo-install
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.10
.\" Date: 2019-10-09
.\" Date: 2019-11-04
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
.TH "CARGO\-INSTALL" "1" "2019-10-09" "\ \&" "\ \&"
.TH "CARGO\-INSTALL" "1" "2019-11-04" "\ \&" "\ \&"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@ -116,6 +116,69 @@ crate has multiple binaries, the \fB\-\-bin\fP argument can selectively install
one of them, and if you\(cqd rather install examples the \fB\-\-example\fP argument can
be used as well.
.sp
If the package is already installed, Cargo will reinstall it if the installed
version does not appear to be up\-to\-date. If any of the following values
change, then Cargo will reinstall the package:
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
. sp -1
. IP \(bu 2.3
.\}
The package version and source.
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
. sp -1
. IP \(bu 2.3
.\}
The set of binary names installed.
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
. sp -1
. IP \(bu 2.3
.\}
The chosen features.
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
. sp -1
. IP \(bu 2.3
.\}
The release mode (\fB\-\-debug\fP).
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
. sp -1
. IP \(bu 2.3
.\}
The target (\fB\-\-target\fP).
.RE
.sp
Installing with \fB\-\-path\fP will always build and install, unless there are
conflicting binaries from another package. The \fB\-\-force\fP flag may be used to
force Cargo to always reinstall the package.
.sp
If the source is crates.io or \fB\-\-git\fP then by default the crate will be built
in a temporary target directory. To avoid this, the target directory can be
specified by setting the \fBCARGO_TARGET_DIR\fP environment variable to a relative
@ -140,7 +203,14 @@ available.
.sp
\fB\-\-vers\fP \fIVERSION\fP, \fB\-\-version\fP \fIVERSION\fP
.RS 4
Specify a version to install.
Specify a version to install. This may be a
.URL "https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.md" "version requirement" ","
like
\fB~1.2\fP, to have Cargo select the newest version from the given
requirement. If the version does not have a requirement operator (such as
\fB^\fP or \fB~\fP), then it must be in the form \fIMAJOR.MINOR.PATCH\fP, and will
install exactly that version; it is \fBnot\fP treated as a caret requirement
like Cargo dependencies are.
.RE
.sp
\fB\-\-git\fP \fIURL\fP
@ -175,8 +245,20 @@ List all installed packages and their versions.
.sp
\fB\-f\fP, \fB\-\-force\fP
.RS 4
Force overwriting existing crates or binaries. This can be used to
reinstall or upgrade a crate.
Force overwriting existing crates or binaries. This can be used if a
package has installed a binary with the same name as another package. This
is also useful if something has changed on the system that you want to
rebuild with, such as a newer version of \fBrustc\fP.
.RE
.sp
\fB\-\-no\-track\fP
.RS 4
By default, Cargo keeps track of the installed packages with a metadata
file stored in the installation root directory. This flag tells Cargo not
to use or create that file. With this flag, Cargo will refuse to overwrite
any existing files unless the \fB\-\-force\fP flag is used. This also disables
Cargo\(cqs ability to protect against multiple concurrent invocations of
Cargo installing at the same time.
.RE
.sp
\fB\-\-bin\fP \fINAME\fP...
@ -385,7 +467,7 @@ Cargo failed to complete.
. sp -1
. IP " 1." 4.2
.\}
Install a package from crates.io:
Install or upgrade a package from crates.io:
.sp
.if n .RS 4
.nf
@ -402,14 +484,31 @@ cargo install ripgrep
. sp -1
. IP " 2." 4.2
.\}
Reinstall or upgrade a package:
Install or reinstall the package in the current directory:
.sp
.if n .RS 4
.nf
cargo install ripgrep \-\-force
cargo install \-\-path .
.fi
.if n .RE
.RE
.sp
.RS 4
.ie n \{\
\h'-04' 3.\h'+01'\c
.\}
.el \{\
. sp -1
. IP " 3." 4.2
.\}
View the list of installed packages:
.sp
.if n .RS 4
.nf
cargo install \-\-list
.fi
.if n .RE
.RE
.SH "SEE ALSO"
.sp
\fBcargo\fP(1), \fBcargo\-uninstall\fP(1), \fBcargo\-search\fP(1), \fBcargo\-publish\fP(1)
\fBcargo\fP(1), \fBcargo\-uninstall\fP(1), \fBcargo\-search\fP(1), \fBcargo\-publish\fP(1)

View File

@ -99,20 +99,8 @@ fn one_install_should_be_bad() {
let b = b.wait_with_output().unwrap();
let a = a.join().unwrap();
let (bad, good) = if a.status.code() == Some(101) {
(a, b)
} else {
(b, a)
};
execs()
.with_status(101)
.with_stderr_contains(
"[ERROR] binary `foo[..]` already exists in destination as part of `[..]`",
)
.run_output(&bad);
execs()
.with_stderr_contains("warning: be sure to add `[..]` to your PATH [..]")
.run_output(&good);
execs().run_output(&a);
execs().run_output(&b);
assert_has_installed_exe(cargo_home(), "foo");
}

View File

@ -251,12 +251,15 @@ fn install_path() {
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_status(101)
.with_stderr(
"\
[ERROR] binary `foo[..]` already exists in destination as part of `foo v0.0.1 [..]`
Add --force to overwrite
[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();
@ -450,27 +453,6 @@ fn examples() {
assert_has_installed_exe(cargo_home(), "foo");
}
#[cargo_test]
fn install_twice() {
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();
cargo_process("install --path")
.arg(p.root())
.with_status(101)
.with_stderr(
"\
[ERROR] binary `foo-bin1[..]` already exists in destination as part of `foo v0.0.1 ([..])`
binary `foo-bin2[..]` already exists in destination as part of `foo v0.0.1 ([..])`
Add --force to overwrite
",
)
.run();
}
#[cargo_test]
fn install_force() {
let p = project().file("src/main.rs", "fn main() {}").build();
@ -1113,22 +1095,6 @@ but cannot be used multiple times
.run();
}
#[cargo_test]
fn legacy_version_requirement() {
pkg("foo", "0.1.1");
cargo_process("install foo --vers 0.1")
.with_stderr_contains(
"\
warning: the `--vers` provided, `0.1`, is not a valid semver version
historically Cargo treated this as a semver version requirement accidentally
and will continue to do so, but this behavior will be removed eventually
",
)
.run();
}
#[cargo_test]
fn test_install_git_cannot_be_a_base_url() {
cargo_process("install --git github.com:rust-lang-nursery/rustfmt.git")

View File

@ -118,8 +118,7 @@ fn validate_trackers(name: &str, version: &str, bins: &[&str]) {
fn registry_upgrade() {
// Installing and upgrading from a registry.
pkg("foo", "1.0.0");
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -137,8 +136,7 @@ fn registry_upgrade() {
installed_process("foo").with_stdout("1.0.0").run();
validate_trackers("foo", "1.0.0", &["foo"]);
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -150,8 +148,7 @@ fn registry_upgrade() {
pkg("foo", "1.0.1");
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -170,22 +167,19 @@ fn registry_upgrade() {
installed_process("foo").with_stdout("1.0.1").run();
validate_trackers("foo", "1.0.1", &["foo"]);
cargo_process("install foo --version=1.0.0 -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo --version=1.0.0")
.with_stderr_contains("[COMPILING] foo v1.0.0")
.run();
installed_process("foo").with_stdout("1.0.0").run();
validate_trackers("foo", "1.0.0", &["foo"]);
cargo_process("install foo --version=^1.0 -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo --version=^1.0")
.with_stderr_contains("[COMPILING] foo v1.0.1")
.run();
installed_process("foo").with_stdout("1.0.1").run();
validate_trackers("foo", "1.0.1", &["foo"]);
cargo_process("install foo --version=^1.0 -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo --version=^1.0")
.with_stderr_contains("[IGNORED] package `foo v1.0.1` is already installed[..]")
.run();
}
@ -194,12 +188,8 @@ fn registry_upgrade() {
fn uninstall() {
// Basic uninstall test.
pkg("foo", "1.0.0");
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("uninstall foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo").run();
cargo_process("uninstall foo").run();
let data = load_crates2();
assert_eq!(data["installs"].as_object().unwrap().len(), 0);
let v1_table = load_crates1();
@ -209,11 +199,8 @@ fn uninstall() {
#[cargo_test]
fn upgrade_force() {
pkg("foo", "1.0.0");
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo -Z install-upgrade --force")
.masquerade_as_nightly_cargo()
cargo_process("install foo").run();
cargo_process("install foo --force")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -233,8 +220,7 @@ fn upgrade_force() {
fn ambiguous_version_no_longer_allowed() {
// Non-semver-requirement is not allowed for `--version`.
pkg("foo", "1.0.0");
cargo_process("install foo --version=1.0 -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo --version=1.0")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -251,11 +237,8 @@ if you want to specify semver range, add an explicit qualifier, like ^1.0
fn path_is_always_dirty() {
// --path should always reinstall.
let p = project().file("src/main.rs", "fn main() {}").build();
p.cargo("install --path . -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
p.cargo("install --path . -Z install-upgrade")
.masquerade_as_nightly_cargo()
p.cargo("install --path .").run();
p.cargo("install --path .")
.with_stderr_contains("[REPLACING] [..]/foo[EXE]")
.run();
}
@ -267,8 +250,7 @@ fn fails_for_conflicts_unknown() {
let exe = installed_exe("foo");
exe.parent().unwrap().mkdir_p();
fs::write(exe, "").unwrap();
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo")
.with_stderr_contains("[ERROR] binary `foo[EXE]` already exists in destination")
.with_status(101)
.run();
@ -281,11 +263,8 @@ fn fails_for_conflicts_known() {
Package::new("bar", "1.0.0")
.file("src/bin/foo.rs", "fn main() {}")
.publish();
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install bar -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo").run();
cargo_process("install bar")
.with_stderr_contains(
"[ERROR] binary `foo[EXE]` already exists in destination as part of `foo v1.0.0`",
)
@ -301,33 +280,23 @@ fn supports_multiple_binary_names() {
.file("src/bin/a.rs", r#"fn main() { println!("a"); }"#)
.file("examples/ex1.rs", r#"fn main() { println!("ex1"); }"#)
.publish();
cargo_process("install foo -Z install-upgrade --bin foo")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --bin foo").run();
installed_process("foo").with_stdout("foo").run();
assert!(!installed_exe("a").exists());
assert!(!installed_exe("ex1").exists());
validate_trackers("foo", "1.0.0", &["foo"]);
cargo_process("install foo -Z install-upgrade --bin a")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --bin a").run();
installed_process("a").with_stdout("a").run();
assert!(!installed_exe("ex1").exists());
validate_trackers("foo", "1.0.0", &["a", "foo"]);
cargo_process("install foo -Z install-upgrade --example ex1")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --example ex1").run();
installed_process("ex1").with_stdout("ex1").run();
validate_trackers("foo", "1.0.0", &["a", "ex1", "foo"]);
cargo_process("uninstall foo -Z install-upgrade --bin foo")
.masquerade_as_nightly_cargo()
.run();
cargo_process("uninstall foo --bin foo").run();
assert!(!installed_exe("foo").exists());
assert!(installed_exe("ex1").exists());
validate_trackers("foo", "1.0.0", &["a", "ex1"]);
cargo_process("uninstall foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("uninstall foo").run();
assert!(!installed_exe("ex1").exists());
assert!(!installed_exe("a").exists());
}
@ -337,9 +306,8 @@ fn v1_already_installed_fresh() {
// Install with v1, then try to install again with v2.
pkg("foo", "1.0.0");
cargo_process("install foo").run();
cargo_process("install foo -Z install-upgrade")
cargo_process("install foo")
.with_stderr_contains("[IGNORED] package `foo v1.0.0` is already installed[..]")
.masquerade_as_nightly_cargo()
.run();
}
@ -349,10 +317,9 @@ fn v1_already_installed_dirty() {
pkg("foo", "1.0.0");
cargo_process("install foo").run();
pkg("foo", "1.0.1");
cargo_process("install foo -Z install-upgrade")
cargo_process("install foo")
.with_stderr_contains("[COMPILING] foo v1.0.1")
.with_stderr_contains("[REPLACING] [..]/foo[EXE]")
.masquerade_as_nightly_cargo()
.run();
validate_trackers("foo", "1.0.1", &["foo"]);
}
@ -385,37 +352,25 @@ fn change_features_rebuilds() {
"#,
)
.publish();
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo").run();
installed_process("foo").with_stdout("f1").run();
cargo_process("install foo -Z install-upgrade --no-default-features")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --no-default-features").run();
installed_process("foo").with_stdout("").run();
cargo_process("install foo -Z install-upgrade --all-features")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --all-features").run();
installed_process("foo").with_stdout("f1\nf2").run();
cargo_process("install foo -Z install-upgrade --no-default-features --features=f1")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --no-default-features --features=f1").run();
installed_process("foo").with_stdout("f1").run();
}
#[cargo_test]
fn change_profile_rebuilds() {
pkg("foo", "1.0.0");
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo -Z install-upgrade --debug")
.masquerade_as_nightly_cargo()
cargo_process("install foo").run();
cargo_process("install foo --debug")
.with_stderr_contains("[COMPILING] foo v1.0.0")
.with_stderr_contains("[REPLACING] [..]foo[EXE]")
.run();
cargo_process("install foo -Z install-upgrade --debug")
.masquerade_as_nightly_cargo()
cargo_process("install foo --debug")
.with_stderr_contains("[IGNORED] package `foo v1.0.0` is already installed[..]")
.run();
}
@ -426,13 +381,10 @@ fn change_target_rebuilds() {
return;
}
pkg("foo", "1.0.0");
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo").run();
let target = cross_compile::alternate();
cargo_process("install foo -v -Z install-upgrade --target")
cargo_process("install foo -v --target")
.arg(&target)
.masquerade_as_nightly_cargo()
.with_stderr_contains("[COMPILING] foo v1.0.0")
.with_stderr_contains("[REPLACING] [..]foo[EXE]")
.with_stderr_contains(&format!("[..]--target {}[..]", target))
@ -447,23 +399,19 @@ fn change_bin_sets_rebuilds() {
.file("src/bin/x.rs", "fn main() { }")
.file("src/bin/y.rs", "fn main() { }")
.publish();
cargo_process("install foo -Z install-upgrade --bin x")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --bin x").run();
assert!(installed_exe("x").exists());
assert!(!installed_exe("y").exists());
assert!(!installed_exe("foo").exists());
validate_trackers("foo", "1.0.0", &["x"]);
cargo_process("install foo -Z install-upgrade --bin y")
.masquerade_as_nightly_cargo()
cargo_process("install foo --bin y")
.with_stderr_contains("[INSTALLED] package `foo v1.0.0` (executable `y[EXE]`)")
.run();
assert!(installed_exe("x").exists());
assert!(installed_exe("y").exists());
assert!(!installed_exe("foo").exists());
validate_trackers("foo", "1.0.0", &["x", "y"]);
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install foo")
.with_stderr_contains("[INSTALLED] package `foo v1.0.0` (executable `foo[EXE]`)")
.with_stderr_contains(
"[REPLACED] package `foo v1.0.0` with `foo v1.0.0` (executables `x[EXE]`, `y[EXE]`)",
@ -480,18 +428,14 @@ fn forwards_compatible() {
// Unknown fields should be preserved.
pkg("foo", "1.0.0");
pkg("bar", "1.0.0");
cargo_process("install foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo").run();
let key = "foo 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)";
let v2 = cargo_home().join(".crates2.json");
let mut data = load_crates2();
data["newfield"] = serde_json::Value::Bool(true);
data["installs"][key]["moreinfo"] = serde_json::Value::String("shazam".to_string());
fs::write(&v2, serde_json::to_string(&data).unwrap()).unwrap();
cargo_process("install bar -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install bar").run();
let data: serde_json::Value = serde_json::from_str(&fs::read_to_string(&v2).unwrap()).unwrap();
assert_eq!(data["newfield"].as_bool().unwrap(), true);
assert_eq!(
@ -510,21 +454,15 @@ fn v2_syncs() {
.file("src/bin/x.rs", "fn main() {}")
.file("src/bin/y.rs", "fn main() {}")
.build();
cargo_process("install one -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install one").run();
validate_trackers("one", "1.0.0", &["one"]);
p.cargo("install -Z install-upgrade --path .")
.masquerade_as_nightly_cargo()
.run();
p.cargo("install --path .").run();
validate_trackers("foo", "1.0.0", &["x", "y"]);
// v1 add/remove
cargo_process("install two").run();
cargo_process("uninstall one").run();
// This should pick up that `two` was added, `one` was removed.
cargo_process("install three -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install three").run();
validate_trackers("three", "1.0.0", &["three"]);
cargo_process("install --list")
.with_stdout(
@ -539,13 +477,10 @@ two v1.0.0:
",
)
.run();
cargo_process("install one -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install one").run();
installed_process("one").with_stdout("1.0.0").run();
validate_trackers("one", "1.0.0", &["one"]);
cargo_process("install two -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install two")
.with_stderr_contains("[IGNORED] package `two v1.0.0` is already installed[..]")
.run();
// v1 remove
@ -553,13 +488,10 @@ two v1.0.0:
pkg("x", "1.0.0");
pkg("y", "1.0.0");
// This should succeed because `x` was removed in V1.
cargo_process("install x -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install x").run();
validate_trackers("x", "1.0.0", &["x"]);
// This should fail because `y` still exists in a different package.
cargo_process("install y -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install y")
.with_stderr_contains(
"[ERROR] binary `y[EXE]` already exists in destination \
as part of `foo v0.0.1 ([..])`",
@ -572,14 +504,12 @@ two v1.0.0:
fn upgrade_git() {
let git_project = git::new("foo", |project| project.file("src/main.rs", "fn main() {}"));
// install
cargo_process("install -Z install-upgrade --git")
cargo_process("install --git")
.arg(git_project.url().to_string())
.masquerade_as_nightly_cargo()
.run();
// Check install stays fresh.
cargo_process("install -Z install-upgrade --git")
cargo_process("install --git")
.arg(git_project.url().to_string())
.masquerade_as_nightly_cargo()
.with_stderr_contains(
"[IGNORED] package `foo v0.0.1 (file://[..]/foo#[..])` is \
already installed,[..]",
@ -591,17 +521,15 @@ fn upgrade_git() {
git::add(&repo);
git::commit(&repo);
// Install should reinstall.
cargo_process("install -Z install-upgrade --git")
cargo_process("install --git")
.arg(git_project.url().to_string())
.masquerade_as_nightly_cargo()
.with_stderr_contains("[COMPILING] foo v0.0.1 ([..])")
.with_stderr_contains("[REPLACING] [..]/foo[EXE]")
.run();
installed_process("foo").with_stdout("onomatopoeia").run();
// Check install stays fresh.
cargo_process("install -Z install-upgrade --git")
cargo_process("install --git")
.arg(git_project.url().to_string())
.masquerade_as_nightly_cargo()
.with_stderr_contains(
"[IGNORED] package `foo v0.0.1 (file://[..]/foo#[..])` is \
already installed,[..]",
@ -627,21 +555,14 @@ fn switch_sources() {
project.file("src/main.rs", r#"fn main() { println!("git"); }"#)
});
cargo_process("install -Z install-upgrade foo")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo").run();
installed_process("foo").with_stdout("1.0.0").run();
cargo_process("install -Z install-upgrade foo --registry alternative")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install foo --registry alternative").run();
installed_process("foo").with_stdout("alt").run();
p.cargo("install -Z install-upgrade --path .")
.masquerade_as_nightly_cargo()
.run();
p.cargo("install --path .").run();
installed_process("foo").with_stdout("local").run();
cargo_process("install -Z install-upgrade --git")
cargo_process("install --git")
.arg(git_project.url().to_string())
.masquerade_as_nightly_cargo()
.run();
installed_process("foo").with_stdout("git").run();
}
@ -659,8 +580,7 @@ fn multiple_report() {
.publish();
}
three("1.0.0");
cargo_process("install -Z install-upgrade one two three")
.masquerade_as_nightly_cargo()
cargo_process("install one two three")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -695,8 +615,7 @@ fn multiple_report() {
pkg("foo", "1.0.1");
pkg("bar", "1.0.1");
three("1.0.1");
cargo_process("install -Z install-upgrade one two three")
.masquerade_as_nightly_cargo()
cargo_process("install one two three")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -716,8 +635,7 @@ fn multiple_report() {
",
)
.run();
cargo_process("uninstall -Z install-upgrade three")
.masquerade_as_nightly_cargo()
cargo_process("uninstall three")
.with_stderr(
"\
[REMOVING] [..]/.cargo/bin/three[EXE]
@ -726,8 +644,7 @@ fn multiple_report() {
",
)
.run();
cargo_process("install -Z install-upgrade three --bin x")
.masquerade_as_nightly_cargo()
cargo_process("install three --bin x")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -740,8 +657,7 @@ fn multiple_report() {
",
)
.run();
cargo_process("install -Z install-upgrade three")
.masquerade_as_nightly_cargo()
cargo_process("install three")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -759,27 +675,13 @@ fn multiple_report() {
.run();
}
#[cargo_test]
fn no_track_gated() {
cargo_process("install --no-track foo")
.masquerade_as_nightly_cargo()
.with_stderr(
"[ERROR] `--no-track` flag is unstable, pass `-Z install-upgrade` to enable it",
)
.with_status(101)
.run();
}
#[cargo_test]
fn no_track() {
pkg("foo", "1.0.0");
cargo_process("install --no-track foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
.run();
cargo_process("install --no-track foo").run();
assert!(!v1_path().exists());
assert!(!v2_path().exists());
cargo_process("install --no-track foo -Z install-upgrade")
.masquerade_as_nightly_cargo()
cargo_process("install --no-track foo")
.with_stderr(
"\
[UPDATING] `[..]` index
@ -807,9 +709,7 @@ fn deletes_orphaned() {
.file("src/bin/other.rs", "fn main() {}")
.file("examples/ex1.rs", "fn main() {}")
.build();
p.cargo("install -Z install-upgrade --path . --bins --examples")
.masquerade_as_nightly_cargo()
.run();
p.cargo("install --path . --bins --examples").run();
assert!(installed_exe("other").exists());
// Remove a binary, add a new one, and bump the version.
@ -823,8 +723,7 @@ fn deletes_orphaned() {
version = "0.2.0"
"#,
);
p.cargo("install -Z install-upgrade --path . --bins --examples")
.masquerade_as_nightly_cargo()
p.cargo("install --path . --bins --examples")
.with_stderr(
"\
[INSTALLING] foo v0.2.0 [..]