From 73a033e77c830ef793a1c302a4794bf73a8c61c4 Mon Sep 17 00:00:00 2001
From: Deadbeef <ent3rm4n@gmail.com>
Date: Mon, 11 Apr 2022 13:13:50 +1000
Subject: [PATCH] feat: allow customizing the command for running build scripts

---
 crates/project_model/src/build_scripts.rs   | 43 ++++++++++++++-------
 crates/project_model/src/cargo_workspace.rs |  2 +
 crates/rust-analyzer/src/config.rs          |  5 +++
 docs/user/generated_config.adoc             |  7 ++++
 editors/code/package.json                   | 11 ++++++
 5 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/crates/project_model/src/build_scripts.rs b/crates/project_model/src/build_scripts.rs
index 6b601c34a24a..d96c135ba5e3 100644
--- a/crates/project_model/src/build_scripts.rs
+++ b/crates/project_model/src/build_scripts.rs
@@ -42,22 +42,15 @@ pub(crate) struct BuildScriptOutput {
 }
 
 impl WorkspaceBuildScripts {
-    pub(crate) fn run(
-        config: &CargoConfig,
-        workspace: &CargoWorkspace,
-        progress: &dyn Fn(String),
-    ) -> Result<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);
+            return cmd;
+        }
+
         let mut cmd = Command::new(toolchain::cargo());
 
-        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
-            // `cargo check`.
-            let myself = std::env::current_exe()?;
-            cmd.env("RUSTC_WRAPPER", myself);
-            cmd.env("RA_RUSTC_WRAPPER", "1");
-        }
-        cmd.current_dir(workspace.workspace_root());
         cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
 
         // --all-targets includes tests, benches and examples in addition to the
@@ -81,6 +74,26 @@ impl WorkspaceBuildScripts {
             }
         }
 
+        cmd
+    }
+    pub(crate) fn run(
+        config: &CargoConfig,
+        workspace: &CargoWorkspace,
+        progress: &dyn Fn(String),
+    ) -> Result<WorkspaceBuildScripts> {
+        let mut cmd = Self::build_command(config);
+
+        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
+            // `cargo check`.
+            let myself = std::env::current_exe()?;
+            cmd.env("RUSTC_WRAPPER", myself);
+            cmd.env("RA_RUSTC_WRAPPER", "1");
+        }
+
+        cmd.current_dir(workspace.workspace_root());
+
         cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
 
         let mut res = WorkspaceBuildScripts::default();
@@ -104,7 +117,7 @@ impl WorkspaceBuildScripts {
                 }
 
                 // Copy-pasted from existing cargo_metadata. It seems like we
-                // should be using sered_stacker here?
+                // should be using serde_stacker here?
                 let mut deserializer = serde_json::Deserializer::from_str(line);
                 deserializer.disable_recursion_limit();
                 let message = Message::deserialize(&mut deserializer)
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index 48051e4b5e8f..76ef44e1471e 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -96,6 +96,8 @@ pub struct CargoConfig {
     pub unset_test_crates: UnsetTestCrates,
 
     pub wrap_rustc_in_build_scripts: bool,
+
+    pub run_build_script_command: Option<Vec<String>>,
 }
 
 impl CargoConfig {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 958ff1fcee1f..c9d07a54bb43 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -79,6 +79,10 @@ config_data! {
         /// Run build scripts (`build.rs`) for more precise code analysis.
         cargo_runBuildScripts |
         cargo_loadOutDirsFromCheck: bool = "true",
+        /// Advanced option, fully override the command rust-analyzer uses to
+        /// run build scripts and build procedural macros. The command should
+        /// include `--message-format=json` or a similar option.
+        cargo_runBuildScriptsCommand: Option<Vec<String>> = "null",
         /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
         /// avoid compiling unnecessary things.
         cargo_useRustcWrapperForBuildScripts: bool = "true",
@@ -803,6 +807,7 @@ impl Config {
             rustc_source,
             unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
             wrap_rustc_in_build_scripts: self.data.cargo_useRustcWrapperForBuildScripts,
+            run_build_script_command: self.data.cargo_runBuildScriptsCommand.clone(),
         }
     }
 
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index d552982768b5..ace74f8e08af 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -64,6 +64,13 @@ List of features to activate.
 --
 Run build scripts (`build.rs`) for more precise code analysis.
 --
+[[rust-analyzer.cargo.runBuildScriptsCommand]]rust-analyzer.cargo.runBuildScriptsCommand (default: `null`)::
++
+--
+Advanced option, fully override the command rust-analyzer uses to
+run build scripts and build procedural macros. The command should
+include `--message-format=json` or a similar option.
+--
 [[rust-analyzer.cargo.useRustcWrapperForBuildScripts]]rust-analyzer.cargo.useRustcWrapperForBuildScripts (default: `true`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index b7bc60a3b8b9..9dfae1a8a097 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -476,6 +476,17 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.cargo.runBuildScriptsCommand": {
+                    "markdownDescription": "Advanced option, fully override the command rust-analyzer uses to\nrun build scripts and build procedural macros. The command should\ninclude `--message-format=json` or a similar option.",
+                    "default": null,
+                    "type": [
+                        "null",
+                        "array"
+                    ],
+                    "items": {
+                        "type": "string"
+                    }
+                },
                 "rust-analyzer.cargo.useRustcWrapperForBuildScripts": {
                     "markdownDescription": "Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to\navoid compiling unnecessary things.",
                     "default": true,