Skip to content

Commit cf8b90d

Browse files
committed
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.
1 parent 8e785ec commit cf8b90d

3 files changed

Lines changed: 50 additions & 74 deletions

File tree

‎Cargo.lock‎

Lines changed: 34 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/cpython-sys/Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ edition = "2024"
77

88
[build-dependencies]
99
bindgen = "0.72.1"
10+
prettyplease = "0.2.37"
1011
shlex = "1.3"
12+
syn = { version = "2.0.110", features = ["full", "parsing"] }

‎Modules/cpython-sys/build.rs‎

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -324,52 +324,24 @@ fn patch_windows_imported_pointer_globals(bindings: String, dll_name: &str) -> S
324324
// The fix: annotate pointer-valued extern statics with `raw-dylib` on
325325
// Windows so Rust generates the import thunk itself and handles the IAT
326326
// indirection correctly — two loads, matching `__declspec(dllimport)`.
327-
let lines: Vec<_> = bindings.lines().collect();
328-
let mut patched = String::with_capacity(bindings.len());
329-
let mut index = 0;
330-
331-
while index < lines.len() {
332-
if lines[index] == "unsafe extern \"C\" {"
333-
&& lines
334-
.get(index + 1)
335-
.and_then(|l| parse_pointer_static_decl(l))
336-
.is_some()
337-
&& lines.get(index + 2).is_some_and(|l| l.trim() == "}")
338-
{
339-
patched.push_str(&format!(
340-
"#[cfg_attr(windows, link(name = \"{dll_name}\", kind = \"raw-dylib\"))]\n"
341-
));
342-
// Keep the original extern block unchanged.
343-
for i in index..index + 3 {
344-
patched.push_str(lines[i]);
345-
patched.push('\n');
346-
}
347-
index += 3;
327+
let mut file = syn::parse_file(&bindings).expect("bindgen emitted invalid Rust");
328+
329+
for item in &mut file.items {
330+
let syn::Item::ForeignMod(foreign_mod) = item else {
331+
continue;
332+
};
333+
let [syn::ForeignItem::Static(static_item)] = foreign_mod.items.as_slice() else {
334+
continue;
335+
};
336+
if !matches!(*static_item.ty, syn::Type::Ptr(_)) {
348337
continue;
349338
}
350-
351-
patched.push_str(lines[index]);
352-
patched.push('\n');
353-
index += 1;
354-
}
355-
356-
patched
357-
}
358-
359-
fn parse_pointer_static_decl(line: &str) -> Option<(&str, bool, &str)> {
360-
let mut decl = line.trim().strip_prefix("pub static ")?;
361-
let is_mut = decl.starts_with("mut ");
362-
if is_mut {
363-
decl = decl.strip_prefix("mut ")?;
364-
}
365-
366-
let (name, ty) = decl.split_once(':')?;
367-
let ty = ty.trim().strip_suffix(';')?;
368-
if !ty.starts_with('*') {
369-
return None;
339+
foreign_mod.attrs.push(syn::parse_quote!(
340+
#[cfg_attr(windows, link(name = #dll_name, kind = "raw-dylib"))]
341+
));
370342
}
371343

372-
Some((name.trim(), is_mut, ty))
344+
prettyplease::unparse(&file)
373345
}
374346

375347
fn add_target_clang_args(

0 commit comments

Comments
 (0)