diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 9b8ff13e7fd..a53a7eba4ab 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -1,6 +1,6 @@ use crate::core::compiler::{BuildConfig, MessageFormat, TimingOutput}; use crate::core::resolver::CliFeatures; -use crate::core::{Edition, Workspace}; +use crate::core::{shell, Edition, Workspace}; use crate::ops::lockfile::LOCKFILE_NAME; use crate::ops::registry::RegistryOrIndex; use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionControl}; @@ -19,6 +19,7 @@ use cargo_util_schemas::manifest::ProfileName; use cargo_util_schemas::manifest::RegistryName; use cargo_util_schemas::manifest::StringOrVec; use clap::builder::UnknownArgumentValueParser; +use home::cargo_home_with_cwd; use std::ffi::{OsStr, OsString}; use std::path::Path; use std::path::PathBuf; @@ -261,7 +262,8 @@ pub trait CommandExt: Sized { }; self._arg( optional_multi_opt("target", "TRIPLE", target) - .help_heading(heading::COMPILATION_OPTIONS), + .help_heading(heading::COMPILATION_OPTIONS) + .add(clap_complete::ArgValueCandidates::new(get_target_triples)), ) ._arg(unsupported_short_arg) } @@ -1027,6 +1029,62 @@ pub fn lockfile_path( return Ok(Some(path)); } +fn get_target_triples() -> Vec { + let mut candidates = Vec::new(); + + if is_rustup() { + if let Ok(targets) = get_target_triples_from_rustup() { + candidates.extend(targets); + } + } else { + if let Ok(targets) = get_target_triples_from_rustc() { + candidates.extend(targets); + } + } + + candidates +} + +fn get_target_triples_from_rustup() -> CargoResult> { + 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> { + 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(&PathBuf::from(&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()) +} + #[track_caller] pub fn ignore_unknown(r: Result) -> T { match r {