af05f10145
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>
176 lines
6.6 KiB
Rust
176 lines
6.6 KiB
Rust
// 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
|
|
}
|