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 "ShadowInputIndex".into(),
132 "Shadow".into(),
133 "QuadInputIndex".into(),
134 "Underline".into(),
135 "UnderlineInputIndex".into(),
136 "Quad".into(),
137 "BorderStyle".into(),
138 "SpriteInputIndex".into(),
139 "MonochromeSprite".into(),
140 "PolychromeSprite".into(),
141 "PathSprite".into(),
142 "SurfaceInputIndex".into(),
143 "SurfaceBounds".into(),
144 "TransformationMatrix".into(),
145 ]);
146 config.no_includes = true;
147 config.enumeration.prefix_with_name = true;
148
149 let mut builder = cbindgen::Builder::new();
150
151 let src_paths = [
152 crate_dir.join("src/scene.rs"),
153 crate_dir.join("src/geometry.rs"),
154 crate_dir.join("src/color.rs"),
155 crate_dir.join("src/window.rs"),
156 crate_dir.join("src/platform.rs"),
157 crate_dir.join("src/platform/mac/metal_renderer.rs"),
158 ];
159 for src_path in src_paths {
160 println!("cargo:rerun-if-changed={}", src_path.display());
161 builder = builder.with_src(src_path);
162 }
163
164 builder
165 .with_config(config)
166 .generate()
167 .expect("Unable to generate bindings")
168 .write_to_file(&output_path);
169
170 output_path
171 }
172
173 /// To enable runtime compilation, we need to "stitch" the shaders file with the generated header
174 /// so that it is self-contained.
175 #[cfg(feature = "runtime_shaders")]
176 fn emit_stitched_shaders(header_path: &Path) {
177 use std::str::FromStr;
178 fn stitch_header(header: &Path, shader_path: &Path) -> std::io::Result<PathBuf> {
179 let header_contents = std::fs::read_to_string(header)?;
180 let shader_contents = std::fs::read_to_string(shader_path)?;
181 let stitched_contents = format!("{header_contents}\n{shader_contents}");
182 let out_path =
183 PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal");
184 std::fs::write(&out_path, stitched_contents)?;
185 Ok(out_path)
186 }
187 let shader_source_path = "./src/platform/mac/shaders.metal";
188 let shader_path = PathBuf::from_str(shader_source_path).unwrap();
189 stitch_header(header_path, &shader_path).unwrap();
190 println!("cargo:rerun-if-changed={}", &shader_source_path);
191 }
192
193 #[cfg(not(feature = "runtime_shaders"))]
194 fn compile_metal_shaders(header_path: &Path) {
195 use std::process::{self, Command};
196 let shader_path = "./src/platform/mac/shaders.metal";
197 let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
198 let metallib_output_path =
199 PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
200 println!("cargo:rerun-if-changed={}", shader_path);
201
202 let output = Command::new("xcrun")
203 .args([
204 "-sdk",
205 "macosx",
206 "metal",
207 "-gline-tables-only",
208 "-mmacosx-version-min=10.15.7",
209 "-MO",
210 "-c",
211 shader_path,
212 "-include",
213 (header_path.to_str().unwrap()),
214 "-o",
215 ])
216 .arg(&air_output_path)
217 .output()
218 .unwrap();
219
220 if !output.status.success() {
221 eprintln!(
222 "metal shader compilation failed:\n{}",
223 String::from_utf8_lossy(&output.stderr)
224 );
225 process::exit(1);
226 }
227
228 let output = Command::new("xcrun")
229 .args(["-sdk", "macosx", "metallib"])
230 .arg(air_output_path)
231 .arg("-o")
232 .arg(metallib_output_path)
233 .output()
234 .unwrap();
235
236 if !output.status.success() {
237 eprintln!(
238 "metallib compilation failed:\n{}",
239 String::from_utf8_lossy(&output.stderr)
240 );
241 process::exit(1);
242 }
243 }
244}