1#![cfg_attr(any(not(target_os = "macos"), feature = "macos-blade"), allow(unused))]
2
3//TODO: consider generating shader code for WGSL
4//TODO: deprecate "runtime-shaders" and "macos-blade"
5
6use std::env;
7
8fn main() {
9 let target = env::var("CARGO_CFG_TARGET_OS");
10 println!("cargo::rustc-check-cfg=cfg(gles)");
11
12 #[cfg(any(
13 not(any(target_os = "macos", target_os = "windows")),
14 feature = "macos-blade"
15 ))]
16 check_wgsl_shaders();
17
18 match target.as_deref() {
19 Ok("macos") => {
20 #[cfg(target_os = "macos")]
21 macos::build();
22 }
23 Ok("windows") => {
24 #[cfg(target_os = "windows")]
25 windows::build();
26 }
27 _ => (),
28 };
29}
30
31#[allow(dead_code)]
32fn check_wgsl_shaders() {
33 use std::path::PathBuf;
34 use std::process;
35 use std::str::FromStr;
36
37 let shader_source_path = "./src/platform/blade/shaders.wgsl";
38 let shader_path = PathBuf::from_str(shader_source_path).unwrap();
39 println!("cargo:rerun-if-changed={}", &shader_path.display());
40
41 let shader_source = std::fs::read_to_string(&shader_path).unwrap();
42
43 match naga::front::wgsl::parse_str(&shader_source) {
44 Ok(_) => {
45 // All clear
46 }
47 Err(e) => {
48 eprintln!("WGSL shader compilation failed:\n{}", e);
49 process::exit(1);
50 }
51 }
52}
53#[cfg(target_os = "macos")]
54mod macos {
55 use std::{
56 env,
57 path::{Path, PathBuf},
58 };
59
60 use cbindgen::Config;
61
62 pub(super) fn build() {
63 generate_dispatch_bindings();
64 #[cfg(not(feature = "macos-blade"))]
65 {
66 let header_path = generate_shader_bindings();
67
68 #[cfg(feature = "runtime_shaders")]
69 emit_stitched_shaders(&header_path);
70 #[cfg(not(feature = "runtime_shaders"))]
71 compile_metal_shaders(&header_path);
72 }
73 }
74
75 fn generate_dispatch_bindings() {
76 println!("cargo:rustc-link-lib=framework=System");
77
78 let bindings = bindgen::Builder::default()
79 .header("src/platform/mac/dispatch.h")
80 .allowlist_var("_dispatch_main_q")
81 .allowlist_var("_dispatch_source_type_data_add")
82 .allowlist_var("DISPATCH_QUEUE_PRIORITY_HIGH")
83 .allowlist_var("DISPATCH_TIME_NOW")
84 .allowlist_function("dispatch_get_global_queue")
85 .allowlist_function("dispatch_async_f")
86 .allowlist_function("dispatch_after_f")
87 .allowlist_function("dispatch_time")
88 .allowlist_function("dispatch_source_merge_data")
89 .allowlist_function("dispatch_source_create")
90 .allowlist_function("dispatch_source_set_event_handler_f")
91 .allowlist_function("dispatch_resume")
92 .allowlist_function("dispatch_suspend")
93 .allowlist_function("dispatch_source_cancel")
94 .allowlist_function("dispatch_set_context")
95 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
96 .layout_tests(false)
97 .generate()
98 .expect("unable to generate bindings");
99
100 let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
101 bindings
102 .write_to_file(out_path.join("dispatch_sys.rs"))
103 .expect("couldn't write dispatch bindings");
104 }
105
106 fn generate_shader_bindings() -> PathBuf {
107 let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
108 let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
109 let mut config = Config {
110 include_guard: Some("SCENE_H".into()),
111 language: cbindgen::Language::C,
112 no_includes: true,
113 ..Default::default()
114 };
115 config.export.include.extend([
116 "Bounds".into(),
117 "Corners".into(),
118 "Edges".into(),
119 "Size".into(),
120 "Pixels".into(),
121 "PointF".into(),
122 "Hsla".into(),
123 "ContentMask".into(),
124 "Uniforms".into(),
125 "AtlasTile".into(),
126 "PathRasterizationInputIndex".into(),
127 "PathVertex_ScaledPixels".into(),
128 "PathRasterizationVertex".into(),
129 "ShadowInputIndex".into(),
130 "Shadow".into(),
131 "QuadInputIndex".into(),
132 "Underline".into(),
133 "UnderlineInputIndex".into(),
134 "Quad".into(),
135 "BorderStyle".into(),
136 "SpriteInputIndex".into(),
137 "MonochromeSprite".into(),
138 "PolychromeSprite".into(),
139 "PathSprite".into(),
140 "SurfaceInputIndex".into(),
141 "SurfaceBounds".into(),
142 "TransformationMatrix".into(),
143 ]);
144 config.no_includes = true;
145 config.enumeration.prefix_with_name = true;
146
147 let mut builder = cbindgen::Builder::new();
148
149 let src_paths = [
150 crate_dir.join("src/scene.rs"),
151 crate_dir.join("src/geometry.rs"),
152 crate_dir.join("src/color.rs"),
153 crate_dir.join("src/window.rs"),
154 crate_dir.join("src/platform.rs"),
155 crate_dir.join("src/platform/mac/metal_renderer.rs"),
156 ];
157 for src_path in src_paths {
158 println!("cargo:rerun-if-changed={}", src_path.display());
159 builder = builder.with_src(src_path);
160 }
161
162 builder
163 .with_config(config)
164 .generate()
165 .expect("Unable to generate bindings")
166 .write_to_file(&output_path);
167
168 output_path
169 }
170
171 /// To enable runtime compilation, we need to "stitch" the shaders file with the generated header
172 /// so that it is self-contained.
173 #[cfg(feature = "runtime_shaders")]
174 fn emit_stitched_shaders(header_path: &Path) {
175 use std::str::FromStr;
176 fn stitch_header(header: &Path, shader_path: &Path) -> std::io::Result<PathBuf> {
177 let header_contents = std::fs::read_to_string(header)?;
178 let shader_contents = std::fs::read_to_string(shader_path)?;
179 let stitched_contents = format!("{header_contents}\n{shader_contents}");
180 let out_path =
181 PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal");
182 std::fs::write(&out_path, stitched_contents)?;
183 Ok(out_path)
184 }
185 let shader_source_path = "./src/platform/mac/shaders.metal";
186 let shader_path = PathBuf::from_str(shader_source_path).unwrap();
187 stitch_header(header_path, &shader_path).unwrap();
188 println!("cargo:rerun-if-changed={}", &shader_source_path);
189 }
190
191 #[cfg(not(feature = "runtime_shaders"))]
192 fn compile_metal_shaders(header_path: &Path) {
193 use std::process::{self, Command};
194 let shader_path = "./src/platform/mac/shaders.metal";
195 let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
196 let metallib_output_path =
197 PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
198 println!("cargo:rerun-if-changed={}", shader_path);
199
200 let output = Command::new("xcrun")
201 .args([
202 "-sdk",
203 "macosx",
204 "metal",
205 "-gline-tables-only",
206 "-mmacosx-version-min=10.15.7",
207 "-MO",
208 "-c",
209 shader_path,
210 "-include",
211 (header_path.to_str().unwrap()),
212 "-o",
213 ])
214 .arg(&air_output_path)
215 .output()
216 .unwrap();
217
218 if !output.status.success() {
219 eprintln!(
220 "metal shader compilation failed:\n{}",
221 String::from_utf8_lossy(&output.stderr)
222 );
223 process::exit(1);
224 }
225
226 let output = Command::new("xcrun")
227 .args(["-sdk", "macosx", "metallib"])
228 .arg(air_output_path)
229 .arg("-o")
230 .arg(metallib_output_path)
231 .output()
232 .unwrap();
233
234 if !output.status.success() {
235 eprintln!(
236 "metallib compilation failed:\n{}",
237 String::from_utf8_lossy(&output.stderr)
238 );
239 process::exit(1);
240 }
241 }
242}
243
244#[cfg(target_os = "windows")]
245mod windows {
246 use std::path::PathBuf;
247
248 pub(super) fn build() {
249 // Link the AMD AGS library
250 link_amd_ags();
251
252 // Embed the Windows manifest and resource file
253 #[cfg(feature = "windows-manifest")]
254 embed_resource();
255 }
256
257 fn link_amd_ags() {
258 // We can not use relative paths in `cargo:rustc-link-search`, so we need to use the absolute path.
259 // See: https://stackoverflow.com/questions/41917096/how-do-i-make-rustc-link-search-relative-to-the-project-location
260 let lib_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("libs");
261 #[cfg(target_pointer_width = "64")]
262 let lib_name = "amd_ags_x64_2022_MT";
263 #[cfg(target_pointer_width = "32")]
264 let lib_name = "amd_ags_x86_2022_MT";
265 println!("cargo:rustc-link-lib=static={}", lib_name);
266 println!("cargo:rustc-link-search=native={}", lib_dir.display());
267 println!(
268 "cargo:rerun-if-changed={}/{}.lib",
269 lib_dir.display(),
270 lib_name
271 );
272 }
273
274 #[cfg(feature = "windows-manifest")]
275 fn embed_resource() {
276 let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml");
277 let rc_file = std::path::Path::new("resources/windows/gpui.rc");
278 println!("cargo:rerun-if-changed={}", manifest.display());
279 println!("cargo:rerun-if-changed={}", rc_file.display());
280 embed_resource::compile(rc_file, embed_resource::NONE)
281 .manifest_required()
282 .unwrap();
283 }
284}