Files
punktfunk/packaging/windows/drivers/vendor/wdk-build/src/bindgen.rs
T
enricobuehler 9fd19b90a9
windows-drivers / probe-and-proto (push) Successful in 24s
apple / swift (push) Successful in 1m8s
windows-drivers / driver-build (push) Failing after 43s
ci / rust (push) Successful in 1m31s
ci / web (push) Successful in 1m5s
ci / docs-site (push) Successful in 52s
apple / screenshots (push) Failing after 2m35s
windows-host / package (push) Successful in 5m23s
ci / bench (push) Successful in 4m48s
android / android (push) Successful in 10m1s
decky / build-publish (push) Successful in 26s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
deb / build-publish (push) Successful in 3m29s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m21s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m23s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m18s
docker / deploy-docs (push) Successful in 21s
feat(windows-drivers): vendor wdk 0.5.1 + add ApiSubset::Iddcx (M1 spike)
Vendor the published, self-contained windows-drivers-rs 0.5.1 crates
(wdk-build, wdk-sys) under vendor/ and add a first-class ApiSubset::Iddcx that
bindgens iddcx/1.10/IddCx.h in an extra pass reusing bindgen::Builder::wdk_default
(allowlist_file (?i).*iddcx.* — emits only IddCx items; WDF/DXGI types resolve to
the shared base/wdf bindings, type-identity by construction). Mirrors the existing
gpio/hid/spb subsets exactly: wdk-build gets the enum variant + iddcx_headers()
(UMDF-only), wdk-sys gets generate_iddcx + the iddcx feature + pub mod iddcx.
[patch.crates-io] redirects all wdk-sys/wdk-build (incl. wdk 0.4.1 transitive) to
the patched copies. wdk-probe enables the iddcx feature.

