From 8fe234c25f5ab5d23a7a4fbd66326daec9e9e2ea Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Fri, 3 Nov 2023 16:53:18 -0700 Subject: [PATCH] feat: cargo-make argument forwarding --- Cargo.lock | 124 +++++++++ Makefile.toml | 1 + README.md | 38 ++- crates/wdk-build/Cargo.toml | 2 + crates/wdk-build/src/cargo_make.rs | 395 +++++++++++++++++++++++++++++ crates/wdk-build/src/lib.rs | 2 + rust-driver-makefile.toml | 87 ++++++- 7 files changed, 646 insertions(+), 3 deletions(-) create mode 100644 Makefile.toml create mode 100644 crates/wdk-build/src/cargo_make.rs diff --git a/Cargo.lock b/Cargo.lock index 0983b7a4..62b03269 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,54 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "basic-toml" version = "0.1.4" @@ -84,6 +132,62 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-cargo" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ae55615695e768a76899c8411b4ebacfbe525e964f94fd24f0007b10b45cd3" +dependencies = [ + "anstyle", + "clap", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "colored" version = "2.0.4" @@ -134,6 +238,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.2" @@ -445,6 +555,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "2.0.32" @@ -551,6 +667,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" @@ -577,6 +699,8 @@ name = "wdk-build" version = "0.1.0" dependencies = [ "bindgen", + "clap", + "clap-cargo", "rustversion", "serde", "serde_json", diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 00000000..4392852e --- /dev/null +++ b/Makefile.toml @@ -0,0 +1 @@ +extend = "./rust-driver-makefile.toml" diff --git a/README.md b/README.md index 8ace57db..20d37385 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # 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. @@ -147,7 +146,44 @@ The crates in this repository are available from [`crates.io`](https://crates.io A `DriverCertificate.cer` file will be generated, and a signed driver package will be available at `target//package` +## 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 arguements 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 ` + +For release builds: + +`cargo make default --release` or `cargo make default --profile release` + +To specify specific features: + +`cargo make default --feature ` + +To specify a specific rust toolchain: + +`cargo make default +` + +To display help and see the full list of supported CLI args to forward to Cargo: + +`cargo make help` + ## 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 diff --git a/crates/wdk-build/Cargo.toml b/crates/wdk-build/Cargo.toml index ed2dbedc..40969331 100644 --- a/crates/wdk-build/Cargo.toml +++ b/crates/wdk-build/Cargo.toml @@ -13,6 +13,8 @@ categories = ["development-tools::build-utils", "development-tools::ffi"] bindgen.workspace = true serde.workspace = true serde_json.workspace = true +clap = { version = "4.4.7", features = ["derive"] } +clap-cargo = "0.13.0" thiserror = "1.0.48" windows = { version = "0.51.1", features = [ "Win32_Foundation", diff --git a/crates/wdk-build/src/cargo_make.rs b/crates/wdk-build/src/cargo_make.rs new file mode 100644 index 00000000..972ae4ee --- /dev/null +++ b/crates/wdk-build/src/cargo_make.rs @@ -0,0 +1,395 @@ +// 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` + +use clap::{Args, Parser}; + +/// The name of the environment variable that cargo-make uses during `cargo +/// build` and `cargo test` commands +const CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR: &str = "CARGO_MAKE_CARGO_BUILD_TEST_FLAGS"; + +const CARGO_MAKE_PROFILE_ENV_VAR: &str = "CARGO_MAKE_PROFILE"; +const CARGO_MAKE_CARGO_PROFILE_ENV_VAR: &str = "CARGO_MAKE_CARGO_PROFILE"; +const CARGO_MAKE_RUST_DEFAULT_TOOLCHAIN_ENV_VAR: &str = "CARGO_MAKE_RUST_DEFAULT_TOOLCHAIN"; + +/// `clap` uses an exit code of 2 for usage errors: +const CLAP_USAGE_EXIT_CODE: i32 = 2; + +trait ParseCargoArg { + fn parse_cargo_arg(&self); +} + +#[derive(Parser, Debug)] +struct CommandLineInterface { + #[command(flatten)] + base: BaseOptions, + + #[command(flatten)] + #[command(next_help_heading = "Package Selection")] + workspace: clap_cargo::Workspace, + + #[command(flatten)] + #[command(next_help_heading = "Feature Selection")] + features: clap_cargo::Features, + + #[command(flatten)] + compilation_options: CompilationOptions, + + #[command(flatten)] + manifest_options: ManifestOptions, +} + +#[derive(Args, Debug)] +struct BaseOptions { + #[arg(long, help = "Do not print cargo log messages")] + quiet: bool, + + #[arg(short, long, action = clap::ArgAction::Count, help = "Use verbose output (-vv very verbose/build.rs output)")] + verbose: u8, +} + +#[derive(Args, Debug)] +#[command(next_help_heading = "Compilation Options")] +struct CompilationOptions { + #[arg( + short, + long, + help = "Build artifacts in release mode, with optimizations" + )] + release: bool, + + #[arg( + long, + value_name = "PROFILE-NAME", + help = "Build artifacts with the specified profile" + )] + profile: Option, + + #[arg( + short, + long, + value_name = "N", + allow_negative_numbers = true, + help = "Number of parallel jobs, defaults to # of CPUs." + )] + jobs: Option, + + // TODO: support building multiple targets at once + #[arg(long, value_name = "TRIPLE", help = "Build for a target triple")] + target: Option, + + #[allow(clippy::option_option)] // This is how clap_derive expects "optional value for optional argument" args + #[arg( + long, + value_name = "FMTS", + require_equals = true, + help = "Timing output formats (unstable) (comma separated): html, json" + )] + timings: Option>, +} + +#[derive(Args, Debug)] +#[command(next_help_heading = "Manifest Options")] +struct ManifestOptions { + #[arg(long, help = "Require Cargo.lock and cache are up to date")] + frozen: bool, + + #[arg(long, help = "Require Cargo.lock is up to date")] + locked: bool, + + #[arg(long, help = "Run without accessing the network")] + offline: bool, +} + +impl ParseCargoArg for BaseOptions { + fn parse_cargo_arg(&self) { + if self.quiet && self.verbose > 0 { + eprintln!("Cannot specify both --quiet and --verbose"); + std::process::exit(CLAP_USAGE_EXIT_CODE); + } + + if self.quiet { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--quiet"); + } + + if self.verbose > 0 { + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + format!("-{}", "v".repeat(self.verbose.into())).as_str(), + ); + } + } +} + +impl ParseCargoArg for clap_cargo::Workspace { + fn parse_cargo_arg(&self) { + if !self.package.is_empty() { + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + self.package + .iter() + .fold( + String::with_capacity({ + const MINIMUM_PACKAGE_SPEC_LENGTH: usize = 1; + const MINIMUM_PACKAGE_ARG_LENGTH: usize = + "--package ".len() + MINIMUM_PACKAGE_SPEC_LENGTH + " ".len(); + self.package.len() * MINIMUM_PACKAGE_ARG_LENGTH + }), + |mut package_args, package_spec| { + package_args.push_str("--package "); + package_args.push_str(package_spec); + package_args.push(' '); + package_args + }, + ) + .trim_end(), + ); + } + + if self.workspace { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--workspace"); + } + + if !self.exclude.is_empty() { + if !self.workspace { + eprintln!("--exclude can only be used together with --workspace"); + std::process::exit(CLAP_USAGE_EXIT_CODE); + } + + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + self.exclude + .iter() + .fold( + String::with_capacity({ + const MINIMUM_PACKAGE_SPEC_LENGTH: usize = 1; + const MINIMUM_EXCLUDE_ARG_LENGTH: usize = + "--exclude ".len() + MINIMUM_PACKAGE_SPEC_LENGTH + " ".len(); + self.package.len() * MINIMUM_EXCLUDE_ARG_LENGTH + }), + |mut exclude_args, package_spec| { + exclude_args.push_str("--exclude "); + exclude_args.push_str(package_spec); + exclude_args.push(' '); + exclude_args + }, + ) + .trim_end(), + ); + } + + if self.all { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--all"); + } + } +} + +impl ParseCargoArg for clap_cargo::Features { + fn parse_cargo_arg(&self) { + if self.all_features { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--all-features"); + } + + if self.no_default_features { + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + "--no-default-features", + ); + } + + if !self.features.is_empty() { + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + self.features + .iter() + .fold( + String::with_capacity({ + const MINIMUM_FEATURE_NAME_LENGTH: usize = 1; + const MINIMUM_FEATURE_ARG_LENGTH: usize = + "--features ".len() + MINIMUM_FEATURE_NAME_LENGTH + " ".len(); + self.features.len() * MINIMUM_FEATURE_ARG_LENGTH + }), + |mut feature_args: String, feature| { + feature_args.push_str("--features "); + feature_args.push_str(feature); + feature_args.push(' '); + feature_args + }, + ) + .trim_end(), + ); + } + } +} + +impl ParseCargoArg for CompilationOptions { + fn parse_cargo_arg(&self) { + if self.release && self.profile.is_some() { + eprintln!("the `--release` flag should not be specified with the `--profile` flag"); + std::process::exit(CLAP_USAGE_EXIT_CODE); + } + match std::env::var(CARGO_MAKE_PROFILE_ENV_VAR) + .unwrap_or_else(|_| panic!("{CARGO_MAKE_PROFILE_ENV_VAR} should be set by cargo-make.")) + .as_ref() + { + "release" => { + // cargo-make release profile sets the `--profile release` flag + if let Some(profile) = &self.profile { + if profile != "release" { + eprintln!( + "Specifying `--profile release` for cargo-make conflicts with the \ + setting `--profile {profile}` to forward to tasks" + ); + std::process::exit(CLAP_USAGE_EXIT_CODE); + } + } + + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + "--profile release", + ); + } + _ => { + // All other cargo-make profiles do not set a specific cargo profile. Cargo + // profiles set by --release, --profile , or -p (after the + // cargo-make task name) are forwarded to cargo commands + if self.release { + println!("{CARGO_MAKE_CARGO_PROFILE_ENV_VAR}=release"); + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--release"); + } else if let Some(profile) = &self.profile { + println!("{CARGO_MAKE_CARGO_PROFILE_ENV_VAR}={profile}"); + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + format!("--profile {profile}").as_str(), + ); + } + } + } + + if let Some(jobs) = &self.jobs { + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + format!("--jobs {jobs}").as_str(), + ); + } + + if let Some(target) = &self.target { + println!("CARGO_MAKE_CRATE_TARGET_TRIPLE={target}"); + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + format!("--target {target}").as_str(), + ); + } + + if let Some(timings_option) = &self.timings { + timings_option.as_ref().map_or_else( + || { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--timings"); + }, + |timings_value| { + append_to_env_var( + CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, + format!("--timings {timings_value}").as_str(), + ); + }, + ); + } + } +} + +impl ParseCargoArg for ManifestOptions { + fn parse_cargo_arg(&self) { + if self.frozen { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--frozen"); + } + + if self.locked { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--locked"); + } + + if self.offline { + append_to_env_var(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR, "--offline"); + } + } +} + +/// Parses the command line arguments, validates that they are supported by +/// `rust-driver-makefile.toml`, and forwards them to `cargo-make` by printing +/// them to stdout. +/// +/// # Panics +/// +/// This function will panic if there's an internal error (i.e. bug) in its +/// argument processing. +pub fn validate_and_forward_args() { + const TOOLCHAIN_ARG_POSITION: usize = 1; + + let mut env_args = std::env::args_os().collect::>(); + + // + is a special argument that can't currently be handled by clap parsing: https://github.com/clap-rs/clap/issues/2468 + let toolchain_arg = if env_args + .get(TOOLCHAIN_ARG_POSITION) + .is_some_and(|arg| arg.to_string_lossy().starts_with('+')) + { + Some( + env_args + .remove(TOOLCHAIN_ARG_POSITION) + .to_string_lossy() + .strip_prefix('+') + .expect("Toolchain arg should have a + prefix") + .to_owned(), + ) + } else { + None + }; + + let command_line_interface: CommandLineInterface = + CommandLineInterface::parse_from(env_args.iter()); + // This print signifies the start of the forwarding and signals to the + // `rust-env-update` plugin that it should forward args. This is also used to + // signal that the auto-generated help from `clap` was not executed. + println!("FORWARDING ARGS TO CARGO-MAKE:"); + + if let Some(toolchain) = toolchain_arg { + println!("{CARGO_MAKE_RUST_DEFAULT_TOOLCHAIN_ENV_VAR}={toolchain}"); + } + + command_line_interface.base.parse_cargo_arg(); + command_line_interface.workspace.parse_cargo_arg(); + command_line_interface.features.parse_cargo_arg(); + command_line_interface.compilation_options.parse_cargo_arg(); + command_line_interface.manifest_options.parse_cargo_arg(); + + forward_env_var_to_cargo_make(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS_ENV_VAR); +} + +fn append_to_env_var>(env_var_name: S, string_to_append: S) { + let env_var_name = env_var_name.as_ref(); + let string_to_append = string_to_append.as_ref(); + + let mut env_var_value = std::env::var(env_var_name).unwrap_or_default(); + env_var_value.push(' '); + env_var_value.push_str(string_to_append); + std::env::set_var(env_var_name, env_var_value.trim()); +} + +fn forward_env_var_to_cargo_make>(env_var_name: S) { + let env_var_name = env_var_name.as_ref(); + + // Since this executes in a child proccess to cargo-make, we need to forward the + // values we want to change to duckscript, in order to get it to modify the + // parent process (ie. cargo-make) + if let Some(env_var_value) = std::env::var_os(env_var_name) { + println!( + "{env_var_name}={}", + env_var_value + .to_str() + .expect("env var value should be valid UTF-8") + ); + } +} diff --git a/crates/wdk-build/src/lib.rs b/crates/wdk-build/src/lib.rs index 0a5fabe2..c355259d 100644 --- a/crates/wdk-build/src/lib.rs +++ b/crates/wdk-build/src/lib.rs @@ -31,6 +31,8 @@ mod bindgen; mod utils; +pub mod cargo_make; + use std::{env, path::PathBuf}; pub use bindgen::BuilderExt; diff --git a/rust-driver-makefile.toml b/rust-driver-makefile.toml index 0b63edd1..b8f278eb 100644 --- a/rust-driver-makefile.toml +++ b/rust-driver-makefile.toml @@ -3,13 +3,96 @@ # FIXME: replace all script blocks with cargo-make commands: "Favor commands over scripts, as commands support more features such as automatic dependencies installation, argument functions, and more..." # 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.3" +init_task = "wdk-build-init" +default_to_workspace = 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 = "" + VC_BUILD_DIR = "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build\\vcvarsamd64_x86.bat" + # FIXME: add --locked for CI builds using CARGO_MAKE_PR and CARGO_MAKE_CI -CARGO_MAKE_CARGO_BUILD_TEST_FLAGS = "--profile ${CARGO_MAKE_CARGO_PROFILE}" # Cargo puts "dev" profile builds in the "debug" target folder: https://doc.rust-lang.org/cargo/guide/build-cache.html#build-cache. This supports cargo-make profile values of both "development" and "dev" -OUTPUT_DIR = { source = "${CARGO_MAKE_CARGO_PROFILE}", default_value = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}\\${CARGO_MAKE_CARGO_PROFILE}", mapping = { "dev" = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}\\debug" } } +OUTPUT_DIR = { source = "${CARGO_MAKE_CARGO_PROFILE}", default_value = "${CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY}\\${CARGO_MAKE_CARGO_PROFILE}", mapping = { "dev" = "${CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY}\\debug" } } + +[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}" + +# Execute rust-script +taskjson = json_parse ${task.as_json} +filepath = set "./target/cargo-make-script/${task.name}/main.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 +cli_args = array_join ${flow.cli.args} " " +trigger_help = get_env TRIGGER_HELP +if not is_empty ${trigger_help} + cli_args = concat ${cli_args} " --help" +end +out = exec --fail-on-error rust-script --base-path ${taskjson.env.CARGO_MAKE_CURRENT_TASK_INITIAL_MAKEFILE_DIRECTORY} ./target/cargo-make-script/${task.name}/main.rs %{cli_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 ./target/cargo-make-script/${task.name}/main.rs" + +# 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 + stdout_first_line = array_get ${script_output_array} 0 + assert_eq ${stdout_first_line} "FORWARDING ARGS TO CARGO-MAKE:" "[tasks.${task.name}]'s script output did not begin with \"FORWARDING ARGS TO CARGO-MAKE:\". Was `--help` passed as one of the arguments?\nstdout:\n${out.stdout}\nstderr:\n${out.stderr}\nThe temporary rust-script file is located at ./target/cargo-make-script/${task.name}/main.rs" + array_remove ${script_output_array} 0 + for line in ${script_output_array} + parts = split ${line} = + key = array_get ${parts} 0 + value = array_get ${parts} 1 + set_env ${key} ${value} + end +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 = "./crates/wdk-build", version = "0.1.0" } +//! ``` +#![allow(unused_doc_comments)] + +wdk_build::cargo_make::validate_and_forward_args(); +''' + +[tasks.help] +workspace = false +env = { "TRIGGER_HELP" = "1" } +run_task = "wdk-build-init" [tasks.rename-dll-to-sys] dependencies = ["build"]