@@ -48,6 +48,34 @@ fn emit_rerun_instructions(builddir: Option<&str>) {
4848 }
4949}
5050
51+ /// Find the newest clang resource directory on the system.
52+ ///
53+ /// Ubuntu 24.04 ships libclang-18 whose built-in headers (stdatomic.h,
54+ /// mmintrin.h) are broken. CI jobs install a newer clang (e.g. clang-20)
55+ /// whose resource directory at /usr/lib/llvm-20/lib/clang/20 has working
56+ /// headers. We pass -resource-dir to bindgen's clang so it picks up those
57+ /// headers instead of the broken libclang-18 ones.
58+ fn newest_clang_resource_dir ( ) -> Option < PathBuf > {
59+ let base = Path :: new ( "/usr/lib" ) ;
60+ let mut best: Option < ( u32 , PathBuf ) > = None ;
61+ for entry in std:: fs:: read_dir ( base) . ok ( ) ?. flatten ( ) {
62+ let name = entry. file_name ( ) ;
63+ let name = name. to_string_lossy ( ) ;
64+ if let Some ( ver_str) = name. strip_prefix ( "llvm-" )
65+ && let Ok ( ver) = ver_str. parse :: < u32 > ( )
66+ {
67+ // Resource dir: /usr/lib/llvm-<N>/lib/clang/<N>
68+ let resource_dir = entry. path ( ) . join ( "lib" ) . join ( "clang" ) . join ( ver_str) ;
69+ if resource_dir. join ( "include" ) . is_dir ( )
70+ && best. as_ref ( ) . map_or ( true , |( v, _) | ver > * v)
71+ {
72+ best = Some ( ( ver, resource_dir) ) ;
73+ }
74+ }
75+ }
76+ best. map ( |( _, p) | p)
77+ }
78+
5179fn gil_disabled ( srcdir : & Path , builddir : Option < & str > ) -> bool {
5280 if env_var_is_truthy ( "PY_GIL_DISABLED" ) {
5381 return true ;
@@ -93,6 +121,21 @@ fn generate_c_api_bindings(
93121 builder = builder. clang_arg ( "-DPy_GIL_DISABLED=1" ) ;
94122 }
95123
124+ // Use the newest clang resource directory available on the system.
125+ // Bindgen links against whatever libclang it finds (often an older
126+ // system version), but the built-in headers in that version may be
127+ // broken (e.g. libclang-18 on Ubuntu 24.04 has broken stdatomic.h
128+ // and mmintrin.h). Overriding -resource-dir makes clang use a
129+ // newer set of built-in headers without changing which libclang.so
130+ // is loaded.
131+ if let Some ( resource_dir) = newest_clang_resource_dir ( ) {
132+ eprintln ! (
133+ "cpython-sys: using clang resource dir {}" ,
134+ resource_dir. display( )
135+ ) ;
136+ builder = builder. clang_arg ( format ! ( "-resource-dir={}" , resource_dir. display( ) ) ) ;
137+ }
138+
96139 // Tell clang the correct target triple for cross-compilation when we have
97140 // an LLVM-specific triple. Otherwise let bindgen translate Cargo's TARGET
98141 // itself (e.g. aarch64-apple-ios-sim -> arm64-apple-ios-simulator).
@@ -180,6 +223,7 @@ fn generate_c_api_bindings(
180223 for dir in include_dirs {
181224 builder = builder. clang_arg ( format ! ( "-I{}" , dir. display( ) ) ) ;
182225 }
226+
183227 builder = add_target_clang_args ( builder, builddir) ;
184228
185229 let bindings = builder
0 commit comments