MAKE-OR-BREAK: does IddCx.h bindgen in wdk-sys config without a header conflict
(issue #515) + does the generated module compile (type-identity)? CI answers it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:12:43 +00:00

274 lines
12 KiB
Rust

// Copyright (c) Microsoft Corporation
// License: MIT OR Apache-2.0
use std::{borrow::Borrow, fmt};
use bindgen::{
Builder,
callbacks::{ItemInfo, ItemKind, ParseCallbacks},
};
use cargo_metadata::MetadataCommand;
use tracing::debug;
use crate::{Config, ConfigError, DriverConfig, find_top_level_cargo_manifest};
/// An extension trait that provides a way to create a [`bindgen::Builder`]
/// configured for generating bindings to the wdk
pub trait BuilderExt {
/// Returns a `bindgen::Builder` with the default configuration for
/// generation of bindings to the WDK
///
/// # Errors
///
/// Implementation may return `wdk_build::ConfigError` if it fails to create
/// a builder
fn wdk_default(config: impl Borrow<Config> + fmt::Debug) -> Result<Builder, ConfigError>;
}
#[derive(Debug)]
struct WdkCallbacks {
wdf_function_table_symbol_name: Option<String>,
}
struct BindgenRustEditionWrapper(bindgen::RustEdition);
impl TryFrom<cargo_metadata::Edition> for BindgenRustEditionWrapper {
type Error = ConfigError;
fn try_from(edition: cargo_metadata::Edition) -> Result<Self, Self::Error> {
match edition {
cargo_metadata::Edition::E2015 => Err(ConfigError::UnsupportedRustEdition {
edition: "2015".to_string(),
}),
cargo_metadata::Edition::E2018 => Ok(Self(bindgen::RustEdition::Edition2018)),
cargo_metadata::Edition::E2021 => Ok(Self(bindgen::RustEdition::Edition2021)),
cargo_metadata::Edition::E2024 => Ok(Self(bindgen::RustEdition::Edition2024)),
cargo_metadata::Edition::_E2027 => Err(ConfigError::UnsupportedRustEdition {
edition: "2027".to_string(),
}),
cargo_metadata::Edition::_E2030 => Err(ConfigError::UnsupportedRustEdition {
edition: "2030".to_string(),
}),
_ => Err(ConfigError::UnsupportedRustEdition {
edition: "unknown".to_string(),
}),
}
}
}
impl BuilderExt for Builder {
/// Returns a `bindgen::Builder` with the default configuration for
/// generation of bindings to the WDK
///
/// # Errors
///
/// Will return `wdk_build::ConfigError` if any of the resolved include or
/// library paths do not exist
#[tracing::instrument(level = "debug")]
fn wdk_default(config: impl Borrow<Config> + fmt::Debug) -> Result<Self, ConfigError> {
let config = config.borrow();
let mut builder = Self::default()
.use_core() // Can't use std for kernel code
.derive_default(true) // allows for default initializing structs
// CStr types are safer and easier to work with when interacting with string constants
// from C
.generate_cstr(true)
// Building in eWDK can pollute system search path when clang-sys tries to detect
// c_search_paths
.detect_include_paths(false)
.clang_args(config.include_paths()?.map(|include_path| {
format!(
"--include-directory={}",
include_path
.to_str()
.expect("Non Unicode paths are not supported")
)
}))
.clang_args(
config
.preprocessor_definitions()
.map(|(key, value)| {
format!(
"--define-macro={key}{}",
value.map(|v| format!("={v}")).unwrap_or_default()
)
})
.chain(Config::wdk_bindgen_compiler_flags()),
)
.blocklist_item("ExAllocatePoolWithTag") // Deprecated
.blocklist_item("ExAllocatePoolWithQuotaTag") // Deprecated
.blocklist_item("ExAllocatePoolWithTagPriority") // Deprecated
.blocklist_item("ExAllocatePool") // Deprecated
.blocklist_item("USBD_CalculateUsbBandwidth") // Deprecated
.blocklist_item("USBD_CreateConfigurationRequest") // Deprecated
.blocklist_item("USBD_Debug_LogEntry") // Deprecated
.blocklist_item("USBD_GetUSBDIVersion") // Deprecated
.blocklist_item("USBD_ParseConfigurationDescriptor") // Deprecated
.blocklist_item("USBD_QueryBusTime") // Deprecated
.blocklist_item("USBD_RegisterHcFilter") // Deprecated
.blocklist_item("IOCTL_USB_DIAG_IGNORE_HUBS_OFF") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_DIAG_IGNORE_HUBS_ON") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_DIAGNOSTIC_MODE_OFF") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_DIAGNOSTIC_MODE_ON") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_GET_HUB_CAPABILITIES") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_HCD_DISABLE_PORT") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_HCD_ENABLE_PORT") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_HCD_GET_STATS_1") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_HCD_GET_STATS_2") // Deprecated/Internal-Use-Only
.blocklist_item("IOCTL_USB_RESET_HUB") // Deprecated/Internal-Use-Only
.opaque_type("_KGDTENTRY64") // No definition in WDK
.opaque_type("_KIDTENTRY64") // No definition in WDK
// FIXME: bitfield generated with non-1byte alignment in _MCG_CAP
.blocklist_item(".*MCG_CAP(?:__bindgen.*)?")
.blocklist_item(".*WHEA_XPF_MCA_SECTION")
.blocklist_item(".*WHEA_ARM_BUS_ERROR(?:__bindgen.*)?")
.blocklist_item(".*WHEA_ARM_PROCESSOR_ERROR")
.blocklist_item(".*WHEA_ARM_CACHE_ERROR")
// FIXME: bindgen unable to generate for anonymous structs
// https://github.com/rust-lang/rust-bindgen/issues/3177
.blocklist_item(".*ADDRESS0_OWNERSHIP_ACQUIRE")
.blocklist_item(".*USBDEVICE_ABORTIO")
.blocklist_item(".*USBDEVICE_STARTIO")
.blocklist_item(".*USBDEVICE_TREE_PURGEIO")
// FIXME: arrays with more than 32 entries currently fail to generate a `Default`` impl: https://github.com/rust-lang/rust-bindgen/issues/2803
.no_default(".*tagMONITORINFOEXA")
.must_use_type("NTSTATUS")
.must_use_type("HRESULT")
// Defaults enums to generate as a set of constants contained in a module (default value
// is EnumVariation::Consts which generates enums as global constants)
.default_enum_style(bindgen::EnumVariation::ModuleConsts)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.parse_callbacks(Box::new(WdkCallbacks::new(config)))
.formatter(bindgen::Formatter::Prettyplease)
.rust_target(get_rust_target()?)
.rust_edition(get_rust_edition()?);
// The `_USBPM_CLIENT_CONFIG_EXTRA_INFO` struct only has members when
// _KERNEL_MODE flag is defined. We need to mark this type as opaque to avoid
// generating an empty struct, since they are not currently supported by
// bindgen: https://github.com/rust-lang/rust-bindgen/issues/1683
if let DriverConfig::Umdf(_) = config.driver_config {
builder = builder.opaque_type("_USBPM_CLIENT_CONFIG_EXTRA_INFO");
}
Ok(builder)
}
}
impl ParseCallbacks for WdkCallbacks {
fn generated_name_override(&self, item_info: ItemInfo) -> Option<String> {
// Override the generated name for the WDF function table symbol, since bindgen is unable to currently translate the #define automatically: https://github.com/rust-lang/rust-bindgen/issues/2544
if let Some(wdf_function_table_symbol_name) = &self.wdf_function_table_symbol_name {
if let ItemInfo {
name: item_name,
kind: ItemKind::Var,
..
} = item_info
{
if item_name == wdf_function_table_symbol_name {
return Some("WdfFunctions".to_string());
}
}
}
None
}
}
impl WdkCallbacks {
#[tracing::instrument(level = "trace")]
fn new(config: &Config) -> Self {
Self {
wdf_function_table_symbol_name: config.compute_wdffunctions_symbol_name(),
}
}
}
// Retrieves the Rust version as a `bindgen::RustTarget` for the current build
// configuration.
//
// If the `nightly` feature is enabled and the current toolchain is `nightly`,
// returns a value allowing `bindgen` to generate code with supported `nightly`
// features. Otherwise, queries the MSRV from the `CARGO_PKG_RUST_VERSION`
// environment variable and uses it to create a `bindgen::RustTarget::stable`
// value.
//
// # Errors
//
// Returns `ConfigError::MsrvNotSupportedByBindgen` if the MSRV is not supported
// by bindgen, or `ConfigError::SemverError` if the MSRV cannot be parsed as a
// semver version.
#[tracing::instrument(level = "trace")]
fn get_rust_target() -> Result<bindgen::RustTarget, ConfigError> {
let nightly_feature = cfg!(feature = "nightly");
let nightly_toolchain = rustversion::cfg!(nightly);
match (nightly_feature, nightly_toolchain) {
(true, true) => Ok(bindgen::RustTarget::nightly()),
(false, false) => get_stable_rust_target(),
(true, false) => {
tracing::warn!(
"A non-nightly toolchain has been detected. Nightly bindgen features are only \
enabled with both nightly feature enablement and nightly toolchain use. "
);
get_stable_rust_target()
}
(false, true) => {
tracing::warn!(
"The nightly feature for wdk-build is disabled. Nightly bindgen features are only \
enabled with both nightly feature enablement and nightly toolchain use. "
);
get_stable_rust_target()
}
}
}
// Retrieves the stable Rust target for the current build configuration.
// Queries the MSRV from the `CARGO_PKG_RUST_VERSION` environment variable and
// uses it to create a `bindgen::RustTarget::stable` value.
#[tracing::instrument(level = "trace")]
fn get_stable_rust_target() -> Result<bindgen::RustTarget, ConfigError> {
let package_msrv = semver::Version::parse(env!("CARGO_PKG_RUST_VERSION"))
.map_err(|e| ConfigError::RustVersionParseError { error_source: e })?;
let bindgen_msrv = bindgen::RustTarget::stable(package_msrv.minor, package_msrv.patch)
.map_err(|e| ConfigError::MsrvNotSupportedByBindgen {
msrv: package_msrv.to_string(),
reason: e.to_string(),
})?;
Ok(bindgen_msrv)
}
// Retrieves the Rust edition from `cargo metadata` and returns the appropriate
// `bindgen::RustEdition` value.
//
// # Errors
//
// Returns `ConfigError::CargoMetadataPackageNotFound` if the `wdk-build`
// package is not found, or `ConfigError::UnsupportedRustEdition` if the edition
// is not supported.
#[tracing::instrument(level = "trace")]
fn get_rust_edition() -> Result<bindgen::RustEdition, ConfigError> {
const WDK_BUILD_PACKAGE_NAME: &str = "wdk-build";
// Run `cargo_metadata` in the same working directory as the top level manifest
// in order to respect `config.toml` overrides
let top_level_cargo_manifest_path = find_top_level_cargo_manifest();
debug!(
"Top level Cargo manifest path: {:?}",
top_level_cargo_manifest_path
);
let cwd = top_level_cargo_manifest_path
.parent()
.expect("Cargo manifest should have a valid parent directory");
let wdk_sys_cargo_metadata = MetadataCommand::new().current_dir(cwd).exec()?;
let wdk_sys_package_metadata = wdk_sys_cargo_metadata
.packages
.iter()
.find(|package| package.name == WDK_BUILD_PACKAGE_NAME)
.ok_or_else(|| ConfigError::WdkBuildPackageNotFoundInCargoMetadata)?;
let rust_edition: BindgenRustEditionWrapper = wdk_sys_package_metadata.edition.try_into()?;
Ok(rust_edition.0)
}