-
Notifications
You must be signed in to change notification settings - Fork 12
/
build.rs
197 lines (179 loc) · 6.8 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
use regex::Regex;
use std::fmt::{Display, Formatter};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::{env, fmt};
#[derive(Clone, Debug)]
pub struct Target {
pub architecture: String,
pub vendor: String,
pub system: Option<String>,
pub abi: Option<String>,
}
impl Target {
pub fn as_strs(&self) -> (&str, &str, Option<&str>, Option<&str>) {
(
self.architecture.as_str(),
self.vendor.as_str(),
self.system.as_deref(),
self.abi.as_deref(),
)
}
}
impl Display for Target {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}-{}", &self.architecture, &self.vendor)?;
if let Some(ref system) = self.system {
write!(f, "-{}", system)
} else {
Ok(())
}?;
if let Some(ref abi) = self.abi {
write!(f, "-{}", abi)
} else {
Ok(())
}
}
}
pub fn ndk() -> String {
env::var("ANDROID_NDK").expect("ANDROID_NDK variable not set")
}
pub fn target_arch(arch: &str) -> &str {
match arch {
"armv7" => "arm",
"aarch64" => "arm64",
"i686" => "x86",
arch => arch,
}
}
fn host_tag() -> &'static str {
// Because this is part of build.rs, the target_os is actually the host system
if cfg!(target_os = "windows") {
"windows-x86_64"
} else if cfg!(target_os = "linux") {
"linux-x86_64"
} else if cfg!(target_os = "macos") {
"darwin-x86_64"
} else {
panic!("host os is not supported")
}
}
/// Get NDK major version from source.properties
fn ndk_major_version(ndk_dir: &Path) -> u32 {
// Capture version from the line with Pkg.Revision
let re = Regex::new(r"Pkg.Revision = (\d+)\.(\d+)\.(\d+)").unwrap();
// There's a source.properties file in the ndk directory, which contains
let mut source_properties =
File::open(ndk_dir.join("source.properties")).expect("Couldn't open source.properties");
let mut buf = String::new();
source_properties
.read_to_string(&mut buf)
.expect("Could not read source.properties");
// Capture version info
let captures = re
.captures(&buf)
.expect("source.properties did not match the regex");
// Capture 0 is the whole line of text
captures[1].parse().expect("could not parse major version")
}
fn main() {
let target_str = env::var("TARGET").unwrap();
let target: Vec<String> = target_str.split('-').map(|s| s.into()).collect();
assert!(target.len() >= 2, "Failed to parse TARGET {}", target_str);
let abi = if target.len() > 3 {
Some(target[3].clone())
} else {
None
};
let system = if target.len() > 2 {
Some(target[2].clone())
} else {
None
};
let target = Target {
architecture: target[0].clone(),
vendor: target[1].clone(),
system,
abi,
};
let mut build = cc::Build::new();
build
.file("./deps/ada.cpp")
.include("./deps")
.cpp(true)
.std("c++17");
let compile_target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH");
let compile_target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS");
let compile_target_feature = env::var("CARGO_CFG_TARGET_FEATURE");
// Except for Emscripten target (which emulates POSIX environment), compile to Wasm via WASI SDK
// which is currently the only standalone provider of stdlib for compilation of C/C++ libraries.
match target.system.as_deref() {
Some("android" | "androideabi") => {
let ndk = ndk();
let major = ndk_major_version(Path::new(&ndk));
if major < 22 {
build
.flag(&format!("--sysroot={}/sysroot", ndk))
.flag(&format!(
"-isystem{}/sources/cxx-stl/llvm-libc++/include",
ndk
));
} else {
// NDK versions >= 22 have the sysroot in the llvm prebuilt by
let host_toolchain = format!("{}/toolchains/llvm/prebuilt/{}", ndk, host_tag());
// sysroot is stored in the prebuilt llvm, under the host
build.flag(&format!("--sysroot={}/sysroot", host_toolchain));
}
}
_ => {
if compile_target_arch.starts_with("wasm") && compile_target_os != "emscripten" {
let wasi_sdk = env::var("WASI_SDK").unwrap_or_else(|_| "/opt/wasi-sdk".to_owned());
assert!(
Path::new(&wasi_sdk).exists(),
"WASI SDK not found at {wasi_sdk}"
);
build.compiler(format!("{wasi_sdk}/bin/clang++"));
let wasi_sysroot_lib = match compile_target_feature {
Ok(compile_target_feature) if compile_target_feature.contains("atomics") => {
"wasm32-wasi-threads"
}
_ => "wasm32-wasi",
};
println!(
"cargo:rustc-link-search={wasi_sdk}/share/wasi-sysroot/lib/{wasi_sysroot_lib}"
);
// Wasm exceptions are new and not yet supported by WASI SDK.
build.flag("-fno-exceptions");
// WASI SDK only has libc++ available.
build.cpp_set_stdlib("c++");
// Explicitly link C++ ABI to avoid linking errors (it takes care of C++ -> C "lowering").
println!("cargo:rustc-link-lib=c++abi");
// Because Ada is a pure parsing library that doesn't need any OS syscalls,
// it's also possible to compile it to wasm32-unknown-unknown.
// This still requires WASI SDK for libc & libc++, but then we need a few hacks / overrides to get a pure Wasm w/o imports instead.
if compile_target_os == "unknown" {
build.target("wasm32-wasi");
println!("cargo:rustc-link-lib=c");
build.file("./deps/wasi_to_unknown.cpp");
}
}
let compiler = build.get_compiler();
// Note: it's possible to use Clang++ explicitly on Windows as well, so this check
// should be specifically for "is target compiler MSVC" and not "is target OS Windows".
if compiler.is_like_msvc() {
build.static_crt(true);
link_args::windows! {
unsafe {
no_default_lib(
"libcmt.lib",
);
}
}
} else if compiler.is_like_clang() && cfg!(feature = "libcpp") {
build.cpp_set_stdlib("c++");
}
}
}
build.compile("ada");
}