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