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