From 8c5c99608a57c34bacf5e2f493e1e3c0697f2a98 Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Thu, 18 Jan 2024 14:36:47 -0800 Subject: [PATCH] feat: support multiple drivers (of same type) in same cargo workspace --- crates/wdk-build/src/cargo_make.rs | 76 ++++++- rust-driver-makefile.toml | 316 +++++++++++++++++------------ 2 files changed, 257 insertions(+), 135 deletions(-) diff --git a/crates/wdk-build/src/cargo_make.rs b/crates/wdk-build/src/cargo_make.rs index 4162fa16..f8c9b600 100644 --- a/crates/wdk-build/src/cargo_make.rs +++ b/crates/wdk-build/src/cargo_make.rs @@ -1,11 +1,14 @@ // Copyright (c) Microsoft Corporation // License: MIT OR Apache-2.0 -//! This module provides argument parsing functionality used by -//! `rust-driver-makefile.toml` to validate and forward arguments common to -//! cargo commands. It uses a combination of `clap` and `clap_cargo` to provide -//! a CLI very close to cargo's own, but only exposes the arguments supported by -//! `rust-driver-makefile.toml`. Help text and other `clap::Arg` +//! This module provides functions used in the rust scripts in +//! `rust-driver-makefile.toml`. This includes argument parsing functionality +//! used by `rust-driver-makefile.toml` to validate and forward arguments common +//! to cargo commands. It uses a combination of `clap` and `clap_cargo` to +//! provide a CLI very close to cargo's own, but only exposes the arguments +//! supported by `rust-driver-makefile.toml`. + +use std::path::{Path, PathBuf}; use clap::{Args, Parser}; @@ -26,6 +29,7 @@ const CARGO_MAKE_CARGO_PROFILE_ENV_VAR: &str = "CARGO_MAKE_CARGO_PROFILE"; const CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY_ENV_VAR: &str = "CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY"; const CARGO_MAKE_RUST_DEFAULT_TOOLCHAIN_ENV_VAR: &str = "CARGO_MAKE_RUST_DEFAULT_TOOLCHAIN"; +const CARGO_MAKE_CRATE_FS_NAME_ENV_VAR: &str = "CARGO_MAKE_CRATE_FS_NAME"; const WDK_BUILD_OUTPUT_DIRECTORY_ENV_VAR: &str = "WDK_BUILD_OUTPUT_DIRECTORY"; /// `clap` uses an exit code of 2 for usage errors: @@ -408,7 +412,6 @@ pub fn validate_and_forward_args() { /// /// This function returns a [`ConfigError::WDKContentRootDetectionError`] if the /// WDK content root directory could not be found. -/// Sets up the path for the WDK build environment. /// /// # Panics /// @@ -478,6 +481,67 @@ pub fn setup_path() -> Result<(), ConfigError> { Ok(()) } +/// Returns the path to the WDK build output directory for the current +/// cargo-make flow +/// +/// # Panics +/// +/// This function will panic if the `WDK_BUILD_OUTPUT_DIRECTORY` environment +/// variable is not set +#[must_use] +pub fn get_wdk_build_output_directory() -> PathBuf { + PathBuf::from( + std::env::var("WDK_BUILD_OUTPUT_DIRECTORY") + .expect("WDK_BUILD_OUTPUT_DIRECTORY should have been set by the wdk-build-init task"), + ) +} + +/// Returns the name of the current cargo package cargo-make is processing +/// +/// # Panics +/// +/// This function will panic if the `CARGO_MAKE_CRATE_FS_NAME` environment +/// variable is not set +#[must_use] +pub fn get_current_package_name() -> String { + std::env::var(CARGO_MAKE_CRATE_FS_NAME_ENV_VAR).unwrap_or_else(|_| { + panic!( + "{} should be set by cargo-make", + &CARGO_MAKE_CRATE_FS_NAME_ENV_VAR + ) + }) +} + +/// Copies the file or directory at `path_to_copy` to the Driver Package folder +/// +/// # Errors +/// +/// This function returns a [`ConfigError::IoError`] if the it encouters IO +/// errors while copying the file or creating the directory +/// +/// # Panics +/// +/// This function will panic if `path_to_copy` does end with a valid file or +/// directory name +pub fn copy_to_driver_package_folder>(path_to_copy: P) -> Result<(), ConfigError> { + let path_to_copy = path_to_copy.as_ref(); + + let package_folder_path = + get_wdk_build_output_directory().join(format!("{}_package", get_current_package_name())); + if !package_folder_path.exists() { + std::fs::create_dir(&package_folder_path)?; + } + + let destination_path = package_folder_path.join( + path_to_copy + .file_name() + .expect("path_to_copy should always end with a valid file or directory name"), + ); + std::fs::copy(path_to_copy, destination_path)?; + + Ok(()) +} + fn configure_wdf_build_output_dir(target_arg: &Option) { let cargo_make_cargo_profile = std::env::var(CARGO_MAKE_CARGO_PROFILE_ENV_VAR).unwrap_or_else(|_| { diff --git a/rust-driver-makefile.toml b/rust-driver-makefile.toml index 04b4f97c..cf1d1f5f 100644 --- a/rust-driver-makefile.toml +++ b/rust-driver-makefile.toml @@ -2,7 +2,7 @@ # FIXME: this flow is based on the signing process of a KMDF PNP driver. There should be different flows availabe for different types of drivers as outlined in https://learn.microsoft.com/en-us/windows-hardware/drivers/install/test-signing-driver-packages [config] -min_version = "0.37.8" +# min_version = "0.37.8" init_task = "wdk-build-init" reduce_output = false @@ -82,41 +82,80 @@ wdk_build::cargo_make::validate_and_forward_args(); wdk_build::cargo_make::setup_path()?; ''' -[tasks.help] -workspace = false -env = { "TRIGGER_HELP" = "1" } -run_task = "wdk-build-init" - [tasks.copy-inx-to-output] +private = true script_runner = "@rust" +script_runner_args = [ + "--base-path", + "${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}", +] script = ''' -use std::path::PathBuf; - -let crate_name = std::env::var("CARGO_MAKE_CRATE_FS_NAME").expect("CARGO_MAKE_CRATE_FS_NAME should be set by cargo-make"); -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 output_directory = std::env::var("WDK_BUILD_OUTPUT_DIRECTORY").expect("WDK_BUILD_OUTPUT_DIRECTORY should be set by cargo-make via the env section of rust-driver-makefile.toml"); +//! ```cargo +//! [dependencies] +//! wdk-build = { path = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] -let output_folder_path = PathBuf::from(&output_directory); +// 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 source_file = format!("{cargo_make_working_directory}/{crate_name}.inx"); -let destination_file = format!("{output_directory}/{crate_name}.inf"); -std::fs::copy(&source_file, &destination_file).expect(&format!("copy of '{source_file}' file to '{destination_file}' file should succeed")); +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::(); + +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-sys-file] private = true dependencies = ["build"] script_runner = "@rust" +script_runner_args = [ + "--base-path", + "${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}", +] script = ''' -let crate_name = std::env::var("CARGO_MAKE_CRATE_FS_NAME").expect("CARGO_MAKE_CRATE_FS_NAME should be set by cargo-make"); -let output_directory = std::env::var("WDK_BUILD_OUTPUT_DIRECTORY").expect("WDK_BUILD_OUTPUT_DIRECTORY should be set by cargo-make via the env section of rust-driver-makefile.toml"); +//! ```cargo +//! [dependencies] +//! wdk-build = { path = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] -let source_file = format!("{output_directory}/{crate_name}.dll"); -let destination_file = format!("{output_directory}/{crate_name}.sys"); -std::fs::copy(&source_file, &destination_file).expect(&format!("copy of '{source_file}' file to '{destination_file}' file should succeed")); +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] @@ -124,18 +163,18 @@ private = true dependencies = ["copy-inx-to-output"] command = "stampinf" args = [ - "-f", - "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}.inf", - "-d", - "*", - "-a", - "amd64", - "-c", - "${CARGO_MAKE_CRATE_FS_NAME}.cat", - "-v", - "*", - "-k", - "1.33", + "-f", + "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}.inf", + "-d", + "*", + "-a", + "amd64", + "-c", + "${CARGO_MAKE_CRATE_FS_NAME}.cat", + "-v", + "*", + "-k", + "1.33", ] [tasks.infverif] @@ -143,89 +182,102 @@ private = true dependencies = ["stampinf"] command = "infverif" args = [ - "/v", - "/w", - "@@split(WDK_BUILD_ADDITIONAL_INFVERIF_FLAGS, )", - "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}.inf", + "/v", + "/w", + "@@split(WDK_BUILD_ADDITIONAL_INFVERIF_FLAGS, )", + "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}.inf", ] [tasks.copy-sys-to-package] +private = true dependencies = ["generate-sys-file"] script_runner = "@rust" +script_runner_args = [ + "--base-path", + "${CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY}", +] script = ''' -use std::path::PathBuf; - -let crate_name = std::env::var("CARGO_MAKE_CRATE_FS_NAME").expect("CARGO_MAKE_CRATE_FS_NAME should be set by cargo-make"); -let output_directory = std::env::var("WDK_BUILD_OUTPUT_DIRECTORY").expect("WDK_BUILD_OUTPUT_DIRECTORY should be set by cargo-make via the env section of rust-driver-makefile.toml"); - -let package_folder_path = [output_directory.as_str(), "package"].iter().collect::(); -if !package_folder_path.exists() { - std::fs::create_dir(&package_folder_path).expect(&format!("creation of '{}' folder should succeed", package_folder_path.display())); -} +//! ```cargo +//! [dependencies] +//! wdk-build = { path = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] -let source_file = format!("{output_directory}/{crate_name}.sys"); -let destination_file = format!("{output_directory}/package/{crate_name}.sys"); -std::fs::copy(&source_file, &destination_file).expect(&format!("copy of '{source_file}' file to '{destination_file}' file should succeed")); +wdk_build::cargo_make::copy_to_driver_package_folder( + wdk_build::cargo_make::get_wdk_build_output_directory().join(format!( + "{}.sys", + 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 = ''' -use std::path::PathBuf; - -let crate_name = std::env::var("CARGO_MAKE_CRATE_FS_NAME").expect("CARGO_MAKE_CRATE_FS_NAME should be set by cargo-make"); -let output_directory = std::env::var("WDK_BUILD_OUTPUT_DIRECTORY").expect("WDK_BUILD_OUTPUT_DIRECTORY should be set by cargo-make via the env section of rust-driver-makefile.toml"); - -let package_folder_path = [output_directory.as_str(), "package"].iter().collect::(); -if !package_folder_path.exists() { - std::fs::create_dir(&package_folder_path).expect(&format!("creation of '{}' folder should succeed", package_folder_path.display())); -} +//! ```cargo +//! [dependencies] +//! wdk-build = { path = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] -let source_file = format!("{output_directory}/{crate_name}.pdb"); -let destination_file = format!("{output_directory}/package/{crate_name}.pdb"); -std::fs::copy(&source_file, &destination_file).expect(&format!("copy of '{source_file}' file to '{destination_file}' file should succeed")); +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 = ''' -use std::path::PathBuf; - -let crate_name = std::env::var("CARGO_MAKE_CRATE_FS_NAME").expect("CARGO_MAKE_CRATE_FS_NAME should be set by cargo-make"); -let output_directory = std::env::var("WDK_BUILD_OUTPUT_DIRECTORY").expect("WDK_BUILD_OUTPUT_DIRECTORY should be set by cargo-make via the env section of rust-driver-makefile.toml"); - -let package_folder_path = [output_directory.as_str(), "package"].iter().collect::(); -if !package_folder_path.exists() { - std::fs::create_dir(&package_folder_path).expect(&format!("creation of '{}' folder should succeed", package_folder_path.display())); -} +//! ```cargo +//! [dependencies] +//! wdk-build = { path = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] -let source_file = format!("{output_directory}/{crate_name}.inf"); -let destination_file = format!("{output_directory}/package/{crate_name}.inf"); -std::fs::copy(&source_file, &destination_file).expect(&format!("copy of '{source_file}' file to '{destination_file}' file should succeed")); +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 = ''' -use std::path::PathBuf; - -let crate_name = std::env::var("CARGO_MAKE_CRATE_FS_NAME").expect("CARGO_MAKE_CRATE_FS_NAME should be set by cargo-make"); -let output_directory = std::env::var("WDK_BUILD_OUTPUT_DIRECTORY").expect("WDK_BUILD_OUTPUT_DIRECTORY should be set by cargo-make via the env section of rust-driver-makefile.toml"); - -let package_folder_path = [output_directory.as_str(), "package"].iter().collect::(); -if !package_folder_path.exists() { - std::fs::create_dir(&package_folder_path).expect(&format!("creation of '{}' folder should succeed", package_folder_path.display())); -} +//! ```cargo +//! [dependencies] +//! wdk-build = { path = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] -let source_file = format!("{output_directory}/deps/{crate_name}.map"); -let destination_file = format!("{output_directory}/package/{crate_name}.map"); -std::fs::copy(&source_file, &destination_file).expect(&format!("copy of '{source_file}' file to '{destination_file}' file should succeed")); +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] @@ -233,15 +285,15 @@ private = true dependencies = ["copy-sys-to-package", "copy-inf-to-package"] command = "inf2cat" args = [ - "/driver:${WDK_BUILD_OUTPUT_DIRECTORY}/package", - "/os:10_NI_X64,10_VB_X64", # TODO: this should be a parameter - "/uselocaltime", + "/driver:${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}_package", + "/os:10_NI_X64,10_VB_X64", # TODO: this should be a parameter + "/uselocaltime", ] [tasks.generate-certificate] private = true condition_script = [ - ''' + ''' #!@duckscript out = exec certmgr.exe -put -s WDRTestCertStore -c -n WDRLocalTestCert ${WDK_BUILD_OUTPUT_DIRECTORY}/WDRLocalTestCert.cer @@ -256,36 +308,37 @@ end ] command = "makecert" args = [ - "-r", - "-pe", - "-a", - "SHA256", - "-eku", - "1.3.6.1.5.5.7.3.3", - "-ss", - "WDRTestCertStore", # TODO: this should be a parameter - "-n", - "CN=WDRLocalTestCert", # TODO: this should be a parameter - "${WDK_BUILD_OUTPUT_DIRECTORY}/WDRLocalTestCert.cer", + "-r", + "-pe", + "-a", + "SHA256", + "-eku", + "1.3.6.1.5.5.7.3.3", + "-ss", + "WDRTestCertStore", # TODO: this should be a parameter + "-n", + "CN=WDRLocalTestCert", # TODO: 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 = ''' -use std::path::PathBuf; - -let output_directory = std::env::var("WDK_BUILD_OUTPUT_DIRECTORY").expect("WDK_BUILD_OUTPUT_DIRECTORY should be set by cargo-make via the env section of rust-driver-makefile.toml"); - -let package_folder_path = [output_directory.as_str(), "package"].iter().collect::(); -if !package_folder_path.exists() { - std::fs::create_dir(&package_folder_path).expect(&format!("creation of '{}' folder should succeed", package_folder_path.display())); -} +//! ```cargo +//! [dependencies] +//! wdk-build = { path = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] -let source_file = format!("{output_directory}/WDRLocalTestCert.cer"); -let destination_file = format!("{output_directory}/package/WDRLocalTestCert.cer"); -std::fs::copy(&source_file, &destination_file).expect(&format!("copy of '{source_file}' file to '{destination_file}' file should succeed")); +wdk_build::cargo_make::copy_to_driver_package_folder( + wdk_build::cargo_make::get_wdk_build_output_directory().join("WDRLocalTestCert.cer"), +); ''' [tasks.signtool] @@ -293,35 +346,35 @@ private = true dependencies = ["inf2cat", "generate-certificate"] command = "signtool" args = [ - "sign", - "/v", - "/s", - "WDRTestCertStore", # TODO: this should be a parameter - "/n", - "WDRLocalTestCert", # TODO: this should be a parameter - "/t", - "http://timestamp.digicert.com", - "/fd", - "SHA256", - "${WDK_BUILD_OUTPUT_DIRECTORY}/package/${CARGO_MAKE_CRATE_FS_NAME}.cat", + "sign", + "/v", + "/s", + "WDRTestCertStore", # TODO: this should be a parameter + "/n", + "WDRLocalTestCert", # TODO: this should be a parameter + "/t", + "http://timestamp.digicert.com", + "/fd", + "SHA256", + "${WDK_BUILD_OUTPUT_DIRECTORY}/${CARGO_MAKE_CRATE_FS_NAME}_package/${CARGO_MAKE_CRATE_FS_NAME}.cat", ] [tasks.package-driver] private = true dependencies = [ - "copy-sys-to-package", - "copy-pdb-to-package", - "copy-inf-to-package", - "copy-map-to-package", - "copy-certificate-to-package", - "signtool", - "infverif", + "copy-sys-to-package", + "copy-pdb-to-package", + "copy-inf-to-package", + "copy-map-to-package", + "copy-certificate-to-package", + "signtool", + "infverif", ] [tasks.package-driver-flow] # Only run flow if the current package is marked as a driver condition_script = [ - ''' + ''' #!@duckscript # Execute Cargo Metadata to get Package information @@ -362,5 +415,10 @@ end ] run_task = "package-driver" +[tasks.help] +workspace = false +env = { "TRIGGER_HELP" = "1" } +run_task = "wdk-build-init" + [tasks.default] alias = "package-driver-flow"