building for 32/64-bit windows/linux works
[kazan.git] / vulkan-driver / build.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
3 extern crate bindgen;
4 extern crate regex;
5 extern crate xmltree;
6 use std::env;
7 use std::fs;
8 use std::io;
9 use std::ops::Deref;
10 use std::path::{Path, PathBuf};
11 use std::str;
12 use xmltree::Element;
13
14 const VULKAN_HEADERS_INCLUDE_PATH: &'static str = "../external/Vulkan-Headers/include";
15
16 fn detect_vulkan_calling_convention() -> io::Result<String> {
17 let code = bindgen::builder()
18 .header_contents(
19 "vulkan_detect.h",
20 r#"#include <vulkan/vk_platform.h>
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24
25 VKAPI_ATTR void VKAPI_CALL detect_fn();
26
27 #ifdef __cplusplus
28 }
29 #endif
30 "#,
31 )
32 .clang_arg("-target")
33 .clang_arg(env::var("TARGET").unwrap())
34 .clang_arg(format!("-I{}", VULKAN_HEADERS_INCLUDE_PATH))
35 .whitelist_function("detect_fn")
36 .generate()
37 .map_err(|_| io::Error::new(io::ErrorKind::Other, "generate() failed"))?
38 .to_string();
39 if let Some(captures) = regex::Regex::new(r#"extern "([^"]+)""#)
40 .unwrap()
41 .captures(&code)
42 {
43 Ok(captures[1].to_owned())
44 } else {
45 eprintln!("code:\n{}", code);
46 Err(io::Error::new(io::ErrorKind::Other, "regex not found"))
47 }
48 }
49
50 fn main() -> io::Result<()> {
51 let vulkan_wrapper_header_path = "vulkan-wrapper.h";
52 let vulkan_vk_xml_path = "../external/Vulkan-Headers/registry/vk.xml";
53 println!("cargo:rerun-if-changed={}", vulkan_wrapper_header_path);
54 println!("cargo:rerun-if-changed={}", VULKAN_HEADERS_INCLUDE_PATH);
55 println!("cargo:rerun-if-changed={}", vulkan_vk_xml_path);
56 let parsed_xml = Element::parse(fs::File::open(&PathBuf::from(vulkan_vk_xml_path))?)
57 .map_err(|v| io::Error::new(io::ErrorKind::Other, format!("{}", v)))?;
58 let types = parsed_xml.get_child("types").unwrap();
59 let header_version: u32 = types
60 .children
61 .iter()
62 .filter_map(|v| {
63 if v.get_child("name")
64 .and_then(|v| v.text.as_ref().map(Deref::deref))
65 == Some("VK_HEADER_VERSION")
66 {
67 Some(v.text.as_ref().unwrap())
68 } else {
69 None
70 }
71 })
72 .next()
73 .unwrap()
74 .trim()
75 .parse()
76 .unwrap();
77 let vulkan_calling_convention = detect_vulkan_calling_convention()?;
78 let match_calling_convention_regex = regex::Regex::new(r#"extern "([^"]+)""#).unwrap();
79 let mut builder = bindgen::builder()
80 .header(vulkan_wrapper_header_path)
81 .clang_arg("-target")
82 .clang_arg(env::var("TARGET").unwrap())
83 .clang_arg(format!("-I{}", VULKAN_HEADERS_INCLUDE_PATH))
84 .prepend_enum_name(false)
85 .layout_tests(false)
86 .whitelist_var("VK_.*")
87 .whitelist_var("ICD_LOADER_MAGIC");
88 for t in types
89 .children
90 .iter()
91 .filter(|v| v.attributes.get("category").map(|v| &**v) == Some("handle"))
92 {
93 let name = if let Some(name) = t.attributes.get("name") {
94 name
95 } else {
96 t.get_child("name").unwrap().text.as_ref().unwrap()
97 };
98 if name.ends_with("NVX") {
99 continue;
100 }
101 builder = builder
102 .blacklist_type(format!("^{}$", name))
103 .blacklist_type(format!("^{}_T$", name));
104 }
105 builder = builder
106 .whitelist_type("PFN_.*")
107 .whitelist_type("^Vk.*")
108 .blacklist_type("^xcb_.*")
109 .blacklist_type("^VkDebugReportCallbackCreateInfoEXT$")
110 .blacklist_type("^VkDebugUtilsMessengerCreateInfoEXT$")
111 .blacklist_type("^VkAllocationCallbacks$")
112 .ignore_functions()
113 .constified_enum(".*");
114 let mut code = builder
115 .generate()
116 .map_err(|_| io::Error::new(io::ErrorKind::Other, "generate() failed"))?
117 .to_string();
118 code = match_calling_convention_regex
119 .replace_all(&code, |captures: &regex::Captures| {
120 if captures[1] == vulkan_calling_convention {
121 r#"extern "system""#
122 } else {
123 let _ = fs::write(
124 PathBuf::from(env::var("OUT_DIR").unwrap()).join("vulkan-types.rs"),
125 &code,
126 );
127 eprintln!("vulkan_calling_convention: {:?}", vulkan_calling_convention);
128 panic!("unhandled non-vulkan calling convention");
129 }
130 })
131 .into_owned();
132 fs::write(
133 PathBuf::from(env::var("OUT_DIR").unwrap()).join("vulkan-types.rs"),
134 code,
135 )?;
136 let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
137 let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
138 println!("target_family = {:?}", target_family);
139 println!("target_os = {:?}", target_os);
140 let driver_name_prefix = if target_family == "unix" {
141 "lib"
142 } else if target_os == "windows" {
143 ""
144 } else {
145 unimplemented!()
146 };
147 let driver_name_suffix = if target_os == "linux" || target_os == "android" {
148 ".so"
149 } else if target_os == "macos" || target_os == "ios" {
150 ".dylib"
151 } else if target_os == "windows" {
152 ".dll"
153 } else {
154 unimplemented!()
155 };
156 let driver_name = format!("{}kazan_driver{}", driver_name_prefix, driver_name_suffix);
157 fs::write(
158 PathBuf::from(env::var("OUT_DIR").unwrap()).join("kazan_driver.json"),
159 format!(
160 r#"{{
161 "file_format_version": "1.0.0",
162 "ICD": {{
163 "library_path": "{}",
164 "api_version": "1.1.{}"
165 }}
166 }}"#,
167 PathBuf::from(env::var("OUT_DIR").unwrap())
168 .parent()
169 .and_then(Path::parent)
170 .and_then(Path::parent)
171 .unwrap_or_else(|| Path::new(""))
172 .join(driver_name)
173 .to_str()
174 .unwrap(),
175 header_version
176 ),
177 )?;
178 Ok(())
179 }