build.rs

  1#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
  2
  3fn main() {
  4    #[cfg(target_os = "macos")]
  5    macos_build::run();
  6}
  7
  8#[cfg(target_os = "macos")]
  9mod macos_build {
 10    use std::{
 11        env,
 12        path::{Path, PathBuf},
 13    };
 14
 15    use cbindgen::Config;
 16
 17    pub fn run() {
 18        let header_path = generate_shader_bindings();
 19
 20        #[cfg(feature = "runtime_shaders")]
 21        emit_stitched_shaders(&header_path);
 22        #[cfg(not(feature = "runtime_shaders"))]
 23        compile_metal_shaders(&header_path);
 24    }
 25
 26    fn generate_shader_bindings() -> PathBuf {
 27        let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
 28
 29        let gpui_dir = find_gpui_crate_dir();
 30
 31        let mut config = Config {
 32            include_guard: Some("SCENE_H".into()),
 33            language: cbindgen::Language::C,
 34            no_includes: true,
 35            ..Default::default()
 36        };
 37        config.export.include.extend([
 38            "Bounds".into(),
 39            "Corners".into(),
 40            "Edges".into(),
 41            "Size".into(),
 42            "Pixels".into(),
 43            "PointF".into(),
 44            "Hsla".into(),
 45            "ContentMask".into(),
 46            "Uniforms".into(),
 47            "AtlasTile".into(),
 48            "PathRasterizationInputIndex".into(),
 49            "PathVertex_ScaledPixels".into(),
 50            "PathRasterizationVertex".into(),
 51            "ShadowInputIndex".into(),
 52            "Shadow".into(),
 53            "QuadInputIndex".into(),
 54            "Underline".into(),
 55            "UnderlineInputIndex".into(),
 56            "Quad".into(),
 57            "BorderStyle".into(),
 58            "SpriteInputIndex".into(),
 59            "MonochromeSprite".into(),
 60            "PolychromeSprite".into(),
 61            "PathSprite".into(),
 62            "SurfaceInputIndex".into(),
 63            "SurfaceBounds".into(),
 64            "TransformationMatrix".into(),
 65        ]);
 66        config.no_includes = true;
 67        config.enumeration.prefix_with_name = true;
 68
 69        let mut builder = cbindgen::Builder::new();
 70
 71        let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
 72
 73        // Source files from gpui that define types used in shaders
 74        let gpui_src_paths = [
 75            gpui_dir.join("src/scene.rs"),
 76            gpui_dir.join("src/geometry.rs"),
 77            gpui_dir.join("src/color.rs"),
 78            gpui_dir.join("src/window.rs"),
 79            gpui_dir.join("src/platform.rs"),
 80        ];
 81
 82        // Source files from this crate
 83        let local_src_paths = [crate_dir.join("src/metal_renderer.rs")];
 84
 85        for src_path in gpui_src_paths.iter().chain(local_src_paths.iter()) {
 86            println!("cargo:rerun-if-changed={}", src_path.display());
 87            builder = builder.with_src(src_path);
 88        }
 89
 90        builder
 91            .with_config(config)
 92            .generate()
 93            .expect("Unable to generate bindings")
 94            .write_to_file(&output_path);
 95
 96        output_path
 97    }
 98
 99    /// Locate the gpui crate directory relative to this crate.
100    fn find_gpui_crate_dir() -> PathBuf {
101        gpui::GPUI_MANIFEST_DIR.into()
102    }
103
104    /// To enable runtime compilation, we need to "stitch" the shaders file with the generated header
105    /// so that it is self-contained.
106    #[cfg(feature = "runtime_shaders")]
107    fn emit_stitched_shaders(header_path: &Path) {
108        fn stitch_header(header: &Path, shader_path: &Path) -> std::io::Result<PathBuf> {
109            let header_contents = std::fs::read_to_string(header)?;
110            let shader_contents = std::fs::read_to_string(shader_path)?;
111            let stitched_contents = format!("{header_contents}\n{shader_contents}");
112            let out_path =
113                PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal");
114            std::fs::write(&out_path, stitched_contents)?;
115            Ok(out_path)
116        }
117        let shader_source_path = "./src/shaders.metal";
118        let shader_path = PathBuf::from(shader_source_path);
119        stitch_header(header_path, &shader_path).unwrap();
120        println!("cargo:rerun-if-changed={}", &shader_source_path);
121    }
122
123    #[cfg(not(feature = "runtime_shaders"))]
124    fn compile_metal_shaders(header_path: &Path) {
125        use std::process::{self, Command};
126        let shader_path = "./src/shaders.metal";
127        let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
128        let metallib_output_path =
129            PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
130        println!("cargo:rerun-if-changed={}", shader_path);
131
132        let output = Command::new("xcrun")
133            .args([
134                "-sdk",
135                "macosx",
136                "metal",
137                "-gline-tables-only",
138                "-mmacosx-version-min=10.15.7",
139                "-MO",
140                "-c",
141                shader_path,
142                "-include",
143                (header_path.to_str().unwrap()),
144                "-o",
145            ])
146            .arg(&air_output_path)
147            .output()
148            .unwrap();
149
150        if !output.status.success() {
151            println!(
152                "cargo::error=metal shader compilation failed:\n{}",
153                String::from_utf8_lossy(&output.stderr)
154            );
155            process::exit(1);
156        }
157
158        let output = Command::new("xcrun")
159            .args(["-sdk", "macosx", "metallib"])
160            .arg(air_output_path)
161            .arg("-o")
162            .arg(metallib_output_path)
163            .output()
164            .unwrap();
165
166        if !output.status.success() {
167            println!(
168                "cargo::error=metallib compilation failed:\n{}",
169                String::from_utf8_lossy(&output.stderr)
170            );
171            process::exit(1);
172        }
173    }
174}