build.rs

  1#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
  2use std::process::Command;
  3
  4fn main() {
  5    #[cfg(target_os = "linux")]
  6    {
  7        // Add rpaths for libraries that webrtc-sys dlopens at runtime.
  8        // This is mostly required for hosts with non-standard SO installation
  9        // locations such as NixOS.
 10        let dlopened_libs = ["libva", "libva-drm", "egl"];
 11
 12        let mut rpath_dirs = std::collections::BTreeSet::new();
 13        for lib in &dlopened_libs {
 14            if let Some(libdir) = pkg_config::get_variable(lib, "libdir").ok() {
 15                rpath_dirs.insert(libdir);
 16            } else {
 17                eprintln!("zed build.rs: {lib} not found in pkg-config's path");
 18            }
 19        }
 20
 21        for dir in &rpath_dirs {
 22            println!("cargo:rustc-link-arg=-Wl,-rpath,{dir}");
 23        }
 24    }
 25
 26    if cfg!(target_os = "macos") {
 27        println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7");
 28
 29        // Weakly link ReplayKit to ensure Zed can be used on macOS 10.15+.
 30        println!("cargo:rustc-link-arg=-Wl,-weak_framework,ReplayKit");
 31
 32        // Seems to be required to enable Swift concurrency
 33        println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/swift");
 34
 35        // Register exported Objective-C selectors, protocols, etc
 36        println!("cargo:rustc-link-arg=-Wl,-ObjC");
 37
 38        // weak link to support Catalina
 39        println!("cargo:rustc-link-arg=-Wl,-weak_framework,ScreenCaptureKit");
 40    }
 41
 42    // Populate git sha environment variable if git is available
 43    println!("cargo:rerun-if-changed=../../.git/logs/HEAD");
 44    println!(
 45        "cargo:rustc-env=TARGET={}",
 46        std::env::var("TARGET").unwrap()
 47    );
 48
 49    let git_sha = match std::env::var("ZED_COMMIT_SHA").ok() {
 50        Some(git_sha) => {
 51            // In deterministic build environments such as Nix, we inject the commit sha into the build script.
 52            Some(git_sha)
 53        }
 54        None => {
 55            if let Some(output) = Command::new("git")
 56                .args(["rev-parse", "HEAD"])
 57                .output()
 58                .ok()
 59                && output.status.success()
 60            {
 61                let git_sha = String::from_utf8_lossy(&output.stdout);
 62                Some(git_sha.trim().to_string())
 63            } else {
 64                None
 65            }
 66        }
 67    };
 68
 69    if let Some(git_sha) = git_sha {
 70        println!("cargo:rustc-env=ZED_COMMIT_SHA={git_sha}");
 71
 72        if let Some(build_identifier) = option_env!("GITHUB_RUN_NUMBER") {
 73            println!("cargo:rustc-env=ZED_BUILD_ID={build_identifier}");
 74        }
 75
 76        if let Ok(build_profile) = std::env::var("PROFILE")
 77            && build_profile == "release"
 78        {
 79            // This is currently the best way to make `cargo build ...`'s build script
 80            // to print something to stdout without extra verbosity.
 81            println!("cargo::warning=Info: using '{git_sha}' hash for ZED_COMMIT_SHA env var");
 82        }
 83    }
 84
 85    if cfg!(windows) {
 86        if cfg!(target_env = "msvc") {
 87            // todo(windows): This is to avoid stack overflow. Remove it when solved.
 88            println!("cargo:rustc-link-arg=/stack:{}", 8 * 1024 * 1024);
 89        }
 90
 91        if cfg!(target_arch = "x86_64") || cfg!(target_arch = "aarch64") {
 92            let out_dir = std::env::var("OUT_DIR").unwrap();
 93            let out_dir: &std::path::Path = out_dir.as_ref();
 94            let target_dir = std::path::Path::new(&out_dir)
 95                .parent()
 96                .and_then(|p| p.parent())
 97                .and_then(|p| p.parent())
 98                .expect("Failed to find target directory");
 99
100            let conpty_dll_target = target_dir.join("conpty.dll");
101            let open_console_target = target_dir.join("OpenConsole.exe");
102
103            let conpty_url = "https://github.com/microsoft/terminal/releases/download/v1.24.10621.0/Microsoft.Windows.Console.ConPTY.1.24.260303001.nupkg";
104            let nupkg_path = out_dir.join("conpty.nupkg.zip");
105            let extract_dir = out_dir.join("conpty");
106
107            let download_script = format!(
108                "$ProgressPreference = 'SilentlyContinue'; [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri '{}' -OutFile '{}'",
109                conpty_url,
110                nupkg_path.display()
111            );
112
113            let download_result = Command::new("powershell")
114                .args([
115                    "-NoProfile",
116                    "-NonInteractive",
117                    "-Command",
118                    &download_script,
119                ])
120                .output();
121
122            match download_result {
123                Ok(output) if output.status.success() => {
124                    println!("Downloaded conpty nupkg successfully");
125
126                    let extract_script = format!(
127                        "$ProgressPreference = 'SilentlyContinue'; Expand-Archive -Path '{}' -DestinationPath '{}' -Force",
128                        nupkg_path.display(),
129                        extract_dir.display()
130                    );
131
132                    let extract_result = Command::new("powershell")
133                        .args(["-NoProfile", "-NonInteractive", "-Command", &extract_script])
134                        .output();
135
136                    match extract_result {
137                        Ok(output) if output.status.success() => {
138                            let (conpty_dll_source, open_console_source) =
139                                if cfg!(target_arch = "x86_64") {
140                                    (
141                                        extract_dir.join("runtimes/win-x64/native/conpty.dll"),
142                                        extract_dir
143                                            .join("build/native/runtimes/x64/OpenConsole.exe"),
144                                    )
145                                } else {
146                                    (
147                                        extract_dir.join("runtimes/win-arm64/native/conpty.dll"),
148                                        extract_dir
149                                            .join("build/native/runtimes/arm64/OpenConsole.exe"),
150                                    )
151                                };
152
153                            match std::fs::copy(&conpty_dll_source, &conpty_dll_target) {
154                                Ok(_) => {
155                                    println!("Copied conpty.dll to {}", conpty_dll_target.display())
156                                }
157                                Err(e) => println!(
158                                    "cargo::warning=Failed to copy conpty.dll from {}: {}",
159                                    conpty_dll_source.display(),
160                                    e
161                                ),
162                            }
163
164                            match std::fs::copy(&open_console_source, &open_console_target) {
165                                Ok(_) => println!(
166                                    "Copied OpenConsole.exe to {}",
167                                    open_console_target.display()
168                                ),
169                                Err(e) => println!(
170                                    "cargo::warning=Failed to copy OpenConsole.exe from {}: {}",
171                                    open_console_source.display(),
172                                    e
173                                ),
174                            }
175                        }
176                        Ok(output) => {
177                            println!(
178                                "cargo::warning=Failed to extract conpty nupkg: {}",
179                                String::from_utf8_lossy(&output.stderr)
180                            );
181                        }
182                        Err(e) => {
183                            println!(
184                                "cargo::warning=Failed to run PowerShell for extraction: {}",
185                                e
186                            );
187                        }
188                    }
189                }
190                Ok(output) => {
191                    println!(
192                        "cargo::warning=Failed to download conpty nupkg: {}",
193                        String::from_utf8_lossy(&output.stderr)
194                    );
195                }
196                Err(e) => {
197                    println!(
198                        "cargo::warning=Failed to run PowerShell for download: {}",
199                        e
200                    );
201                }
202            }
203        }
204
205        let release_channel = option_env!("RELEASE_CHANNEL").unwrap_or("dev");
206        let icon = match release_channel {
207            "stable" => "resources/windows/app-icon.ico",
208            "preview" => "resources/windows/app-icon-preview.ico",
209            "nightly" => "resources/windows/app-icon-nightly.ico",
210            "dev" => "resources/windows/app-icon-dev.ico",
211            _ => "resources/windows/app-icon-dev.ico",
212        };
213        let icon = std::path::Path::new(icon);
214
215        println!("cargo:rerun-if-env-changed=RELEASE_CHANNEL");
216        println!("cargo:rerun-if-changed={}", icon.display());
217
218        #[cfg(windows)]
219        {
220            let mut res = winresource::WindowsResource::new();
221
222            // Depending on the security applied to the computer, winresource might fail
223            // fetching the RC path. Therefore, we add a way to explicitly specify the
224            // toolkit path, allowing winresource to use a valid RC path.
225            if let Some(explicit_rc_toolkit_path) = std::env::var("ZED_RC_TOOLKIT_PATH").ok() {
226                res.set_toolkit_path(explicit_rc_toolkit_path.as_str());
227            }
228            res.set_icon(icon.to_str().unwrap());
229            res.set("FileDescription", "Zed");
230            res.set("ProductName", "Zed");
231
232            if let Err(e) = res.compile() {
233                eprintln!("{}", e);
234                std::process::exit(1);
235            }
236        }
237    }
238}