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