add SPIRV-Headers submodule
[kazan.git] / shader-compiler-backend-llvm-7 / build.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
3
4 // Partially based on llvm-sys; llvm-sys's license is reproduced below:
5
6 // Copyright (c) 2015 Peter Marheine
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy of
9 // this software and associated documentation files (the "Software"), to deal in
10 // the Software without restriction, including without limitation the rights to
11 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 // of the Software, and to permit persons to whom the Software is furnished to do
13 // so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
25
26 extern crate bindgen;
27 extern crate cc;
28 extern crate cmake;
29 extern crate fs2;
30 extern crate reqwest;
31 extern crate ring;
32 extern crate tar;
33 extern crate which;
34 extern crate xz2;
35 use fs2::FileExt;
36 use std::env;
37 use std::ffi::OsStr;
38 use std::fs;
39 use std::io;
40 use std::io::prelude::*;
41 use std::path::{Path, PathBuf};
42 use std::process::Command;
43
44 const LLVM_7_SOURCE_TAR_XZ_URL: &'static str =
45 "http://releases.llvm.org/7.0.0/llvm-7.0.0.src.tar.xz";
46
47 const LLVM_7_SOURCE_TAR_XZ_SHA256_HASH: &'static [u8; 32] = &[
48 0x8b, 0xc1, 0xf8, 0x44, 0xe6, 0xcb, 0xde, 0x1b, 0x65, 0x2c, 0x19, 0xc1, 0xed, 0xeb, 0xc1, 0x86,
49 0x44, 0x56, 0xfd, 0x9c, 0x78, 0xb8, 0xc1, 0xbe, 0xa0, 0x38, 0xe5, 0x1b, 0x36, 0x3f, 0xe2, 0x22,
50 ];
51
52 const LLVM_7_SOURCE_DIR_SUFFIX: &'static str = "llvm-7.0.0.src";
53
54 fn verify_sha256(mut f: fs::File, file_path: &Path) -> fs::File {
55 f.seek(io::SeekFrom::Start(0)).unwrap();
56 let mut context = ring::digest::Context::new(&ring::digest::SHA256);
57 let mut buffer = [0; 1 << 12]; // 4KiB
58 loop {
59 let count = f.read(&mut buffer).unwrap();
60 if count == 0 {
61 break;
62 }
63 context.update(&buffer[..count]);
64 }
65 let hash = context.finish();
66 if hash.as_ref() != LLVM_7_SOURCE_TAR_XZ_SHA256_HASH {
67 panic!(
68 "file is corrupted: SHA256 doesn't match; try deleting {} and rerunning cargo",
69 file_path.display(),
70 );
71 }
72 f.seek(io::SeekFrom::Start(0)).unwrap();
73 f
74 }
75
76 fn download_llvm_7(out_dir: &Path) -> io::Result<fs::File> {
77 let filename = LLVM_7_SOURCE_TAR_XZ_URL.rsplit('/').next().unwrap();
78 let file_path = out_dir.join(filename);
79 match fs::File::open(&file_path) {
80 Ok(file) => return Ok(verify_sha256(file, &file_path)),
81 Err(ref error) if error.kind() == io::ErrorKind::NotFound => {}
82 Err(error) => return Err(error),
83 };
84 let response = reqwest::get(LLVM_7_SOURCE_TAR_XZ_URL)
85 .map_err(|v| io::Error::new(io::ErrorKind::Other, v))?;
86 let file = fs::OpenOptions::new()
87 .write(true)
88 .create_new(true)
89 .open(&file_path)?;
90 { response }
91 .copy_to(&mut { file })
92 .map_err(|v| io::Error::new(io::ErrorKind::Other, v))?;
93 Ok(verify_sha256(fs::File::open(&file_path)?, &file_path))
94 }
95
96 fn extract_tar_xz<R: Read, T: AsRef<Path>>(r: R, target_path: T) -> io::Result<()> {
97 tar::Archive::new(xz2::read::XzDecoder::new(r)).unpack(target_path)
98 }
99
100 fn download_and_extract_llvm_7_if_needed(llvm_dir: &Path) -> io::Result<PathBuf> {
101 let source_dir = llvm_dir.join(LLVM_7_SOURCE_DIR_SUFFIX);
102 match fs::File::open(source_dir.join("CMakeLists.txt")) {
103 Ok(_) => return Ok(source_dir),
104 Err(ref error) if error.kind() == io::ErrorKind::NotFound => {}
105 Err(error) => return Err(error),
106 }
107 extract_tar_xz(download_llvm_7(llvm_dir)?, llvm_dir)?;
108 Ok(source_dir)
109 }
110
111 fn make_config(llvm_dir: &Path) -> cmake::Config {
112 let mut retval = cmake::Config::new(llvm_dir.join(LLVM_7_SOURCE_DIR_SUFFIX));
113 let found_ccache = match which::which("ccache") {
114 Err(ref error) if error.kind() == which::ErrorKind::CannotFindBinaryPath => false,
115 result => {
116 result.unwrap();
117 true
118 }
119 };
120 retval
121 .generator("Ninja")
122 .define("LLVM_TARGETS_TO_BUILD", "host")
123 .define("LLVM_CCACHE_BUILD", if found_ccache { "ON" } else { "OFF" })
124 .define(
125 "LLVM_TARGET_ARCH",
126 env::var("TARGET").unwrap().split("-").next().unwrap(),
127 )
128 .out_dir(llvm_dir)
129 .profile("Debug")
130 .always_configure(false);
131 retval
132 }
133
134 fn llvm_config<A: IntoIterator<Item = S>, S: AsRef<OsStr>>(
135 llvm_config_path: &Path,
136 args: A,
137 ) -> String {
138 String::from_utf8(
139 Command::new(llvm_config_path)
140 .arg("--link-static")
141 .args(args)
142 .output()
143 .unwrap()
144 .stdout,
145 )
146 .unwrap()
147 }
148
149 fn get_libs<A: IntoIterator<Item = S>, S: AsRef<OsStr>>(
150 llvm_config_path: &Path,
151 args: A,
152 ) -> Vec<String> {
153 llvm_config(llvm_config_path, args)
154 .split_whitespace()
155 .chain(llvm_config(llvm_config_path, Some("--system-libs")).split_whitespace())
156 .filter_map(|flag| {
157 if flag == "" {
158 None
159 } else if cfg!(target_env = "msvc") {
160 // Same as --libnames, foo.lib
161 assert!(flag.ends_with(".lib"));
162 Some(&flag[..flag.len() - 4])
163 } else {
164 // Linker flags style, -lfoo
165 assert!(flag.starts_with("-l"));
166 Some(&flag[2..])
167 }
168 })
169 .map(Into::into)
170 .collect()
171 }
172
173 struct LockedFile(fs::File);
174
175 impl Drop for LockedFile {
176 fn drop(&mut self) {
177 let _ = self.0.unlock();
178 }
179 }
180
181 impl LockedFile {
182 fn new<T: AsRef<Path>>(file_path: T) -> io::Result<Self> {
183 let file = fs::OpenOptions::new()
184 .read(true)
185 .write(true)
186 .create(true)
187 .open(file_path)?;
188 file.lock_exclusive()?;
189 Ok(LockedFile(file))
190 }
191 }
192
193 fn build_llvm() -> PathBuf {
194 assert_eq!(
195 env::var_os("TARGET"),
196 env::var_os("HOST"),
197 "cross-compilation is not supported"
198 );
199 let llvm_dir = env::current_dir()
200 .unwrap()
201 .join("..")
202 .join("external")
203 .join("llvm-7")
204 .join(env::var_os("TARGET").unwrap());
205 fs::create_dir_all(&llvm_dir).unwrap();
206 let _locked_file = LockedFile::new(llvm_dir.join(".build-lock")).unwrap();
207 download_and_extract_llvm_7_if_needed(&llvm_dir).unwrap();
208 make_config(&llvm_dir).build_target("install").build();
209 #[cfg(windows)]
210 let llvm_config_path = llvm_dir.join("bin").join("llvm-config.exe");
211 #[cfg(not(windows))]
212 let llvm_config_path = llvm_dir.join("bin").join("llvm-config");
213 llvm_config_path
214 }
215
216 fn main() {
217 let out_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).to_path_buf();
218 let llvm_config_path = build_llvm();
219 println!(
220 "cargo:rustc-link-search=native={}",
221 llvm_config(&llvm_config_path, Some("--libdir"))
222 );
223 let llvm_libs = get_libs(
224 &llvm_config_path,
225 &["--libs", "orcjit", "native", "analysis"],
226 );
227 let header = r#"
228 #include "llvm-c/Core.h"
229 #include "llvm-c/OrcBindings.h"
230 #include "llvm-c/Target.h"
231 #include "llvm-c/Analysis.h"
232 #include <stdbool.h>
233
234 #ifdef __cplusplus
235 extern "C"
236 {
237 #endif
238
239 void LLVM_InitializeNativeTarget(void);
240 void LLVM_InitializeNativeAsmParser(void);
241 void LLVM_InitializeNativeAsmPrinter(void);
242 void LLVM_InitializeNativeDisassembler(void);
243
244 #ifdef __cplusplus
245 }
246 #endif
247 "#;
248 let header_path = out_dir.join("llvm_bindings.h");
249 fs::write(&header_path, header).unwrap();
250 let llvm_bindings_source = format!("#include {:?}\n", header_path) + r#"
251 void LLVM_InitializeNativeTarget(void)
252 {
253 LLVM_NATIVE_TARGETINFO();
254 LLVM_NATIVE_TARGET();
255 LLVM_NATIVE_TARGETMC();
256 }
257
258 void LLVM_InitializeNativeAsmParser(void)
259 {
260 LLVM_NATIVE_ASMPARSER();
261 }
262
263 void LLVM_InitializeNativeAsmPrinter(void)
264 {
265 LLVM_NATIVE_ASMPRINTER();
266 }
267
268 void LLVM_InitializeNativeDisassembler(void)
269 {
270 LLVM_NATIVE_DISASSEMBLER();
271 }
272 "#;
273 let llvm_bindings_path = out_dir.join("llvm_bindings.c");
274 fs::write(&llvm_bindings_path, llvm_bindings_source).unwrap();
275 let include_dir: String = llvm_config(&llvm_config_path, Some("--includedir"))
276 .trim_right()
277 .into();
278 let builder = bindgen::Builder::default()
279 .header(header_path.to_str().unwrap())
280 .clang_arg("-I")
281 .clang_arg(&include_dir as &str)
282 .rustfmt_bindings(true)
283 .whitelist_type("LLVM.*")
284 .whitelist_function("LLVM.*")
285 .whitelist_var("LLVM.*")
286 .blacklist_type("^__.*")
287 .prepend_enum_name(false)
288 .constified_enum("LLVM.*");
289 builder
290 .generate()
291 .unwrap()
292 .write_to_file(out_dir.join("llvm_c.rs"))
293 .unwrap();
294 let build_llvm_bindings = || {
295 let mut retval = cc::Build::new();
296 retval
297 .cpp(true)
298 .file(&llvm_bindings_path)
299 .include(&include_dir);
300 retval
301 };
302 build_llvm_bindings()
303 .cpp_link_stdlib(None)
304 .compile("llvm_bindings");
305 for lib in llvm_libs {
306 println!("cargo:rustc-link-lib={}", lib);
307 }
308 // build twice to get the c++ standard library linked after LLVM with llvm_bindings before LLVM
309 build_llvm_bindings().compile("llvm_bindings");
310 }