@@ -9,13 +9,37 @@ fn main() {
99 . expect ( "expected Modules/cpython-sys to live under the source tree" ) ;
1010 let out_path = PathBuf :: from ( env:: var ( "OUT_DIR" ) . unwrap ( ) ) ;
1111 let builddir = env:: var ( "PYTHON_BUILD_DIR" ) . ok ( ) ;
12+ emit_rerun_instructions ( builddir. as_deref ( ) ) ;
1213 if gil_disabled ( srcdir, builddir. as_deref ( ) ) {
1314 println ! ( "cargo:rustc-cfg=py_gil_disabled" ) ;
1415 }
1516 println ! ( "cargo::rustc-check-cfg=cfg(py_gil_disabled)" ) ;
1617 generate_c_api_bindings ( srcdir, builddir. as_deref ( ) , out_path. as_path ( ) ) ;
1718}
1819
20+ // Bindgen depends on build-time env and, on iOS, can also inherit the
21+ // deployment target from the generated Makefile. Declare both so Cargo reruns
22+ // the build script when those inputs change.
23+ fn emit_rerun_instructions ( builddir : Option < & str > ) {
24+ for var in [
25+ "IPHONEOS_DEPLOYMENT_TARGET" ,
26+ "LLVM_TARGET" ,
27+ "PYTHON_BUILD_DIR" ,
28+ "PY_CC" ,
29+ "PY_CPPFLAGS" ,
30+ "PY_CFLAGS" ,
31+ "TARGET" ,
32+ "WASI_SDK_PATH" ,
33+ ] {
34+ println ! ( "cargo:rerun-if-env-changed={var}" ) ;
35+ }
36+
37+ if let Some ( builddir) = builddir {
38+ let makefile = Path :: new ( builddir) . join ( "Makefile" ) ;
39+ println ! ( "cargo:rerun-if-changed={}" , makefile. display( ) ) ;
40+ }
41+ }
42+
1943fn gil_disabled ( srcdir : & Path , builddir : Option < & str > ) -> bool {
2044 let mut candidates = Vec :: new ( ) ;
2145 if let Some ( build) = builddir {
@@ -39,16 +63,13 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat
3963 // Suppress all clang warnings (deprecation warnings, etc.)
4064 builder = builder. clang_arg ( "-w" ) ;
4165
42- // Tell clang the correct target triple for cross-compilation.
43- // LLVM_TARGET is the clang/LLVM triple which may differ from the Rust
44- // target (e.g. arm64-apple-macosx vs aarch64-apple-darwin, or
45- // riscv64-unknown-linux-gnu vs riscv64gc-unknown-linux-gnu).
46- // Falls back to Cargo's TARGET if LLVM_TARGET is not set.
47- let target = env:: var ( "LLVM_TARGET" )
48- . or_else ( |_| env:: var ( "TARGET" ) )
49- . unwrap_or_default ( ) ;
50- if !target. is_empty ( ) {
51- builder = builder. clang_arg ( format ! ( "--target={}" , target) ) ;
66+ // Tell clang the correct target triple for cross-compilation when we have
67+ // an LLVM-specific triple. Otherwise let bindgen translate Cargo's TARGET
68+ // itself (e.g. aarch64-apple-ios-sim -> arm64-apple-ios-simulator).
69+ let cargo_target = env:: var ( "TARGET" ) . unwrap_or_default ( ) ;
70+ let llvm_target = env:: var ( "LLVM_TARGET" ) . unwrap_or_default ( ) ;
71+ if !llvm_target. is_empty ( ) && llvm_target != cargo_target {
72+ builder = builder. clang_arg ( format ! ( "--target={llvm_target}" ) ) ;
5273 }
5374
5475 // Extract cross-compilation flags from the C compiler command (PY_CC),
@@ -88,7 +109,7 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat
88109 // WASI SDK: WASI_SDK_PATH is set by Tools/wasm/wasi/__main__.py.
89110 // The sysroot is at $WASI_SDK_PATH/share/wasi-sysroot.
90111 if !have_sysroot
91- && target . contains ( "wasi" )
112+ && cargo_target . contains ( "wasi" )
92113 && let Ok ( sdk_path) = env:: var ( "WASI_SDK_PATH" )
93114 {
94115 let sysroot = PathBuf :: from ( & sdk_path) . join ( "share" ) . join ( "wasi-sysroot" ) ;
@@ -104,7 +125,7 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat
104125 // The sysroot is a sibling of bin/:
105126 // .../toolchains/llvm/prebuilt/<host>/sysroot
106127 if !have_sysroot
107- && target . contains ( "android" )
128+ && cargo_target . contains ( "android" )
108129 && let Ok ( cc) = env:: var ( "PY_CC" )
109130 && let Some ( parts) = shlex:: split ( & cc)
110131 && let Some ( binary) = parts. first ( )
@@ -129,6 +150,7 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat
129150 for dir in include_dirs {
130151 builder = builder. clang_arg ( format ! ( "-I{}" , dir. display( ) ) ) ;
131152 }
153+ builder = add_target_clang_args ( builder, builddir) ;
132154
133155 let bindings = builder
134156 . allowlist_function ( "_?Py.*" )
@@ -145,3 +167,40 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat
145167 . write_to_file ( out_path. join ( "c_api.rs" ) )
146168 . expect ( "Couldn't write bindings!" ) ;
147169}
170+
171+ fn add_target_clang_args (
172+ mut builder : bindgen:: Builder ,
173+ builddir : Option < & str > ,
174+ ) -> bindgen:: Builder {
175+ let target = env:: var ( "TARGET" ) . unwrap_or_default ( ) ;
176+ if !target. contains ( "apple-ios" ) {
177+ return builder;
178+ }
179+
180+ // For iOS targets, bindgen may parse headers with an iOS simulator/device
181+ // target but without a deployment minimum, which disables TLS support.
182+ let deployment_target = ios_deployment_target ( builddir) . unwrap_or_else ( || "13.0" . to_string ( ) ) ;
183+ builder = builder. clang_arg ( format ! ( "-mios-version-min={deployment_target}" ) ) ;
184+ builder
185+ }
186+
187+ fn ios_deployment_target ( builddir : Option < & str > ) -> Option < String > {
188+ if let Ok ( value) = env:: var ( "IPHONEOS_DEPLOYMENT_TARGET" )
189+ && !value. is_empty ( )
190+ {
191+ return Some ( value) ;
192+ }
193+
194+ let builddir = builddir?;
195+ let makefile = Path :: new ( builddir) . join ( "Makefile" ) ;
196+ let text = std:: fs:: read_to_string ( makefile) . ok ( ) ?;
197+ for line in text. lines ( ) {
198+ if let Some ( value) = line. strip_prefix ( "IPHONEOS_DEPLOYMENT_TARGET=" ) {
199+ let value = value. trim ( ) ;
200+ if !value. is_empty ( ) {
201+ return Some ( value. to_string ( ) ) ;
202+ }
203+ }
204+ }
205+ None
206+ }
0 commit comments