Skip to content

Commit

Permalink
Auto merge of #6869 - alexcrichton:vendor, r=ehuss
Browse files Browse the repository at this point in the history
Import the cargo-vendor subcommand into Cargo

This commit imports the external [alexcrichton/cargo-vendor
repository][repo] into Cargo itself. This means it will no longer be
necessary to install the `cargo-vendor` subcommand in order to vendor
dependencies. Additionally it'll always support the latest feature set
of Cargo as it'll be built into Cargo!

All tests were imported as part of this commit, but not all features
were imported. Some flags have been left out that were added later in
the lifetime of `cargo vendor` which seem like they're more questionable
to stabilize. I'm hoping that they can have separate PRs adding their
implementation here, and we can make a decision of their stabilization
at a later date.

The current man page for `cargo vendor -h` will look like:

    cargo-vendor
    Vendor all dependencies for a project locally

    USAGE:
	cargo vendor [OPTIONS] [--] [path]

    OPTIONS:
	-q, --quiet                    No output printed to stdout
	    --manifest-path <PATH>     Path to Cargo.toml
	    --no-delete                Don't delete older crates in the vendor directory
	-s, --sync <TOML>...           Additional `Cargo.toml` to sync and vendor
	    --respect-source-config    Respect `[source]` config in `.cargo/config`
	-v, --verbose                  Use verbose output (-vv very verbose/build.rs output)
	    --color <WHEN>             Coloring: auto, always, never
	    --frozen                   Require Cargo.lock and cache are up to date
	    --locked                   Require Cargo.lock is up to date
	-Z <FLAG>...                   Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
	-h, --help                     Prints help information

    ARGS:
	<path>    Where to vendor crates (`vendor` by default)

    This cargo subcommand will vendor all crates.io and git dependencies for a
    project into the specified directory at `<path>`. After this command completes
    the vendor directory specified by `<path>` will contain all remote sources from
    dependencies specified. Additionally manifest beyond the default one can be
    specified with the `-s` option.

    The `cargo vendor` command will also print out the configuration necessary
    to use the vendored sources, which when needed is then encoded into
    `.cargo/config`.

Since this change is not importing 100% of the functionality of the
existing `cargo vendor` this change does run a risk of being a breaking
change for any folks using such functionality. Executing `cargo vendor`
will favor the built-in command rather than an external subcommand,
causing unimplemented features to become errors about flag usage.

[repo]: https://github.com/alexcrichton/cargo-vendor
  • Loading branch information
