Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regenerate windows sys bindings #1132

Merged
merged 7 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev-tools/gen-windows-sys-binding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ publish = false
[dependencies]
windows-bindgen = "0.58"
tempfile = "3"
regex = "1"
45 changes: 35 additions & 10 deletions dev-tools/gen-windows-sys-binding/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

use std::{
fs,
io::{self, Read, Write},
io::{BufWriter, Write as _},
};

use regex::Regex;

/// This is printed to the file before the rest of the contents.
const PRELUDE: &str = r#"// This file is autogenerated.
//
Expand All @@ -17,7 +19,7 @@ const PRELUDE: &str = r#"// This file is autogenerated.
// ```
"#;

fn main() -> io::Result<()> {
fn main() {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let temp_file = tempfile::Builder::new()
.suffix(".rs")
Expand Down Expand Up @@ -49,16 +51,39 @@ fn main() -> io::Result<()> {
// Generate bindings.
windows_bindgen::bindgen(&args).expect("running bindgen failed");

let mut bindings = String::new();
fs::File::open(temp_file.path())
.expect("failed to open temp windows_sys.rs")
.read_to_string(&mut bindings)
.expect("failed to read temp windows_sys.rs");
let bindings = fs::read_to_string(temp_file.path())
.expect("failed to read temp windows_sys.rs")
.replace(
"windows_targets::link!",
"super::windows_targets::link_macro!",
);
NobodyXu marked this conversation as resolved.
Show resolved Hide resolved

let mut f = fs::File::create(format!("{manifest_dir}/../../src/windows/windows_sys.rs"))
.map(BufWriter::new)
.expect("failed to create windows_sys.rs");
f.write_all(PRELUDE.as_bytes())?;
f.write_all(bindings.as_bytes())?;

Ok(())
write!(&mut f, "{PRELUDE}\n{bindings}\n").unwrap();

let mut dll_names: Vec<&str> = Regex::new(r#"link_macro!\("(.*)\.dll""#)
.unwrap()
.captures_iter(&bindings)
.map(|caps| caps.extract().1)
.map(|[dll_name]| dll_name)
.filter(|dll_name| *dll_name != "kernel32")
.collect();

if !dll_names.is_empty() {
dll_names.sort_unstable();
dll_names.dedup();

for dll_name in dll_names {
write!(&mut f, r#"#[link(name = "{dll_name}")]"#).unwrap();
f.write_all("\n".as_bytes()).unwrap();
}
Comment on lines +74 to +77
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the import library is often named the same as the DLL, this isn't always true (e.g. there's a Synchronization.lib but not synchronization.dll). This doesn't affect cc-rs at the moment but it could do in the future. I wonder if it would be better to maintain a manual mapping, even if this is more verbose?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't always true (e.g. there's a Synchronization.lib but not synchronization.dll)

How many of these cases are there?

If there're only a few, then I could hard code it here, so that people don't have to modify the macro_rules!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows API is sprawling so it's hard to answer definitely but it seems like there are a lot. Complicating matters further are functions like GetFileVersionInfoExA which is exported from version.dll and there is version.lib but the lib doesn't contain that function. I don't know how many of them there are but cases like that would need to be handled specially by matching on the function name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively we could bump the msrv up to 1.65 and just use raw-dylib. That would completely side-step any issues.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively we could bump the msrv up to 1.65 and just use raw-dylib. That would completely side-step any issues.

I'm ok with raw-dynlib, but the problem is that we can't enable it just cc-rs, it's not a feature but a cfg.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows API is sprawling so it's hard to answer definitely but it seems like there are a lot.

Hmmm, I think it's fine for now, it's not like we add new windows sys binding very often.

Thank you so much, and I will merge it as now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with raw-dynlib, but the problem is that we can't enable it just cc-rs, it's not a feature but a cfg.

It's not a cfg if we implement in a macro ourselves. Then it just a #[link ... kind="raw-dylib"].

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm I didn't know we could do that, would open another PR.


f.write_all(r#"extern "C" {}"#.as_bytes()).unwrap();
f.write_all("\n".as_bytes()).unwrap();
}

f.into_inner().unwrap().sync_all().unwrap();
}
2 changes: 2 additions & 0 deletions src/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub mod find_tools;

#[cfg(windows)]
pub(crate) mod windows_sys;
#[cfg(windows)]
mod windows_targets;

#[cfg(windows)]
mod registry;
Expand Down
130 changes: 23 additions & 107 deletions src/windows/windows_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
// cd generate-windows-sys/
// cargo run
// ```
// Bindings generated by `windows-bindgen` 0.57.0

// Bindings generated by `windows-bindgen` 0.58.0

#![allow(
non_snake_case,
Expand All @@ -15,112 +16,22 @@
dead_code,
clippy::all
)]
#[link(name = "advapi32")]
extern "system" {
pub fn RegCloseKey(hkey: HKEY) -> WIN32_ERROR;
}
#[link(name = "advapi32")]
extern "system" {
pub fn RegEnumKeyExW(
hkey: HKEY,
dwindex: u32,
lpname: PWSTR,
lpcchname: *mut u32,
lpreserved: *const u32,
lpclass: PWSTR,
lpcchclass: *mut u32,
lpftlastwritetime: *mut FILETIME,
) -> WIN32_ERROR;
}
#[link(name = "advapi32")]
extern "system" {
pub fn RegOpenKeyExW(
hkey: HKEY,
lpsubkey: PCWSTR,
uloptions: u32,
samdesired: REG_SAM_FLAGS,
phkresult: *mut HKEY,
) -> WIN32_ERROR;
}
#[link(name = "advapi32")]
extern "system" {
pub fn RegQueryValueExW(
hkey: HKEY,
lpvaluename: PCWSTR,
lpreserved: *const u32,
lptype: *mut REG_VALUE_TYPE,
lpdata: *mut u8,
lpcbdata: *mut u32,
) -> WIN32_ERROR;
}
#[link(name = "kernel32")]
extern "system" {
pub fn FreeLibrary(hlibmodule: HMODULE) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn GetMachineTypeAttributes(
machine: u16,
machinetypeattributes: *mut MACHINE_ATTRIBUTES,
) -> HRESULT;
}
#[link(name = "kernel32")]
extern "system" {
pub fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC;
}
#[link(name = "kernel32")]
extern "system" {
pub fn LoadLibraryA(lplibfilename: PCSTR) -> HMODULE;
}
#[link(name = "kernel32")]
extern "system" {
pub fn OpenSemaphoreA(dwdesiredaccess: u32, binherithandle: BOOL, lpname: PCSTR) -> HANDLE;
}
#[link(name = "kernel32")]
extern "system" {
pub fn PeekNamedPipe(
hnamedpipe: HANDLE,
lpbuffer: *mut core::ffi::c_void,
nbuffersize: u32,
lpbytesread: *mut u32,
lptotalbytesavail: *mut u32,
lpbytesleftthismessage: *mut u32,
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn ReleaseSemaphore(
hsemaphore: HANDLE,
lreleasecount: i32,
lppreviouscount: *mut i32,
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT;
}
#[link(name = "ole32")]
extern "system" {
pub fn CoCreateInstance(
rclsid: *const GUID,
punkouter: *mut core::ffi::c_void,
dwclscontext: CLSCTX,
riid: *const GUID,
ppv: *mut *mut core::ffi::c_void,
) -> HRESULT;
}
#[link(name = "ole32")]
extern "system" {
pub fn CoInitializeEx(pvreserved: *const core::ffi::c_void, dwcoinit: u32) -> HRESULT;
}
#[link(name = "oleaut32")]
extern "system" {
pub fn SysFreeString(bstrstring: BSTR);
}
#[link(name = "oleaut32")]
extern "system" {
pub fn SysStringLen(pbstr: BSTR) -> u32;
}
super::windows_targets::link_macro!("advapi32.dll" "system" fn RegCloseKey(hkey : HKEY) -> WIN32_ERROR);
super::windows_targets::link_macro!("advapi32.dll" "system" fn RegEnumKeyExW(hkey : HKEY, dwindex : u32, lpname : PWSTR, lpcchname : *mut u32, lpreserved : *const u32, lpclass : PWSTR, lpcchclass : *mut u32, lpftlastwritetime : *mut FILETIME) -> WIN32_ERROR);
super::windows_targets::link_macro!("advapi32.dll" "system" fn RegOpenKeyExW(hkey : HKEY, lpsubkey : PCWSTR, uloptions : u32, samdesired : REG_SAM_FLAGS, phkresult : *mut HKEY) -> WIN32_ERROR);
super::windows_targets::link_macro!("advapi32.dll" "system" fn RegQueryValueExW(hkey : HKEY, lpvaluename : PCWSTR, lpreserved : *const u32, lptype : *mut REG_VALUE_TYPE, lpdata : *mut u8, lpcbdata : *mut u32) -> WIN32_ERROR);
super::windows_targets::link_macro!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL);
super::windows_targets::link_macro!("kernel32.dll" "system" fn GetMachineTypeAttributes(machine : u16, machinetypeattributes : *mut MACHINE_ATTRIBUTES) -> HRESULT);
super::windows_targets::link_macro!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
super::windows_targets::link_macro!("kernel32.dll" "system" fn LoadLibraryA(lplibfilename : PCSTR) -> HMODULE);
super::windows_targets::link_macro!("kernel32.dll" "system" fn OpenSemaphoreA(dwdesiredaccess : u32, binherithandle : BOOL, lpname : PCSTR) -> HANDLE);
super::windows_targets::link_macro!("kernel32.dll" "system" fn PeekNamedPipe(hnamedpipe : HANDLE, lpbuffer : *mut core::ffi::c_void, nbuffersize : u32, lpbytesread : *mut u32, lptotalbytesavail : *mut u32, lpbytesleftthismessage : *mut u32) -> BOOL);
super::windows_targets::link_macro!("kernel32.dll" "system" fn ReleaseSemaphore(hsemaphore : HANDLE, lreleasecount : i32, lppreviouscount : *mut i32) -> BOOL);
super::windows_targets::link_macro!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
super::windows_targets::link_macro!("ole32.dll" "system" fn CoCreateInstance(rclsid : *const GUID, punkouter : * mut core::ffi::c_void, dwclscontext : CLSCTX, riid : *const GUID, ppv : *mut *mut core::ffi::c_void) -> HRESULT);
super::windows_targets::link_macro!("ole32.dll" "system" fn CoInitializeEx(pvreserved : *const core::ffi::c_void, dwcoinit : u32) -> HRESULT);
super::windows_targets::link_macro!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR));
super::windows_targets::link_macro!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32);
pub type ADVANCED_FEATURE_FLAGS = u16;
pub type BOOL = i32;
pub type BSTR = *const u16;
Expand Down Expand Up @@ -203,3 +114,8 @@ pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32;
pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32;
pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32;
pub type WIN32_ERROR = u32;

#[link(name = "advapi32")]
#[link(name = "ole32")]
#[link(name = "oleaut32")]
extern "C" {}
19 changes: 19 additions & 0 deletions src/windows/windows_targets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Provides the `link!` macro used by the generated windows bindings.
//!
//! This is a simple wrapper around an `extern` block with a `#[link]` attribute.
//! It's very roughly equivalent to the windows-targets crate.

macro_rules! link_macro {
($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => (
// Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't
// have in this repo. So instead we always link kernel32.lib and add the rest of the import
// libraries below by using an empty extern block. This works because extern blocks are not
// connected to the library given in the #[link] attribute.
#[link(name = "kernel32")]
extern $abi {
$(#[link_name=$link_name])?
pub fn $($function)*;
}
Comment on lines +7 to +16
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example:

Suggested change
($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => (
// Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't
// have in this repo. So instead we always link kernel32.lib and add the rest of the import
// libraries below by using an empty extern block. This works because extern blocks are not
// connected to the library given in the #[link] attribute.
#[link(name = "kernel32")]
extern $abi {
$(#[link_name=$link_name])?
pub fn $($function)*;
}
("advapi32.dll" $($tt:tt)*) => (link_macro!{internal "AdvAPI32" $($tt)*});
("kernel32.dll" $($tt:tt)*) => (link_macro!{internal "kernel32" $($tt)*});
("ole32.dll" $($tt:tt)*) => (link_macro!{internal "Ole32" $($tt)*});
("oleaut32.dll" $($tt:tt)*) => (link_macro!{internal "OleAut32" $($tt)*});
($dll:literal $($tt:tt)*) => (compile_error!("unknown dll"));
(internal $library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => (
#[link(name = $library)]
extern $abi {
$(#[link_name=$link_name])?
pub fn $($function)*;
}
)

)
}
pub(crate) use link_macro;