From 22dc3b061bc828e8bc5bb3141ce50d358d6c464b Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Mon, 7 Sep 2026 18:51:27 +0200 Subject: [PATCH] fix(cargo-anvil): shorten Windows coverage paths Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 460b188a-08ea-418c-802a-41d557329876 --- .anvil.lock | 4 +- crates/cargo-anvil/docs/design/checks.md | 2 +- .../justfiles/anvil/checks/llvm-cov.just | 16 ++++-- crates/cargo-anvil/tests/recipe_contracts.rs | 57 +++++++++++++++++++ justfiles/anvil/checks/llvm-cov.just | 16 ++++-- 5 files changed, 84 insertions(+), 11 deletions(-) diff --git a/.anvil.lock b/.anvil.lock index 19659c75..4a5a34d6 100644 --- a/.anvil.lock +++ b/.anvil.lock @@ -1,7 +1,7 @@ version = 1 tool = "anvil" tool_version = "0.8.0" -catalog_checksum = "sha256:25beddc77b7a0f7c6d7569bd7d252f63770603600e5f3a2701eb0c5adf8ca44f" +catalog_checksum = "sha256:b878cd5259480f652389bae4c82c73bf8a776386113600d8c76e6f2b8353fd47" [[file]] path = ".anvil/container/Dockerfile.dockerignore" @@ -125,7 +125,7 @@ checksum = "sha256:7aa0560dc9088b33e80cd55aee0a25090cbd3d3610652afd3734d0ca52ee7 [[file]] path = "justfiles/anvil/checks/llvm-cov.just" -checksum = "sha256:a6c45a290030fc56ab39ce92d7f89b7169485edc58d931381bf7957eec1622bd" +checksum = "sha256:36986c41d7eb1a1d3c9da2243b5e3b176b4d353be0a3d2c6fe296f7c2fa67a6c" [[file]] path = "justfiles/anvil/checks/loom.just" diff --git a/crates/cargo-anvil/docs/design/checks.md b/crates/cargo-anvil/docs/design/checks.md index ea68717e..615337a7 100644 --- a/crates/cargo-anvil/docs/design/checks.md +++ b/crates/cargo-anvil/docs/design/checks.md @@ -205,7 +205,7 @@ matrix overhead. | Check | Invocation | Source | |--------------|-----------------------------------------------------------------------------|--------| -| `llvm-cov` | Runs tests for every affected package under both feature configurations. Packages with a positive coverage threshold run through self-contained `cargo + llvm-cov nextest --no-report` invocations and produce per-config lcov/cobertura reports. Packages declaring `min-lines-percent = 0` still run through plain `cargo nextest`; the opt-out disables measurement and gating, never tests. Per-config reports avoid Windows command-line overflow and are reconciled downstream by cargo-coverage-gate, Codecov, and ADO. Codecov is display-only; the local coverage gate is authoritative. | oxidizer, oxidizer-github; gate via [`cargo-coverage-gate`](../../../cargo-coverage-gate) | +| `llvm-cov` | Runs tests for every affected package under both feature configurations. Packages with a positive coverage threshold run through self-contained `cargo + llvm-cov nextest --no-report` invocations and produce per-config lcov/cobertura reports. Packages declaring `min-lines-percent = 0` still run through plain `cargo nextest`; the opt-out disables measurement and gating, never tests. Per-config reports reduce each `llvm-cov` object list, and Windows uses the compact private build directory `target/c` to shorten every object path under the CreateProcess command-line limit. Reports are reconciled downstream by cargo-coverage-gate, Codecov, and ADO. Codecov is display-only; the local coverage gate is authoritative. | oxidizer, oxidizer-github; gate via [`cargo-coverage-gate`](../../../cargo-coverage-gate) | | `doc-test` | Two cargo-test runs over the same affected set: `cargo test --doc --workspace --all-features --locked` and `cargo test --doc --workspace --locked` (default features). Running both catches doctests that only compile under one feature configuration (oxidizer-github runs both). nextest does not run doctests, so this stays a separate cargo-test invocation. | oxidizer, oxidizer-github | | `examples` | `cargo build --workspace --examples --all-features --locked` -- verifies that example targets compile. Local `--run` executes selected examples after compilation with a bounded timeout; cloud workflows never pass it. Packages exclude interactive, credentialed, or otherwise unsuitable examples from an unfiltered run with `[package.metadata.anvil.examples] no-run = ["name"]`. An explicit `--example ` overrides the default exclusion. | oxidizer, oxidizer-github | diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just index ce5718f1..cbce532a 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just @@ -93,6 +93,13 @@ anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact Write-Host 'anvil-llvm-cov: all affected packages opted out of coverage; tests completed' exit 0 } + # cargo-llvm-cov passes every instrumented object to llvm-cov on one + # command line. Keep its private Windows build directory compact so the + # repeated object paths leave enough room under CreateProcess's 32,767 + # character limit. Preserve an explicit caller override. + if ($IsWindows -and -not $env:CARGO_LLVM_COV_TARGET_DIR) { + $env:CARGO_LLVM_COV_TARGET_DIR = 'target/c' + } # The *report* output directory (target/coverage/) is something we # choose and must exist before --output-path runs. [System.IO.Directory]::CreateDirectory('target/coverage') | Out-Null @@ -104,11 +111,12 @@ anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact # # Crucially this does NOT merge both configs' objects into one report. # A single `llvm-cov report` over the union shells one `--object=` per - # test binary and, on a large workspace, overflows the Windows + # test binary and, on a large workspace, can overflow the Windows # CreateProcess command-line limit (os error 206, "filename or extension - # is too long"). Each per-config report references only ~half the - # objects and stays under the limit. The two configs are reconciled - # *downstream* instead of via a merged report: + # is too long"). Per-config reports reduce the object count, while the + # compact Windows target directory above shortens every remaining object + # path. The two configs are reconciled *downstream* instead of via a + # merged report: # * cargo-coverage-gate merges the two lcov files at the line level # (per-line counts summed) -- this is the gate; # * Codecov ingests both lcov files (it coalesces uploads); diff --git a/crates/cargo-anvil/tests/recipe_contracts.rs b/crates/cargo-anvil/tests/recipe_contracts.rs index e226f7f4..b6222e1e 100644 --- a/crates/cargo-anvil/tests/recipe_contracts.rs +++ b/crates/cargo-anvil/tests/recipe_contracts.rs @@ -71,6 +71,12 @@ fn bolero_uses_its_supported_release_profile_option() { ); } +#[test] +fn llvm_cov_shortens_its_private_windows_target_directory() { + assert!(LLVM_COV.contains("if ($IsWindows -and -not $env:CARGO_LLVM_COV_TARGET_DIR)")); + assert!(LLVM_COV.contains("$env:CARGO_LLVM_COV_TARGET_DIR = 'target/c'")); +} + #[test] fn developer_options_are_explicit_and_cloud_defaults_stay_non_interactive() { assert!(BUILD.contains("[arg(\"package\", long")); @@ -109,6 +115,9 @@ if ($env:FAKE_CARGO_TOOLCHAIN_LOG) { if ($env:FAKE_CARGO_AUTO_INSTALL_LOG) { Add-Content -LiteralPath $env:FAKE_CARGO_AUTO_INSTALL_LOG -Value $env:RUSTUP_AUTO_INSTALL } +if ($env:FAKE_CARGO_LLVM_COV_TARGET_DIR_LOG) { + Add-Content -LiteralPath $env:FAKE_CARGO_LLVM_COV_TARGET_DIR_LOG -Value $env:CARGO_LLVM_COV_TARGET_DIR +} if ($args -contains 'each') { exit [int]$env:FAKE_EACH_EXIT } @@ -1562,6 +1571,54 @@ fn all_coverage_opted_out_packages_run_both_test_configurations() { assert_failed(&failed, "plain nextest failure for an opted-out package"); } +#[cfg(windows)] +#[test] +fn windows_coverage_uses_the_compact_target_directory() { + if !tools_available() { + return; + } + let tmp = fixture( + &[("llvm-cov.just", LLVM_COV), ("impact.just", IMPACT)], + &[ + "anvil-component-nightly-llvm-tools-validate-prereqs", + "anvil-tool-cargo-llvm-cov-validate-prereqs", + "anvil-tool-cargo-nextest-validate-prereqs", + "anvil-tool-cargo-coverage-gate-validate-prereqs", + "anvil-component-nightly-llvm-tools-install", + "anvil-tool-cargo-llvm-cov-install installer", + "anvil-tool-cargo-nextest-install installer", + "anvil-tool-cargo-coverage-gate-install installer", + "anvil-impact", + ], + ); + let target_dir_log = tmp.path().join("target-dir.log"); + seed_include(tmp.path(), "affected", "--package fixture@0.1.0 --package measured@0.1.0"); + let output = run_just( + tmp.path(), + &["anvil-llvm-cov"], + &[ + ("FAKE_SECOND_PACKAGE_NAME", OsStr::new("measured")), + ("FAKE_CARGO_LLVM_COV_TARGET_DIR_LOG", target_dir_log.as_os_str()), + ], + ); + assert!( + output.status.success(), + "Windows coverage path should succeed:\n{}", + String::from_utf8_lossy(&output.stderr) + ); + let configured_dirs: Vec<_> = fs::read_to_string(target_dir_log) + .unwrap() + .lines() + .filter(|line| !line.is_empty()) + .map(str::to_owned) + .collect(); + assert!(!configured_dirs.is_empty()); + assert!( + configured_dirs.iter().all(|dir| dir == "target/c"), + "unexpected coverage target directories: {configured_dirs:?}" + ); +} + #[cfg(windows)] #[test] fn windows_arm64_fallback_accepts_empty_nextest_sets_in_both_configurations() { diff --git a/justfiles/anvil/checks/llvm-cov.just b/justfiles/anvil/checks/llvm-cov.just index ce5718f1..cbce532a 100644 --- a/justfiles/anvil/checks/llvm-cov.just +++ b/justfiles/anvil/checks/llvm-cov.just @@ -93,6 +93,13 @@ anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact Write-Host 'anvil-llvm-cov: all affected packages opted out of coverage; tests completed' exit 0 } + # cargo-llvm-cov passes every instrumented object to llvm-cov on one + # command line. Keep its private Windows build directory compact so the + # repeated object paths leave enough room under CreateProcess's 32,767 + # character limit. Preserve an explicit caller override. + if ($IsWindows -and -not $env:CARGO_LLVM_COV_TARGET_DIR) { + $env:CARGO_LLVM_COV_TARGET_DIR = 'target/c' + } # The *report* output directory (target/coverage/) is something we # choose and must exist before --output-path runs. [System.IO.Directory]::CreateDirectory('target/coverage') | Out-Null @@ -104,11 +111,12 @@ anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact # # Crucially this does NOT merge both configs' objects into one report. # A single `llvm-cov report` over the union shells one `--object=` per - # test binary and, on a large workspace, overflows the Windows + # test binary and, on a large workspace, can overflow the Windows # CreateProcess command-line limit (os error 206, "filename or extension - # is too long"). Each per-config report references only ~half the - # objects and stays under the limit. The two configs are reconciled - # *downstream* instead of via a merged report: + # is too long"). Per-config reports reduce the object count, while the + # compact Windows target directory above shortens every remaining object + # path. The two configs are reconciled *downstream* instead of via a + # merged report: # * cargo-coverage-gate merges the two lcov files at the line level # (per-line counts summed) -- this is the gate; # * Codecov ingests both lcov files (it coalesces uploads);