2 use cargo
::core
::shell
::Shell
;
3 use cargo
::core
::{features, CliUnstable}
;
4 use cargo
::{self, drop_print, drop_println, CliResult, Config}
;
5 use clap
::{AppSettings, Arg, ArgMatches}
;
6 use itertools
::Itertools
;
7 use std
::collections
::HashMap
;
11 use super::list_commands
;
12 use crate::command_prelude
::*;
13 use cargo
::core
::features
::HIDDEN
;
15 lazy_static
::lazy_static
! {
16 // Maps from commonly known external commands (not builtin to cargo) to their
17 // description, for the help page. Reserved for external subcommands that are
18 // core within the rust ecosystem (esp ones that might become internal in the future).
19 static ref KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS
: HashMap
<&'
static str, &'
static str> = HashMap
::from([
20 ("clippy", "Checks a package to catch common mistakes and improve your Rust code."),
21 ("fmt", "Formats all bin and lib files of the current crate using rustfmt."),
25 pub fn main(config
: &mut LazyConfig
) -> CliResult
{
26 let args
= cli().try_get_matches()?
;
28 // CAUTION: Be careful with using `config` until it is configured below.
29 // In general, try to avoid loading config values unless necessary (like
30 // the [alias] table).
31 let config
= config
.get_mut();
33 // Global args need to be extracted before expanding aliases because the
34 // clap code for extracting a subcommand discards global options
35 // (appearing before the subcommand).
36 let (expanded_args
, global_args
) = expand_aliases(config
, args
, vec
![])?
;
39 .get_one
::<String
>("unstable-features")
43 let options
= CliUnstable
::help();
44 let non_hidden_options
: Vec
<(String
, String
)> = options
46 .filter(|(_
, help_message
)| *help_message
!= HIDDEN
)
47 .map(|(name
, help
)| (name
.to_string(), help
.to_string()))
49 let longest_option
= non_hidden_options
51 .map(|(option_name
, _
)| option_name
.len())
54 let help_lines
: Vec
<String
> = non_hidden_options
56 .map(|(option_name
, option_help_message
)| {
57 let option_name_kebab_case
= option_name
.replace("_", "-");
58 let padding
= " ".repeat(longest_option
- option_name
.len()); // safe to subtract
61 option_name_kebab_case
, padding
, option_help_message
65 let joined
= help_lines
.join("\n");
69 Available unstable (nightly-only) flags:
73 Run with 'cargo -Z [FLAG] [SUBCOMMAND]'",
76 if !config
.nightly_features_allowed
{
79 "\nUnstable flags are only available on the nightly channel \
80 of Cargo, but this is the `{}` channel.\n\
83 features
::SEE_CHANNELS
88 "\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
89 for more information about these flags."
94 let is_verbose
= expanded_args
.verbose() > 0;
95 if expanded_args
.flag("version") {
96 let version
= get_version_string(is_verbose
);
97 drop_print
!(config
, "{}", version
);
101 if let Some(code
) = expanded_args
.get_one
::<String
>("explain") {
102 let mut procss
= config
.load_global_rustc(None
)?
.process();
103 procss
.arg("--explain").arg(code
).exec()?
;
107 if expanded_args
.flag("list") {
108 drop_println
!(config
, "Installed Commands:");
109 for (name
, command
) in list_commands(config
) {
110 let known_external_desc
= KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS
.get(name
.as_str());
112 CommandInfo
::BuiltIn { about }
=> {
114 known_external_desc
.is_none(),
115 "KNOWN_EXTERNAL_COMMANDS shouldn't contain builtin \"{}\"",
118 let summary
= about
.unwrap_or_default();
119 let summary
= summary
.lines().next().unwrap_or(&summary
); // display only the first line
120 drop_println
!(config
, " {:<20} {}", name
, summary
);
122 CommandInfo
::External { path }
=> {
123 if let Some(desc
) = known_external_desc
{
124 drop_println
!(config
, " {:<20} {}", name
, desc
);
125 } else if is_verbose
{
126 drop_println
!(config
, " {:<20} {}", name
, path
.display());
128 drop_println
!(config
, " {}", name
);
131 CommandInfo
::Alias { target }
=> {
136 target
.iter().join(" ")
144 let (cmd
, subcommand_args
) = match expanded_args
.subcommand() {
145 Some((cmd
, args
)) => (cmd
, args
),
147 // No subcommand provided.
152 config_configure(config
, &expanded_args
, subcommand_args
, global_args
)?
;
153 super::init_git_transports(config
);
155 execute_subcommand(config
, cmd
, subcommand_args
)
158 pub fn get_version_string(is_verbose
: bool
) -> String
{
159 let version
= cargo
::version();
160 let mut version_string
= format
!("cargo {}\n", version
);
162 version_string
.push_str(&format
!("release: {}\n", version
.version
));
163 if let Some(ref ci
) = version
.commit_info
{
164 version_string
.push_str(&format
!("commit-hash: {}\n", ci
.commit_hash
));
165 version_string
.push_str(&format
!("commit-date: {}\n", ci
.commit_date
));
167 writeln
!(version_string
, "host: {}", env
!("RUST_HOST_TARGET")).unwrap();
168 add_libgit2(&mut version_string
);
169 add_curl(&mut version_string
);
170 add_ssl(&mut version_string
);
171 writeln
!(version_string
, "os: {}", os_info
::get()).unwrap();
176 fn add_libgit2(version_string
: &mut String
) {
177 let git2_v
= git2
::Version
::get();
178 let lib_v
= git2_v
.libgit2_version();
179 let vendored
= if git2_v
.vendored() {
186 "libgit2: {}.{}.{} (sys:{} {})",
190 git2_v
.crate_version(),
196 fn add_curl(version_string
: &mut String
) {
197 let curl_v
= curl
::Version
::get();
198 let vendored
= if curl_v
.vendored() {
205 "libcurl: {} (sys:{} {} ssl:{})",
207 curl_sys
::rust_crate_version(),
209 curl_v
.ssl_version().unwrap_or("none")
214 fn add_ssl(version_string
: &mut String
) {
215 #[cfg(feature = "openssl")]
217 writeln
!(version_string
, "ssl: {}", openssl
::version
::version()).unwrap();
219 #[cfg(not(feature = "openssl"))]
221 let _
= version_string
; // Silence unused warning.
228 mut already_expanded
: Vec
<String
>,
229 ) -> Result
<(ArgMatches
, GlobalArgs
), CliError
> {
230 if let Some((cmd
, args
)) = args
.subcommand() {
232 commands
::builtin_exec(cmd
),
233 super::aliased_command(config
, cmd
)?
,
235 (Some(_
), Some(_
)) => {
236 // User alias conflicts with a built-in subcommand
237 config
.shell().warn(format
!(
238 "user-defined alias `{}` is ignored, because it is shadowed by a built-in command",
243 // Command is built-in and is not conflicting with alias, but contains ignored values.
244 if let Some(mut values
) = args
.get_many
::<String
>("") {
245 config
.shell().warn(format
!(
246 "trailing arguments after built-in command `{}` are ignored: `{}`",
253 (_
, Some(mut alias
)) => {
254 // Check if this alias is shadowing an external subcommand
255 // (binary of the form `cargo-<subcommand>`)
256 // Currently this is only a warning, but after a transition period this will become
258 if let Some(path
) = super::find_external_subcommand(config
, cmd
) {
259 config
.shell().warn(format
!(
261 user-defined alias `{}` is shadowing an external subcommand found at: `{}`
262 This was previously accepted but is being phased out; it will become a hard error in a future release.
263 For more information, see issue #10049 <https://github.com/rust-lang/cargo/issues/10049>.",
269 alias
.extend(args
.get_many
::<String
>("").unwrap_or_default().cloned());
270 // new_args strips out everything before the subcommand, so
271 // capture those global options now.
272 // Note that an alias to an external command will not receive
273 // these arguments. That may be confusing, but such is life.
274 let global_args
= GlobalArgs
::new(args
);
275 let new_args
= cli().no_binary_name(true).try_get_matches_from(alias
)?
;
277 let new_cmd
= new_args
.subcommand_name().expect("subcommand is required");
278 already_expanded
.push(cmd
.to_string());
279 if already_expanded
.contains(&new_cmd
.to_string()) {
280 // Crash if the aliases are corecursive / unresolvable
282 "alias {} has unresolvable recursive definition: {} -> {}",
284 already_expanded
.join(" -> "),
290 let (expanded_args
, _
) = expand_aliases(config
, new_args
, already_expanded
)?
;
291 return Ok((expanded_args
, global_args
));
296 Ok((args
, GlobalArgs
::default()))
302 subcommand_args
: &ArgMatches
,
303 global_args
: GlobalArgs
,
305 let arg_target_dir
= &subcommand_args
.value_of_path("target-dir", config
);
306 let verbose
= global_args
.verbose
+ args
.verbose();
307 // quiet is unusual because it is redefined in some subcommands in order
308 // to provide custom help text.
309 let quiet
= args
.flag("quiet") || subcommand_args
.flag("quiet") || global_args
.quiet
;
310 let global_color
= global_args
.color
; // Extract so it can take reference.
312 .get_one
::<String
>("color")
314 .or_else(|| global_color
.as_deref());
315 let frozen
= args
.flag("frozen") || global_args
.frozen
;
316 let locked
= args
.flag("locked") || global_args
.locked
;
317 let offline
= args
.flag("offline") || global_args
.offline
;
318 let mut unstable_flags
= global_args
.unstable_flags
;
319 if let Some(values
) = args
.get_many
::<String
>("unstable-features") {
320 unstable_flags
.extend(values
.cloned());
322 let mut config_args
= global_args
.config_args
;
323 if let Some(values
) = args
.get_many
::<String
>("config") {
324 config_args
.extend(values
.cloned());
340 fn execute_subcommand(config
: &mut Config
, cmd
: &str, subcommand_args
: &ArgMatches
) -> CliResult
{
341 if let Some(exec
) = commands
::builtin_exec(cmd
) {
342 return exec(config
, subcommand_args
);
345 let mut ext_args
: Vec
<&str> = vec
![cmd
];
348 .get_many
::<String
>("")
350 .map(String
::as_str
),
352 super::execute_external_subcommand(config
, cmd
, &ext_args
)
359 color
: Option
<String
>,
363 unstable_flags
: Vec
<String
>,
364 config_args
: Vec
<String
>,
368 fn new(args
: &ArgMatches
) -> GlobalArgs
{
370 verbose
: args
.verbose(),
371 quiet
: args
.flag("quiet"),
372 color
: args
.get_one
::<String
>("color").cloned(),
373 frozen
: args
.flag("frozen"),
374 locked
: args
.flag("locked"),
375 offline
: args
.flag("offline"),
377 .get_many
::<String
>("unstable-features")
382 .get_many
::<String
>("config")
390 pub fn cli() -> App
{
391 let is_rustup
= std
::env
::var_os("RUSTUP_HOME").is_some();
392 let usage
= if is_rustup
{
393 "cargo [+toolchain] [OPTIONS] [SUBCOMMAND]"
395 "cargo [OPTIONS] [SUBCOMMAND]"
398 .allow_external_subcommands(true)
399 .setting(AppSettings
::DeriveDisplayOrder
)
400 // Doesn't mix well with our list of common cargo commands. See clap-rs/clap#3108 for
401 // opening clap up to allow us to style our help template
402 .disable_colored_help(true)
403 // Provide a custom help subcommand for calling into man pages
404 .disable_help_subcommand(true)
405 .override_usage(usage
)
408 Rust's package manager
416 Some common cargo commands are (see all commands with --list):
417 build, b Compile the current package
418 check, c Analyze the current package and report errors, but don't build object files
419 clean Remove the target directory
420 doc, d Build this package's and its dependencies' documentation
421 new Create a new cargo package
422 init Create a new cargo package in an existing directory
423 add Add dependencies to a manifest file
424 run, r Run a binary or example of the local package
425 test, t Run the tests
426 bench Run the benchmarks
427 update Update dependencies listed in Cargo.lock
428 search Search registry for crates
429 publish Package and upload this package to the registry
430 install Install a Rust binary. Default location is $HOME/.cargo/bin
431 uninstall Uninstall a Rust binary
433 See 'cargo help <command>' for more information on a specific command.\n",
435 .arg(flag("version", "Print version info and exit").short('V'
))
436 .arg(flag("list", "List installed commands"))
437 .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
441 "Use verbose output (-vv very verbose/build.rs output)",
444 .action(ArgAction
::Count
)
449 opt("color", "Coloring: auto, always, never")
453 .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
454 .arg(flag("locked", "Require Cargo.lock is up to date").global(true))
455 .arg(flag("offline", "Run without accessing the network").global(true))
456 .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
458 Arg
::new("unstable-features")
459 .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
462 .action(ArgAction
::Append
)
465 .subcommands(commands
::builtin())
468 /// Delay loading [`Config`] until access.
470 /// In the common path, the [`Config`] is dependent on CLI parsing and shouldn't be loaded until
471 /// after that is done but some other paths (like fix or earlier errors) might need access to it,
472 /// so this provides a way to share the instance and the implementation across these different
474 pub struct LazyConfig
{
475 config
: Option
<Config
>,
479 pub fn new() -> Self {
480 Self { config: None }
483 /// Get the config, loading it if needed
485 /// On error, the process is terminated
486 pub fn get(&mut self) -> &Config
{
490 /// Get the config, loading it if needed
492 /// On error, the process is terminated
493 pub fn get_mut(&mut self) -> &mut Config
{
494 self.config
.get_or_insert_with(|| match Config
::default() {
497 let mut shell
= Shell
::new();
498 cargo
::exit_with_error(e
.into(), &mut shell
)
506 cli().debug_assert();