From cf8b90db185c60400e4b9b22dcba4c05da107807 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Sun, 9 Aug 2026 21:33:20 -0700 Subject: [PATCH] Fix extern static parsing for cpython-sys The previous manual parsing for cpython-sys was not robust to different bindgen outputs. Rather than manual parsing, we now use syn and prettyplease to parse the bindgen output. --- Cargo.lock | 66 +++++++++++++++++----------------- Modules/cpython-sys/Cargo.toml | 2 ++ Modules/cpython-sys/build.rs | 56 ++++++++--------------------- 3 files changed, 50 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 132b913b06d5e5..14e6791704c196 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,9 +12,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.10.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "cexpr" @@ -62,9 +62,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ "glob", "libc", @@ -90,20 +90,22 @@ name = "cpython-sys" version = "0.1.0" dependencies = [ "bindgen", + "prettyplease", "shlex", + "syn", ] [[package]] name = "either" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "itertools" @@ -116,9 +118,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libloading" @@ -132,15 +134,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "minimal-lexical" @@ -170,27 +172,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -200,9 +202,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -211,15 +213,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "shlex" @@ -229,9 +231,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -240,9 +242,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "windows-link" diff --git a/Modules/cpython-sys/Cargo.toml b/Modules/cpython-sys/Cargo.toml index a12556c6e305a1..a7039840d1aa1a 100644 --- a/Modules/cpython-sys/Cargo.toml +++ b/Modules/cpython-sys/Cargo.toml @@ -7,4 +7,6 @@ edition = "2024" [build-dependencies] bindgen = "0.72.1" +prettyplease = "0.2.37" shlex = "1.3" +syn = { version = "2.0.110", features = ["full", "parsing"] } diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index 03335128e9fbe5..0918ffd0b83da4 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -324,52 +324,24 @@ fn patch_windows_imported_pointer_globals(bindings: String, dll_name: &str) -> S // The fix: annotate pointer-valued extern statics with `raw-dylib` on // Windows so Rust generates the import thunk itself and handles the IAT // indirection correctly — two loads, matching `__declspec(dllimport)`. - let lines: Vec<_> = bindings.lines().collect(); - let mut patched = String::with_capacity(bindings.len()); - let mut index = 0; - - while index < lines.len() { - if lines[index] == "unsafe extern \"C\" {" - && lines - .get(index + 1) - .and_then(|l| parse_pointer_static_decl(l)) - .is_some() - && lines.get(index + 2).is_some_and(|l| l.trim() == "}") - { - patched.push_str(&format!( - "#[cfg_attr(windows, link(name = \"{dll_name}\", kind = \"raw-dylib\"))]\n" - )); - // Keep the original extern block unchanged. - for i in index..index + 3 { - patched.push_str(lines[i]); - patched.push('\n'); - } - index += 3; + let mut file = syn::parse_file(&bindings).expect("bindgen emitted invalid Rust"); + + for item in &mut file.items { + let syn::Item::ForeignMod(foreign_mod) = item else { + continue; + }; + let [syn::ForeignItem::Static(static_item)] = foreign_mod.items.as_slice() else { + continue; + }; + if !matches!(*static_item.ty, syn::Type::Ptr(_)) { continue; } - - patched.push_str(lines[index]); - patched.push('\n'); - index += 1; - } - - patched -} - -fn parse_pointer_static_decl(line: &str) -> Option<(&str, bool, &str)> { - let mut decl = line.trim().strip_prefix("pub static ")?; - let is_mut = decl.starts_with("mut "); - if is_mut { - decl = decl.strip_prefix("mut ")?; - } - - let (name, ty) = decl.split_once(':')?; - let ty = ty.trim().strip_suffix(';')?; - if !ty.starts_with('*') { - return None; + foreign_mod.attrs.push(syn::parse_quote!( + #[cfg_attr(windows, link(name = #dll_name, kind = "raw-dylib"))] + )); } - Some((name.trim(), is_mut, ty)) + prettyplease::unparse(&file) } fn add_target_clang_args(