@@ -10,7 +10,6 @@ fn main() {
1010 let out_path = PathBuf :: from ( env:: var ( "OUT_DIR" ) . unwrap ( ) ) ;
1111 let builddir = env:: var ( "PYTHON_BUILD_DIR" ) . ok ( ) ;
1212 emit_rerun_instructions ( builddir. as_deref ( ) ) ;
13- prefer_newest_libclang ( ) ;
1413 if gil_disabled ( srcdir, builddir. as_deref ( ) ) {
1514 println ! ( "cargo:rustc-cfg=py_gil_disabled" ) ;
1615 }
@@ -41,38 +40,32 @@ fn emit_rerun_instructions(builddir: Option<&str>) {
4140 }
4241}
4342
44- /// When LIBCLANG_PATH is not already set, scan /usr/lib/llvm-*/lib for the
45- /// newest available libclang and point bindgen at it. Ubuntu 24.04 ships
46- /// libclang-18 by default, which has broken headers (stdatomic.h, mmintrin.h).
47- /// CI jobs that install a newer LLVM (e.g. clang-20) also get a working
48- /// libclang in /usr/lib/llvm-20/lib -- we just need to tell bindgen about it.
49- fn prefer_newest_libclang ( ) {
50- if env:: var_os ( "LIBCLANG_PATH" ) . is_some ( ) {
51- return ;
52- }
43+ /// Find the newest clang resource directory on the system.
44+ ///
45+ /// Ubuntu 24.04 ships libclang-18 whose built-in headers (stdatomic.h,
46+ /// mmintrin.h) are broken. CI jobs install a newer clang (e.g. clang-20)
47+ /// whose resource directory at /usr/lib/llvm-20/lib/clang/20 has working
48+ /// headers. We pass -resource-dir to bindgen's clang so it picks up those
49+ /// headers instead of the broken libclang-18 ones.
50+ fn newest_clang_resource_dir ( ) -> Option < PathBuf > {
5351 let base = Path :: new ( "/usr/lib" ) ;
5452 let mut best: Option < ( u32 , PathBuf ) > = None ;
55- if let Ok ( entries) = std:: fs:: read_dir ( base) {
56- for entry in entries. flatten ( ) {
57- let name = entry. file_name ( ) ;
58- let name = name. to_string_lossy ( ) ;
59- if let Some ( ver_str) = name. strip_prefix ( "llvm-" ) {
60- if let Ok ( ver) = ver_str. parse :: < u32 > ( ) {
61- let lib_dir = entry. path ( ) . join ( "lib" ) ;
62- if lib_dir. is_dir ( ) {
63- if best. as_ref ( ) . map_or ( true , |( v, _) | ver > * v) {
64- best = Some ( ( ver, lib_dir) ) ;
65- }
66- }
67- }
53+ for entry in std:: fs:: read_dir ( base) . ok ( ) ?. flatten ( ) {
54+ let name = entry. file_name ( ) ;
55+ let name = name. to_string_lossy ( ) ;
56+ if let Some ( ver_str) = name. strip_prefix ( "llvm-" )
57+ && let Ok ( ver) = ver_str. parse :: < u32 > ( )
58+ {
59+ // Resource dir: /usr/lib/llvm-<N>/lib/clang/<N>
60+ let resource_dir = entry. path ( ) . join ( "lib" ) . join ( "clang" ) . join ( ver_str) ;
61+ if resource_dir. join ( "include" ) . is_dir ( )
62+ && best. as_ref ( ) . map_or ( true , |( v, _) | ver > * v)
63+ {
64+ best = Some ( ( ver, resource_dir) ) ;
6865 }
6966 }
7067 }
71- if let Some ( ( ver, lib_dir) ) = best {
72- eprintln ! ( "cpython-sys: using libclang from llvm-{ver}" ) ;
73- // SAFETY: build scripts are single-threaded.
74- unsafe { env:: set_var ( "LIBCLANG_PATH" , & lib_dir) } ;
75- }
68+ best. map ( |( _, p) | p)
7669}
7770
7871fn gil_disabled ( srcdir : & Path , builddir : Option < & str > ) -> bool {
@@ -98,6 +91,18 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat
9891 // Suppress all clang warnings (deprecation warnings, etc.)
9992 builder = builder. clang_arg ( "-w" ) ;
10093
94+ // Use the newest clang resource directory available on the system.
95+ // Bindgen links against whatever libclang it finds (often an older
96+ // system version), but the built-in headers in that version may be
97+ // broken (e.g. libclang-18 on Ubuntu 24.04 has broken stdatomic.h
98+ // and mmintrin.h). Overriding -resource-dir makes clang use a
99+ // newer set of built-in headers without changing which libclang.so
100+ // is loaded.
101+ if let Some ( resource_dir) = newest_clang_resource_dir ( ) {
102+ eprintln ! ( "cpython-sys: using clang resource dir {}" , resource_dir. display( ) ) ;
103+ builder = builder. clang_arg ( format ! ( "-resource-dir={}" , resource_dir. display( ) ) ) ;
104+ }
105+
101106 // Tell clang the correct target triple for cross-compilation when we have
102107 // an LLVM-specific triple. Otherwise let bindgen translate Cargo's TARGET
103108 // itself (e.g. aarch64-apple-ios-sim -> arm64-apple-ios-simulator).
0 commit comments