bors committed Jun 3, 2019
2 parents f97b11c + 3842d8e commit 0e38712
Show file tree
Hide file tree
Showing 21 changed files with 1,553 additions and 60 deletions.
3 changes: 3 additions & 0 deletions src/bin/cargo/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub fn builtin() -> Vec<App> {
test::cli(),
uninstall::cli(),
update::cli(),
vendor::cli(),
verify_project::cli(),
version::cli(),
yank::cli(),
Expand Down Expand Up @@ -66,6 +67,7 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches<'_>) -> Cli
"test" => test::exec,
"uninstall" => uninstall::exec,
"update" => update::exec,
"vendor" => vendor::exec,
"verify-project" => verify_project::exec,
"version" => version::exec,
"yank" => yank::exec,
Expand Down Expand Up @@ -102,6 +104,7 @@ pub mod search;
pub mod test;
pub mod uninstall;
pub mod update;
pub mod vendor;
pub mod verify_project;
pub mod version;
pub mod yank;
123 changes: 123 additions & 0 deletions src/bin/cargo/commands/vendor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use crate::command_prelude::*;
use cargo::ops;
use std::path::PathBuf;

pub fn cli() -> App {
subcommand("vendor")
.about("Vendor all dependencies for a project locally")
.arg(opt("quiet", "No output printed to stdout").short("q"))
.arg_manifest_path()
.arg(Arg::with_name("path").help("Where to vendor crates (`vendor` by default)"))
.arg(
Arg::with_name("no-delete")
.long("no-delete")
.help("Don't delete older crates in the vendor directory"),
)
.arg(
Arg::with_name("tomls")
.short("s")
.long("sync")
.help("Additional `Cargo.toml` to sync and vendor")
.value_name("TOML")
.multiple(true),
)
.arg(
Arg::with_name("respect-source-config")
.long("respect-source-config")
.help("Respect `[source]` config in `.cargo/config`")
.multiple(true),
)
.arg(
Arg::with_name("no-merge-sources")
.long("no-merge-sources")
.hidden(true),
)
.arg(
Arg::with_name("relative-path")
.long("relative-path")
.hidden(true),
)
.arg(
Arg::with_name("only-git-deps")
.long("only-git-deps")
.hidden(true),
)
.arg(
Arg::with_name("explicit-version")
.short("-x")
.long("explicit-version")
.hidden(true),
)
.arg(
Arg::with_name("disallow-duplicates")
.long("disallow-duplicates")
.hidden(true),
)
.after_help(
"\
This cargo subcommand will vendor all crates.io and git dependencies for a
project into the specified directory at `<path>`. After this command completes
the vendor directory specified by `<path>` will contain all remote sources from
dependencies specified. Additional manifests beyond the default one can be
specified with the `-s` option.
The `cargo vendor` command will also print out the configuration necessary
to use the vendored sources, which you will need to add to `.cargo/config`.
",
)
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
// We're doing the vendoring operation outselves, so we don't actually want
// to respect any of the `source` configuration in Cargo itself. That's
// intended for other consumers of Cargo, but we want to go straight to the
// source, e.g. crates.io, to fetch crates.
if !args.is_present("respect-source-config") {
config.values_mut()?.remove("source");
}

// When we moved `cargo vendor` into Cargo itself we didn't stabilize a few
// flags, so try to provide a helpful error message in that case to enusre
// that users currently using the flag aren't tripped up.
let crates_io_cargo_vendor_flag = if args.is_present("no-merge-sources") {
Some("--no-merge-sources")
} else if args.is_present("relative-path") {
Some("--relative-path")
} else if args.is_present("only-git-deps") {
Some("--only-git-deps")
} else if args.is_present("explicit-version") {
Some("--explicit-version")
} else if args.is_present("disallow-duplicates") {
Some("--disallow-duplicates")
} else {
None
};
if let Some(flag) = crates_io_cargo_vendor_flag {
return Err(failure::format_err!("\
the crates.io `cargo vendor` command has now been merged into Cargo itself
and does not support the flag `{}` currently; to continue using the flag you
can execute `cargo-vendor vendor ...`, and if you would like to see this flag
supported in Cargo itself please feel free to file an issue at
https://github.com/rust-lang/cargo/issues/new
", flag).into());
}

let ws = args.workspace(config)?;
let path = args
.value_of_os("path")
.map(|val| PathBuf::from(val.to_os_string()))
.unwrap_or(PathBuf::from("vendor"));
ops::vendor(
&ws,
&ops::VendorOptions {
no_delete: args.is_present("no-delete"),
destination: &path,
extra: args
.values_of_os("tomls")
.unwrap_or_default()
.map(|s| PathBuf::from(s.to_os_string()))
.collect(),
},
)?;
Ok(())
}
42 changes: 29 additions & 13 deletions src/cargo/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#![allow(clippy::unneeded_field_pattern)]

use std::fmt;
use std::io;

use failure::Error;
use log::debug;
Expand Down Expand Up @@ -147,23 +148,38 @@ fn handle_cause(cargo_err: &Error, shell: &mut Shell) -> bool {
drop(writeln!(shell.err(), " {}", error));
}

fn print_stderror_causes(error: &dyn std::error::Error, shell: &mut Shell) {
let mut cur = std::error::Error::source(error);
while let Some(err) = cur {
print(&err.to_string(), shell);
cur = std::error::Error::source(err);
}
}

let verbose = shell.verbosity();

if verbose == Verbose {
// The first error has already been printed to the shell.
// Print all remaining errors.
for err in cargo_err.iter_causes() {
print(&err.to_string(), shell);
// The first error has already been printed to the shell.
for err in cargo_err.iter_causes() {

// If we're not in verbose mode then print remaining errors until one
// marked as `Internal` appears.
if verbose != Verbose && err.downcast_ref::<Internal>().is_some() {
return false;
}
} else {
// The first error has already been printed to the shell.
// Print remaining errors until one marked as `Internal` appears.
for err in cargo_err.iter_causes() {
if err.downcast_ref::<Internal>().is_some() {
return false;
}

print(&err.to_string(), shell);
print(&err.to_string(), shell);

// Using the `failure` crate currently means that when using
// `iter_causes` we're only iterating over the `failure` causes, but
// this doesn't include the causes from the standard library `Error`
// trait. We don't have a great way of getting an `&dyn Error` from a
// `&dyn Fail`, so we currently just special case a few errors that are
// known to maybe have causes and we try to print them here.
//
// Note that this isn't an exhaustive match since causes for
// `std::error::Error` aren't the most common thing in the world.
if let Some(io) = err.downcast_ref::<io::Error>() {
print_stderror_causes(io, shell);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/cargo/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub use self::resolve::{
add_overrides, get_resolved_packages, resolve_with_previous, resolve_ws, resolve_ws_precisely,
resolve_ws_with_method,
};
pub use self::vendor::{vendor, VendorOptions};

mod cargo_clean;
mod cargo_compile;
Expand All @@ -46,3 +47,4 @@ mod fix;
mod lockfile;
mod registry;
mod resolve;
mod vendor;
Loading

0 comments on commit 0e38712

Please sign in to comment.