From 7e2c41dbd68014acbf91c2657a975c5a4b486da9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 27 Aug 2022 18:28:09 +0200 Subject: [PATCH] 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,