feat(windows-drivers): vendor wdk 0.5.1 + add ApiSubset::Iddcx (M1 spike)
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
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
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:
@@ -0,0 +1,136 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [0.5.1](https://github.com/microsoft/windows-drivers-rs/compare/wdk-build-v0.5.0...wdk-build-v0.5.1) - 2025-11-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- fallback to detection logic when WDK env vars have invalid unicode ([#579](https://github.com/microsoft/windows-drivers-rs/pull/579))
|
||||
- path construction in `wdk-build` `get_wdk_tools_root` and `get_wdk_bin_root` ([#574](https://github.com/microsoft/windows-drivers-rs/pull/574))
|
||||
|
||||
### Other
|
||||
|
||||
- fix wdk-build version in readme ([#568](https://github.com/microsoft/windows-drivers-rs/pull/568))
|
||||
|
||||
## [0.5.0](https://github.com/microsoft/windows-drivers-rs/compare/wdk-build-v0.4.0...wdk-build-v0.5.0) - 2025-11-06
|
||||
|
||||
### Added
|
||||
|
||||
- enhance error handling with IoError and IoErrorMetadata for improved std::io::Error diagnostics for fs errors ([#480](https://github.com/microsoft/windows-drivers-rs/pull/480))
|
||||
- add color to cargo wdk and cargo make argument forwarding ([#519](https://github.com/microsoft/windows-drivers-rs/pull/519))
|
||||
- enhance debug tracing in bindgen and config modules ([#455](https://github.com/microsoft/windows-drivers-rs/pull/455))
|
||||
- enhance cargo metadata parsing to respect config.toml ([#451](https://github.com/microsoft/windows-drivers-rs/pull/451))
|
||||
- *(ci)* install and use `nuget` packages in CI workflows ([#406](https://github.com/microsoft/windows-drivers-rs/pull/406))
|
||||
- make `emit_check_cfg_settings` function public ([#352](https://github.com/microsoft/windows-drivers-rs/pull/352))
|
||||
|
||||
### Fixed
|
||||
|
||||
- use latest version of ucx in the WDKContent as default ([#411](https://github.com/microsoft/windows-drivers-rs/pull/411))
|
||||
- improve error reporting when no wdk-build package is found ([#339](https://github.com/microsoft/windows-drivers-rs/pull/339))
|
||||
|
||||
### Other
|
||||
|
||||
- Prepare cargo-wdk for release ([#560](https://github.com/microsoft/windows-drivers-rs/pull/560))
|
||||
- [**breaking**] bump to Rust 2024 Edition ([#430](https://github.com/microsoft/windows-drivers-rs/pull/430))
|
||||
- use `std::path::absolute` instead of canonicalize + strip_extended_path_prefix ([#462](https://github.com/microsoft/windows-drivers-rs/pull/462))
|
||||
- Bump tracing-subscriber from 0.3.19 to 0.3.20 ([#492](https://github.com/microsoft/windows-drivers-rs/pull/492))
|
||||
- enforce typo checking ([#452](https://github.com/microsoft/windows-drivers-rs/pull/452))
|
||||
- update crate references for consistency in documentation ([#440](https://github.com/microsoft/windows-drivers-rs/pull/440))
|
||||
- improve cargo-wdk tests ([#429](https://github.com/microsoft/windows-drivers-rs/pull/429))
|
||||
|
||||
## [0.4.0](https://github.com/microsoft/windows-drivers-rs/compare/wdk-build-v0.3.0...wdk-build-v0.4.0) - 2025-04-18
|
||||
|
||||
### Added
|
||||
|
||||
- extend coverage in `wdk-sys` to include usb-related headers ([#296](https://github.com/microsoft/windows-drivers-rs/pull/296))
|
||||
- expand wdk-sys coverage to include gpio and parallel ports related headers ([#278](https://github.com/microsoft/windows-drivers-rs/pull/278))
|
||||
- add support for Storage API subset in `wdk-sys` ([#287](https://github.com/microsoft/windows-drivers-rs/pull/287))
|
||||
- expand `wdk-sys` coverage to include spb-related headers ([#263](https://github.com/microsoft/windows-drivers-rs/pull/263))
|
||||
- [**breaking**] expand `wdk-sys` coverage to include hid-related headers ([#260](https://github.com/microsoft/windows-drivers-rs/pull/260))
|
||||
|
||||
### Fixed
|
||||
|
||||
- [**breaking**] specify rust version & edition to wdk-default bindgen::builder ([#314](https://github.com/microsoft/windows-drivers-rs/pull/314))
|
||||
- [**breaking**] explicitly mark `_KGDTENTRY64` and `_KIDTENTRY64` as opaque types in `bindgen` ([#277](https://github.com/microsoft/windows-drivers-rs/pull/277))
|
||||
- suppress linker warnings exposed by nightly rustc change ([#279](https://github.com/microsoft/windows-drivers-rs/pull/279))
|
||||
- add missing arm64rt library to linker flags for arm64 kernel-mode builds ([#261](https://github.com/microsoft/windows-drivers-rs/pull/261))
|
||||
|
||||
### Other
|
||||
|
||||
- update README to clarify community engagement and contact methods ([#312](https://github.com/microsoft/windows-drivers-rs/pull/312))
|
||||
- remove noop `must_use` on trait impl ([#302](https://github.com/microsoft/windows-drivers-rs/pull/302))
|
||||
- [**breaking**] Remove lazy static instances ([#250](https://github.com/microsoft/windows-drivers-rs/pull/250))
|
||||
- fix panic condition docs for `package_driver_flow_condition_script` ([#264](https://github.com/microsoft/windows-drivers-rs/pull/264))
|
||||
- port certificate-generation condition script to Rust ([#259](https://github.com/microsoft/windows-drivers-rs/pull/259))
|
||||
- remove redundant code-path in `detect_wdk_content_root` ([#249](https://github.com/microsoft/windows-drivers-rs/pull/249))
|
||||
- use `next_back` instead of `last` on double-ended iterators (`clippy::double_ended_iterator_last`) ([#262](https://github.com/microsoft/windows-drivers-rs/pull/262))
|
||||
- use `is_none_or` for `clippy::nonminimal_bool` and resolve `clippy::needless_raw_string_hashes` ([#231](https://github.com/microsoft/windows-drivers-rs/pull/231))
|
||||
- fix `clippy::nonminimal_bool` and `clippy::ref_option` issues ([#230](https://github.com/microsoft/windows-drivers-rs/pull/230))
|
||||
|
||||
## [0.3.0](https://github.com/microsoft/windows-drivers-rs/compare/wdk-build-v0.2.0...wdk-build-v0.3.0) - 2024-09-27
|
||||
|
||||
### Added
|
||||
|
||||
- add `skip_umdf_static_crt_check` unstable option to prevent static crt linkage check ([#217](https://github.com/microsoft/windows-drivers-rs/pull/217))
|
||||
- [**breaking**] add 'ExAllocatePool' to blocklist due to deprecation ([#190](https://github.com/microsoft/windows-drivers-rs/pull/190))
|
||||
- configure WDK configuration via parsing Cargo manifest metadata ([#186](https://github.com/microsoft/windows-drivers-rs/pull/186))
|
||||
|
||||
### Fixed
|
||||
|
||||
- typos in Getting Started section of README.md ([#213](https://github.com/microsoft/windows-drivers-rs/pull/213))
|
||||
- skip infverif task for sample drivers built with certain GE WDK versions ([#143](https://github.com/microsoft/windows-drivers-rs/pull/143))
|
||||
- [**breaking**] prevent linking of wdk libraries in tests that depend on `wdk-sys` ([#118](https://github.com/microsoft/windows-drivers-rs/pull/118))
|
||||
|
||||
### Other
|
||||
|
||||
- fix `clippy::empty-line-after-doc-comments` lint issues ([#221](https://github.com/microsoft/windows-drivers-rs/pull/221))
|
||||
- move infverif task's condition script logic to cargo_make.rs ([#216](https://github.com/microsoft/windows-drivers-rs/pull/216))
|
||||
- remove unstable `rustfmt` `version` setting (replaced by auto-detected `edition`) ([#220](https://github.com/microsoft/windows-drivers-rs/pull/220))
|
||||
- replace directory substitution plugin with condition_script_runner_args ([#208](https://github.com/microsoft/windows-drivers-rs/pull/208))
|
||||
- use cargo-make's built-in arg expansion instead of custom plugin support in `nested-cargo-workspace-in-cargo-make-emulated-workspace-support` ([#201](https://github.com/microsoft/windows-drivers-rs/pull/201))
|
||||
- Improve doc comments to comply with `too_long_first_doc_paragraph` clippy lint ([#202](https://github.com/microsoft/windows-drivers-rs/pull/202))
|
||||
- Update README.md ([#180](https://github.com/microsoft/windows-drivers-rs/pull/180))
|
||||
- update readme to call out bugged LLVM 18 versions ([#169](https://github.com/microsoft/windows-drivers-rs/pull/169))
|
||||
- Build perf: Make calls to bindgen run in parallel ([#159](https://github.com/microsoft/windows-drivers-rs/pull/159))
|
||||
- add support for rustc-check-cfg ([#150](https://github.com/microsoft/windows-drivers-rs/pull/150))
|
||||
- Bump windows from 0.52.0 to 0.56.0 ([#144](https://github.com/microsoft/windows-drivers-rs/pull/144))
|
||||
- Bump rustversion from 1.0.14 to 1.0.15 ([#145](https://github.com/microsoft/windows-drivers-rs/pull/145))
|
||||
- use a standardized workspace lint table ([#134](https://github.com/microsoft/windows-drivers-rs/pull/134))
|
||||
- Bump clap from 4.4.18 to 4.5.4 ([#130](https://github.com/microsoft/windows-drivers-rs/pull/130))
|
||||
- Bump thiserror from 1.0.56 to 1.0.59 ([#142](https://github.com/microsoft/windows-drivers-rs/pull/142))
|
||||
- fix `winget` llvm install command option ([#115](https://github.com/microsoft/windows-drivers-rs/pull/115))
|
||||
- fix various pipeline breakages (nightly rustfmt bug, new nightly clippy lints, upstream winget dependency issue) ([#117](https://github.com/microsoft/windows-drivers-rs/pull/117))
|
||||
- add lint exceptions for clippy::manual_c_str_literals and clippy::ref_as_ptr ([#108](https://github.com/microsoft/windows-drivers-rs/pull/108))
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [0.2.0](https://github/microsoft/windows-drivers-rs/compare/wdk-build-v0.1.0...wdk-build-v0.2.0) - 2024-02-08
|
||||
|
||||
### Added
|
||||
- package rust-driver-makefile.toml with wdk-build package ([#36](https://github/microsoft/windows-drivers-rs/pull/36))
|
||||
- support multiple drivers (of same type) in same cargo workspace
|
||||
- cargo-make argument forwarding
|
||||
- generate CStr for c string constants instead of &[u8] ([#72](https://github/microsoft/windows-drivers-rs/pull/72))
|
||||
|
||||
### Fixed
|
||||
- resolve warnings in rust-script blocks and only fail warnings in CI ([#87](https://github/microsoft/windows-drivers-rs/pull/87))
|
||||
- add missing cpu-arch macro definitions
|
||||
- fix wdk path regkey detection
|
||||
|
||||
### Other
|
||||
- update versions in readme and rust-driver-makefile.toml
|
||||
- update dependencies
|
||||
- allow multiple_crate_versions in wdk-build (build dependency) ([#98](https://github/microsoft/windows-drivers-rs/pull/98))
|
||||
- update cargo-make tasks with arch-specific tools
|
||||
- Bump thiserror from 1.0.48 to 1.0.55 ([#59](https://github/microsoft/windows-drivers-rs/pull/59))
|
||||
- restrict to one unsafe operation per block ([#24](https://github/microsoft/windows-drivers-rs/pull/24))
|
||||
- [**breaking**] enable rustdoc lints and resolve errors
|
||||
- initial open-source check in
|
||||
+918
@@ -0,0 +1,918 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||
|
||||
[[package]]
|
||||
name = "assert_fs"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a652f6cb1f516886fcfee5e7a5c078b9ade62cfcb889524efe5a64d682dd27a9"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"doc-comment",
|
||||
"globwalk",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.71.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap-cargo"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d546f0e84ff2bfa4da1ce9b54be42285767ba39c688572ca32412a09a73851e5"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globwalk"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"globset",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"same-file",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
"predicates-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wdk-build"
|
||||
version = "0.5.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_fs",
|
||||
"bindgen",
|
||||
"camino",
|
||||
"cargo_metadata",
|
||||
"cfg-if",
|
||||
"clap",
|
||||
"clap-cargo",
|
||||
"paste",
|
||||
"regex",
|
||||
"rustversion",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -0,0 +1,142 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2024"
|
||||
rust-version = "1.85.0"
|
||||
name = "wdk-build"
|
||||
version = "0.5.1"
|
||||
build = "build.rs"
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "A library to configure a Cargo build script for binding generation and downstream linking of the WDK (Windows Driver Kit)"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"wdk",
|
||||
"windows",
|
||||
"build-dependencies",
|
||||
]
|
||||
categories = [
|
||||
"development-tools::build-utils",
|
||||
"development-tools::ffi",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/microsoft/windows-drivers-rs"
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["regex"]
|
||||
|
||||
[features]
|
||||
nightly = []
|
||||
|
||||
[lib]
|
||||
name = "wdk_build"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.anyhow]
|
||||
version = "1.0.97"
|
||||
|
||||
[dependencies.bindgen]
|
||||
version = "0.71.0"
|
||||
|
||||
[dependencies.camino]
|
||||
version = "1.1.9"
|
||||
|
||||
[dependencies.cargo_metadata]
|
||||
version = "0.19.2"
|
||||
|
||||
[dependencies.cfg-if]
|
||||
version = "1.0.3"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "4.5.40"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.clap-cargo]
|
||||
version = "0.15.2"
|
||||
|
||||
[dependencies.paste]
|
||||
version = "1.0.15"
|
||||
|
||||
[dependencies.regex]
|
||||
version = "1.11.1"
|
||||
features = ["unicode-case"]
|
||||
|
||||
[dependencies.rustversion]
|
||||
version = "1.0.20"
|
||||
|
||||
[dependencies.semver]
|
||||
version = "1.0.26"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.serde_json]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "2.0.12"
|
||||
|
||||
[dependencies.tracing]
|
||||
version = "0.1.40"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.58.0"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Registry",
|
||||
]
|
||||
|
||||
[dev-dependencies.assert_fs]
|
||||
version = "1.1.3"
|
||||
|
||||
[dev-dependencies.windows]
|
||||
version = "0.58.0"
|
||||
features = ["Win32_UI_Shell"]
|
||||
|
||||
[build-dependencies.rustversion]
|
||||
version = "1.0.20"
|
||||
|
||||
[lints.clippy]
|
||||
all = "deny"
|
||||
cargo = "warn"
|
||||
multiple_unsafe_ops_per_block = "deny"
|
||||
nursery = "warn"
|
||||
pedantic = "warn"
|
||||
undocumented_unsafe_blocks = "deny"
|
||||
unnecessary_safety_doc = "deny"
|
||||
|
||||
[lints.rust]
|
||||
missing_docs = "warn"
|
||||
unsafe_op_in_unsafe_fn = "forbid"
|
||||
|
||||
[lints.rust.unexpected_cfgs]
|
||||
level = "warn"
|
||||
priority = 0
|
||||
check-cfg = [
|
||||
"cfg(wdk_build_unstable)",
|
||||
"cfg(skip_umdf_static_crt_check)",
|
||||
]
|
||||
|
||||
[lints.rustdoc]
|
||||
bare_urls = "warn"
|
||||
broken_intra_doc_links = "warn"
|
||||
invalid_codeblock_attributes = "warn"
|
||||
invalid_html_tags = "warn"
|
||||
invalid_rust_codeblocks = "warn"
|
||||
missing_crate_level_docs = "warn"
|
||||
private_intra_doc_links = "warn"
|
||||
redundant_explicit_links = "warn"
|
||||
unescaped_backticks = "warn"
|
||||
@@ -0,0 +1,237 @@
|
||||
# windows-drivers-rs
|
||||
|
||||
This repo is a collection of Rust crates that enable developers to develop Windows Drivers in Rust. It is the intention to support both WDM and WDF driver development models. This repo contains the following crates:
|
||||
|
||||
* [wdk-build](./crates/wdk-build): A library to configure a Cargo build script for binding generation and downstream linking of the WDK (Windows Driver Kit). While this crate is written to be flexible with different WDK releases and different WDF version, it is currently only tested for NI eWDK, KMDF 1.33, UMDF 2.33, and WDM Drivers. There may be missing linker options for older DDKs.
|
||||
* [wdk-sys](./crates/wdk-sys): Direct FFI bindings to APIs available in the Windows Development Kit (WDK). This includes both autogenerated ffi bindings from `bindgen`, and also manual re-implementations of macros that bindgen fails to generate.
|
||||
* [wdk](./crates/wdk): Safe idiomatic bindings to APIs available in the Windows Development Kit (WDK)
|
||||
* [wdk-panic](./crates/wdk-panic/): Default panic handler implementations for programs built with WDK
|
||||
* [wdk-alloc](./crates/wdk-alloc): alloc support for binaries compiled with the Windows Development Kit (WDK)
|
||||
* [wdk-macros](./crates/wdk-macros): A collection of macros that help make it easier to interact with wdk-sys's direct bindings. This crate is re-exported via `wdk-sys` and crates should typically never need to directly depend on `wdk-macros`
|
||||
|
||||
To see an example of this repo used to create drivers, see [Windows-rust-driver-samples](https://github.com/microsoft/Windows-rust-driver-samples).
|
||||
|
||||
Note: This project is still in early stages of development and is not yet recommended for production use. We encourage community experimentation and collaboration through our [GitHub Discussions forum](https://github.com/microsoft/windows-drivers-rs/discussions)!
|
||||
|
||||
## <a name="supported-configs">Supported Configurations
|
||||
|
||||
This project was built with support of WDM, KMDF, and UMDF drivers in mind, as well as Win32 Services. This includes support for all versions of WDF included in WDK 22H2 and newer. Currently, the crates available on [`crates.io`](https://crates.io) only support KMDF v1.33, but bindings can be generated for everything else by cloning `windows-drivers-rs` and modifying the config specified in [`build.rs` of `wdk-sys`](./crates/wdk-sys/build.rs). Crates.io support for other WDK configurations is planned in the near future.
|
||||
|
||||
## Repo Layout
|
||||
|
||||
* [crates](./crates): Contains all the main crates that are a part of the Cargo workspace.
|
||||
* [examples](./examples): Contains workspace-level examples. These examples consist of different types of minimal Windows drivers (ie. WDM, KMDF, UMDF).
|
||||
* [tests](./tests): Contains workspace-level tests, including tests for metadata-based wdk configuration in packages and workspaces.
|
||||
|
||||
**Note:**: Since the workspace level examples and tests use different WDK configurations, and WDR only supports one WDK configuration per workspace, the workspace-level examples and tests folder are excluded from the [repository root's Cargo manifest](./Cargo.toml).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Build Requirements
|
||||
|
||||
* Binding generation via `bindgen` requires `libclang`. The easiest way to acquire this is via `winget`
|
||||
* `winget install -i LLVM.LLVM --version 17.0.6 --force`
|
||||
* Ensure you select the GUI option to add LLVM to the PATH
|
||||
* LLVM 18 has a bug that causes bindings to fail to generate for ARM64. Continue using LLVM 17 until LLVM 19 comes out with [the fix](https://github.com/llvm/llvm-project/pull/93235). See [this](https://github.com/rust-lang/rust-bindgen/issues/2842) for more details.
|
||||
* To execute post-build tasks (ie. `inf2cat`, `infverif`, etc.), `cargo make` is used
|
||||
* `cargo install --locked cargo-make --no-default-features --features tls-native`
|
||||
|
||||
* Building programs with the WDK also requires being in a valid WDK environment. The recommended way to do this is to [enter an eWDK developer prompt](https://learn.microsoft.com/en-us/windows-hardware/drivers/develop/using-the-enterprise-wdk#getting-started)
|
||||
|
||||
### Adding windows-drivers-rs to Your Driver Package
|
||||
|
||||
The crates in this repository are available from [`crates.io`](https://crates.io), but take into account the current limitations outlined in [Supported Configurations](#supported-configs). If you need to support a different config, try cloning this repo and using [path dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-path-dependencies)
|
||||
|
||||
1. Create a new Cargo package with a lib crate:
|
||||
|
||||
```pwsh
|
||||
cargo new <driver_name> --lib
|
||||
```
|
||||
|
||||
1. Add dependencies on `windows-drivers-rs` crates:
|
||||
|
||||
```pwsh
|
||||
cd <driver_name>
|
||||
cargo add --build wdk-build
|
||||
cargo add wdk wdk-sys wdk-alloc wdk-panic
|
||||
```
|
||||
|
||||
1. Set the crate type to `cdylib` by adding the following snippet to `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
```
|
||||
|
||||
1. Add a wdk metadata section and configure the wdk for your use case. This also lets the cargo-make tasks know that the package is a driver and that the driver packaging steps need to run.
|
||||
|
||||
UMDF Example:
|
||||
```toml
|
||||
[package.metadata.wdk.driver-model]
|
||||
driver-type = "UMDF"
|
||||
umdf-version-major = 1
|
||||
target-umdf-version-minor = 33
|
||||
```
|
||||
|
||||
1. **For Kernel Mode crates** (ex. `KMDF` drivers, `WDM` drivers): Set crate panic strategy to `abort` in `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
```
|
||||
|
||||
1. Create a `build.rs` and add the following snippet:
|
||||
|
||||
```rust
|
||||
fn main() -> Result<(), wdk_build::ConfigError> {
|
||||
wdk_build::configure_wdk_binary_build()
|
||||
}
|
||||
```
|
||||
|
||||
1. **For Kernel Mode crates** (ex. `KMDF` drivers, `WDM` drivers): Mark your driver crate as `no_std` in `lib.rs`:
|
||||
|
||||
```rust
|
||||
#![no_std]
|
||||
```
|
||||
|
||||
1. **For Kernel Mode crates** (ex. `KMDF` drivers, `WDM` drivers): Add a panic handler in `lib.rs`:
|
||||
|
||||
```rust
|
||||
#[cfg(not(test))]
|
||||
extern crate wdk_panic;
|
||||
|
||||
```
|
||||
|
||||
1. **For Kernel Mode crates** (ex. `KMDF` drivers, `WDM` drivers): Add an optional global allocator in `lib.rs`:
|
||||
|
||||
```rust
|
||||
#[cfg(not(test))]
|
||||
use wdk_alloc::WdkAllocator;
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[global_allocator]
|
||||
static GLOBAL_ALLOCATOR: WdkAllocator = WdkAllocator;
|
||||
```
|
||||
|
||||
This is only required if you want to be able to use the [`alloc` modules](https://doc.rust-lang.org/alloc/) in the rust standard library.
|
||||
|
||||
1. Add a DriverEntry in `lib.rs`:
|
||||
|
||||
```rust
|
||||
use wdk_sys::{
|
||||
PDRIVER_OBJECT,
|
||||
NTSTATUS,
|
||||
PCUNICODE_STRING,
|
||||
};
|
||||
|
||||
// SAFETY: "DriverEntry" is the required symbol name for Windows driver entry points.
|
||||
// No other function in this compilation unit exports this name, preventing symbol conflicts.
|
||||
#[unsafe(export_name = "DriverEntry")] // WDF expects a symbol with the name DriverEntry
|
||||
pub unsafe extern "system" fn driver_entry(
|
||||
driver: PDRIVER_OBJECT,
|
||||
registry_path: PCUNICODE_STRING,
|
||||
) -> NTSTATUS {
|
||||
0
|
||||
}
|
||||
```
|
||||
|
||||
Note: In Kernel Mode crates, you can use `driver: &mut DRIVER_OBJECT` instead of `driver: PDRIVER_OBJECT`.
|
||||
|
||||
1. Add a `Makefile.toml`:
|
||||
```toml
|
||||
extend = "target/rust-driver-makefile.toml"
|
||||
|
||||
[config]
|
||||
load_script = '''
|
||||
#!@rust
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = "0.5.1"
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
wdk_build::cargo_make::load_rust_driver_makefile()?
|
||||
'''
|
||||
```
|
||||
|
||||
1. Add an inx file that matches the name of your `cdylib` crate.
|
||||
|
||||
1. Enable static crt linkage. One approach is to add this to your `.cargo/config.toml`:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
```
|
||||
|
||||
1. Build the driver:
|
||||
|
||||
```pwsh
|
||||
cargo make
|
||||
```
|
||||
|
||||
A signed driver package, including a `WDRLocalTestCert.cer` file, will be generated at `target/<Cargo profile>/package`. If a specific target architecture was specified, the driver package will be generated at `target/<target architecture>/<Cargo profile>/package`
|
||||
|
||||
Minimal examples of `WDM`, `KMDF`, and `UMDF` drivers can be found in the [examples directory](./examples).
|
||||
|
||||
## Cargo Make
|
||||
|
||||
[`cargo-make`](https://github.com/sagiegurari/cargo-make) is used to facilitate builds using `windows-drivers-rs`, including for executing post-build driver packaging steps.
|
||||
|
||||
To execute the default action (build and package driver):
|
||||
|
||||
`cargo make default`
|
||||
|
||||
When executing the default task, just `cargo make` make also works since the `default` task is implied.
|
||||
|
||||
### Argument Forwarding
|
||||
|
||||
`windows-drivers-rs` extends `cargo make` to forward specific arguments to the underlying `cargo` commands. In order to specify arguments to forward, they must be provided **after explicitly specifying the `cargo-make` task name** (ie. omitting the name for the `default` task is not supported).
|
||||
|
||||
#### Examples
|
||||
|
||||
For a specific target:
|
||||
|
||||
`cargo make default --target <TARGET TRIPLE>`
|
||||
|
||||
For release builds:
|
||||
|
||||
`cargo make default --release` or `cargo make default --profile release`
|
||||
|
||||
To specify specific features:
|
||||
|
||||
`cargo make default --features <FEATURES>`
|
||||
|
||||
To specify a specific rust toolchain:
|
||||
|
||||
`cargo make default +<TOOLCHAIN>`
|
||||
|
||||
To display help and see the full list of supported CLI args to forward to Cargo:
|
||||
|
||||
`cargo make help`
|
||||
|
||||
### Driver Package Signature Verification
|
||||
|
||||
The `WDK_BUILD_ENABLE_SIGNTOOL_VERIFY` [cargo-make environment variable](https://github.com/sagiegurari/cargo-make?tab=readme-ov-file#environment-variables) can be set to `true` to enable tasks that handle signature verification of the generated `.sys` and `.cat` files. `signtool verify` requires the certificate to be installed as in the `Trusted Root Certification Authorities` for this verification to function. These tasks are not enabled by default as the default behavior of `WDR` is to sign with a generated test certificate. These test certificates are typically only installed into `Trusted Root Certification Authorities` on computers dedicated to testing drivers, and not personal development machines, given the security implications of installing your own root certificates.
|
||||
|
||||
If you understand these implications, and have installed the test certificate, then you may validate the signatures as follows:
|
||||
|
||||
```
|
||||
cargo make --env WDK_BUILD_ENABLE_SIGNTOOL_VERIFY=true
|
||||
```
|
||||
|
||||
## Contact
|
||||
|
||||
* For bug reports, feature requests, and other actionable items, please use [GitHub Issues](https://github.com/microsoft/windows-drivers-rs/issues)
|
||||
* For broader questions, architectural discussions, and community engagement, please use [GitHub Discussions](https://github.com/microsoft/windows-drivers-rs/discussions)
|
||||
* For inquiries not suitable for public forums, email us at <RustWindowsDrivers@microsoft.com>
|
||||
|
||||
## Crates.io Release Policy
|
||||
|
||||
Releases to crates.io are not made after every change merged to main. Releases will only be made when requested by the community, or when the `windows-drivers-rs` team believes there is sufficient value in pushing a release.
|
||||
|
||||
## Trademark Notice
|
||||
|
||||
Trademarks This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft’s Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies.
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// License: MIT OR Apache-2.0
|
||||
|
||||
//! Build script for the `wdk-build` crate
|
||||
//!
|
||||
//! This provides a `nightly_toolchain` feature to the `wdk-build` crate, so
|
||||
//! that it can conditionally enable unstable features.
|
||||
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(nightly_toolchain)");
|
||||
setup_nightly_cfgs();
|
||||
}
|
||||
|
||||
// Custom attributes cannot be applied to expressions yet, so separate functions are required for nightly/non-nightly: https://github.com/rust-lang/rust/issues/15701
|
||||
#[rustversion::nightly]
|
||||
fn setup_nightly_cfgs() {
|
||||
println!("cargo::rustc-cfg=nightly_toolchain");
|
||||
}
|
||||
|
||||
#[rustversion::not(nightly)]
|
||||
const fn setup_nightly_cfgs() {}
|
||||
@@ -0,0 +1,567 @@
|
||||
# This file is leveraged to build downstream drivers. See examples at https://github.com/microsoft/Windows-rust-drivers-samples
|
||||
|
||||
[config]
|
||||
min_version = "0.37.16"
|
||||
init_task = "wdk-build-init"
|
||||
reduce_output = false
|
||||
|
||||
[env]
|
||||
# This allows all workspace members to access this makefile
|
||||
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
|
||||
# CARGO_MAKE_CARGO_BUILD_TEST_FLAGS is set to "--all-features" by default in cargo-make: https://github.com/sagiegurari/cargo-make/blob/c0abc4d0ae1bcc03adde22b63fa0accc4af2b3bc/src/lib/descriptor/makefiles/stable.toml#L31
|
||||
# This is set to "" here to match the default behavior of Cargo.
|
||||
CARGO_MAKE_CARGO_BUILD_TEST_FLAGS = { unset = true }
|
||||
|
||||
# rust-script condition_script's return `Err` to signal that the task should not be run. Hide Err backtraces by default to keep output cleaner.
|
||||
RUST_LIB_BACKTRACE = 0
|
||||
|
||||
WDK_BUILD_BASE_INFVERIF_FLAGS = "/v"
|
||||
|
||||
[plugins.impl.rust-env-update]
|
||||
script = '''
|
||||
assert ${task.has_script} "script is required for rust-env-update plugin"
|
||||
assert_eq ${task.script_runner} @rust "script_runner must be set to @rust for rust-env-update plugin"
|
||||
|
||||
cargo_make_rust_script_provider = get_env CARGO_MAKE_RUST_SCRIPT_PROVIDER
|
||||
assert_eq ${cargo_make_rust_script_provider} rust-script "rust-env-update plugin is only compatible with rust-script"
|
||||
|
||||
taskjson = json_parse ${task.as_json}
|
||||
|
||||
# Install dependency crate
|
||||
out = exec --fail-on-error cargo install ${taskjson.install_crate.crate_name} --version ${taskjson.install_crate.min_version}
|
||||
assert_eq ${out.code} 0 "[tasks.${task.name}]'s install_crate failed with exit code: ${out.code}\nstdout:\n${out.stdout}\nstderr:\n${out.stderr}"
|
||||
|
||||
# Enable rust-env-update's rust-script cache (Note: when developing locally on WDR itself, rust-script.exe --clear-cache can be used to force a rebuild of the script's wdk-build dependency)
|
||||
filepath = set "${CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY}/cargo-make-script/${task.name}/rust-env-update.rs"
|
||||
# If a file already exists, only overwrite it if the script has changed (so that rust-script caching can be leveraged)
|
||||
if is_file ${filepath}
|
||||
old_hash = digest --algo sha256 --file ${filepath}
|
||||
new_hash = digest --algo sha256 ${taskjson.script}
|
||||
if not eq ${old_hash} ${new_hash}
|
||||
writefile ${filepath} ${taskjson.script}
|
||||
end
|
||||
else
|
||||
writefile ${filepath} ${taskjson.script}
|
||||
end
|
||||
|
||||
# Append cli args to task args
|
||||
task_args = array_join ${task.args} " "
|
||||
cli_args = array_join ${flow.cli.args} " "
|
||||
combined_args = concat ${cli_args} " " ${task_args}
|
||||
combined_args = trim ${combined_args}
|
||||
|
||||
# Execute rust-script
|
||||
out = exec --fail-on-error rust-script --base-path ${taskjson.env.CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY} ${CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY}/cargo-make-script/${task.name}/rust-env-update.rs %{combined_args}
|
||||
assert_eq ${out.code} 0 "[tasks.${task.name}]'s script failed with exit code: ${out.code}\nstdout:\n${out.stdout}\nstderr:\n${out.stderr}\nThe temporary rust-script file is located at ${CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY}/cargo-make-script/${task.name}/rust-env-update.rs"
|
||||
|
||||
if contains ${combined_args} "--help"
|
||||
println ${out.stdout}
|
||||
|
||||
# If help was triggered, exit with code 1 to prevent the rest of the makefile from running
|
||||
exit 1
|
||||
end
|
||||
|
||||
# Set cargo-make env vars based on output of rust-script
|
||||
script_output = trim ${out.stdout}
|
||||
if not is_empty ${script_output}
|
||||
script_output_array = split ${script_output} \n
|
||||
|
||||
# Search the stdout output of the script, with the following behaviours for each line:
|
||||
# 1. If the line is between the "FORWARDING ARGS TO CARGO-MAKE:" start delimited and the "END OF FORWARDING ARGS TO CARGO-MAKE" end delimiter, update the cargo-make process' environment variables based on the key-value pairs in the line.
|
||||
# 2. If the line is not between the start and end delimiters, print the line normally.
|
||||
looking_for_start_delimiter = set true
|
||||
for line in ${script_output_array}
|
||||
if ${looking_for_start_delimiter}
|
||||
if eq ${line} "FORWARDING ARGS TO CARGO-MAKE:"
|
||||
looking_for_start_delimiter = set false
|
||||
else
|
||||
# Any output not surrounded by the start and end delimiter lines should be printed normally
|
||||
println ${line}
|
||||
end
|
||||
else
|
||||
if eq ${line} "END OF FORWARDING ARGS TO CARGO-MAKE"
|
||||
looking_for_start_delimiter = set true
|
||||
else
|
||||
# Set cargo-make env_var based on line output
|
||||
parts = split ${line} =
|
||||
key = array_get ${parts} 0
|
||||
value = array_get ${parts} 1
|
||||
set_env ${key} ${value}
|
||||
end
|
||||
end
|
||||
end
|
||||
assert ${looking_for_start_delimiter} "A matching \"END OF FORWARDING ARGS TO CARGO-MAKE\" for a \"FORWARDING ARGS TO CARGO-MAKE:\" was not found in script output."
|
||||
end
|
||||
'''
|
||||
|
||||
# This plugin adds support for cargo-make's emulated workspace feature to work on emulated workspace members which are Cargo workspaces themselves.
|
||||
# Since Cargo workspaces are not detected in cargo-make emulated workspace members, the task is rerun in a forked subprocess with the CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER env var unset to allow cargo-make's workspace detection to run.
|
||||
[plugins.impl.nested-cargo-workspace-in-cargo-make-emulated-workspace-support]
|
||||
script = '''
|
||||
# If current flow is executing in a Cargo workspace, which is a member of a cargo-make emulated workspace
|
||||
if ${CARGO_MAKE_WORKSPACE_EMULATION} and ${CARGO_MAKE_CRATE_IS_WORKSPACE}
|
||||
|
||||
# Re-run the task in a forked subprocess, allowing cargo-make to run in a workspace context (i.e. running on each of the members of the Cargo workspace)
|
||||
echo Executing \"${task.name}\" Task in a forked subprocess to run on Cargo workspace: ${CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER}
|
||||
|
||||
# Unset the CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER env var to allow cargo-make's workspace detection to run
|
||||
unset_env CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER
|
||||
|
||||
cm_plugin_run_custom_task "{\"run_task\":{\"name\":\"${task.name}\",\"fork\":true}}"
|
||||
else
|
||||
cm_plugin_run_task
|
||||
end
|
||||
'''
|
||||
|
||||
[tasks.wdk-build-init]
|
||||
private = true
|
||||
install_crate = { crate_name = "rust-script", min_version = "0.30.0" }
|
||||
plugin = "rust-env-update"
|
||||
script_runner = "@rust"
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
let cli_env_vars = wdk_build::cargo_make::validate_command_line_args();
|
||||
let path_env_vars = wdk_build::cargo_make::setup_path()?;
|
||||
let wdk_version_env_vars = wdk_build::cargo_make::setup_wdk_version()?;
|
||||
|
||||
wdk_build::cargo_make::forward_printed_env_vars(
|
||||
cli_env_vars.into_iter().chain(path_env_vars).chain(wdk_version_env_vars),
|
||||
);
|
||||
'''
|
||||
|
||||
[tasks.setup-wdk-config-env-vars]
|
||||
# This exists as a separate task outside of `wdk-build-init` so that any wdk-metadata-detection errors can be a hard error, without failing every task flow that executes on non-driver crates in the workspace.
|
||||
private = true
|
||||
install_crate = { crate_name = "rust-script", min_version = "0.30.0" }
|
||||
plugin = "rust-env-update"
|
||||
script_runner = "@rust"
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
let serialized_wdk_metadata_map = wdk_build::metadata::to_map_with_prefix::<std::collections::BTreeMap<_, _>>(
|
||||
"WDK_BUILD_METADATA",
|
||||
&wdk_build::metadata::Wdk::try_from(&wdk_build::cargo_make::get_cargo_metadata()?)?,
|
||||
)?;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
compile_error!(
|
||||
"windows-drivers-rs is designed to be run on a Windows host machine in a WDK \
|
||||
environment. Please build using a Windows target."
|
||||
);
|
||||
|
||||
for (key, value) in &serialized_wdk_metadata_map {
|
||||
// SAFETY: this function is only conditionally compiled for windows targets, and
|
||||
// env::set_var is always safe for windows targets
|
||||
unsafe {
|
||||
std::env::set_var(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
wdk_build::cargo_make::forward_printed_env_vars(
|
||||
serialized_wdk_metadata_map
|
||||
.into_iter()
|
||||
.map(|(key, _)| key),
|
||||
);
|
||||
'''
|
||||
|
||||
[tasks.copy-inx-to-output]
|
||||
private = true
|
||||
script_runner = "@rust"
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
// Create build output directory if it doesn't exist
|
||||
let output_folder_path = wdk_build::cargo_make::get_wdk_build_output_directory();
|
||||
if !output_folder_path.exists() {
|
||||
std::fs::create_dir_all(&output_folder_path).expect(&format!("creation of '{}' folder should succeed", output_folder_path.display()));
|
||||
}
|
||||
|
||||
let cargo_make_working_directory = std::env::var("CARGO_MAKE_WORKING_DIRECTORY").expect(
|
||||
"CARGO_MAKE_WORKING_DIRECTORY should be set by cargo-make via the env section of \
|
||||
rust-driver-makefile.toml",
|
||||
);
|
||||
|
||||
let source_file = [
|
||||
cargo_make_working_directory,
|
||||
format!("{}.inx", wdk_build::cargo_make::get_current_package_name()),
|
||||
]
|
||||
.iter()
|
||||
.collect::<std::path::PathBuf>();
|
||||
|
||||
let destination_file = wdk_build::cargo_make::get_wdk_build_output_directory().join(format!(
|
||||
"{}.inf",
|
||||
wdk_build::cargo_make::get_current_package_name()
|
||||
));
|
||||
|
||||
std::fs::copy(&source_file, &destination_file).expect(&format!(
|
||||
"copy of '{}' file to '{}' file should succeed",
|
||||
source_file.display(),
|
||||
destination_file.display()
|
||||
));
|
||||
'''
|
||||
|
||||
[tasks.generate-driver-binary-file]
|
||||
private = true
|
||||
dependencies = ["setup-wdk-config-env-vars", "build"]
|
||||
condition_script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
condition_script = '''
|
||||
#!@rust
|
||||
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
wdk_build::cargo_make::condition_script(|| {
|
||||
let driver_type = std::env::var("WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE")
|
||||
.expect("WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE should be set by setup-wdk-config-env-vars cargo-make task");
|
||||
|
||||
match driver_type.as_str() {
|
||||
"WDM" | "KMDF" => Ok(()),
|
||||
_ => Err("Non-Kernel Mode Driver detected. Skipping generate-driver-binary-file task."),
|
||||
}
|
||||
})?
|
||||
'''
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
#!@rust
|
||||
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
let source_file = wdk_build::cargo_make::get_wdk_build_output_directory().join(format!(
|
||||
"{}.dll",
|
||||
wdk_build::cargo_make::get_current_package_name()
|
||||
));
|
||||
|
||||
let destination_file = wdk_build::cargo_make::get_wdk_build_output_directory().join(format!(
|
||||
"{}.sys",
|
||||
wdk_build::cargo_make::get_current_package_name()
|
||||
));
|
||||
|
||||
std::fs::copy(&source_file, &destination_file).expect(&format!(
|
||||
"copy of '{}' file to '{}' file should succeed",
|
||||
source_file.display(),
|
||||
destination_file.display()
|
||||
));
|
||||
'''
|
||||
|
||||
[tasks.stampinf]
|
||||
private = true
|
||||
dependencies = ["setup-wdk-config-env-vars", "copy-inx-to-output"]
|
||||
env = { "WDK_BUILD_STAMPINF_WDF_FLAGS" = { source = "${WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE}", default_value = "", mapping = { "KMDF" = "-k ${WDK_BUILD_METADATA-DRIVER_MODEL-KMDF_VERSION_MAJOR}.${WDK_BUILD_METADATA-DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR}", "UMDF" = "-u ${WDK_BUILD_METADATA-DRIVER_MODEL-UMDF_VERSION_MAJOR}.${WDK_BUILD_METADATA-DRIVER_MODEL-TARGET_UMDF_VERSION_MINOR}.0" } }, "WDK_BUILD_STAMPINF_ARCH" = { source = "${CARGO_MAKE_CRATE_TARGET_TRIPLE}", default_value = "UNKNOWN", mapping = { "x86_64-pc-windows-msvc" = "amd64", "aarch64-pc-windows-msvc" = "arm64" } } }
|
||||
command = "stampinf"
|
||||
args = [
|
||||
"-f",
|
||||
"${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}.inf",
|
||||
"-d",
|
||||
"*",
|
||||
"-a",
|
||||
"${WDK_BUILD_STAMPINF_ARCH}",
|
||||
"-c",
|
||||
"${CARGO_MAKE_CRATE_FS_NAME}.cat",
|
||||
"-v",
|
||||
"*",
|
||||
"@@split(WDK_BUILD_STAMPINF_WDF_FLAGS, ,remove-empty)",
|
||||
]
|
||||
|
||||
[tasks.infverif]
|
||||
private = true
|
||||
dependencies = ["setup-wdk-config-env-vars", "stampinf"]
|
||||
# TODO: This should be if WDK <= GE && DRIVER_MODEL == UMDF
|
||||
env = { "WDK_BUILD_BASE_INFVERIF_FLAGS" = { source = "${WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE}", default_value = "${WDK_BUILD_BASE_INFVERIF_FLAGS} /w", mapping = { "UMDF" = "${WDK_BUILD_BASE_INFVERIF_FLAGS} /u" } } }
|
||||
command = "infverif"
|
||||
args = [
|
||||
"@@split(WDK_BUILD_BASE_INFVERIF_FLAGS, ,remove-empty)",
|
||||
"@@split(WDK_BUILD_ADDITIONAL_INFVERIF_FLAGS, ,remove-empty)",
|
||||
"${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}.inf",
|
||||
]
|
||||
|
||||
[tasks.copy-driver-binary-to-package]
|
||||
private = true
|
||||
dependencies = ["setup-wdk-config-env-vars", "generate-driver-binary-file"]
|
||||
env = { "WDK_BUILD_DRIVER_EXTENSION" = { source = "${WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE}", default_value = "UNKNOWN_EXTENSION", mapping = { "WDM" = "sys", "KMDF" = "sys", "UMDF" = "dll" } } }
|
||||
script_runner = "@rust"
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
let driver_binary_extension = std::env::var("WDK_BUILD_DRIVER_EXTENSION").expect("WDK_BUILD_DRIVER_EXTENSION should be set by cargo-make");
|
||||
wdk_build::cargo_make::copy_to_driver_package_folder(
|
||||
wdk_build::cargo_make::get_wdk_build_output_directory().join(format!(
|
||||
"{}.{driver_binary_extension}",
|
||||
wdk_build::cargo_make::get_current_package_name()
|
||||
)),
|
||||
)?
|
||||
'''
|
||||
|
||||
[tasks.copy-pdb-to-package]
|
||||
private = true
|
||||
dependencies = ["build"]
|
||||
script_runner = "@rust"
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
wdk_build::cargo_make::copy_to_driver_package_folder(
|
||||
wdk_build::cargo_make::get_wdk_build_output_directory().join(format!(
|
||||
"{}.pdb",
|
||||
wdk_build::cargo_make::get_current_package_name()
|
||||
)),
|
||||
)?
|
||||
'''
|
||||
|
||||
[tasks.copy-inf-to-package]
|
||||
private = true
|
||||
dependencies = ["stampinf"]
|
||||
script_runner = "@rust"
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
wdk_build::cargo_make::copy_to_driver_package_folder(
|
||||
wdk_build::cargo_make::get_wdk_build_output_directory().join(format!(
|
||||
"{}.inf",
|
||||
wdk_build::cargo_make::get_current_package_name()
|
||||
)),
|
||||
)?
|
||||
'''
|
||||
|
||||
[tasks.copy-map-to-package]
|
||||
private = true
|
||||
dependencies = ["build"]
|
||||
script_runner = "@rust"
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
wdk_build::cargo_make::copy_to_driver_package_folder(
|
||||
wdk_build::cargo_make::get_wdk_build_output_directory().join(format!(
|
||||
"deps/{}.map",
|
||||
wdk_build::cargo_make::get_current_package_name()
|
||||
)),
|
||||
)?
|
||||
'''
|
||||
|
||||
[tasks.inf2cat]
|
||||
private = true
|
||||
dependencies = ["copy-driver-binary-to-package", "copy-inf-to-package"]
|
||||
env = { "WDK_BUILD_INF2CAT_OS" = { source = "${CARGO_MAKE_CRATE_TARGET_TRIPLE}", default_value = "UNKNOWN", mapping = { "x86_64-pc-windows-msvc" = "10_x64", "aarch64-pc-windows-msvc" = "Server10_arm64" } } }
|
||||
command = "inf2cat"
|
||||
args = [
|
||||
"/driver:${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}_package",
|
||||
"/os:${WDK_BUILD_INF2CAT_OS}",
|
||||
"/uselocaltime",
|
||||
]
|
||||
|
||||
[tasks.generate-certificate]
|
||||
private = true
|
||||
condition_script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
condition_script = '''
|
||||
#!@rust
|
||||
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! anyhow = "1"
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
wdk_build::cargo_make::generate_certificate_condition_script()
|
||||
}
|
||||
'''
|
||||
command = "makecert"
|
||||
args = [
|
||||
"-r",
|
||||
"-pe",
|
||||
"-a",
|
||||
"SHA256",
|
||||
"-eku",
|
||||
"1.3.6.1.5.5.7.3.3",
|
||||
"-ss",
|
||||
"WDRTestCertStore", # FIXME: this should be a parameter
|
||||
"-n",
|
||||
"CN=WDRLocalTestCert", # FIXME: this should be a parameter
|
||||
"${WDK_BUILD_OUTPUT_DIRECTORY}/WDRLocalTestCert.cer",
|
||||
]
|
||||
|
||||
[tasks.copy-certificate-to-package]
|
||||
private = true
|
||||
dependencies = ["generate-certificate"]
|
||||
script_runner = "@rust"
|
||||
script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
wdk_build::cargo_make::copy_to_driver_package_folder(
|
||||
wdk_build::cargo_make::get_wdk_build_output_directory().join("WDRLocalTestCert.cer"),
|
||||
)?
|
||||
'''
|
||||
|
||||
[tasks.signtool-sign]
|
||||
private = true
|
||||
dependencies = ["generate-certificate"]
|
||||
command = "signtool"
|
||||
args = [
|
||||
"sign",
|
||||
"/v",
|
||||
"/s",
|
||||
"WDRTestCertStore", # FIXME: this should be a parameter
|
||||
"/n",
|
||||
"WDRLocalTestCert", # FIXME: this should be a parameter
|
||||
"/t",
|
||||
"http://timestamp.digicert.com",
|
||||
"/fd",
|
||||
"SHA256",
|
||||
"${WDK_BUILD_SIGNTOOL_SIGN_INPUT_FILE}",
|
||||
]
|
||||
|
||||
[tasks.sign-driver-binary]
|
||||
private = true
|
||||
dependencies = ["setup-wdk-config-env-vars", "copy-driver-binary-to-package"]
|
||||
env = { "WDK_BUILD_SIGNTOOL_SIGN_INPUT_FILE" = "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}_package/${CARGO_MAKE_CRATE_FS_NAME}.${WDK_BUILD_DRIVER_EXTENSION}" }
|
||||
run_task = "signtool-sign"
|
||||
|
||||
[tasks.sign-cat]
|
||||
private = true
|
||||
dependencies = ["inf2cat", "sign-driver-binary"]
|
||||
env = { "WDK_BUILD_SIGNTOOL_SIGN_INPUT_FILE" = "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}_package/${CARGO_MAKE_CRATE_FS_NAME}.cat" }
|
||||
run_task = "signtool-sign"
|
||||
|
||||
[tasks.signtool-verify]
|
||||
private = true
|
||||
condition = { env_true = ["WDK_BUILD_ENABLE_SIGNTOOL_VERIFY"] }
|
||||
command = "signtool"
|
||||
args = ["verify", "/v", "/pa", "${WDK_BUILD_SIGNTOOL_VERIFY_INPUT_FILE}"]
|
||||
|
||||
[tasks.verify-signature-driver-binary]
|
||||
private = true
|
||||
dependencies = ["setup-wdk-config-env-vars", "sign-driver-binary"]
|
||||
env = { "WDK_BUILD_SIGNTOOL_VERIFY_INPUT_FILE" = "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}_package/${CARGO_MAKE_CRATE_FS_NAME}.${WDK_BUILD_DRIVER_EXTENSION}" }
|
||||
run_task = "signtool-verify"
|
||||
|
||||
[tasks.verify-signature-cat]
|
||||
private = true
|
||||
dependencies = ["sign-cat"]
|
||||
env = { "WDK_BUILD_SIGNTOOL_VERIFY_INPUT_FILE" = "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}_package/${CARGO_MAKE_CRATE_FS_NAME}.cat" }
|
||||
run_task = "signtool-verify"
|
||||
|
||||
[tasks.package-driver]
|
||||
private = true
|
||||
dependencies = [
|
||||
"copy-driver-binary-to-package",
|
||||
"copy-pdb-to-package",
|
||||
"copy-inf-to-package",
|
||||
"copy-map-to-package",
|
||||
"copy-certificate-to-package",
|
||||
"sign-driver-binary",
|
||||
"verify-signature-driver-binary",
|
||||
"sign-cat",
|
||||
"verify-signature-cat",
|
||||
"infverif",
|
||||
]
|
||||
|
||||
[tasks.package-driver-flow]
|
||||
# Note: Dependencies are always run, regardless of the condition_script result. This allows `cargo make` in mixed driver/non-driver workspaces
|
||||
dependencies = ["build"]
|
||||
# Only run package-driver flow if the current package is marked as a driver
|
||||
plugin = "nested-cargo-workspace-in-cargo-make-emulated-workspace-support"
|
||||
condition_script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
condition_script = '''
|
||||
#!@rust
|
||||
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! anyhow = "1"
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
wdk_build::cargo_make::package_driver_flow_condition_script()
|
||||
}
|
||||
'''
|
||||
run_task = "package-driver"
|
||||
|
||||
[tasks.help]
|
||||
extend = "wdk-build-init"
|
||||
private = false
|
||||
workspace = false
|
||||
args = ["--help"]
|
||||
|
||||
[tasks.default]
|
||||
alias = "package-driver-flow"
|
||||
@@ -0,0 +1,48 @@
|
||||
# This file is used to extend the standard rust-driver-makefile to build official sample drivers. See examples at https://github.com/microsoft/Windows-rust-drivers-samples
|
||||
# Using this file requires extending both the standard makefile and this makefile in order, as follows:
|
||||
# extend = [ { path = "target/rust-driver-makefile.toml" }, { path = "target/rust-driver-sample-makefile.toml" } ]
|
||||
|
||||
[tasks.wdk-samples-setup]
|
||||
private = true
|
||||
install_crate = { crate_name = "rust-script", min_version = "0.30.0" }
|
||||
plugin = "rust-env-update"
|
||||
script_runner = "@rust"
|
||||
script = '''
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
let env_string = std::env::var_os(wdk_build::cargo_make::WDK_VERSION_ENV_VAR)
|
||||
.map_or_else(
|
||||
|| panic!("Couldn't read WDK build version that should have been set in init"),
|
||||
|os_env_string| os_env_string.to_string_lossy().into_owned(),
|
||||
);
|
||||
let samples_infverif_env_vars = wdk_build::cargo_make::setup_infverif_for_samples(&env_string)?;
|
||||
|
||||
wdk_build::cargo_make::forward_printed_env_vars(
|
||||
samples_infverif_env_vars,
|
||||
);
|
||||
'''
|
||||
|
||||
[tasks.infverif]
|
||||
dependencies = ["wdk-samples-setup", "stampinf"]
|
||||
condition_script_runner_args = [
|
||||
"--base-path",
|
||||
"${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}",
|
||||
]
|
||||
condition_script = '''
|
||||
#!@rust
|
||||
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! wdk-build = { path = ".", version = "0.5.1" }
|
||||
//! anyhow = "1"
|
||||
//! ```
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
wdk_build::cargo_make::driver_sample_infverif_condition_script()
|
||||
}
|
||||
'''
|
||||
@@ -0,0 +1,273 @@
|
||||
// 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)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+2232
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// License: MIT OR Apache-2.0
|
||||
|
||||
use serde::ser::{self};
|
||||
use thiserror::Error;
|
||||
|
||||
/// A specialized [`Result`] type for [`metadata`](crate::metadata)
|
||||
/// serialization and deserialization operations.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// This type represents all possible errors that can occur when serializing
|
||||
/// or deserializing [`metadata::Wdk`](crate::metadata::Wdk).
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// catch-all error emitted during serialization, when a more specific
|
||||
/// error type is not available. This type of error is commonly
|
||||
/// generated from [`serde`]'s `derive` feature's generated `Serialize`
|
||||
/// impls.
|
||||
#[error("custom serialization error: {message}")]
|
||||
CustomSerialization {
|
||||
/// Message describing the error
|
||||
message: String,
|
||||
},
|
||||
|
||||
/// error emitted when an empty key name is encountered during
|
||||
/// serialization. Serialization of values always requires a non-empty
|
||||
/// key name
|
||||
#[error("empty key name encountered during serialization of value: {value_being_serialized}")]
|
||||
EmptySerializationKeyName {
|
||||
/// Value being serialized
|
||||
value_being_serialized: String,
|
||||
},
|
||||
|
||||
/// error emitted when duplicate key names are found during
|
||||
/// serialization. Serializing into a
|
||||
/// [`metadata::Map`](crate::metadata::Map) requires unique key names
|
||||
#[error(
|
||||
"duplicate keys found during serialization:\nkey: {key}\nvalue 1: {value_1}\nvalue 2: \
|
||||
{value_2}"
|
||||
)]
|
||||
DuplicateSerializationKeys {
|
||||
/// Key name
|
||||
key: String,
|
||||
/// One of the conflicting values
|
||||
value_1: String,
|
||||
/// One of the conflicting values
|
||||
value_2: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl ser::Error for Error {
|
||||
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||
Self::CustomSerialization {
|
||||
message: msg.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// License: MIT OR Apache-2.0
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, btree_map, hash_map},
|
||||
hash::{BuildHasher, Hash},
|
||||
};
|
||||
|
||||
/// Trait for map-like type that is returned by
|
||||
/// [`metadata::to_map`](crate::metadata::to_map)
|
||||
/// and [`metadata::to_map_with_prefix`](crate::metadata::to_map_with_prefix).
|
||||
pub trait Map<K, V>: Default {
|
||||
/// Creates a new, empty map
|
||||
#[must_use]
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Inserts a new key-value pair into the map, or calls a function/closure
|
||||
/// if the key already exists.
|
||||
///
|
||||
/// The function/closure is called with the existing key, the existing
|
||||
/// value, and the new value it tried to insert. The closure can decide
|
||||
/// whether the function will return an `Err` or if it will still return a
|
||||
/// `Ok` despite not inserting the value.
|
||||
///
|
||||
/// # Errors
|
||||
/// This function returns an error if the key already exists and `f` returns
|
||||
/// an `Err` value
|
||||
fn insert_or_else<F, E>(&mut self, key: K, value: V, f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnMut(&K, &V, V) -> Result<(), E>;
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash, V, S: BuildHasher + Default> Map<K, V> for HashMap<K, V, S> {
|
||||
fn insert_or_else<F, E>(&mut self, key: K, value: V, mut f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnMut(&K, &V, V) -> Result<(), E>,
|
||||
{
|
||||
match self.entry(key) {
|
||||
hash_map::Entry::Occupied(entry) => f(entry.key(), entry.get(), value),
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Ord, V> Map<K, V> for BTreeMap<K, V> {
|
||||
fn insert_or_else<F, E>(&mut self, key: K, value: V, mut f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnMut(&K, &V, V) -> Result<(), E>,
|
||||
{
|
||||
match self.entry(key) {
|
||||
btree_map::Entry::Occupied(entry) => f(entry.key(), entry.get(), value),
|
||||
btree_map::Entry::Vacant(entry) => {
|
||||
entry.insert(value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,746 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// License: MIT OR Apache-2.0
|
||||
|
||||
use serde::{
|
||||
Serialize,
|
||||
ser::{self, Impossible},
|
||||
};
|
||||
|
||||
use super::{
|
||||
error::{Error, Result},
|
||||
map::Map,
|
||||
};
|
||||
|
||||
/// delimiter used to separate the names of the different nodes encoded into a
|
||||
/// key name. Since `-` is not valid in Rust identifiers, it is used
|
||||
/// as a separator between different node names.
|
||||
pub const KEY_NAME_SEPARATOR: char = '-';
|
||||
|
||||
/// Serialize a value into a [`Map`] where the keys represent a
|
||||
/// `KEY_NAME_SEPARATOR`-separated list of field names.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the type being serialized:
|
||||
/// * results in duplicate key names
|
||||
/// * results in an empty key name
|
||||
/// * otherwise fails to be parsed and correctly serialized into a [`Map`]
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use std::collections::BTreeMap;
|
||||
///
|
||||
/// use wdk_build::{
|
||||
/// DriverConfig,
|
||||
/// KmdfConfig,
|
||||
/// metadata::{self, to_map},
|
||||
/// };
|
||||
///
|
||||
/// let wdk_metadata = metadata::Wdk {
|
||||
/// driver_model: DriverConfig::Kmdf(KmdfConfig {
|
||||
/// kmdf_version_major: 1,
|
||||
/// target_kmdf_version_minor: 23,
|
||||
/// minimum_kmdf_version_minor: None,
|
||||
/// }),
|
||||
/// };
|
||||
///
|
||||
/// let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
|
||||
///
|
||||
/// assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
|
||||
/// assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
|
||||
/// assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "23");
|
||||
///
|
||||
/// // `None` values are not serialized
|
||||
/// assert_eq!(output.get("DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"), None);
|
||||
/// ```
|
||||
pub fn to_map<M>(value: &impl Serialize) -> Result<M>
|
||||
where
|
||||
M: Map<String, String>,
|
||||
{
|
||||
let mut serialization_buffer: Vec<(String, String)> = Vec::new();
|
||||
value.serialize(&mut Serializer::new(&mut serialization_buffer))?;
|
||||
convert_serialized_output_to_map(serialization_buffer)
|
||||
}
|
||||
|
||||
/// Serialize a value into a [`Map`] where the keys represent a
|
||||
/// `KEY_NAME_SEPARATOR`-separated list of field names prepended with a
|
||||
/// prefix.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the type being serialized:
|
||||
/// * results in duplicate key names
|
||||
/// * results in an empty key name
|
||||
/// * otherwise fails to be parsed and correctly serialized into a [`Map`]
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use std::collections::BTreeMap;
|
||||
///
|
||||
/// use wdk_build::{
|
||||
/// DriverConfig,
|
||||
/// KmdfConfig,
|
||||
/// metadata::{self, to_map_with_prefix},
|
||||
/// };
|
||||
///
|
||||
/// let wdk_metadata = metadata::Wdk {
|
||||
/// driver_model: DriverConfig::Kmdf(KmdfConfig {
|
||||
/// kmdf_version_major: 1,
|
||||
/// target_kmdf_version_minor: 33,
|
||||
/// minimum_kmdf_version_minor: Some(31),
|
||||
/// }),
|
||||
/// };
|
||||
///
|
||||
/// let output = to_map_with_prefix::<BTreeMap<_, _>>("WDK_BUILD_METADATA", &wdk_metadata).unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE"],
|
||||
/// "KMDF"
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-KMDF_VERSION_MAJOR"],
|
||||
/// "1"
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"],
|
||||
/// "33"
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"],
|
||||
/// "31"
|
||||
/// );
|
||||
/// ```
|
||||
pub fn to_map_with_prefix<M>(prefix: impl Into<String>, value: &impl Serialize) -> Result<M>
|
||||
where
|
||||
M: Map<String, String>,
|
||||
{
|
||||
let mut serialization_buffer: Vec<(String, String)> = Vec::new();
|
||||
value.serialize(&mut Serializer::with_prefix(
|
||||
prefix.into(),
|
||||
&mut serialization_buffer,
|
||||
))?;
|
||||
convert_serialized_output_to_map(serialization_buffer)
|
||||
}
|
||||
|
||||
fn convert_serialized_output_to_map<M>(serialization_buffer: Vec<(String, String)>) -> Result<M>
|
||||
where
|
||||
M: Map<String, String>,
|
||||
{
|
||||
let mut output_map = M::new();
|
||||
for (key, value) in serialization_buffer {
|
||||
output_map.insert_or_else(key, value, |key, existing_value, new_value| {
|
||||
Err(Error::DuplicateSerializationKeys {
|
||||
key: key.clone(),
|
||||
value_1: existing_value.clone(),
|
||||
value_2: new_value,
|
||||
})
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(output_map)
|
||||
}
|
||||
|
||||
/// [`serde`] serializer that serializes values into a [`Vec`] of key-value
|
||||
/// pairs.
|
||||
///
|
||||
/// This serializer is useful when you want to have more granular control of the
|
||||
/// output of the serializer. Most usecases should already be covered by the
|
||||
/// [`to_map`] and [`to_map_with_prefix`] functions.
|
||||
pub struct Serializer<'a> {
|
||||
root_key_name: Option<String>,
|
||||
dst: &'a mut Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl<'a> ser::Serializer for &'a mut Serializer<'a> {
|
||||
type Error = Error;
|
||||
type Ok = ();
|
||||
type SerializeMap = Impossible<Self::Ok, Self::Error>;
|
||||
type SerializeSeq = Impossible<Self::Ok, Self::Error>;
|
||||
type SerializeStruct = Self;
|
||||
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
|
||||
type SerializeTuple = Impossible<Self::Ok, Self::Error>;
|
||||
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
|
||||
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
|
||||
|
||||
unsupported_serde_serialize_method! {
|
||||
// simple types
|
||||
bytes newtype_struct newtype_variant unit_struct unit_variant
|
||||
// complex types (returns SerializeXYZ types)
|
||||
map seq struct_variant tuple tuple_struct tuple_variant
|
||||
}
|
||||
|
||||
fn serialize_str(self, value: &str) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_bool(self, value: bool) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_char(self, value: char) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i16(self, value: i16) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i32(self, value: i32) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i64(self, value: i64) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_f32(self, value: f32) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_f64(self, value: f64) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u8(self, value: u8) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u16(self, value: u16) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u32(self, value: u32) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
|
||||
self.dst.push((
|
||||
self.root_key_name
|
||||
.clone()
|
||||
.ok_or_else(|| Error::EmptySerializationKeyName {
|
||||
value_being_serialized: value.to_string(),
|
||||
})?,
|
||||
value.to_string(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeStruct for &'a mut Serializer<'a> {
|
||||
type Error = Error;
|
||||
type Ok = ();
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<Self::Ok>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut Serializer::with_prefix(
|
||||
self.root_key_name.as_ref().map_or_else(
|
||||
|| key.to_string(),
|
||||
|root_key_name| format!("{root_key_name}{KEY_NAME_SEPARATOR}{key}"),
|
||||
),
|
||||
self.dst,
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Serializer<'a> {
|
||||
/// Create a new instance of the `Serializer` struct
|
||||
pub const fn new(dst: &'a mut Vec<(String, String)>) -> Self {
|
||||
Self {
|
||||
root_key_name: None,
|
||||
dst,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new instance of the `Serializer` struct with a prefix used as
|
||||
/// the root for all keys
|
||||
pub const fn with_prefix(prefix: String, dst: &'a mut Vec<(String, String)>) -> Self {
|
||||
Self {
|
||||
root_key_name: Some(prefix),
|
||||
dst,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Helper macro when implementing the `Serializer` part of a new data
|
||||
/// format for Serde.
|
||||
///
|
||||
/// Generates [`serde::ser::Serializer`] trait methods for serde data model
|
||||
/// types that aren't supported by this serializer. This generates a
|
||||
/// method that calls [`unimplemented!`].
|
||||
macro_rules! unsupported_serde_serialize_method {
|
||||
($($method_type:ident)*) => {
|
||||
$(unsupported_serde_serialize_method_helper! {$method_type})*
|
||||
};
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub(crate) use unsupported_serde_serialize_method;
|
||||
|
||||
#[doc(hidden)]
|
||||
macro_rules! unsupported_serde_serialize_method_helper {
|
||||
// methods for simple types (returns Ok)
|
||||
(bytes) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_bytes(_v: &[u8]) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(newtype_struct) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_newtype_struct<T>(_name: &'static str, _value: &T) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(newtype_variant) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_newtype_variant<T>(_name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(none) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_none() -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(some) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_some<T>(_value: &T) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(str) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_str(_v: &str) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(unit) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_unit() -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(unit_struct) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_unit_struct(_name: &'static str) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(unit_variant) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_unit_variant(_name: &'static str, _variant_index: u32, _variant: &'static str) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
// methods for complex types (returns SerializeXYZ types)
|
||||
(map) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_map(_len: Option<usize>) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::SerializeMap,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(struct) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_struct(_name: &'static str, _len: usize) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::SerializeStruct,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(struct_variant) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_struct_variant(_name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::SerializeStructVariant,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(seq) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_seq(_len: Option<usize>) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::SerializeSeq,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(tuple) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_tuple(_len: usize) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::SerializeTuple,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(tuple_struct) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_tuple_struct(_name: &'static str, _len: usize) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::SerializeTupleStruct,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
(tuple_variant) => {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
serialize_tuple_variant(_name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::SerializeTupleVariant,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
};
|
||||
// every other method has no extra arguments and is for simple types
|
||||
($method_type:ident) => {
|
||||
paste::paste! {
|
||||
unsupported_serde_serialize_method_definition! {
|
||||
[<serialize_ $method_type>](_v: $method_type) -> std::result::Result<
|
||||
<Self as serde::ser::Serializer>::Ok,
|
||||
<Self as serde::ser::Serializer>::Error,
|
||||
>
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub(crate) use unsupported_serde_serialize_method_helper;
|
||||
|
||||
#[doc(hidden)]
|
||||
macro_rules! unsupported_serde_serialize_method_definition {
|
||||
// methods with generic argument
|
||||
($func:ident <$generic_arg:ident> ($($arg:ident : $ty:ty),*) -> std::result::Result<$ok:ty, $err:ty$(,)?>) => {
|
||||
#[inline]
|
||||
fn $func <$generic_arg> (self, $($arg: $ty,)*) -> std::result::Result<$ok, $err>
|
||||
where
|
||||
$generic_arg: ?Sized + Serialize {
|
||||
unimplemented!(
|
||||
"{} is not implemented for {} since it is currently not needed to serialize the metadata::Wdk struct",
|
||||
stringify!($func),
|
||||
std::any::type_name::<Self>(),
|
||||
)
|
||||
}
|
||||
};
|
||||
// methods without generic argument
|
||||
($func:ident ($($arg:ident : $ty:ty),*) -> std::result::Result<$ok:ty, $err:ty$(,)?>) => {
|
||||
#[inline]
|
||||
fn $func (self, $($arg: $ty,)*) -> std::result::Result<$ok, $err> {
|
||||
unimplemented!(
|
||||
"{} is not implemented for {} since it is currently not needed to serialize the metadata::Wdk struct",
|
||||
stringify!($func),
|
||||
std::any::type_name::<Self>(),
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub(crate) use unsupported_serde_serialize_method_definition;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
vec,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::{DriverConfig, KmdfConfig, UmdfConfig, metadata};
|
||||
|
||||
#[test]
|
||||
fn test_kmdf() {
|
||||
let wdk_metadata = metadata::Wdk {
|
||||
driver_model: DriverConfig::Kmdf(KmdfConfig {
|
||||
kmdf_version_major: 1,
|
||||
target_kmdf_version_minor: 23,
|
||||
minimum_kmdf_version_minor: Some(21),
|
||||
}),
|
||||
};
|
||||
|
||||
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
|
||||
|
||||
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
|
||||
assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
|
||||
assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "23");
|
||||
assert_eq!(output["DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"], "21");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmdf_no_minimum() {
|
||||
let wdk_metadata = metadata::Wdk {
|
||||
driver_model: DriverConfig::Kmdf(KmdfConfig {
|
||||
kmdf_version_major: 1,
|
||||
target_kmdf_version_minor: 23,
|
||||
minimum_kmdf_version_minor: None,
|
||||
}),
|
||||
};
|
||||
|
||||
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
|
||||
|
||||
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
|
||||
assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
|
||||
assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "23");
|
||||
|
||||
// `None` values are not serialized
|
||||
assert_eq!(output.get("DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmdf_with_prefix() {
|
||||
let wdk_metadata = metadata::Wdk {
|
||||
driver_model: DriverConfig::Kmdf(KmdfConfig {
|
||||
kmdf_version_major: 1,
|
||||
target_kmdf_version_minor: 33,
|
||||
minimum_kmdf_version_minor: Some(31),
|
||||
}),
|
||||
};
|
||||
|
||||
let output =
|
||||
to_map_with_prefix::<BTreeMap<_, _>>("WDK_BUILD_METADATA", &wdk_metadata).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
output["WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE"],
|
||||
"KMDF"
|
||||
);
|
||||
assert_eq!(
|
||||
output["WDK_BUILD_METADATA-DRIVER_MODEL-KMDF_VERSION_MAJOR"],
|
||||
"1"
|
||||
);
|
||||
assert_eq!(
|
||||
output["WDK_BUILD_METADATA-DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"],
|
||||
"33"
|
||||
);
|
||||
assert_eq!(
|
||||
output["WDK_BUILD_METADATA-DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"],
|
||||
"31"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kmdf_with_hashmap() {
|
||||
let wdk_metadata = metadata::Wdk {
|
||||
driver_model: DriverConfig::Kmdf(KmdfConfig {
|
||||
kmdf_version_major: 1,
|
||||
target_kmdf_version_minor: 33,
|
||||
minimum_kmdf_version_minor: Some(31),
|
||||
}),
|
||||
};
|
||||
|
||||
let output = to_map::<HashMap<_, _>>(&wdk_metadata).unwrap();
|
||||
|
||||
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
|
||||
assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
|
||||
assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "33");
|
||||
assert_eq!(output["DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"], "31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_umdf() {
|
||||
let wdk_metadata = metadata::Wdk {
|
||||
driver_model: DriverConfig::Umdf(UmdfConfig {
|
||||
umdf_version_major: 1,
|
||||
target_umdf_version_minor: 23,
|
||||
minimum_umdf_version_minor: Some(21),
|
||||
}),
|
||||
};
|
||||
|
||||
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
|
||||
|
||||
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "UMDF");
|
||||
assert_eq!(output["DRIVER_MODEL-UMDF_VERSION_MAJOR"], "1");
|
||||
assert_eq!(output["DRIVER_MODEL-TARGET_UMDF_VERSION_MINOR"], "23");
|
||||
assert_eq!(output["DRIVER_MODEL-MINIMUM_UMDF_VERSION_MINOR"], "21");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_umdf_no_minimum() {
|
||||
let wdk_metadata = metadata::Wdk {
|
||||
driver_model: DriverConfig::Umdf(UmdfConfig {
|
||||
umdf_version_major: 1,
|
||||
target_umdf_version_minor: 23,
|
||||
minimum_umdf_version_minor: None,
|
||||
}),
|
||||
};
|
||||
|
||||
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
|
||||
|
||||
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "UMDF");
|
||||
assert_eq!(output["DRIVER_MODEL-UMDF_VERSION_MAJOR"], "1");
|
||||
assert_eq!(output["DRIVER_MODEL-TARGET_UMDF_VERSION_MINOR"], "23");
|
||||
|
||||
// `None` values are not serialized
|
||||
assert_eq!(output.get("DRIVER_MODEL-MINIMUM_UMDF_VERSION_MINOR"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wdm() {
|
||||
let wdk_metadata = metadata::Wdk {
|
||||
driver_model: DriverConfig::Wdm,
|
||||
};
|
||||
|
||||
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
|
||||
|
||||
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "WDM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conflicting_keys_in_convert_serialized_output_to_map() {
|
||||
let input = vec![("KEY_NAME", "VALUE_1"), ("KEY_NAME", "VALUE_2")]
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect();
|
||||
|
||||
let err = convert_serialized_output_to_map::<BTreeMap<_, _>>(input).unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
err,
|
||||
Error::DuplicateSerializationKeys {
|
||||
key,
|
||||
value_1,
|
||||
value_2,
|
||||
} if key == "KEY_NAME" && value_1 == "VALUE_1" && value_2 == "VALUE_2"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// License: MIT OR Apache-2.0
|
||||
|
||||
//! Private module for utility code related to the cargo-make experience for
|
||||
//! building drivers.
|
||||
|
||||
use std::{
|
||||
env,
|
||||
ffi::{CStr, OsStr},
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use windows::{
|
||||
Win32::System::Registry::{
|
||||
HKEY,
|
||||
HKEY_LOCAL_MACHINE,
|
||||
KEY_READ,
|
||||
RRF_RT_REG_SZ,
|
||||
RegCloseKey,
|
||||
RegGetValueA,
|
||||
RegOpenKeyExA,
|
||||
},
|
||||
core::{PCSTR, s},
|
||||
};
|
||||
|
||||
use crate::{ConfigError, CpuArchitecture, IoError, TwoPartVersion};
|
||||
|
||||
/// Detect `WDKContentRoot` Directory. Logic is based off of Toolset.props in
|
||||
/// NI(22H2) WDK
|
||||
#[must_use]
|
||||
pub fn detect_wdk_content_root() -> Option<PathBuf> {
|
||||
// If WDKContentRoot is present in environment(ex. running in an eWDK prompt),
|
||||
// use it
|
||||
if let Ok(wdk_content_root) = env::var("WDKContentRoot") {
|
||||
let path = Path::new(wdk_content_root.as_str());
|
||||
if path.is_dir() {
|
||||
return Some(path.to_path_buf());
|
||||
}
|
||||
eprintln!(
|
||||
"WDKContentRoot was detected to be {}, but does not exist or is not a valid directory.",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
|
||||
// If MicrosoftKitRoot environment variable is set, use it to set WDKContentRoot
|
||||
if let Ok(microsoft_kit_root) = env::var("MicrosoftKitRoot") {
|
||||
let path = Path::new(microsoft_kit_root.as_str());
|
||||
|
||||
if !path.is_absolute() {
|
||||
eprintln!(
|
||||
"MicrosoftKitRoot({}) was found in environment, but is not an absolute path.",
|
||||
path.display()
|
||||
);
|
||||
} else if !path.is_dir() {
|
||||
eprintln!(
|
||||
"MicrosoftKitRoot({}) was found in environment, but does not exist or is not a \
|
||||
valid directory.",
|
||||
path.display()
|
||||
);
|
||||
} else {
|
||||
let wdk_kit_version = env::var("WDKKitVersion").unwrap_or_else(|_| "10.0".to_string());
|
||||
let path = path.join("Windows Kits").join(wdk_kit_version);
|
||||
if path.is_dir() {
|
||||
return Some(path);
|
||||
}
|
||||
eprintln!(
|
||||
"WDKContentRoot was detected to be {}, but does not exist or is not a valid \
|
||||
directory.",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed
|
||||
// Roots@KitsRoot10 registry key
|
||||
if let Some(path) = read_registry_key_string_value(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
s!(r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"),
|
||||
s!(r"KitsRoot10"),
|
||||
) {
|
||||
return Some(Path::new(path.as_str()).to_path_buf());
|
||||
}
|
||||
|
||||
// Check HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows
|
||||
// Kits\Installed Roots@KitsRoot10 registry key
|
||||
if let Some(path) = read_registry_key_string_value(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
s!(r"SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots"),
|
||||
s!(r"KitsRoot10"),
|
||||
) {
|
||||
return Some(Path::new(path.as_str()).to_path_buf());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Searches a directory and determines the latest windows SDK version in that
|
||||
/// directory
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `ConfigError::DirectoryNotFound` error if the directory provided
|
||||
/// does not exist.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the path provided is not valid Unicode.
|
||||
pub fn get_latest_windows_sdk_version(path_to_search: &Path) -> Result<String, ConfigError> {
|
||||
Ok(path_to_search
|
||||
.read_dir()
|
||||
.map_err(|source| IoError::with_path(path_to_search, source))?
|
||||
.filter_map(std::result::Result::ok)
|
||||
.map(|valid_directory_entry| valid_directory_entry.path())
|
||||
.filter(|path| {
|
||||
path.is_dir()
|
||||
&& path.file_name().is_some_and(|directory_name| {
|
||||
directory_name
|
||||
.to_str()
|
||||
.is_some_and(|directory_name| directory_name.starts_with("10."))
|
||||
})
|
||||
})
|
||||
.max() // Get the latest SDK folder in case there are multiple installed
|
||||
.ok_or(ConfigError::DirectoryNotFound {
|
||||
directory: format!(
|
||||
"Windows SDK Directory in {}",
|
||||
path_to_search.to_string_lossy()
|
||||
),
|
||||
})?
|
||||
.file_name()
|
||||
.expect("path should never terminate in ..")
|
||||
.to_str()
|
||||
.expect("directory name should always be valid Unicode")
|
||||
.to_string())
|
||||
}
|
||||
|
||||
/// Detect architecture based on cargo TARGET variable.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the `CARGO_CFG_TARGET_ARCH` environment variable is not set,
|
||||
/// or if the cargo architecture is unsupported.
|
||||
#[must_use]
|
||||
pub fn detect_cpu_architecture_in_build_script() -> CpuArchitecture {
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect(
|
||||
"Cargo should have set the CARGO_CFG_TARGET_ARCH environment variable when executing \
|
||||
build.rs",
|
||||
);
|
||||
|
||||
CpuArchitecture::try_from_cargo_str(&target_arch).unwrap_or_else(|| {
|
||||
panic!("The target architecture, {target_arch}, is currently not supported.")
|
||||
})
|
||||
}
|
||||
|
||||
/// Validates that a given string matches the WDK version format (10.xxx.yyy.zzz
|
||||
/// where xxx, yyy, and zzz are numeric and not necessarily 3 digits long).
|
||||
#[rustversion::attr(
|
||||
nightly,
|
||||
allow(
|
||||
clippy::nonminimal_bool,
|
||||
reason = "is_some_or is not stable until 1.82.0 is released on 10/17/24"
|
||||
)
|
||||
)]
|
||||
pub fn validate_wdk_version_format<S: AsRef<str>>(version_string: S) -> bool {
|
||||
let version = version_string.as_ref();
|
||||
let version_parts: Vec<&str> = version.split('.').collect();
|
||||
|
||||
// First, check if we have "10" as our first value
|
||||
if version_parts.first().is_none_or(|first| *first != "10") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now check that we have four entries.
|
||||
if version_parts.len() != 4 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally, confirm each part is numeric.
|
||||
if !version_parts
|
||||
.iter()
|
||||
.all(|version_part| version_part.parse::<i32>().is_ok())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns the version number from a full WDK version string.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function returns a [`ConfigError::WdkVersionStringFormatError`] if the
|
||||
/// version string provided is ill-formed.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the WDK version format validation function is ever changed not to
|
||||
/// validate that there are 4 substrings in the WDK version string, this
|
||||
/// function will panic.
|
||||
pub fn get_wdk_version_number<S: AsRef<str> + ToString + ?Sized>(
|
||||
version_string: &S,
|
||||
) -> Result<String, ConfigError> {
|
||||
if !validate_wdk_version_format(version_string) {
|
||||
return Err(ConfigError::WdkVersionStringFormatError {
|
||||
version: version_string.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let version_substrings = version_string.as_ref().split('.').collect::<Vec<&str>>();
|
||||
let version_substring = version_substrings.get(2).expect(
|
||||
"WDK version string was validated to be well-formatted, but we couldn't get the \
|
||||
appropriate substring!",
|
||||
);
|
||||
Ok((*version_substring).to_string())
|
||||
}
|
||||
|
||||
/// Read a string value from a registry key
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `key_handle` - a [`windows::Win32::System::Registry::HKEY`] to the base
|
||||
/// key
|
||||
/// * `sub_key` - a [`windows::core::PCSTR`] that is the path of a registry key
|
||||
/// relative to the `key_handle` argument
|
||||
/// * `value` - a [`windows::core::PCSTR`] that is the name of the string
|
||||
/// registry value to read
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if read value isn't valid UTF-8 or if the opened regkey could not be
|
||||
/// closed
|
||||
fn read_registry_key_string_value(
|
||||
key_handle: HKEY,
|
||||
sub_key: PCSTR,
|
||||
value: PCSTR,
|
||||
) -> Option<String> {
|
||||
let mut opened_key_handle = HKEY::default();
|
||||
let mut len = 0;
|
||||
if
|
||||
// SAFETY: `&mut opened_key_handle` is coerced to a &raw mut, so the address passed as the
|
||||
// argument is always valid. `&mut opened_key_handle` is coerced to a pointer of the correct
|
||||
// type.
|
||||
unsafe { RegOpenKeyExA(key_handle, sub_key, 0, KEY_READ, &raw mut opened_key_handle) }
|
||||
.is_ok()
|
||||
{
|
||||
if
|
||||
// SAFETY: `opened_key_handle` is valid key opened with the `KEY_QUERY_VALUE` access right
|
||||
// (included in `KEY_READ`). `&mut len` is coerced to a &raw mut, so the address passed as
|
||||
// the argument is always valid. `&mut len` is coerced to a pointer of the correct
|
||||
// type.
|
||||
unsafe {
|
||||
RegGetValueA(
|
||||
opened_key_handle,
|
||||
None,
|
||||
value,
|
||||
RRF_RT_REG_SZ,
|
||||
None,
|
||||
None,
|
||||
Some(&raw mut len),
|
||||
)
|
||||
}
|
||||
.is_ok()
|
||||
{
|
||||
let mut buffer = vec![0u8; len as usize];
|
||||
if
|
||||
// SAFETY: `opened_key_handle` is valid key opened with the `KEY_QUERY_VALUE` access
|
||||
// right (included in `KEY_READ`). `&mut buffer` is coerced to a &raw mut,
|
||||
// so the address passed as the argument is always valid. `&mut buffer` is
|
||||
// coerced to a pointer of the correct type. `&mut len` is coerced to a &raw
|
||||
// mut, so the address passed as the argument is always valid. `&mut len` is
|
||||
// coerced to a pointer of the correct type.
|
||||
unsafe {
|
||||
RegGetValueA(
|
||||
opened_key_handle,
|
||||
None,
|
||||
value,
|
||||
RRF_RT_REG_SZ,
|
||||
None,
|
||||
Some(buffer.as_mut_ptr().cast()),
|
||||
Some(&raw mut len),
|
||||
)
|
||||
}
|
||||
.is_ok()
|
||||
{
|
||||
// SAFETY: `opened_key_handle` is valid opened key that was opened by
|
||||
// `RegOpenKeyExA`
|
||||
unsafe { RegCloseKey(opened_key_handle) }
|
||||
.ok()
|
||||
.expect("opened_key_handle should be successfully closed");
|
||||
return Some(
|
||||
CStr::from_bytes_with_nul(&buffer[..len as usize])
|
||||
.expect(
|
||||
"RegGetValueA should always return a null-terminated string. The read \
|
||||
string (REG_SZ) from the registry should not contain any interior \
|
||||
nulls.",
|
||||
)
|
||||
.to_str()
|
||||
.expect("Registry value should be parseable as UTF8")
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `opened_key_handle` is valid opened key that was opened by
|
||||
// `RegOpenKeyExA`
|
||||
unsafe { RegCloseKey(opened_key_handle) }
|
||||
.ok()
|
||||
.expect("opened_key_handle should be successfully closed");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Detects the Windows SDK version from the `Version_Number` env var or from
|
||||
/// the WDK content's `Lib` directory.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `wdk_content_root` - A reference to the path where the WDK content root is
|
||||
/// located.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `ConfigError::DirectoryNotFound` error if the directory provided
|
||||
/// does not exist.
|
||||
pub fn detect_windows_sdk_version(wdk_content_root: &Path) -> Result<String, ConfigError> {
|
||||
env::var("Version_Number")
|
||||
.or_else(|_| get_latest_windows_sdk_version(&wdk_content_root.join("Lib")))
|
||||
}
|
||||
|
||||
/// Finds the maximum version in a directory where subdirectories are named with
|
||||
/// version format "x.y"
|
||||
pub fn find_max_version_in_directory<P: AsRef<Path>>(
|
||||
directory_path: P,
|
||||
) -> Result<TwoPartVersion, IoError> {
|
||||
let directory_path = directory_path.as_ref();
|
||||
std::fs::read_dir(directory_path)
|
||||
.map_err(|source| IoError::with_path(directory_path, source))?
|
||||
.flatten()
|
||||
.filter(|entry| entry.file_type().is_ok_and(|ft| ft.is_dir()))
|
||||
.filter_map(|entry| entry.file_name().to_str()?.parse().ok())
|
||||
.max()
|
||||
.ok_or_else(|| {
|
||||
IoError::with_path(
|
||||
directory_path,
|
||||
io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("Maximum version in {} not found", directory_path.display()),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Safely sets an environment variable. Will not compile if crate is not
|
||||
/// targeted for Windows.
|
||||
///
|
||||
/// This function provides a safe wrapper around [`std::env::set_var`] that
|
||||
/// became unsafe in Rust 2024 edition.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if key is empty, contains an ASCII equals sign '='
|
||||
/// or the NUL character '\0', or when value contains the NUL character.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn set_var<K, V>(key: K, value: V)
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
// SAFETY: this function is only conditionally compiled for windows targets, and
|
||||
// env::set_var is always safe for windows targets
|
||||
unsafe {
|
||||
env::set_var(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn set_var<K, V>(_key: K, _value: V)
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
compile_error!(
|
||||
"windows-drivers-rs is designed to be run on a Windows host machine in a WDK environment. \
|
||||
Please build using a Windows target."
|
||||
);
|
||||
}
|
||||
|
||||
/// Safely removes an environment variable. Will not compile if crate is not
|
||||
/// targeted for Windows.
|
||||
///
|
||||
/// This function provides a safe wrapper around [`std::env::remove_var`] that
|
||||
/// became unsafe in Rust 2024 edition.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if key is empty, contains an ASCII equals sign '='
|
||||
/// or the NUL character '\0', or when value contains the NUL character.
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn remove_var<K>(key: K)
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
{
|
||||
// SAFETY: this function is only conditionally compiled for windows targets, and
|
||||
// env::remove_var is always safe for windows targets
|
||||
unsafe {
|
||||
env::remove_var(key);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn remove_var<K>(_key: K)
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
{
|
||||
compile_error!(
|
||||
"windows-drivers-rs is designed to be run on a Windows host machine in a WDK environment. \
|
||||
Please build using a Windows target."
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use assert_fs::prelude::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
// Function with_clean_env clears the inputted environment variable and runs the
|
||||
// closure
|
||||
fn with_clean_env<F>(key: &str, f: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
let original = env::var(key).ok();
|
||||
|
||||
// SAFETY: We have verified that this is built for a Windows host due to no
|
||||
// compile errors from building `set_var`.
|
||||
unsafe {
|
||||
env::remove_var(key);
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
if let Some(val) = &original {
|
||||
// SAFETY: We have verified that this is built for a Windows host due to no
|
||||
// compile errors from building `set_var`.
|
||||
unsafe {
|
||||
env::set_var(key, val);
|
||||
}
|
||||
} else {
|
||||
// SAFETY: We have verified that this is built for a Windows host due to no
|
||||
// compile errors from building `set_var`.
|
||||
unsafe {
|
||||
env::remove_var(key);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(env::var(key).ok() == original);
|
||||
}
|
||||
|
||||
mod read_registry_key_string_value {
|
||||
use windows::Win32::UI::Shell::{
|
||||
FOLDERID_ProgramFiles,
|
||||
KF_FLAG_DEFAULT,
|
||||
SHGetKnownFolderPath,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn read_reg_key_programfilesdir() {
|
||||
let program_files_dir =
|
||||
// SAFETY: FOLDERID_ProgramFiles is a constant from the windows crate, so the pointer (resulting from its reference being coerced) is always valid to be dereferenced
|
||||
unsafe { SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, None) }
|
||||
.expect("Program Files Folder should always resolve via SHGetKnownFolderPath.");
|
||||
|
||||
assert_eq!(
|
||||
read_registry_key_string_value(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
s!(r"SOFTWARE\Microsoft\Windows\CurrentVersion"),
|
||||
s!("ProgramFilesDir")
|
||||
),
|
||||
Some(
|
||||
// SAFETY: program_files_dir pointer stays valid for reads up until and
|
||||
// including its terminating null
|
||||
unsafe { program_files_dir.to_string() }
|
||||
.expect("Path resolved from FOLDERID_ProgramFiles should be valid UTF16.")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_wdk_strings() {
|
||||
let test_string = "10.0.12345.0";
|
||||
assert_eq!(
|
||||
get_wdk_version_number(test_string).ok(),
|
||||
Some("12345".to_string())
|
||||
);
|
||||
let test_string = "10.0.5.0";
|
||||
assert_eq!(
|
||||
get_wdk_version_number(test_string).ok(),
|
||||
Some("5".to_string())
|
||||
);
|
||||
let test_string = "10.0.0.0";
|
||||
assert_eq!(
|
||||
get_wdk_version_number(test_string).ok(),
|
||||
Some("0".to_string())
|
||||
);
|
||||
let test_string = "11.0.0.0";
|
||||
assert_eq!(
|
||||
format!("{}", get_wdk_version_number(test_string).err().unwrap()),
|
||||
format!(
|
||||
"the WDK version string provided ({}) was not in a valid format",
|
||||
test_string
|
||||
)
|
||||
);
|
||||
let test_string = "10.0.12345.0.0";
|
||||
assert_eq!(
|
||||
format!("{}", get_wdk_version_number(test_string).err().unwrap()),
|
||||
format!(
|
||||
"the WDK version string provided ({}) was not in a valid format",
|
||||
test_string
|
||||
)
|
||||
);
|
||||
let test_string = "10.0.12345.a";
|
||||
assert_eq!(
|
||||
format!("{}", get_wdk_version_number(test_string).err().unwrap()),
|
||||
format!(
|
||||
"the WDK version string provided ({}) was not in a valid format",
|
||||
test_string
|
||||
)
|
||||
);
|
||||
let test_string = "10.0.12345";
|
||||
assert_eq!(
|
||||
format!("{}", get_wdk_version_number(test_string).err().unwrap()),
|
||||
format!(
|
||||
"the WDK version string provided ({}) was not in a valid format",
|
||||
test_string
|
||||
)
|
||||
);
|
||||
let test_string = "10.0.1234!5.0";
|
||||
assert_eq!(
|
||||
format!("{}", get_wdk_version_number(test_string).err().unwrap()),
|
||||
format!(
|
||||
"the WDK version string provided ({}) was not in a valid format",
|
||||
test_string
|
||||
)
|
||||
);
|
||||
let test_string = "Not a real version!";
|
||||
assert_eq!(
|
||||
format!("{}", get_wdk_version_number(test_string).err().unwrap()),
|
||||
format!(
|
||||
"the WDK version string provided ({}) was not in a valid format",
|
||||
test_string
|
||||
)
|
||||
);
|
||||
let test_string = "";
|
||||
assert_eq!(
|
||||
format!("{}", get_wdk_version_number(test_string).err().unwrap()),
|
||||
format!(
|
||||
"the WDK version string provided ({}) was not in a valid format",
|
||||
test_string
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
mod find_max_version_in_directory {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_directory() {
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
let result = find_max_version_in_directory(temp_dir.path());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().source.kind(),
|
||||
std::io::ErrorKind::NotFound
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonexistent_directory() {
|
||||
let nonexistent_path = std::path::Path::new("/this/path/does/not/exist");
|
||||
let result = find_max_version_in_directory(nonexistent_path);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_version_directories() {
|
||||
// Single valid version directory
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("3.14").create_dir_all().unwrap();
|
||||
temp_dir.child("folder1").create_dir_all().unwrap();
|
||||
assert_eq!(
|
||||
find_max_version_in_directory(temp_dir.path()).unwrap(),
|
||||
TwoPartVersion(3, 14)
|
||||
);
|
||||
// Multiple valid version directories
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("1.2").create_dir_all().unwrap();
|
||||
temp_dir.child("1.10").create_dir_all().unwrap();
|
||||
temp_dir.child("2.0").create_dir_all().unwrap();
|
||||
temp_dir.child("not_a_version").create_dir_all().unwrap();
|
||||
assert_eq!(
|
||||
find_max_version_in_directory(temp_dir.path()).unwrap(),
|
||||
TwoPartVersion(2, 0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_version_directories() {
|
||||
// Single invalid directory
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("folder1").create_dir_all().unwrap();
|
||||
let result = find_max_version_in_directory(temp_dir.path());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().source.kind(),
|
||||
std::io::ErrorKind::NotFound
|
||||
);
|
||||
|
||||
// Multiple invalid directories
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("folder1").create_dir_all().unwrap();
|
||||
temp_dir.child("1.2.3").create_dir_all().unwrap(); // Too many dots
|
||||
temp_dir.child("a.b").create_dir_all().unwrap(); // Non-numeric
|
||||
temp_dir.child("1").create_dir_all().unwrap(); // No dot
|
||||
temp_dir.child("1.").create_dir_all().unwrap(); // Missing minor
|
||||
temp_dir.child(".5").create_dir_all().unwrap(); // Missing major
|
||||
let result = find_max_version_in_directory(temp_dir.path());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().source.kind(),
|
||||
std::io::ErrorKind::NotFound
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn major_version_priority() {
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("1.999").create_dir_all().unwrap();
|
||||
temp_dir.child("2.0").create_dir_all().unwrap();
|
||||
temp_dir.child("1.1000").create_dir_all().unwrap();
|
||||
assert_eq!(
|
||||
find_max_version_in_directory(temp_dir.path()).unwrap(),
|
||||
TwoPartVersion(2, 0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minor_version_comparison() {
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("1.5").create_dir_all().unwrap();
|
||||
temp_dir.child("1.10").create_dir_all().unwrap();
|
||||
temp_dir.child("1.2").create_dir_all().unwrap();
|
||||
assert_eq!(
|
||||
find_max_version_in_directory(temp_dir.path()).unwrap(),
|
||||
TwoPartVersion(1, 10)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_versions() {
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("0.0").create_dir_all().unwrap();
|
||||
temp_dir.child("0.1").create_dir_all().unwrap();
|
||||
assert_eq!(
|
||||
find_max_version_in_directory(temp_dir.path()).unwrap(),
|
||||
TwoPartVersion(0, 1)
|
||||
);
|
||||
temp_dir.child("1.0").create_dir_all().unwrap();
|
||||
assert_eq!(
|
||||
find_max_version_in_directory(temp_dir.path()).unwrap(),
|
||||
TwoPartVersion(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_valid_and_invalid_entries() {
|
||||
let temp_dir = assert_fs::TempDir::new().unwrap();
|
||||
temp_dir.child("1.5").create_dir_all().unwrap();
|
||||
temp_dir.child("2.0").create_dir_all().unwrap();
|
||||
temp_dir.child("invalid").create_dir_all().unwrap();
|
||||
temp_dir.child("1.2.3").create_dir_all().unwrap(); // Invalid: too many dots
|
||||
temp_dir.child("a.b").create_dir_all().unwrap(); // Invalid: non-numeric
|
||||
temp_dir.child("not_version").touch().unwrap(); // File: ignored
|
||||
temp_dir.child("3.0").touch().unwrap(); // File: ignored
|
||||
// Should find the maximum among valid version directories only
|
||||
assert_eq!(
|
||||
find_max_version_in_directory(temp_dir.path()).unwrap(),
|
||||
TwoPartVersion(2, 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod safe_env_vars {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn set_var_and_remove_var() {
|
||||
let key = "WDK_BUILD_TEST_VAR";
|
||||
with_clean_env(key, || {
|
||||
set_var(key, "test_value");
|
||||
assert_eq!(env::var(key).unwrap(), "test_value");
|
||||
remove_var(key);
|
||||
assert!(env::var(key).is_err());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user