Allow local build of remote_server dev to be deployed to different linux than local (#33395)

Gwen Lg created

setup local build of `remote_server` to not depend of the local linux
libraries by :
- enable `vendored-libgit2` feature of git2
- setup target triple to `unknown-linux-musl` (mirror bundle-linux
script)
- add flag ` -C target-feature=+crt-static` in `RUSTFLAGS` env var
(mirror bundle-linux script)

Bonus:
Add an option to setup mold as linker of local build.

Closes #33341

Release Notes:

 - N/A

Change summary

Cargo.lock                       |   1 
crates/remote/src/ssh_session.rs | 269 +++++++++++++++++----------------
crates/remote_server/Cargo.toml  |   5 
3 files changed, 143 insertions(+), 132 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -13213,6 +13213,7 @@ dependencies = [
  "fs",
  "futures 0.3.31",
  "git",
+ "git2",
  "git_hosting_providers",
  "gpui",
  "gpui_tokio",

crates/remote/src/ssh_session.rs 🔗

@@ -314,20 +314,6 @@ pub struct SshPlatform {
     pub arch: &'static str,
 }
 
-impl SshPlatform {
-    pub fn triple(&self) -> Option<String> {
-        Some(format!(
-            "{}-{}",
-            self.arch,
-            match self.os {
-                "linux" => "unknown-linux-gnu",
-                "macos" => "apple-darwin",
-                _ => return None,
-            }
-        ))
-    }
-}
-
 pub trait SshClientDelegate: Send + Sync {
     fn ask_password(&self, prompt: String, tx: oneshot::Sender<String>, cx: &mut AsyncApp);
     fn get_download_params(
@@ -2068,6 +2054,7 @@ impl SshRemoteConnection {
         cx: &mut AsyncApp,
     ) -> Result<PathBuf> {
         use smol::process::{Command, Stdio};
+        use std::env::VarError;
 
         async fn run_cmd(command: &mut Command) -> Result<()> {
             let output = command
@@ -2082,70 +2069,37 @@ impl SshRemoteConnection {
             Ok(())
         }
 
+        let triple = format!(
+            "{}-{}",
+            self.ssh_platform.arch,
+            match self.ssh_platform.os {
+                "linux" => "unknown-linux-musl",
+                "macos" => "apple-darwin",
+                _ => anyhow::bail!("can't cross compile for: {:?}", self.ssh_platform),
+            }
+        );
+        let mut rust_flags = match std::env::var("RUSTFLAGS") {
+            Ok(val) => val,
+            Err(VarError::NotPresent) => String::new(),
+            Err(e) => {
+                log::error!("Failed to get env var `RUSTFLAGS` value: {e}");
+                String::new()
+            }
+        };
+        if self.ssh_platform.os == "linux" {
+            rust_flags.push_str(" -C target-feature=+crt-static");
+        }
+        if build_remote_server.contains("mold") {
+            rust_flags.push_str(" -C link-arg=-fuse-ld=mold");
+        }
+
         if self.ssh_platform.arch == std::env::consts::ARCH
             && self.ssh_platform.os == std::env::consts::OS
         {
             delegate.set_status(Some("Building remote server binary from source"), cx);
             log::info!("building remote server binary from source");
-            run_cmd(Command::new("cargo").args([
-                "build",
-                "--package",
-                "remote_server",
-                "--features",
-                "debug-embed",
-                "--target-dir",
-                "target/remote_server",
-            ]))
-            .await?;
-
-            delegate.set_status(Some("Compressing binary"), cx);
-
-            run_cmd(Command::new("gzip").args([
-                "-9",
-                "-f",
-                "target/remote_server/debug/remote_server",
-            ]))
-            .await?;
-
-            let path = std::env::current_dir()?.join("target/remote_server/debug/remote_server.gz");
-            return Ok(path);
-        }
-        let Some(triple) = self.ssh_platform.triple() else {
-            anyhow::bail!("can't cross compile for: {:?}", self.ssh_platform);
-        };
-        smol::fs::create_dir_all("target/remote_server").await?;
-
-        if build_remote_server.contains("cross") {
-            #[cfg(target_os = "windows")]
-            use util::paths::SanitizedPath;
-
-            delegate.set_status(Some("Installing cross.rs for cross-compilation"), cx);
-            log::info!("installing cross");
-            run_cmd(Command::new("cargo").args([
-                "install",
-                "cross",
-                "--git",
-                "https://github.com/cross-rs/cross",
-            ]))
-            .await?;
-
-            delegate.set_status(
-                Some(&format!(
-                    "Building remote server binary from source for {} with Docker",
-                    &triple
-                )),
-                cx,
-            );
-            log::info!("building remote server binary from source for {}", &triple);
-
-            // On Windows, the binding needs to be set to the canonical path
-            #[cfg(target_os = "windows")]
-            let src =
-                SanitizedPath::from(smol::fs::canonicalize("./target").await?).to_glob_string();
-            #[cfg(not(target_os = "windows"))]
-            let src = "./target";
             run_cmd(
-                Command::new("cross")
+                Command::new("cargo")
                     .args([
                         "build",
                         "--package",
@@ -2157,73 +2111,126 @@ impl SshRemoteConnection {
                         "--target",
                         &triple,
                     ])
-                    .env(
-                        "CROSS_CONTAINER_OPTS",
-                        format!("--mount type=bind,src={src},dst=/app/target"),
-                    ),
+                    .env("RUSTFLAGS", &rust_flags),
             )
             .await?;
         } else {
-            let which = cx
-                .background_spawn(async move { which::which("zig") })
-                .await;
+            if build_remote_server.contains("cross") {
+                #[cfg(target_os = "windows")]
+                use util::paths::SanitizedPath;
+
+                delegate.set_status(Some("Installing cross.rs for cross-compilation"), cx);
+                log::info!("installing cross");
+                run_cmd(Command::new("cargo").args([
+                    "install",
+                    "cross",
+                    "--git",
+                    "https://github.com/cross-rs/cross",
+                ]))
+                .await?;
 
-            if which.is_err() {
-                #[cfg(not(target_os = "windows"))]
-                {
-                    anyhow::bail!(
-                        "zig not found on $PATH, install zig (see https://ziglang.org/learn/getting-started or use zigup) or pass ZED_BUILD_REMOTE_SERVER=cross to use cross"
-                    )
-                }
+                delegate.set_status(
+                    Some(&format!(
+                        "Building remote server binary from source for {} with Docker",
+                        &triple
+                    )),
+                    cx,
+                );
+                log::info!("building remote server binary from source for {}", &triple);
+
+                // On Windows, the binding needs to be set to the canonical path
                 #[cfg(target_os = "windows")]
-                {
-                    anyhow::bail!(
-                        "zig not found on $PATH, install zig (use `winget install -e --id zig.zig` or see https://ziglang.org/learn/getting-started or use zigup) or pass ZED_BUILD_REMOTE_SERVER=cross to use cross"
-                    )
+                let src =
+                    SanitizedPath::from(smol::fs::canonicalize("./target").await?).to_glob_string();
+                #[cfg(not(target_os = "windows"))]
+                let src = "./target";
+                run_cmd(
+                    Command::new("cross")
+                        .args([
+                            "build",
+                            "--package",
+                            "remote_server",
+                            "--features",
+                            "debug-embed",
+                            "--target-dir",
+                            "target/remote_server",
+                            "--target",
+                            &triple,
+                        ])
+                        .env(
+                            "CROSS_CONTAINER_OPTS",
+                            format!("--mount type=bind,src={src},dst=/app/target"),
+                        )
+                        .env("RUSTFLAGS", &rust_flags),
+                )
+                .await?;
+            } else {
+                let which = cx
+                    .background_spawn(async move { which::which("zig") })
+                    .await;
+
+                if which.is_err() {
+                    #[cfg(not(target_os = "windows"))]
+                    {
+                        anyhow::bail!(
+                            "zig not found on $PATH, install zig (see https://ziglang.org/learn/getting-started or use zigup) or pass ZED_BUILD_REMOTE_SERVER=cross to use cross"
+                        )
+                    }
+                    #[cfg(target_os = "windows")]
+                    {
+                        anyhow::bail!(
+                            "zig not found on $PATH, install zig (use `winget install -e --id zig.zig` or see https://ziglang.org/learn/getting-started or use zigup) or pass ZED_BUILD_REMOTE_SERVER=cross to use cross"
+                        )
+                    }
                 }
-            }
 
-            delegate.set_status(Some("Adding rustup target for cross-compilation"), cx);
-            log::info!("adding rustup target");
-            run_cmd(Command::new("rustup").args(["target", "add"]).arg(&triple)).await?;
+                delegate.set_status(Some("Adding rustup target for cross-compilation"), cx);
+                log::info!("adding rustup target");
+                run_cmd(Command::new("rustup").args(["target", "add"]).arg(&triple)).await?;
 
-            delegate.set_status(Some("Installing cargo-zigbuild for cross-compilation"), cx);
-            log::info!("installing cargo-zigbuild");
-            run_cmd(Command::new("cargo").args(["install", "--locked", "cargo-zigbuild"])).await?;
+                delegate.set_status(Some("Installing cargo-zigbuild for cross-compilation"), cx);
+                log::info!("installing cargo-zigbuild");
+                run_cmd(Command::new("cargo").args(["install", "--locked", "cargo-zigbuild"]))
+                    .await?;
 
-            delegate.set_status(
-                Some(&format!(
-                    "Building remote binary from source for {triple} with Zig"
-                )),
-                cx,
-            );
-            log::info!("building remote binary from source for {triple} with Zig");
-            run_cmd(Command::new("cargo").args([
-                "zigbuild",
-                "--package",
-                "remote_server",
-                "--features",
-                "debug-embed",
-                "--target-dir",
-                "target/remote_server",
-                "--target",
-                &triple,
-            ]))
-            .await?;
+                delegate.set_status(
+                    Some(&format!(
+                        "Building remote binary from source for {triple} with Zig"
+                    )),
+                    cx,
+                );
+                log::info!("building remote binary from source for {triple} with Zig");
+                run_cmd(
+                    Command::new("cargo")
+                        .args([
+                            "zigbuild",
+                            "--package",
+                            "remote_server",
+                            "--features",
+                            "debug-embed",
+                            "--target-dir",
+                            "target/remote_server",
+                            "--target",
+                            &triple,
+                        ])
+                        .env("RUSTFLAGS", &rust_flags),
+                )
+                .await?;
+            }
         };
+        let bin_path = Path::new("target")
+            .join("remote_server")
+            .join(&triple)
+            .join("debug")
+            .join("remote_server");
 
-        let mut path = format!("target/remote_server/{triple}/debug/remote_server").into();
-        if !build_remote_server.contains("nocompress") {
+        let path = if !build_remote_server.contains("nocompress") {
             delegate.set_status(Some("Compressing binary"), cx);
 
             #[cfg(not(target_os = "windows"))]
             {
-                run_cmd(Command::new("gzip").args([
-                    "-9",
-                    "-f",
-                    &format!("target/remote_server/{}/debug/remote_server", triple),
-                ]))
-                .await?;
+                run_cmd(Command::new("gzip").args(["-9", "-f", &bin_path.to_string_lossy()]))
+                    .await?;
             }
             #[cfg(target_os = "windows")]
             {
@@ -2237,17 +2244,19 @@ impl SshRemoteConnection {
                     "a",
                     "-tgzip",
                     &gz_path,
-                    &format!("target/remote_server/{}/debug/remote_server", triple),
+                    &bin_path.to_string_lossy(),
                 ]))
                 .await?;
             }
 
-            path = std::env::current_dir()?.join(format!(
-                "target/remote_server/{triple}/debug/remote_server.gz"
-            ));
-        }
+            let mut archive_path = bin_path;
+            archive_path.set_extension("gz");
+            std::env::current_dir()?.join(archive_path)
+        } else {
+            bin_path
+        };
 
-        return Ok(path);
+        Ok(path)
     }
 }
 

crates/remote_server/Cargo.toml 🔗

@@ -37,6 +37,7 @@ fs.workspace = true
 futures.workspace = true
 git.workspace = true
 git_hosting_providers.workspace = true
+git2 = { workspace = true, features = ["vendored-libgit2"] }
 gpui.workspace = true
 gpui_tokio.workspace = true
 http_client.workspace = true
@@ -85,7 +86,7 @@ node_runtime = { workspace = true, features = ["test-support"] }
 project = { workspace = true, features = ["test-support"] }
 remote = { workspace = true, features = ["test-support"] }
 language_model = { workspace = true, features = ["test-support"] }
-lsp = { workspace = true, features=["test-support"] }
+lsp = { workspace = true, features = ["test-support"] }
 unindent.workspace = true
 serde_json.workspace = true
 zlog.workspace = true
@@ -95,4 +96,4 @@ cargo_toml.workspace = true
 toml.workspace = true
 
 [package.metadata.cargo-machete]
-ignored = ["rust-embed", "paths"]
+ignored = ["git2", "rust-embed", "paths"]