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+ }
289
3- // Taken from https://github.com/Brooooooklyn/ada-url/blob/main/ada/build.rs
490fn 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+ } ;
8113
9114 let mut build = cc:: Build :: new ( ) ;
10115 build
@@ -20,52 +125,76 @@ fn main() {
20125 let compile_target_feature = env:: var ( "CARGO_CFG_TARGET_FEATURE" ) ;
21126 // Except for Emscripten target (which emulates POSIX environment), compile to Wasm via WASI SDK
22127 // 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) ) ;
33145 }
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" ) ;
50146 }
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}"
64163 ) ;
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++" ) ;
65180 }
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+ }
69198 }
70199
71200 build. compile ( "ada" ) ;
0 commit comments