@@ -65,36 +65,70 @@ struct Bindings {
6565// bindgen needs access to libclang.
6666// On windows, this doesn't just work, you have to set LIBCLANG_PATH.
6767// Rather than download the 400Mb+ files, like gecko does, let's just reuse their work.
68+ // On macOS, clang-sys prefers the highest-versioned libclang it can find, which may be a
69+ // Homebrew LLVM that doesn't have the correct macOS SDK include paths, resulting in broken
70+ // bindings. Force use of Xcode's libclang instead.
6871fn setup_clang ( ) {
69- // If this isn't Windows, or we're in CI, then we don't need to do anything.
70- if env:: consts:: OS != "windows" || env:: var ( "GITHUB_WORKFLOW" ) . unwrap_or_default ( ) == "CI" {
72+ println ! ( "cargo:rerun-if-env-changed=LIBCLANG_PATH" ) ;
73+ println ! ( "cargo:rerun-if-env-changed=CI" ) ;
74+ // In CI, the environment is already configured correctly.
75+ if env:: var ( "CI" ) . is_ok ( ) {
7176 return ;
7277 }
73- println ! ( "rerun-if-env-changed=LIBCLANG_PATH" ) ;
74- println ! ( "rerun-if-env-changed=MOZBUILD_STATE_PATH" ) ;
7578 if env:: var ( "LIBCLANG_PATH" ) . is_ok ( ) {
7679 return ;
7780 }
78- let mozbuild_root = if let Ok ( dir) = env:: var ( "MOZBUILD_STATE_PATH" ) {
79- PathBuf :: from ( dir. trim ( ) )
80- } else {
81- eprintln ! ( "warning: Building without a gecko setup is not likely to work." ) ;
82- eprintln ! ( " A working libclang is needed to build nss-rs." ) ;
83- eprintln ! ( " Either LIBCLANG_PATH or MOZBUILD_STATE_PATH needs to be set." ) ;
84- eprintln ! ( ) ;
85- eprintln ! ( " We recommend checking out https://github.com/mozilla/gecko-dev" ) ;
86- eprintln ! ( " Then run `./mach bootstrap` which will retrieve clang." ) ;
87- eprintln ! ( " Make sure to export MOZBUILD_STATE_PATH when building." ) ;
88- return ;
89- } ;
90- let libclang_dir = mozbuild_root. join ( "clang" ) . join ( "lib" ) ;
91- if libclang_dir. is_dir ( ) {
92- unsafe {
93- env:: set_var ( "LIBCLANG_PATH" , libclang_dir. to_str ( ) . unwrap ( ) ) ;
81+ if env:: consts:: OS == "macos" {
82+ if let Ok ( output) = Command :: new ( "xcode-select" ) . arg ( "--print-path" ) . output ( ) {
83+ if output. status . success ( ) {
84+ let xcode_path = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
85+ let candidates = [
86+ PathBuf :: from ( & xcode_path) . join ( "Toolchains/XcodeDefault.xctoolchain/usr/lib" ) ,
87+ PathBuf :: from ( & xcode_path) . join ( "usr/lib" ) ,
88+ ] ;
89+ if let Some ( libclang_dir) = candidates. iter ( ) . find ( |p| p. is_dir ( ) ) {
90+ unsafe {
91+ env:: set_var ( "LIBCLANG_PATH" , libclang_dir. to_str ( ) . unwrap ( ) ) ;
92+ }
93+ } else {
94+ println ! (
95+ "cargo:warning=Xcode toolchain libclang not found at {}; set LIBCLANG_PATH if build fails" ,
96+ candidates[ 0 ] . display( )
97+ ) ;
98+ }
99+ } else {
100+ println ! (
101+ "cargo:warning=xcode-select returned an error; set LIBCLANG_PATH if build fails"
102+ ) ;
103+ }
104+ } else {
105+ println ! ( "cargo:warning=xcode-select not found; set LIBCLANG_PATH if build fails" ) ;
106+ }
107+ } else if env:: consts:: OS == "windows" {
108+ println ! ( "cargo:rerun-if-env-changed=MOZBUILD_STATE_PATH" ) ;
109+ let mozbuild_root = if let Ok ( dir) = env:: var ( "MOZBUILD_STATE_PATH" ) {
110+ PathBuf :: from ( dir. trim ( ) )
111+ } else {
112+ println ! ( "cargo:warning=Building without a gecko setup is not likely to work." ) ;
113+ println ! ( "cargo:warning=A working libclang is needed to build nss-rs." ) ;
114+ println ! ( "cargo:warning=Either LIBCLANG_PATH or MOZBUILD_STATE_PATH needs to be set." ) ;
115+ println ! (
116+ "cargo:warning=We recommend checking out https://github.com/mozilla/gecko-dev"
117+ ) ;
118+ println ! ( "cargo:warning=Then run `./mach bootstrap` which will retrieve clang." ) ;
119+ println ! ( "cargo:warning=Make sure to export MOZBUILD_STATE_PATH when building." ) ;
120+ return ;
121+ } ;
122+ let libclang_dir = mozbuild_root. join ( "clang" ) . join ( "lib" ) ;
123+ if libclang_dir. is_dir ( ) {
124+ unsafe {
125+ env:: set_var ( "LIBCLANG_PATH" , libclang_dir. to_str ( ) . unwrap ( ) ) ;
126+ }
127+ } else {
128+ println ! (
129+ "cargo:warning=LIBCLANG_PATH isn't set; maybe run ./mach bootstrap with gecko"
130+ ) ;
94131 }
95- println ! ( "rustc-env:LIBCLANG_PATH={}" , libclang_dir. to_str( ) . unwrap( ) ) ;
96- } else {
97- println ! ( "warning: LIBCLANG_PATH isn't set; maybe run ./mach bootstrap with gecko" ) ;
98132 }
99133}
100134
@@ -167,8 +201,7 @@ fn build_nss(dir: PathBuf) {
167201 // Generate static libraries in addition to shared libraries.
168202 String :: from( "--static" ) ,
169203 ] ;
170- let target = env:: var ( "TARGET" ) . unwrap ( ) ;
171- if target. starts_with ( "aarch64-" ) {
204+ if env:: var ( "CARGO_CFG_TARGET_ARCH" ) . unwrap ( ) == "aarch64" {
172205 build_nss. push ( String :: from ( "--target=arm64" ) ) ;
173206 }
174207 let status = Command :: new ( get_bash ( ) )
@@ -180,7 +213,8 @@ fn build_nss(dir: PathBuf) {
180213}
181214
182215fn dynamic_link ( ) {
183- let dynamic_libs = if env:: consts:: OS == "windows" {
216+ let target_os = env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) ;
217+ let dynamic_libs = if target_os == "windows" {
184218 [
185219 "nssutil3.dll" ,
186220 "nss3.dll" ,
@@ -198,12 +232,13 @@ fn dynamic_link() {
198232}
199233
200234fn static_link ( ) {
235+ let target_os = env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) ;
201236 let mut static_libs = vec ! [
202237 "certdb" ,
203238 "certhi" ,
204239 "cryptohi" ,
205240 "freebl_static" ,
206- if env :: consts :: OS == "windows" {
241+ if target_os == "windows" {
207242 "libnspr4"
208243 } else {
209244 "nspr4"
@@ -215,12 +250,12 @@ fn static_link() {
215250 "nsspki" ,
216251 "nssutil" ,
217252 "pk11wrap_static" ,
218- if env :: consts :: OS == "windows" {
253+ if target_os == "windows" {
219254 "libplc4"
220255 } else {
221256 "plc4"
222257 } ,
223- if env :: consts :: OS == "windows" {
258+ if target_os == "windows" {
224259 "libplds4"
225260 } else {
226261 "plds4"
@@ -230,7 +265,7 @@ fn static_link() {
230265 ] ;
231266 // macOS always dynamically links against the system sqlite library.
232267 // See https://github.com/nss-dev/nss/blob/a8c22d8fc0458db3e261acc5e19b436ab573a961/coreconf/Darwin.mk#L130-L135
233- if env :: consts :: OS == "macos" {
268+ if target_os == "macos" {
234269 println ! ( "cargo:rustc-link-lib=dylib=sqlite3" ) ;
235270 } else {
236271 static_libs. push ( "sqlite" ) ;
@@ -263,11 +298,7 @@ fn static_link() {
263298fn get_includes ( nsstarget : & Path , nssdist : & Path ) -> Vec < PathBuf > {
264299 let nsprinclude = nsstarget. join ( "include" ) . join ( "nspr" ) ;
265300 let nssinclude = nssdist. join ( "public" ) . join ( "nss" ) ;
266- let includes = vec ! [ nsprinclude, nssinclude] ;
267- for i in & includes {
268- println ! ( "cargo:include={}" , i. to_str( ) . unwrap( ) ) ;
269- }
270- includes
301+ vec ! [ nsprinclude, nssinclude]
271302}
272303
273304fn build_bindings ( base : & str , bindings : & Bindings , flags : & [ String ] , gecko : bool ) {
@@ -285,14 +316,15 @@ fn build_bindings(base: &str, bindings: &Bindings, flags: &[String], gecko: bool
285316 builder = builder. clang_arg ( "-v" ) ;
286317
287318 if !gecko {
319+ let target_os = env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) ;
288320 builder = builder. clang_arg ( "-DNO_NSPR_10_SUPPORT" ) ;
289- if env :: consts :: OS == "windows" {
321+ if target_os == "windows" {
290322 builder = builder. clang_arg ( "-DWIN" ) ;
291- } else if env :: consts :: OS == "macos" {
323+ } else if target_os == "macos" {
292324 builder = builder. clang_arg ( "-DDARWIN" ) ;
293- } else if env :: consts :: OS == "linux" {
325+ } else if target_os == "linux" {
294326 builder = builder. clang_arg ( "-DLINUX" ) ;
295- } else if env :: consts :: OS == "android" {
327+ } else if target_os == "android" {
296328 builder = builder. clang_arg ( "-DLINUX" ) ;
297329 builder = builder. clang_arg ( "-DANDROID" ) ;
298330 }
@@ -365,11 +397,9 @@ fn pkg_config() -> Result<Vec<String>, Box<dyn Error>> {
365397
366398 let mut flags: Vec < String > = Vec :: new ( ) ;
367399
368- for f in cfg_str. split ( ' ' ) {
369- if let Some ( include ) = f . strip_prefix ( "-I" ) {
400+ for f in cfg_str. split_whitespace ( ) {
401+ if f . starts_with ( "-I" ) {
370402 flags. push ( String :: from ( f) ) ;
371-
372- println ! ( "cargo:include={include}" ) ;
373403 } else if let Some ( path) = f. strip_prefix ( "-L" ) {
374404 println ! ( "cargo:rustc-link-search=native={path}" ) ;
375405 } else if let Some ( lib) = f. strip_prefix ( "-l" ) {
@@ -383,15 +413,12 @@ fn pkg_config() -> Result<Vec<String>, Box<dyn Error>> {
383413}
384414
385415fn setup_standalone ( nss_dir : String ) -> Vec < String > {
386- setup_clang ( ) ;
387-
388416 let nss = PathBuf :: from ( nss_dir) ;
389- println ! ( "cargo:rerun-if-env-changed={}" , nss . display ( ) ) ;
417+ println ! ( "cargo:rerun-if-env-changed=NSS_DIR" ) ;
390418 println ! ( "cargo:rerun-if-env-changed=NSS_PREBUILT" ) ;
391419
392420 // $NSS_DIR/../dist/
393421 let nssdist = nss. parent ( ) . unwrap ( ) . join ( "dist" ) ;
394- println ! ( "cargo:rerun-if-env-changed={}" , nssdist. display( ) ) ;
395422 let nsstarget = "Release" ;
396423
397424 // If NSS_PREBUILT is set to a non-zero value, we assume that the NSS libraries are already
@@ -411,7 +438,7 @@ fn setup_standalone(nss_dir: String) -> Vec<String> {
411438 if env:: var ( "CARGO_CFG_FUZZING" ) . is_ok ( )
412439 || env:: var ( "PROFILE" ) . unwrap_or_default ( ) == "debug"
413440 // FIXME: NSPR doesn't build proper dynamic libraries on Windows.
414- || env:: consts :: OS == "windows"
441+ || env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) == "windows"
415442 {
416443 static_link ( ) ;
417444 } else {
@@ -537,7 +564,10 @@ fn process_config(config: &mut HashMap<String, Bindings>) {
537564}
538565
539566fn main ( ) {
567+ println ! ( "cargo:rerun-if-changed=src/min_version.rs" ) ;
568+ println ! ( "cargo:rerun-if-changed=min_version.txt" ) ;
540569 println ! ( "cargo:rustc-check-cfg=cfg(nss_nodb)" ) ;
570+ setup_clang ( ) ;
541571
542572 let flags = if cfg ! ( feature = "gecko" ) {
543573 setup_for_gecko ( )
0 commit comments