From c1083b7f1ce4f72cb3bcfc6f9fec31788f594ddd Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 1 Mar 2022 09:02:44 +0100 Subject: [PATCH] Fixup clap args (#38) * Clap 3 by default doesn't add author/version/about metadata * Change option to `--manifest-version` * Update CHANGELOG * Add snapshot testing for CLI * Actually run CLI tests in CI * Update snapshots * Force terminal width --- .github/workflows/rust-ci.yml | 15 +++++ CHANGELOG.md | 4 ++ Cargo.lock | 42 +++++++++++++ Cargo.toml | 3 +- src/main.rs | 99 ++++++++++++++++++++++++++++-- tests/snapshots/xwin-download.snap | 20 ++++++ tests/snapshots/xwin-list.snap | 23 +++++++ tests/snapshots/xwin-splat.snap | 52 ++++++++++++++++ tests/snapshots/xwin-unpack.snap | 19 ++++++ tests/snapshots/xwin.snap | 88 ++++++++++++++++++++++++++ 10 files changed, 359 insertions(+), 6 deletions(-) create mode 100644 tests/snapshots/xwin-download.snap create mode 100644 tests/snapshots/xwin-list.snap create mode 100644 tests/snapshots/xwin-splat.snap create mode 100644 tests/snapshots/xwin-unpack.snap create mode 100644 tests/snapshots/xwin.snap diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index a2eead9..a9cf107 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -79,6 +79,21 @@ jobs: - name: cargo test run: cargo test --release verify_deterministic + check_cli: + name: Verify CLI + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - run: cargo fetch + - name: cargo test build + run: cargo build --tests + - name: cargo test + run: cargo test cli_help + deny-check: name: cargo-deny runs-on: ubuntu-20.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index 13eaec4..ab0bd04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Changed +- [PR#37](https://github.com/Jake-Shadle/xwin/pull/37) changed from structopt to clap v3 for arguments parsing. Thanks [@messense](https://github.com/messense)! +- [PR#38](https://github.com/Jake-Shadle/xwin/pull/38) fixed up the clap arguments to include metadata to be closer to the original structopt output with eg. `xwin -V`, however this exposed a problem that clap couldn't handle the old `--version ` flag since it clashed with `-V, --version`, so the flag has been renamed to `--manifest-version`. This is unfortunately a breaking change for the CLI. + ## [0.1.10] - 2022-02-28 ### Fixed - [PR#34](https://github.com/Jake-Shadle/xwin/pull/34) changed some code so that it is possible to compile and run for `x86_64-pc-windows-msvc`, though this target is not explicitly support. Thanks [@messense](https://github.com/messense)! diff --git a/Cargo.lock b/Cargo.lock index 95c7a8e..748ebab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,6 +567,20 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "insta" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c0c443f6dceb3a1cb7607c87501aa91e4b9c976044f725c2a74ca2152c91a4" +dependencies = [ + "console", + "once_cell", + "serde", + "serde_json", + "serde_yaml", + "similar", +] + [[package]] name = "instant" version = "0.1.12" @@ -609,6 +623,12 @@ version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "lock_api" version = "0.4.6" @@ -1108,6 +1128,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -1697,6 +1729,7 @@ dependencies = [ "cli-table", "flate2", "indicatif", + "insta", "msi", "parking_lot", "rayon", @@ -1714,6 +1747,15 @@ dependencies = [ "zip", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zip" version = "0.5.13" diff --git a/Cargo.toml b/Cargo.toml index a237fe6..e588f99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "xwin" version = "0.1.10" -description = "Allows downloading and repacking the MSVC and Windows SDK for cross compilation" +description = "Allows downloading and repacking the MSVC CRT and Windows SDK for cross compilation" authors = ["Jake Shadle "] edition = "2018" license = "Apache-2.0 OR MIT" @@ -70,5 +70,6 @@ twox-hash = "1.6" zip = { version = "0.5", default-features = false, features = ["deflate"] } [dev-dependencies] +insta = "1.12" similar-asserts = "1.1" walkdir = "2.3" diff --git a/src/main.rs b/src/main.rs index 9030ee8..a74e752 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,6 +92,7 @@ fn parse_level(s: &str) -> Result { } #[derive(Parser)] +#[clap(author, version, about, long_about = None)] pub struct Args { /// Doesn't display the prompt to accept the license #[clap(long, env = "XWIN_ACCEPT_LICENSE")] @@ -119,12 +120,12 @@ pub struct Args { cache_dir: Option, /// Specifies a VS manifest to use from a file, rather than downloading it /// from the Microsoft site. - #[clap(long, conflicts_with_all = &["version", "channel"])] + #[clap(long, conflicts_with_all = &["manifest-version", "channel"])] manifest: Option, /// The version to retrieve, can either be a major version of 15 or 16, or /// a "." version. #[clap(long, default_value = "16")] - version: String, + manifest_version: String, /// The product channel to use. #[clap(long, default_value = "release")] channel: String, @@ -347,9 +348,12 @@ fn load_manifest( serde_json::from_str(&manifest_content) .with_context(|| format!("failed to deserialize manifest in '{}'", manifest_path))? } - None => { - xwin::manifest::get_manifest(ctx, &args.version, &args.channel, manifest_pb.clone())? - } + None => xwin::manifest::get_manifest( + ctx, + &args.manifest_version, + &args.channel, + manifest_pb.clone(), + )?, }; let pkg_manifest = xwin::manifest::get_package_manifest(ctx, &manifest, manifest_pb.clone())?; @@ -357,3 +361,88 @@ fn load_manifest( manifest_pb.finish_with_message("📥 downloaded"); Ok(pkg_manifest) } + +#[cfg(test)] +mod test { + #[test] + fn cli_help() { + use clap::CommandFactory; + + let snapshot_path = format!("{}/tests/snapshots", env!("CARGO_MANIFEST_DIR")); + + insta::with_settings!({ + prepend_module_to_snapshot => false, + snapshot_path => snapshot_path, + }, { + // the tests here will force maps to sort + snapshot_test_cli_command(super::Args::command().name(env!("CARGO_PKG_NAME")), "xwin".to_owned(), &SnapshotTestDesc { + manifest_path: env!("CARGO_MANIFEST_DIR"), + module_path: module_path!(), + file: file!(), + line: line!(), + }); + }); + } + + use clap::{ColorChoice, Command}; + + pub struct SnapshotTestDesc { + pub manifest_path: &'static str, + pub module_path: &'static str, + pub file: &'static str, + pub line: u32, + } + + fn snapshot_test_cli_command(app: Command<'_>, cmd_name: String, desc: &SnapshotTestDesc) { + let mut app = app + .clone() + // we do not want ASCII colors in our snapshot test output + .color(ColorChoice::Never) + // override versions to not have to update test when changing versions + .version("0.0.0") + .long_version("0.0.0") + .term_width(80); + + // don't show current env vars as that will make snapshot test output diff depending on environment run in + let arg_names = app + .get_arguments() + .map(|a| a.get_id()) + .filter(|a| *a != "version" && *a != "help") + .collect::>(); + for arg_name in arg_names { + app = app.mut_arg(arg_name, |arg| arg.hide_env_values(true)); + } + + // get the long help text for the command + let mut buffer = Vec::new(); + app.write_long_help(&mut buffer).unwrap(); + let help_text = std::str::from_utf8(&buffer).unwrap(); + + // use internal `insta` function instead of the macro so we can pass in the + // right module information from the crate and to gather up the errors instead of panicking directly on failures + insta::_macro_support::assert_snapshot( + cmd_name.clone().into(), + help_text, + desc.manifest_path, + "cli-cmd", + desc.module_path, + desc.file, + desc.line, + "help_text", + ) + .unwrap(); + + // recursively test all subcommands + for app in app.get_subcommands() { + if app.get_name() == "help" { + continue; + } + + snapshot_test_cli_command( + app.clone(), + format!("{}-{}", cmd_name, app.get_name()), + desc, + ); + } + } +} diff --git a/tests/snapshots/xwin-download.snap b/tests/snapshots/xwin-download.snap new file mode 100644 index 0000000..8193263 --- /dev/null +++ b/tests/snapshots/xwin-download.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +assertion_line: 382 +expression: help_text + +--- +download 0.0.0 +Downloads all the selected packages that aren't already present in the download +cache + +USAGE: + download + +OPTIONS: + -h, --help + Print help information + + -V, --version + Print version information + diff --git a/tests/snapshots/xwin-list.snap b/tests/snapshots/xwin-list.snap new file mode 100644 index 0000000..d7f1a58 --- /dev/null +++ b/tests/snapshots/xwin-list.snap @@ -0,0 +1,23 @@ +--- +source: src/main.rs +assertion_line: 382 +expression: help_text + +--- +list 0.0.0 +Displays a summary of the packages that would be downloaded. + +Note that this is not a full list as the SDK uses MSI files for many packages, +so they would need to be downloaded and inspected to determine which CAB files +must also be downloaded to get the content needed. + +USAGE: + list + +OPTIONS: + -h, --help + Print help information + + -V, --version + Print version information + diff --git a/tests/snapshots/xwin-splat.snap b/tests/snapshots/xwin-splat.snap new file mode 100644 index 0000000..9953c4b --- /dev/null +++ b/tests/snapshots/xwin-splat.snap @@ -0,0 +1,52 @@ +--- +source: src/main.rs +assertion_line: 382 +expression: help_text + +--- +splat 0.0.0 +Fixes the packages to prune unneeded files and adds symlinks to address file +casing issues and then spalts the final artifacts into directories + +USAGE: + splat [OPTIONS] + +OPTIONS: + --copy + Copies files from the unpack directory to the splat directory + instead of moving them, which preserves the original unpack + directories but increases overall time and disk usage + + --disable-symlinks + By default, symlinks are added to both the CRT and WindowsSDK to + address casing issues in general usage. For example, if you are + compiling C/C++ code that does `#include `, it will break + on a case-sensitive file system, as the actual path in the + WindowsSDK is `Windows.h`. This also applies even if the C/C++ you + are compiling uses correct casing for all CRT/SDK includes, as the + internal headers also use incorrect casing in most cases + + -h, --help + Print help information + + --include-debug-libs + The MSVCRT includes (non-redistributable) debug versions of the + various libs that are generally uninteresting to keep for most usage + + --include-debug-symbols + The MSVCRT includes PDB (debug symbols) files for several of the + libraries that are generally uninteresting to keep for most usage + + --output + The root output directory. Defaults to `./.xwin-cache/splat` if not + specified + + --preserve-ms-arch-notation + By default, we convert the MS specific `x64`, `arm`, and `arm64` + target architectures to the more canonical `x86_64`, `aarch`, and + `aarch64` of LLVM etc when creating directories/names. Passing this + flag will preserve the MS names for those targets + + -V, --version + Print version information + diff --git a/tests/snapshots/xwin-unpack.snap b/tests/snapshots/xwin-unpack.snap new file mode 100644 index 0000000..e8ea698 --- /dev/null +++ b/tests/snapshots/xwin-unpack.snap @@ -0,0 +1,19 @@ +--- +source: src/main.rs +assertion_line: 382 +expression: help_text + +--- +unpack 0.0.0 +Unpacks all of the downloaded packages to disk + +USAGE: + unpack + +OPTIONS: + -h, --help + Print help information + + -V, --version + Print version information + diff --git a/tests/snapshots/xwin.snap b/tests/snapshots/xwin.snap new file mode 100644 index 0000000..d0a18fa --- /dev/null +++ b/tests/snapshots/xwin.snap @@ -0,0 +1,88 @@ +--- +source: src/main.rs +assertion_line: 382 +expression: help_text + +--- +xwin 0.0.0 +Jake Shadle +Allows downloading and repacking the MSVC CRT and Windows SDK for cross +compilation + +USAGE: + xwin [OPTIONS] + +OPTIONS: + --accept-license + Doesn't display the prompt to accept the license + + [env: XWIN_ACCEPT_LICENSE] + + --arch + The architectures to include + + [default: x86_64] + [possible values: x86, x86_64, aarch, aarch64] + + --cache-dir + Specifies the cache directory used to persist downloaded items to + disk. Defaults to `./.xwin-cache` if not specified + + --channel + The product channel to use + + [default: release] + + -h, --help + Print help information + + --json + Output log messages as json + + -L, --log-level + The log level for messages, only log messages at or above the level + will be emitted + + [default: info] + [possible values: off, error, warn, info, debug, trace] + + --manifest + Specifies a VS manifest to use from a file, rather than downloading + it from the Microsoft site + + --manifest-version + The version to retrieve, can either be a major version of 15 or 16, + or a "." version + + [default: 16] + + --temp + If set, will use a temporary directory for all files used for + creating the archive and deleted upon exit, otherwise, all + downloaded files are kept in the `--cache-dir` and won't be + retrieved again + + -V, --version + Print version information + + --variant + The variants to include + + [default: desktop] + [possible values: desktop, onecore, spectre] + +SUBCOMMANDS: + download + Downloads all the selected packages that aren't already present in + the download cache + help + Print this message or the help of the given subcommand(s) + list + Displays a summary of the packages that would be downloaded + splat + Fixes the packages to prune unneeded files and adds symlinks to + address file casing issues and then spalts the final artifacts into + directories + unpack + Unpacks all of the downloaded packages to disk +