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