1 use crate::core
::compiler
::{BuildConfig, MessageFormat}
;
2 use crate::core
::resolver
::CliFeatures
;
3 use crate::core
::{Edition, Workspace}
;
4 use crate::ops
::{CompileFilter, CompileOptions, NewOptions, Packages, VersionControl}
;
5 use crate::sources
::CRATES_IO_REGISTRY
;
6 use crate::util
::important_paths
::find_root_manifest_for_wd
;
7 use crate::util
::interning
::InternedString
;
8 use crate::util
::restricted_names
::is_glob_pattern
;
9 use crate::util
::toml
::{StringOrVec, TomlProfile}
;
10 use crate::util
::validate_package_name
;
12 print_available_benches
, print_available_binaries
, print_available_examples
,
13 print_available_packages
, print_available_tests
,
15 use crate::CargoResult
;
17 use cargo_util
::paths
;
18 use clap
::{self, SubCommand}
;
19 use std
::ffi
::{OsStr, OsString}
;
20 use std
::path
::PathBuf
;
22 pub use crate::core
::compiler
::CompileMode
;
23 pub use crate::{CliError, CliResult, Config}
;
24 pub use clap
::{AppSettings, Arg, ArgMatches}
;
26 pub type App
= clap
::App
<'
static, '
static>;
28 pub trait AppExt
: Sized
{
29 fn _arg(self, arg
: Arg
<'
static, '
static>) -> Self;
31 /// Do not use this method, it is only for backwards compatibility.
32 /// Use `arg_package_spec_no_all` instead.
35 package
: &'
static str,
37 exclude
: &'
static str,
39 self.arg_package_spec_no_all(package
, all
, exclude
)
40 ._arg(opt("all", "Alias for --workspace (deprecated)"))
43 /// Variant of arg_package_spec that does not include the `--all` flag
44 /// (but does include `--workspace`). Used to avoid confusion with
45 /// historical uses of `--all`.
46 fn arg_package_spec_no_all(
48 package
: &'
static str,
50 exclude
: &'
static str,
52 self.arg_package_spec_simple(package
)
53 ._arg(opt("workspace", all
))
54 ._arg(multi_opt("exclude", "SPEC", exclude
))
57 fn arg_package_spec_simple(self, package
: &'
static str) -> Self {
58 self._arg(optional_multi_opt("package", "SPEC", package
).short("p"))
61 fn arg_package(self, package
: &'
static str) -> Self {
63 optional_opt("package", package
)
69 fn arg_jobs(self) -> Self {
71 opt("jobs", "Number of parallel jobs, defaults to # of CPUs")
82 example
: &'
static str,
83 examples
: &'
static str,
87 benches
: &'
static str,
90 self.arg_targets_lib_bin_example(lib
, bin
, bins
, example
, examples
)
91 ._arg(optional_multi_opt("test", "NAME", test
))
92 ._arg(opt("tests", tests
))
93 ._arg(optional_multi_opt("bench", "NAME", bench
))
94 ._arg(opt("benches", benches
))
95 ._arg(opt("all-targets", all
))
98 fn arg_targets_lib_bin_example(
103 example
: &'
static str,
104 examples
: &'
static str,
106 self._arg(opt("lib", lib
))
107 ._arg(optional_multi_opt("bin", "NAME", bin
))
108 ._arg(opt("bins", bins
))
109 ._arg(optional_multi_opt("example", "NAME", example
))
110 ._arg(opt("examples", examples
))
113 fn arg_targets_bins_examples(
117 example
: &'
static str,
118 examples
: &'
static str,
120 self._arg(optional_multi_opt("bin", "NAME", bin
))
121 ._arg(opt("bins", bins
))
122 ._arg(optional_multi_opt("example", "NAME", example
))
123 ._arg(opt("examples", examples
))
126 fn arg_targets_bin_example(self, bin
: &'
static str, example
: &'
static str) -> Self {
127 self._arg(optional_multi_opt("bin", "NAME", bin
))
128 ._arg(optional_multi_opt("example", "NAME", example
))
131 fn arg_features(self) -> Self {
135 "Space or comma separated list of features to activate",
137 ._arg(opt("all-features", "Activate all available features"))
139 "no-default-features",
140 "Do not activate the `default` feature",
144 fn arg_release(self, release
: &'
static str) -> Self {
145 self._arg(opt("release", release
))
148 fn arg_profile(self, profile
: &'
static str) -> Self {
149 self._arg(opt("profile", profile
).value_name("PROFILE-NAME"))
152 fn arg_doc(self, doc
: &'
static str) -> Self {
153 self._arg(opt("doc", doc
))
156 fn arg_target_triple(self, target
: &'
static str) -> Self {
157 self._arg(multi_opt("target", "TRIPLE", target
))
160 fn arg_target_dir(self) -> Self {
162 opt("target-dir", "Directory for all generated artifacts").value_name("DIRECTORY"),
166 fn arg_manifest_path(self) -> Self {
167 self._arg(opt("manifest-path", "Path to Cargo.toml").value_name("PATH"))
170 fn arg_message_format(self) -> Self {
171 self._arg(multi_opt("message-format", "FMT", "Error format"))
174 fn arg_build_plan(self) -> Self {
177 "Output the build plan in JSON (unstable)",
181 fn arg_unit_graph(self) -> Self {
182 self._arg(opt("unit-graph", "Output build graph in JSON (unstable)"))
185 fn arg_new_opts(self) -> Self {
189 "Initialize a new repository for the given version \
190 control system (git, hg, pijul, or fossil) or do not \
191 initialize any version control at all (none), overriding \
192 a global configuration.",
195 .possible_values(&["git", "hg", "pijul", "fossil", "none"]),
197 ._arg(opt("bin", "Use a binary (application) template [default]"))
198 ._arg(opt("lib", "Use a library template"))
200 opt("edition", "Edition to set for the crate generated")
201 .possible_values(Edition
::CLI_VALUES
)
207 "Set the resulting package name, defaults to the directory name",
213 fn arg_index(self) -> Self {
214 self._arg(opt("index", "Registry index URL to upload the package to").value_name("INDEX"))
216 opt("host", "DEPRECATED, renamed to '--index'")
222 fn arg_dry_run(self, dry_run
: &'
static str) -> Self {
223 self._arg(opt("dry-run", dry_run
))
226 fn arg_ignore_rust_version(self) -> Self {
228 "ignore-rust-version",
229 "Ignore `rust-version` specification in packages",
233 fn arg_future_incompat_report(self) -> Self {
235 "future-incompat-report",
236 "Outputs a future incompatibility report at the end of the build (unstable)",
241 impl AppExt
for App
{
242 fn _arg(self, arg
: Arg
<'
static, '
static>) -> Self {
247 pub fn opt(name
: &'
static str, help
: &'
static str) -> Arg
<'
static, '
static> {
248 Arg
::with_name(name
).long(name
).help(help
)
251 pub fn optional_opt(name
: &'
static str, help
: &'
static str) -> Arg
<'
static, '
static> {
252 opt(name
, help
).min_values(0)
255 pub fn optional_multi_opt(
257 value_name
: &'
static str,
259 ) -> Arg
<'
static, '
static> {
261 .value_name(value_name
)
269 value_name
: &'
static str,
271 ) -> Arg
<'
static, '
static> {
272 // Note that all `.multiple(true)` arguments in Cargo should specify
273 // `.number_of_values(1)` as well, so that `--foo val1 val2` is
274 // *not* parsed as `foo` with values ["val1", "val2"].
275 // `number_of_values` should become the default in clap 3.
277 .value_name(value_name
)
282 pub fn subcommand(name
: &'
static str) -> App
{
283 SubCommand
::with_name(name
).settings(&[
284 AppSettings
::UnifiedHelpMessage
,
285 AppSettings
::DeriveDisplayOrder
,
286 AppSettings
::DontCollapseArgsInUsage
,
290 // Determines whether or not to gate `--profile` as unstable when resolving it.
291 pub enum ProfileChecking
{
292 // `cargo rustc` historically has allowed "test", "bench", and "check". This
293 // variant explicitly allows those.
295 // `cargo check` and `cargo fix` historically has allowed "test". This variant
296 // explicitly allows that on stable.
298 // All other commands, which allow any valid custom named profile.
302 pub trait ArgMatchesExt
{
303 fn value_of_u32(&self, name
: &str) -> CargoResult
<Option
<u32>> {
304 let arg
= match self._value_of(name
) {
306 Some(arg
) => Some(arg
.parse
::<u32>().map_err(|_
| {
307 clap
::Error
::value_validation_auto(format
!("could not parse `{}` as a number", arg
))
313 /// Returns value of the `name` command-line argument as an absolute path
314 fn value_of_path(&self, name
: &str, config
: &Config
) -> Option
<PathBuf
> {
315 self._value_of(name
).map(|path
| config
.cwd().join(path
))
318 fn root_manifest(&self, config
: &Config
) -> CargoResult
<PathBuf
> {
319 if let Some(path
) = self.value_of_path("manifest-path", config
) {
320 // In general, we try to avoid normalizing paths in Cargo,
321 // but in this particular case we need it to fix #3586.
322 let path
= paths
::normalize_path(&path
);
323 if !path
.ends_with("Cargo.toml") {
324 anyhow
::bail
!("the manifest-path must be a path to a Cargo.toml file")
328 "manifest path `{}` does not exist",
329 self._value_of("manifest-path").unwrap()
334 find_root_manifest_for_wd(config
.cwd())
337 fn workspace
<'a
>(&self, config
: &'a Config
) -> CargoResult
<Workspace
<'a
>> {
338 let root
= self.root_manifest(config
)?
;
339 let mut ws
= Workspace
::new(&root
, config
)?
;
340 if config
.cli_unstable().avoid_dev_deps
{
341 ws
.set_require_optional_deps(false);
346 fn jobs(&self) -> CargoResult
<Option
<u32>> {
347 self.value_of_u32("jobs")
350 fn targets(&self) -> Vec
<String
> {
351 self._values_of("target")
358 profile_checking
: ProfileChecking
,
359 ) -> CargoResult
<InternedString
> {
360 let specified_profile
= self._value_of("profile");
362 // Check for allowed legacy names.
363 // This is an early exit, since it allows combination with `--release`.
364 match (specified_profile
, profile_checking
) {
365 // `cargo rustc` has legacy handling of these names
366 (Some(name @
("dev" | "test" | "bench" | "check")), ProfileChecking
::LegacyRustc
) |
367 // `cargo fix` and `cargo check` has legacy handling of this profile name
368 (Some(name @
"test"), ProfileChecking
::LegacyTestOnly
) => return Ok(InternedString
::new(name
)),
372 let conflict
= |flag
: &str, equiv
: &str, specified
: &str| -> anyhow
::Error
{
374 "conflicting usage of --profile={} and --{flag}\n\
375 The `--{flag}` flag is the same as `--profile={equiv}`.\n\
376 Remove one flag or the other to continue.",
384 self._is_present("release"),
385 self._is_present("debug"),
388 (false, false, None
) => default,
389 (true, _
, None
| Some("release")) => "release",
390 (true, _
, Some(name
)) => return Err(conflict("release", "release", name
)),
391 (_
, true, None
| Some("dev")) => "dev",
392 (_
, true, Some(name
)) => return Err(conflict("debug", "dev", name
)),
393 // `doc` is separate from all the other reservations because
394 // [profile.doc] was historically allowed, but is deprecated and
395 // has no effect. To avoid potentially breaking projects, it is a
396 // warning in Cargo.toml, but since `--profile` is new, we can
397 // reject it completely here.
398 (_
, _
, Some("doc")) => {
399 bail
!("profile `doc` is reserved and not allowed to be explicitly specified")
401 (_
, _
, Some(name
)) => {
402 TomlProfile
::validate_name(name
)?
;
407 Ok(InternedString
::new(name
))
410 fn packages_from_flags(&self) -> CargoResult
<Packages
> {
411 Packages
::from_flags(
412 // TODO Integrate into 'workspace'
413 self._is_present("workspace") || self._is_present("all"),
414 self._values_of("exclude"),
415 self._values_of("package"),
423 workspace
: Option
<&Workspace
<'_
>>,
424 profile_checking
: ProfileChecking
,
425 ) -> CargoResult
<CompileOptions
> {
426 let spec
= self.packages_from_flags()?
;
427 let mut message_format
= None
;
428 let default_json
= MessageFormat
::Json
{
431 render_diagnostics
: false,
433 for fmt
in self._values_of("message-format") {
434 for fmt
in fmt
.split('
,'
) {
435 let fmt
= fmt
.to_ascii_lowercase();
438 if message_format
.is_some() {
439 bail
!("cannot specify two kinds of `message-format` arguments");
441 message_format
= Some(default_json
);
444 if message_format
.is_some() {
445 bail
!("cannot specify two kinds of `message-format` arguments");
447 message_format
= Some(MessageFormat
::Human
);
450 if message_format
.is_some() {
451 bail
!("cannot specify two kinds of `message-format` arguments");
453 message_format
= Some(MessageFormat
::Short
);
455 "json-render-diagnostics" => {
456 if message_format
.is_none() {
457 message_format
= Some(default_json
);
459 match &mut message_format
{
460 Some(MessageFormat
::Json
{
461 render_diagnostics
, ..
462 }) => *render_diagnostics
= true,
463 _
=> bail
!("cannot specify two kinds of `message-format` arguments"),
466 "json-diagnostic-short" => {
467 if message_format
.is_none() {
468 message_format
= Some(default_json
);
470 match &mut message_format
{
471 Some(MessageFormat
::Json { short, .. }
) => *short
= true,
472 _
=> bail
!("cannot specify two kinds of `message-format` arguments"),
475 "json-diagnostic-rendered-ansi" => {
476 if message_format
.is_none() {
477 message_format
= Some(default_json
);
479 match &mut message_format
{
480 Some(MessageFormat
::Json { ansi, .. }
) => *ansi
= true,
481 _
=> bail
!("cannot specify two kinds of `message-format` arguments"),
484 s
=> bail
!("invalid message format specifier: `{}`", s
),
489 let mut build_config
= BuildConfig
::new(config
, self.jobs()?
, &self.targets(), mode
)?
;
490 build_config
.message_format
= message_format
.unwrap_or(MessageFormat
::Human
);
491 build_config
.requested_profile
= self.get_profile_name(config
, "dev", profile_checking
)?
;
492 build_config
.build_plan
= self._is_present("build-plan");
493 build_config
.unit_graph
= self._is_present("unit-graph");
494 build_config
.future_incompat_report
= self._is_present("future-incompat-report");
495 if build_config
.build_plan
{
498 .fail_if_stable_opt("--build-plan", 5579)?
;
500 if build_config
.unit_graph
{
503 .fail_if_stable_opt("--unit-graph", 8002)?
;
505 if build_config
.future_incompat_report
{
508 .fail_if_stable_opt("--future-incompat-report", 9241)?
;
510 if !config
.cli_unstable().future_incompat_report
{
512 "Usage of `--future-incompat-report` requires `-Z future-incompat-report`"
517 let opts
= CompileOptions
{
519 cli_features
: self.cli_features()?
,
521 filter
: CompileFilter
::from_raw_arguments(
522 self._is_present("lib"),
523 self._values_of("bin"),
524 self._is_present("bins"),
525 self._values_of("test"),
526 self._is_present("tests"),
527 self._values_of("example"),
528 self._is_present("examples"),
529 self._values_of("bench"),
530 self._is_present("benches"),
531 self._is_present("all-targets"),
533 target_rustdoc_args
: None
,
534 target_rustc_args
: None
,
535 local_rustdoc_args
: None
,
536 rustdoc_document_private_items
: false,
537 honor_rust_version
: !self._is_present("ignore-rust-version"),
540 if let Some(ws
) = workspace
{
541 self.check_optional_opts(ws
, &opts
)?
;
542 } else if self.is_present_with_zero_values("package") {
543 // As for cargo 0.50.0, this won't occur but if someone sneaks in
544 // we can still provide this informative message for them.
546 "\"--package <SPEC>\" requires a SPEC format value, \
547 which can be any package ID specifier in the dependency graph.\n\
548 Run `cargo help pkgid` for more information about SPEC format."
555 fn cli_features(&self) -> CargoResult
<CliFeatures
> {
556 CliFeatures
::from_command_line(
557 &self._values_of("features"),
558 self._is_present("all-features"),
559 !self._is_present("no-default-features"),
563 fn compile_options_for_single_package(
567 workspace
: Option
<&Workspace
<'_
>>,
568 profile_checking
: ProfileChecking
,
569 ) -> CargoResult
<CompileOptions
> {
570 let mut compile_opts
= self.compile_options(config
, mode
, workspace
, profile_checking
)?
;
571 let spec
= self._values_of("package");
572 if spec
.iter().any(is_glob_pattern
) {
573 anyhow
::bail
!("Glob patterns on package selection are not supported.")
575 compile_opts
.spec
= Packages
::Packages(spec
);
579 fn new_options(&self, config
: &Config
) -> CargoResult
<NewOptions
> {
580 let vcs
= self._value_of("vcs").map(|vcs
| match vcs
{
581 "git" => VersionControl
::Git
,
582 "hg" => VersionControl
::Hg
,
583 "pijul" => VersionControl
::Pijul
,
584 "fossil" => VersionControl
::Fossil
,
585 "none" => VersionControl
::NoVcs
,
586 vcs
=> panic
!("Impossible vcs: {:?}", vcs
),
590 self._is_present("bin"),
591 self._is_present("lib"),
592 self.value_of_path("path", config
).unwrap(),
593 self._value_of("name").map(|s
| s
.to_string()),
594 self._value_of("edition").map(|s
| s
.to_string()),
595 self.registry(config
)?
,
599 fn registry(&self, config
: &Config
) -> CargoResult
<Option
<String
>> {
600 match self._value_of("registry") {
602 validate_package_name(registry
, "registry name", "")?
;
604 if registry
== CRATES_IO_REGISTRY
{
605 // If "crates.io" is specified, then we just need to return `None`,
606 // as that will cause cargo to use crates.io. This is required
607 // for the case where a default alternative registry is used
608 // but the user wants to switch back to crates.io for a single
612 Ok(Some(registry
.to_string()))
615 None
=> config
.default_registry(),
619 fn index(&self, config
: &Config
) -> CargoResult
<Option
<String
>> {
620 // TODO: deprecated. Remove once it has been decided `--host` can be removed
621 // We may instead want to repurpose the host flag, as mentioned in issue
622 // rust-lang/cargo#4208.
623 let msg
= "The flag '--host' is no longer valid.
625 Previous versions of Cargo accepted this flag, but it is being
626 deprecated. The flag is being renamed to 'index', as the flag
627 wants the location of the index. Please use '--index' instead.
629 This will soon become a hard error, so it's either recommended
630 to update to a fixed version or contact the upstream maintainer
631 about this warning.";
633 let index
= match self._value_of("host") {
635 config
.shell().warn(&msg
)?
;
636 Some(host
.to_string())
638 None
=> self._value_of("index").map(|s
| s
.to_string()),
643 fn check_optional_opts(
645 workspace
: &Workspace
<'_
>,
646 compile_opts
: &CompileOptions
,
647 ) -> CargoResult
<()> {
648 if self.is_present_with_zero_values("package") {
649 print_available_packages(workspace
)?
652 if self.is_present_with_zero_values("example") {
653 print_available_examples(workspace
, compile_opts
)?
;
656 if self.is_present_with_zero_values("bin") {
657 print_available_binaries(workspace
, compile_opts
)?
;
660 if self.is_present_with_zero_values("bench") {
661 print_available_benches(workspace
, compile_opts
)?
;
664 if self.is_present_with_zero_values("test") {
665 print_available_tests(workspace
, compile_opts
)?
;
671 fn is_present_with_zero_values(&self, name
: &str) -> bool
{
672 self._is_present(name
) && self._value_of(name
).is_none()
675 fn _value_of(&self, name
: &str) -> Option
<&str>;
677 fn _values_of(&self, name
: &str) -> Vec
<String
>;
679 fn _value_of_os(&self, name
: &str) -> Option
<&OsStr
>;
681 fn _values_of_os(&self, name
: &str) -> Vec
<OsString
>;
683 fn _is_present(&self, name
: &str) -> bool
;
686 impl<'a
> ArgMatchesExt
for ArgMatches
<'a
> {
687 fn _value_of(&self, name
: &str) -> Option
<&str> {
691 fn _value_of_os(&self, name
: &str) -> Option
<&OsStr
> {
692 self.value_of_os(name
)
695 fn _values_of(&self, name
: &str) -> Vec
<String
> {
698 .map(|s
| s
.to_string())
702 fn _values_of_os(&self, name
: &str) -> Vec
<OsString
> {
703 self.values_of_os(name
)
705 .map(|s
| s
.to_os_string())
709 fn _is_present(&self, name
: &str) -> bool
{
710 self.is_present(name
)
714 pub fn values(args
: &ArgMatches
<'_
>, name
: &str) -> Vec
<String
> {
715 args
._values_of(name
)
718 pub fn values_os(args
: &ArgMatches
<'_
>, name
: &str) -> Vec
<OsString
> {
719 args
._values_of_os(name
)
722 #[derive(PartialEq, Eq, PartialOrd, Ord)]
723 pub enum CommandInfo
{
724 BuiltIn { about: Option<String> }
,
725 External { path: PathBuf }
,
726 Alias { target: StringOrVec }
,