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>
This commit is contained in:
2026-06-24 14:12:43 +00:00
parent 19ca676549
commit af05f10145
38 changed files with 13208 additions and 5 deletions
@@ -0,0 +1,175 @@
// Copyright (c) Microsoft Corporation
// License: MIT OR Apache-2.0
//! Parsing and serializing metadata about WDK projects
//!
//! This module provides a [`Wdk`] struct that represents the cargo metadata
//! specified in the `metadata.wdk` section any `Cargo.toml`. This corresponds
//! with the settings in the `Driver Settings` property pages for WDK projects
//! in Visual Studio. This module also also provides [`serde`]-compatible
//! serialization and deserialization for the metadata.
pub use error::{Error, Result};
pub use map::Map;
pub use ser::{Serializer, to_map, to_map_with_prefix};
pub(crate) mod ser;
mod error;
mod map;
use std::collections::HashSet;
use camino::Utf8PathBuf;
use cargo_metadata::Metadata;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::DriverConfig;
/// Metadata specified in the `metadata.wdk` section of the `Cargo.toml`
/// of a crate that depends on the WDK, or in a cargo workspace.
///
/// This corresponds with the settings in the `Driver Settings` property pages
/// for WDK projects in Visual Studio
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(
deny_unknown_fields,
rename_all(serialize = "SCREAMING_SNAKE_CASE", deserialize = "kebab-case")
)]
pub struct Wdk {
/// Metadata corresponding to the `Driver Model` property page in the WDK
pub driver_model: DriverConfig,
}
/// Errors that could result from trying to construct a
/// [`metadata::Wdk`](crate::metadata::Wdk) from information parsed by `cargo
/// metadata`
#[derive(Debug, Error)]
pub enum TryFromCargoMetadataError {
/// Error returned when no WDK configuration metadata is detected in the
/// dependency graph
#[error(
"no WDK configuration metadata is detected in the dependency graph. This could happen \
when building WDR itself, or building library crates that depend on the WDK but defer \
WDK configuration to their consumers"
)]
NoWdkConfigurationsDetected,
/// Error returned when multiple configurations of the WDK are detected
/// across the dependency graph
#[error(
"multiple configurations of the WDK are detected across the dependency graph, but only \
one configuration is allowed: {wdk_metadata_configurations:#?}"
)]
MultipleWdkConfigurationsDetected {
/// [`HashSet`] of unique [`metadata::Wdk`](crate::metadata::Wdk)
/// derived from detected WDK metadata
wdk_metadata_configurations: HashSet<Wdk>,
},
/// Error returned when [`crate::metadata::Wdk`] fails to be deserialized
/// from [`cargo_metadata::Metadata`] output
#[error("failed to deserialize metadata::Wdk from {metadata_source}")]
WdkMetadataDeserialization {
/// `String` that describes what part of
/// `cargo_metadata::Metadata` was used as the source for
/// deserialization
metadata_source: String,
/// [`serde_json::Error`] that caused the deserialization to fail
#[source]
error_source: serde_json::Error,
},
}
impl TryFrom<&Metadata> for Wdk {
type Error = TryFromCargoMetadataError;
fn try_from(metadata: &Metadata) -> std::result::Result<Self, Self::Error> {
let wdk_metadata_configurations = {
// Parse WDK metadata from workspace and all packages
let mut configs = parse_packages_wdk_metadata(&metadata.packages)?;
if let Some(workspace_metadata) =
parse_workspace_wdk_metadata(&metadata.workspace_metadata)?
{
configs.insert(workspace_metadata);
}
configs
};
// Ensure that only one configuration of WDK is allowed per dependency graph
match wdk_metadata_configurations.len() {
1 => Ok(wdk_metadata_configurations.into_iter().next().expect(
"wdk_metadata_configurations should have exactly one element because of the \
.len() check above",
)),
0 => Err(TryFromCargoMetadataError::NoWdkConfigurationsDetected),
_ => Err(
TryFromCargoMetadataError::MultipleWdkConfigurationsDetected {
wdk_metadata_configurations,
},
),
}
}
}
fn parse_packages_wdk_metadata(
packages: &[cargo_metadata::Package],
) -> std::result::Result<HashSet<Wdk>, TryFromCargoMetadataError> {
let wdk_metadata_configurations = packages
.iter()
.filter_map(|package| match &package.metadata["wdk"] {
serde_json::Value::Null => None,
// When wdk section is empty, treat it as if it wasn't there. This is to allow for using
// empty wdk metadata sections to mark the package as a driver (ex. for detection in
// `package_driver_flow_condition_script`)
serde_json::Value::Object(map) if map.is_empty() => None,
wdk_metadata => Some(Wdk::deserialize(wdk_metadata).map_err(|err| {
TryFromCargoMetadataError::WdkMetadataDeserialization {
metadata_source: format!(
"{} for {} package",
stringify!(package.metadata["wdk"]),
package.name
),
error_source: err,
}
})),
})
.collect::<std::result::Result<HashSet<_>, _>>()?;
Ok(wdk_metadata_configurations)
}
fn parse_workspace_wdk_metadata(
workspace_metadata: &serde_json::Value,
) -> std::result::Result<Option<Wdk>, TryFromCargoMetadataError> {
Ok(match &workspace_metadata["wdk"] {
serde_json::Value::Null => None,
wdk_metadata => Some(Wdk::deserialize(wdk_metadata).map_err(|err| {
TryFromCargoMetadataError::WdkMetadataDeserialization {
metadata_source: stringify!(workspace_metadata["wdk"]).to_string(),
error_source: err,
}
})?),
})
}
pub(crate) fn iter_manifest_paths(metadata: Metadata) -> impl IntoIterator<Item = Utf8PathBuf> {
let mut cargo_manifest_paths = HashSet::new();
// Add all package manifest paths
for package in metadata.packages {
cargo_manifest_paths.insert(package.manifest_path);
}
// Add workspace manifest path
let workspace_manifest_path: Utf8PathBuf = {
let mut path = metadata.workspace_root;
path.push("Cargo.toml");
path
};
cargo_manifest_paths.insert(workspace_manifest_path);
cargo_manifest_paths
}