| use crate::CargoResult; |
| use crate::compiler::{BuildConfig, CompileKind, MessageFormat, RustcTargetData}; |
| use crate::ops::registry::RegistryOrIndex; |
| use crate::ops::{self, CompileFilter, CompileOptions, NewOptions, Packages, VersionControl}; |
| use crate::resolver::{CliFeatures, ForceAllTargets, HasDevUnits}; |
| use crate::util::data_structures::IndexSet; |
| use crate::util::data_structures::{HashMap, HashSet}; |
| use crate::util::important_paths::find_root_manifest_for_wd; |
| use crate::util::interning::InternedString; |
| use crate::util::is_rustup; |
| use crate::util::restricted_names; |
| use crate::util::{ |
| print_available_benches, print_available_binaries, print_available_examples, |
| print_available_packages, print_available_tests, |
| }; |
| use crate::workspace::Dependency; |
| use crate::workspace::parser::is_embedded; |
| use crate::workspace::{Edition, Package, TargetKind, Workspace, profiles::Profiles}; |
| use anyhow::bail; |
| use cargo_util::paths; |
| use cargo_util_schemas::manifest::ProfileName; |
| use cargo_util_schemas::manifest::RegistryName; |
| use cargo_util_schemas::manifest::StringOrVec; |
| use cargo_util_terminal as shell; |
| use clap::builder::UnknownArgumentValueParser; |
| use clap_complete::ArgValueCandidates; |
| use home::cargo_home_with_cwd; |
| use itertools::Itertools; |
| use semver::Version; |
| use std::collections::BTreeMap; |
| use std::ffi::{OsStr, OsString}; |
| use std::path::Path; |
| use std::path::PathBuf; |
| |
| pub use crate::compiler::UserIntent; |
| pub use crate::{CliError, CliResult, GlobalContext}; |
| pub use clap::{Arg, ArgAction, ArgMatches, value_parser}; |
| |
| pub use clap::Command; |
| |
| use super::IntoUrl; |
| use crate::context::JobsConfig; |
| |
| pub mod heading { |
| pub const PACKAGE_SELECTION: &str = "Package Selection"; |
| pub const TARGET_SELECTION: &str = "Target Selection"; |
| pub const FEATURE_SELECTION: &str = "Feature Selection"; |
| pub const COMPILATION_OPTIONS: &str = "Compilation Options"; |
| pub const MANIFEST_OPTIONS: &str = "Manifest Options"; |
| } |
| |
| pub trait CommandExt: Sized { |
| fn _arg(self, arg: Arg) -> Self; |
| |
| /// Do not use this method, it is only for backwards compatibility. |
| /// Use `arg_package_spec_no_all` instead. |
| fn arg_package_spec( |
| self, |
| package: &'static str, |
| all: &'static str, |
| exclude: &'static str, |
| ) -> Self { |
| self.arg_package_spec_no_all( |
| package, |
| all, |
| exclude, |
| ArgValueCandidates::new(get_ws_member_candidates), |
| ) |
| ._arg( |
| flag("all", "Alias for --workspace (deprecated)") |
| .help_heading(heading::PACKAGE_SELECTION), |
| ) |
| } |
| |
| /// Variant of `arg_package_spec` that does not include the `--all` flag |
| /// (but does include `--workspace`). Used to avoid confusion with |
| /// historical uses of `--all`. |
| fn arg_package_spec_no_all( |
| self, |
| package: &'static str, |
| all: &'static str, |
| exclude: &'static str, |
| package_completion: ArgValueCandidates, |
| ) -> Self { |
| let unsupported_short_arg = { |
| let value_parser = UnknownArgumentValueParser::suggest_arg("--exclude"); |
| Arg::new("unsupported-short-exclude-flag") |
| .help("") |
| .short('x') |
| .value_parser(value_parser) |
| .action(ArgAction::SetTrue) |
| .hide(true) |
| }; |
| self.arg_package_spec_simple(package, package_completion) |
| ._arg(flag("workspace", all).help_heading(heading::PACKAGE_SELECTION)) |
| ._arg( |
| multi_opt("exclude", "SPEC", exclude) |
| .help_heading(heading::PACKAGE_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new( |
| get_ws_member_candidates, |
| )), |
| ) |
| ._arg(unsupported_short_arg) |
| } |
| |
| fn arg_package_spec_simple( |
| self, |
| package: &'static str, |
| package_completion: ArgValueCandidates, |
| ) -> Self { |
| self._arg( |
| optional_multi_opt("package", "SPEC", package) |
| .short('p') |
| .help_heading(heading::PACKAGE_SELECTION) |
| .add(package_completion), |
| ) |
| } |
| |
| fn arg_package(self, package: &'static str) -> Self { |
| self._arg( |
| optional_opt("package", package) |
| .short('p') |
| .value_name("SPEC") |
| .help_heading(heading::PACKAGE_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_ws_member_candidates() |
| })), |
| ) |
| } |
| |
| fn arg_parallel(self) -> Self { |
| self.arg_jobs()._arg( |
| flag( |
| "keep-going", |
| "Do not abort the build as soon as there is an error", |
| ) |
| .help_heading(heading::COMPILATION_OPTIONS), |
| ) |
| } |
| |
| fn arg_jobs(self) -> Self { |
| self._arg( |
| opt("jobs", "Number of parallel jobs, defaults to # of CPUs.") |
| .short('j') |
| .value_name("N") |
| .allow_hyphen_values(true) |
| .help_heading(heading::COMPILATION_OPTIONS), |
| ) |
| } |
| |
| fn arg_unsupported_keep_going(self) -> Self { |
| let msg = "use `--no-fail-fast` to run as many tests as possible regardless of failure"; |
| let value_parser = UnknownArgumentValueParser::suggest(msg); |
| self._arg(flag("keep-going", "").value_parser(value_parser).hide(true)) |
| } |
| |
| fn arg_redundant_default_mode( |
| self, |
| default_mode: &'static str, |
| command: &'static str, |
| supported_mode: &'static str, |
| ) -> Self { |
| let msg = format!( |
| "`--{default_mode}` is the default for `cargo {command}`; instead `--{supported_mode}` is supported" |
| ); |
| let value_parser = UnknownArgumentValueParser::suggest(msg); |
| self._arg( |
| flag(default_mode, "") |
| .conflicts_with("profile") |
| .value_parser(value_parser) |
| .hide(true), |
| ) |
| } |
| |
| fn arg_targets_all( |
| self, |
| lib: &'static str, |
| bin: &'static str, |
| bins: &'static str, |
| example: &'static str, |
| examples: &'static str, |
| test: &'static str, |
| tests: &'static str, |
| bench: &'static str, |
| benches: &'static str, |
| all: &'static str, |
| ) -> Self { |
| self.arg_targets_lib_bin_example(lib, bin, bins, example, examples) |
| ._arg(flag("tests", tests).help_heading(heading::TARGET_SELECTION)) |
| ._arg( |
| optional_multi_opt("test", "NAME", test) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::Test).unwrap_or_default() |
| })), |
| ) |
| ._arg(flag("benches", benches).help_heading(heading::TARGET_SELECTION)) |
| ._arg( |
| optional_multi_opt("bench", "NAME", bench) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::Bench).unwrap_or_default() |
| })), |
| ) |
| ._arg(flag("all-targets", all).help_heading(heading::TARGET_SELECTION)) |
| } |
| |
| fn arg_targets_lib_bin_example( |
| self, |
| lib: &'static str, |
| bin: &'static str, |
| bins: &'static str, |
| example: &'static str, |
| examples: &'static str, |
| ) -> Self { |
| self._arg(flag("lib", lib).help_heading(heading::TARGET_SELECTION)) |
| ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION)) |
| ._arg( |
| optional_multi_opt("bin", "NAME", bin) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::Bin).unwrap_or_default() |
| })), |
| ) |
| ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION)) |
| ._arg( |
| optional_multi_opt("example", "NAME", example) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::ExampleBin).unwrap_or_default() |
| })), |
| ) |
| } |
| |
| fn arg_targets_bins_examples( |
| self, |
| bin: &'static str, |
| bins: &'static str, |
| example: &'static str, |
| examples: &'static str, |
| ) -> Self { |
| self._arg( |
| optional_multi_opt("bin", "NAME", bin) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::Bin).unwrap_or_default() |
| })), |
| ) |
| ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION)) |
| ._arg( |
| optional_multi_opt("example", "NAME", example) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::ExampleBin).unwrap_or_default() |
| })), |
| ) |
| ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION)) |
| } |
| |
| fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self { |
| self._arg( |
| optional_multi_opt("bin", "NAME", bin) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::Bin).unwrap_or_default() |
| })), |
| ) |
| ._arg( |
| optional_multi_opt("example", "NAME", example) |
| .help_heading(heading::TARGET_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_crate_candidates(TargetKind::ExampleBin).unwrap_or_default() |
| })), |
| ) |
| } |
| |
| fn arg_features(self) -> Self { |
| self._arg( |
| multi_opt( |
| "features", |
| "FEATURES", |
| "Space or comma separated list of features to activate", |
| ) |
| .short('F') |
| .help_heading(heading::FEATURE_SELECTION) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| get_feature_candidates().unwrap_or_default() |
| })), |
| ) |
| ._arg( |
| flag("all-features", "Activate all available features") |
| .help_heading(heading::FEATURE_SELECTION), |
| ) |
| ._arg( |
| flag( |
| "no-default-features", |
| "Do not activate the `default` feature", |
| ) |
| .help_heading(heading::FEATURE_SELECTION), |
| ) |
| } |
| |
| fn arg_release(self, release: &'static str) -> Self { |
| self._arg( |
| flag("release", release) |
| .short('r') |
| .conflicts_with("profile") |
| .help_heading(heading::COMPILATION_OPTIONS), |
| ) |
| } |
| |
| fn arg_profile(self, profile: &'static str) -> Self { |
| self._arg( |
| opt("profile", profile) |
| .value_name("PROFILE-NAME") |
| .help_heading(heading::COMPILATION_OPTIONS) |
| .add(clap_complete::ArgValueCandidates::new(|| { |
| let candidates = get_profile_candidates(); |
| candidates |
| })), |
| ) |
| } |
| |
| fn arg_doc(self, doc: &'static str) -> Self { |
| self._arg(flag("doc", doc)) |
| } |
| |
| fn arg_target_triple(self, target: &'static str) -> Self { |
| self.arg_target_triple_with_candidates(target, ArgValueCandidates::new(get_target_triples)) |
| } |
| |
| fn arg_target_triple_with_candidates( |
| self, |
| target: &'static str, |
| target_completion: ArgValueCandidates, |
| ) -> Self { |
| let unsupported_short_arg = { |
| let value_parser = UnknownArgumentValueParser::suggest_arg("--target"); |
| Arg::new("unsupported-short-target-flag") |
| .help("") |
| .short('t') |
| .value_parser(value_parser) |
| .action(ArgAction::SetTrue) |
| .hide(true) |
| }; |
| self._arg( |
| optional_multi_opt("target", "TRIPLE", target) |
| .help_heading(heading::COMPILATION_OPTIONS) |
| .add(target_completion), |
| ) |
| ._arg(unsupported_short_arg) |
| } |
| |
| fn arg_target_dir(self) -> Self { |
| self._arg( |
| opt("target-dir", "Directory for all generated artifacts") |
| .value_name("DIRECTORY") |
| .help_heading(heading::COMPILATION_OPTIONS), |
| ) |
| } |
| |
| fn arg_manifest_path(self) -> Self { |
| // We use `--manifest-path` instead of `--path`. |
| let unsupported_path_arg = { |
| let value_parser = UnknownArgumentValueParser::suggest_arg("--manifest-path"); |
| flag("unsupported-path-flag", "") |
| .long("path") |
| .value_parser(value_parser) |
| .hide(true) |
| }; |
| self.arg_manifest_path_without_unsupported_path_tip() |
| ._arg(unsupported_path_arg) |
| } |
| |
| // `cargo add` has a `--path` flag to install a crate from a local path. |
| fn arg_manifest_path_without_unsupported_path_tip(self) -> Self { |
| self._arg( |
| opt("manifest-path", "Path to Cargo.toml") |
| .short('m') |
| .value_name("PATH") |
| .help_heading(heading::MANIFEST_OPTIONS) |
| .add(clap_complete::engine::ArgValueCompleter::new( |
| clap_complete::engine::PathCompleter::any().filter(|path: &Path| { |
| if path.file_name() == Some(OsStr::new("Cargo.toml")) { |
| return true; |
| } |
| if is_embedded(path) { |
| return true; |
| } |
| false |
| }), |
| )), |
| ) |
| } |
| |
| fn arg_message_format(self) -> Self { |
| self._arg( |
| multi_opt("message-format", "FMT", "Error format") |
| .value_parser([ |
| "human", |
| "short", |
| "json", |
| "json-diagnostic-short", |
| "json-diagnostic-rendered-ansi", |
| "json-render-diagnostics", |
| ]) |
| .value_delimiter(',') |
| .ignore_case(true), |
| ) |
| } |
| |
| fn arg_unit_graph(self) -> Self { |
| self._arg( |
| flag("unit-graph", "Output build graph in JSON (unstable)") |
| .help_heading(heading::COMPILATION_OPTIONS), |
| ) |
| } |
| |
| fn arg_new_opts(self) -> Self { |
| self._arg( |
| opt( |
| "vcs", |
| "Initialize a new repository for the given version \ |
| control system, overriding \ |
| a global configuration.", |
| ) |
| .value_name("VCS") |
| .value_parser(["git", "hg", "pijul", "fossil", "none"]), |
| ) |
| ._arg(flag("bin", "Use a binary (application) template [default]")) |
| ._arg(flag("lib", "Use a library template")) |
| ._arg( |
| opt("edition", "Edition to set for the crate generated") |
| .value_parser(Edition::CLI_VALUES) |
| .value_name("YEAR"), |
| ) |
| ._arg( |
| opt( |
| "name", |
| "Set the resulting package name, defaults to the directory name", |
| ) |
| .value_name("NAME"), |
| ) |
| } |
| |
| fn arg_registry(self, help: &'static str) -> Self { |
| self._arg(opt("registry", help).value_name("REGISTRY").add( |
| clap_complete::ArgValueCandidates::new(|| { |
| let candidates = get_registry_candidates(); |
| candidates.unwrap_or_default() |
| }), |
| )) |
| } |
| |
| fn arg_index(self, help: &'static str) -> Self { |
| // Always conflicts with `--registry`. |
| self._arg( |
| opt("index", help) |
| .value_name("INDEX") |
| .conflicts_with("registry"), |
| ) |
| } |
| |
| fn arg_dry_run(self, dry_run: &'static str) -> Self { |
| self._arg(flag("dry-run", dry_run).short('n')) |
| } |
| |
| fn arg_ignore_rust_version(self) -> Self { |
| self.arg_ignore_rust_version_with_help("Ignore `rust-version` specification in packages") |
| } |
| |
| fn arg_ignore_rust_version_with_help(self, help: &'static str) -> Self { |
| self._arg(flag("ignore-rust-version", help).help_heading(heading::MANIFEST_OPTIONS)) |
| } |
| |
| fn arg_future_incompat_report(self) -> Self { |
| self._arg(flag( |
| "future-incompat-report", |
| "Outputs a future incompatibility report at the end of the build", |
| )) |
| } |
| |
| /// Adds a suggestion for the `--silent` or `-s` flags to use the |
| /// `--quiet` flag instead. This is to help with people familiar with |
| /// other tools that use `-s`. |
| /// |
| /// Every command should call this, unless it has its own `-s` short flag. |
| fn arg_silent_suggestion(self) -> Self { |
| let value_parser = UnknownArgumentValueParser::suggest_arg("--quiet"); |
| self._arg( |
| flag("silent", "") |
| .short('s') |
| .value_parser(value_parser) |
| .hide(true), |
| ) |
| } |
| |
| fn arg_timings(self) -> Self { |
| self._arg( |
| flag( |
| "timings", |
| "Output a build timing report at the end of the build", |
| ) |
| .help_heading(heading::COMPILATION_OPTIONS), |
| ) |
| } |
| |
| fn arg_artifact_dir(self) -> Self { |
| let unsupported_short_arg = { |
| let value_parser = UnknownArgumentValueParser::suggest_arg("--artifact-dir"); |
| Arg::new("unsupported-short-artifact-dir-flag") |
| .help("") |
| .short('O') |
| .value_parser(value_parser) |
| .action(ArgAction::SetTrue) |
| .hide(true) |
| }; |
| |
| self._arg( |
| opt( |
| "artifact-dir", |
| "Copy final artifacts to this directory (unstable)", |
| ) |
| .value_name("PATH") |
| .help_heading(heading::COMPILATION_OPTIONS), |
| ) |
| ._arg(unsupported_short_arg) |
| ._arg({ |
| let value_parser = UnknownArgumentValueParser::suggest_arg("--artifact-dir"); |
| Arg::new("unsupported-out-dir-flag") |
| .help("") |
| .long("out-dir") |
| .value_name("PATH") |
| .value_parser(value_parser) |
| .action(ArgAction::SetTrue) |
| .hide(true) |
| }) |
| } |
| |
| fn arg_compile_time_deps(self) -> Self { |
| self._arg(flag("compile-time-deps", "").hide(true)) |
| } |
| } |
| |
| impl CommandExt for Command { |
| fn _arg(self, arg: Arg) -> Self { |
| self.arg(arg) |
| } |
| } |
| |
| pub fn flag(name: &'static str, help: &'static str) -> Arg { |
| Arg::new(name) |
| .long(name) |
| .help(help) |
| .action(ArgAction::SetTrue) |
| } |
| |
| pub fn opt(name: &'static str, help: &'static str) -> Arg { |
| Arg::new(name).long(name).help(help).action(ArgAction::Set) |
| } |
| |
| pub fn optional_opt(name: &'static str, help: &'static str) -> Arg { |
| opt(name, help).num_args(0..=1) |
| } |
| |
| pub fn optional_multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg { |
| opt(name, help) |
| .value_name(value_name) |
| .num_args(0..=1) |
| .action(ArgAction::Append) |
| } |
| |
| pub fn multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg { |
| opt(name, help) |
| .value_name(value_name) |
| .action(ArgAction::Append) |
| } |
| |
| pub fn subcommand(name: &'static str) -> Command { |
| Command::new(name) |
| } |
| |
| /// Determines whether or not to gate `--profile` as unstable when resolving it. |
| pub enum ProfileChecking { |
| /// `cargo rustc` historically has allowed "test", "bench", and "check". This |
| /// variant explicitly allows those. |
| LegacyRustc, |
| /// `cargo check` and `cargo fix` historically has allowed "test". This variant |
| /// explicitly allows that on stable. |
| LegacyTestOnly, |
| /// All other commands, which allow any valid custom named profile. |
| Custom, |
| } |
| |
| pub trait ArgMatchesExt { |
| fn value_of_u32(&self, name: &str) -> CargoResult<Option<u32>> { |
| let arg = match self._value_of(name) { |
| None => None, |
| Some(arg) => Some(arg.parse::<u32>().map_err(|_| { |
| clap::Error::raw( |
| clap::error::ErrorKind::ValueValidation, |
| format!("invalid value: could not parse `{}` as a number", arg), |
| ) |
| })?), |
| }; |
| Ok(arg) |
| } |
| |
| fn value_of_i32(&self, name: &str) -> CargoResult<Option<i32>> { |
| let arg = match self._value_of(name) { |
| None => None, |
| Some(arg) => Some(arg.parse::<i32>().map_err(|_| { |
| clap::Error::raw( |
| clap::error::ErrorKind::ValueValidation, |
| format!("invalid value: could not parse `{}` as a number", arg), |
| ) |
| })?), |
| }; |
| Ok(arg) |
| } |
| |
| /// Returns value of the `name` command-line argument as an absolute path |
| fn value_of_path(&self, name: &str, gctx: &GlobalContext) -> Option<PathBuf> { |
| self._value_of(name).map(|path| gctx.cwd().join(path)) |
| } |
| |
| fn root_manifest(&self, gctx: &GlobalContext) -> CargoResult<PathBuf> { |
| root_manifest(self._value_of("manifest-path").map(Path::new), gctx) |
| } |
| |
| #[tracing::instrument(skip_all)] |
| fn workspace<'a>(&self, gctx: &'a GlobalContext) -> CargoResult<Workspace<'a>> { |
| let root = self.root_manifest(gctx)?; |
| let mut ws = Workspace::new(&root, gctx)?; |
| ws.set_resolve_honors_rust_version(self.honor_rust_version()); |
| if gctx.cli_unstable().avoid_dev_deps { |
| ws.set_require_optional_deps(false); |
| } |
| Ok(ws) |
| } |
| |
| fn jobs(&self) -> CargoResult<Option<JobsConfig>> { |
| let arg = match self._value_of("jobs") { |
| None => None, |
| Some(arg) => match arg.parse::<i32>() { |
| Ok(j) => Some(JobsConfig::Integer(j)), |
| Err(_) => Some(JobsConfig::String(arg.to_string())), |
| }, |
| }; |
| |
| Ok(arg) |
| } |
| |
| fn verbose(&self) -> u32 { |
| self._count("verbose") |
| } |
| |
| fn dry_run(&self) -> bool { |
| self.flag("dry-run") |
| } |
| |
| fn keep_going(&self) -> bool { |
| self.maybe_flag("keep-going") |
| } |
| |
| fn honor_rust_version(&self) -> Option<bool> { |
| self.flag("ignore-rust-version").then_some(false) |
| } |
| |
| fn targets(&self) -> CargoResult<Vec<String>> { |
| if self.is_present_with_zero_values("target") { |
| let cmd = if is_rustup() { |
| "rustup target list" |
| } else { |
| "rustc --print target-list" |
| }; |
| bail!( |
| "\"--target\" takes a target architecture as an argument. |
| |
| Run `{cmd}` to see possible targets." |
| ); |
| } |
| Ok(self._values_of("target")) |
| } |
| |
| fn get_profile_name( |
| &self, |
| default: &str, |
| profile_checking: ProfileChecking, |
| ) -> CargoResult<InternedString> { |
| let specified_profile = self._value_of("profile"); |
| |
| // Check for allowed legacy names. |
| // This is an early exit, since it allows combination with `--release`. |
| match (specified_profile, profile_checking) { |
| // `cargo rustc` has legacy handling of these names |
| (Some(name @ ("dev" | "test" | "bench" | "check")), ProfileChecking::LegacyRustc) |
| // `cargo fix` and `cargo check` has legacy handling of this profile name |
| | (Some(name @ "test"), ProfileChecking::LegacyTestOnly) => { |
| return Ok(name.into()); |
| } |
| _ => {} |
| } |
| |
| let name = match ( |
| self.maybe_flag("release"), |
| self.maybe_flag("debug"), |
| specified_profile, |
| ) { |
| (false, false, None) => default, |
| (true, _, None) => "release", |
| (_, true, None) => "dev", |
| // `doc` is separate from all the other reservations because |
| // [profile.doc] was historically allowed, but is deprecated and |
| // has no effect. To avoid potentially breaking projects, it is a |
| // warning in Cargo.toml, but since `--profile` is new, we can |
| // reject it completely here. |
| (_, _, Some("doc")) => { |
| bail!("profile `doc` is reserved and not allowed to be explicitly specified") |
| } |
| (_, _, Some(name)) => { |
| ProfileName::new(name)?; |
| name |
| } |
| }; |
| |
| Ok(name.into()) |
| } |
| |
| fn packages_from_flags(&self) -> CargoResult<Packages> { |
| Packages::from_flags( |
| // TODO Integrate into 'workspace' |
| self.flag("workspace") || self.flag("all"), |
| self._values_of("exclude"), |
| self._values_of("package"), |
| ) |
| } |
| |
| fn compile_options( |
| &self, |
| gctx: &GlobalContext, |
| intent: UserIntent, |
| workspace: Option<&Workspace<'_>>, |
| profile_checking: ProfileChecking, |
| ) -> CargoResult<CompileOptions> { |
| let spec = self.packages_from_flags()?; |
| let mut message_format = None; |
| let default_json = MessageFormat::Json { |
| short: false, |
| ansi: false, |
| render_diagnostics: false, |
| }; |
| let two_kinds_of_msg_format_err = "cannot specify two kinds of `message-format` arguments"; |
| for fmt in self._values_of("message-format") { |
| for fmt in fmt.split(',') { |
| let fmt = fmt.to_ascii_lowercase(); |
| match fmt.as_str() { |
| "json" => { |
| if message_format.is_some() { |
| bail!(two_kinds_of_msg_format_err); |
| } |
| message_format = Some(default_json); |
| } |
| "human" => { |
| if message_format.is_some() { |
| bail!(two_kinds_of_msg_format_err); |
| } |
| message_format = Some(MessageFormat::Human); |
| } |
| "short" => { |
| if message_format.is_some() { |
| bail!(two_kinds_of_msg_format_err); |
| } |
| message_format = Some(MessageFormat::Short); |
| } |
| "json-render-diagnostics" => { |
| if message_format.is_none() { |
| message_format = Some(default_json); |
| } |
| match &mut message_format { |
| Some(MessageFormat::Json { |
| render_diagnostics, .. |
| }) => *render_diagnostics = true, |
| _ => bail!(two_kinds_of_msg_format_err), |
| } |
| } |
| "json-diagnostic-short" => { |
| if message_format.is_none() { |
| message_format = Some(default_json); |
| } |
| match &mut message_format { |
| Some(MessageFormat::Json { short, .. }) => *short = true, |
| _ => bail!(two_kinds_of_msg_format_err), |
| } |
| } |
| "json-diagnostic-rendered-ansi" => { |
| if message_format.is_none() { |
| message_format = Some(default_json); |
| } |
| match &mut message_format { |
| Some(MessageFormat::Json { ansi, .. }) => *ansi = true, |
| _ => bail!(two_kinds_of_msg_format_err), |
| } |
| } |
| s => bail!("invalid message format specifier: `{}`", s), |
| } |
| } |
| } |
| |
| let mut build_config = BuildConfig::new( |
| gctx, |
| self.jobs()?, |
| self.keep_going(), |
| &self.targets()?, |
| intent, |
| )?; |
| build_config.message_format = message_format.unwrap_or(MessageFormat::Human); |
| build_config.requested_profile = self.get_profile_name("dev", profile_checking)?; |
| build_config.unit_graph = self.flag("unit-graph"); |
| build_config.future_incompat_report = self.flag("future-incompat-report"); |
| build_config.compile_time_deps_only = self.flag("compile-time-deps"); |
| build_config.timing_report = self.flag("timings"); |
| |
| if build_config.unit_graph { |
| gctx.cli_unstable() |
| .fail_if_stable_opt("--unit-graph", 8002)?; |
| } |
| if build_config.compile_time_deps_only { |
| gctx.cli_unstable() |
| .fail_if_stable_opt("--compile-time-deps", 14434)?; |
| } |
| |
| let opts = CompileOptions { |
| build_config, |
| cli_features: self.cli_features()?, |
| spec, |
| filter: CompileFilter::from_raw_arguments( |
| self.flag("lib"), |
| self._values_of("bin"), |
| self.flag("bins"), |
| self._values_of("test"), |
| self.flag("tests"), |
| self._values_of("example"), |
| self.flag("examples"), |
| self._values_of("bench"), |
| self.flag("benches"), |
| self.flag("all-targets"), |
| ), |
| target_rustdoc_args: None, |
| target_rustc_args: None, |
| target_rustc_crate_types: None, |
| rustdoc_document_private_items: false, |
| honor_rust_version: self.honor_rust_version(), |
| }; |
| |
| if let Some(ws) = workspace { |
| self.check_optional_opts(ws, &opts)?; |
| } else if self.is_present_with_zero_values("package") { |
| // As for cargo 0.50.0, this won't occur but if someone sneaks in |
| // we can still provide this informative message for them. |
| anyhow::bail!( |
| "\"--package <SPEC>\" requires a SPEC format value, \ |
| which can be any package ID specifier in the dependency graph.\n\ |
| Run `cargo help pkgid` for more information about SPEC format." |
| ) |
| } |
| |
| Ok(opts) |
| } |
| |
| fn cli_features(&self) -> CargoResult<CliFeatures> { |
| CliFeatures::from_command_line( |
| &self._values_of("features"), |
| self.flag("all-features"), |
| !self.flag("no-default-features"), |
| ) |
| } |
| |
| fn compile_options_for_single_package( |
| &self, |
| gctx: &GlobalContext, |
| intent: UserIntent, |
| workspace: Option<&Workspace<'_>>, |
| profile_checking: ProfileChecking, |
| ) -> CargoResult<CompileOptions> { |
| let mut compile_opts = self.compile_options(gctx, intent, workspace, profile_checking)?; |
| let spec = self._values_of("package"); |
| if spec.iter().any(restricted_names::is_glob_pattern) { |
| anyhow::bail!("glob patterns on package selection are not supported.") |
| } |
| compile_opts.spec = Packages::Packages(spec); |
| Ok(compile_opts) |
| } |
| |
| fn new_options(&self, gctx: &GlobalContext) -> CargoResult<NewOptions> { |
| let vcs = self._value_of("vcs").map(|vcs| match vcs { |
| "git" => VersionControl::Git, |
| "hg" => VersionControl::Hg, |
| "pijul" => VersionControl::Pijul, |
| "fossil" => VersionControl::Fossil, |
| "none" => VersionControl::NoVcs, |
| vcs => panic!("Impossible vcs: {:?}", vcs), |
| }); |
| NewOptions::new( |
| vcs, |
| self.flag("bin"), |
| self.flag("lib"), |
| self.value_of_path("path", gctx).unwrap(), |
| self._value_of("name").map(|s| s.to_string()), |
| self._value_of("edition").map(|s| s.to_string()), |
| self.registry(gctx)?, |
| ) |
| } |
| |
| fn registry_or_index(&self, gctx: &GlobalContext) -> CargoResult<Option<RegistryOrIndex>> { |
| let registry = self._value_of("registry"); |
| let index = self._value_of("index"); |
| let result = match (registry, index) { |
| (None, None) => gctx.default_registry()?.map(RegistryOrIndex::Registry), |
| (None, Some(i)) => Some(RegistryOrIndex::Index(i.into_url()?)), |
| (Some(r), None) => { |
| RegistryName::new(r)?; |
| Some(RegistryOrIndex::Registry(r.to_string())) |
| } |
| (Some(_), Some(_)) => { |
| // Should be guarded by clap |
| unreachable!("both `--index` and `--registry` should not be set at the same time") |
| } |
| }; |
| Ok(result) |
| } |
| |
| fn registry(&self, gctx: &GlobalContext) -> CargoResult<Option<String>> { |
| match self._value_of("registry").map(|s| s.to_string()) { |
| None => gctx.default_registry(), |
| Some(registry) => { |
| RegistryName::new(®istry)?; |
| Ok(Some(registry)) |
| } |
| } |
| } |
| |
| fn check_optional_opts( |
| &self, |
| workspace: &Workspace<'_>, |
| compile_opts: &CompileOptions, |
| ) -> CargoResult<()> { |
| if self.is_present_with_zero_values("package") { |
| print_available_packages(workspace)? |
| } |
| |
| if self.is_present_with_zero_values("example") { |
| print_available_examples(workspace, compile_opts)?; |
| } |
| |
| if self.is_present_with_zero_values("bin") { |
| print_available_binaries(workspace, compile_opts)?; |
| } |
| |
| if self.is_present_with_zero_values("bench") { |
| print_available_benches(workspace, compile_opts)?; |
| } |
| |
| if self.is_present_with_zero_values("test") { |
| print_available_tests(workspace, compile_opts)?; |
| } |
| |
| Ok(()) |
| } |
| |
| fn is_present_with_zero_values(&self, name: &str) -> bool { |
| self._contains(name) && self._value_of(name).is_none() |
| } |
| |
| fn flag(&self, name: &str) -> bool; |
| |
| fn maybe_flag(&self, name: &str) -> bool; |
| |
| fn _value_of(&self, name: &str) -> Option<&str>; |
| |
| fn _values_of(&self, name: &str) -> Vec<String>; |
| |
| fn _value_of_os(&self, name: &str) -> Option<&OsStr>; |
| |
| fn _values_of_os(&self, name: &str) -> Vec<OsString>; |
| |
| fn _count(&self, name: &str) -> u32; |
| |
| fn _contains(&self, name: &str) -> bool; |
| } |
| |
| impl<'a> ArgMatchesExt for ArgMatches { |
| fn flag(&self, name: &str) -> bool { |
| ignore_unknown(self.try_get_one::<bool>(name)) |
| .copied() |
| .unwrap_or(false) |
| } |
| |
| // This works around before an upstream fix in clap for `UnknownArgumentValueParser` accepting |
| // generics arguments. `flag()` cannot be used with `--keep-going` at this moment due to |
| // <https://github.com/clap-rs/clap/issues/5081>. |
| fn maybe_flag(&self, name: &str) -> bool { |
| self.try_get_one::<bool>(name) |
| .ok() |
| .flatten() |
| .copied() |
| .unwrap_or_default() |
| } |
| |
| fn _value_of(&self, name: &str) -> Option<&str> { |
| ignore_unknown(self.try_get_one::<String>(name)).map(String::as_str) |
| } |
| |
| fn _value_of_os(&self, name: &str) -> Option<&OsStr> { |
| ignore_unknown(self.try_get_one::<OsString>(name)).map(OsString::as_os_str) |
| } |
| |
| fn _values_of(&self, name: &str) -> Vec<String> { |
| ignore_unknown(self.try_get_many::<String>(name)) |
| .unwrap_or_default() |
| .cloned() |
| .collect() |
| } |
| |
| fn _values_of_os(&self, name: &str) -> Vec<OsString> { |
| ignore_unknown(self.try_get_many::<OsString>(name)) |
| .unwrap_or_default() |
| .cloned() |
| .collect() |
| } |
| |
| fn _count(&self, name: &str) -> u32 { |
| *ignore_unknown(self.try_get_one::<u8>(name)).expect("defaulted by clap") as u32 |
| } |
| |
| fn _contains(&self, name: &str) -> bool { |
| ignore_unknown(self.try_contains_id(name)) |
| } |
| } |
| |
| pub fn values(args: &ArgMatches, name: &str) -> Vec<String> { |
| args._values_of(name) |
| } |
| |
| pub fn values_os(args: &ArgMatches, name: &str) -> Vec<OsString> { |
| args._values_of_os(name) |
| } |
| |
| pub fn root_manifest(manifest_path: Option<&Path>, gctx: &GlobalContext) -> CargoResult<PathBuf> { |
| if let Some(manifest_path) = manifest_path { |
| let path = gctx.cwd().join(manifest_path); |
| // In general, we try to avoid normalizing paths in Cargo, |
| // but in this particular case we need it to fix #3586. |
| let path = paths::normalize_path(&path); |
| if !path.exists() { |
| anyhow::bail!("manifest path `{}` does not exist", manifest_path.display()) |
| } else if path.is_dir() { |
| let child_path = path.join("Cargo.toml"); |
| let suggested_path = if child_path.exists() { |
| format!("\nhelp: {} exists", child_path.display()) |
| } else { |
| "".to_string() |
| }; |
| anyhow::bail!( |
| "manifest path `{}` is a directory but expected a file{suggested_path}", |
| manifest_path.display() |
| ) |
| } else if !path.ends_with("Cargo.toml") && !crate::workspace::parser::is_embedded(&path) { |
| if gctx.cli_unstable().script { |
| anyhow::bail!( |
| "the manifest-path must be a path to a Cargo.toml or script file: `{}`", |
| path.display() |
| ) |
| } else { |
| anyhow::bail!( |
| "the manifest-path must be a path to a Cargo.toml file: `{}`", |
| path.display() |
| ) |
| } |
| } |
| if crate::workspace::parser::is_embedded(&path) && !gctx.cli_unstable().script { |
| anyhow::bail!("embedded manifest `{}` requires `-Zscript`", path.display()) |
| } |
| Ok(path) |
| } else { |
| find_root_manifest_for_wd(gctx.cwd()) |
| } |
| } |
| |
| pub fn get_registry_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> { |
| let gctx = new_gctx_for_completions()?; |
| |
| if let Ok(Some(registries)) = |
| gctx.get::<Option<HashMap<String, HashMap<String, String>>>>("registries") |
| { |
| Ok(registries |
| .keys() |
| .map(|name| clap_complete::CompletionCandidate::new(name.to_owned())) |
| .collect()) |
| } else { |
| Ok(vec![]) |
| } |
| } |
| |
| fn get_profile_candidates() -> Vec<clap_complete::CompletionCandidate> { |
| match get_workspace_profile_candidates() { |
| Ok(candidates) if !candidates.is_empty() => candidates, |
| // fallback to default profile candidates |
| _ => default_profile_candidates(), |
| } |
| } |
| |
| fn get_workspace_profile_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> { |
| let gctx = new_gctx_for_completions()?; |
| let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; |
| let profiles = Profiles::new(&ws, "dev".into())?; |
| |
| let mut candidates = Vec::new(); |
| for name in profiles.profile_names() { |
| let Ok(profile_instance) = Profiles::new(&ws, name) else { |
| continue; |
| }; |
| let base_profile = profile_instance.base_profile(); |
| |
| let mut description = String::from(if base_profile.opt_level.as_str() == "0" { |
| "unoptimized" |
| } else { |
| "optimized" |
| }); |
| |
| if base_profile.debuginfo.is_turned_on() { |
| description.push_str(" + debuginfo"); |
| } |
| |
| candidates |
| .push(clap_complete::CompletionCandidate::new(&name).help(Some(description.into()))); |
| } |
| |
| Ok(candidates) |
| } |
| |
| fn default_profile_candidates() -> Vec<clap_complete::CompletionCandidate> { |
| vec![ |
| clap_complete::CompletionCandidate::new("dev").help(Some("unoptimized + debuginfo".into())), |
| clap_complete::CompletionCandidate::new("release").help(Some("optimized".into())), |
| clap_complete::CompletionCandidate::new("test") |
| .help(Some("unoptimized + debuginfo".into())), |
| clap_complete::CompletionCandidate::new("bench").help(Some("optimized".into())), |
| ] |
| } |
| |
| fn get_feature_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> { |
| let gctx = new_gctx_for_completions()?; |
| |
| let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; |
| let mut feature_candidates = Vec::new(); |
| |
| // Process all packages in the workspace |
| for package in ws.members() { |
| let package_name = package.name(); |
| |
| // Add direct features with package info |
| for feature_name in package.summary().features().keys() { |
| let order = if ws.current_opt().map(|p| p.name()) == Some(package_name) { |
| 0 |
| } else { |
| 1 |
| }; |
| feature_candidates.push( |
| clap_complete::CompletionCandidate::new(feature_name) |
| .display_order(Some(order)) |
| .help(Some(format!("from {}", package_name).into())), |
| ); |
| } |
| } |
| |
| Ok(feature_candidates) |
| } |
| |
| fn get_crate_candidates(kind: TargetKind) -> CargoResult<Vec<clap_complete::CompletionCandidate>> { |
| let gctx = new_gctx_for_completions()?; |
| |
| let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; |
| |
| let targets = ws |
| .members() |
| .flat_map(|pkg| pkg.targets().into_iter().cloned().map(|t| (pkg.name(), t))) |
| .filter(|(_, target)| *target.kind() == kind) |
| .map(|(pkg_name, target)| { |
| let order = if ws.current_opt().map(|p| p.name()) == Some(pkg_name) { |
| 0 |
| } else { |
| 1 |
| }; |
| clap_complete::CompletionCandidate::new(target.name()) |
| .display_order(Some(order)) |
| .help(Some(format!("from {}", pkg_name).into())) |
| }) |
| .collect::<Vec<_>>(); |
| |
| Ok(targets) |
| } |
| |
| fn get_target_triples() -> Vec<clap_complete::CompletionCandidate> { |
| let mut candidates = Vec::new(); |
| |
| if let Ok(targets) = get_target_triples_from_rustup() { |
| candidates = targets; |
| } |
| |
| if candidates.is_empty() { |
| if let Ok(targets) = get_target_triples_from_rustc() { |
| candidates = targets; |
| } |
| } |
| |
| // Allow tab-completion for `host-tuple` as the desired target. |
| candidates.insert( |
| 0, |
| clap_complete::CompletionCandidate::new("host-tuple").help(Some( |
| concat!("alias for: ", env!("RUST_HOST_TARGET")).into(), |
| )), |
| ); |
| |
| candidates |
| } |
| |
| pub fn get_target_triples_with_all() -> Vec<clap_complete::CompletionCandidate> { |
| let mut candidates = vec![ |
| clap_complete::CompletionCandidate::new("all").help(Some("Include all targets".into())), |
| ]; |
| candidates.extend(get_target_triples()); |
| candidates |
| } |
| |
| fn get_target_triples_from_rustup() -> CargoResult<Vec<clap_complete::CompletionCandidate>> { |
| let output = std::process::Command::new("rustup") |
| .arg("target") |
| .arg("list") |
| .output()?; |
| |
| if !output.status.success() { |
| return Ok(vec![]); |
| } |
| |
| let stdout = String::from_utf8(output.stdout)?; |
| |
| Ok(stdout |
| .lines() |
| .map(|line| { |
| let target = line.split_once(' '); |
| match target { |
| None => clap_complete::CompletionCandidate::new(line.to_owned()).hide(true), |
| Some((target, _installed)) => clap_complete::CompletionCandidate::new(target), |
| } |
| }) |
| .collect()) |
| } |
| |
| fn get_target_triples_from_rustc() -> CargoResult<Vec<clap_complete::CompletionCandidate>> { |
| let gctx = new_gctx_for_completions()?; |
| |
| let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx); |
| |
| let rustc = gctx.load_global_rustc(ws.as_ref().ok())?; |
| |
| let (stdout, _stderr) = |
| rustc.cached_output(rustc.process().arg("--print").arg("target-list"), 0)?; |
| |
| Ok(stdout |
| .lines() |
| .map(|line| clap_complete::CompletionCandidate::new(line.to_owned())) |
| .collect()) |
| } |
| |
| pub fn get_ws_member_candidates() -> Vec<clap_complete::CompletionCandidate> { |
| get_ws_member_packages() |
| .unwrap_or_default() |
| .into_iter() |
| .map(|pkg| { |
| clap_complete::CompletionCandidate::new(pkg.name().as_str()).help( |
| pkg.manifest() |
| .metadata() |
| .description |
| .to_owned() |
| .map(From::from), |
| ) |
| }) |
| .collect::<Vec<_>>() |
| } |
| |
| fn get_ws_member_packages() -> CargoResult<Vec<Package>> { |
| let gctx = new_gctx_for_completions()?; |
| let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; |
| let packages = ws.members().map(Clone::clone).collect::<Vec<_>>(); |
| Ok(packages) |
| } |
| |
| pub fn get_pkg_id_spec_candidates() -> Vec<clap_complete::CompletionCandidate> { |
| let mut candidates = vec![]; |
| |
| let package_map = HashMap::<&str, Vec<Package>>::default(); |
| let package_map = |
| get_packages() |
| .unwrap_or_default() |
| .into_iter() |
| .fold(package_map, |mut map, package| { |
| map.entry(package.name().as_str()) |
| .or_insert_with(Vec::new) |
| .push(package); |
| map |
| }); |
| |
| let unique_name_candidates = package_map |
| .iter() |
| .filter(|(_name, packages)| packages.len() == 1) |
| .map(|(name, packages)| { |
| clap_complete::CompletionCandidate::new(name.to_string()).help( |
| packages[0] |
| .manifest() |
| .metadata() |
| .description |
| .to_owned() |
| .map(From::from), |
| ) |
| }) |
| .collect::<Vec<_>>(); |
| |
| let duplicate_name_pairs = package_map |
| .iter() |
| .filter(|(_name, packages)| packages.len() > 1) |
| .collect::<Vec<_>>(); |
| |
| let mut duplicate_name_candidates = vec![]; |
| for (name, packages) in duplicate_name_pairs { |
| let mut version_count: HashMap<&Version, usize> = HashMap::default(); |
| |
| for package in packages { |
| *version_count.entry(package.version()).or_insert(0) += 1; |
| } |
| |
| for package in packages { |
| if let Some(&count) = version_count.get(package.version()) { |
| if count == 1 { |
| duplicate_name_candidates.push( |
| clap_complete::CompletionCandidate::new(format!( |
| "{}@{}", |
| name, |
| package.version() |
| )) |
| .help( |
| package |
| .manifest() |
| .metadata() |
| .description |
| .to_owned() |
| .map(From::from), |
| ), |
| ); |
| } else { |
| duplicate_name_candidates.push( |
| clap_complete::CompletionCandidate::new(format!( |
| "{}", |
| package.package_id().to_spec() |
| )) |
| .help( |
| package |
| .manifest() |
| .metadata() |
| .description |
| .to_owned() |
| .map(From::from), |
| ), |
| ) |
| } |
| } |
| } |
| } |
| |
| candidates.extend(unique_name_candidates); |
| candidates.extend(duplicate_name_candidates); |
| |
| candidates |
| } |
| |
| pub fn get_pkg_name_candidates() -> Vec<clap_complete::CompletionCandidate> { |
| let packages: BTreeMap<_, _> = get_packages() |
| .unwrap_or_default() |
| .into_iter() |
| .map(|package| { |
| ( |
| package.name(), |
| package.manifest().metadata().description.clone(), |
| ) |
| }) |
| .collect(); |
| |
| packages |
| .into_iter() |
| .map(|(name, description)| { |
| clap_complete::CompletionCandidate::new(name.as_str()).help(description.map(From::from)) |
| }) |
| .collect() |
| } |
| |
| fn get_packages() -> CargoResult<Vec<Package>> { |
| let gctx = new_gctx_for_completions()?; |
| |
| let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; |
| |
| let requested_kinds = CompileKind::from_requested_targets(ws.gctx(), &[])?; |
| let mut target_data = RustcTargetData::new(&ws, &requested_kinds)?; |
| // `cli_features.all_features` must be true in case that `specs` is empty. |
| let cli_features = CliFeatures::new_all(true); |
| let has_dev_units = HasDevUnits::Yes; |
| let force_all_targets = ForceAllTargets::No; |
| let dry_run = true; |
| |
| let ws_resolve = ops::resolve_ws_with_opts( |
| &ws, |
| &mut target_data, |
| &requested_kinds, |
| &cli_features, |
| &[], |
| has_dev_units, |
| force_all_targets, |
| dry_run, |
| )?; |
| |
| let packages = ws_resolve |
| .pkg_set |
| .packages() |
| .map(Clone::clone) |
| .collect::<Vec<_>>(); |
| |
| Ok(packages) |
| } |
| |
| pub fn get_direct_dependencies_pkg_name_candidates() -> Vec<clap_complete::CompletionCandidate> { |
| let (current_package_deps, all_package_deps) = match get_dependencies_from_metadata() { |
| Ok(v) => v, |
| Err(_) => return Vec::new(), |
| }; |
| |
| let current_package_deps_package_names = current_package_deps |
| .into_iter() |
| .map(|dep| dep.package_name().to_string()) |
| .sorted(); |
| let all_package_deps_package_names = all_package_deps |
| .into_iter() |
| .map(|dep| dep.package_name().to_string()) |
| .sorted(); |
| |
| let mut package_names_set = IndexSet::default(); |
| package_names_set.extend(current_package_deps_package_names); |
| package_names_set.extend(all_package_deps_package_names); |
| |
| package_names_set |
| .into_iter() |
| .map(|name| name.into()) |
| .collect_vec() |
| } |
| |
| fn get_dependencies_from_metadata() -> CargoResult<(Vec<Dependency>, Vec<Dependency>)> { |
| let cwd = std::env::current_dir()?; |
| let gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?); |
| let ws = Workspace::new(&find_root_manifest_for_wd(&cwd)?, &gctx)?; |
| let current_package = ws.current().ok(); |
| |
| let current_package_dependencies = ws |
| .current() |
| .map(|current| current.dependencies()) |
| .unwrap_or_default() |
| .to_vec(); |
| let all_other_packages_dependencies = ws |
| .members() |
| .filter(|&member| Some(member) != current_package) |
| .flat_map(|pkg| pkg.dependencies().into_iter().cloned()) |
| .collect::<HashSet<_>>() |
| .into_iter() |
| .collect::<Vec<_>>(); |
| |
| Ok(( |
| current_package_dependencies, |
| all_other_packages_dependencies, |
| )) |
| } |
| |
| pub fn new_gctx_for_completions() -> CargoResult<GlobalContext> { |
| let cwd = std::env::current_dir()?; |
| let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?); |
| |
| let verbose = 0; |
| let quiet = true; |
| let color = None; |
| let frozen = false; |
| let locked = true; |
| let offline = false; |
| let target_dir = None; |
| let unstable_flags = &[]; |
| let cli_config = &[]; |
| |
| gctx.configure( |
| verbose, |
| quiet, |
| color, |
| frozen, |
| locked, |
| offline, |
| &target_dir, |
| unstable_flags, |
| cli_config, |
| )?; |
| |
| Ok(gctx) |
| } |
| |
| #[track_caller] |
| pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T { |
| match r { |
| Ok(t) => t, |
| Err(clap::parser::MatchesError::UnknownArgument { .. }) => Default::default(), |
| Err(e) => { |
| panic!("Mismatch between definition and access: {}", e); |
| } |
| } |
| } |
| |
| #[derive(PartialEq, Eq, PartialOrd, Ord)] |
| pub enum CommandInfo { |
| BuiltIn { about: Option<String> }, |
| External { path: PathBuf }, |
| Alias { target: StringOrVec }, |
| } |