git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

build.rs (9.6K)

  1 use std::env;
  2 use std::path::PathBuf;
  3 
  4 fn main() {
  5     println!("cargo:rerun-if-changed=src/server/wlroots_log_wrapper.c");
  6     println!("cargo:rerun-if-changed=wrapper.h");
  7     // Vendored scenefx sources: without these, editing a scenefx .c/.h
  8     // silently ships a stale static lib (meson only reruns when build.rs
  9     // does; meson compile is a fast no-op when nothing changed).
 10     println!("cargo:rerun-if-changed=scenefx/types");
 11     println!("cargo:rerun-if-changed=scenefx/render");
 12     println!("cargo:rerun-if-changed=scenefx/include");
 13     println!("cargo:rerun-if-changed=protocol/river-xkb-bindings-v1.xml");
 14     println!("cargo:rerun-if-changed=protocol/river-layer-shell-v1.xml");
 15     println!("cargo:rerun-if-changed=protocol/river-input-management-v1.xml");
 16 
 17     // Build local scenefx
 18     let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
 19     if !std::path::Path::new("scenefx/build").exists() {
 20         let status = std::process::Command::new("meson")
 21             .args(&["setup", "build", "--buildtype=release", "--default-library=static"])
 22             .current_dir("scenefx")
 23             .status()
 24             .expect("Failed to run meson setup");
 25         assert!(status.success(), "meson setup failed");
 26     }
 27     let status = std::process::Command::new("meson")
 28         .args(&["compile", "-C", "build"])
 29         .current_dir("scenefx")
 30         .status()
 31         .expect("Failed to run meson compile");
 32     assert!(status.success(), "meson compile failed");
 33 
 34     let scenefx_inc1 = PathBuf::from(&manifest_dir).join("scenefx/include");
 35     let scenefx_inc2 = PathBuf::from(&manifest_dir).join("scenefx/build/include");
 36     let scenefx_inc3 = PathBuf::from(&manifest_dir).join("scenefx/build/protocol");
 37     let scenefx_include_paths = vec![scenefx_inc1, scenefx_inc2, scenefx_inc3];
 38 
 39     println!("cargo:rustc-link-search=native={}/scenefx/build", manifest_dir);
 40     println!("cargo:rustc-link-lib=static=scenefx-0.5");
 41     println!("cargo:rustc-link-lib=dylib=GLESv2");
 42     println!("cargo:rustc-link-lib=dylib=EGL");
 43     println!("cargo:rustc-link-lib=dylib=drm");
 44     println!("cargo:rustc-link-lib=dylib=gbm");
 45     println!("cargo:rustc-link-lib=dylib=lcms2");
 46 
 47     let wlroots = pkg_config::Config::new()
 48         .atleast_version("0.20.0")
 49         .probe("wlroots-0.20")
 50         .expect("wlroots-0.20 is required");
 51 
 52     let wl_server = pkg_config::probe_library("wayland-server")
 53         .expect("wayland-server is required");
 54 
 55     let xkb = pkg_config::probe_library("xkbcommon")
 56         .expect("xkbcommon is required");
 57 
 58     let pixman = pkg_config::probe_library("pixman-1")
 59         .expect("pixman-1 is required");
 60 
 61     let libinput = pkg_config::probe_library("libinput")
 62         .expect("libinput is required");
 63 
 64     let libevdev = pkg_config::probe_library("libevdev")
 65         .expect("libevdev is required");
 66 
 67     let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
 68 
 69     // Helper to fix XML files starting with comments instead of the XML declaration
 70     let clean_xml = |src: &str, dst: &std::path::Path| {
 71         let content = std::fs::read_to_string(src).expect("Failed to read XML source");
 72         let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
 73         if lines.len() > 1 && lines[0].starts_with("<!--") && lines[1].starts_with("<?xml") {
 74             lines.swap(0, 1);
 75         }
 76         let cleaned = lines.join("\n");
 77         std::fs::write(dst, cleaned).expect("Failed to write cleaned XML");
 78     };
 79 
 80     // Generate upstream protocol headers (header-only)
 81     let upstream_protocols = vec![
 82         ("wlr-layer-shell-unstable-v1.xml", "protocol/upstream/wlr-layer-shell-unstable-v1.xml"),
 83         ("wlr-output-power-management-unstable-v1.xml", "protocol/upstream/wlr-output-power-management-unstable-v1.xml"),
 84         ("virtual-keyboard-unstable-v1.xml", "protocol/upstream/virtual-keyboard-unstable-v1.xml"),
 85         ("tablet-v2.xml", "/usr/share/wayland-protocols/stable/tablet/tablet-v2.xml"),
 86         ("xdg-shell.xml", "/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml"),
 87         ("color-management-v1.xml", "/usr/share/wayland-protocols/staging/color-management/color-management-v1.xml"),
 88         ("content-type-v1.xml", "/usr/share/wayland-protocols/staging/content-type/content-type-v1.xml"),
 89         ("cursor-shape-v1.xml", "/usr/share/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml"),
 90         ("ext-image-copy-capture-v1.xml", "/usr/share/wayland-protocols/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml"),
 91         ("pointer-constraints-unstable-v1.xml", "/usr/share/wayland-protocols/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"),
 92         ("tearing-control-v1.xml", "/usr/share/wayland-protocols/staging/tearing-control/tearing-control-v1.xml"),
 93     ];
 94 
 95     for (name, path) in upstream_protocols {
 96         let temp_xml = out_dir.join(format!("{}-temp.xml", name));
 97         clean_xml(path, &temp_xml);
 98         let header_name = name.replace(".xml", "-protocol.h");
 99         let status = std::process::Command::new("wayland-scanner")
100             .args(&[
101                 "server-header",
102                 temp_xml.to_str().unwrap(),
103                 out_dir.join(&header_name).to_str().unwrap(),
104             ])
105             .status()
106             .expect("failed to execute wayland-scanner");
107         assert!(status.success(), "wayland-scanner server-header failed for {}", name);
108     }
109 
110     // Generate custom river protocol headers and private-code C files
111     let custom_protocols = vec![
112         ("river-xkb-bindings-v1.xml", "protocol/river-xkb-bindings-v1.xml"),
113         ("river-layer-shell-v1.xml", "protocol/river-layer-shell-v1.xml"),
114         ("river-input-management-v1.xml", "protocol/river-input-management-v1.xml"),
115         ("river-libinput-config-v1.xml", "protocol/river-libinput-config-v1.xml"),
116         ("river-xkb-config-v1.xml", "protocol/river-xkb-config-v1.xml"),
117         ("cce-inspector-v1.xml", "protocol/cce-inspector-v1.xml"),
118         ("cce-window-management-v1.xml", "protocol/cce-window-management-v1.xml"),
119     ];
120 
121     let mut generated_c_files = Vec::new();
122 
123     for (name, path) in custom_protocols {
124         let temp_xml = out_dir.join(format!("{}-temp.xml", name));
125         clean_xml(path, &temp_xml);
126 
127         let header_name = name.replace(".xml", "-protocol.h");
128         let status_h = std::process::Command::new("wayland-scanner")
129             .args(&[
130                 "server-header",
131                 temp_xml.to_str().unwrap(),
132                 out_dir.join(&header_name).to_str().unwrap(),
133             ])
134             .status()
135             .expect("failed to execute wayland-scanner");
136         assert!(status_h.success(), "wayland-scanner server-header failed for {}", name);
137 
138         let code_name = name.replace(".xml", "-protocol.c");
139         let code_path = out_dir.join(&code_name);
140         let status_c = std::process::Command::new("wayland-scanner")
141             .args(&[
142                 "private-code",
143                 temp_xml.to_str().unwrap(),
144                 code_path.to_str().unwrap(),
145             ])
146             .status()
147             .expect("failed to execute wayland-scanner");
148         assert!(status_c.success(), "wayland-scanner private-code failed for {}", name);
149 
150         generated_c_files.push(code_path);
151     }
152 
153     // Compile the C wrapper and protocol C files
154     let mut build = cc::Build::new();
155     build.file("src/server/wlroots_log_wrapper.c")
156         .define("WLR_USE_UNSTABLE", None)
157         .flag("-std=c99")
158         .flag("-O2")
159         .include(&out_dir);
160 
161     for c_file in generated_c_files {
162         build.file(c_file);
163     }
164 
165     // Add include paths for scenefx, wlroots, and wayland-server
166     for path in &scenefx_include_paths {
167         build.include(path);
168     }
169     for path in &wlroots.include_paths {
170         build.include(path);
171     }
172     for path in &wl_server.include_paths {
173         build.include(path);
174     }
175     build.compile("wlroots_log_wrapper");
176 
177 
178     // Setup bindgen
179     let mut builder = bindgen::Builder::default()
180         .header("wrapper.h")
181         .clang_arg("-DWLR_USE_UNSTABLE")
182         .layout_tests(false)
183         .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
184 
185     // Pass include paths to bindgen clang argument parser
186     let mut include_paths = vec![out_dir.clone()];
187     include_paths.extend(scenefx_include_paths.clone());
188     include_paths.extend(wlroots.include_paths.clone());
189     include_paths.extend(wl_server.include_paths.clone());
190     include_paths.extend(xkb.include_paths.clone());
191     include_paths.extend(pixman.include_paths.clone());
192     include_paths.extend(libinput.include_paths.clone());
193     include_paths.extend(libevdev.include_paths.clone());
194 
195     for path in include_paths {
196         builder = builder.clang_arg(format!("-I{}", path.display()));
197     }
198 
199     let bindings = builder
200         .blocklist_item("FP_NAN")
201         .blocklist_item("FP_INFINITE")
202         .blocklist_item("FP_ZERO")
203         .blocklist_item("FP_SUBNORMAL")
204         .blocklist_item("FP_NORMAL")
205         .blocklist_item("wl_listener")
206         .blocklist_item("wlr_addon")
207         .blocklist_item("wlr_input_device")
208         .blocklist_item("pixman_region32")
209         .blocklist_item("pixman_region32_t")
210         .blocklist_item("pixman_box32")
211         .blocklist_item("pixman_box32_t")
212         .blocklist_item("pixman_rectangle32")
213         .blocklist_item("pixman_rectangle32_t")
214         .blocklist_item("pixman_region32_data")
215         .blocklist_item("pixman_region32_data_t")
216         .generate()
217         .expect("Unable to generate bindings");
218 
219     let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
220     bindings
221         .write_to_file(out_path.join("bindings.rs"))
222         .expect("Couldn't write bindings!");
223 }