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}