1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
4 // Partially based on llvm-sys; llvm-sys's license is reproduced below:
6 // Copyright (c) 2015 Peter Marheine
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:
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
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
40 use std::io::prelude::*;
41 use std::path::{Path, PathBuf};
42 use std::process::Command;
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";
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,
52 const LLVM_7_SOURCE_DIR_SUFFIX: &'static str = "llvm-7.0.0.src";
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
59 let count = f.read(&mut buffer).unwrap();
63 context.update(&buffer[..count]);
65 let hash = context.finish();
66 if hash.as_ref() != LLVM_7_SOURCE_TAR_XZ_SHA256_HASH {
68 "file is corrupted: SHA256 doesn't match; try deleting {} and rerunning cargo",
72 f.seek(io::SeekFrom::Start(0)).unwrap();
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),
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()
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))
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)
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),
107 extract_tar_xz(download_llvm_7(llvm_dir)?, llvm_dir)?;
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,
122 .define("LLVM_TARGETS_TO_BUILD", "host")
123 .define("LLVM_CCACHE_BUILD", if found_ccache { "ON" } else { "OFF" })
126 env::var("TARGET").unwrap().split("-").next().unwrap(),
130 .always_configure(false);
134 fn llvm_config<A: IntoIterator<Item = S>, S: AsRef<OsStr>>(
135 llvm_config_path: &Path,
139 Command::new(llvm_config_path)
140 .arg("--link-static")
149 fn get_libs<A: IntoIterator<Item = S>, S: AsRef<OsStr>>(
150 llvm_config_path: &Path,
153 llvm_config(llvm_config_path, args)
155 .chain(llvm_config(llvm_config_path, Some("--system-libs")).split_whitespace())
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])
164 // Linker flags style, -lfoo
165 assert!(flag.starts_with("-l"));
173 struct LockedFile(fs::File);
175 impl Drop for LockedFile {
177 let _ = self.0.unlock();
182 fn new<T: AsRef<Path>>(file_path: T) -> io::Result<Self> {
183 let file = fs::OpenOptions::new()
188 file.lock_exclusive()?;
193 fn build_llvm() -> PathBuf {
195 env::var_os("TARGET"),
197 "cross-compilation is not supported"
199 let llvm_dir = env::current_dir()
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();
210 let llvm_config_path = llvm_dir.join("bin").join("llvm-config.exe");
212 let llvm_config_path = llvm_dir.join("bin").join("llvm-config");
217 let out_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).to_path_buf();
218 let llvm_config_path = build_llvm();
220 "cargo:rustc-link-search=native={}",
221 llvm_config(&llvm_config_path, Some("--libdir"))
223 let llvm_libs = get_libs(
225 &["--libs", "orcjit", "native", "analysis"],
228 #include "llvm-c/Core.h"
229 #include "llvm-c/OrcBindings.h"
230 #include "llvm-c/Target.h"
231 #include "llvm-c/Analysis.h"
239 void LLVM_InitializeNativeTarget(void);
240 void LLVM_InitializeNativeAsmParser(void);
241 void LLVM_InitializeNativeAsmPrinter(void);
242 void LLVM_InitializeNativeDisassembler(void);
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)
253 LLVM_NATIVE_TARGETINFO();
254 LLVM_NATIVE_TARGET();
255 LLVM_NATIVE_TARGETMC();
258 void LLVM_InitializeNativeAsmParser(void)
260 LLVM_NATIVE_ASMPARSER();
263 void LLVM_InitializeNativeAsmPrinter(void)
265 LLVM_NATIVE_ASMPRINTER();
268 void LLVM_InitializeNativeDisassembler(void)
270 LLVM_NATIVE_DISASSEMBLER();
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"))
278 let builder = bindgen::Builder::default()
279 .header(header_path.to_str().unwrap())
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.*");
292 .write_to_file(out_dir.join("llvm_c.rs"))
294 let build_llvm_bindings = || {
295 let mut retval = cc::Build::new();
298 .file(&llvm_bindings_path)
299 .include(&include_dir);
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);
308 // build twice to get the c++ standard library linked after LLVM with llvm_bindings before LLVM
309 build_llvm_bindings().compile("llvm_bindings");