Display embedded man pages for built-in commands.

This commit is contained in:
Eric Huss 2020-07-05 18:15:07 -07:00
parent 9138d65e4c
commit 0e26eae5c1
36 changed files with 291 additions and 325 deletions

View File

@ -106,6 +106,10 @@ features = [
cargo-test-macro = { path = "crates/cargo-test-macro", version = "0.1.0" }
cargo-test-support = { path = "crates/cargo-test-support", version = "0.1.0" }
[build-dependencies]
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
tar = { version = "0.4.26", default-features = false }
[[bin]]
name = "cargo"
test = false

34
build.rs Normal file
View File

@ -0,0 +1,34 @@
use flate2::{Compression, GzBuilder};
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
fn main() {
compress_man();
}
fn compress_man() {
let out_path = Path::new(&std::env::var("OUT_DIR").unwrap()).join("man.tgz");
let dst = fs::File::create(out_path).unwrap();
let encoder = GzBuilder::new()
.filename("man.tar")
.write(dst, Compression::best());
let mut ar = tar::Builder::new(encoder);
let mut add_files = |dir, extension| {
for entry in fs::read_dir(dir).unwrap() {
let path = entry.unwrap().path();
if path.extension() != Some(extension) {
continue;
}
println!("cargo:rerun-if-changed={}", path.display());
ar.append_path_with_name(&path, path.file_name().unwrap())
.unwrap();
}
};
add_files(Path::new("src/etc/man"), OsStr::new("1"));
add_files(Path::new("src/doc/man/generated_txt"), OsStr::new("txt"));
let encoder = ar.into_inner().unwrap();
encoder.finish().unwrap();
}

View File

@ -10,6 +10,11 @@ pub fn main(config: &mut Config) -> CliResult {
// CAUTION: Be careful with using `config` until it is configured below.
// In general, try to avoid loading config values unless necessary (like
// the [alias] table).
if commands::help::handle_embedded_help(config) {
return Ok(());
}
let args = match cli().get_matches_safe() {
Ok(args) => args,
Err(e) => {

View File

@ -45,30 +45,7 @@ pub fn cli() -> App {
"Run all benchmarks regardless of failure",
))
.arg_unit_graph()
.after_help(
"\
The benchmark filtering argument BENCHNAME and all the arguments following the
two dashes (`--`) are passed to the benchmark binaries and thus to libtest
(rustc's built in unit-test and micro-benchmarking framework). If you're
passing arguments to both Cargo and the binary, the ones after `--` go to the
binary, the ones before go to Cargo. For details about libtest's arguments see
the output of `cargo bench -- --help`.
If the `--package` argument is given, then SPEC is a package ID specification
which indicates which package should be benchmarked. If it is not given, then
the current package is benchmarked. For more information on SPEC and its format,
see the `cargo help pkgid` command.
All packages in the workspace are benchmarked if the `--workspace` flag is supplied. The
`--workspace` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--workspace` flag.
The `--jobs` argument affects the building of the benchmark executable but does
not affect how many jobs are used when running the benchmarks.
Compilation can be customized with the `bench` profile in the manifest.
",
)
.after_help("Run `cargo help bench` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -42,17 +42,7 @@ pub fn cli() -> App {
.arg_message_format()
.arg_build_plan()
.arg_unit_graph()
.after_help(
"\
All packages in the workspace are built if the `--workspace` flag is supplied. The
`--workspace` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--workspace` flag.
Compilation can be configured via the use of profiles which are configured in
the manifest. The default profile for this command is `dev`, but passing
the --release flag will use the `release` profile instead.
",
)
.after_help("Run `cargo help build` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -34,25 +34,7 @@ pub fn cli() -> App {
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
If the `--package` argument is given, then SPEC is a package ID specification
which indicates which package should be built. If it is not given, then the
current package is built. For more information on SPEC and its format, see the
`cargo help pkgid` command.
All packages in the workspace are checked if the `--workspace` flag is supplied. The
`--workspace` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--workspace` flag.
Compilation can be configured via the use of profiles which are configured in
the manifest. The default profile for this command is `dev`, but passing
the `--release` flag will use the `release` profile instead.
The `--profile test` flag can be used to check unit tests with the
`#[cfg(test)]` attribute.
",
)
.after_help("Run `cargo help check` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -13,14 +13,7 @@ pub fn cli() -> App {
.arg_release("Whether or not to clean release artifacts")
.arg_profile("Clean artifacts of the specified profile")
.arg_doc("Whether or not to clean just the documentation directory")
.after_help(
"\
If the `--package` argument is given, then SPEC is a package ID specification
which indicates which package's artifacts should be cleaned out. If it is not
given, then all packages' artifacts are removed. For more information on SPEC
and its format, see the `cargo help pkgid` command.
",
)
.after_help("Run `cargo help clean` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -31,22 +31,7 @@ pub fn cli() -> App {
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
By default the documentation for the local package and all dependencies is
built. The output is all placed in `target/doc` in rustdoc's usual format.
All packages in the workspace are documented if the `--workspace` flag is
supplied. The `--workspace` flag is automatically assumed for a virtual
manifest. Note that `--exclude` has to be specified in conjunction with the
`--workspace` flag.
If the `--package` argument is given, then SPEC is a package ID specification
which indicates which package should be documented. If it is not given, then the
current package is documented. For more information on SPEC and its format, see
the `cargo help pkgid` command.
",
)
.after_help("Run `cargo help doc` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -9,18 +9,7 @@ pub fn cli() -> App {
.arg(opt("quiet", "No output printed to stdout").short("q"))
.arg_manifest_path()
.arg_target_triple("Fetch dependencies for the target triple")
.after_help(
"\
If a lock file is available, this command will ensure that all of the Git
dependencies and/or registries dependencies are downloaded and locally
available. The network is never touched after a `cargo fetch` unless
the lock file changes.
If the lock file is not available, then this is the equivalent of
`cargo generate-lockfile`. A lock file is generated and dependencies are also
all updated.
",
)
.after_help("Run `cargo help fetch` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -72,37 +72,7 @@ pub fn cli() -> App {
.long("allow-staged")
.help("Fix code even if the working directory has staged changes"),
)
.after_help(
"\
This Cargo subcommand will automatically take rustc's suggestions from
diagnostics like warnings and apply them to your source code. This is intended
to help automate tasks that rustc itself already knows how to tell you to fix!
The `cargo fix` subcommand is also being developed for the Rust 2018 edition
to provide code the ability to easily opt-in to the new edition without having
to worry about any breakage.
Executing `cargo fix` will under the hood execute `cargo check`. Any warnings
applicable to your crate will be automatically fixed (if possible) and all
remaining warnings will be displayed when the check process is finished. For
example if you'd like to prepare for the 2018 edition, you can do so by
executing:
cargo fix --edition
which behaves the same as `cargo check --all-targets`. Similarly if you'd like
to fix code for different platforms you can do:
cargo fix --edition --target x86_64-pc-windows-gnu
or if your crate has optional features:
cargo fix --edition --no-default-features --features foo
If you encounter any problems with `cargo fix` or otherwise have any questions
or feature requests please don't hesitate to file an issue at
https://github.com/rust-lang/cargo
",
)
.after_help("Run `cargo help fix` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -7,6 +7,7 @@ pub fn cli() -> App {
.about("Generate the lockfile for a package")
.arg(opt("quiet", "No output printed to stdout").short("q"))
.arg_manifest_path()
.after_help("Run `cargo help generate-lockfile` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -0,0 +1,128 @@
use crate::aliased_command;
use cargo::util::errors::CargoResult;
use cargo::util::paths::resolve_executable;
use cargo::Config;
use flate2::read::GzDecoder;
use std::ffi::OsString;
use std::io::Read;
use std::io::Write;
use std::path::Path;
const COMPRESSED_MAN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/man.tgz"));
/// Checks if the `help` command is being issued.
///
/// This runs before clap processing, because it needs to intercept the `help`
/// command if a man page is available.
///
/// Returns `true` if a man page was displayed. In this case, Cargo should
/// exit.
pub fn handle_embedded_help(config: &Config) -> bool {
match try_help(config) {
Ok(true) => true,
Ok(false) => false,
Err(e) => {
log::warn!("man failed: {:?}", e);
false
}
}
}
fn try_help(config: &Config) -> CargoResult<bool> {
let mut args = std::env::args_os()
.skip(1)
.skip_while(|arg| arg.to_str().map_or(false, |s| s.starts_with('-')));
if !args
.next()
.map_or(false, |arg| arg.to_str() == Some("help"))
{
return Ok(false);
}
let subcommand = match args.next() {
Some(arg) => arg,
None => return Ok(false),
};
let subcommand = match subcommand.to_str() {
Some(s) => s,
None => return Ok(false),
};
// Check if this is a built-in command (or alias);
let subcommand = match check_alias(config, subcommand) {
Some(s) => s,
None => return Ok(false),
};
if resolve_executable(Path::new("man")).is_ok() {
let man = match extract_man(&subcommand, "1")? {
Some(man) => man,
None => return Ok(false),
};
write_and_spawn(&man, "man")?;
} else {
let txt = match extract_man(&subcommand, "txt")? {
Some(txt) => txt,
None => return Ok(false),
};
if resolve_executable(Path::new("less")).is_ok() {
write_and_spawn(&txt, "less")?;
} else if resolve_executable(Path::new("more")).is_ok() {
write_and_spawn(&txt, "more")?;
} else {
drop(std::io::stdout().write_all(&txt));
}
}
Ok(true)
}
/// Checks if the given subcommand is a built-in command (possibly via an alias).
///
/// Returns None if it is not a built-in command.
fn check_alias(config: &Config, subcommand: &str) -> Option<String> {
if super::builtin_exec(subcommand).is_some() {
return Some(subcommand.to_string());
}
match aliased_command(config, subcommand) {
Ok(Some(alias)) => {
let alias = alias.into_iter().next()?;
if super::builtin_exec(&alias).is_some() {
Some(alias)
} else {
None
}
}
_ => None,
}
}
/// Extracts the given man page from the compressed archive.
///
/// Returns None if the command wasn't found.
fn extract_man(subcommand: &str, extension: &str) -> CargoResult<Option<Vec<u8>>> {
let extract_name = OsString::from(format!("cargo-{}.{}", subcommand, extension));
let gz = GzDecoder::new(COMPRESSED_MAN);
let mut ar = tar::Archive::new(gz);
for entry in ar.entries().unwrap() {
let mut entry = entry.unwrap();
let path = entry.path().unwrap();
if path.file_name().unwrap() != extract_name {
continue;
}
let mut result = Vec::new();
entry.read_to_end(&mut result).unwrap();
return Ok(Some(result));
}
Ok(None)
}
/// Write the contents of a man page to disk and spawn the given command to
/// display it.
fn write_and_spawn(contents: &[u8], command: &str) -> CargoResult<()> {
let mut tmp = tempfile::Builder::new().prefix("cargo-man").tempfile()?;
let f = tmp.as_file_mut();
f.write_all(&contents)?;
f.flush()?;
let mut cmd = std::process::Command::new(command)
.arg(tmp.path())
.spawn()?;
drop(cmd.wait());
Ok(())
}

View File

@ -9,6 +9,7 @@ pub fn cli() -> App {
.arg(Arg::with_name("path").default_value("."))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.arg_new_opts()
.after_help("Run `cargo help init` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -71,39 +71,7 @@ pub fn cli() -> App {
.requires("crate")
.conflicts_with_all(&["git", "path", "index"]),
)
.after_help(
"\
This command manages Cargo's local set of installed binary crates. Only
packages which have executable [[bin]] or [[example]] targets can be
installed, and all executables are installed into the installation root's
`bin` folder. The installation root is determined, in order of precedence, by
`--root`, `$CARGO_INSTALL_ROOT`, the `install.root` configuration key, and
finally the home directory (which is either `$CARGO_HOME` if set or
`$HOME/.cargo` by default).
There are multiple sources from which a crate can be installed. The default
location is crates.io but the `--git`, `--path`, and `--registry` flags can
change this source. If the source contains more than one package (such as
crates.io or a git repository with multiple crates) the `<crate>` argument is
required to indicate which crate should be installed.
Crates from crates.io can optionally specify the version they wish to install
via the `--version` flags, and similarly packages from git repositories can
optionally specify the branch, tag, or revision that should be installed. If a
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. 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
specified by setting the `CARGO_TARGET_DIR` environment variable to a relative
path. In particular, this can be useful for caching build artifacts on
continuous integration systems.",
)
.after_help("Run `cargo help install` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -6,6 +6,7 @@ pub fn cli() -> App {
.about("Print a JSON representation of a Cargo.toml file's location")
.arg(opt("quiet", "No output printed to stdout").short("q"))
.arg_manifest_path()
.after_help("Run `cargo help locate-project` for more detailed information.\n")
}
#[derive(Serialize)]

View File

@ -16,6 +16,7 @@ pub fn cli() -> App {
.hidden(true),
)
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.after_help("Run `cargo help login` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -26,6 +26,7 @@ pub fn cli() -> App {
.value_name("VERSION")
.possible_value("1"),
)
.after_help("Run `cargo help metadata` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -85,6 +85,7 @@ pub mod fetch;
pub mod fix;
pub mod generate_lockfile;
pub mod git_checkout;
pub mod help;
pub mod init;
pub mod install;
pub mod locate_project;

View File

@ -9,6 +9,7 @@ pub fn cli() -> App {
.arg(Arg::with_name("path").required(true))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.arg_new_opts()
.after_help("Run `cargo help new` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -27,15 +27,7 @@ pub fn cli() -> App {
.arg(opt("index", "Registry index to modify owners for").value_name("INDEX"))
.arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.after_help(
"\
This command will modify the owners for a crate on the specified registry (or
default). Owners of a crate can upload new versions and yank old versions.
Explicitly named owners can also modify the set of owners, so take care!
See https://doc.rust-lang.org/cargo/reference/publishing.html#cargo-owner
for detailed documentation and troubleshooting.",
)
.after_help("Run `cargo help owner` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -30,6 +30,7 @@ pub fn cli() -> App {
.arg_features()
.arg_manifest_path()
.arg_jobs()
.after_help("Run `cargo help package` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -9,28 +9,7 @@ pub fn cli() -> App {
.arg(Arg::with_name("spec"))
.arg_package("Argument to get the package ID specifier for")
.arg_manifest_path()
.after_help(
"\
Given a <spec> argument, print out the fully qualified package ID specifier.
This command will generate an error if <spec> is ambiguous as to which package
it refers to in the dependency graph. If no <spec> is given, then the pkgid for
the local package is printed.
This command requires that a lockfile is available and dependencies have been
fetched.
Example Package IDs
pkgid | name | version | url
|-----------------------------|--------|-----------|---------------------|
foo | foo | * | *
foo:1.2.3 | foo | 1.2.3 | *
crates.io/foo | foo | * | *://crates.io/foo
crates.io/foo#1.2.3 | foo | 1.2.3 | *://crates.io/foo
crates.io/bar#foo:1.2.3 | foo | 1.2.3 | *://crates.io/bar
https://crates.io/foo#1.2.3 | foo | 1.2.3 | https://crates.io/foo
",
)
.after_help("Run `cargo help pkgid` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -23,6 +23,7 @@ pub fn cli() -> App {
.arg_jobs()
.arg_dry_run("Perform all checks without uploading")
.arg(opt("registry", "Registry to publish to").value_name("REGISTRY"))
.after_help("Run `cargo help publish` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -25,18 +25,7 @@ pub fn cli() -> App {
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
If neither `--bin` nor `--example` are given, then if the package only has one
bin target it will be run. Otherwise `--bin` specifies the bin target to run,
and `--example` specifies the example target to run. At most one of `--bin` or
`--example` can be provided.
All the arguments following the two dashes (`--`) are passed to the binary to
run. If you're passing arguments to both Cargo and the binary, the ones after
`--` go to the binary, the ones before go to Cargo.
",
)
.after_help("Run `cargo help run` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -30,22 +30,7 @@ pub fn cli() -> App {
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
The specified target for the current package (or package specified by SPEC if
provided) will be compiled along with all of its dependencies. The specified
<args>... will all be passed to the final compiler invocation, not any of the
dependencies. Note that the compiler will still unconditionally receive
arguments such as -L, --extern, and --crate-type, and the specified <args>...
will simply be added to the compiler invocation.
This command requires that only one target is being compiled. If more than one
target is available for the current package the filters of --lib, --bin, etc,
must be used to select which target is compiled. To pass flags to all compiler
processes spawned by Cargo, use the $RUSTFLAGS environment variable or the
`build.rustflags` configuration option.
",
)
.after_help("Run `cargo help rustc` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -34,21 +34,7 @@ pub fn cli() -> App {
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
The specified target for the current package (or package specified by SPEC if
provided) will be documented with the specified `<opts>...` being passed to the
final rustdoc invocation. Dependencies will not be documented as part of this
command. Note that rustdoc will still unconditionally receive arguments such
as `-L`, `--extern`, and `--crate-type`, and the specified `<opts>...` will
simply be added to the rustdoc invocation.
If the `--package` argument is given, then SPEC is a package ID specification
which indicates which package should be documented. If it is not given, then the
current package is documented. For more information on SPEC and its format, see
the `cargo help pkgid` command.
",
)
.after_help("Run `cargo help rustdoc` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -18,6 +18,7 @@ pub fn cli() -> App {
.value_name("LIMIT"),
)
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.after_help("Run `cargo help search` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -55,48 +55,7 @@ pub fn cli() -> App {
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
The test filtering argument TESTNAME and all the arguments following the
two dashes (`--`) are passed to the test binaries and thus to libtest
(rustc's built in unit-test and micro-benchmarking framework). If you're
passing arguments to both Cargo and the binary, the ones after `--` go to the
binary, the ones before go to Cargo. For details about libtest's arguments see
the output of `cargo test -- --help`. As an example, this will run all
tests with `foo` in their name on 3 threads in parallel:
cargo test foo -- --test-threads 3
If the `--package` argument is given, then SPEC is a package ID specification
which indicates which package should be tested. If it is not given, then the
current package is tested. For more information on SPEC and its format, see the
`cargo help pkgid` command.
All packages in the workspace are tested if the `--workspace` flag is supplied. The
`--workspace` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--workspace` flag.
The `--jobs` argument affects the building of the test executable but does
not affect how many jobs are used when running the tests. The default value
for the `--jobs` argument is the number of CPUs. If you want to control the
number of simultaneous running test cases, pass the `--test-threads` option
to the test binaries:
cargo test -- --test-threads=1
Compilation can be configured via the `test` profile in the manifest.
By default the rust test harness hides output from test execution to
keep results readable. Test output can be recovered (e.g., for debugging)
by passing `--nocapture` to the test binaries:
cargo test -- --nocapture
To get the list of all options available for the test binaries use this:
cargo test -- --help
",
)
.after_help("Run `cargo help test` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -96,6 +96,7 @@ pub fn cli() -> App {
.short("V")
.hidden(true),
)
.after_help("Run `cargo help tree` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -10,14 +10,7 @@ pub fn cli() -> App {
.arg_package_spec_simple("Package to uninstall")
.arg(multi_opt("bin", "NAME", "Only uninstall the binary NAME"))
.arg(opt("root", "Directory to uninstall packages from").value_name("DIR"))
.after_help(
"\
The argument SPEC is a package ID specification (see `cargo help pkgid`) to
specify which crate should be uninstalled. By default all binaries are
uninstalled for a crate but the `--bin` and `--example` flags can be used to
only uninstall particular binaries.
",
)
.after_help("Run `cargo help uninstall` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -14,29 +14,7 @@ pub fn cli() -> App {
.arg_dry_run("Don't actually write the lockfile")
.arg(opt("precise", "Update a single dependency to exactly PRECISE").value_name("PRECISE"))
.arg_manifest_path()
.after_help(
"\
This command requires that a `Cargo.lock` already exists as generated by
`cargo build` or related commands.
If SPEC is given, then a conservative update of the lockfile will be
performed. This means that only the dependency specified by SPEC will be
updated. Its transitive dependencies will be updated only if SPEC cannot be
updated without updating dependencies. All other dependencies will remain
locked at their currently recorded versions.
If PRECISE is specified, then `--aggressive` must not also be specified. The
argument PRECISE is a string representing a precise revision that the package
being updated should be updated to. For example, if the package comes from a git
repository, then PRECISE would be the exact revision that the repository should
be updated to.
If SPEC is not given, then all dependencies will be re-resolved and
updated.
For more information about package ID specifications, see `cargo help pkgid`.
",
)
.after_help("Run `cargo help update` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -52,18 +52,7 @@ pub fn cli() -> App {
.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`.
",
)
.after_help("Run `cargo help vendor` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -8,6 +8,7 @@ pub fn cli() -> App {
.about("Check correctness of crate manifest")
.arg(opt("quiet", "No output printed to stdout").short("q"))
.arg_manifest_path()
.after_help("Run `cargo help verify-project` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -5,6 +5,7 @@ pub fn cli() -> App {
subcommand("version")
.about("Show version information")
.arg(opt("quiet", "No output printed to stdout").short("q"))
.after_help("Run `cargo help version` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -15,17 +15,7 @@ pub fn cli() -> App {
.arg(opt("index", "Registry index to yank from").value_name("INDEX"))
.arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.after_help(
"\
The yank command removes a previously pushed crate's version from the server's
index. This command does not delete any data, and the crate will still be
available for download via the registry's download link.
Note that existing crates locked to a yanked version will still be able to
download the yanked version to use it. Cargo will, however, not allow any new
crates to be locked to any yanked version.
",
)
.after_help("Run `cargo help yank` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

View File

@ -1,7 +1,10 @@
//! Tests for cargo's help output.
use cargo_test_support::cargo_process;
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_manifest, cargo_exe, cargo_process, paths, process, project};
use std::fs;
use std::path::Path;
use std::str::from_utf8;
#[cargo_test]
fn help() {
@ -47,3 +50,88 @@ fn z_flags_help() {
.with_stdout_contains(" -Z unstable-options -- Allow the usage of unstable options")
.run();
}
fn help_with_man(display_command: &str) {
// Build a "man" process that just echoes the contents.
let p = project()
.at(display_command)
.file("Cargo.toml", &basic_manifest(display_command, "1.0.0"))
.file(
"src/main.rs",
&r#"
fn main() {
eprintln!("custom __COMMAND__");
let path = std::env::args().skip(1).next().unwrap();
let mut f = std::fs::File::open(path).unwrap();
std::io::copy(&mut f, &mut std::io::stdout()).unwrap();
}
"#
.replace("__COMMAND__", display_command),
)
.build();
p.cargo("build").run();
help_with_man_and_path(display_command, "build", "build", &p.target_debug_dir());
}
fn help_with_man_and_path(
display_command: &str,
subcommand: &str,
actual_subcommand: &str,
path: &Path,
) {
let contents = if display_command == "man" {
fs::read_to_string(format!("src/etc/man/cargo-{}.1", actual_subcommand)).unwrap()
} else {
fs::read_to_string(format!(
"src/doc/man/generated_txt/cargo-{}.txt",
actual_subcommand
))
.unwrap()
};
let output = process(&cargo_exe())
.arg("help")
.arg(subcommand)
.env("PATH", path)
.exec_with_output()
.unwrap();
assert!(output.status.success());
let stderr = from_utf8(&output.stderr).unwrap();
if display_command.is_empty() {
assert_eq!(stderr, "");
} else {
assert_eq!(stderr, format!("custom {}\n", display_command));
}
let stdout = from_utf8(&output.stdout).unwrap();
assert_eq!(stdout, contents);
}
#[cargo_test]
fn help_man() {
// Checks that `help command` displays the man page using the given command.
help_with_man("man");
help_with_man("less");
help_with_man("more");
// Check with no commands in PATH.
help_with_man_and_path("", "build", "build", Path::new(""));
}
#[cargo_test]
fn help_alias() {
// Check that `help some_alias` will resolve.
help_with_man_and_path("", "b", "build", Path::new(""));
let config = paths::root().join(".cargo/config");
fs::create_dir_all(config.parent().unwrap()).unwrap();
fs::write(
config,
r#"
[alias]
my-alias = ["build", "--release"]
"#,
)
.unwrap();
help_with_man_and_path("", "my-alias", "build", Path::new(""));
}