2 use cargo
::core
::{features, CliUnstable}
;
3 use cargo
::{self, drop_print, drop_println, CliResult, Config}
;
4 use clap
::{AppSettings, Arg, ArgMatches}
;
5 use itertools
::Itertools
;
6 use std
::collections
::HashMap
;
10 use super::list_commands
;
11 use crate::command_prelude
::*;
12 use cargo
::core
::features
::HIDDEN
;
14 lazy_static
::lazy_static
! {
15 // Maps from commonly known external commands (not builtin to cargo) to their
16 // description, for the help page. Reserved for external subcommands that are
17 // core within the rust ecosystem (esp ones that might become internal in the future).
18 static ref KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS
: HashMap
<&'
static str, &'
static str> = HashMap
::from([
19 ("clippy", "Checks a package to catch common mistakes and improve your Rust code."),
20 ("fmt", "Formats all bin and lib files of the current crate using rustfmt."),
24 pub fn main(config
: &mut Config
) -> CliResult
{
25 // CAUTION: Be careful with using `config` until it is configured below.
26 // In general, try to avoid loading config values unless necessary (like
27 // the [alias] table).
29 let args
= cli().try_get_matches()?
;
31 // Global args need to be extracted before expanding aliases because the
32 // clap code for extracting a subcommand discards global options
33 // (appearing before the subcommand).
34 let (expanded_args
, global_args
) = expand_aliases(config
, args
, vec
![])?
;
37 .get_one
::<String
>("unstable-features")
41 let options
= CliUnstable
::help();
42 let non_hidden_options
: Vec
<(String
, String
)> = options
44 .filter(|(_
, help_message
)| *help_message
!= HIDDEN
)
45 .map(|(name
, help
)| (name
.to_string(), help
.to_string()))
47 let longest_option
= non_hidden_options
49 .map(|(option_name
, _
)| option_name
.len())
52 let help_lines
: Vec
<String
> = non_hidden_options
54 .map(|(option_name
, option_help_message
)| {
55 let option_name_kebab_case
= option_name
.replace("_", "-");
56 let padding
= " ".repeat(longest_option
- option_name
.len()); // safe to subtract
59 option_name_kebab_case
, padding
, option_help_message
63 let joined
= help_lines
.join("\n");
67 Available unstable (nightly-only) flags:
71 Run with 'cargo -Z [FLAG] [SUBCOMMAND]'",
74 if !config
.nightly_features_allowed
{
77 "\nUnstable flags are only available on the nightly channel \
78 of Cargo, but this is the `{}` channel.\n\
81 features
::SEE_CHANNELS
86 "\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
87 for more information about these flags."
92 let is_verbose
= expanded_args
.verbose() > 0;
93 if expanded_args
.flag("version") {
94 let version
= get_version_string(is_verbose
);
95 drop_print
!(config
, "{}", version
);
99 if let Some(code
) = expanded_args
.get_one
::<String
>("explain") {
100 let mut procss
= config
.load_global_rustc(None
)?
.process();
101 procss
.arg("--explain").arg(code
).exec()?
;
105 if expanded_args
.flag("list") {
106 drop_println
!(config
, "Installed Commands:");
107 for (name
, command
) in list_commands(config
) {
108 let known_external_desc
= KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS
.get(name
.as_str());
110 CommandInfo
::BuiltIn { about }
=> {
112 known_external_desc
.is_none(),
113 "KNOWN_EXTERNAL_COMMANDS shouldn't contain builtin \"{}\"",
116 let summary
= about
.unwrap_or_default();
117 let summary
= summary
.lines().next().unwrap_or(&summary
); // display only the first line
118 drop_println
!(config
, " {:<20} {}", name
, summary
);
120 CommandInfo
::External { path }
=> {
121 if let Some(desc
) = known_external_desc
{
122 drop_println
!(config
, " {:<20} {}", name
, desc
);
123 } else if is_verbose
{
124 drop_println
!(config
, " {:<20} {}", name
, path
.display());
126 drop_println
!(config
, " {}", name
);
129 CommandInfo
::Alias { target }
=> {
134 target
.iter().join(" ")
142 let (cmd
, subcommand_args
) = match expanded_args
.subcommand() {
143 Some((cmd
, args
)) => (cmd
, args
),
145 // No subcommand provided.
150 config_configure(config
, &expanded_args
, subcommand_args
, global_args
)?
;
151 super::init_git_transports(config
);
153 execute_subcommand(config
, cmd
, subcommand_args
)
156 pub fn get_version_string(is_verbose
: bool
) -> String
{
157 let version
= cargo
::version();
158 let mut version_string
= format
!("cargo {}\n", version
);
160 version_string
.push_str(&format
!("release: {}\n", version
.version
));
161 if let Some(ref ci
) = version
.commit_info
{
162 version_string
.push_str(&format
!("commit-hash: {}\n", ci
.commit_hash
));
163 version_string
.push_str(&format
!("commit-date: {}\n", ci
.commit_date
));
165 writeln
!(version_string
, "host: {}", env
!("RUST_HOST_TARGET")).unwrap();
166 add_libgit2(&mut version_string
);
167 add_curl(&mut version_string
);
168 add_ssl(&mut version_string
);
169 writeln
!(version_string
, "os: {}", os_info
::get()).unwrap();
174 fn add_libgit2(version_string
: &mut String
) {
175 let git2_v
= git2
::Version
::get();
176 let lib_v
= git2_v
.libgit2_version();
177 let vendored
= if git2_v
.vendored() {
184 "libgit2: {}.{}.{} (sys:{} {})",
188 git2_v
.crate_version(),
194 fn add_curl(version_string
: &mut String
) {
195 let curl_v
= curl
::Version
::get();
196 let vendored
= if curl_v
.vendored() {
203 "libcurl: {} (sys:{} {} ssl:{})",
205 curl_sys
::rust_crate_version(),
207 curl_v
.ssl_version().unwrap_or("none")
212 fn add_ssl(version_string
: &mut String
) {
213 #[cfg(feature = "openssl")]
215 writeln
!(version_string
, "ssl: {}", openssl
::version
::version()).unwrap();
217 #[cfg(not(feature = "openssl"))]
219 let _
= version_string
; // Silence unused warning.
226 mut already_expanded
: Vec
<String
>,
227 ) -> Result
<(ArgMatches
, GlobalArgs
), CliError
> {
228 if let Some((cmd
, args
)) = args
.subcommand() {
230 commands
::builtin_exec(cmd
),
231 super::aliased_command(config
, cmd
)?
,
233 (Some(_
), Some(_
)) => {
234 // User alias conflicts with a built-in subcommand
235 config
.shell().warn(format
!(
236 "user-defined alias `{}` is ignored, because it is shadowed by a built-in command",
241 // Command is built-in and is not conflicting with alias, but contains ignored values.
242 if let Some(mut values
) = args
.get_many
::<String
>("") {
243 config
.shell().warn(format
!(
244 "trailing arguments after built-in command `{}` are ignored: `{}`",
251 (_
, Some(mut alias
)) => {
252 // Check if this alias is shadowing an external subcommand
253 // (binary of the form `cargo-<subcommand>`)
254 // Currently this is only a warning, but after a transition period this will become
256 if let Some(path
) = super::find_external_subcommand(config
, cmd
) {
257 config
.shell().warn(format
!(
259 user-defined alias `{}` is shadowing an external subcommand found at: `{}`
260 This was previously accepted but is being phased out; it will become a hard error in a future release.
261 For more information, see issue #10049 <https://github.com/rust-lang/cargo/issues/10049>.",
267 alias
.extend(args
.get_many
::<String
>("").unwrap_or_default().cloned());
268 // new_args strips out everything before the subcommand, so
269 // capture those global options now.
270 // Note that an alias to an external command will not receive
271 // these arguments. That may be confusing, but such is life.
272 let global_args
= GlobalArgs
::new(args
);
273 let new_args
= cli().no_binary_name(true).try_get_matches_from(alias
)?
;
275 let new_cmd
= new_args
.subcommand_name().expect("subcommand is required");
276 already_expanded
.push(cmd
.to_string());
277 if already_expanded
.contains(&new_cmd
.to_string()) {
278 // Crash if the aliases are corecursive / unresolvable
280 "alias {} has unresolvable recursive definition: {} -> {}",
282 already_expanded
.join(" -> "),
288 let (expanded_args
, _
) = expand_aliases(config
, new_args
, already_expanded
)?
;
289 return Ok((expanded_args
, global_args
));
294 Ok((args
, GlobalArgs
::default()))
300 subcommand_args
: &ArgMatches
,
301 global_args
: GlobalArgs
,
303 let arg_target_dir
= &subcommand_args
.value_of_path("target-dir", config
);
304 let verbose
= global_args
.verbose
+ args
.verbose();
305 // quiet is unusual because it is redefined in some subcommands in order
306 // to provide custom help text.
307 let quiet
= args
.flag("quiet") || subcommand_args
.flag("quiet") || global_args
.quiet
;
308 let global_color
= global_args
.color
; // Extract so it can take reference.
310 .get_one
::<String
>("color")
312 .or_else(|| global_color
.as_deref());
313 let frozen
= args
.flag("frozen") || global_args
.frozen
;
314 let locked
= args
.flag("locked") || global_args
.locked
;
315 let offline
= args
.flag("offline") || global_args
.offline
;
316 let mut unstable_flags
= global_args
.unstable_flags
;
317 if let Some(values
) = args
.get_many
::<String
>("unstable-features") {
318 unstable_flags
.extend(values
.cloned());
320 let mut config_args
= global_args
.config_args
;
321 if let Some(values
) = args
.get_many
::<String
>("config") {
322 config_args
.extend(values
.cloned());
338 fn execute_subcommand(config
: &mut Config
, cmd
: &str, subcommand_args
: &ArgMatches
) -> CliResult
{
339 if let Some(exec
) = commands
::builtin_exec(cmd
) {
340 return exec(config
, subcommand_args
);
343 let mut ext_args
: Vec
<&str> = vec
![cmd
];
346 .get_many
::<String
>("")
348 .map(String
::as_str
),
350 super::execute_external_subcommand(config
, cmd
, &ext_args
)
357 color
: Option
<String
>,
361 unstable_flags
: Vec
<String
>,
362 config_args
: Vec
<String
>,
366 fn new(args
: &ArgMatches
) -> GlobalArgs
{
368 verbose
: args
.verbose(),
369 quiet
: args
.flag("quiet"),
370 color
: args
.get_one
::<String
>("color").cloned(),
371 frozen
: args
.flag("frozen"),
372 locked
: args
.flag("locked"),
373 offline
: args
.flag("offline"),
375 .get_many
::<String
>("unstable-features")
380 .get_many
::<String
>("config")
388 pub fn cli() -> App
{
389 let is_rustup
= std
::env
::var_os("RUSTUP_HOME").is_some();
390 let usage
= if is_rustup
{
391 "cargo [+toolchain] [OPTIONS] [SUBCOMMAND]"
393 "cargo [OPTIONS] [SUBCOMMAND]"
396 .allow_external_subcommands(true)
397 .setting(AppSettings
::DeriveDisplayOrder
)
398 // Doesn't mix well with our list of common cargo commands. See clap-rs/clap#3108 for
399 // opening clap up to allow us to style our help template
400 .disable_colored_help(true)
401 // Provide a custom help subcommand for calling into man pages
402 .disable_help_subcommand(true)
403 .override_usage(usage
)
406 Rust's package manager
414 Some common cargo commands are (see all commands with --list):
415 build, b Compile the current package
416 check, c Analyze the current package and report errors, but don't build object files
417 clean Remove the target directory
418 doc, d Build this package's and its dependencies' documentation
419 new Create a new cargo package
420 init Create a new cargo package in an existing directory
421 add Add dependencies to a manifest file
422 run, r Run a binary or example of the local package
423 test, t Run the tests
424 bench Run the benchmarks
425 update Update dependencies listed in Cargo.lock
426 search Search registry for crates
427 publish Package and upload this package to the registry
428 install Install a Rust binary. Default location is $HOME/.cargo/bin
429 uninstall Uninstall a Rust binary
431 See 'cargo help <command>' for more information on a specific command.\n",
433 .arg(flag("version", "Print version info and exit").short('V'
))
434 .arg(flag("list", "List installed commands"))
435 .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
439 "Use verbose output (-vv very verbose/build.rs output)",
442 .action(ArgAction
::Count
)
447 opt("color", "Coloring: auto, always, never")
451 .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
452 .arg(flag("locked", "Require Cargo.lock is up to date").global(true))
453 .arg(flag("offline", "Run without accessing the network").global(true))
454 .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
456 Arg
::new("unstable-features")
457 .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
460 .action(ArgAction
::Append
)
463 .subcommands(commands
::builtin())
468 cli().debug_assert();