From 7e2c41dbd68014acbf91c2657a975c5a4b486da9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 27 Aug 2022 18:28:09 +0200 Subject: [PATCH 1/5] Implement invocation strategy config for build scripts --- crates/project-model/src/build_scripts.rs | 400 ++++++++++++-------- crates/project-model/src/cargo_workspace.rs | 3 +- crates/project-model/src/lib.rs | 8 + crates/project-model/src/workspace.rs | 60 ++- crates/rust-analyzer/src/config.rs | 32 ++ crates/rust-analyzer/src/reload.rs | 6 +- docs/user/generated_config.adoc | 11 + editors/code/package.json | 15 + 8 files changed, 373 insertions(+), 162 deletions(-) diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index d9f09c0349..6e6654e74e 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -6,7 +6,12 @@ //! This module implements this second part. We use "build script" terminology //! here, but it covers procedural macros as well. -use std::{cell::RefCell, io, path::PathBuf, process::Command}; +use std::{ + cell::RefCell, + io, mem, + path::{self, PathBuf}, + process::Command, +}; use cargo_metadata::{camino::Utf8Path, Message}; use la_arena::ArenaMap; @@ -15,11 +20,13 @@ use rustc_hash::FxHashMap; use semver::Version; use serde::Deserialize; -use crate::{cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, Package}; +use crate::{ + cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, Package, +}; #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct WorkspaceBuildScripts { - outputs: ArenaMap>, + outputs: ArenaMap, error: Option, } @@ -38,76 +45,55 @@ pub(crate) struct BuildScriptOutput { pub(crate) proc_macro_dylib_path: Option, } +impl BuildScriptOutput { + fn is_unchanged(&self) -> bool { + self.cfgs.is_empty() + && self.envs.is_empty() + && self.out_dir.is_none() + && self.proc_macro_dylib_path.is_none() + } +} + impl WorkspaceBuildScripts { - fn build_command(config: &CargoConfig) -> Command { - if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() { - let mut cmd = Command::new(program); - cmd.args(args); - cmd.envs(&config.extra_env); - return cmd; - } + fn build_command(config: &CargoConfig) -> io::Result { + let mut cmd = match config.run_build_script_command.as_deref() { + Some([program, args @ ..]) => { + let mut cmd = Command::new(program); + cmd.args(args); + cmd + } + _ => { + let mut cmd = Command::new(toolchain::cargo()); - let mut cmd = Command::new(toolchain::cargo()); + cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]); + + // --all-targets includes tests, benches and examples in addition to the + // default lib and bins. This is an independent concept from the --targets + // flag below. + cmd.arg("--all-targets"); + + if let Some(target) = &config.target { + cmd.args(&["--target", target]); + } + + match &config.features { + CargoFeatures::All => { + cmd.arg("--all-features"); + } + CargoFeatures::Selected { features, no_default_features } => { + if *no_default_features { + cmd.arg("--no-default-features"); + } + if !features.is_empty() { + cmd.arg("--features"); + cmd.arg(features.join(" ")); + } + } + } + cmd + } + }; cmd.envs(&config.extra_env); - cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]); - - // --all-targets includes tests, benches and examples in addition to the - // default lib and bins. This is an independent concept from the --targets - // flag below. - cmd.arg("--all-targets"); - - if let Some(target) = &config.target { - cmd.args(&["--target", target]); - } - - match &config.features { - CargoFeatures::All => { - cmd.arg("--all-features"); - } - CargoFeatures::Selected { features, no_default_features } => { - if *no_default_features { - cmd.arg("--no-default-features"); - } - if !features.is_empty() { - cmd.arg("--features"); - cmd.arg(features.join(" ")); - } - } - } - - cmd - } - - pub(crate) fn run( - config: &CargoConfig, - workspace: &CargoWorkspace, - progress: &dyn Fn(String), - toolchain: &Option, - ) -> io::Result { - const RUST_1_62: Version = Version::new(1, 62, 0); - - match Self::run_(Self::build_command(config), config, workspace, progress) { - Ok(WorkspaceBuildScripts { error: Some(error), .. }) - if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) => - { - // building build scripts failed, attempt to build with --keep-going so - // that we potentially get more build data - let mut cmd = Self::build_command(config); - cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); - let mut res = Self::run_(cmd, config, workspace, progress)?; - res.error = Some(error); - Ok(res) - } - res => res, - } - } - - fn run_( - mut cmd: Command, - config: &CargoConfig, - workspace: &CargoWorkspace, - progress: &dyn Fn(String), - ) -> io::Result { if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use // that to compile only proc macros and build scripts during the initial @@ -117,7 +103,107 @@ impl WorkspaceBuildScripts { cmd.env("RA_RUSTC_WRAPPER", "1"); } - cmd.current_dir(workspace.workspace_root()); + Ok(cmd) + } + + /// Runs the build scripts for the given workspace + pub(crate) fn run_for_workspace( + config: &CargoConfig, + workspace: &CargoWorkspace, + progress: &dyn Fn(String), + toolchain: &Option, + ) -> io::Result { + const RUST_1_62: Version = Version::new(1, 62, 0); + + match Self::run_per_ws(Self::build_command(config)?, config, workspace, progress) { + Ok(WorkspaceBuildScripts { error: Some(error), .. }) + if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) => + { + // building build scripts failed, attempt to build with --keep-going so + // that we potentially get more build data + let mut cmd = Self::build_command(config)?; + cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); + let mut res = Self::run_per_ws(cmd, config, workspace, progress)?; + res.error = Some(error); + Ok(res) + } + res => res, + } + } + + /// Runs the build scripts by invoking the configured command *once*. + /// This populates the outputs for all passed in workspaces. + pub(crate) fn run_once( + config: &CargoConfig, + workspaces: &[&CargoWorkspace], + progress: &dyn Fn(String), + ) -> io::Result> { + assert_eq!(config.invocation_strategy, InvocationStrategy::OnceInRoot); + let cmd = Self::build_command(config)?; + // NB: Cargo.toml could have been modified between `cargo metadata` and + // `cargo check`. We shouldn't assume that package ids we see here are + // exactly those from `config`. + let mut by_id = FxHashMap::default(); + let mut res: Vec<_> = workspaces + .iter() + .enumerate() + .map(|(idx, workspace)| { + let mut res = WorkspaceBuildScripts::default(); + for package in workspace.packages() { + res.outputs.insert(package, BuildScriptOutput::default()); + by_id.insert(workspace[package].id.clone(), (package, idx)); + } + res + }) + .collect(); + + let errors = Self::run_command( + cmd, + |package, cb| { + if let Some(&(package, workspace)) = by_id.get(package) { + cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]); + } + }, + progress, + )?; + res.iter_mut().for_each(|it| it.error = errors.clone()); + + if tracing::enabled!(tracing::Level::INFO) { + for (idx, workspace) in workspaces.iter().enumerate() { + for package in workspace.packages() { + let package_build_data = &mut res[idx].outputs[package]; + if !package_build_data.is_unchanged() { + tracing::info!( + "{}: {:?}", + workspace[package].manifest.parent().display(), + package_build_data, + ); + } + } + } + } + + Ok(res) + } + + fn run_per_ws( + mut cmd: Command, + config: &CargoConfig, + workspace: &CargoWorkspace, + progress: &dyn Fn(String), + ) -> io::Result { + let workspace_root: &path::Path = &workspace.workspace_root().as_ref(); + + match config.invocation_strategy { + InvocationStrategy::OnceInRoot => (), + InvocationStrategy::PerWorkspaceWithManifestPath => { + cmd.arg("--manifest-path"); + cmd.arg(workspace_root.join("Cargo.toml")); + } + InvocationStrategy::PerWorkspace => { + cmd.current_dir(workspace_root); + } + } let mut res = WorkspaceBuildScripts::default(); let outputs = &mut res.outputs; @@ -126,10 +212,44 @@ impl WorkspaceBuildScripts { // exactly those from `config`. let mut by_id: FxHashMap = FxHashMap::default(); for package in workspace.packages() { - outputs.insert(package, None); + outputs.insert(package, BuildScriptOutput::default()); by_id.insert(workspace[package].id.clone(), package); } + res.error = Self::run_command( + cmd, + |package, cb| { + if let Some(&package) = by_id.get(package) { + cb(&workspace[package].name, &mut outputs[package]); + } + }, + progress, + )?; + + if tracing::enabled!(tracing::Level::INFO) { + for package in workspace.packages() { + let package_build_data = &mut outputs[package]; + if !package_build_data.is_unchanged() { + tracing::info!( + "{}: {:?}", + workspace[package].manifest.parent().display(), + package_build_data, + ); + } + } + } + + Ok(res) + } + + fn run_command( + cmd: Command, + // ideally this would be something like: + // with_output_for: impl FnMut(&str, dyn FnOnce(&mut BuildScriptOutput)), + // but owned trait objects aren't a thing + mut with_output_for: impl FnMut(&str, &mut dyn FnMut(&str, &mut BuildScriptOutput)), + progress: &dyn Fn(String), + ) -> io::Result> { let errors = RefCell::new(String::new()); let push_err = |err: &str| { let mut e = errors.borrow_mut(); @@ -149,61 +269,58 @@ impl WorkspaceBuildScripts { .unwrap_or_else(|_| Message::TextLine(line.to_string())); match message { - Message::BuildScriptExecuted(message) => { - let package = match by_id.get(&message.package_id.repr) { - Some(&it) => it, - None => return, - }; - progress(format!("running build-script: {}", workspace[package].name)); - - let cfgs = { - let mut acc = Vec::new(); - for cfg in message.cfgs { - match cfg.parse::() { - Ok(it) => acc.push(it), - Err(err) => { - push_err(&format!( - "invalid cfg from cargo-metadata: {}", - err - )); - return; - } - }; + Message::BuildScriptExecuted(mut message) => { + with_output_for(&message.package_id.repr, &mut |name, data| { + progress(format!("running build-script: {}", name)); + let cfgs = { + let mut acc = Vec::new(); + for cfg in &message.cfgs { + match cfg.parse::() { + Ok(it) => acc.push(it), + Err(err) => { + push_err(&format!( + "invalid cfg from cargo-metadata: {}", + err + )); + return; + } + }; + } + acc + }; + if !message.env.is_empty() { + data.envs = mem::take(&mut message.env); } - acc - }; - // cargo_metadata crate returns default (empty) path for - // older cargos, which is not absolute, so work around that. - let out_dir = message.out_dir.into_os_string(); - if !out_dir.is_empty() { - let data = outputs[package].get_or_insert_with(Default::default); - data.out_dir = Some(AbsPathBuf::assert(PathBuf::from(out_dir))); - data.cfgs = cfgs; - } - if !message.env.is_empty() { - outputs[package].get_or_insert_with(Default::default).envs = - message.env; - } + // cargo_metadata crate returns default (empty) path for + // older cargos, which is not absolute, so work around that. + let out_dir = mem::take(&mut message.out_dir).into_os_string(); + if !out_dir.is_empty() { + let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir)); + // inject_cargo_env(package, package_build_data); + // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() + if let Some(out_dir) = + out_dir.as_os_str().to_str().map(|s| s.to_owned()) + { + data.envs.push(("OUT_DIR".to_string(), out_dir)); + } + data.out_dir = Some(out_dir); + data.cfgs = cfgs; + } + }); } Message::CompilerArtifact(message) => { - let package = match by_id.get(&message.package_id.repr) { - Some(it) => *it, - None => return, - }; - - progress(format!("building proc-macros: {}", message.target.name)); - - if message.target.kind.iter().any(|k| k == "proc-macro") { - // Skip rmeta file - if let Some(filename) = - message.filenames.iter().find(|name| is_dylib(name)) - { - let filename = AbsPathBuf::assert(PathBuf::from(&filename)); - outputs[package] - .get_or_insert_with(Default::default) - .proc_macro_dylib_path = Some(filename); + with_output_for(&message.package_id.repr, &mut |name, data| { + progress(format!("building proc-macros: {}", name)); + if message.target.kind.iter().any(|k| k == "proc-macro") { + // Skip rmeta file + if let Some(filename) = + message.filenames.iter().find(|name| is_dylib(name)) + { + let filename = AbsPathBuf::assert(PathBuf::from(&filename)); + data.proc_macro_dylib_path = Some(filename); + } } - } + }); } Message::CompilerMessage(message) => { progress(message.target.name); @@ -222,32 +339,13 @@ impl WorkspaceBuildScripts { }, )?; - for package in workspace.packages() { - if let Some(package_build_data) = &mut outputs[package] { - tracing::info!( - "{}: {:?}", - workspace[package].manifest.parent().display(), - package_build_data, - ); - // inject_cargo_env(package, package_build_data); - if let Some(out_dir) = &package_build_data.out_dir { - // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() - if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) { - package_build_data.envs.push(("OUT_DIR".to_string(), out_dir)); - } - } - } - } - - let mut errors = errors.into_inner(); - if !output.status.success() { - if errors.is_empty() { - errors = "cargo check failed".to_string(); - } - res.error = Some(errors); - } - - Ok(res) + let errors = if !output.status.success() { + let errors = errors.into_inner(); + Some(if errors.is_empty() { "cargo check failed".to_string() } else { errors }) + } else { + None + }; + Ok(errors) } pub fn error(&self) -> Option<&str> { @@ -255,11 +353,11 @@ impl WorkspaceBuildScripts { } pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> { - self.outputs.get(idx)?.as_ref() + self.outputs.get(idx) } } -// FIXME: File a better way to know if it is a dylib. +// FIXME: Find a better way to know if it is a dylib. fn is_dylib(path: &Utf8Path) -> bool { match path.extension().map(|e| e.to_string().to_lowercase()) { None => false, diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 8e690f1125..79b56815c0 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -14,8 +14,8 @@ use rustc_hash::FxHashMap; use serde::Deserialize; use serde_json::from_value; -use crate::CfgOverrides; use crate::{utf8_stdout, ManifestPath}; +use crate::{CfgOverrides, InvocationStrategy}; /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. @@ -106,6 +106,7 @@ pub struct CargoConfig { pub run_build_script_command: Option>, /// Extra env vars to set when invoking the cargo command pub extra_env: FxHashMap, + pub invocation_strategy: InvocationStrategy, } impl CargoConfig { diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index ce78ce8569..13a86901f7 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -157,3 +157,11 @@ fn utf8_stdout(mut cmd: Command) -> Result { let stdout = String::from_utf8(output.stdout)?; Ok(stdout.trim().to_string()) } + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum InvocationStrategy { + OnceInRoot, + PerWorkspaceWithManifestPath, + #[default] + PerWorkspace, +} diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 72ddf80928..01f5157093 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -2,7 +2,7 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, process::Command}; +use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc}; use anyhow::{format_err, Context, Result}; use base_db::{ @@ -21,8 +21,8 @@ use crate::{ cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, - utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, Package, ProjectJson, ProjectManifest, - Sysroot, TargetKind, WorkspaceBuildScripts, + utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package, + ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts, }; /// A set of cfg-overrides per crate. @@ -294,6 +294,7 @@ impl ProjectWorkspace { Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) } + /// Runs the build scripts for this [`ProjectWorkspace`]. pub fn run_build_scripts( &self, config: &CargoConfig, @@ -301,9 +302,13 @@ impl ProjectWorkspace { ) -> Result { match self { ProjectWorkspace::Cargo { cargo, toolchain, .. } => { - WorkspaceBuildScripts::run(config, cargo, progress, toolchain).with_context(|| { - format!("Failed to run build scripts for {}", &cargo.workspace_root().display()) - }) + WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, toolchain) + .with_context(|| { + format!( + "Failed to run build scripts for {}", + &cargo.workspace_root().display() + ) + }) } ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => { Ok(WorkspaceBuildScripts::default()) @@ -311,6 +316,49 @@ impl ProjectWorkspace { } } + /// Runs the build scripts for the given [`ProjectWorkspace`]s. Depending on the invocation + /// strategy this may run a single build process for all project workspaces. + pub fn run_all_build_scripts( + workspaces: &[ProjectWorkspace], + config: &CargoConfig, + progress: &dyn Fn(String), + ) -> Vec> { + if let InvocationStrategy::PerWorkspaceWithManifestPath | InvocationStrategy::PerWorkspace = + config.invocation_strategy + { + return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect(); + } + + let cargo_ws: Vec<_> = workspaces + .iter() + .filter_map(|it| match it { + ProjectWorkspace::Cargo { cargo, .. } => Some(cargo), + _ => None, + }) + .collect(); + let ref mut outputs = match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) { + Ok(it) => Ok(it.into_iter()), + // io::Error is not Clone? + Err(e) => Err(Arc::new(e)), + }; + + workspaces + .iter() + .map(|it| match it { + ProjectWorkspace::Cargo { cargo, .. } => match outputs { + Ok(outputs) => Ok(outputs.next().unwrap()), + Err(e) => Err(e.clone()).with_context(|| { + format!( + "Failed to run build scripts for {}", + &cargo.workspace_root().display() + ) + }), + }, + _ => Ok(WorkspaceBuildScripts::default()), + }) + .collect() + } + pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) { match self { ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 577a8640a4..79f6ded489 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -69,6 +69,14 @@ config_data! { cargo_autoreload: bool = "true", /// Run build scripts (`build.rs`) for more precise code analysis. cargo_buildScripts_enable: bool = "true", + /// Specifies the invocation strategy to use when running the build scripts command. + /// If `per_workspace_with_manifest_path` is set, the command will be executed for each + /// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and + /// the command will be executed from the project root. + /// If `per_workspace` is set, the command will be executed for each workspace and the + /// command will be executed from the corresponding workspace root. + /// If `once_in_root` is set, the command will be executed once in the project root. + cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"", /// Override the command rust-analyzer uses to run build scripts and /// build procedural macros. The command is required to output json /// and should therefore include `--message-format=json` or a similar @@ -1056,6 +1064,13 @@ impl Config { rustc_source, unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()), wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, + invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy { + InvocationStrategy::OnceInRoot => project_model::InvocationStrategy::OnceInRoot, + InvocationStrategy::PerWorkspaceWithManifestPath => { + project_model::InvocationStrategy::PerWorkspaceWithManifestPath + } + InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, + }, run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), extra_env: self.data.cargo_extraEnv.clone(), } @@ -1587,6 +1602,14 @@ enum CargoFeaturesDef { Selected(Vec), } +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +enum InvocationStrategy { + OnceInRoot, + PerWorkspaceWithManifestPath, + PerWorkspace, +} + #[derive(Deserialize, Debug, Clone)] #[serde(untagged)] enum LifetimeElisionDef { @@ -2001,6 +2024,15 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Render annotations above the whole item, including documentation comments and attributes." ], }, + "InvocationStrategy" => set! { + "type": "string", + "enum": ["per_workspace", "per_workspace_with_manifest_path", "once_in_root"], + "enumDescriptions": [ + "The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.", + "The command will be executed for each workspace and the command will be executed from the corresponding workspace root.", + "The command will be executed once in the project root." + ], + }, _ => panic!("missing entry for {}: {}", ty, default), } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index f873489394..bd5741f615 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -175,10 +175,8 @@ impl GlobalState { sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() } }; - let mut res = Vec::new(); - for ws in workspaces.iter() { - res.push(ws.run_build_scripts(&config, &progress)); - } + let res = ProjectWorkspace::run_all_build_scripts(&workspaces, &config, &progress); + sender.send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))).unwrap(); }); } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index acf0aaea85..a5307b6315 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -24,6 +24,17 @@ Automatically refresh project info via `cargo metadata` on -- Run build scripts (`build.rs`) for more precise code analysis. -- +[[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`):: ++ +-- +Specifies the invocation strategy to use when running the build scripts command. +If `per_workspace_with_manifest_path` is set, the command will be executed for each +workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and +the command will be executed from the project root. +If `per_workspace` is set, the command will be executed for each workspace and the +command will be executed from the corresponding workspace root. +If `once_in_root` is set, the command will be executed once in the project root. +-- [[rust-analyzer.cargo.buildScripts.overrideCommand]]rust-analyzer.cargo.buildScripts.overrideCommand (default: `null`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index f1dd3aa79f..b1b565106a 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -421,6 +421,21 @@ "default": true, "type": "boolean" }, + "rust-analyzer.cargo.buildScripts.invocationStrategy": { + "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace_with_manifest_path` is set, the command will be executed for each\nworkspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and\nthe command will be executed from the project root.\nIf `per_workspace` is set, the command will be executed for each workspace and the\ncommand will be executed from the corresponding workspace root.\nIf `once_in_root` is set, the command will be executed once in the project root.", + "default": "per_workspace", + "type": "string", + "enum": [ + "per_workspace", + "per_workspace_with_manifest_path", + "once_in_root" + ], + "enumDescriptions": [ + "The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.", + "The command will be executed for each workspace and the command will be executed from the corresponding workspace root.", + "The command will be executed once in the project root." + ] + }, "rust-analyzer.cargo.buildScripts.overrideCommand": { "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets\n```\n.", "default": null, From 4a287d25258aac0873863584e6f3d075dfb69033 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 15 Sep 2022 13:28:09 +0200 Subject: [PATCH 2/5] Implement invocation strategy config for checkOnSave Note that due to how cargo works, none of the modes currently work for r-a --- crates/flycheck/src/lib.rs | 53 ++++++++++++++++++++---- crates/rust-analyzer/src/config.rs | 17 ++++++++ crates/rust-analyzer/src/reload.rs | 65 ++++++++++++++++++------------ docs/user/generated_config.adoc | 11 +++++ editors/code/package.json | 15 +++++++ 5 files changed, 127 insertions(+), 34 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index e8c63d410a..c3976e6b7a 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -6,6 +6,7 @@ use std::{ fmt, io, + path::Path, process::{ChildStderr, ChildStdout, Command, Stdio}, time::Duration, }; @@ -21,6 +22,14 @@ pub use cargo_metadata::diagnostic::{ DiagnosticSpanMacroExpansion, }; +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum InvocationStrategy { + OnceInRoot, + PerWorkspaceWithManifestPath, + #[default] + PerWorkspace, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlycheckConfig { CargoCommand { @@ -32,11 +41,13 @@ pub enum FlycheckConfig { features: Vec, extra_args: Vec, extra_env: FxHashMap, + invocation_strategy: InvocationStrategy, }, CustomCommand { command: String, args: Vec, extra_env: FxHashMap, + invocation_strategy: InvocationStrategy, }, } @@ -136,7 +147,9 @@ enum Restart { No, } +/// A [`FlycheckActor`] is a single check instance of a workspace. struct FlycheckActor { + /// The workspace id of this flycheck instance. id: usize, sender: Box, config: FlycheckConfig, @@ -164,9 +177,11 @@ impl FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { id, sender, config, workspace_root, cargo_handle: None } } - fn progress(&self, progress: Progress) { + + fn report_progress(&self, progress: Progress) { self.send(Message::Progress { id: self.id, progress }); } + fn next_event(&self, inbox: &Receiver) -> Option { let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver); if let Ok(msg) = inbox.try_recv() { @@ -178,6 +193,7 @@ impl FlycheckActor { recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), } } + fn run(mut self, inbox: Receiver) { 'event: while let Some(event) = self.next_event(&inbox) { match event { @@ -194,7 +210,20 @@ impl FlycheckActor { } } - let command = self.check_command(); + let mut command = self.check_command(); + let invocation_strategy = self.invocation_strategy(); + match invocation_strategy { + InvocationStrategy::OnceInRoot => (), + InvocationStrategy::PerWorkspaceWithManifestPath => { + command.arg("--manifest-path"); + command.arg(<_ as AsRef>::as_ref( + &self.workspace_root.join("Cargo.toml"), + )); + } + InvocationStrategy::PerWorkspace => { + command.current_dir(&self.workspace_root); + } + } tracing::debug!(?command, "will restart flycheck"); match CargoHandle::spawn(command) { Ok(cargo_handle) => { @@ -203,10 +232,10 @@ impl FlycheckActor { "did restart flycheck" ); self.cargo_handle = Some(cargo_handle); - self.progress(Progress::DidStart); + self.report_progress(Progress::DidStart); } Err(error) => { - self.progress(Progress::DidFailToRestart(format!( + self.report_progress(Progress::DidFailToRestart(format!( "Failed to run the following command: {:?} error={}", self.check_command(), error @@ -226,11 +255,11 @@ impl FlycheckActor { self.check_command() ); } - self.progress(Progress::DidFinish(res)); + self.report_progress(Progress::DidFinish(res)); } Event::CheckEvent(Some(message)) => match message { CargoMessage::CompilerArtifact(msg) => { - self.progress(Progress::DidCheckCrate(msg.target.name)); + self.report_progress(Progress::DidCheckCrate(msg.target.name)); } CargoMessage::Diagnostic(msg) => { @@ -254,7 +283,14 @@ impl FlycheckActor { "did cancel flycheck" ); cargo_handle.cancel(); - self.progress(Progress::DidCancel); + self.report_progress(Progress::DidCancel); + } + } + + fn invocation_strategy(&self) -> InvocationStrategy { + match self.config { + FlycheckConfig::CargoCommand { invocation_strategy, .. } + | FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy, } } @@ -269,6 +305,7 @@ impl FlycheckActor { extra_args, features, extra_env, + invocation_strategy: _, } => { let mut cmd = Command::new(toolchain::cargo()); cmd.arg(command); @@ -297,7 +334,7 @@ impl FlycheckActor { cmd.envs(extra_env); cmd } - FlycheckConfig::CustomCommand { command, args, extra_env } => { + FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy: _ } => { let mut cmd = Command::new(command); cmd.args(args); cmd.envs(extra_env); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 79f6ded489..a61e38706e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -130,6 +130,14 @@ config_data! { /// /// Set to `"all"` to pass `--all-features` to Cargo. checkOnSave_features: Option = "null", + /// Specifies the invocation strategy to use when running the checkOnSave command. + /// If `per_workspace_with_manifest_path` is set, the command will be executed for each + /// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and + /// the command will be executed from the project root. + /// If `per_workspace` is set, the command will be executed for each workspace and the + /// command will be executed from the corresponding workspace root. + /// If `once_in_root` is set, the command will be executed once in the project root. + checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", /// Whether to pass `--no-default-features` to Cargo. Defaults to /// `#rust-analyzer.cargo.noDefaultFeatures#`. checkOnSave_noDefaultFeatures: Option = "null", @@ -1094,6 +1102,13 @@ impl Config { if !self.data.checkOnSave_enable { return None; } + let invocation_strategy = match self.data.cargo_buildScripts_invocationStrategy { + InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot, + InvocationStrategy::PerWorkspaceWithManifestPath => { + flycheck::InvocationStrategy::PerWorkspaceWithManifestPath + } + InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace, + }; let flycheck_config = match &self.data.checkOnSave_overrideCommand { Some(args) if !args.is_empty() => { let mut args = args.clone(); @@ -1102,6 +1117,7 @@ impl Config { command, args, extra_env: self.check_on_save_extra_env(), + invocation_strategy, } } Some(_) | None => FlycheckConfig::CargoCommand { @@ -1131,6 +1147,7 @@ impl Config { }, extra_args: self.data.checkOnSave_extraArgs.clone(), extra_env: self.check_on_save_extra_env(), + invocation_strategy, }, }; Some(flycheck_config) diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index bd5741f615..5382790f6e 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -473,32 +473,45 @@ impl GlobalState { }; let sender = self.flycheck_sender.clone(); - self.flycheck = self - .workspaces - .iter() - .enumerate() - .filter_map(|(id, w)| match w { - ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())), - ProjectWorkspace::Json { project, .. } => { - // Enable flychecks for json projects if a custom flycheck command was supplied - // in the workspace configuration. - match config { - FlycheckConfig::CustomCommand { .. } => Some((id, project.path())), - _ => None, - } - } - ProjectWorkspace::DetachedFiles { .. } => None, - }) - .map(|(id, root)| { - let sender = sender.clone(); - FlycheckHandle::spawn( - id, - Box::new(move |msg| sender.send(msg).unwrap()), - config.clone(), - root.to_path_buf(), - ) - }) - .collect(); + let (FlycheckConfig::CargoCommand { invocation_strategy, .. } + | FlycheckConfig::CustomCommand { invocation_strategy, .. }) = config; + + self.flycheck = match invocation_strategy { + flycheck::InvocationStrategy::OnceInRoot => vec![FlycheckHandle::spawn( + 0, + Box::new(move |msg| sender.send(msg).unwrap()), + config.clone(), + self.config.root_path().clone(), + )], + flycheck::InvocationStrategy::PerWorkspaceWithManifestPath + | flycheck::InvocationStrategy::PerWorkspace => { + self.workspaces + .iter() + .enumerate() + .filter_map(|(id, w)| match w { + ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())), + ProjectWorkspace::Json { project, .. } => { + // Enable flychecks for json projects if a custom flycheck command was supplied + // in the workspace configuration. + match config { + FlycheckConfig::CustomCommand { .. } => Some((id, project.path())), + _ => None, + } + } + ProjectWorkspace::DetachedFiles { .. } => None, + }) + .map(|(id, root)| { + let sender = sender.clone(); + FlycheckHandle::spawn( + id, + Box::new(move |msg| sender.send(msg).unwrap()), + config.clone(), + root.to_path_buf(), + ) + }) + .collect() + } + }; } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index a5307b6315..3ced42ef72 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -129,6 +129,17 @@ List of features to activate. Defaults to Set to `"all"` to pass `--all-features` to Cargo. -- +[[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`):: ++ +-- +Specifies the invocation strategy to use when running the checkOnSave command. +If `per_workspace_with_manifest_path` is set, the command will be executed for each +workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and +the command will be executed from the project root. +If `per_workspace` is set, the command will be executed for each workspace and the +command will be executed from the corresponding workspace root. +If `once_in_root` is set, the command will be executed once in the project root. +-- [[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index b1b565106a..3af32685fd 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -561,6 +561,21 @@ } ] }, + "rust-analyzer.checkOnSave.invocationStrategy": { + "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace_with_manifest_path` is set, the command will be executed for each\nworkspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and\nthe command will be executed from the project root.\nIf `per_workspace` is set, the command will be executed for each workspace and the\ncommand will be executed from the corresponding workspace root.\nIf `once_in_root` is set, the command will be executed once in the project root.", + "default": "per_workspace", + "type": "string", + "enum": [ + "per_workspace", + "per_workspace_with_manifest_path", + "once_in_root" + ], + "enumDescriptions": [ + "The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.", + "The command will be executed for each workspace and the command will be executed from the corresponding workspace root.", + "The command will be executed once in the project root." + ] + }, "rust-analyzer.checkOnSave.noDefaultFeatures": { "markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.", "default": null, From 5174b65ed8ada51977b5621bc070fe5bf9137dc4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 15 Sep 2022 13:35:46 +0200 Subject: [PATCH 3/5] Use correct invocation strategy config for checkOnSave --- crates/rust-analyzer/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a61e38706e..9f022d6add 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -1102,7 +1102,7 @@ impl Config { if !self.data.checkOnSave_enable { return None; } - let invocation_strategy = match self.data.cargo_buildScripts_invocationStrategy { + let invocation_strategy = match self.data.checkOnSave_invocationStrategy { InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot, InvocationStrategy::PerWorkspaceWithManifestPath => { flycheck::InvocationStrategy::PerWorkspaceWithManifestPath From 7db50294a3e458b7dd00ee646eee75a7ec933e3b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 26 Sep 2022 15:58:55 +0200 Subject: [PATCH 4/5] {manifest-path} interpolation --- crates/flycheck/src/lib.rs | 69 +++++++++++------------ crates/project-model/src/build_scripts.rs | 66 +++++++++++++++------- crates/project-model/src/lib.rs | 3 +- crates/project-model/src/workspace.rs | 4 +- crates/rust-analyzer/src/config.rs | 38 ++++++------- crates/rust-analyzer/src/reload.rs | 3 +- docs/user/generated_config.adoc | 26 +++++---- editors/code/package.json | 12 ++-- 8 files changed, 118 insertions(+), 103 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index c3976e6b7a..cdb3c2969c 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -6,7 +6,6 @@ use std::{ fmt, io, - path::Path, process::{ChildStderr, ChildStdout, Command, Stdio}, time::Duration, }; @@ -25,7 +24,6 @@ pub use cargo_metadata::diagnostic::{ #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum InvocationStrategy { OnceInRoot, - PerWorkspaceWithManifestPath, #[default] PerWorkspace, } @@ -153,7 +151,9 @@ struct FlycheckActor { id: usize, sender: Box, config: FlycheckConfig, - workspace_root: AbsPathBuf, + /// Either the workspace root of the workspace we are flychecking, + /// or the project root of the project. + root: AbsPathBuf, /// CargoHandle exists to wrap around the communication needed to be able to /// run `cargo check` without blocking. Currently the Rust standard library /// doesn't provide a way to read sub-process output without blocking, so we @@ -175,7 +175,7 @@ impl FlycheckActor { workspace_root: AbsPathBuf, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); - FlycheckActor { id, sender, config, workspace_root, cargo_handle: None } + FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None } } fn report_progress(&self, progress: Progress) { @@ -210,20 +210,7 @@ impl FlycheckActor { } } - let mut command = self.check_command(); - let invocation_strategy = self.invocation_strategy(); - match invocation_strategy { - InvocationStrategy::OnceInRoot => (), - InvocationStrategy::PerWorkspaceWithManifestPath => { - command.arg("--manifest-path"); - command.arg(<_ as AsRef>::as_ref( - &self.workspace_root.join("Cargo.toml"), - )); - } - InvocationStrategy::PerWorkspace => { - command.current_dir(&self.workspace_root); - } - } + let command = self.check_command(); tracing::debug!(?command, "will restart flycheck"); match CargoHandle::spawn(command) { Ok(cargo_handle) => { @@ -265,7 +252,7 @@ impl FlycheckActor { CargoMessage::Diagnostic(msg) => { self.send(Message::AddDiagnostic { id: self.id, - workspace_root: self.workspace_root.clone(), + workspace_root: self.root.clone(), diagnostic: msg, }); } @@ -287,15 +274,8 @@ impl FlycheckActor { } } - fn invocation_strategy(&self) -> InvocationStrategy { - match self.config { - FlycheckConfig::CargoCommand { invocation_strategy, .. } - | FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy, - } - } - fn check_command(&self) -> Command { - let mut cmd = match &self.config { + let (mut cmd, args, invocation_strategy) = match &self.config { FlycheckConfig::CargoCommand { command, target_triple, @@ -305,13 +285,11 @@ impl FlycheckActor { extra_args, features, extra_env, - invocation_strategy: _, + invocation_strategy, } => { let mut cmd = Command::new(toolchain::cargo()); cmd.arg(command); - cmd.current_dir(&self.workspace_root); - cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) - .arg(self.workspace_root.join("Cargo.toml").as_os_str()); + cmd.args(&["--workspace", "--message-format=json"]); if let Some(target) = target_triple { cmd.args(&["--target", target.as_str()]); @@ -330,18 +308,35 @@ impl FlycheckActor { cmd.arg(features.join(" ")); } } - cmd.args(extra_args); cmd.envs(extra_env); - cmd + (cmd, extra_args, invocation_strategy) } - FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy: _ } => { + FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { let mut cmd = Command::new(command); - cmd.args(args); cmd.envs(extra_env); - cmd + (cmd, args, invocation_strategy) } }; - cmd.current_dir(&self.workspace_root); + if let InvocationStrategy::PerWorkspace = invocation_strategy { + let mut with_manifest_path = false; + for arg in args { + if let Some(_) = arg.find("$manifest_path") { + with_manifest_path = true; + cmd.arg(arg.replace( + "$manifest_path", + &self.root.join("Cargo.toml").display().to_string(), + )); + } else { + cmd.arg(arg); + } + } + + if !with_manifest_path { + cmd.current_dir(&self.root); + } + } else { + cmd.args(args); + } cmd } diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 6e6654e74e..12be67d9a0 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -55,11 +55,37 @@ impl BuildScriptOutput { } impl WorkspaceBuildScripts { - fn build_command(config: &CargoConfig) -> io::Result { + fn build_command( + config: &CargoConfig, + workspace_root: Option<&path::Path>, + ) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); - cmd.args(args); + + // FIXME: strategy and workspace root are coupled, express that in code + if let (InvocationStrategy::PerWorkspace, Some(workspace_root)) = + (config.invocation_strategy, workspace_root) + { + let mut with_manifest_path = false; + for arg in args { + if let Some(_) = arg.find("$manifest_path") { + with_manifest_path = true; + cmd.arg(arg.replace( + "$manifest_path", + &workspace_root.join("Cargo.toml").display().to_string(), + )); + } else { + cmd.arg(arg); + } + } + + if !with_manifest_path { + cmd.current_dir(workspace_root); + } + } else { + cmd.args(args); + } cmd } _ => { @@ -90,9 +116,15 @@ impl WorkspaceBuildScripts { } } } + + if let Some(workspace_root) = workspace_root { + cmd.current_dir(workspace_root); + } + cmd } }; + cmd.envs(&config.extra_env); if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use @@ -115,15 +147,21 @@ impl WorkspaceBuildScripts { ) -> io::Result { const RUST_1_62: Version = Version::new(1, 62, 0); - match Self::run_per_ws(Self::build_command(config)?, config, workspace, progress) { + let workspace_root: &path::Path = &workspace.workspace_root().as_ref(); + + match Self::run_per_ws( + Self::build_command(config, Some(workspace_root))?, + workspace, + progress, + ) { Ok(WorkspaceBuildScripts { error: Some(error), .. }) if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) => { // building build scripts failed, attempt to build with --keep-going so // that we potentially get more build data - let mut cmd = Self::build_command(config)?; + let mut cmd = Self::build_command(config, Some(workspace_root))?; cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); - let mut res = Self::run_per_ws(cmd, config, workspace, progress)?; + let mut res = Self::run_per_ws(cmd, workspace, progress)?; res.error = Some(error); Ok(res) } @@ -139,7 +177,7 @@ impl WorkspaceBuildScripts { progress: &dyn Fn(String), ) -> io::Result> { assert_eq!(config.invocation_strategy, InvocationStrategy::OnceInRoot); - let cmd = Self::build_command(config)?; + let cmd = Self::build_command(config, None)?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. @@ -187,24 +225,10 @@ impl WorkspaceBuildScripts { } fn run_per_ws( - mut cmd: Command, - config: &CargoConfig, + cmd: Command, workspace: &CargoWorkspace, progress: &dyn Fn(String), ) -> io::Result { - let workspace_root: &path::Path = &workspace.workspace_root().as_ref(); - - match config.invocation_strategy { - InvocationStrategy::OnceInRoot => (), - InvocationStrategy::PerWorkspaceWithManifestPath => { - cmd.arg("--manifest-path"); - cmd.arg(workspace_root.join("Cargo.toml")); - } - InvocationStrategy::PerWorkspace => { - cmd.current_dir(workspace_root); - } - } - let mut res = WorkspaceBuildScripts::default(); let outputs = &mut res.outputs; // NB: Cargo.toml could have been modified between `cargo metadata` and diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 13a86901f7..956c872ccc 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -158,10 +158,9 @@ fn utf8_stdout(mut cmd: Command) -> Result { Ok(stdout.trim().to_string()) } -#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum InvocationStrategy { OnceInRoot, - PerWorkspaceWithManifestPath, #[default] PerWorkspace, } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 01f5157093..561bad3a6a 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -323,9 +323,7 @@ impl ProjectWorkspace { config: &CargoConfig, progress: &dyn Fn(String), ) -> Vec> { - if let InvocationStrategy::PerWorkspaceWithManifestPath | InvocationStrategy::PerWorkspace = - config.invocation_strategy - { + if let InvocationStrategy::PerWorkspace = config.invocation_strategy { return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect(); } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 9f022d6add..3bf28cb7b4 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -70,12 +70,15 @@ config_data! { /// Run build scripts (`build.rs`) for more precise code analysis. cargo_buildScripts_enable: bool = "true", /// Specifies the invocation strategy to use when running the build scripts command. - /// If `per_workspace_with_manifest_path` is set, the command will be executed for each - /// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and - /// the command will be executed from the project root. - /// If `per_workspace` is set, the command will be executed for each workspace and the - /// command will be executed from the corresponding workspace root. + /// If `per_workspace` is set, the command will be executed for each workspace and all + /// occurrences of `$manifest_path` in the command will be replaced by the corresponding + /// manifest path of the workspace that the command is being invoked for. If interpolation + /// for the manifest path happens at least once, the commands will be executed from the + /// project root, otherwise the commands will be executed from the corresponding workspace + /// root. /// If `once_in_root` is set, the command will be executed once in the project root. + /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// is set. cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"", /// Override the command rust-analyzer uses to run build scripts and /// build procedural macros. The command is required to output json @@ -131,12 +134,15 @@ config_data! { /// Set to `"all"` to pass `--all-features` to Cargo. checkOnSave_features: Option = "null", /// Specifies the invocation strategy to use when running the checkOnSave command. - /// If `per_workspace_with_manifest_path` is set, the command will be executed for each - /// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and - /// the command will be executed from the project root. - /// If `per_workspace` is set, the command will be executed for each workspace and the - /// command will be executed from the corresponding workspace root. + /// If `per_workspace` is set, the command will be executed for each workspace and all + /// occurrences of `$manifest_path` in the command will be replaced by the corresponding + /// manifest path of the workspace that the command is being invoked for. If interpolation + /// for the manifest path happens at least once, the commands will be executed from the + /// project root, otherwise the commands will be executed from the corresponding workspace + /// root. /// If `once_in_root` is set, the command will be executed once in the project root. + /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// is set. checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", /// Whether to pass `--no-default-features` to Cargo. Defaults to /// `#rust-analyzer.cargo.noDefaultFeatures#`. @@ -1074,9 +1080,6 @@ impl Config { wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy { InvocationStrategy::OnceInRoot => project_model::InvocationStrategy::OnceInRoot, - InvocationStrategy::PerWorkspaceWithManifestPath => { - project_model::InvocationStrategy::PerWorkspaceWithManifestPath - } InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), @@ -1104,9 +1107,6 @@ impl Config { } let invocation_strategy = match self.data.checkOnSave_invocationStrategy { InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot, - InvocationStrategy::PerWorkspaceWithManifestPath => { - flycheck::InvocationStrategy::PerWorkspaceWithManifestPath - } InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace, }; let flycheck_config = match &self.data.checkOnSave_overrideCommand { @@ -1623,7 +1623,6 @@ enum CargoFeaturesDef { #[serde(rename_all = "snake_case")] enum InvocationStrategy { OnceInRoot, - PerWorkspaceWithManifestPath, PerWorkspace, } @@ -2043,10 +2042,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "InvocationStrategy" => set! { "type": "string", - "enum": ["per_workspace", "per_workspace_with_manifest_path", "once_in_root"], + "enum": ["per_workspace", "once_in_root"], "enumDescriptions": [ - "The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.", - "The command will be executed for each workspace and the command will be executed from the corresponding workspace root.", + "The command will be executed for each workspace and `{manifest-path}` usages will be interpolated with the corresponding workspace manifests. If `{manifest-path}` is used, the commands will be executed in the project root, otherwise in the corresponding workspace roots.", "The command will be executed once in the project root." ], }, diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 5382790f6e..af7a51a68e 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -483,8 +483,7 @@ impl GlobalState { config.clone(), self.config.root_path().clone(), )], - flycheck::InvocationStrategy::PerWorkspaceWithManifestPath - | flycheck::InvocationStrategy::PerWorkspace => { + flycheck::InvocationStrategy::PerWorkspace => { self.workspaces .iter() .enumerate() diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 3ced42ef72..3948d8f7e7 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -28,12 +28,15 @@ Run build scripts (`build.rs`) for more precise code analysis. + -- Specifies the invocation strategy to use when running the build scripts command. -If `per_workspace_with_manifest_path` is set, the command will be executed for each -workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and -the command will be executed from the project root. -If `per_workspace` is set, the command will be executed for each workspace and the -command will be executed from the corresponding workspace root. +If `per_workspace` is set, the command will be executed for each workspace and all +occurrences of `$manifest_path` in the command will be replaced by the corresponding +manifest path of the workspace that the command is being invoked for. If interpolation +for the manifest path happens at least once, the commands will be executed from the +project root, otherwise the commands will be executed from the corresponding workspace +root. If `once_in_root` is set, the command will be executed once in the project root. +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +is set. -- [[rust-analyzer.cargo.buildScripts.overrideCommand]]rust-analyzer.cargo.buildScripts.overrideCommand (default: `null`):: + @@ -133,12 +136,15 @@ Set to `"all"` to pass `--all-features` to Cargo. + -- Specifies the invocation strategy to use when running the checkOnSave command. -If `per_workspace_with_manifest_path` is set, the command will be executed for each -workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and -the command will be executed from the project root. -If `per_workspace` is set, the command will be executed for each workspace and the -command will be executed from the corresponding workspace root. +If `per_workspace` is set, the command will be executed for each workspace and all +occurrences of `$manifest_path` in the command will be replaced by the corresponding +manifest path of the workspace that the command is being invoked for. If interpolation +for the manifest path happens at least once, the commands will be executed from the +project root, otherwise the commands will be executed from the corresponding workspace +root. If `once_in_root` is set, the command will be executed once in the project root. +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +is set. -- [[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: + diff --git a/editors/code/package.json b/editors/code/package.json index 3af32685fd..8c40a3878f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -422,17 +422,15 @@ "type": "boolean" }, "rust-analyzer.cargo.buildScripts.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace_with_manifest_path` is set, the command will be executed for each\nworkspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and\nthe command will be executed from the project root.\nIf `per_workspace` is set, the command will be executed for each workspace and the\ncommand will be executed from the corresponding workspace root.\nIf `once_in_root` is set, the command will be executed once in the project root.", + "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace and all\noccurrences of `$manifest_path` in the command will be replaced by the corresponding\nmanifest path of the workspace that the command is being invoked for. If interpolation\nfor the manifest path happens at least once, the commands will be executed from the\nproject root, otherwise the commands will be executed from the corresponding workspace\nroot.\nIf `once_in_root` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ "per_workspace", - "per_workspace_with_manifest_path", "once_in_root" ], "enumDescriptions": [ - "The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.", - "The command will be executed for each workspace and the command will be executed from the corresponding workspace root.", + "The command will be executed for each workspace and `{manifest-path}` usages will be interpolated with the corresponding workspace manifests. If `{manifest-path}` is used, the commands will be executed in the project root, otherwise in the corresponding workspace roots.", "The command will be executed once in the project root." ] }, @@ -562,17 +560,15 @@ ] }, "rust-analyzer.checkOnSave.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace_with_manifest_path` is set, the command will be executed for each\nworkspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and\nthe command will be executed from the project root.\nIf `per_workspace` is set, the command will be executed for each workspace and the\ncommand will be executed from the corresponding workspace root.\nIf `once_in_root` is set, the command will be executed once in the project root.", + "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace and all\noccurrences of `$manifest_path` in the command will be replaced by the corresponding\nmanifest path of the workspace that the command is being invoked for. If interpolation\nfor the manifest path happens at least once, the commands will be executed from the\nproject root, otherwise the commands will be executed from the corresponding workspace\nroot.\nIf `once_in_root` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ "per_workspace", - "per_workspace_with_manifest_path", "once_in_root" ], "enumDescriptions": [ - "The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.", - "The command will be executed for each workspace and the command will be executed from the corresponding workspace root.", + "The command will be executed for each workspace and `{manifest-path}` usages will be interpolated with the corresponding workspace manifests. If `{manifest-path}` is used, the commands will be executed in the project root, otherwise in the corresponding workspace roots.", "The command will be executed once in the project root." ] }, From 46732369f4b8246e6dc764ef1aa01d03ab5f52b7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 19 Oct 2022 23:34:36 +0200 Subject: [PATCH 5/5] Remove simplistic interpolation for manifest-path --- crates/flycheck/src/lib.rs | 26 ++++---------------- crates/project-model/src/build_scripts.rs | 27 ++------------------ crates/project-model/src/lib.rs | 2 +- crates/rust-analyzer/src/config.rs | 30 +++++++++-------------- crates/rust-analyzer/src/reload.rs | 2 +- docs/user/generated_config.adoc | 20 +++++---------- editors/code/package.json | 12 ++++----- 7 files changed, 32 insertions(+), 87 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index cdb3c2969c..0debf3270f 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -23,7 +23,7 @@ pub use cargo_metadata::diagnostic::{ #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum InvocationStrategy { - OnceInRoot, + Once, #[default] PerWorkspace, } @@ -317,26 +317,10 @@ impl FlycheckActor { (cmd, args, invocation_strategy) } }; - if let InvocationStrategy::PerWorkspace = invocation_strategy { - let mut with_manifest_path = false; - for arg in args { - if let Some(_) = arg.find("$manifest_path") { - with_manifest_path = true; - cmd.arg(arg.replace( - "$manifest_path", - &self.root.join("Cargo.toml").display().to_string(), - )); - } else { - cmd.arg(arg); - } - } - - if !with_manifest_path { - cmd.current_dir(&self.root); - } - } else { - cmd.args(args); - } + match invocation_strategy { + InvocationStrategy::PerWorkspace => cmd.current_dir(&self.root), + InvocationStrategy::Once => cmd.args(args), + }; cmd } diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 12be67d9a0..0bb9bd65dc 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -62,30 +62,7 @@ impl WorkspaceBuildScripts { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); - - // FIXME: strategy and workspace root are coupled, express that in code - if let (InvocationStrategy::PerWorkspace, Some(workspace_root)) = - (config.invocation_strategy, workspace_root) - { - let mut with_manifest_path = false; - for arg in args { - if let Some(_) = arg.find("$manifest_path") { - with_manifest_path = true; - cmd.arg(arg.replace( - "$manifest_path", - &workspace_root.join("Cargo.toml").display().to_string(), - )); - } else { - cmd.arg(arg); - } - } - - if !with_manifest_path { - cmd.current_dir(workspace_root); - } - } else { - cmd.args(args); - } + cmd.args(args); cmd } _ => { @@ -176,7 +153,7 @@ impl WorkspaceBuildScripts { workspaces: &[&CargoWorkspace], progress: &dyn Fn(String), ) -> io::Result> { - assert_eq!(config.invocation_strategy, InvocationStrategy::OnceInRoot); + assert_eq!(config.invocation_strategy, InvocationStrategy::Once); let cmd = Self::build_command(config, None)?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 956c872ccc..2cc0a60bca 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -160,7 +160,7 @@ fn utf8_stdout(mut cmd: Command) -> Result { #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum InvocationStrategy { - OnceInRoot, + Once, #[default] PerWorkspace, } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 3bf28cb7b4..3669fda926 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -70,13 +70,9 @@ config_data! { /// Run build scripts (`build.rs`) for more precise code analysis. cargo_buildScripts_enable: bool = "true", /// Specifies the invocation strategy to use when running the build scripts command. - /// If `per_workspace` is set, the command will be executed for each workspace and all - /// occurrences of `$manifest_path` in the command will be replaced by the corresponding - /// manifest path of the workspace that the command is being invoked for. If interpolation - /// for the manifest path happens at least once, the commands will be executed from the - /// project root, otherwise the commands will be executed from the corresponding workspace - /// root. - /// If `once_in_root` is set, the command will be executed once in the project root. + /// If `per_workspace` is set, the command will be executed for each workspace from the + /// corresponding workspace root. + /// If `once` is set, the command will be executed once in the project root. /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` /// is set. cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"", @@ -134,13 +130,9 @@ config_data! { /// Set to `"all"` to pass `--all-features` to Cargo. checkOnSave_features: Option = "null", /// Specifies the invocation strategy to use when running the checkOnSave command. - /// If `per_workspace` is set, the command will be executed for each workspace and all - /// occurrences of `$manifest_path` in the command will be replaced by the corresponding - /// manifest path of the workspace that the command is being invoked for. If interpolation - /// for the manifest path happens at least once, the commands will be executed from the - /// project root, otherwise the commands will be executed from the corresponding workspace - /// root. - /// If `once_in_root` is set, the command will be executed once in the project root. + /// If `per_workspace` is set, the command will be executed for each workspace from the + /// corresponding workspace root. + /// If `once` is set, the command will be executed once in the project root. /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` /// is set. checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", @@ -1079,7 +1071,7 @@ impl Config { unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()), wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy { - InvocationStrategy::OnceInRoot => project_model::InvocationStrategy::OnceInRoot, + InvocationStrategy::Once => project_model::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), @@ -1106,7 +1098,7 @@ impl Config { return None; } let invocation_strategy = match self.data.checkOnSave_invocationStrategy { - InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot, + InvocationStrategy::Once => flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace, }; let flycheck_config = match &self.data.checkOnSave_overrideCommand { @@ -1622,7 +1614,7 @@ enum CargoFeaturesDef { #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum InvocationStrategy { - OnceInRoot, + Once, PerWorkspace, } @@ -2042,9 +2034,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "InvocationStrategy" => set! { "type": "string", - "enum": ["per_workspace", "once_in_root"], + "enum": ["per_workspace", "once"], "enumDescriptions": [ - "The command will be executed for each workspace and `{manifest-path}` usages will be interpolated with the corresponding workspace manifests. If `{manifest-path}` is used, the commands will be executed in the project root, otherwise in the corresponding workspace roots.", + "The command will be executed for each workspace from the corresponding workspace root.", "The command will be executed once in the project root." ], }, diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index af7a51a68e..f7db62baf2 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -477,7 +477,7 @@ impl GlobalState { | FlycheckConfig::CustomCommand { invocation_strategy, .. }) = config; self.flycheck = match invocation_strategy { - flycheck::InvocationStrategy::OnceInRoot => vec![FlycheckHandle::spawn( + flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn( 0, Box::new(move |msg| sender.send(msg).unwrap()), config.clone(), diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 3948d8f7e7..e5d4395c34 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -28,13 +28,9 @@ Run build scripts (`build.rs`) for more precise code analysis. + -- Specifies the invocation strategy to use when running the build scripts command. -If `per_workspace` is set, the command will be executed for each workspace and all -occurrences of `$manifest_path` in the command will be replaced by the corresponding -manifest path of the workspace that the command is being invoked for. If interpolation -for the manifest path happens at least once, the commands will be executed from the -project root, otherwise the commands will be executed from the corresponding workspace -root. -If `once_in_root` is set, the command will be executed once in the project root. +If `per_workspace` is set, the command will be executed for each workspace from the +corresponding workspace root. +If `once` is set, the command will be executed once in the project root. This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` is set. -- @@ -136,13 +132,9 @@ Set to `"all"` to pass `--all-features` to Cargo. + -- Specifies the invocation strategy to use when running the checkOnSave command. -If `per_workspace` is set, the command will be executed for each workspace and all -occurrences of `$manifest_path` in the command will be replaced by the corresponding -manifest path of the workspace that the command is being invoked for. If interpolation -for the manifest path happens at least once, the commands will be executed from the -project root, otherwise the commands will be executed from the corresponding workspace -root. -If `once_in_root` is set, the command will be executed once in the project root. +If `per_workspace` is set, the command will be executed for each workspace from the +corresponding workspace root. +If `once` is set, the command will be executed once in the project root. This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` is set. -- diff --git a/editors/code/package.json b/editors/code/package.json index 8c40a3878f..d685d63410 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -422,15 +422,15 @@ "type": "boolean" }, "rust-analyzer.cargo.buildScripts.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace and all\noccurrences of `$manifest_path` in the command will be replaced by the corresponding\nmanifest path of the workspace that the command is being invoked for. If interpolation\nfor the manifest path happens at least once, the commands will be executed from the\nproject root, otherwise the commands will be executed from the corresponding workspace\nroot.\nIf `once_in_root` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace from the\ncorresponding workspace root.\nIf `once` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ "per_workspace", - "once_in_root" + "once" ], "enumDescriptions": [ - "The command will be executed for each workspace and `{manifest-path}` usages will be interpolated with the corresponding workspace manifests. If `{manifest-path}` is used, the commands will be executed in the project root, otherwise in the corresponding workspace roots.", + "The command will be executed for each workspace from the corresponding workspace root.", "The command will be executed once in the project root." ] }, @@ -560,15 +560,15 @@ ] }, "rust-analyzer.checkOnSave.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace and all\noccurrences of `$manifest_path` in the command will be replaced by the corresponding\nmanifest path of the workspace that the command is being invoked for. If interpolation\nfor the manifest path happens at least once, the commands will be executed from the\nproject root, otherwise the commands will be executed from the corresponding workspace\nroot.\nIf `once_in_root` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace from the\ncorresponding workspace root.\nIf `once` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ "per_workspace", - "once_in_root" + "once" ], "enumDescriptions": [ - "The command will be executed for each workspace and `{manifest-path}` usages will be interpolated with the corresponding workspace manifests. If `{manifest-path}` is used, the commands will be executed in the project root, otherwise in the corresponding workspace roots.", + "The command will be executed for each workspace from the corresponding workspace root.", "The command will be executed once in the project root." ] },