feat: Support passing multiple targets to cargo (for Rust 1.64.0+)

This commit is contained in:
Alexis (Poliorcetics) Bourget 2022-09-25 01:22:27 +02:00
parent c6c932d3f3
commit 0d4737adb6
6 changed files with 87 additions and 35 deletions

View File

@ -40,7 +40,7 @@ pub enum InvocationLocation {
pub enum FlycheckConfig { pub enum FlycheckConfig {
CargoCommand { CargoCommand {
command: String, command: String,
target_triple: Option<String>, target_triples: Vec<String>,
all_targets: bool, all_targets: bool,
no_default_features: bool, no_default_features: bool,
all_features: bool, all_features: bool,
@ -286,7 +286,7 @@ fn check_command(&self) -> Command {
let (mut cmd, args) = match &self.config { let (mut cmd, args) = match &self.config {
FlycheckConfig::CargoCommand { FlycheckConfig::CargoCommand {
command, command,
target_triple, target_triples,
no_default_features, no_default_features,
all_targets, all_targets,
all_features, all_features,
@ -300,7 +300,7 @@ fn check_command(&self) -> Command {
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
.arg(self.root.join("Cargo.toml").as_os_str()); .arg(self.root.join("Cargo.toml").as_os_str());
if let Some(target) = target_triple { for target in target_triples {
cmd.args(&["--target", target.as_str()]); cmd.args(&["--target", target.as_str()]);
} }
if *all_targets { if *all_targets {

View File

@ -69,7 +69,7 @@ fn build_command(config: &CargoConfig) -> io::Result<Command> {
cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
// --all-targets includes tests, benches and examples in addition to the // --all-targets includes tests, benches and examples in addition to the
// default lib and bins. This is an independent concept from the --targets // default lib and bins. This is an independent concept from the --target
// flag below. // flag below.
cmd.arg("--all-targets"); cmd.arg("--all-targets");

View File

@ -270,11 +270,7 @@ pub fn fetch_metadata(
config: &CargoConfig, config: &CargoConfig,
progress: &dyn Fn(String), progress: &dyn Fn(String),
) -> Result<cargo_metadata::Metadata> { ) -> Result<cargo_metadata::Metadata> {
let target = config let targets = find_list_of_build_targets(config, cargo_toml);
.target
.clone()
.or_else(|| cargo_config_build_target(cargo_toml, &config.extra_env))
.or_else(|| rustc_discover_host_triple(cargo_toml, &config.extra_env));
let mut meta = MetadataCommand::new(); let mut meta = MetadataCommand::new();
meta.cargo_path(toolchain::cargo()); meta.cargo_path(toolchain::cargo());
@ -294,8 +290,12 @@ pub fn fetch_metadata(
} }
meta.current_dir(current_dir.as_os_str()); meta.current_dir(current_dir.as_os_str());
if let Some(target) = target { if !targets.is_empty() {
meta.other_options(vec![String::from("--filter-platform"), target]); let other_options: Vec<_> = targets
.into_iter()
.flat_map(|target| ["--filter-platform".to_string(), target])
.collect();
meta.other_options(other_options);
} }
// FIXME: Fetching metadata is a slow process, as it might require // FIXME: Fetching metadata is a slow process, as it might require
@ -469,6 +469,19 @@ fn is_unique(&self, name: &str) -> bool {
} }
} }
fn find_list_of_build_targets(config: &CargoConfig, cargo_toml: &ManifestPath) -> Vec<String> {
if let Some(target) = &config.target {
return [target.into()].to_vec();
}
let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env);
if !build_targets.is_empty() {
return build_targets;
}
rustc_discover_host_triple(cargo_toml, &config.extra_env).into_iter().collect()
}
fn rustc_discover_host_triple( fn rustc_discover_host_triple(
cargo_toml: &ManifestPath, cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>, extra_env: &FxHashMap<String, String>,
@ -499,7 +512,7 @@ fn rustc_discover_host_triple(
fn cargo_config_build_target( fn cargo_config_build_target(
cargo_toml: &ManifestPath, cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>, extra_env: &FxHashMap<String, String>,
) -> Option<String> { ) -> Vec<String> {
let mut cargo_config = Command::new(toolchain::cargo()); let mut cargo_config = Command::new(toolchain::cargo());
cargo_config.envs(extra_env); cargo_config.envs(extra_env);
cargo_config cargo_config
@ -507,12 +520,21 @@ fn cargo_config_build_target(
.args(&["-Z", "unstable-options", "config", "get", "build.target"]) .args(&["-Z", "unstable-options", "config", "get", "build.target"])
.env("RUSTC_BOOTSTRAP", "1"); .env("RUSTC_BOOTSTRAP", "1");
// if successful we receive `build.target = "target-triple"` // if successful we receive `build.target = "target-triple"`
// or `build.target = ["<target 1>", ..]`
tracing::debug!("Discovering cargo config target by {:?}", cargo_config); tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
match utf8_stdout(cargo_config) { utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default()
Ok(stdout) => stdout }
.strip_prefix("build.target = \"")
.and_then(|stdout| stdout.strip_suffix('"')) fn parse_output_cargo_config_build_target(stdout: String) -> Vec<String> {
.map(ToOwned::to_owned), let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
Err(_) => None,
} if !trimmed.starts_with('[') {
return [trimmed.to_string()].to_vec();
}
let res = serde_json::from_str(trimmed);
if let Err(e) = &res {
tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e);
}
res.unwrap_or_default()
} }

View File

@ -118,6 +118,8 @@ struct ConfigData {
/// This option does not take effect until rust-analyzer is restarted. /// This option does not take effect until rust-analyzer is restarted.
cargo_sysroot: Option<String> = "\"discover\"", cargo_sysroot: Option<String> = "\"discover\"",
/// Compilation target override (target triple). /// Compilation target override (target triple).
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
// than `checkOnSave_target`
cargo_target: Option<String> = "null", cargo_target: Option<String> = "null",
/// Unsets `#[cfg(test)]` for the specified crates. /// Unsets `#[cfg(test)]` for the specified crates.
cargo_unsetTest: Vec<String> = "[\"core\"]", cargo_unsetTest: Vec<String> = "[\"core\"]",
@ -174,9 +176,13 @@ struct ConfigData {
/// ``` /// ```
/// . /// .
checkOnSave_overrideCommand: Option<Vec<String>> = "null", checkOnSave_overrideCommand: Option<Vec<String>> = "null",
/// Check for a specific target. Defaults to /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
/// `#rust-analyzer.cargo.target#`. ///
checkOnSave_target: Option<String> = "null", /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
/// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
///
/// Aliased as `"checkOnSave.targets"`.
checkOnSave_target | checkOnSave_targets: CheckOnSaveTargets = "[]",
/// Toggles the additional completions that automatically add imports when completed. /// Toggles the additional completions that automatically add imports when completed.
/// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
@ -1147,11 +1153,10 @@ pub fn flycheck(&self) -> Option<FlycheckConfig> {
} }
Some(_) | None => FlycheckConfig::CargoCommand { Some(_) | None => FlycheckConfig::CargoCommand {
command: self.data.checkOnSave_command.clone(), command: self.data.checkOnSave_command.clone(),
target_triple: self target_triples: match &self.data.checkOnSave_target.0[..] {
.data [] => self.data.cargo_target.clone().into_iter().collect(),
.checkOnSave_target targets => targets.into(),
.clone() },
.or_else(|| self.data.cargo_target.clone()),
all_targets: self.data.checkOnSave_allTargets, all_targets: self.data.checkOnSave_allTargets,
no_default_features: self no_default_features: self
.data .data
@ -1657,6 +1662,9 @@ enum InvocationStrategy {
PerWorkspace, PerWorkspace,
} }
#[derive(Deserialize, Debug, Clone)]
struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec<String>);
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
enum InvocationLocation { enum InvocationLocation {
@ -2118,6 +2126,17 @@ macro_rules! set {
"The command will be executed in the project root." "The command will be executed in the project root."
], ],
}, },
"CheckOnSaveTargets" => set! {
"anyOf": [
{
"type": "string",
},
{
"type": "array",
"items": { "type": "string" }
},
],
},
_ => panic!("missing entry for {}: {}", ty, default), _ => panic!("missing entry for {}: {}", ty, default),
} }

View File

@ -190,11 +190,15 @@ cargo check --workspace --message-format=json --all-targets
``` ```
. .
-- --
[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: [[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `[]`)::
+ +
-- --
Check for a specific target. Defaults to Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
`#rust-analyzer.cargo.target#`.
Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
`["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
Aliased as `"checkOnSave.targets"`.
-- --
[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`)::
+ +

View File

@ -634,11 +634,18 @@
} }
}, },
"rust-analyzer.checkOnSave.target": { "rust-analyzer.checkOnSave.target": {
"markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.", "markdownDescription": "Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.\n\nCan be a single target, e.g. `\"x86_64-unknown-linux-gnu\"` or a list of targets, e.g.\n`[\"aarch64-apple-darwin\", \"x86_64-apple-darwin\"]`.\n\nAliased as `\"checkOnSave.targets\"`.",
"default": null, "default": [],
"type": [ "anyOf": [
"null", {
"string" "type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
] ]
}, },
"rust-analyzer.completion.autoimport.enable": { "rust-analyzer.completion.autoimport.enable": {