diff --git a/crates/cargo-wdk/README.md b/crates/cargo-wdk/README.md index 277a62507..ad2d0a106 100644 --- a/crates/cargo-wdk/README.md +++ b/crates/cargo-wdk/README.md @@ -84,6 +84,9 @@ Driver Signing: Inf2Cat Options: --inf2cat-args Custom arguments to pass to `inf2cat` when generating the catalog file, e.g. `--inf2cat-args '/os:10_x64,10_GE_X64 /uselocaltime'` +InfVerif Options: + --infverif-args Custom arguments to pass to `infverif` when validating the INF, e.g. `--infverif-args '/rulever 10.0.22621 /info'` + Feature Selection: --all-features Activate all available features --no-default-features Do not activate the `default` feature @@ -112,6 +115,10 @@ If you have a workspace with a mix of sample and non-sample driver projects, the To target a specific set of Windows versions or to customize the behaviour of `inf2cat` in any other way, pass `--inf2cat-args` with a string of the arguments to forward to `inf2cat`. `cargo-wdk` itself provides the `/driver` argument so do not include it or its alias `/drv`. +#### Customizing `infverif` arguments + +To customize the behaviour of `infverif`, pass `--infverif-args` with arguments to forward to `infverif`. Args `-h`, `-w`, `-u` and paths to `.INF` files are not allowed because they are always supplied by `cargo-wdk` itself. + #### Signing and Verification The `build` command has a `--sign-mode` flag that controls how driver artifacts are signed. It accepts the following values: diff --git a/crates/cargo-wdk/src/actions/build/mod.rs b/crates/cargo-wdk/src/actions/build/mod.rs index 709cd38d0..e02a3311e 100644 --- a/crates/cargo-wdk/src/actions/build/mod.rs +++ b/crates/cargo-wdk/src/actions/build/mod.rs @@ -80,6 +80,7 @@ pub struct BuildActionParams<'a> { pub target_arch: Option, pub sign_mode: SignMode, pub inf2cat_args: Option>, + pub infverif_args: Option>, pub is_sample_class: bool, pub locked: bool, pub target_platform: TargetPlatform, @@ -95,6 +96,7 @@ pub struct BuildAction<'a> { target_arch: Option, sign_mode: SignMode, inf2cat_args: Option>, + infverif_args: Option>, is_sample_class: bool, locked: bool, target_platform: TargetPlatform, @@ -144,6 +146,7 @@ impl<'a> BuildAction<'a> { target_arch: params.target_arch, sign_mode: params.sign_mode.clone(), inf2cat_args: params.inf2cat_args.clone(), + infverif_args: params.infverif_args.clone(), is_sample_class: params.is_sample_class, locked: params.locked, target_platform: params.target_platform, @@ -453,6 +456,7 @@ impl<'a> BuildAction<'a> { target_arch: &target_arch, sign_mode: self.sign_mode.clone(), inf2cat_args: self.inf2cat_args.clone(), + infverif_args: self.infverif_args.clone(), sample_class: self.is_sample_class, driver_model, target_platform: self.target_platform, diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index b479a9b32..0b1e0be6f 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -85,6 +85,7 @@ pub struct PackageTaskParams<'a> { pub target_arch: &'a CpuArchitecture, pub sign_mode: SignMode, pub inf2cat_args: Option>, + pub infverif_args: Option>, pub sample_class: bool, pub driver_model: DriverConfig, pub target_platform: TargetPlatform, @@ -95,6 +96,7 @@ pub struct PackageTask<'a> { package_name: String, sign_mode: SignMode, inf2cat_args: Option>, + infverif_args: Option>, sample_class: bool, // src paths @@ -207,6 +209,7 @@ impl<'a> PackageTask<'a> { package_name, sign_mode: params.sign_mode, inf2cat_args: params.inf2cat_args, + infverif_args: params.infverif_args, sample_class: params.sample_class, src_inx_file_path, src_driver_binary_file_path, @@ -642,6 +645,9 @@ impl<'a> PackageTask<'a> { if self.sample_class { args.push(additional_args); } + if let Some(infverif_args) = &self.infverif_args { + args.extend(infverif_args.iter().map(String::as_str)); + } args.push(&inf_path); if let Err(e) = self.command_exec.run("infverif", &args, None, None) { @@ -737,6 +743,7 @@ mod tests { signtool_args: Vec::new(), }, inf2cat_args: None, + infverif_args: None, target_platform: TargetPlatform::Universal, }; let dest_root = target_dir.join(format!("{package_name}_package")); @@ -811,6 +818,7 @@ mod tests { signtool_args: Vec::new(), }, inf2cat_args: None, + infverif_args: None, target_platform: TargetPlatform::Universal, }; @@ -842,6 +850,7 @@ mod tests { signtool_args: Vec::new(), }, inf2cat_args: None, + infverif_args: None, target_platform: TargetPlatform::Universal, }; @@ -882,6 +891,7 @@ mod tests { signtool_args: Vec::new(), }, inf2cat_args: None, + infverif_args: None, target_platform: TargetPlatform::Universal, }; @@ -940,6 +950,7 @@ mod tests { signtool_args: Vec::new(), }, inf2cat_args: None, + infverif_args: None, target_platform: TargetPlatform::Universal, }; @@ -985,6 +996,7 @@ mod tests { signtool_args: Vec::new(), }, inf2cat_args: Some(Vec::new()), + infverif_args: None, target_platform: TargetPlatform::Universal, }; @@ -1030,6 +1042,7 @@ mod tests { "/os:10_x64,10_CO_X64".to_string(), "/verbose".to_string(), ]), + infverif_args: None, target_platform: TargetPlatform::Universal, }; @@ -1088,6 +1101,7 @@ mod tests { sample_class: false, sign_mode: SignMode::Off, inf2cat_args: None, + infverif_args: None, target_platform: TargetPlatform::Universal, }; PackageTask::new(params, wdk_build, command_exec, fs) @@ -1285,6 +1299,7 @@ mod tests { ], }, inf2cat_args: None, + infverif_args: None, target_platform: TargetPlatform::Universal, }; let task = PackageTask::new(params, &wdk_build, &command_exec, &fs); @@ -1312,6 +1327,7 @@ mod tests { sample_class: false, sign_mode: SignMode::Off, inf2cat_args: None, + infverif_args: None, target_platform, }; @@ -1374,6 +1390,63 @@ mod tests { ); } + #[test] + fn run_infverif_with_custom_args_forwards_them_verbatim() { + let working_dir = PathBuf::from("C:/abs/driver"); + let target_dir = PathBuf::from("C:/abs/driver/target/debug"); + let arch = CpuArchitecture::Amd64; + + let params = PackageTaskParams { + package_name: "driver", + working_dir: &working_dir, + target_dir: &target_dir, + target_arch: &arch, + driver_model: DriverConfig::Kmdf(KmdfConfig::default()), + sample_class: true, + sign_mode: SignMode::Off, + inf2cat_args: None, + infverif_args: Some(vec![ + "/rulever".to_string(), + "10.0.22621".to_string(), + "/info".to_string(), + ]), + target_platform: TargetPlatform::Universal, + }; + + let fs = Fs::default(); + let mut wdk_build = WdkBuild::default(); + wdk_build + .expect_detect_wdk_build_number() + .once() + .returning(|| Ok(26101)); + + let expected_args_before_inf = ["/v", "/u", "/samples", "/rulever", "10.0.22621", "/info"]; + let expected_inf_path = target_dir + .join("driver_package") + .join("driver.inf") + .to_string_lossy() + .to_string(); + let mut command_exec = CommandExec::default(); + command_exec + .expect_run() + .withf(move |cmd: &str, args: &[&str], _, _| { + cmd == "infverif" + && args[..args.len() - 1] == expected_args_before_inf + && args[args.len() - 1] == expected_inf_path + }) + .once() + .returning(|_, _, _, _| { + Ok(Output { + status: ExitStatus::default(), + stdout: vec![], + stderr: vec![], + }) + }); + + let task = PackageTask::new(params, &wdk_build, &command_exec, &fs); + assert!(task.run_infverif().is_ok()); + } + mod named_mutex { use std::{ ffi::CString, diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 75ab50984..808334e70 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -1754,6 +1754,7 @@ fn initialize_build_action<'a>( target_arch, sign_mode, inf2cat_args: None, + infverif_args: None, is_sample_class: sample_class, locked: test_build_action.locked, target_platform: TargetPlatform::Universal, diff --git a/crates/cargo-wdk/src/cli.rs b/crates/cargo-wdk/src/cli.rs index a0d581e78..1ed472984 100644 --- a/crates/cargo-wdk/src/cli.rs +++ b/crates/cargo-wdk/src/cli.rs @@ -164,6 +164,18 @@ pub struct BuildArgs { )] pub inf2cat_args: Option, + /// Custom arguments to pass to `infverif` when validating the INF, + /// e.g. `--infverif-args '/rulever 10.0.22621 /info'`. + #[arg( + long, + value_name = "ARGS", + // `infverif` args can be `-` prefixed. + allow_hyphen_values = true, + value_parser = parse_passthrough_args, + help_heading = "InfVerif Options" + )] + pub infverif_args: Option, + /// Assert that `Cargo.lock` will remain unchanged #[arg(long)] pub locked: bool, @@ -228,6 +240,37 @@ impl BuildArgs { } Ok(Some(args)) } + + /// Resolves the arguments to forward to `infverif`. Rejects the arguments + /// cargo-wdk supplies itself: the mode flags derived from the + /// `--target-platform` option and the INF file path. + fn infverif_args(&self) -> Result>, clap::Error> { + const MODE_FLAGS: [&str; 3] = ["h", "w", "u"]; + + let Some(args) = self.infverif_args.clone().map(|parsed| parsed.0) else { + return Ok(None); + }; + for arg in &args { + let mode_flag = arg.trim_start_matches(['/', '-']).to_ascii_lowercase(); + let reason = if MODE_FLAGS.contains(&mode_flag.as_str()) { + format!( + "cargo-wdk derives the mode flag `{arg}` from the `--target-platform` option" + ) + } else if Path::new(arg) + .extension() + .is_some_and(|extension| extension.eq_ignore_ascii_case("inf")) + { + "cargo-wdk supplies the INF file path itself".to_string() + } else { + continue; + }; + return Err(Cli::command().error( + ErrorKind::ArgumentConflict, + format!("`--infverif-args` must not contain `{arg}`; {reason}"), + )); + } + Ok(Some(args)) + } } /// `value_parser` for passthrough tool arguments: tokenizes the raw string @@ -355,6 +398,7 @@ impl Cli { Subcmd::Build(cli_args) => { let sign_mode = cli_args.sign_mode()?; let inf2cat_args = cli_args.inf2cat_args()?; + let infverif_args = cli_args.infverif_args()?; BuildAction::new( &BuildActionParams { working_dir: Path::new("."), // Using current dir as working dir @@ -362,6 +406,7 @@ impl Cli { target_arch: cli_args.target_arch, sign_mode, inf2cat_args, + infverif_args, is_sample_class: cli_args.sample, locked: cli_args.locked, target_platform: cli_args.target_platform.into(), @@ -556,6 +601,70 @@ mod tests { Some(vec!["/os:10_x64".to_string(), "/uselocaltime".to_string()]) ); } + + fn assert_infverif_args_rejected(value: &str, expected_reason: &str) { + let args = parse_build_args(&["--infverif-args", value]).expect("args should parse"); + let err = args + .infverif_args() + .expect_err("reserved argument should be rejected"); + assert!( + err.to_string().contains(expected_reason), + "unexpected error for {value:?}: {err}" + ); + } + + #[test] + fn infverif_args_rejects_mode_flags() { + for (value, mode_flag) in [ + ("/h", "/h"), + ("-w", "-w"), + ("/U", "/U"), + ("/info -w", "-w"), + ("-info /w", "/w"), + ("/rulever 10.0.22621 -h /info /w pkg.inf", "-h"), + ] { + assert_infverif_args_rejected( + value, + &format!( + "cargo-wdk derives the mode flag `{mode_flag}` from the \ + `--target-platform` option" + ), + ); + } + } + + #[test] + fn infverif_args_rejects_inf_paths() { + for value in [ + "extra.inf", + "/info C:\\pkg\\other.INF", + "-info C:\\pkg\\other.INF", + ] { + assert_infverif_args_rejected(value, "cargo-wdk supplies the INF file path itself"); + } + } + + #[test] + fn infverif_args_allows_other_args() { + for (value, expected) in [ + ( + "/rulever 10.0.22621 -info", + ["/rulever", "10.0.22621", "-info"], + ), + ( + "-rulever 10.0.22621 /info", + ["-rulever", "10.0.22621", "/info"], + ), + ] { + let args = + parse_build_args(&["--infverif-args", value]).expect("args should parse"); + assert_eq!( + args.infverif_args().expect("should resolve"), + Some(expected.map(str::to_string).to_vec()), + "unexpected args for {value:?}" + ); + } + } } mod parse_passthrough_args { diff --git a/crates/cargo-wdk/tests/build_command_test.rs b/crates/cargo-wdk/tests/build_command_test.rs index 020950750..6f22a8a35 100644 --- a/crates/cargo-wdk/tests/build_command_test.rs +++ b/crates/cargo-wdk/tests/build_command_test.rs @@ -745,6 +745,25 @@ fn kmdf_driver_with_custom_inf2cat_args_builds_successfully() { ); } +#[test] +fn kmdf_driver_with_custom_infverif_args_builds_successfully() { + let stderr = clean_build_and_verify_project( + "kmdf", + "kmdf-driver", + None, + None, + None, + None, + None, + None, + Some(&["--infverif-args", "-rulever 10.0.22621 /stampinf", "-v"]), + ); + assert!( + stderr.contains("\"-rulever\", \"10.0.22621\", \"/stampinf\""), + "expected `--infverif-args` to be forwarded to `infverif`; stderr:\n{stderr}" + ); +} + #[allow(clippy::too_many_arguments)] fn clean_build_and_verify_project( driver_type: &str,