Skip to content

Commit 2ed828b

Browse files
committed
Use syn to add link attribute for Windows dllimport of globals
1 parent fa8b035 commit 2ed828b

3 files changed

Lines changed: 52 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
@@ -8,4 +8,6 @@ rust-version.workspace = true
88

99
[build-dependencies]
1010
bindgen = "0.72.1"
11+
prettyplease = "0.2.37"
1112
shlex = "1.3"
13+
syn = { version = "2.0.110", features = ["full", "parsing"] }

‎Modules/cpython-sys/build.rs‎

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -324,52 +324,26 @@ 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
340+
.attrs
341+
.push(syn::parse_quote!(
342+
#[cfg_attr(windows, link(name = #dll_name, kind = "raw-dylib"))]
343+
));
370344
}
371345

372-
Some((name.trim(), is_mut, ty))
346+
prettyplease::unparse(&file)
373347
}
374348

375349
fn add_target_clang_args(

0 commit comments

Comments
 (0)