Skip to content

Commit

Permalink
Support pwsh format for show-env (#411)
Browse files Browse the repository at this point in the history
  • Loading branch information
LittleBoxOfSunshine authored Jan 10, 2025
1 parent 73f6618 commit c0715a3
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 8 deletions.
1 change: 1 addition & 0 deletions .github/.cspell/project-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ nextest
notcovered
profdata
profraw
pwsh
rmeta
rustfilt
rustix
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,12 @@ Note: cargo-llvm-cov subcommands other than `report` and `clean` may not work co

Note: To include coverage for doctests you also need to pass `--doctests` to both `cargo llvm-cov show-env` and `cargo llvm-cov report`.

> The same thing can be achieved in PowerShell 6+ by substituting the source command with:
>
> ```powershell
> Invoke-Expression (cargo llvm-cov show-env --with-pwsh-env-prefix | Out-String)
> ```
### Exclude file from coverage
To exclude specific file patterns from the report, use the `--ignore-filename-regex` option.
Expand Down
4 changes: 4 additions & 0 deletions docs/cargo-llvm-cov-show-env.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ OPTIONS:
--export-prefix
Prepend "export " to each line, so that the output is suitable to be sourced by bash

--with-pwsh-env-prefix
Unicode escape and double quote values + prepend "$env:", so that the output is suitable
to be used with Invoke-Expression in PowerShell 6+.

--doctests
Including doc tests (unstable)

Expand Down
69 changes: 63 additions & 6 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,60 @@ impl LlvmCovOptions {
}

#[derive(Debug, Clone)]
pub(crate) struct ShowEnvOptions {
pub(crate) enum ShowEnvFormat {
/// Each line: key=<escaped value>, escaped using [`shell_escape::escape`].
EscapedKeyValuePair,
/// Prepend "export " to each line, so that the output is suitable to be sourced by bash.
pub(crate) export_prefix: bool,
UnixExport,
/// Each value: "$env:{key}={value}", where {value} is PowerShell Unicode escaped e.g. "`u{72}".
Pwsh,
}

impl ShowEnvFormat {
pub(crate) fn new(export_prefix: bool, with_pwsh_env_prefix: bool) -> Result<Self> {
if export_prefix && with_pwsh_env_prefix {
conflicts("--export-prefix", "--with-pwsh-env-prefix")?;
}

Ok(if export_prefix {
ShowEnvFormat::UnixExport
} else if with_pwsh_env_prefix {
ShowEnvFormat::Pwsh
} else {
ShowEnvFormat::EscapedKeyValuePair
})
}

pub(crate) fn export_string(&self, key: &str, value: &str) -> String {
match self {
ShowEnvFormat::EscapedKeyValuePair => {
format!("{key}={}", shell_escape::escape(value.into()))
}
ShowEnvFormat::UnixExport => {
format!("export {key}={}", shell_escape::escape(value.into()))
}
ShowEnvFormat::Pwsh => {
// PowerShell 6+ expects encoded UTF-8 text. Some env vars like CARGO_ENCODED_RUSTFLAGS
// have non-printable binary characters. We can work around this and any other escape
// related considerations by just escaping all characters. Rust's Unicode escape is
// of form "\u{<code>}", but PowerShell expects "`u{<code>}". A replace call fixes
// this.
let value = value.escape_unicode().to_string().replace('\\', "`");
format!("$env:{key}=\"{value}\"")
}
}
}
}

impl Default for ShowEnvFormat {
fn default() -> Self {
Self::EscapedKeyValuePair
}
}

#[derive(Debug, Clone)]
pub(crate) struct ShowEnvOptions {
pub(crate) show_env_format: ShowEnvFormat,
}

// https://doc.rust-lang.org/nightly/cargo/commands/cargo-test.html#manifest-options
Expand Down Expand Up @@ -532,6 +583,7 @@ impl Args {

// show-env options
let mut export_prefix = false;
let mut with_pwsh_env_prefix = false;

// options ambiguous between nextest-related and others
let mut profile = None;
Expand Down Expand Up @@ -711,6 +763,7 @@ impl Args {

// show-env options
Long("export-prefix") => parse_flag!(export_prefix),
Long("with-pwsh-env-prefix") => parse_flag!(with_pwsh_env_prefix),

// ambiguous between nextest-related and others will be handled later
Long("archive-file") => parse_opt_passthrough!(archive_file),
Expand Down Expand Up @@ -814,14 +867,18 @@ impl Args {
term::set_coloring(&mut color);

// unexpected options
match subcommand {
Subcommand::ShowEnv => {}
let show_env_format = match subcommand {
Subcommand::ShowEnv => ShowEnvFormat::new(export_prefix, with_pwsh_env_prefix)?,
_ => {
if export_prefix {
unexpected("--export-prefix", subcommand)?;
}
if with_pwsh_env_prefix {
unexpected("--with-pwsh-env-prefix", subcommand)?;
}
ShowEnvFormat::default()
}
}
};
if doc || doctests {
let flag = if doc { "--doc" } else { "--doctests" };
match subcommand {
Expand Down Expand Up @@ -1211,7 +1268,7 @@ impl Args {
branch,
mcdc,
},
show_env: ShowEnvOptions { export_prefix },
show_env: ShowEnvOptions { show_env_format },
doctests,
ignore_run_fail,
lib,
Expand Down
3 changes: 1 addition & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,7 @@ struct ShowEnvWriter<W: io::Write> {

impl<W: io::Write> EnvTarget for ShowEnvWriter<W> {
fn set(&mut self, key: &str, value: &str) -> Result<()> {
let prefix = if self.options.export_prefix { "export " } else { "" };
writeln!(self.writer, "{prefix}{key}={}", shell_escape::escape(value.into()))
writeln!(self.writer, "{}", self.options.show_env_format.export_string(key, value))
.context("failed to write env to stdout")
}
fn unset(&mut self, key: &str) -> Result<()> {
Expand Down
24 changes: 24 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod auxiliary;

use std::path::Path;

use cargo_config2::Flags;
use fs_err as fs;

use self::auxiliary::{
Expand Down Expand Up @@ -286,6 +287,25 @@ fn open_report() {
fn show_env() {
cargo_llvm_cov("show-env").assert_success().stdout_not_contains("export");
cargo_llvm_cov("show-env").arg("--export-prefix").assert_success().stdout_contains("export");

let mut flags = Flags::default();
flags.push("--deny warnings");
flags.push("--cfg=tests");
let flags = flags.encode().unwrap();

cargo_llvm_cov("show-env")
.env("CARGO_ENCODED_RUSTFLAGS", flags)
.arg("--with-pwsh-env-prefix")
.assert_success()
// Verify the prefix related content + the encoding of "--"
.stdout_contains("$env:CARGO_ENCODED_RUSTFLAGS=\"`u{2d}`u{2d}")
// Verify binary character didn't lead to incompatible output for pwsh
.stdout_contains("`u{1f}");
cargo_llvm_cov("show-env")
.arg("--export-prefix")
.arg("--with-pwsh-env-prefix")
.assert_failure()
.stderr_contains("may not be used together with");
}

#[test]
Expand All @@ -298,6 +318,10 @@ fn invalid_arg() {
.arg("--export-prefix")
.assert_failure()
.stderr_contains("invalid option '--export-prefix'");
cargo_llvm_cov(subcommand)
.arg("--with-pwsh-env-prefix")
.assert_failure()
.stderr_contains("invalid option '--with-pwsh-env-prefix'");
}
if !matches!(subcommand, "" | "test") {
if matches!(subcommand, "nextest" | "nextest-archive") {
Expand Down

0 comments on commit c0715a3

Please sign in to comment.