format source
[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("LLVM_APPEND_VC_REV", "OFF") // stop llvm needing relink after git commit
125 .define(
126 "LLVM_TARGET_ARCH",
127 env::var("TARGET").unwrap().split("-").next().unwrap(),
128 )
129 .out_dir(llvm_dir)
130 .profile("Debug")
131 .always_configure(false);
132 retval
133 }
134
135 fn llvm_config<A: IntoIterator<Item = S>, S: AsRef<OsStr>>(
136 llvm_config_path: &Path,
137 args: A,
138 ) -> String {
139 String::from_utf8(
140 Command::new(llvm_config_path)
141 .arg("--link-static")
142 .args(args)
143 .output()
144 .unwrap()
145 .stdout,
146 )
147 .unwrap()
148 }
149
150 fn get_libs<A: IntoIterator<Item = S>, S: AsRef<OsStr>>(
151 llvm_config_path: &Path,
152 args: A,
153 ) -> Vec<String> {
154 llvm_config(llvm_config_path, args)
155 .split_whitespace()
156 .chain(llvm_config(llvm_config_path, Some("--system-libs")).split_whitespace())
157 .filter_map(|flag| {
158 if flag == "" {
159 None
160 } else if cfg!(target_env = "msvc") {
161 // Same as --libnames, foo.lib
162 assert!(flag.ends_with(".lib"));
163 Some(&flag[..flag.len() - 4])
164 } else {
165 // Linker flags style, -lfoo
166 assert!(flag.starts_with("-l"));
167 Some(&flag[2..])
168 }
169 })
170 .map(Into::into)
171 .collect()
172 }
173
174 struct LockedFile(fs::File);
175
176 impl Drop for LockedFile {
177 fn drop(&mut self) {
178 let _ = self.0.unlock();
179 }
180 }
181
182 impl LockedFile {
183 fn new<T: AsRef<Path>>(file_path: T) -> io::Result<Self> {
184 let file = fs::OpenOptions::new()
185 .read(true)
186 .write(true)
187 .create(true)
188 .open(file_path)?;
189 file.lock_exclusive()?;
190 Ok(LockedFile(file))
191 }
192 }
193
194 fn build_llvm() -> PathBuf {
195 assert_eq!(
196 env::var_os("TARGET"),
197 env::var_os("HOST"),
198 "cross-compilation is not supported"
199 );
200 let llvm_dir = env::current_dir()
201 .unwrap()
202 .join("..")
203 .join("external")
204 .join("llvm-7")
205 .join(env::var_os("TARGET").unwrap());
206 fs::create_dir_all(&llvm_dir).unwrap();
207 let _locked_file = LockedFile::new(llvm_dir.join(".build-lock")).unwrap();
208 download_and_extract_llvm_7_if_needed(&llvm_dir).unwrap();
209 make_config(&llvm_dir).build_target("install").build();
210 #[cfg(windows)]
211 let llvm_config_path = llvm_dir.join("bin").join("llvm-config.exe");
212 #[cfg(not(windows))]
213 let llvm_config_path = llvm_dir.join("bin").join("llvm-config");
214 llvm_config_path
215 }
216
217 fn main() {
218 let out_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).to_path_buf();
219 let llvm_config_path = build_llvm();
220 println!(
221 "cargo:rustc-link-search=native={}",
222 llvm_config(&llvm_config_path, Some("--libdir"))
223 );
224 let llvm_libs = get_libs(
225 &llvm_config_path,
226 &["--libs", "orcjit", "native", "analysis"],
227 );
228 let header = r#"
229 #include "llvm-c/Core.h"
230 #include "llvm-c/OrcBindings.h"
231 #include "llvm-c/Target.h"
232 #include "llvm-c/Analysis.h"
233 #include <stdbool.h>
234
235 #ifdef __cplusplus
236 extern "C"
237 {
238 #endif
239
240 void LLVM_InitializeNativeTarget(void);
241 void LLVM_InitializeNativeAsmParser(void);
242 void LLVM_InitializeNativeAsmPrinter(void);
243 void LLVM_InitializeNativeDisassembler(void);
244
245 #ifdef __cplusplus
246 }
247 #endif
248 "#;
249 let header_path = out_dir.join("llvm_bindings.h");
250 fs::write(&header_path, header).unwrap();
251 let llvm_bindings_source = format!("#include {:?}\n", header_path)
252 + r#"
253 void LLVM_InitializeNativeTarget(void)
254 {
255 LLVM_NATIVE_TARGETINFO();
256 LLVM_NATIVE_TARGET();
257 LLVM_NATIVE_TARGETMC();
258 }
259
260 void LLVM_InitializeNativeAsmParser(void)
261 {
262 LLVM_NATIVE_ASMPARSER();
263 }
264
265 void LLVM_InitializeNativeAsmPrinter(void)
266 {
267 LLVM_NATIVE_ASMPRINTER();
268 }
269
270 void LLVM_InitializeNativeDisassembler(void)
271 {
272 LLVM_NATIVE_DISASSEMBLER();
273 }
274 "#;
275 let llvm_bindings_path = out_dir.join("llvm_bindings.c");
276 fs::write(&llvm_bindings_path, llvm_bindings_source).unwrap();
277 let include_dir: String = llvm_config(&llvm_config_path, Some("--includedir"))
278 .trim_right()
279 .into();
280 let builder = bindgen::Builder::default()
281 .header(header_path.to_str().unwrap())
282 .clang_arg("-I")
283 .clang_arg(&include_dir as &str)
284 .rustfmt_bindings(true)
285 .whitelist_type("LLVM.*")
286 .whitelist_function("LLVM.*")
287 .whitelist_var("LLVM.*")
288 .blacklist_type("^__.*")
289 .prepend_enum_name(false)
290 .constified_enum("LLVM.*");
291 builder
292 .generate()
293 .unwrap()
294 .write_to_file(out_dir.join("llvm_c.rs"))
295 .unwrap();
296 let build_llvm_bindings = || {
297 let mut retval = cc::Build::new();
298 retval
299 .cpp(true)
300 .file(&llvm_bindings_path)
301 .include(&include_dir);
302 retval
303 };
304 build_llvm_bindings()
305 .cpp_link_stdlib(None)
306 .compile("llvm_bindings");
307 for lib in llvm_libs {
308 println!("cargo:rustc-link-lib={}", lib);
309 }
310 // build twice to get the c++ standard library linked after LLVM with llvm_bindings before LLVM
311 build_llvm_bindings().compile("llvm_bindings");
312 }