From 71e845b5f95488328908183927bc08eca3120ca8 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 15:11:29 +0300 Subject: [PATCH 01/17] Bootstrap score_coverage module from tooling/coverage Move the LLVM source-based coverage pipeline out of eclipse-score/tooling (coverage/ at commit 9a61f42) into its own Bazel module, score_coverage. Layout: - defs.bzl, BUILD: consumer API at the module root (score_coverage_scope, score_coverage_reporter, //:merger, //:generate_coverage_html, //:enable_llvm_coverage_for_death_tests). The root package loads only runtime dependencies, so it stays loadable for consumers that do not see this module's dev dependencies. - score_coverage/: implementation and unit tests. The Python package is named score_coverage (not coverage) so it cannot shadow coverage.py when the module's own tests run under bazel coverage. - integration_tests/: consumer workspace, now overriding score_coverage instead of score_tooling; excluded from //... via .bazelignore. - tools/: copyright and format targets (dev-only loads). - .github/workflows: tests, integration test, format, copyright, gitlint, license check. Adaptations from the original: @score_tooling//coverage: labels become @score_coverage//:, the pip hub is renamed pip_score_coverage to avoid a collision with score_tooling < 3, and the repo-bound combined_report and llvm_profile_wrapper helpers are not carried over. Verified locally: bazel build //..., 3 unit tests, format and copyright checks, and integration_tests/run_integration_test.sh (all 9 checks pass; raw 58.82% / effective 61.76% line coverage on the fixture workspace). Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .bazelignore | 3 + .bazelrc | 24 + .bazelversion | 1 + .github/actions/gitlint/action.yml | 41 + .github/workflows/copyright.yml | 25 + .github/workflows/format.yml | 39 + .github/workflows/gitlint.yml | 33 + .github/workflows/license_check.yml | 28 + .github/workflows/tests.yml | 80 + .gitignore | 20 + BUILD | 84 + COVERAGE_GUIDE.md | 341 + LICENSE | 177 + MODULE.bazel | 77 + MODULE.bazel.lock | 5653 +++++++++++++++++ NOTICE | 38 + README.md | 348 +- REUSE.toml | 24 + defs.bzl | 90 + integration_tests/.bazelrc | 78 + integration_tests/.bazelversion | 1 + integration_tests/BUILD | 16 + integration_tests/MODULE.bazel | 68 + integration_tests/MODULE.bazel.lock | 877 +++ integration_tests/run_integration_test.sh | 156 + integration_tests/rust/BUILD | 37 + integration_tests/rust/lib.rs | 45 + integration_tests/rust/main.rs | 19 + integration_tests/src/BUILD | 37 + integration_tests/src/coverable.cpp | 27 + integration_tests/src/coverable.h | 24 + integration_tests/src/coverable_test.cpp | 28 + integration_tests/src/uncovered.cpp | 24 + integration_tests/src/uncovered.h | 24 + integration_tests/tools/coverage/BUILD | 38 + .../coverage/coverage_justifications.yaml | 25 + pyproject.toml | 47 + score_coverage/BUILD | 116 + score_coverage/coverage_scope.bzl | 202 + score_coverage/coverage_summary.py | 338 + score_coverage/effective_coverage.py | 1180 ++++ score_coverage/generate_coverage_html.sh | 298 + score_coverage/justify.py | 409 ++ score_coverage/merger.py | 250 + score_coverage/reporter.py | 775 +++ score_coverage/reporter_wrapper.bzl | 155 + score_coverage/requirements.in | 1 + score_coverage/requirements_3_11.txt | 81 + score_coverage/requirements_3_12.txt | 81 + score_coverage/tests/BUILD | 32 + score_coverage/tests/coverage_summary_test.py | 181 + score_coverage/tests/merger_test.py | 116 + score_coverage/tests/reporter_test.py | 166 + tools/BUILD | 49 + 54 files changed, 13125 insertions(+), 2 deletions(-) create mode 100644 .bazelignore create mode 100644 .bazelrc create mode 100644 .bazelversion create mode 100644 .github/actions/gitlint/action.yml create mode 100644 .github/workflows/copyright.yml create mode 100644 .github/workflows/format.yml create mode 100644 .github/workflows/gitlint.yml create mode 100644 .github/workflows/license_check.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 BUILD create mode 100644 COVERAGE_GUIDE.md create mode 100644 LICENSE create mode 100644 MODULE.bazel create mode 100644 MODULE.bazel.lock create mode 100644 NOTICE create mode 100644 REUSE.toml create mode 100644 defs.bzl create mode 100644 integration_tests/.bazelrc create mode 100644 integration_tests/.bazelversion create mode 100644 integration_tests/BUILD create mode 100644 integration_tests/MODULE.bazel create mode 100644 integration_tests/MODULE.bazel.lock create mode 100755 integration_tests/run_integration_test.sh create mode 100644 integration_tests/rust/BUILD create mode 100644 integration_tests/rust/lib.rs create mode 100644 integration_tests/rust/main.rs create mode 100644 integration_tests/src/BUILD create mode 100644 integration_tests/src/coverable.cpp create mode 100644 integration_tests/src/coverable.h create mode 100644 integration_tests/src/coverable_test.cpp create mode 100644 integration_tests/src/uncovered.cpp create mode 100644 integration_tests/src/uncovered.h create mode 100644 integration_tests/tools/coverage/BUILD create mode 100644 integration_tests/tools/coverage/coverage_justifications.yaml create mode 100644 pyproject.toml create mode 100644 score_coverage/BUILD create mode 100644 score_coverage/coverage_scope.bzl create mode 100644 score_coverage/coverage_summary.py create mode 100644 score_coverage/effective_coverage.py create mode 100755 score_coverage/generate_coverage_html.sh create mode 100644 score_coverage/justify.py create mode 100644 score_coverage/merger.py create mode 100644 score_coverage/reporter.py create mode 100644 score_coverage/reporter_wrapper.bzl create mode 100644 score_coverage/requirements.in create mode 100644 score_coverage/requirements_3_11.txt create mode 100644 score_coverage/requirements_3_12.txt create mode 100644 score_coverage/tests/BUILD create mode 100644 score_coverage/tests/coverage_summary_test.py create mode 100644 score_coverage/tests/merger_test.py create mode 100644 score_coverage/tests/reporter_test.py create mode 100644 tools/BUILD diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..c6bb27f --- /dev/null +++ b/.bazelignore @@ -0,0 +1,3 @@ +# Nested consumer workspace with its own MODULE.bazel; built by +# integration_tests/run_integration_test.sh, not as part of //... +integration_tests diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..2880dd4 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,24 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ +common --registry=https://bcr.bazel.build + +# score_tooling (dev dependency) transitively brings android_sdk_repository +# into the graph; an empty ANDROID_HOME keeps it inert on stripped runners. +common --repo_env=ANDROID_HOME= + +test --test_output=errors + +# Per-developer overrides (disk cache, etc.). Must stay last: bazelrc is last-wins. +try-import %workspace%/user.bazelrc diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..df5119e --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +8.7.0 diff --git a/.github/actions/gitlint/action.yml b/.github/actions/gitlint/action.yml new file mode 100644 index 0000000..2857a41 --- /dev/null +++ b/.github/actions/gitlint/action.yml @@ -0,0 +1,41 @@ +# ******************************************************************************* +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: "Gitlint Action" +description: "An action to install and run Gitlint on PR commits" +inputs: + pr-number: + description: "Pull Request number used to fetch commits" + required: true + base-branch: + description: "Base branch to compare commits against (default: origin/main)" + default: "origin/main" + required: false +runs: + using: "docker" + image: "jorisroovers/gitlint:0.19.1" + entrypoint: /bin/sh + args: + - -c + - | + git config --global --add safe.directory /github/workspace && \ + git fetch origin +refs/heads/main:refs/remotes/origin/main && \ + git fetch origin +refs/pull/${{ inputs.pr-number }}/head && \ + if ! gitlint --commits origin/main..HEAD; then \ + echo -e "\nWARNING: Your commit message does not follow the required format." && \ + echo "Formatting rules: https://eclipse-score.github.io/score/process/guidance/git/index.html" && \ + echo -e "To fix your commit message, run:\n" && \ + echo " git commit --amend" && \ + echo "Then update your commit (fix gitlint warnings). Finally, force-push:" && \ + echo " git push --force-with-lease" && \ + exit 1; \ + fi diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml new file mode 100644 index 0000000..a69d02a --- /dev/null +++ b/.github/workflows/copyright.yml @@ -0,0 +1,25 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: Copyright checks +on: + pull_request: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +permissions: + contents: read +jobs: + copyright-check: + uses: eclipse-score/cicd-workflows/.github/workflows/copyright.yml@93aac16ada7d247bbb6ae926509ddea74cf5213a # v0.0.2 + with: + bazel-target: "run //tools:copyright.check" diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..3b74800 --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,39 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: Formatting checks +on: + pull_request: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +permissions: + contents: read +env: + ANDROID_HOME: "" + ANDROID_SDK_ROOT: "" +jobs: + formatting-check: + runs-on: ubuntu-24.04 + steps: + - name: Checkout repository + uses: actions/checkout@v7.0.1 + - uses: castler/setup-bazel@cache-optimized + with: + bazelisk-cache: true + disk-cache: format + repository-cache: true + cache-optimized: true + cache-save: ${{ github.ref == 'refs/heads/main' }} + - name: Run formatting checks + # score_tooling >= 2 tags format tests `manual`; lift the tag filter. + run: bazel test --test_tag_filters= //tools:format.check diff --git a/.github/workflows/gitlint.yml b/.github/workflows/gitlint.yml new file mode 100644 index 0000000..567223e --- /dev/null +++ b/.github/workflows/gitlint.yml @@ -0,0 +1,33 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: Gitlint check +on: + pull_request: + types: [opened, synchronize, reopened] +permissions: + contents: read +jobs: + lint-commits: + name: check-commit-messages + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v7.0.1 + with: + fetch-depth: 0 + - name: Run Gitlint Action + if: ${{ github.event_name == 'pull_request' }} + uses: ./.github/actions/gitlint + with: + pr-number: ${{ github.event.number }} + base-branch: ${{ github.event.pull_request.base.ref }} diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yml new file mode 100644 index 0000000..0667027 --- /dev/null +++ b/.github/workflows/license_check.yml @@ -0,0 +1,28 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: License check preparation +on: + pull_request_target: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +permissions: + pull-requests: write + issues: write +jobs: + license-check: + uses: eclipse-score/cicd-workflows/.github/workflows/license-check.yml@main + with: + repo-url: "${{ github.server_url }}/${{ github.repository }}" + secrets: + dash-api-token: ${{ secrets.ECLIPSE_GITLAB_API_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..20cd228 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,80 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: Tests +on: + pull_request: + types: [opened, reopened, synchronize] + push: + branches: + - main + merge_group: + types: [checks_requested] +concurrency: + group: tests-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} +permissions: + contents: read +env: + ANDROID_HOME: "" + ANDROID_SDK_ROOT: "" +jobs: + unit_tests: + runs-on: ubuntu-24.04 + steps: + - name: Checkout repository + uses: actions/checkout@v7.0.1 + - name: Free Disk Space (Ubuntu) + uses: eclipse-score/more-disk-space@v1 + with: + level: 4 + - uses: castler/setup-bazel@cache-optimized + with: + bazelisk-cache: true + disk-cache: unit_tests + repository-cache: true + cache-optimized: true + cache-save: ${{ github.ref == 'refs/heads/main' }} + - name: Build everything + run: bazel build --lockfile_mode=error //... + - name: Run unit tests + run: bazel test --lockfile_mode=error //score_coverage/tests:all + - name: Ensure the lockfile is up to date + run: | + bazel mod deps --lockfile_mode=update + git diff --exit-code MODULE.bazel.lock + integration_tests: + runs-on: ubuntu-24.04 + steps: + - name: Checkout repository + uses: actions/checkout@v7.0.1 + - name: Free Disk Space (Ubuntu) + uses: eclipse-score/more-disk-space@v1 + with: + level: 4 + - uses: castler/setup-bazel@cache-optimized + with: + bazelisk-cache: true + disk-cache: integration_tests + repository-cache: true + cache-optimized: true + cache-save: ${{ github.ref == 'refs/heads/main' }} + - name: Run the end-to-end pipeline test (C++ + Rust consumer workspace) + run: integration_tests/run_integration_test.sh + - name: Upload coverage report of the integration workspace + if: always() + uses: actions/upload-artifact@v4 + with: + name: integration_coverage_report + path: integration_tests/coverage_artifact + if-no-files-found: ignore + retention-days: 10 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0f3b70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# SPDX-License-Identifier: Apache-2.0 + +bazel-* +external +.vscode/ +.clwb/ +user.bazelrc + +__pycache__ +.ruff_cache/ + +# Outputs of integration_tests/run_integration_test.sh +integration_tests/coverage_artifacts.zip +integration_tests/lcov.dat +integration_tests/coverage_linux/ +integration_tests/summary.md +integration_tests/step_summary.md +integration_tests/coverage_artifact/ diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..f7c2622 --- /dev/null +++ b/BUILD @@ -0,0 +1,84 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_cc//cc/toolchains:args.bzl", "cc_args") +load("@rules_cc//cc/toolchains:feature.bzl", "cc_feature") + +# IMPORTANT: this file is loaded by every consumer of @score_coverage. It must +# only load from the module's runtime dependencies (never from dev +# dependencies such as score_tooling): dev deps are dropped when this module is +# not the root, and a failing load here breaks the whole @score_coverage// +# package for the consumer. Repository hygiene targets live in //tools. +package(default_visibility = ["//visibility:public"]) + +exports_files([ + "MODULE.bazel", + "pyproject.toml", +]) + +# ============================================================================= +# Consumer-facing labels. Everything a consumer references lives at the root +# of this module: +# load("@score_coverage//:defs.bzl", "score_coverage_scope", "score_coverage_reporter") +# coverage:llvm_cov --coverage_output_generator=@score_coverage//:merger +# bazel run @score_coverage//:generate_coverage_html -- ... +# llvm.toolchain(extra_known_features = ["@score_coverage//:enable_llvm_coverage_for_death_tests"]) +# The implementation lives in //score_coverage. +# ============================================================================= + +alias( + name = "merger", + actual = "//score_coverage:merger", +) + +alias( + name = "generate_coverage_html", + actual = "//score_coverage:generate_coverage_html", +) + +# Used by generate_coverage_html.sh through nested `bazel run` invocations. +alias( + name = "justify", + actual = "//score_coverage:justify", +) + +alias( + name = "effective_coverage", + actual = "//score_coverage:effective_coverage", +) + +alias( + name = "coverage_summary", + actual = "//score_coverage:coverage_summary", +) + +# These compile time options are required to cover abnormal termination cases +# (death tests). LLVM provides them in combination with a specific profile +# setting which is enabled in Bazel via LLVM_PROFILE_CONTINUOUS_MODE. +cc_args( + name = "runtime_relocation_args", + actions = [ + "@rules_cc//cc/toolchains/actions:compile_actions", + "@rules_cc//cc/toolchains/actions:link_actions", + ], + args = [ + "-mllvm", + "-runtime-counter-relocation", + ], +) + +cc_feature( + name = "enable_llvm_coverage_for_death_tests", + args = [":runtime_relocation_args"], + feature_name = "enable_llvm_coverage_for_death_tests", +) diff --git a/COVERAGE_GUIDE.md b/COVERAGE_GUIDE.md new file mode 100644 index 0000000..72cdab7 --- /dev/null +++ b/COVERAGE_GUIDE.md @@ -0,0 +1,341 @@ + + +# Unified Code Coverage (LLVM) — how the pipeline works + +This document explains, from first principles, how the unified Rust + C++ +coverage pipeline in `@score_coverage` works: the mechanism, the +module/consumer split, the non-obvious pitfalls, and where the pipeline comes +from. For the step-by-step consumer setup see [README.md](README.md); for a +working consumer workspace see [integration_tests/](integration_tests/). + +--- + +## 1. Background concepts + +### 1.1 What "code coverage" means here + +When we run the test suite, we want to know **which lines of production code +were actually executed**. The compiler helps: it can *instrument* the code — +insert tiny counters at every branch and statement. When an instrumented test +binary runs, it writes the counter values to a file. Tooling then maps those +counters back to source lines and produces a report: green = executed, +red = never executed. + +### 1.2 Bazel in three paragraphs + +Bazel is the build system used by S-CORE. Code is organized into **targets** +(a library, a binary, a test), declared in files named `BUILD`. Targets +reference each other by **labels** like `//src/rust/mycrate:tests` +(`//path/to/package:target_name`). + +External dependencies (compilers, libraries) are declared in `MODULE.bazel`. +A **toolchain** is Bazel's packaging of a compiler + flags; you can register +several and select one per build. Command-line defaults live in `.bazelrc`, +grouped into named **configs**: `bazel test --config=foo` applies all lines +starting with `test:foo`. + +Bazel has a built-in coverage mode: `bazel coverage ` builds the +targets with instrumentation, runs the tests, and post-processes the results. +Two hooks matter for us: `--coverage_output_generator` (a tool that processes +each test's raw coverage output) and `--coverage_report_generator` (a tool +that combines everything into the final report). **This pipeline replaces +both with its own tools** — that is its core. + +### 1.3 Rust in two paragraphs + +Rust code is organized into **crates** (≈ libraries/binaries). The Rust +compiler is `rustc`; S-CORE uses **Ferrocene**, a safety-certified Rust +toolchain distribution. Bazel builds Rust via the `rules_rust` plugin. + +Crucially, `rustc` is built on the same compiler backend as Clang (**LLVM**). +That means Rust and C++ can use the *same* coverage instrumentation format — +which is what makes a single unified report possible. + +### 1.4 The LLVM coverage toolchain + +The pipeline is built on LLVM's "source-based coverage": + +| Artifact | Produced by | Contains | +|---|---|---| +| instrumented binary | Clang (C++) / rustc (Rust) with coverage flags | counters + a "covmap" mapping counters → source lines | +| `.profraw` | running the instrumented test | raw counter values | +| `.profdata` | `llvm-profdata merge` | merged, indexed counters | +| HTML / LCOV / text report | `llvm-cov show/export/report` | human- and machine-readable coverage | + +--- + +## 2. How the pipeline works + +There are two phases. + +### 2.1 Phase 1 — collection (`bazel coverage`) + +``` +bazel coverage --config=llvm_cov //... --build_tests_only +``` + +The `llvm_cov` config (the consumer copies it from +[integration_tests/.bazelrc](integration_tests/.bazelrc)) does four things: + +1. **Swaps the compilers.** C++ is compiled with a hermetic Clang/LLVM + toolchain (`@llvm_toolchain`) instead of GCC; Rust with a Ferrocene + toolchain that has LLVM coverage tools attached — wired in automatically + by score_toolchains_rust >= 0.9.2 from the coverage-tools tarball, see §5. + Both emit the same covmap format. +2. **Turns on instrumentation.** `--experimental_use_llvm_covmap` plus the + `coverage` feature for C++; `rules_rust` adds `-Cinstrument-coverage` to + rustc automatically once the toolchain declares coverage tools. An extra + flag (`-Cllvm-args=-runtime-counter-relocation`) enables "continuous + mode" so coverage survives even if a test terminates abnormally (same + purpose as the `enable_llvm_coverage_for_death_tests` cc_feature on the + C++ side). **Branch coverage** needs one more flag per language: Clang + emits branch regions by default, but rustc only does so with + `-Zcoverage-options=branch` — an unstable option that works on + *rolling* (nightly-based) Ferrocene builds. On a stable-channel + Ferrocene the flag must be dropped (Rust branch columns revert to `-`) + until rustc stabilizes it. +3. **Installs the per-test tool** + (`--coverage_output_generator=@score_coverage//:merger`). After + each test runs, `merger.py` finds the test's `.profraw` files, merges + them into one `.profdata` with `llvm-profdata`, records which + instrumented binary was involved, and zips both up as the test's + `coverage.dat`. +4. **Installs the final tool** (`--coverage_report_generator=` the + consumer's `score_coverage_reporter` target). After all tests finish, + `reporter.py` merges every per-test `.profdata` into one, then runs + `llvm-cov` three times: `show` → HTML report, `export` → LCOV data (for + dashboards), `report` → text summary. All three are zipped into + `bazel-out/_coverage/_coverage_report.dat`. + +**Scope — which files appear in the report.** LLVM covmap instruments +*everything*, including test code and third-party libraries. Filtering +happens at report time using an **allowlist** generated by the consumer's +`score_coverage_scope` target (`coverage_scope.bzl`): an aspect walks the +dependency graph starting from the listed production targets and collects +every in-workspace source file they own. The reporter excludes everything +else (test sources, googletest, external deps, ...). + +**Baseline — files with no tests at all.** A file that no test executes +produces no coverage data, so naive tooling silently omits it — it looks +like there is no problem when in fact coverage is 0%. The scope aspect +therefore also collects the compiled libraries/binaries (`.a` archives for +cc_library/rust_library, the coverage-built executable for rust_binary), and +the reporter runs `llvm-cov --empty-profile` over them so untested files +show up with **exact** 0% line and branch entries — the denominators come +from the compiler's own coverage map, not from any source-text heuristic. +Rust rlib archives need special handling here: their leading `lib.rmeta` +member makes llvm-cov reject the whole archive, so the reporter expands them +into their `.o` members first. + +### 2.2 Phase 2 — report generation & gating + +``` +bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml +``` + +`generate_coverage_html.sh` unpacks the HTML from the zip, then applies the +**justification system**: + +- `justify.py` reads the consumer's `coverage_justifications.yaml` plus + in-code markers and produces a manifest of "argued" lines. A justification + says: *this line cannot reasonably be covered by a test, and here is why* + (e.g. defensive code for conditions that cannot occur). Markers work in + both languages: + + ```rust + unreachable!(); // COV_JUSTIFIED my-justification-id + ``` + ```cpp + default: return Error; // COV_JUSTIFIED my-justification-id + ``` + + Every marker id must exist in the YAML with a category and a written + reason — a justification is a reviewed engineering argument, not an + opt-out. + +- `effective_coverage.py` recolors justified lines **orange** in the HTML + (with the reason as tooltip) and computes: + + ``` + raw coverage = covered / total + effective coverage = (covered + justified) / total + ``` + + It also flags **stale** justifications — lines that are justified but + meanwhile covered by a test — so the database stays clean. + +- Finally the script compares effective line coverage against the + `COVERAGE_THRESHOLD` environment variable (default **100**) and **fails + (exit 1)** when below. The model: every uncovered line must eventually be + either tested or justified; the threshold is ratcheted up as gaps close. + +- Optionally a **markdown job summary** is emitted (`--summary-md `, or + appended to `GITHUB_STEP_SUMMARY` automatically inside GitHub Actions when + the flag is absent): overall/per-directory tables computed from the LCOV + data (which includes the exact-0% baseline records), raw-vs-effective + numbers when justifications ran, and collapsible least-covered/0% file + lists. It is written before the gate decides the exit code, so a failing + gate still leaves the summary on the run page. + +### 2.3 Day-to-day commands + +```bash +# collect coverage (Rust + C++, one run) +bazel coverage --config=llvm_cov //... --build_tests_only + +# report without gating +COVERAGE_THRESHOLD=0 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml + +# open it +xdg-open coverage_linux/index.html + +# CI-style archive (HTML + LCOV + justification report + JUnit XMLs) +bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml --archive my-report +``` + +> **Do not** combine `--config=llvm_cov` with configs that register other +> C++ toolchains (e.g. a GCC host config): the last `--extra_toolchains` +> wins resolution, GCC cannot produce covmap data, and the report script +> fails loudly on the resulting non-zip report. + +--- + +## 3. The module/consumer split + +Almost everything in the pipeline is generic. What is repo-specific is +exactly three things: **(a)** the list of production targets in the scope, +**(b)** the justification YAML, **(c)** the toolchain pins in MODULE.bazel. +The split follows directly: + +**Lives in `@score_coverage` (shared):** + +| File | Role | +|---|---| +| `merger.py` | per-test profraw → profdata (C++ `objects_list.txt` and Rust ELF-manifest discovery) | +| `reporter.py` | final merge + llvm-cov show/export/report + allowlist filtering + `--empty-profile` baselines + rlib expansion | +| `coverage_scope.bzl` | the scope aspect/rule (CcInfo + CrateInfo) | +| `reporter_wrapper.bzl` + `defs.bzl` | the consumer-facing `score_coverage_scope` / `score_coverage_reporter` API | +| `justify.py`, `effective_coverage.py`, `generate_coverage_html.sh` | justification + gating layer | +| `enable_llvm_coverage_for_death_tests` | cc_feature for continuous-mode profiling | + +**Lives in the consumer repository:** + +| Piece | Why it cannot move | +|---|---| +| `score_coverage_scope(deps = [...])` | names the repo's production targets | +| `score_coverage_reporter(...)` | carries the repo's LLVM tool labels and workspace root | +| `coverage_justifications.yaml` | reviewed, repo-specific engineering arguments | +| MODULE.bazel toolchain blocks | LLVM + Ferrocene pins are per-repo decisions | +| the `coverage:llvm_cov` bazelrc block | bazelrc cannot be imported across modules; copied from the canonical snippet | + +Two wiring details make the external hosting work, both easy to get wrong: + +- **Runfiles paths span repositories.** The reporter wrapper mixes files + from `_main` (the consumer), `score_coverage` and toolchain repos, so every + path in the generated launcher uses rlocation form (`../repo/...` → + `repo/...`), and the launcher derives its own `RUNFILES_DIR` from `$0` — + the inherited value points at the *test's* runfiles tree, not ours. +- **The baseline manifest lists consumer files.** The reporter resolves + manifest entries against `_main` explicitly; using the runfiles library's + "current repository" would resolve against `score_coverage` and find + nothing. + +--- + +## 4. Non-obvious pitfalls (why these lines exist) + +These are the "landmines" discovered while bringing the pipeline up in +communication and persistency: + +1. **Warnings-as-errors under Clang.** Repos whose deps request the + `treat_warnings_as_errors` feature may need + `--features=-treat_warnings_as_errors` **and** + `--host_features=-treat_warnings_as_errors` in the coverage config: + Clang emits warnings GCC doesn't, and Bazel builds the coverage reporter + (and hence the scope's libraries) a second time "as a tool" in a separate + configuration — that's what the `--host_features` variant covers. +2. **Never disable the `coverage` feature** under the LLVM toolchain — it + *is* the instrumentation (`-fprofile-instr-generate + -fcoverage-mapping`). +3. **`-Cllvm-args=-runtime-counter-relocation`** for Rust — without it, + continuous-mode profiling errors out and Rust tests write no `.profraw`. +4. **`llvm-cov report` prints raw covmap paths** (`/proc/self/cwd/...`) — + `--path-equivalence` does not rewrite *displayed* paths; the reporter + normalizes them, otherwise the allowlist silently excludes all C++ + files. +5. **The Rust toolchain lists its own `llvm-cov`/`llvm-profdata` binaries as + coverage metadata** — the merger must skip `external/` entries or the + final merge emits "mismatched data" warnings. +6. **Rust branch coverage is opt-in and channel-dependent** — llvm-cov only + renders branch data that the compiler wrote into the covmap; stable rustc + writes none. `-Zcoverage-options=branch` enables it on nightly-based + toolchains (like the Ferrocene rolling build). Verify with + `llvm-cov export`: the `branches` arrays must be non-empty for Rust + files. + +--- + +## 5. Toolchain provisioning (score_toolchains_rust + ferrocene_toolchain_builder) + +Solved at the source, no consumer configuration needed: + +- `ferrocene_toolchain_builder` >= **1.3.1** ships `llvm-cov`, + `llvm-profdata` and `llvm-cxxfilt` in the coverage-tools tarball, built + from the same LLVM tree as rustc (so the tools can always read the + profraw/covmap the compiler emits). 1.3.1 also rebuilds ALL artifacts from + a single tree — toolchain tarballs, miri-sysroots (now including + `libprofiler_builtins`) and coverage tools are ABI-consistent. +- `score_toolchains_rust` >= **0.9.2** (current: 0.10.0) auto-wires the + tools into the generated `rust_toolchain` whenever the coverage-tools + tarball contains them. rules_rust then instruments crates under + `bazel coverage` and exports `RUST_LLVM_COV`/`RUST_LLVM_PROFDATA` to the + coverage runner. + +Consequently consumers need no coverage-specific Rust toolchain at all: the +**standard** toolchains declared in score_toolchains_rust's own MODULE.bazel +(e.g. `@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu`) +already pin the coverage-tools tarball, so the toolchain registered for +regular builds is the one that produces coverage — same rustc, same LLVM. + +--- + +## 6. Origins and validation evidence + +The pipeline core was built in the `communication` repository +(eclipse-score/communication, Rust support merged with PR #772 and visible +in its nightly coverage reports), then ported to `persistency` and finally +centralized here. During the persistency port, the previous Rust mechanism +(Ferrocene `symbol-report` + `blanket`, per-target 90% gate — the flow this +module's removed `rust_coverage_report` rule drove) was run against the new +pipeline **on the same commit**. Percentages agreed within a few points on +most files (the two tools attribute lines differently: blanket is +symbol-oriented, llvm-cov counts every executable region), with one headline +difference: + +> A 311-line, 0%-covered Rust binary (`kvs_tool.rs`, no tests) was +> **invisible** to the old pipeline while its 90% gate passed. The new +> baseline mechanism reports it at exact 0% and the effective-coverage gate +> accounts for it. + +That gap — untested files silently missing from reports — is the main +correctness argument for this pipeline, alongside unified C++ + Rust +reporting and branch coverage for Rust. + +Planned next step for the ecosystem: a reusable GitHub Actions workflow in +`eclipse-score/cicd-workflows` wrapping the collection + report + artifact +steps, so consumer repos add one `uses:` block instead of a hand-written +job. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..e268882 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,77 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# score_coverage: LLVM source-based code coverage pipeline for Eclipse S-CORE. +# The version is intentionally absent; the S-CORE Bazel registry patches it in +# from the release tag (same mechanism as score_tooling). +module(name = "score_coverage") + +############################################################################### +# Runtime dependencies of the pipeline (visible to consumers) +############################################################################### +bazel_dep(name = "platforms", version = "1.0.0") +bazel_dep(name = "rules_cc", version = "0.2.16") # cc_args/cc_feature for the death-test feature +bazel_dep(name = "rules_python", version = "1.8.5") +bazel_dep(name = "rules_shell", version = "0.6.1") + +# CrateInfo provider consumed by the coverage-scope aspect (coverage_scope.bzl). +bazel_dep(name = "rules_rust", version = "0.68.2-score") + +############################################################################### +# Python toolchain +############################################################################### +DEFAULT_PYTHON_VERSION = "3.12" + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + configure_coverage_tool = True, + python_version = "3.11", +) +python.toolchain( + configure_coverage_tool = True, + is_default = True, + python_version = DEFAULT_PYTHON_VERSION, +) + +############################################################################### +# Pip hub (pyyaml for justify.py). Hub name is unique to this module so it +# never collides with the legacy pip_coverage hub still declared by +# score_tooling < 3. +############################################################################### +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") + +[ + pip.parse( + envsubst = ["PIP_INDEX_URL"], + extra_pip_args = ["--index-url=${PIP_INDEX_URL:-https://pypi.org/simple/}"], + hub_name = "pip_score_coverage", + python_version = "3.{}".format(version), + requirements_lock = "//score_coverage:requirements_3_{}.txt".format(version), + ) + for version in [ + "11", + "12", + ] +] + +use_repo(pip, "pip_score_coverage") + +############################################################################### +# Development-only dependencies (repository hygiene: copyright, formatting) +############################################################################### +bazel_dep(name = "score_tooling", version = "2.2.0", dev_dependency = True) + +# use_format_targets() (from score_tooling) loads these from the ROOT module's +# repo mapping, so the root has to declare them itself. +bazel_dep(name = "aspect_rules_lint", version = "2.7.1", dev_dependency = True) +bazel_dep(name = "buildifier_prebuilt", version = "8.5.1", dev_dependency = True) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock new file mode 100644 index 0000000..ef5ed46 --- /dev/null +++ b/MODULE.bazel.lock @@ -0,0 +1,5653 @@ +{ + "lockFileVersion": 24, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20220623.1/MODULE.bazel": "73ae41b6818d423a11fd79d95aedef1258f304448193d4db4ff90e5e7a0f076c", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.0/MODULE.bazel": "98dc378d64c12a4e4741ad3362f87fb737ee6a0886b2d90c3cdbb4d93ea3e0bf", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16", + "https://bcr.bazel.build/modules/abseil-cpp/20240722.0/MODULE.bazel": "88668a07647adbdc14cb3a7cd116fb23c9dda37a90a1681590b6c9d8339a5b84", + "https://bcr.bazel.build/modules/abseil-cpp/20250127.0/MODULE.bazel": "d1086e248cda6576862b4b3fe9ad76a214e08c189af5b42557a6e1888812c5d5", + "https://bcr.bazel.build/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1", + "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215", + "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/source.json": "d725d73707d01bb46ab3ca59ba408b8e9bd336642ca77a2269d4bfb8bbfd413d", + "https://bcr.bazel.build/modules/abseil-py/2.1.0/MODULE.bazel": "5ebe5bf853769c65707e5c28f216798f7a4b1042015e6a36e6d03094d94bec8a", + "https://bcr.bazel.build/modules/abseil-py/2.1.0/source.json": "0e8fc4f088ce07099c1cd6594c20c7ddbb48b4b3c0849b7d94ba94be88ff042b", + "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896", + "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", + "https://bcr.bazel.build/modules/apple_support/1.17.1/MODULE.bazel": "655c922ab1209978a94ef6ca7d9d43e940cd97d9c172fb55f94d91ac53f8610b", + "https://bcr.bazel.build/modules/apple_support/1.22.1/MODULE.bazel": "90bd1a660590f3ceffbdf524e37483094b29352d85317060b2327fff8f3f4458", + "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", + "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8", + "https://bcr.bazel.build/modules/apple_support/1.24.1/source.json": "cf725267cbacc5f028ef13bb77e7f2c2e0066923a4dab1025e4a0511b1ed258a", + "https://bcr.bazel.build/modules/aspect_bazel_lib/1.31.2/MODULE.bazel": "7bee702b4862612f29333590f4b658a5832d433d6f8e4395f090e8f4e85d442f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/1.38.0/MODULE.bazel": "6307fec451ba9962c1c969eb516ebfe1e46528f7fa92e1c9ac8646bef4cdaa3f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/1.42.2/MODULE.bazel": "2e0d8ab25c57a14f56ace1c8e881b69050417ff91b2fb7718dc00d201f3c3478", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.0.0/MODULE.bazel": "e118477db5c49419a88d78ebc7a2c2cea9d49600fe0f490c1903324a2c16ecd9", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.11.0/MODULE.bazel": "cb1ba9f9999ed0bc08600c221f532c1ddd8d217686b32ba7d45b0713b5131452", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.16.0/MODULE.bazel": "852f9ebbda017572a7c113a2434592dd3b2f55cd9a0faea3d4be5a09a59e4900", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.20.0/MODULE.bazel": "c5565bac49e1973227225b441fad1c938d498d83df62dc5da95b2fab0f0626a2", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.0/MODULE.bazel": "7fe0191f047d4fe4a4a46c1107e2350cbb58a8fc2e10913aa4322d3190dec0bf", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.0/source.json": "369df5b7f2eae82f200fff95cf1425f90dee90a0d0948122060b48150ff0e224", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "491f8681205e31bb57892d67442ce448cda4f472a8e6b3dc062865e29a64f89c", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.9.3/MODULE.bazel": "66baf724dbae7aff4787bf2245cc188d50cb08e07789769730151c0943587c14", + "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.21.0/MODULE.bazel": "77dc393c43ad79398b05865444c5200c6f1aae6765615544f2c7730b5858d533", + "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.21.0/source.json": "062b1d3dba8adcfeb28fe60c185647f5a53ec0487ffe93cf0ae91566596e4b49", + "https://bcr.bazel.build/modules/aspect_rules_js/1.33.1/MODULE.bazel": "db3e7f16e471cf6827059d03af7c21859e7a0d2bc65429a3a11f005d46fc501b", + "https://bcr.bazel.build/modules/aspect_rules_js/1.40.0/MODULE.bazel": "01a1014e95e6816b68ecee2584ae929c7d6a1b72e4333ab1ff2d2c6c30babdf1", + "https://bcr.bazel.build/modules/aspect_rules_js/2.0.0/MODULE.bazel": "b45b507574aa60a92796e3e13c195cd5744b3b8aff516a9c0cb5ae6a048161c5", + "https://bcr.bazel.build/modules/aspect_rules_js/2.3.8/MODULE.bazel": "74bf20a7a6bd5f2be09607fdb4196cfd6f203422ea271752ec2b1afe95426101", + "https://bcr.bazel.build/modules/aspect_rules_js/2.3.8/source.json": "411ec9d79d6f5fe8a083359588c21d01a5b48d88a2cbd334a4c90365015b7836", + "https://bcr.bazel.build/modules/aspect_rules_lint/0.12.0/MODULE.bazel": "e767c5dbfeb254ec03275a7701b5cfde2c4d2873676804bc7cb27ddff3728fed", + "https://bcr.bazel.build/modules/aspect_rules_lint/2.5.0/MODULE.bazel": "24f49acb3b8375fa138ba6ab53bcf517ae92529770a04ab04d47c958ff47b63e", + "https://bcr.bazel.build/modules/aspect_rules_lint/2.7.1/MODULE.bazel": "52f070e52379d262523676b6c5db430b6615849c68c001c3e16d72c6ac411b18", + "https://bcr.bazel.build/modules/aspect_rules_lint/2.7.1/source.json": "afc63f90e4fbcdcb9ec01a472609b8b778efb86156ae34ff4a58a1a2262489d7", + "https://bcr.bazel.build/modules/aspect_rules_py/1.4.0/MODULE.bazel": "6fd29b93207a31445d5d3ab9d9882fd5511e43c95e8e82e7492872663720fd44", + "https://bcr.bazel.build/modules/aspect_rules_py/1.4.0/source.json": "fb1ba946478fb6dbb26d49307d756b0fd2ff88be339af23c39c0397d59143d2c", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.6.0/MODULE.bazel": "d0045b5eabb012be550a609589b3e5e47eba682344b19cfd9365d4d896ed07df", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.6.0/source.json": "5593e3f1cd0dd5147f7748e163307fd5c2e1077913d6945b58739ad8d770a290", + "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/MODULE.bazel": "aa975a83e72bcaac62ee61ab12b788ea324a1d05c4aab28aadb202f647881679", + "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/MODULE.bazel": "37c764292861c2f70314efa9846bb6dbb44fc0308903b3285da6528305450183", + "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/source.json": "605086bbc197743a0d360f7ddc550a1d4dfa0441bc807236e17170f636153348", + "https://bcr.bazel.build/modules/bazel_features/0.1.0/MODULE.bazel": "47011d645b0f949f42ee67f2e8775188a9cf4a0a1528aa2fa4952f2fd00906fd", + "https://bcr.bazel.build/modules/bazel_features/1.0.0/MODULE.bazel": "d7f022dc887efb96e1ee51cec7b2e48d41e36ff59a6e4f216c40e4029e1585bf", + "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b", + "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.13.0/MODULE.bazel": "c14c33c7c3c730612bdbe14ebbb5e61936b6f11322ea95a6e91cd1ba962f94df", + "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://bcr.bazel.build/modules/bazel_features/1.20.0/MODULE.bazel": "8b85300b9c8594752e0721a37210e34879d23adc219ed9dc8f4104a4a1750920", + "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://bcr.bazel.build/modules/bazel_features/1.23.0/MODULE.bazel": "fd1ac84bc4e97a5a0816b7fd7d4d4f6d837b0047cf4cbd81652d616af3a6591a", + "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", + "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", + "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", + "https://bcr.bazel.build/modules/bazel_features/1.32.0/MODULE.bazel": "095d67022a58cb20f7e20e1aefecfa65257a222c18a938e2914fd257b5f1ccdc", + "https://bcr.bazel.build/modules/bazel_features/1.34.0/MODULE.bazel": "e8475ad7c8965542e0c7aac8af68eb48c4af904be3d614b6aa6274c092c2ea1e", + "https://bcr.bazel.build/modules/bazel_features/1.39.0/MODULE.bazel": "28739425c1fc283c91931619749c832b555e60bcd1010b40d8441ce0a5cf726d", + "https://bcr.bazel.build/modules/bazel_features/1.39.0/source.json": "f63cbeb4c602098484d57001e5a07d31cb02bbccde9b5e2c9bf0b29d05283e93", + "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_jar_jar/0.1.7/MODULE.bazel": "d2736a1dbfd8f72befc532823b5112f2597a28064ce4aac280c65bee7d8830a0", + "https://bcr.bazel.build/modules/bazel_jar_jar/0.1.7/source.json": "f894f62528821f749b8d57fb8fb737b13f6cfacde422c88019d4c48077ad0d96", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0-beta.1/MODULE.bazel": "407729e232f611c3270005b016b437005daa7b1505826798ea584169a476e878", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "d6e00979a98ac14ada5e31c8794708b41434d461e7e7ca39b59b765e6d233b18", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0/source.json": "895f21909c6fba01d7c17914bb6c8e135982275a1b18cdaa4e62272217ef1751", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.1/MODULE.bazel": "02a13b77321773b2042e70ee5e4c5e099c8ddee4cf2da9cd420442c36938d4bd", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/MODULE.bazel": "460aa12d01231a80cce03c548287b433b321d205b0028ae596728c35e5ee442e", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/source.json": "d353c410d47a8b65d09fa98e83d57ebec257a2c2b9c6e42d6fda1cb25e5464a5", + "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/MODULE.bazel": "82494a01018bb7ef06d4a17ec4cd7a758721f10eb8b6c820a818e70d669500db", + "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/source.json": "a2d30458fd86cf022c2b6331e652526fa08e17573b2f5034a9dbcacdf9c2583c", + "https://bcr.bazel.build/modules/boringssl/0.0.0-20211025-d4f1ab9/MODULE.bazel": "6ee6353f8b1a701fe2178e1d925034294971350b6d3ac37e67e5a7d463267834", + "https://bcr.bazel.build/modules/boringssl/0.0.0-20230215-5c22014/MODULE.bazel": "4b03dc0d04375fa0271174badcd202ed249870c8e895b26664fd7298abea7282", + "https://bcr.bazel.build/modules/boringssl/0.0.0-20240530-2db0eb3/MODULE.bazel": "d0405b762c5e87cd445b7015f2b8da5400ef9a8dbca0bfefa6c1cea79d528a97", + "https://bcr.bazel.build/modules/boringssl/0.20240913.0/MODULE.bazel": "fcaa7503a5213290831a91ed1eb538551cf11ac0bc3a6ad92d0fef92c5bd25fb", + "https://bcr.bazel.build/modules/boringssl/0.20241024.0/MODULE.bazel": "b540cff73d948cb79cb0bc108d7cef391d2098a25adabfda5043e4ef548dbc87", + "https://bcr.bazel.build/modules/boringssl/0.20241024.0/source.json": "d843092e682b84188c043ac742965d7f96e04c846c7e338187e03238674909a9", + "https://bcr.bazel.build/modules/buildifier_prebuilt/6.4.0/MODULE.bazel": "37389c6b5a40c59410b4226d3bb54b08637f393d66e2fa57925c6fcf68e64bf4", + "https://bcr.bazel.build/modules/buildifier_prebuilt/7.3.1/MODULE.bazel": "537faf0ad9f5892910074b8e43b4c91c96f1d5d86b6ed04bdbe40cf68aa48b68", + "https://bcr.bazel.build/modules/buildifier_prebuilt/8.2.0.2/MODULE.bazel": "a9b689711d5b69f9db741649b218c119b9fdf82924ba390415037e09798edd03", + "https://bcr.bazel.build/modules/buildifier_prebuilt/8.5.1/MODULE.bazel": "77f2a1958d1d07376dd3ce3ae16540f2c1b01921c1fd21930827271260e75a66", + "https://bcr.bazel.build/modules/buildifier_prebuilt/8.5.1/source.json": "ae9f3d9dc7bec033976cf47165a78788bebad2b5c272241063ae31bad8664fd4", + "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/c-ares/1.15.0/MODULE.bazel": "ba0a78360fdc83f02f437a9e7df0532ad1fbaa59b722f6e715c11effebaa0166", + "https://bcr.bazel.build/modules/c-ares/1.15.0/source.json": "5e3ed991616c5ec4cc09b0893b29a19232de4a1830eb78c567121bfea87453f7", + "https://bcr.bazel.build/modules/cel-spec/0.15.0/MODULE.bazel": "e1eed53d233acbdcf024b4b0bc1528116d92c29713251b5154078ab1348cb600", + "https://bcr.bazel.build/modules/cel-spec/0.15.0/source.json": "ab7dccdf21ea2261c0f809b5a5221a4d7f8b580309f285fdf1444baaca75d44a", + "https://bcr.bazel.build/modules/civetweb/1.16/MODULE.bazel": "46a38f9daeb57392e3827fce7d40926be0c802bd23cdd6bfd3a96c804de42fae", + "https://bcr.bazel.build/modules/civetweb/1.16/source.json": "ba8b9585adb8355cb51b999d57172fd05e7a762c56b8d4bac6db42c99de3beb7", + "https://bcr.bazel.build/modules/curl/8.4.0/MODULE.bazel": "0bc250aa1cb69590049383df7a9537c809591fcf876c620f5f097c58fdc9bc10", + "https://bcr.bazel.build/modules/curl/8.7.1/MODULE.bazel": "088221c35a2939c555e6e47cb31a81c15f8b59f4daa8009b1e9271a502d33485", + "https://bcr.bazel.build/modules/curl/8.7.1/source.json": "bf9890e809717445b10a3ddc323b6d25c46631589c693a232df8310a25964484", + "https://bcr.bazel.build/modules/cython/3.0.11-1/MODULE.bazel": "868b3f5c956c3657420d2302004c6bb92606bfa47e314bab7f2ba0630c7c966c", + "https://bcr.bazel.build/modules/cython/3.0.11-1/source.json": "da318be900b8ca9c3d1018839d3bebc5a8e1645620d0848fa2c696d4ecf7c296", + "https://bcr.bazel.build/modules/diff.bzl/0.5.1/MODULE.bazel": "bad8dd444e512b6fcbcd969d6385cd6bc093d4c12e5640fb1f0ace2282f99ef9", + "https://bcr.bazel.build/modules/diff.bzl/0.5.1/source.json": "64571044143273ff8adb322c0f7acefca36f9589ff37a743e3cac9c35ce6b010", + "https://bcr.bazel.build/modules/download_utils/1.2.2/MODULE.bazel": "7d185ec9dd3c5ee277f269e3a8e5f09b9de4cb7ba34d06b93dce9bf41c1279f8", + "https://bcr.bazel.build/modules/download_utils/1.2.2/source.json": "c88be2bc48c98371d35665b805f307a647c98c83327345c918d9088822d77928", + "https://bcr.bazel.build/modules/envoy_api/0.0.0-20241214-918efc9/MODULE.bazel": "24e05f6f52f37be63a795192848555a2c8c855e7814dbc1ed419fb04a7005464", + "https://bcr.bazel.build/modules/envoy_api/0.0.0-20241214-918efc9/source.json": "212043ab69d87f7a04aa4f627f725b540cff5e145a3a31a9403d8b6ec2e920c9", + "https://bcr.bazel.build/modules/flatbuffers/25.9.23/MODULE.bazel": "32753ba60bf3bacfe7737c0f3e8e3e55624b19af5d398c485580d57492d145d8", + "https://bcr.bazel.build/modules/flatbuffers/25.9.23/source.json": "a2116f0017f6896353fd3abf65ef2b89b0a257e8a87f395c5000f53934829f31", + "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8", + "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/MODULE.bazel": "f1b7bb2dd53e8f2ef984b39485ec8a44e9076dda5c4b8efd2fb4c6a6e856a31d", + "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/source.json": "ebe931bfe362e4b41e59ee00a528db6074157ff2ced92eb9e970acab2e1089c9", + "https://bcr.bazel.build/modules/gazelle/0.27.0/MODULE.bazel": "3446abd608295de6d90b4a8a118ed64a9ce11dcb3dda2dc3290a22056bd20996", + "https://bcr.bazel.build/modules/gazelle/0.30.0/MODULE.bazel": "f888a1effe338491f35f0e0e85003b47bb9d8295ccba73c37e07702d8d31c65b", + "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8", + "https://bcr.bazel.build/modules/gazelle/0.33.0/MODULE.bazel": "a13a0f279b462b784fb8dd52a4074526c4a2afe70e114c7d09066097a46b3350", + "https://bcr.bazel.build/modules/gazelle/0.34.0/MODULE.bazel": "abdd8ce4d70978933209db92e436deb3a8b737859e9354fb5fd11fb5c2004c8a", + "https://bcr.bazel.build/modules/gazelle/0.36.0/MODULE.bazel": "e375d5d6e9a6ca59b0cb38b0540bc9a05b6aa926d322f2de268ad267a2ee74c0", + "https://bcr.bazel.build/modules/gazelle/0.40.0/MODULE.bazel": "42ba5378ebe845fca43989a53186ab436d956db498acde790685fe0e8f9c6146", + "https://bcr.bazel.build/modules/gazelle/0.44.0/MODULE.bazel": "fd3177ca0938da57a1e416cad3f39b9c4334defbc717e89aba9d9ddbbb0341da", + "https://bcr.bazel.build/modules/gazelle/0.44.0/source.json": "7fb65ef9c1ce470d099ca27fd478673d9d64c844af28d0d472b0874c7d590cb6", + "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/google_benchmark/1.8.4/MODULE.bazel": "c6d54a11dcf64ee63545f42561eda3fd94c1b5f5ebe1357011de63ae33739d5e", + "https://bcr.bazel.build/modules/google_benchmark/1.8.4/source.json": "84590f7bc5a1fd99e1ef274ee16bb41c214f705e62847b42e705010dfa81fe53", + "https://bcr.bazel.build/modules/googleapis/0.0.0-20240326-1c8d509c5/MODULE.bazel": "a4b7e46393c1cdcc5a00e6f85524467c48c565256b22b5fae20f84ab4a999a68", + "https://bcr.bazel.build/modules/googleapis/0.0.0-20240819-fe8ba054a/MODULE.bazel": "117b7c7be7327ed5d6c482274533f2dbd78631313f607094d4625c28203cacdf", + "https://bcr.bazel.build/modules/googleapis/0.0.0-20240819-fe8ba054a/source.json": "b31fc7eb283a83f71d2e5bfc3d1c562d2994198fa1278409fbe8caec3afc1d3e", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.13.0/MODULE.bazel": "369533f4a302dc7d9ad1cd9a09a9e820a1d9a4011fad2dfa636b5bb225b9a6c7", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/googletest/1.15.2/MODULE.bazel": "6de1edc1d26cafb0ea1a6ab3f4d4192d91a312fd2d360b63adaa213cd00b2108", + "https://bcr.bazel.build/modules/googletest/1.17.0.bcr.2/MODULE.bazel": "827f54f492a3ce549c940106d73de332c2b30cebd0c20c0bc5d786aba7f116cb", + "https://bcr.bazel.build/modules/googletest/1.17.0.bcr.2/source.json": "3664514073a819992320ffbce5825e4238459df344d8b01748af2208f8d2e1eb", + "https://bcr.bazel.build/modules/googletest/1.17.0/MODULE.bazel": "dbec758171594a705933a29fcf69293d2468c49ec1f2ebca65c36f504d72df46", + "https://bcr.bazel.build/modules/grpc-java/1.62.2/MODULE.bazel": "99b8771e8c7cacb130170fed2a10c9e8fed26334a93e73b42d2953250885a158", + "https://bcr.bazel.build/modules/grpc-java/1.66.0/MODULE.bazel": "86ff26209fac846adb89db11f3714b3dc0090fb2fb81575673cc74880cda4e7e", + "https://bcr.bazel.build/modules/grpc-java/1.75.0.bcr.1/MODULE.bazel": "ba818f142b40cc7fb82fbdc8a9f7475b6783cd7849f9a71da814bacaf0acfd74", + "https://bcr.bazel.build/modules/grpc-java/1.75.0.bcr.1/source.json": "4cc3ef019b5a60031125af394b6c52df4da807528be110d9834e9e2690dfcf16", + "https://bcr.bazel.build/modules/grpc-proto/0.0.0-20240627-ec30f58/MODULE.bazel": "88de79051e668a04726e9ea94a481ec6f1692086735fd6f488ab908b3b909238", + "https://bcr.bazel.build/modules/grpc-proto/0.0.0-20240627-ec30f58/source.json": "5035d379c61042930244ab59e750106d893ec440add92ec0df6a0098ca7f131d", + "https://bcr.bazel.build/modules/grpc/1.41.0/MODULE.bazel": "5bcbfc2b274dabea628f0649dc50c90cf36543b1cfc31624832538644ad1aae8", + "https://bcr.bazel.build/modules/grpc/1.56.3.bcr.1/MODULE.bazel": "cd5b1eb276b806ec5ab85032921f24acc51735a69ace781be586880af20ab33f", + "https://bcr.bazel.build/modules/grpc/1.62.1/MODULE.bazel": "2998211594b8a79a6b459c4e797cfa19f0fb8b3be3149760ec7b8c99abfd426f", + "https://bcr.bazel.build/modules/grpc/1.66.0.bcr.2/MODULE.bazel": "0fa2b0fd028ce354febf0fe90f1ed8fecfbfc33118cddd95ac0418cc283333a0", + "https://bcr.bazel.build/modules/grpc/1.66.0.bcr.3/MODULE.bazel": "f6047e89faf488f5e3e65cb2594c6f5e86992abec7487163ff6b623526e543b0", + "https://bcr.bazel.build/modules/grpc/1.70.1/MODULE.bazel": "b800cd8e3e7555c1e61cba2e02d3a2fcf0e91f66e800db286d965d3b7a6a721a", + "https://bcr.bazel.build/modules/grpc/1.70.1/source.json": "e2977ea6cf9f2755418934d4ae134a6569713dd200fd7aded86a4b7f1b86efc9", + "https://bcr.bazel.build/modules/jq.bzl/0.1.0/MODULE.bazel": "2ce69b1af49952cd4121a9c3055faa679e748ce774c7f1fda9657f936cae902f", + "https://bcr.bazel.build/modules/jq.bzl/0.6.0/MODULE.bazel": "26ec5118e66a55fef36f8ea39d6415d55fc966671fe4d61a6b9ef0cd6bc7b6a1", + "https://bcr.bazel.build/modules/jq.bzl/0.6.0/source.json": "2ed4e35b9fa9505495784114f7637fbde5846ec14af917841b6c045d877f20eb", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/source.json": "a04756d367a2126c3541682864ecec52f92cdee80a35735a3cb249ce015ca000", + "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/libpfm/4.11.0/source.json": "caaffb3ac2b59b8aac456917a4ecf3167d40478ee79f15ab7a877ec9273937c9", + "https://bcr.bazel.build/modules/lobster/1.0.6/MODULE.bazel": "fd5778e9a1db6d19d7c9028dce2e40e29cf63809c9a95a53c0a80d954aa92f84", + "https://bcr.bazel.build/modules/lobster/1.0.6/source.json": "6be6b3030dbaede36aba42faba16f5d0a31346985bdf29ff236e47c3592ff4bf", + "https://bcr.bazel.build/modules/mbedtls/3.6.0/MODULE.bazel": "8e380e4698107c5f8766264d4df92e36766248447858db28187151d884995a09", + "https://bcr.bazel.build/modules/mbedtls/3.6.0/source.json": "1dbe7eb5258050afcc3806b9d43050f71c6f539ce0175535c670df606790b30c", + "https://bcr.bazel.build/modules/nlohmann_json/3.11.3/MODULE.bazel": "87023db2f55fc3a9949c7b08dc711fae4d4be339a80a99d04453c4bb3998eefc", + "https://bcr.bazel.build/modules/nlohmann_json/3.11.3/source.json": "296c63a90c6813e53b3812d24245711981fc7e563d98fe15625f55181494488a", + "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", + "https://bcr.bazel.build/modules/opencensus-cpp/0.0.0-20230502-50eb5de/MODULE.bazel": "02201d2921dadb4ec90c4980eca4b2a02904eddcf6fa02f3da7594fb7b0d821c", + "https://bcr.bazel.build/modules/opencensus-cpp/0.0.0-20230502-50eb5de/source.json": "f50efc07822f5425bd1d3e40e977484f9c0142463052717d40ec85cd6744243e", + "https://bcr.bazel.build/modules/opencensus-proto/0.4.1/MODULE.bazel": "4a2e8b4d0b544002502474d611a5a183aa282251e14f6a01afe841c0c1b10372", + "https://bcr.bazel.build/modules/opencensus-proto/0.4.1/source.json": "a7d956700a85b833c43fc61455c0e111ab75bab40768ed17a206ee18a2bbe38f", + "https://bcr.bazel.build/modules/opentelemetry-cpp/1.14.2/MODULE.bazel": "089a5613c2a159c7dfde098dabfc61e966889c7d6a81a98422a84c51535ed17d", + "https://bcr.bazel.build/modules/opentelemetry-cpp/1.16.0/MODULE.bazel": "b7379a140f538cea3f749179a2d481ed81942cc6f7b05a6113723eb34ac3b3e7", + "https://bcr.bazel.build/modules/opentelemetry-cpp/1.16.0/source.json": "da0cf667713b1e48d7f8912b100b4e0a8284c8a95717af5eb8c830d699e61cf5", + "https://bcr.bazel.build/modules/opentelemetry-proto/1.1.0/MODULE.bazel": "a49f406e99bf05ab43ed4f5b3322fbd33adfd484b6546948929d1316299b68bf", + "https://bcr.bazel.build/modules/opentelemetry-proto/1.3.1/MODULE.bazel": "0141a50e989576ee064c11ce8dd5ec89993525bd9f9a09c5618e4dacc8df9352", + "https://bcr.bazel.build/modules/opentelemetry-proto/1.4.0.bcr.1/MODULE.bazel": "5ceaf25e11170d22eded4c8032728b4a3f273765fccda32f9e94f463755c4167", + "https://bcr.bazel.build/modules/opentelemetry-proto/1.4.0.bcr.1/source.json": "fb9e01517460cfad8bafab082f2e1508d3cc2b7ed700cff19f3c7c84b146e5eb", + "https://bcr.bazel.build/modules/opentracing-cpp/1.6.0/MODULE.bazel": "b3925269f63561b8b880ae7cf62ccf81f6ece55b62cd791eda9925147ae116ec", + "https://bcr.bazel.build/modules/opentracing-cpp/1.6.0/source.json": "da1cb1add160f5e5074b7272e9db6fd8f1b3336c15032cd0a653af9d2f484aed", + "https://bcr.bazel.build/modules/package_metadata/0.0.2/MODULE.bazel": "fb8d25550742674d63d7b250063d4580ca530499f045d70748b1b142081ebb92", + "https://bcr.bazel.build/modules/package_metadata/0.0.6/MODULE.bazel": "341dab6f417197494517d54c8e557c0baee1de7aec83543a4fbefe57900acb7e", + "https://bcr.bazel.build/modules/package_metadata/0.0.7/MODULE.bazel": "7adb03933fc8401f495800cf4eafcff0edc6da0ff55c7db223ef69d19f689486", + "https://bcr.bazel.build/modules/package_metadata/0.0.7/source.json": "50639625e937b56115012674c797cca7a05a96b4878c87d803c13dc2b31de8a0", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", + "https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96", + "https://bcr.bazel.build/modules/prometheus-cpp/1.2.4/MODULE.bazel": "0fbe5dcff66311947a3f6b86ebc6a6d9328e31a28413ca864debc4a043f371e5", + "https://bcr.bazel.build/modules/prometheus-cpp/1.3.0/MODULE.bazel": "ce82e086bbc0b60267e970f6a54b2ca6d0f22d3eb6633e00e2cc2899c700f3d8", + "https://bcr.bazel.build/modules/prometheus-cpp/1.3.0/source.json": "8cb66b4e535afc718e9d104a3db96ccb71a42ee816a100e50fd0d5ac843c0606", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/23.1/MODULE.bazel": "88b393b3eb4101d18129e5db51847cd40a5517a53e81216144a8c32dfeeca52a", + "https://bcr.bazel.build/modules/protobuf/24.4/MODULE.bazel": "7bc7ce5f2abf36b3b7b7c8218d3acdebb9426aeb35c2257c96445756f970eb12", + "https://bcr.bazel.build/modules/protobuf/26.0.bcr.1/MODULE.bazel": "8f04d38c2da40a3715ff6bdce4d32c5981e6432557571482d43a62c31a24c2cf", + "https://bcr.bazel.build/modules/protobuf/26.0.bcr.2/MODULE.bazel": "62e0b84ca727bdeb55a6fe1ef180e6b191bbe548a58305ea1426c158067be534", + "https://bcr.bazel.build/modules/protobuf/26.0/MODULE.bazel": "8402da964092af40097f4a205eec2a33fd4a7748dc43632b7d1629bfd9a2b856", + "https://bcr.bazel.build/modules/protobuf/27.0-rc2/MODULE.bazel": "b2b0dbafd57b6bec0ca9b251da02e628c357dab53a097570aa7d79d020f107cf", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://bcr.bazel.build/modules/protobuf/27.2/MODULE.bazel": "32450b50673882e4c8c3d10a83f3bc82161b213ed2f80d17e38bece8f165c295", + "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573", + "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", + "https://bcr.bazel.build/modules/protobuf/31.1/MODULE.bazel": "379a389bb330b7b8c1cdf331cc90bf3e13de5614799b3b52cdb7c6f389f6b38e", + "https://bcr.bazel.build/modules/protobuf/31.1/source.json": "25af5d0219da0c0fc4d1191a24ce438e6ca7f49d2e1a94f354efeba6ef10426f", + "https://bcr.bazel.build/modules/protoc-gen-validate/1.0.4.bcr.2/MODULE.bazel": "c4bd2c850211ff5b7dadf9d2d0496c1c922fdedc303c775b01dfd3b3efc907ed", + "https://bcr.bazel.build/modules/protoc-gen-validate/1.0.4.bcr.2/source.json": "4cc97f70b521890798058600a927ce4b0def8ee84ff2a5aa632aabcb4234aa0b", + "https://bcr.bazel.build/modules/protoc-gen-validate/1.0.4/MODULE.bazel": "b8913c154b16177990f6126d2d2477d187f9ddc568e95ee3e2d50fc65d2c494a", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34", + "https://bcr.bazel.build/modules/pybind11_bazel/2.13.6/MODULE.bazel": "2d746fda559464b253b2b2e6073cb51643a2ac79009ca02100ebbc44b4548656", + "https://bcr.bazel.build/modules/pybind11_bazel/2.13.6/source.json": "6aa0703de8efb20cc897bbdbeb928582ee7beaf278bcd001ac253e1605bddfae", + "https://bcr.bazel.build/modules/rapidjson/1.1.0.bcr.20241007/MODULE.bazel": "82fbcb2e42f9e0040e76ccc74c06c3e46dfd33c64ca359293f8b84df0e6dff4c", + "https://bcr.bazel.build/modules/rapidjson/1.1.0.bcr.20241007/source.json": "5c42389ad0e21fc06b95ad7c0b730008271624a2fa3292e0eab5f30e15adeee3", + "https://bcr.bazel.build/modules/re2/2021-09-01/MODULE.bazel": "bcb6b96f3b071e6fe2d8bed9cc8ada137a105f9d2c5912e91d27528b3d123833", + "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://bcr.bazel.build/modules/re2/2024-05-01/MODULE.bazel": "55a3f059538f381107824e7d00df5df6d061ba1fb80e874e4909c0f0549e8f3e", + "https://bcr.bazel.build/modules/re2/2024-07-02.bcr.1/MODULE.bazel": "b4963dda9b31080be1905ef085ecd7dd6cd47c05c79b9cdf83ade83ab2ab271a", + "https://bcr.bazel.build/modules/re2/2024-07-02/MODULE.bazel": "0eadc4395959969297cbcf31a249ff457f2f1d456228c67719480205aa306daa", + "https://bcr.bazel.build/modules/re2/2025-08-12.bcr.1/MODULE.bazel": "e09b434b122bfb786a69179f9b325e35cb1856c3f56a7a81dd61609260ed46e1", + "https://bcr.bazel.build/modules/re2/2025-08-12.bcr.1/source.json": "a8ae7c09533bf67f9f6e5122d884d5741600b09d78dca6fc0f2f8d2ee0c2d957", + "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://bcr.bazel.build/modules/rules_android/0.6.6/MODULE.bazel": "b0fb569752aab65ab1a9db0a8f6cfaf5aa1754965e17e95dcf0e4d88e192a68d", + "https://bcr.bazel.build/modules/rules_android/0.6.6/source.json": "a9d8dc2d5a102dc03269a94acc886a4cab82cdcb9ccbc77b0f665d6d17a6ae09", + "https://bcr.bazel.build/modules/rules_apple/3.16.0/MODULE.bazel": "0d1caf0b8375942ce98ea944be754a18874041e4e0459401d925577624d3a54a", + "https://bcr.bazel.build/modules/rules_apple/3.16.0/source.json": "d8b5fe461272018cc07cfafce11fe369c7525330804c37eec5a82f84cd475366", + "https://bcr.bazel.build/modules/rules_apple/3.5.1/MODULE.bazel": "3d1bbf65ad3692003d36d8a29eff54d4e5c1c5f4bfb60f79e28646a924d9101c", + "https://bcr.bazel.build/modules/rules_buf/0.1.1/MODULE.bazel": "6189aec18a4f7caff599ad41b851ab7645d4f1e114aa6431acf9b0666eb92162", + "https://bcr.bazel.build/modules/rules_buf/0.5.2/MODULE.bazel": "5f2492d284ab9bedf2668178303abf5f3cd7d8cdf85d768951008e88456e9c6a", + "https://bcr.bazel.build/modules/rules_buf/0.5.2/source.json": "41876d4834c0832de4b393de6e55dfd1cb3b25d3109e4ba90eb7fb57c560e0d9", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", + "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.5/MODULE.bazel": "be41f87587998fe8890cd82ea4e848ed8eb799e053c224f78f3ff7fe1a1d9b74", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.4/MODULE.bazel": "bb03a452a7527ac25a7518fb86a946ef63df860b9657d8323a0c50f8504fb0b9", + "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", + "https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c", + "https://bcr.bazel.build/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8", + "https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4", + "https://bcr.bazel.build/modules/rules_cc/0.2.16/source.json": "d03d5cde49376d87e14ec14b666c56075e5e3926930327fd5d0484a1ff2ac1cc", + "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc", + "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", + "https://bcr.bazel.build/modules/rules_distroless/0.8.0/MODULE.bazel": "a3e473943b685190e20668f5149f62a116fdcbeba66c7b9778f71d2aed4533c1", + "https://bcr.bazel.build/modules/rules_distroless/0.8.0/source.json": "a6931e20d97a13675693adb8b6cd3a0e6eb5218db43dfc83b75566ddc4158efc", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.10.1/MODULE.bazel": "b9527010e5fef060af92b6724edb3691970a5b1f76f74b21d39f7d433641be60", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.10.1/source.json": "9300e71df0cdde0952f10afff1401fa664e9fc5d9ae6204660ba1b158d90d6a6", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_go/0.33.0/MODULE.bazel": "a2b11b64cd24bf94f57454f53288a5dacfe6cb86453eee7761b7637728c1910c", + "https://bcr.bazel.build/modules/rules_go/0.38.1/MODULE.bazel": "fb8e73dd3b6fc4ff9d260ceacd830114891d49904f5bda1c16bc147bcc254f71", + "https://bcr.bazel.build/modules/rules_go/0.39.1/MODULE.bazel": "d34fb2a249403a5f4339c754f1e63dc9e5ad70b47c5e97faee1441fc6636cd61", + "https://bcr.bazel.build/modules/rules_go/0.41.0/MODULE.bazel": "55861d8e8bb0e62cbd2896f60ff303f62ffcb0eddb74ecb0e5c0cbe36fc292c8", + "https://bcr.bazel.build/modules/rules_go/0.42.0/MODULE.bazel": "8cfa875b9aa8c6fce2b2e5925e73c1388173ea3c32a0db4d2b4804b453c14270", + "https://bcr.bazel.build/modules/rules_go/0.45.1/MODULE.bazel": "6d7884f0edf890024eba8ab31a621faa98714df0ec9d512389519f0edff0281a", + "https://bcr.bazel.build/modules/rules_go/0.46.0/MODULE.bazel": "3477df8bdcc49e698b9d25f734c4f3a9f5931ff34ee48a2c662be168f5f2d3fd", + "https://bcr.bazel.build/modules/rules_go/0.48.0/MODULE.bazel": "d00ebcae0908ee3f5e6d53f68677a303d6d59a77beef879598700049c3980a03", + "https://bcr.bazel.build/modules/rules_go/0.50.1/MODULE.bazel": "b91a308dc5782bb0a8021ad4330c81fea5bda77f96b9e4c117b9b9c8f6665ee0", + "https://bcr.bazel.build/modules/rules_go/0.51.0-rc2/MODULE.bazel": "edfc3a9cea7bedb0eaaff37b0d7817c1a4bf72b3c615580b0ffcee6c52690fd4", + "https://bcr.bazel.build/modules/rules_go/0.51.0/MODULE.bazel": "b6920f505935bfd69381651c942496d99b16e2a12f3dd5263b90ded16f3b4d0f", + "https://bcr.bazel.build/modules/rules_go/0.51.0/source.json": "473c0263360b1ae3aca71758e001d257a638620b2e2a36e3a2721fdae04377ec", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.1.0/MODULE.bazel": "324b6478b0343a3ce7a9add8586ad75d24076d6d43d2f622990b9c1cfd8a1b15", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/5.5.0/MODULE.bazel": "486ad1aa15cdc881af632b4b1448b0136c76025a1fe1ad1b65c5899376b83a50", + "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", + "https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963", + "https://bcr.bazel.build/modules/rules_java/6.3.1/MODULE.bazel": "5a3471c8b84d53d58d5f6e316313680d7dd2c70afac696dbe14b761b0b5c6a06", + "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", + "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://bcr.bazel.build/modules/rules_java/7.0.6/MODULE.bazel": "6ddb07d9857a1a3accc9f6d005f20c969c4659c7710e6269a51db3527e0ea969", + "https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel": "30d9135a2b6561c761bd67bd4990da591e6bdc128790ce3e7afd6a3558b2fb64", + "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", + "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel": "a592852f8a3dd539e82ee6542013bf2cadfc4c6946be8941e189d224500a8934", + "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://bcr.bazel.build/modules/rules_java/8.13.0/MODULE.bazel": "0444ebf737d144cf2bb2ccb368e7f1cce735264285f2a3711785827c1686625e", + "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://bcr.bazel.build/modules/rules_java/8.15.1/MODULE.bazel": "5071eebf0fd602ab0617f846e0e0d8f388d66c961513c736e0ac4a1dcde3ff2c", + "https://bcr.bazel.build/modules/rules_java/8.15.1/source.json": "e48286d5819767bc5b3d457539ae7f94e28a9b3e55d092d5c47176cb6a2a289b", + "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", + "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", + "https://bcr.bazel.build/modules/rules_java/8.6.0/MODULE.bazel": "9c064c434606d75a086f15ade5edb514308cccd1544c2b2a89bbac4310e41c71", + "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", + "https://bcr.bazel.build/modules/rules_java/8.8.0/MODULE.bazel": "de589d0880911ac007abd521b9f0ddcd8b0dbd05c8553e6f8124a050b83acf7d", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://bcr.bazel.build/modules/rules_jvm_external/6.0/MODULE.bazel": "37c93a5a78d32e895d52f86a8d0416176e915daabd029ccb5594db422e87c495", + "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", + "https://bcr.bazel.build/modules/rules_jvm_external/6.10/MODULE.bazel": "33e636ca6bc9ee0fa090a38aa33c631ded2d8cf6fead4124181d1b35dc474f7c", + "https://bcr.bazel.build/modules/rules_jvm_external/6.10/source.json": "c191249787625db72616a3fb3cc2786ab57355a2e3b615402b8b3b66b0f995b7", + "https://bcr.bazel.build/modules/rules_jvm_external/6.2/MODULE.bazel": "36a6e52487a855f33cb960724eb56547fa87e2c98a0474c3acad94339d7f8e99", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://bcr.bazel.build/modules/rules_jvm_external/6.6/MODULE.bazel": "153042249c7060536dc95b6bb9f9bb8063b8a0b0cb7acdb381bddbc2374aed55", + "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.5/MODULE.bazel": "043a16a572f610558ec2030db3ff0c9938574e7dd9f58bded1bb07c0192ef025", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://bcr.bazel.build/modules/rules_kotlin/2.1.3/MODULE.bazel": "ce7def6d576aa8d3a9c6d10e13b4d157296229674371f67dbf788dae0afae3d5", + "https://bcr.bazel.build/modules/rules_kotlin/2.1.3/source.json": "0b0dc9400f14b5fbb13d278ad3bf0413cdbaf0da0db337e055b855e35b878a3b", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_multirun/0.9.0/MODULE.bazel": "32d628ef586b5b23f67e55886b7bc38913ea4160420d66ae90521dda2ff37df0", + "https://bcr.bazel.build/modules/rules_multirun/0.9.0/source.json": "e882ba77962fa6c5fe68619e5c7d0374ec9a219fb8d03c42eadaf6d0243771bd", + "https://bcr.bazel.build/modules/rules_multitool/0.11.0/MODULE.bazel": "8d9dda78d2398e136300d3ef4fbcc89ede7c32c158d8c016fa7d032df41c4aaf", + "https://bcr.bazel.build/modules/rules_multitool/1.9.0/MODULE.bazel": "8a042b0dbf35e4aaa94c28ad69efa75c9e673e9ea4bd5c0fb70bab75ef9c636b", + "https://bcr.bazel.build/modules/rules_multitool/1.9.0/source.json": "d9a01604a8b5c4a0e9430824dd34ca5b1b3f5b25277b755e8f3ae91f2c9362a3", + "https://bcr.bazel.build/modules/rules_nodejs/5.8.2/MODULE.bazel": "6bc03c8f37f69401b888023bf511cb6ee4781433b0cb56236b2e55a21e3a026a", + "https://bcr.bazel.build/modules/rules_nodejs/6.2.0/MODULE.bazel": "ec27907f55eb34705adb4e8257952162a2d4c3ed0f0b3b4c3c1aad1fac7be35e", + "https://bcr.bazel.build/modules/rules_nodejs/6.3.0/MODULE.bazel": "45345e4aba35dd6e4701c1eebf5a4e67af4ed708def9ebcdc6027585b34ee52d", + "https://bcr.bazel.build/modules/rules_nodejs/6.3.3/MODULE.bazel": "b66eadebd10f1f1b25f52f95ab5213a57e82c37c3f656fcd9a57ad04d2264ce7", + "https://bcr.bazel.build/modules/rules_nodejs/6.7.3/MODULE.bazel": "c22a48b2a0dbf05a9dc5f83837bbc24c226c1f6e618de3c3a610044c9f336056", + "https://bcr.bazel.build/modules/rules_nodejs/6.7.3/source.json": "a3f966f4415a8a6545e560ee5449eac95cc633f96429d08e87c87775c72f5e09", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483", + "https://bcr.bazel.build/modules/rules_proto/6.0.0/MODULE.bazel": "b531d7f09f58dce456cd61b4579ce8c86b38544da75184eadaf0a7cb7966453f", + "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96", + "https://bcr.bazel.build/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.18.0/MODULE.bazel": "4927032417a3ad1ff220c3e6f940b6811387151aec9e43d3ba76fd7ac8962be8", + "https://bcr.bazel.build/modules/rules_python/0.20.0/MODULE.bazel": "bfe14d17f20e3fe900b9588f526f52c967a6f281e47a1d6b988679bd15082286", + "https://bcr.bazel.build/modules/rules_python/0.22.0/MODULE.bazel": "b8057bafa11a9e0f4b08fc3b7cd7bee0dcbccea209ac6fc9a3ff051cd03e19e9", + "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", + "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://bcr.bazel.build/modules/rules_python/0.26.0/MODULE.bazel": "42cb98cd15954e83b96b540dcc6d5a618eb061f056147ac4ea46e687a066a7c7", + "https://bcr.bazel.build/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53", + "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.29.0/MODULE.bazel": "2ac8cd70524b4b9ec49a0b8284c79e4cd86199296f82f6e0d5da3f783d660c82", + "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937", + "https://bcr.bazel.build/modules/rules_python/0.34.0/MODULE.bazel": "1d623d026e075b78c9fde483a889cda7996f5da4f36dffb24c246ab30f06513a", + "https://bcr.bazel.build/modules/rules_python/0.37.1/MODULE.bazel": "3faeb2d9fa0a81f8980643ee33f212308f4d93eea4b9ce6f36d0b742e71e9500", + "https://bcr.bazel.build/modules/rules_python/0.37.2/MODULE.bazel": "b5ffde91410745750b6c13be1c5dc4555ef5bc50562af4a89fd77807fdde626a", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", + "https://bcr.bazel.build/modules/rules_python/1.1.0/MODULE.bazel": "57e01abae22956eb96d891572490d20e07d983e0c065de0b2170cafe5053e788", + "https://bcr.bazel.build/modules/rules_python/1.5.1/MODULE.bazel": "acfe65880942d44a69129d4c5c3122d57baaf3edf58ae5a6bd4edea114906bf5", + "https://bcr.bazel.build/modules/rules_python/1.8.5/MODULE.bazel": "28b2d79ed8368d7d45b34bacc220e3c0b99cbcd9392641961b849e4c3f55dd30", + "https://bcr.bazel.build/modules/rules_python/1.8.5/source.json": "e261b03c8804f2582c9536013f987e1ea105a2b38c238aa2ac8f98fc34c8b18a", + "https://bcr.bazel.build/modules/rules_python_gazelle_plugin/1.5.1/MODULE.bazel": "371440271705f949a1b51ca875c7d00ce9057e540fdbf26311445cc10810974b", + "https://bcr.bazel.build/modules/rules_python_gazelle_plugin/1.5.1/source.json": "c52e4d2229fbd92b658bf60a7638e79b96525e8f7ed6c59036b4827cade9e430", + "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "d44fec647d0aeb67b9f3b980cf68ba634976f3ae7ccd6c07d790b59b87a4f251", + "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/source.json": "37c10335f2361c337c5c1f34ed36d2da70534c23088062b33a8bdaab68aa9dea", + "https://bcr.bazel.build/modules/rules_rust/0.67.0/MODULE.bazel": "87c3816c4321352dcfd9e9e26b58e84efc5b21351ae3ef8fb5d0d57bde7237f5", + "https://bcr.bazel.build/modules/rules_shell/0.1.2/MODULE.bazel": "66e4ca3ce084b04af0b9ff05ff14cab4e5df7503973818bb91cbc6cda08d32fc", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.4.1/MODULE.bazel": "00e501db01bbf4e3e1dd1595959092c2fadf2087b2852d3f553b5370f5633592", + "https://bcr.bazel.build/modules/rules_shell/0.5.0/MODULE.bazel": "8c8447370594d45539f66858b602b0bb2cb2d3401a4ebb9ad25830c59c0f366d", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c", + "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca", + "https://bcr.bazel.build/modules/rules_swift/1.18.0/MODULE.bazel": "a6aba73625d0dc64c7b4a1e831549b6e375fbddb9d2dde9d80c9de6ec45b24c9", + "https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046", + "https://bcr.bazel.build/modules/rules_swift/2.1.1/source.json": "40fc69dfaac64deddbb75bd99cdac55f4427d9ca0afbe408576a65428427a186", + "https://bcr.bazel.build/modules/stardoc/0.5.0/MODULE.bazel": "f9f1f46ba8d9c3362648eea571c6f9100680efc44913618811b58cc9c02cd678", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.4/MODULE.bazel": "6569966df04610b8520957cb8e97cf2e9faac2c0309657c537ab51c16c18a2a4", + "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", + "https://bcr.bazel.build/modules/stardoc/0.6.2/MODULE.bazel": "7060193196395f5dd668eda046ccbeacebfd98efc77fed418dbe2b82ffaa39fd", + "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", + "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", + "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb", + "https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468", + "https://bcr.bazel.build/modules/tar.bzl/0.5.1/MODULE.bazel": "7c2eb3dcfc53b0f3d6f9acdfd911ca803eaf92aadf54f8ca6e4c1f3aee288351", + "https://bcr.bazel.build/modules/tar.bzl/0.6.0/MODULE.bazel": "a3584b4edcfafcabd9b0ef9819808f05b372957bbdff41601429d5fd0aac2e7c", + "https://bcr.bazel.build/modules/tar.bzl/0.6.0/source.json": "4a620381df075a16cb3a7ed57bd1d05f7480222394c64a20fa51bdb636fda658", + "https://bcr.bazel.build/modules/trlc/3.0.0/MODULE.bazel": "45444f9112300e7a716bf842beb17ed8f84d7d1f84c3d1ed126f57bec9bdf2a9", + "https://bcr.bazel.build/modules/trlc/3.0.1/MODULE.bazel": "9ece74d6ae5a718850a6f39d94ac52a84c002e0a5c6dadf676433e330c2e2f6c", + "https://bcr.bazel.build/modules/trlc/3.0.1/source.json": "3bd9ec62c5a44d759377acee89b7ed966e2ce227292b2b175424bf4343ed0b66", + "https://bcr.bazel.build/modules/upb/0.0.0-20211020-160625a/MODULE.bazel": "6cced416be2dc5b9c05efd5b997049ba795e5e4e6fafbe1624f4587767638928", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9", + "https://bcr.bazel.build/modules/upb/0.0.0-20230907-e7430e6/MODULE.bazel": "3a7dedadf70346e678dc059dbe44d05cbf3ab17f1ce43a1c7a42edc7cbf93fd9", + "https://bcr.bazel.build/modules/xds/0.0.0-20240423-555b57e/MODULE.bazel": "cea509976a77e34131411684ef05a1d6ad194dd71a8d5816643bc5b0af16dc0f", + "https://bcr.bazel.build/modules/xds/0.0.0-20240423-555b57e/source.json": "7227e1fcad55f3f3cab1a08691ecd753cb29cc6380a47bc650851be9f9ad6d20", + "https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072", + "https://bcr.bazel.build/modules/yq.bzl/0.3.1/MODULE.bazel": "9bcb7151b3cd4681b89d350530eaf7b45e32a44dda94843b8932b0cb1cd4594a", + "https://bcr.bazel.build/modules/yq.bzl/0.3.1/source.json": "f0b0f204a2a6b0e34b4c9541efe8c04f2ef1af65948daa784eccea738b21dbd2", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", + "https://bcr.bazel.build/modules/zlib/1.2.13/MODULE.bazel": "aa6deb1b83c18ffecd940c4119aff9567cd0a671d7bba756741cb2ef043a29d5", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.1/MODULE.bazel": "6a9fe6e3fc865715a7be9823ce694ceb01e364c35f7a846bf0d2b34762bc066b", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", + "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198", + "https://bcr.bazel.build/modules/zlib/1.3/MODULE.bazel": "6a9c02f19a24dcedb05572b2381446e27c272cd383aed11d41d99da9e3167a72", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20210324.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20211102.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20220623.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230125.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20240116.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20240116.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20240116.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20240722.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20250127.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20250127.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20250512.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-py/2.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.11.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.15.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.17.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.22.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.23.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.24.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.31.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.38.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.42.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.16.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.20.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.22.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.9.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_esbuild/0.21.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_js/1.33.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_js/1.40.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_js/2.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_js/2.3.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/0.12.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/2.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/2.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_py/1.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_ts/3.6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_tools_telemetry/0.2.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_tools_telemetry/0.3.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/0.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.10.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.13.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.15.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.17.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.18.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.19.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.20.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.21.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.23.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.27.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.28.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.30.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.32.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.34.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.39.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_jar_jar/0.1.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.0.0-beta.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.4.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.8.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_worker_api/0.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_worker_api/0.0.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_worker_java/0.0.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/boringssl/0.0.0-20211025-d4f1ab9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/boringssl/0.0.0-20230215-5c22014/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/boringssl/0.0.0-20240530-2db0eb3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/boringssl/0.20240913.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/boringssl/0.20241024.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/6.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/7.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/8.2.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/8.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildozer/7.1.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/c-ares/1.15.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/cel-spec/0.15.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/civetweb/1.16/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/curl/8.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/curl/8.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/cython/3.0.11-1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/diff.bzl/0.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/download_utils/1.2.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/envoy_api/0.0.0-20241214-918efc9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/flatbuffers/25.9.23/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gawk/5.3.2.bcr.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.27.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.30.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.32.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.33.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.34.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.36.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.40.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.44.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/google_benchmark/1.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/google_benchmark/1.8.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googleapis/0.0.0-20240326-1c8d509c5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googleapis/0.0.0-20240819-fe8ba054a/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.13.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.15.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.17.0.bcr.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.17.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc-java/1.62.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc-java/1.66.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc-java/1.75.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc-proto/0.0.0-20240627-ec30f58/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc/1.41.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc/1.56.3.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc/1.62.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc/1.66.0.bcr.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc/1.66.0.bcr.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/grpc/1.70.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jq.bzl/0.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jq.bzl/0.6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jsoncpp/1.9.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jsoncpp/1.9.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/libpfm/4.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/lobster/1.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/mbedtls/3.6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/nlohmann_json/3.11.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/nlohmann_json/3.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opencensus-cpp/0.0.0-20230502-50eb5de/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opencensus-proto/0.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opentelemetry-cpp/1.14.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opentelemetry-cpp/1.16.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opentelemetry-proto/1.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opentelemetry-proto/1.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opentelemetry-proto/1.4.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/opentracing-cpp/1.6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/package_metadata/0.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/package_metadata/0.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/package_metadata/0.0.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.10/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.11/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/prometheus-cpp/1.2.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/prometheus-cpp/1.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/21.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/23.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/24.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/26.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/26.0.bcr.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/26.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.0-rc2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0-rc2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0-rc3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/3.19.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/3.19.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/3.19.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/31.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protoc-gen-validate/1.0.4.bcr.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protoc-gen-validate/1.0.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/pybind11_bazel/2.11.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/pybind11_bazel/2.12.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/pybind11_bazel/2.13.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rapidjson/1.1.0.bcr.20241007/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2021-09-01/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2023-09-01/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2024-05-01/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2024-07-02.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2024-07-02/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2025-08-12.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_android/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_android/0.6.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_apple/3.16.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_apple/3.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_buf/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_buf/0.5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.10/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.13/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.14/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.15/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.16/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.17/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.14/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.16/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_distroless/0.8.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_foreign_cc/0.10.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_fuzzing/0.5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.33.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.38.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.39.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.41.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.42.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.45.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.46.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.48.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.50.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.51.0-rc2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.51.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/4.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/5.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/5.3.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/5.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.10.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.12.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.3.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.13.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.15.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.3.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.8.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/4.4.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.10/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/2.1.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multirun/0.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/0.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/1.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/5.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/6.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/6.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/6.3.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/6.7.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/0.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/4.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/7.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/7.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.10.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.18.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.20.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.22.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.22.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.23.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.25.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.26.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.27.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.28.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.29.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.31.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.33.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.34.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.37.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.37.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.40.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.8.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python_gazelle_plugin/1.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.67.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.1-score/MODULE.bazel": "dc1c87d74ef6d32190e65c3c8aabfa7e7764e457bf9888312e0313c3c11fdb69", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.2-score/MODULE.bazel": "37be8dee6df19d666c1d4266e1266d82012aa83bd82de38b3100fd7f641d064b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.2-score/source.json": "f88ad98dd08f296a546677e86ad42b20f61851e41a9fd3e0449971162fcaf784", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.1.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/1.16.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/1.18.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/2.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/MODULE.bazel": "ade2bad4a331b02d9b7e7d9842e8de8c6fded6186486e02c4f7db5cd4b71d34d", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/source.json": "fbcbc738e652b0c68d5d28dd1db09f2e643dc111f5739b2f6af7ec56c2e88043", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_tooling/2.2.0/MODULE.bazel": "178ba4862246b6ba2bbcd96b7e9e728299b19fb94bcf23d315dc2299aabf7178", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_tooling/2.2.0/source.json": "a76f2d093cff26d5b256ce531bb2f69b6c667c968f99a156327fd194d4f36e61", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.6.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/tar.bzl/0.2.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/tar.bzl/0.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/tar.bzl/0.6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/trlc/3.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/trlc/3.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/upb/0.0.0-20211020-160625a/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/upb/0.0.0-20230907-e7430e6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/xds/0.0.0-20240423-555b57e/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/yq.bzl/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/yq.bzl/0.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.2.11/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.2.12/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.2.13/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3/MODULE.bazel": "not found" + }, + "selectedYankedVersions": {}, + "moduleExtensions": { + "@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": { + "general": { + "bzlTransitiveDigest": "uvKBzzynYgAz3E6uzLKr7emNyi+uIepPAiUlbUilv2w=", + "usagesDigest": "sj4kz7yaVclWMuWhUhSLq0bVH7+HrkWyMdODMeA7Zhw=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "esbuild_darwin-x64": { + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", + "attributes": { + "esbuild_version": "0.19.9", + "platform": "darwin-x64" + } + }, + "esbuild_darwin-arm64": { + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", + "attributes": { + "esbuild_version": "0.19.9", + "platform": "darwin-arm64" + } + }, + "esbuild_linux-x64": { + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", + "attributes": { + "esbuild_version": "0.19.9", + "platform": "linux-x64" + } + }, + "esbuild_linux-arm64": { + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", + "attributes": { + "esbuild_version": "0.19.9", + "platform": "linux-arm64" + } + }, + "esbuild_win32-x64": { + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", + "attributes": { + "esbuild_version": "0.19.9", + "platform": "win32-x64" + } + }, + "esbuild_toolchains": { + "repoRuleId": "@@aspect_rules_esbuild+//esbuild/private:toolchains_repo.bzl%toolchains_repo", + "attributes": { + "esbuild_version": "0.19.9", + "user_repository_name": "esbuild" + } + }, + "npm__esbuild_0.19.9": { + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule", + "attributes": { + "package": "esbuild", + "version": "0.19.9", + "root_package": "", + "link_workspace": "", + "link_packages": {}, + "integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==", + "url": "", + "commit": "", + "patch_args": [ + "-p0" + ], + "patches": [], + "custom_postinstall": "", + "npm_auth": "", + "npm_auth_basic": "", + "npm_auth_username": "", + "npm_auth_password": "", + "lifecycle_hooks": [], + "extra_build_content": "", + "generate_bzl_library_targets": false, + "extract_full_archive": false, + "exclude_package_contents": [], + "system_tar": "auto" + } + }, + "npm__esbuild_0.19.9__links": { + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links", + "attributes": { + "package": "esbuild", + "version": "0.19.9", + "dev": false, + "root_package": "", + "link_packages": {}, + "deps": {}, + "transitive_closure": {}, + "lifecycle_build_target": false, + "lifecycle_hooks_env": [], + "lifecycle_hooks_execution_requirements": [ + "no-sandbox" + ], + "lifecycle_hooks_use_default_shell_env": false, + "bins": {}, + "package_visibility": [ + "//visibility:public" + ], + "replace_package": "", + "exclude_package_contents": [] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "aspect_bazel_lib+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "aspect_bazel_lib+", + "bazel_tools", + "bazel_tools" + ], + [ + "aspect_bazel_lib+", + "tar.bzl", + "tar.bzl+" + ], + [ + "aspect_rules_esbuild+", + "aspect_rules_js", + "aspect_rules_js+" + ], + [ + "aspect_rules_esbuild+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "aspect_rules_js+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "aspect_rules_js+", + "aspect_rules_js", + "aspect_rules_js+" + ], + [ + "aspect_rules_js+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "aspect_rules_js+", + "bazel_tools", + "bazel_tools" + ], + [ + "tar.bzl+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "tar.bzl+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "tar.bzl+", + "tar.bzl", + "tar.bzl+" + ] + ] + } + }, + "@@aspect_rules_js+//npm:extensions.bzl%pnpm": { + "general": { + "bzlTransitiveDigest": "8vuF34437k2v0cxa1rCNL0YtLzocf2Q3v6QZpSjOiS0=", + "usagesDigest": "kbjSw2REjlSC0HtTZDf2p+l/dmiMt3NHLoiWEXYAoQI=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "pnpm": { + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule", + "attributes": { + "package": "pnpm", + "version": "8.6.7", + "root_package": "", + "link_workspace": "", + "link_packages": {}, + "integrity": "sha512-vRIWpD/L4phf9Bk2o/O2TDR8fFoJnpYrp2TKqTIZF/qZ2/rgL3qKXzHofHgbXsinwMoSEigz28sqk3pQ+yMEQQ==", + "url": "", + "commit": "", + "patch_args": [ + "-p0" + ], + "patches": [], + "custom_postinstall": "", + "npm_auth": "", + "npm_auth_basic": "", + "npm_auth_username": "", + "npm_auth_password": "", + "lifecycle_hooks": [], + "extra_build_content": "load(\"@aspect_rules_js//js:defs.bzl\", \"js_binary\")\njs_binary(name = \"pnpm\", data = glob([\"package/**\"]), entry_point = \"package/dist/pnpm.cjs\", visibility = [\"//visibility:public\"])", + "generate_bzl_library_targets": false, + "extract_full_archive": true, + "exclude_package_contents": [], + "system_tar": "auto" + } + }, + "pnpm__links": { + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links", + "attributes": { + "package": "pnpm", + "version": "8.6.7", + "dev": false, + "root_package": "", + "link_packages": {}, + "deps": {}, + "transitive_closure": {}, + "lifecycle_build_target": false, + "lifecycle_hooks_env": [], + "lifecycle_hooks_execution_requirements": [ + "no-sandbox" + ], + "lifecycle_hooks_use_default_shell_env": false, + "bins": {}, + "package_visibility": [ + "//visibility:public" + ], + "replace_package": "", + "exclude_package_contents": [] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "aspect_bazel_lib+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "aspect_bazel_lib+", + "bazel_tools", + "bazel_tools" + ], + [ + "aspect_bazel_lib+", + "tar.bzl", + "tar.bzl+" + ], + [ + "aspect_rules_js+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "aspect_rules_js+", + "aspect_rules_js", + "aspect_rules_js+" + ], + [ + "aspect_rules_js+", + "bazel_features", + "bazel_features+" + ], + [ + "aspect_rules_js+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "aspect_rules_js+", + "bazel_tools", + "bazel_tools" + ], + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "tar.bzl+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "tar.bzl+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "tar.bzl+", + "tar.bzl", + "tar.bzl+" + ] + ] + } + }, + "@@aspect_rules_py+//py:extensions.bzl%py_tools": { + "general": { + "bzlTransitiveDigest": "13DN9p/N8uOU1SMHBh9nZIlptJtS3qP63UkXe+NH8SQ=", + "usagesDigest": "NC1b49l5tenTBVWEUGzzC0j5Kg1GH+l5lBw5JRCldIU=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "bsd_tar_darwin_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "darwin_amd64" + } + }, + "bsd_tar_darwin_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "darwin_arm64" + } + }, + "bsd_tar_linux_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "linux_amd64" + } + }, + "bsd_tar_linux_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "linux_arm64" + } + }, + "bsd_tar_windows_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "windows_amd64" + } + }, + "bsd_tar_windows_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "windows_arm64" + } + }, + "bsd_tar_toolchains": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%tar_toolchains_repo", + "attributes": { + "user_repository_name": "bsd_tar" + } + }, + "rules_py_tools.darwin_amd64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "darwin_amd64" + } + }, + "rules_py_tools.darwin_arm64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "darwin_arm64" + } + }, + "rules_py_tools.linux_amd64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "linux_amd64" + } + }, + "rules_py_tools.linux_arm64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "linux_arm64" + } + }, + "rules_py_tools": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:repo.bzl%toolchains_repo", + "attributes": { + "user_repository_name": "rules_py_tools" + } + }, + "rules_py_pex_2_3_1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "urls": [ + "https://files.pythonhosted.org/packages/e7/d0/fbda2a4d41d62d86ce53f5ae4fbaaee8c34070f75bb7ca009090510ae874/pex-2.3.1-py2.py3-none-any.whl" + ], + "sha256": "64692a5bf6f298403aab930d22f0d836ae4736c5bc820e262e9092fe8c56f830", + "downloaded_file_path": "pex-2.3.1-py2.py3-none-any.whl" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "aspect_bazel_lib+", + "bazel_tools", + "bazel_tools" + ], + [ + "aspect_rules_py+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "aspect_rules_py+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@aspect_rules_ts+//ts:extensions.bzl%ext": { + "general": { + "bzlTransitiveDigest": "LW1zC7Im6EhnBiPOn9FBRp3lYTjaUmo9dSdS2dJvXH0=", + "usagesDigest": "caXVbnxEUN71ZxydJbg6pZ8NaFPVDbNp12wS9TMC824=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "npm_typescript": { + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", + "attributes": { + "bzlmod": true, + "version": "5.8.3", + "integrity": "", + "build_file": "@@aspect_rules_ts+//ts:BUILD.typescript", + "build_file_substitutions": { + "bazel_worker_version": "5.4.2", + "google_protobuf_version": "3.20.1" + }, + "urls": [ + "https://registry.npmjs.org/typescript/-/typescript-{}.tgz" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "aspect_rules_ts+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@aspect_tools_telemetry+//:extension.bzl%telemetry": { + "general": { + "bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=", + "usagesDigest": "tiZsmifkpLM+xnn6EXkTF48bYLsbvhr5rcGkv4SuImc=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "aspect_tools_telemetry_report": { + "repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository", + "attributes": { + "deps": { + "aspect_rules_lint": "2.7.1", + "aspect_tools_telemetry": "0.3.3" + } + } + } + }, + "recordedRepoMappingEntries": [ + [ + "aspect_tools_telemetry+", + "bazel_lib", + "bazel_lib+" + ], + [ + "aspect_tools_telemetry+", + "bazel_skylib", + "bazel_skylib+" + ] + ] + } + }, + "@@bazel_jar_jar+//internal:non_module_deps.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "IfOiDBJwnddmiy546C4iX/vpN6o6p7joO6VC37WBYhw=", + "usagesDigest": "P1uSZ4XnqOp90Mkh0A4Nrjx12Bgz/iceROnSbTsawGk=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "jvm__jarjar_abrams_assembly": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "urls": [ + "https://repo1.maven.org/maven2/com/eed3si9n/jarjarabrams/jarjar-abrams-assembly_2.12/1.14.0/jarjar-abrams-assembly_2.12-1.14.0.jar" + ], + "sha256": "75f86f7588136d6ca92d6fed8d58e6666e04c507b71de378527c053fd2a151c2" + } + }, + "jvm__com_twitter__scalding_args": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "urls": [ + "https://repo1.maven.org/maven2/com/twitter/scalding-args_2.12/0.17.4/scalding-args_2.12-0.17.4.jar" + ], + "sha256": "e0de2ad8ef344bb11a2854275b5b85a1adb17f0e0ed9740177d940a602cd977b" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "bazel_jar_jar+", + "bazel_tools", + "bazel_tools" + ], + [ + "bazel_tools", + "rules_java", + "rules_java+" + ], + [ + "protobuf+", + "proto_bazel_features", + "bazel_features+" + ], + [ + "rules_cc+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", + "rules_cc", + "rules_cc+" + ], + [ + "rules_java+", + "bazel_features", + "bazel_features+" + ], + [ + "rules_java+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_java+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_java+", + "com_google_protobuf", + "protobuf+" + ], + [ + "rules_java+", + "compatibility_proxy", + "rules_java++compatibility_proxy+compatibility_proxy" + ], + [ + "rules_java+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_java++compatibility_proxy+compatibility_proxy", + "rules_java", + "rules_java+" + ] + ] + } + }, + "@@cel-spec+//:extensions.bzl%non_module_dependencies": { + "general": { + "bzlTransitiveDigest": "43iembF10pz9FJIRseDrjWB9kp/ODYb+7FBYAtvSt3Q=", + "usagesDigest": "HFQJtQrL9nKaFZEjgwaHVMHALMW+cafu696xy7J4ueM=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_google_googleapis": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "bd8e735d881fb829751ecb1a77038dda4a8d274c45490cb9fcf004583ee10571", + "strip_prefix": "googleapis-07c27163ac591955d736f3057b1619ece66f5b99", + "urls": [ + "https://github.com/googleapis/googleapis/archive/07c27163ac591955d736f3057b1619ece66f5b99.tar.gz" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "cel-spec+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@cel-spec+//:googleapis_ext.bzl%googleapis_ext": { + "general": { + "bzlTransitiveDigest": "yun2jmsomFi3bs5bjQWXApBzqQf66zBJ39JEBYigzdc=", + "usagesDigest": "Ek7VfZ+tuyRBx/1h5wcmtnW9EGpOb0dkXUwBluZbD8k=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_google_googleapis_imports": { + "repoRuleId": "@@cel-spec++non_module_dependencies+com_google_googleapis//:repository_rules.bzl%switched_rules", + "attributes": { + "rules": { + "proto_library_with_info": [ + "", + "" + ], + "moved_proto_library": [ + "", + "" + ], + "java_proto_library": [ + "", + "" + ], + "java_grpc_library": [ + "", + "" + ], + "java_gapic_library": [ + "", + "" + ], + "java_gapic_test": [ + "", + "" + ], + "java_gapic_assembly_gradle_pkg": [ + "", + "" + ], + "py_proto_library": [ + "", + "" + ], + "py_grpc_library": [ + "", + "" + ], + "py_gapic_library": [ + "", + "" + ], + "py_test": [ + "", + "" + ], + "py_gapic_assembly_pkg": [ + "", + "" + ], + "py_import": [ + "", + "" + ], + "go_proto_library": [ + "", + "" + ], + "go_library": [ + "", + "" + ], + "go_test": [ + "", + "" + ], + "go_gapic_library": [ + "", + "" + ], + "go_gapic_assembly_pkg": [ + "", + "" + ], + "cc_proto_library": [ + "native.cc_proto_library", + "" + ], + "cc_grpc_library": [ + "", + "" + ], + "cc_gapic_library": [ + "", + "" + ], + "php_proto_library": [ + "", + "php_proto_library" + ], + "php_grpc_library": [ + "", + "php_grpc_library" + ], + "php_gapic_library": [ + "", + "php_gapic_library" + ], + "php_gapic_assembly_pkg": [ + "", + "php_gapic_assembly_pkg" + ], + "nodejs_gapic_library": [ + "", + "typescript_gapic_library" + ], + "nodejs_gapic_assembly_pkg": [ + "", + "typescript_gapic_assembly_pkg" + ], + "ruby_proto_library": [ + "", + "" + ], + "ruby_grpc_library": [ + "", + "" + ], + "ruby_ads_gapic_library": [ + "", + "" + ], + "ruby_cloud_gapic_library": [ + "", + "" + ], + "ruby_gapic_assembly_pkg": [ + "", + "" + ], + "csharp_proto_library": [ + "", + "" + ], + "csharp_grpc_library": [ + "", + "" + ], + "csharp_gapic_library": [ + "", + "" + ], + "csharp_gapic_assembly_pkg": [ + "", + "" + ] + } + } + } + }, + "recordedRepoMappingEntries": [ + [ + "cel-spec+", + "com_google_googleapis", + "cel-spec++non_module_dependencies+com_google_googleapis" + ] + ] + } + }, + "@@envoy_api+//bazel:repositories.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "6K7ebXGqru7CSFs/38zH8GGvDgewYM/qjlz7Lqm2XXY=", + "usagesDigest": "cxAa0VVo9d210JBUBw6wpuGp8jg+ltqw3tzH0tPtIEg=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "prometheus_metrics_model": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/prometheus/client_model/archive/v0.6.1.tar.gz" + ], + "sha256": "b9b690bc35d80061f255faa7df7621eae39fe157179ccd78ff6409c3b004f05e", + "strip_prefix": "client_model-0.6.1", + "build_file_content": "\nload(\"@envoy_api//bazel:api_build_system.bzl\", \"api_cc_py_proto_library\")\nload(\"@io_bazel_rules_go//proto:def.bzl\", \"go_proto_library\")\n\napi_cc_py_proto_library(\n name = \"client_model\",\n srcs = [\n \"io/prometheus/client/metrics.proto\",\n ],\n visibility = [\"//visibility:public\"],\n)\n\ngo_proto_library(\n name = \"client_model_go_proto\",\n importpath = \"github.com/prometheus/client_model/go\",\n proto = \":client_model\",\n visibility = [\"//visibility:public\"],\n)\n" + } + }, + "com_github_bufbuild_buf": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/bufbuild/buf/releases/download/v1.47.2/buf-Linux-x86_64.tar.gz" + ], + "sha256": "39716cfe0185df3cba21f66ec739620ffb6876c48b2da4338a8c68c290c9b116", + "strip_prefix": "buf", + "build_file_content": "\npackage(\n default_visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"buf\",\n srcs = [\n \"@com_github_bufbuild_buf//:bin/buf\",\n ],\n tags = [\"manual\"], # buf is downloaded as a linux binary; tagged manual to prevent build for non-linux users\n)\n" + } + }, + "envoy_toolshed": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/envoyproxy/toolshed/archive/bazel-v0.2.0.tar.gz" + ], + "sha256": "ef5e95580c41f6805beec197d9a4f6683550f4bfc1e1c678449b6d205dbf000b", + "strip_prefix": "toolshed-bazel-v0.2.0/bazel" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "envoy_api+", + "bazel_tools", + "bazel_tools" + ], + [ + "envoy_api+", + "envoy_api", + "envoy_api+" + ] + ] + } + }, + "@@googleapis+//:extensions.bzl%switched_rules": { + "general": { + "bzlTransitiveDigest": "vG6fuTzXD8MMvHWZEQud0MMH7eoC4GXY0va7VrFFh04=", + "usagesDigest": "+R+o+SUmw755TgAo3pOLuat5DyxcMumhHtsr8ivEIRg=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_google_googleapis_imports": { + "repoRuleId": "@@googleapis+//:repository_rules.bzl%switched_rules", + "attributes": { + "rules": { + "proto_library_with_info": [ + "", + "" + ], + "moved_proto_library": [ + "", + "" + ], + "java_proto_library": [ + "", + "" + ], + "java_grpc_library": [ + "", + "" + ], + "java_gapic_library": [ + "", + "" + ], + "java_gapic_test": [ + "", + "" + ], + "java_gapic_assembly_gradle_pkg": [ + "", + "" + ], + "py_proto_library": [ + "", + "" + ], + "py_grpc_library": [ + "", + "" + ], + "py_gapic_library": [ + "", + "" + ], + "py_test": [ + "", + "" + ], + "py_gapic_assembly_pkg": [ + "", + "" + ], + "py_import": [ + "", + "" + ], + "go_proto_library": [ + "", + "" + ], + "go_grpc_library": [ + "", + "" + ], + "go_library": [ + "", + "" + ], + "go_test": [ + "", + "" + ], + "go_gapic_library": [ + "", + "" + ], + "go_gapic_assembly_pkg": [ + "", + "" + ], + "cc_proto_library": [ + "", + "" + ], + "cc_grpc_library": [ + "", + "" + ], + "cc_gapic_library": [ + "", + "" + ], + "php_proto_library": [ + "", + "php_proto_library" + ], + "php_grpc_library": [ + "", + "php_grpc_library" + ], + "php_gapic_library": [ + "", + "php_gapic_library" + ], + "php_gapic_assembly_pkg": [ + "", + "php_gapic_assembly_pkg" + ], + "nodejs_gapic_library": [ + "", + "typescript_gapic_library" + ], + "nodejs_gapic_assembly_pkg": [ + "", + "typescript_gapic_assembly_pkg" + ], + "ruby_proto_library": [ + "", + "" + ], + "ruby_grpc_library": [ + "", + "" + ], + "ruby_ads_gapic_library": [ + "", + "" + ], + "ruby_cloud_gapic_library": [ + "", + "" + ], + "ruby_gapic_assembly_pkg": [ + "", + "" + ], + "csharp_proto_library": [ + "", + "" + ], + "csharp_grpc_library": [ + "", + "" + ], + "csharp_gapic_library": [ + "", + "" + ], + "csharp_gapic_assembly_pkg": [ + "", + "" + ] + } + } + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@pybind11_bazel+//:internal_configure.bzl%internal_configure_extension": { + "general": { + "bzlTransitiveDigest": "qQEChI+4ZOV4xn6LulonXjQy5+Z0z1gELJDmBl1ePy4=", + "usagesDigest": "tVQNvLoXMWAbiK39am3yovKGpwINdftfn7RpDyN+JZc=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "pybind11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@pybind11_bazel+//:pybind11-BUILD.bazel", + "strip_prefix": "pybind11-2.13.6", + "url": "https://github.com/pybind/pybind11/archive/refs/tags/v2.13.6.tar.gz", + "integrity": "sha256-4Iy4f0dz2pf6e18DXeh2OrxlbYfVdz5i9toFh9Hw7CA=" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "pybind11_bazel+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_android+//bzlmod_extensions:apksig.bzl%apksig_extension": { + "general": { + "bzlTransitiveDigest": "c6mHv2L0P+tWDe7lh/mpzTEedvukBq17Gr9gP16UdxU=", + "usagesDigest": "xq6OVkELeJvOgYo3oY/sUBsGFbcqdV+9BYiNgSPV/po=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "apksig": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://android.googlesource.com/platform/tools/apksig/+archive/24e3075e68ebe17c0b529bb24bfda819db5e2f3b.tar.gz", + "build_file": "@@rules_android+//bzlmod_extensions:apksig.BUILD" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_android+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_android+//bzlmod_extensions:com_android_dex.bzl%com_android_dex_extension": { + "general": { + "bzlTransitiveDigest": "BbM3I4LvjudqQ6/IwaP/LaJXdj2EPfqPleTjiFG4Lg0=", + "usagesDigest": "toF8IFMu98H/VU2p1sfVC5fVXVYJunpbbmtM6tOsQXY=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_android_dex": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://android.googlesource.com/platform/dalvik/+archive/5a81c499a569731e2395f7c8d13c0e0d4e17a2b6.tar.gz", + "build_file": "@@rules_android+//bzlmod_extensions:com_android_dex.BUILD" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_android+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_android+//rules/android_sdk_repository:rule.bzl%android_sdk_repository_extension": { + "general": { + "bzlTransitiveDigest": "NAy+0M15JNVEBb8Tny6t7j3lKqTnsAMjoBB6LJ+C370=", + "usagesDigest": "g9Ur6X6qhf9a8MmY9qXU/jFjkyk/aZVBegI0yVMF0z4=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "androidsdk": { + "repoRuleId": "@@rules_android+//rules/android_sdk_repository:rule.bzl%_android_sdk_repository", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@rules_apple+//apple:apple.bzl%provisioning_profile_repository_extension": { + "general": { + "bzlTransitiveDigest": "pdRt+Wm+XQmS427nAIg9z7qfm56CnZChO+HJ4zu3Xa8=", + "usagesDigest": "vsJl8Rw5NL+5Ag2wdUDoTeRF/5klkXO8545Iy7U1Q08=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_provisioning_profiles": { + "repoRuleId": "@@rules_apple+//apple/internal:local_provisioning_profiles.bzl%provisioning_profile_repository", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "bazel_tools", + "rules_cc", + "rules_cc+" + ], + [ + "rules_apple+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_apple+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_apple+", + "build_bazel_apple_support", + "apple_support+" + ], + [ + "rules_apple+", + "build_bazel_rules_swift", + "rules_swift+" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", + "rules_cc", + "rules_cc+" + ], + [ + "rules_swift+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_swift+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_swift+", + "build_bazel_apple_support", + "apple_support+" + ], + [ + "rules_swift+", + "build_bazel_rules_swift", + "rules_swift+" + ], + [ + "rules_swift+", + "build_bazel_rules_swift_local_config", + "rules_swift++non_module_deps+build_bazel_rules_swift_local_config" + ] + ] + } + }, + "@@rules_apple+//apple:extensions.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "ul8vHGy74hBD66XzLuB09UmLKPv7O6yFI8pwN7jMWdc=", + "usagesDigest": "M3VqFpeTCo4qmrNKGZw0dxBHvTYDrfV3cscGzlSAhQ4=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "xctestrunner": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/google/xctestrunner/archive/b7698df3d435b6491b4b4c0f9fc7a63fbed5e3a6.tar.gz" + ], + "strip_prefix": "xctestrunner-b7698df3d435b6491b4b4c0f9fc7a63fbed5e3a6", + "sha256": "ae3a063c985a8633cb7eb566db21656f8db8eb9a0edb8c182312c7f0db53730d" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_apple+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_buf+//buf:extensions.bzl%buf": { + "general": { + "bzlTransitiveDigest": "dSWqckK2ILN7aDIDHfv+Qrl1fb1hF7o7MDXY6T8C41s=", + "usagesDigest": "vxN6C2h72rUERbAmd1476FWpxdxo1NhYoY5JSFXJT3g=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_buf_toolchains": { + "repoRuleId": "@@rules_buf+//buf/internal:toolchain.bzl%buf_download_releases", + "attributes": { + "version": "v1.47.2", + "sha256": "1b37b75dc0a777a0cba17fa2604bc9906e55bb4c578823d8b7a8fe3fc9fe4439" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_buf+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_distroless+//apt:extensions.bzl%apt": { + "general": { + "bzlTransitiveDigest": "9dVC3uLi04oXYQ63WRNkwE2eWtRHXoyVy1wiyRFJqcg=", + "usagesDigest": "niPoYX/Hk8+YvcLiQxLt75319UN95ORAe5Ii2c+VVlk=", + "recordedFileInputs": { + "@@score_tooling+//third_party/docs_runtime/manifest.yaml": "f8bd762e0dcaf3150504eca8dfd60819fc3be428d3a8b58d1d9cc52d68e16a45", + "@@score_tooling+//third_party/tooling_sysroot/manifest.yaml": "ef1e4af34dbf67a9e332c69ccad0b12b2e295849c33e98658c6ccd69e363c4cd" + }, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "docs_runtime_base-files_13ubuntu10.4_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/base-files/base-files_13ubuntu10.4_amd64.deb" + ], + "sha256": "94daf9ede91f6263d676611623d4c9edab695c728a584ab2fb1d11cabb8479e8" + } + }, + "docs_runtime_libcrypt1_1-4.4.36-4build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcrypt/libcrypt1_4.4.36-4build1_amd64.deb" + ], + "sha256": "9474785cd6f398512bf8c305c3901dbb111569dccb6f5832002373c0a8ac5832" + } + }, + "docs_runtime_libc6_2.39-0ubuntu8.7_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc6_2.39-0ubuntu8.7_amd64.deb" + ], + "sha256": "955644e8bc2930a9bf8eea5e4c2237c8a118c1e2ac2845b993b6f7f35eefd293" + } + }, + "docs_runtime_libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libgcc-s1_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "aa7fadbe33b78bcf99885318040601c550c208929565b179891d9a3cc2aa68cd" + } + }, + "docs_runtime_gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/gcc-14-base_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "b95c172411a7fdae70307cf33a9f5320ba5e056b556454543dd5b679d5ce1c4f" + } + }, + "docs_runtime_mawk_1.3.4.20240123-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/m/mawk/mawk_1.3.4.20240123-1build1_amd64.deb" + ], + "sha256": "dc7f7f4dad4b48f6012ea65de3198d8376604afef39f06d65ec6167740e203c9" + } + }, + "docs_runtime_fakechroot_2.20.1-p-ds-15_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/f/fakechroot/fakechroot_2.20.1+ds-15_all.deb" + ], + "sha256": "f311e5c449314e8e66427277e901a415d3d77d4e3e8c91817a4a831e47467675" + } + }, + "docs_runtime_libfakechroot_2.20.1-p-ds-15_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/f/fakechroot/libfakechroot_2.20.1+ds-15_amd64.deb" + ], + "sha256": "6a9dd5907de0b94ab9e79cedd39b91a5cc783708675869dd588b0f5bebb97d70" + } + }, + "docs_runtime_binutils_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/binutils_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "b3b5a84181a38fd191820b2cdcc1a3eeb1cd6333ad472f2092f96e81047e9c74" + } + }, + "docs_runtime_binutils-x86-64-linux-gnu_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/binutils-x86-64-linux-gnu_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "1e510a15f30208d39edcd840e48f26a77bbca7c417805eeccb1e3f7de198ef29" + } + }, + "docs_runtime_zlib1g_1-1.3.dfsg-3.1ubuntu2.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.1_amd64.deb" + ], + "sha256": "7074b6a2f6367a10d280c00a1cb02e74277709180bab4f2491a2f355ab2d6c20" + } + }, + "docs_runtime_libzstd1_1.5.5-p-dfsg2-2build1.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libz/libzstd/libzstd1_1.5.5+dfsg2-2build1.1_amd64.deb" + ], + "sha256": "dfcf25061e07aad7efd3f4f880ba5ad4d4d09ebe7fc8cc77ab6b8a161d6d4727" + } + }, + "docs_runtime_libstdc-p--p-6_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libstdc++6_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "a51f8de7829211db961a31f02158058ad1a95f92ac6d0a5dff6350e2821c54c0" + } + }, + "docs_runtime_libsframe1_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libsframe1_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "72093fb456864db55f1352bfa5e952a94f7abaff64e71dff1fbf001db1984564" + } + }, + "docs_runtime_libjansson4_2.14-2build2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/j/jansson/libjansson4_2.14-2build2_amd64.deb" + ], + "sha256": "0cf79113f5d193ce9af2be2ff4b2c3b30dd4e55a0b6c47f7d28f6c849ff3aa60" + } + }, + "docs_runtime_libgprofng0_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libgprofng0_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "1b7e3c2fc162e8358ca6e5a3fffdb4d0d632f790630323215841bb36a63c0ab8" + } + }, + "docs_runtime_libbinutils_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libbinutils_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "064dce00ce94e1fc2d33779cb0071088f4c8aac79e85345f2e78a020f7d14699" + } + }, + "docs_runtime_binutils-common_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/binutils-common_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "d136073f5e2153f3df11c1d08d66727b9466b28ff483f50085f14bbe3464b5ee" + } + }, + "docs_runtime_libctf0_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libctf0_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "7ec86d697c3668503c85f308a6832f092075b5880ad002f22185264da0bd4645" + } + }, + "docs_runtime_libctf-nobfd0_2.42-4ubuntu2.10_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libctf-nobfd0_2.42-4ubuntu2.10_amd64.deb" + ], + "sha256": "da352eb7fa6c4369d2a6c1e5e680f574eda2e38576563326b18d9e47e61c4078" + } + }, + "docs_runtime_graphviz_2.42.2-9ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/graphviz_2.42.2-9ubuntu0.1_amd64.deb" + ], + "sha256": "be4e5b1b036babfb1663c095bc645c2fbab79dd57b213fc3b1ab0d48e810dd85" + } + }, + "docs_runtime_libxt6t64_1-1.2.1-1.2build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxt/libxt6t64_1.2.1-1.2build1_amd64.deb" + ], + "sha256": "2e4317d5ae1a45e517771ea0b8d93519b5f915ab08983facecd480d64dbd1489" + } + }, + "docs_runtime_libx11-6_2-1.8.7-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libx11/libx11-6_1.8.7-1build1_amd64.deb" + ], + "sha256": "397f84347476a3c5786b39f3ff6f0f82866eb3d8be6d2ad3efeadf019efe5b80" + } + }, + "docs_runtime_libx11-data_2-1.8.7-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libx11/libx11-data_1.8.7-1build1_all.deb" + ], + "sha256": "9ae01f7747e7f479394c697c55acf11d3baf139e8e54b7c4adfb55ef9c50de08" + } + }, + "docs_runtime_libxcb1_1.15-1ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcb/libxcb1_1.15-1ubuntu2_amd64.deb" + ], + "sha256": "e1c6611d11ad7398326f1bf028afc34c3b14c51d917a3426b966ed4b9687fa58" + } + }, + "docs_runtime_libxdmcp6_1-1.1.3-0ubuntu6_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxdmcp/libxdmcp6_1.1.3-0ubuntu6_amd64.deb" + ], + "sha256": "bcd336fce11ce2a45f34d0f95e6980af22529f22147e8f98c156e5cee8ee42bb" + } + }, + "docs_runtime_libbsd0_0.12.1-1build1.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libb/libbsd/libbsd0_0.12.1-1build1.1_amd64.deb" + ], + "sha256": "f3857b0863ac5cfd4263e9bf6cfb1d4be88d5321e4070d5bc2b62b0949e6c86f" + } + }, + "docs_runtime_libmd0_1.1.0-2build1.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libm/libmd/libmd0_1.1.0-2build1.1_amd64.deb" + ], + "sha256": "e5ba01d3c41f256aaf57ec59aa0554857162e3e7f97cdfbff1ed2c0e8d720ee7" + } + }, + "docs_runtime_libxau6_1-1.0.9-1build6_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxau/libxau6_1.0.9-1build6_amd64.deb" + ], + "sha256": "e40d29f1d1a62393bacaedebe0da3d9006084152a9f7e5e029293f08ce1c5c80" + } + }, + "docs_runtime_libsm6_2-1.2.3-1build3_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libs/libsm/libsm6_1.2.3-1build3_amd64.deb" + ], + "sha256": "1d27ebc381b499075a28c504be4d7d424c63b6866354736ac7f8d25813860cdf" + } + }, + "docs_runtime_libuuid1_2.39.3-9ubuntu6.5_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/u/util-linux/libuuid1_2.39.3-9ubuntu6.5_amd64.deb" + ], + "sha256": "2f801582b5f0e7cb0c2d3d3651193e69f4540ee3b7dcb6079a3562f0880b9edf" + } + }, + "docs_runtime_libice6_2-1.0.10-1build3_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libi/libice/libice6_1.0.10-1build3_amd64.deb" + ], + "sha256": "ad1edb5303574fee154e487947177e5bd15363aa71b92f78f5a2a7145fdb81c5" + } + }, + "docs_runtime_x11-common_1-7.7-p-23ubuntu3_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/x/xorg/x11-common_7.7+23ubuntu3_all.deb" + ], + "sha256": "b545fa5196dd7467ba3770d6ce575abcd7941071961ea9f7535a319d9d20fd46" + } + }, + "docs_runtime_sysvinit-utils_3.08-6ubuntu3_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/s/sysvinit/sysvinit-utils_3.08-6ubuntu3_amd64.deb" + ], + "sha256": "03cf5c9c28a5d186fb7f5214e464f831ff28177695cbb61e66083237267a047e" + } + }, + "docs_runtime_libxmu6_2-1.1.3-3build2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxmu/libxmu6_1.1.3-3build2_amd64.deb" + ], + "sha256": "05c13bce0e9a80139dfa72cc8e7a695b4f7d58b1884423929eade62179d99853" + } + }, + "docs_runtime_libxext6_2-1.3.4-1build2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxext/libxext6_1.3.4-1build2_amd64.deb" + ], + "sha256": "45783969a9ece9d7b7b733b8c60981584c53c6bc5ee3b42d295d2f80d1285679" + } + }, + "docs_runtime_libxaw7_2-1.0.14-1build2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxaw/libxaw7_1.0.14-1build2_amd64.deb" + ], + "sha256": "2a9cdc82ecf566de76e05394efc604c9cced49e12c28b10e1ca101062b47bac0" + } + }, + "docs_runtime_libxpm4_1-3.5.17-1build2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxpm/libxpm4_3.5.17-1build2_amd64.deb" + ], + "sha256": "ad9d9cb9be156d8b80c12735ecb4aae57e5501280a9f952749e8b71a7e5f9c97" + } + }, + "docs_runtime_liblab-gamut1_2.42.2-9ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/liblab-gamut1_2.42.2-9ubuntu0.1_amd64.deb" + ], + "sha256": "b9fa73d671d96b07360dd3b7a4d35814c8b892b5bf4b1e0945d3bc7937c9ee3d" + } + }, + "docs_runtime_libgvpr2_2.42.2-9ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libgvpr2_2.42.2-9ubuntu0.1_amd64.deb" + ], + "sha256": "1a560080462b80ab56e0fe09af2abd52974a256e25b4f384b9be5872c1073c82" + } + }, + "docs_runtime_libcgraph6_2.42.2-9ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libcgraph6_2.42.2-9ubuntu0.1_amd64.deb" + ], + "sha256": "4555e20d8cd068de6343484c5bf825d03830c5048ccf56a9baec9638ca5815b2" + } + }, + "docs_runtime_libcdt5_2.42.2-9ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libcdt5_2.42.2-9ubuntu0.1_amd64.deb" + ], + "sha256": "3c49c2624e6a895d729bff5be16c96beeb1347e3282bc40f3d210c715fba07ef" + } + }, + "docs_runtime_libgvc6_2.42.2-9ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libgvc6_2.42.2-9ubuntu0.1_amd64.deb" + ], + "sha256": "dc674afb98d9b64699f63be3a4e92d4f3b563435ebfc70cbaec0c91b8f41fb48" + } + }, + "docs_runtime_libwebp7_1.3.2-0.4build3_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libw/libwebp/libwebp7_1.3.2-0.4build3_amd64.deb" + ], + "sha256": "c86f6439f0bbc531f7199794654231f3b29327d6585958511b958795d51c2484" + } + }, + "docs_runtime_libsharpyuv0_1.3.2-0.4build3_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libw/libwebp/libsharpyuv0_1.3.2-0.4build3_amd64.deb" + ], + "sha256": "4e1763494e5b9d34192313aa88e38201fc67b3f13ea96ca268d26401a3002791" + } + }, + "docs_runtime_libpathplan4_2.42.2-9ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libpathplan4_2.42.2-9ubuntu0.1_amd64.deb" + ], + "sha256": "8cff70c607271662df4719d2e2848731d8f23ab9108e79404066a6f93af9fd23" + } + }, + "docs_runtime_libpangoft2-1.0-0_1.52.1-p-ds-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.52.1+ds-1build1_amd64.deb" + ], + "sha256": "cc6bc9d86ef5f329edacaa4895232d8a9b8f95c5cbc77c3c460a3f64cecc213d" + } + }, + "docs_runtime_libpango-1.0-0_1.52.1-p-ds-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pango1.0/libpango-1.0-0_1.52.1+ds-1build1_amd64.deb" + ], + "sha256": "09dfa5c881ab273ec6bc1830adcefdc407fcd619316e68fb26ca3a23d0fc9f54" + } + }, + "docs_runtime_libthai0_0.1.29-2build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libt/libthai/libthai0_0.1.29-2build1_amd64.deb" + ], + "sha256": "5ada7045c5f84a4b774e6449151800aa6470e8d01dc9d6124ef4a44ae6af5508" + } + }, + "docs_runtime_libdatrie1_0.2.13-3build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libd/libdatrie/libdatrie1_0.2.13-3build1_amd64.deb" + ], + "sha256": "1ae164e40e413eac4fbd38ce1c6ef9591f7a03278ad9076d71fa29258727f447" + } + }, + "docs_runtime_libthai-data_0.1.29-2build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libt/libthai/libthai-data_0.1.29-2build1_all.deb" + ], + "sha256": "6035d4a714290b97816bed7f3bf64921c6358ddb84d1dacdddccdc1cf39bffb7" + } + }, + "docs_runtime_libharfbuzz0b_8.3.0-2build2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/h/harfbuzz/libharfbuzz0b_8.3.0-2build2_amd64.deb" + ], + "sha256": "d6f7eea2244f98aa0463a056680d4629476bf624767ede301560e24add686b5c" + } + }, + "docs_runtime_libgraphite2-3_1.3.14-2build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/graphite2/libgraphite2-3_1.3.14-2build1_amd64.deb" + ], + "sha256": "e2a91c59a2b26649ed0b331bedab0c92e56cc2021cce27df1080089edaad7aba" + } + }, + "docs_runtime_libglib2.0-0t64_2.80.0-6ubuntu3.8_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glib2.0/libglib2.0-0t64_2.80.0-6ubuntu3.8_amd64.deb" + ], + "sha256": "ca4eda0e01c76ba2e5e501e77decce53d98a641234b517eb98fc5d74980f913f" + } + }, + "docs_runtime_libselinux1_3.5-2ubuntu2.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libs/libselinux/libselinux1_3.5-2ubuntu2.1_amd64.deb" + ], + "sha256": "6abaa6c26f46ef17764c4a753e0e84de1cdadde5634fd2987621fdc617988d19" + } + }, + "docs_runtime_libpcre2-8-0_10.42-4ubuntu2.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pcre2/libpcre2-8-0_10.42-4ubuntu2.1_amd64.deb" + ], + "sha256": "110a797a57673d3ee497a141cf988199258058c57525799c63194d81822529a0" + } + }, + "docs_runtime_libmount1_2.39.3-9ubuntu6.5_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/u/util-linux/libmount1_2.39.3-9ubuntu6.5_amd64.deb" + ], + "sha256": "7ca31424fbfc96fbf245e6fe232fd2d2ec74169ca1add968e1987bece5bc0d1f" + } + }, + "docs_runtime_libblkid1_2.39.3-9ubuntu6.5_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/u/util-linux/libblkid1_2.39.3-9ubuntu6.5_amd64.deb" + ], + "sha256": "2307a9ed92642a69de0284ac63e233e45ac5a2c3831f04399f7d212379487e82" + } + }, + "docs_runtime_libffi8_3.4.6-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libf/libffi/libffi8_3.4.6-1build1_amd64.deb" + ], + "sha256": "637e6a7744de08cd331a41f4efd0d24e6ea9064843dea9d1c6ca87bdb5f038a2" + } + }, + "docs_runtime_libfreetype6_2.13.2-p-dfsg-1ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/freetype/libfreetype6_2.13.2+dfsg-1ubuntu0.1_amd64.deb" + ], + "sha256": "f6937fd8a77e83001dcfd3857d5a5cfbd4caaeb297c7fdb71ec50514843e48af" + } + }, + "docs_runtime_libpng16-16t64_1.6.43-5ubuntu0.5_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libp/libpng1.6/libpng16-16t64_1.6.43-5ubuntu0.5_amd64.deb" + ], + "sha256": "074c954a7a01069c3b3db9146911666f6ac75c1277e034eba901ee274554dadc" + } + }, + "docs_runtime_libbz2-1.0_1.0.8-5.1build0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/bzip2/libbz2-1.0_1.0.8-5.1build0.1_amd64.deb" + ], + "sha256": "d557ab12b42ab370249142099fae3cbb979948934e4dfa58c2ab59bf5bbbda73" + } + }, + "docs_runtime_libbrotli1_1.1.0-2build2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/brotli/libbrotli1_1.1.0-2build2_amd64.deb" + ], + "sha256": "74492419b8fda803774b8c9acef6afc5d2f9ff31782635aae212906adae7b277" + } + }, + "docs_runtime_libfribidi0_1.0.13-3build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fribidi/libfribidi0_1.0.13-3build1_amd64.deb" + ], + "sha256": "6cd50259d39ce0dfafee2632c6268538e6a02590e77161a133e9683af346dd1d" + } + }, + "docs_runtime_fontconfig_2.15.0-1.1ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fontconfig/fontconfig_2.15.0-1.1ubuntu2_amd64.deb" + ], + "sha256": "ddf0aace42b13a5bc38904e848b145b787ea254cddf5213d150254bd654db6e5" + } + }, + "docs_runtime_fontconfig-config_2.15.0-1.1ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fontconfig/fontconfig-config_2.15.0-1.1ubuntu2_amd64.deb" + ], + "sha256": "9df0579cd298a1f2b7a40efd519c849745aecd193a09733cf4d1032284a62bbf" + } + }, + "docs_runtime_fonts-dejavu-core_2.37-8_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fonts-dejavu/fonts-dejavu-core_2.37-8_all.deb" + ], + "sha256": "40049660c194f3b8a2541fc7369efebb10e9f94bdac836a2f38fafedd10fa73a" + } + }, + "docs_runtime_fonts-dejavu-mono_2.37-8_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fonts-dejavu/fonts-dejavu-mono_2.37-8_all.deb" + ], + "sha256": "8a599d6553307db7ecb795d2f0e5a301e03234afc75c7358b0ba43466454c89a" + } + }, + "docs_runtime_libfontconfig1_2.15.0-1.1ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fontconfig/libfontconfig1_2.15.0-1.1ubuntu2_amd64.deb" + ], + "sha256": "a2bc05cfef021fdb84285036f98eda5ebec3c4b7a378f5aa2bdba5c4d3d8d586" + } + }, + "docs_runtime_libexpat1_2.6.1-2ubuntu0.4_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/e/expat/libexpat1_2.6.1-2ubuntu0.4_amd64.deb" + ], + "sha256": "126a5612e652bdc2edee19ae8fe4308db72b5b3b0a5581bf885b44a093baf3e5" + } + }, + "docs_runtime_libpangocairo-1.0-0_1.52.1-p-ds-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pango1.0/libpangocairo-1.0-0_1.52.1+ds-1build1_amd64.deb" + ], + "sha256": "98cf5fc9076c2911fe20dfc1e41cd8f686a2bb5d539b1105fab18105b0db1376" + } + }, + "docs_runtime_libcairo2_1.18.0-3build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/c/cairo/libcairo2_1.18.0-3build1_amd64.deb" + ], + "sha256": "96950b306889ff6e4248bb937b0a3b56e72dacf643fe732214ce375f0f6bbb36" + } + }, + "docs_runtime_libxrender1_1-0.9.10-1.1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxrender/libxrender1_0.9.10-1.1build1_amd64.deb" + ], + "sha256": "d70bd831aebe8d4834b5dd2ed98df26dd6bd27f1042c47543bd7f66df1ae22ea" + } + }, + "docs_runtime_libxcb-shm0_1.15-1ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcb/libxcb-shm0_1.15-1ubuntu2_amd64.deb" + ], + "sha256": "229d1280d459f1ba44c22939d3f9b61d9d20932d9a646b3fe4ce50be4cdf2325" + } + }, + "docs_runtime_libxcb-render0_1.15-1ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcb/libxcb-render0_1.15-1ubuntu2_amd64.deb" + ], + "sha256": "7d83a0668b1a693b5ea2e496206e8371fb6beda4c54a2b4b3902005915c272c5" + } + }, + "docs_runtime_libpixman-1-0_0.42.2-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pixman/libpixman-1-0_0.42.2-1build1_amd64.deb" + ], + "sha256": "d9c2931c4c424615eeab9dd5ae08bfc608b84e803c1d3ccddf319270db213421" + } + }, + "docs_runtime_libltdl7_2.4.7-7build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libt/libtool/libltdl7_2.4.7-7build1_amd64.deb" + ], + "sha256": "02d0d2d7e1bf73f838a92f8df7a9b07b698c837e82bd89aee6a581cf793cd966" + } + }, + "docs_runtime_libgts-0.7-5t64_0.7.6-p-darcs121130-5.2build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/gts/libgts-0.7-5t64_0.7.6+darcs121130-5.2build1_amd64.deb" + ], + "sha256": "a6741ed94f3e786eaed0a9fcb69f641327cfe288af4f043e7036b5257e87fc48" + } + }, + "docs_runtime_libgd3_2.3.3-9ubuntu5_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libg/libgd2/libgd3_2.3.3-9ubuntu5_amd64.deb" + ], + "sha256": "dbef5ad99eec399fd6afeb6d0cc92d81a8b3295a7ac80ac71311ea09c49704ae" + } + }, + "docs_runtime_libtiff6_4.5.1-p-git230720-4ubuntu2.5_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/t/tiff/libtiff6_4.5.1+git230720-4ubuntu2.5_amd64.deb" + ], + "sha256": "564cdf59dda5e1a826c51b5868ee2785fffa26ee5a7bad95bc5e8c8224c931b8" + } + }, + "docs_runtime_liblzma5_5.6.1-p-really5.4.5-1ubuntu0.2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/x/xz-utils/liblzma5_5.6.1+really5.4.5-1ubuntu0.2_amd64.deb" + ], + "sha256": "4c31cc76391ca47dbb4585edc740728f43fe7b6b090f5b3947fc8072db698aca" + } + }, + "docs_runtime_liblerc4_4.0.0-p-ds-4ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/l/lerc/liblerc4_4.0.0+ds-4ubuntu2_amd64.deb" + ], + "sha256": "f1b9dbfa28c56564f6b544f3757f5f3f4d549ce9962f39e897812c23b0a5d0e7" + } + }, + "docs_runtime_libjpeg8_8c-2ubuntu11_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libj/libjpeg8-empty/libjpeg8_8c-2ubuntu11_amd64.deb" + ], + "sha256": "4d6ed682972bddf62af158a06d68a0dda497e421423b7ca92fb450b3d9ceaac9" + } + }, + "docs_runtime_libjpeg-turbo8_2.1.5-2ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.1.5-2ubuntu2_amd64.deb" + ], + "sha256": "f68b5b23bc8a1688fb787d2aed7e2cdf895a73022f6a5025e183162dac4500b2" + } + }, + "docs_runtime_libjbig0_2.1-6.1ubuntu2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/j/jbigkit/libjbig0_2.1-6.1ubuntu2_amd64.deb" + ], + "sha256": "074a760a86213a1286ad60f56394c90885cf45af070626a88c480d90b78c175a" + } + }, + "docs_runtime_libdeflate0_1.19-1build1.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libd/libdeflate/libdeflate0_1.19-1build1.1_amd64.deb" + ], + "sha256": "8e7dfa9e63a9b5058071b84da48bb2e30dea07de169e80fbc759fe0e68639269" + } + }, + "docs_runtime_libheif1_1.17.6-1ubuntu4.2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libh/libheif/libheif1_1.17.6-1ubuntu4.2_amd64.deb" + ], + "sha256": "c41176242ae4a6a66fa50fab00dc6d951711f0cf8aab32cf13530c4a404a7026" + } + }, + "docs_runtime_libheif-plugin-libde265_1.17.6-1ubuntu4.2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libh/libheif/libheif-plugin-libde265_1.17.6-1ubuntu4.2_amd64.deb" + ], + "sha256": "f5284ae47a70538c503b15f458783df569ac7b8d5d16aaeac4e04c2d3b8e7341" + } + }, + "docs_runtime_libde265-0_1.0.15-1build3_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libd/libde265/libde265-0_1.0.15-1build3_amd64.deb" + ], + "sha256": "3b8cb1fb70933261d076d1b2882fb303aa8da1a951ad7614234f890468c90310" + } + }, + "docs_runtime_libheif-plugin-aomdec_1.17.6-1ubuntu4.2_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libh/libheif/libheif-plugin-aomdec_1.17.6-1ubuntu4.2_amd64.deb" + ], + "sha256": "65ce5cb8f2ee27127b7df6b358bde6e58ebcf00e1d9f5c92c32c842cfa260ef6" + } + }, + "docs_runtime_libaom3_3.8.2-2ubuntu0.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/a/aom/libaom3_3.8.2-2ubuntu0.1_amd64.deb" + ], + "sha256": "1623e49454a2981069ed4801c853630a63a68f77c73056dea11e3289a8467eb9" + } + }, + "docs_runtime_libann0_1.1.2-p-doc-9build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/a/ann/libann0_1.1.2+doc-9build1_amd64.deb" + ], + "sha256": "88347025b6292517dc591fd6a51c28b6a91a46c999505e3e54677c99038074e1" + } + }, + "docs_runtime_resolve": { + "repoRuleId": "@@rules_distroless+//apt/private:deb_resolve.bzl%deb_resolve", + "attributes": { + "manifest": "@@score_tooling+//third_party/docs_runtime:manifest.yaml", + "resolve_transitive": true + } + }, + "docs_runtime": { + "repoRuleId": "@@rules_distroless+//apt/private:deb_translate_lock.bzl%deb_translate_lock", + "attributes": { + "lock_content": "{\n\t\"packages\": [\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcrypt1_1-4.4.36-4build1_amd64\",\n\t\t\t\t\t\"name\": \"libcrypt1\",\n\t\t\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"mawk_1.3.4.20240123-1build1_amd64\",\n\t\t\t\t\t\"name\": \"mawk\",\n\t\t\t\t\t\"version\": \"1.3.4.20240123-1build1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"base-files_13ubuntu10.4_amd64\",\n\t\t\t\"name\": \"base-files\",\n\t\t\t\"sha256\": \"94daf9ede91f6263d676611623d4c9edab695c728a584ab2fb1d11cabb8479e8\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/base-files/base-files_13ubuntu10.4_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"13ubuntu10.4\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libcrypt1_1-4.4.36-4build1_amd64\",\n\t\t\t\"name\": \"libcrypt1\",\n\t\t\t\"sha256\": \"9474785cd6f398512bf8c305c3901dbb111569dccb6f5832002373c0a8ac5832\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcrypt/libcrypt1_4.4.36-4build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\"name\": \"libc6\",\n\t\t\t\"sha256\": \"955644e8bc2930a9bf8eea5e4c2237c8a118c1e2ac2845b993b6f7f35eefd293\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc6_2.39-0ubuntu8.7_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\"sha256\": \"aa7fadbe33b78bcf99885318040601c550c208929565b179891d9a3cc2aa68cd\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libgcc-s1_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\"sha256\": \"b95c172411a7fdae70307cf33a9f5320ba5e056b556454543dd5b679d5ce1c4f\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/gcc-14-base_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"mawk_1.3.4.20240123-1build1_amd64\",\n\t\t\t\"name\": \"mawk\",\n\t\t\t\"sha256\": \"dc7f7f4dad4b48f6012ea65de3198d8376604afef39f06d65ec6167740e203c9\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/m/mawk/mawk_1.3.4.20240123-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.3.4.20240123-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libfakechroot_2.20.1-p-ds-15_amd64\",\n\t\t\t\t\t\"name\": \"libfakechroot\",\n\t\t\t\t\t\"version\": \"2.20.1+ds-15\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"binutils_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"binutils\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"binutils-x86-64-linux-gnu_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"binutils-x86-64-linux-gnu\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"zlib1g_1-1.3.dfsg-3.1ubuntu2.1_amd64\",\n\t\t\t\t\t\"name\": \"zlib1g\",\n\t\t\t\t\t\"version\": \"1:1.3.dfsg-3.1ubuntu2.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libzstd1_1.5.5-p-dfsg2-2build1.1_amd64\",\n\t\t\t\t\t\"name\": \"libzstd1\",\n\t\t\t\t\t\"version\": \"1.5.5+dfsg2-2build1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libstdc-p--p-6_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libstdc++6\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libsframe1_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"libsframe1\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libjansson4_2.14-2build2_amd64\",\n\t\t\t\t\t\"name\": \"libjansson4\",\n\t\t\t\t\t\"version\": \"2.14-2build2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgprofng0_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"libgprofng0\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libbinutils_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"libbinutils\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"binutils-common_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"binutils-common\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libctf0_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"libctf0\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libctf-nobfd0_2.42-4ubuntu2.10_amd64\",\n\t\t\t\t\t\"name\": \"libctf-nobfd0\",\n\t\t\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"fakechroot_2.20.1-p-ds-15_amd64\",\n\t\t\t\"name\": \"fakechroot\",\n\t\t\t\"sha256\": \"f311e5c449314e8e66427277e901a415d3d77d4e3e8c91817a4a831e47467675\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/f/fakechroot/fakechroot_2.20.1+ds-15_all.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.20.1+ds-15\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libfakechroot_2.20.1-p-ds-15_amd64\",\n\t\t\t\"name\": \"libfakechroot\",\n\t\t\t\"sha256\": \"6a9dd5907de0b94ab9e79cedd39b91a5cc783708675869dd588b0f5bebb97d70\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/f/fakechroot/libfakechroot_2.20.1+ds-15_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.20.1+ds-15\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"binutils_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"binutils\",\n\t\t\t\"sha256\": \"b3b5a84181a38fd191820b2cdcc1a3eeb1cd6333ad472f2092f96e81047e9c74\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/binutils_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"binutils-x86-64-linux-gnu_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"binutils-x86-64-linux-gnu\",\n\t\t\t\"sha256\": \"1e510a15f30208d39edcd840e48f26a77bbca7c417805eeccb1e3f7de198ef29\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/binutils-x86-64-linux-gnu_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"zlib1g_1-1.3.dfsg-3.1ubuntu2.1_amd64\",\n\t\t\t\"name\": \"zlib1g\",\n\t\t\t\"sha256\": \"7074b6a2f6367a10d280c00a1cb02e74277709180bab4f2491a2f355ab2d6c20\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:1.3.dfsg-3.1ubuntu2.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libzstd1_1.5.5-p-dfsg2-2build1.1_amd64\",\n\t\t\t\"name\": \"libzstd1\",\n\t\t\t\"sha256\": \"dfcf25061e07aad7efd3f4f880ba5ad4d4d09ebe7fc8cc77ab6b8a161d6d4727\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libz/libzstd/libzstd1_1.5.5+dfsg2-2build1.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.5.5+dfsg2-2build1.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libstdc-p--p-6_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libstdc++6\",\n\t\t\t\"sha256\": \"a51f8de7829211db961a31f02158058ad1a95f92ac6d0a5dff6350e2821c54c0\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libstdc++6_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libsframe1_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"libsframe1\",\n\t\t\t\"sha256\": \"72093fb456864db55f1352bfa5e952a94f7abaff64e71dff1fbf001db1984564\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libsframe1_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libjansson4_2.14-2build2_amd64\",\n\t\t\t\"name\": \"libjansson4\",\n\t\t\t\"sha256\": \"0cf79113f5d193ce9af2be2ff4b2c3b30dd4e55a0b6c47f7d28f6c849ff3aa60\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/j/jansson/libjansson4_2.14-2build2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.14-2build2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgprofng0_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"libgprofng0\",\n\t\t\t\"sha256\": \"1b7e3c2fc162e8358ca6e5a3fffdb4d0d632f790630323215841bb36a63c0ab8\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libgprofng0_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libbinutils_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"libbinutils\",\n\t\t\t\"sha256\": \"064dce00ce94e1fc2d33779cb0071088f4c8aac79e85345f2e78a020f7d14699\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libbinutils_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"binutils-common_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"binutils-common\",\n\t\t\t\"sha256\": \"d136073f5e2153f3df11c1d08d66727b9466b28ff483f50085f14bbe3464b5ee\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/binutils-common_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libctf0_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"libctf0\",\n\t\t\t\"sha256\": \"7ec86d697c3668503c85f308a6832f092075b5880ad002f22185264da0bd4645\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libctf0_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libctf-nobfd0_2.42-4ubuntu2.10_amd64\",\n\t\t\t\"name\": \"libctf-nobfd0\",\n\t\t\t\"sha256\": \"da352eb7fa6c4369d2a6c1e5e680f574eda2e38576563326b18d9e47e61c4078\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/binutils/libctf-nobfd0_2.42-4ubuntu2.10_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42-4ubuntu2.10\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxt6t64_1-1.2.1-1.2build1_amd64\",\n\t\t\t\t\t\"name\": \"libxt6t64\",\n\t\t\t\t\t\"version\": \"1:1.2.1-1.2build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libx11-6_2-1.8.7-1build1_amd64\",\n\t\t\t\t\t\"name\": \"libx11-6\",\n\t\t\t\t\t\"version\": \"2:1.8.7-1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libx11-data_2-1.8.7-1build1_amd64\",\n\t\t\t\t\t\"name\": \"libx11-data\",\n\t\t\t\t\t\"version\": \"2:1.8.7-1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxcb1_1.15-1ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"libxcb1\",\n\t\t\t\t\t\"version\": \"1.15-1ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxdmcp6_1-1.1.3-0ubuntu6_amd64\",\n\t\t\t\t\t\"name\": \"libxdmcp6\",\n\t\t\t\t\t\"version\": \"1:1.1.3-0ubuntu6\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libbsd0_0.12.1-1build1.1_amd64\",\n\t\t\t\t\t\"name\": \"libbsd0\",\n\t\t\t\t\t\"version\": \"0.12.1-1build1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libmd0_1.1.0-2build1.1_amd64\",\n\t\t\t\t\t\"name\": \"libmd0\",\n\t\t\t\t\t\"version\": \"1.1.0-2build1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxau6_1-1.0.9-1build6_amd64\",\n\t\t\t\t\t\"name\": \"libxau6\",\n\t\t\t\t\t\"version\": \"1:1.0.9-1build6\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libsm6_2-1.2.3-1build3_amd64\",\n\t\t\t\t\t\"name\": \"libsm6\",\n\t\t\t\t\t\"version\": \"2:1.2.3-1build3\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libuuid1_2.39.3-9ubuntu6.5_amd64\",\n\t\t\t\t\t\"name\": \"libuuid1\",\n\t\t\t\t\t\"version\": \"2.39.3-9ubuntu6.5\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libice6_2-1.0.10-1build3_amd64\",\n\t\t\t\t\t\"name\": \"libice6\",\n\t\t\t\t\t\"version\": \"2:1.0.10-1build3\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"x11-common_1-7.7-p-23ubuntu3_amd64\",\n\t\t\t\t\t\"name\": \"x11-common\",\n\t\t\t\t\t\"version\": \"1:7.7+23ubuntu3\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"sysvinit-utils_3.08-6ubuntu3_amd64\",\n\t\t\t\t\t\"name\": \"sysvinit-utils\",\n\t\t\t\t\t\"version\": \"3.08-6ubuntu3\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxmu6_2-1.1.3-3build2_amd64\",\n\t\t\t\t\t\"name\": \"libxmu6\",\n\t\t\t\t\t\"version\": \"2:1.1.3-3build2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxext6_2-1.3.4-1build2_amd64\",\n\t\t\t\t\t\"name\": \"libxext6\",\n\t\t\t\t\t\"version\": \"2:1.3.4-1build2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxaw7_2-1.0.14-1build2_amd64\",\n\t\t\t\t\t\"name\": \"libxaw7\",\n\t\t\t\t\t\"version\": \"2:1.0.14-1build2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxpm4_1-3.5.17-1build2_amd64\",\n\t\t\t\t\t\"name\": \"libxpm4\",\n\t\t\t\t\t\"version\": \"1:3.5.17-1build2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libstdc-p--p-6_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libstdc++6\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"liblab-gamut1_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"liblab-gamut1\",\n\t\t\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgvpr2_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"libgvpr2\",\n\t\t\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcgraph6_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"libcgraph6\",\n\t\t\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcdt5_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"libcdt5\",\n\t\t\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgvc6_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"libgvc6\",\n\t\t\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"zlib1g_1-1.3.dfsg-3.1ubuntu2.1_amd64\",\n\t\t\t\t\t\"name\": \"zlib1g\",\n\t\t\t\t\t\"version\": \"1:1.3.dfsg-3.1ubuntu2.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libwebp7_1.3.2-0.4build3_amd64\",\n\t\t\t\t\t\"name\": \"libwebp7\",\n\t\t\t\t\t\"version\": \"1.3.2-0.4build3\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libsharpyuv0_1.3.2-0.4build3_amd64\",\n\t\t\t\t\t\"name\": \"libsharpyuv0\",\n\t\t\t\t\t\"version\": \"1.3.2-0.4build3\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libpathplan4_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"libpathplan4\",\n\t\t\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libpangoft2-1.0-0_1.52.1-p-ds-1build1_amd64\",\n\t\t\t\t\t\"name\": \"libpangoft2-1.0-0\",\n\t\t\t\t\t\"version\": \"1.52.1+ds-1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libpango-1.0-0_1.52.1-p-ds-1build1_amd64\",\n\t\t\t\t\t\"name\": \"libpango-1.0-0\",\n\t\t\t\t\t\"version\": \"1.52.1+ds-1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libthai0_0.1.29-2build1_amd64\",\n\t\t\t\t\t\"name\": \"libthai0\",\n\t\t\t\t\t\"version\": \"0.1.29-2build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libdatrie1_0.2.13-3build1_amd64\",\n\t\t\t\t\t\"name\": \"libdatrie1\",\n\t\t\t\t\t\"version\": \"0.2.13-3build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libthai-data_0.1.29-2build1_amd64\",\n\t\t\t\t\t\"name\": \"libthai-data\",\n\t\t\t\t\t\"version\": \"0.1.29-2build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libharfbuzz0b_8.3.0-2build2_amd64\",\n\t\t\t\t\t\"name\": \"libharfbuzz0b\",\n\t\t\t\t\t\"version\": \"8.3.0-2build2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgraphite2-3_1.3.14-2build1_amd64\",\n\t\t\t\t\t\"name\": \"libgraphite2-3\",\n\t\t\t\t\t\"version\": \"1.3.14-2build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libglib2.0-0t64_2.80.0-6ubuntu3.8_amd64\",\n\t\t\t\t\t\"name\": \"libglib2.0-0t64\",\n\t\t\t\t\t\"version\": \"2.80.0-6ubuntu3.8\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libselinux1_3.5-2ubuntu2.1_amd64\",\n\t\t\t\t\t\"name\": \"libselinux1\",\n\t\t\t\t\t\"version\": \"3.5-2ubuntu2.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libpcre2-8-0_10.42-4ubuntu2.1_amd64\",\n\t\t\t\t\t\"name\": \"libpcre2-8-0\",\n\t\t\t\t\t\"version\": \"10.42-4ubuntu2.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libmount1_2.39.3-9ubuntu6.5_amd64\",\n\t\t\t\t\t\"name\": \"libmount1\",\n\t\t\t\t\t\"version\": \"2.39.3-9ubuntu6.5\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libblkid1_2.39.3-9ubuntu6.5_amd64\",\n\t\t\t\t\t\"name\": \"libblkid1\",\n\t\t\t\t\t\"version\": \"2.39.3-9ubuntu6.5\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libffi8_3.4.6-1build1_amd64\",\n\t\t\t\t\t\"name\": \"libffi8\",\n\t\t\t\t\t\"version\": \"3.4.6-1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libfreetype6_2.13.2-p-dfsg-1ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"libfreetype6\",\n\t\t\t\t\t\"version\": \"2.13.2+dfsg-1ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libpng16-16t64_1.6.43-5ubuntu0.5_amd64\",\n\t\t\t\t\t\"name\": \"libpng16-16t64\",\n\t\t\t\t\t\"version\": \"1.6.43-5ubuntu0.5\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libbz2-1.0_1.0.8-5.1build0.1_amd64\",\n\t\t\t\t\t\"name\": \"libbz2-1.0\",\n\t\t\t\t\t\"version\": \"1.0.8-5.1build0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libbrotli1_1.1.0-2build2_amd64\",\n\t\t\t\t\t\"name\": \"libbrotli1\",\n\t\t\t\t\t\"version\": \"1.1.0-2build2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libfribidi0_1.0.13-3build1_amd64\",\n\t\t\t\t\t\"name\": \"libfribidi0\",\n\t\t\t\t\t\"version\": \"1.0.13-3build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"fontconfig_2.15.0-1.1ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"fontconfig\",\n\t\t\t\t\t\"version\": \"2.15.0-1.1ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"fontconfig-config_2.15.0-1.1ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"fontconfig-config\",\n\t\t\t\t\t\"version\": \"2.15.0-1.1ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"fonts-dejavu-core_2.37-8_amd64\",\n\t\t\t\t\t\"name\": \"fonts-dejavu-core\",\n\t\t\t\t\t\"version\": \"2.37-8\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"fonts-dejavu-mono_2.37-8_amd64\",\n\t\t\t\t\t\"name\": \"fonts-dejavu-mono\",\n\t\t\t\t\t\"version\": \"2.37-8\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libfontconfig1_2.15.0-1.1ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"libfontconfig1\",\n\t\t\t\t\t\"version\": \"2.15.0-1.1ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libexpat1_2.6.1-2ubuntu0.4_amd64\",\n\t\t\t\t\t\"name\": \"libexpat1\",\n\t\t\t\t\t\"version\": \"2.6.1-2ubuntu0.4\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libpangocairo-1.0-0_1.52.1-p-ds-1build1_amd64\",\n\t\t\t\t\t\"name\": \"libpangocairo-1.0-0\",\n\t\t\t\t\t\"version\": \"1.52.1+ds-1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcairo2_1.18.0-3build1_amd64\",\n\t\t\t\t\t\"name\": \"libcairo2\",\n\t\t\t\t\t\"version\": \"1.18.0-3build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxrender1_1-0.9.10-1.1build1_amd64\",\n\t\t\t\t\t\"name\": \"libxrender1\",\n\t\t\t\t\t\"version\": \"1:0.9.10-1.1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxcb-shm0_1.15-1ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"libxcb-shm0\",\n\t\t\t\t\t\"version\": \"1.15-1ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libxcb-render0_1.15-1ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"libxcb-render0\",\n\t\t\t\t\t\"version\": \"1.15-1ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libpixman-1-0_0.42.2-1build1_amd64\",\n\t\t\t\t\t\"name\": \"libpixman-1-0\",\n\t\t\t\t\t\"version\": \"0.42.2-1build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libltdl7_2.4.7-7build1_amd64\",\n\t\t\t\t\t\"name\": \"libltdl7\",\n\t\t\t\t\t\"version\": \"2.4.7-7build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgts-0.7-5t64_0.7.6-p-darcs121130-5.2build1_amd64\",\n\t\t\t\t\t\"name\": \"libgts-0.7-5t64\",\n\t\t\t\t\t\"version\": \"0.7.6+darcs121130-5.2build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgd3_2.3.3-9ubuntu5_amd64\",\n\t\t\t\t\t\"name\": \"libgd3\",\n\t\t\t\t\t\"version\": \"2.3.3-9ubuntu5\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libtiff6_4.5.1-p-git230720-4ubuntu2.5_amd64\",\n\t\t\t\t\t\"name\": \"libtiff6\",\n\t\t\t\t\t\"version\": \"4.5.1+git230720-4ubuntu2.5\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libzstd1_1.5.5-p-dfsg2-2build1.1_amd64\",\n\t\t\t\t\t\"name\": \"libzstd1\",\n\t\t\t\t\t\"version\": \"1.5.5+dfsg2-2build1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"liblzma5_5.6.1-p-really5.4.5-1ubuntu0.2_amd64\",\n\t\t\t\t\t\"name\": \"liblzma5\",\n\t\t\t\t\t\"version\": \"5.6.1+really5.4.5-1ubuntu0.2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"liblerc4_4.0.0-p-ds-4ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"liblerc4\",\n\t\t\t\t\t\"version\": \"4.0.0+ds-4ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libjpeg8_8c-2ubuntu11_amd64\",\n\t\t\t\t\t\"name\": \"libjpeg8\",\n\t\t\t\t\t\"version\": \"8c-2ubuntu11\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libjpeg-turbo8_2.1.5-2ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"libjpeg-turbo8\",\n\t\t\t\t\t\"version\": \"2.1.5-2ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libjbig0_2.1-6.1ubuntu2_amd64\",\n\t\t\t\t\t\"name\": \"libjbig0\",\n\t\t\t\t\t\"version\": \"2.1-6.1ubuntu2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libdeflate0_1.19-1build1.1_amd64\",\n\t\t\t\t\t\"name\": \"libdeflate0\",\n\t\t\t\t\t\"version\": \"1.19-1build1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libheif1_1.17.6-1ubuntu4.2_amd64\",\n\t\t\t\t\t\"name\": \"libheif1\",\n\t\t\t\t\t\"version\": \"1.17.6-1ubuntu4.2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libheif-plugin-libde265_1.17.6-1ubuntu4.2_amd64\",\n\t\t\t\t\t\"name\": \"libheif-plugin-libde265\",\n\t\t\t\t\t\"version\": \"1.17.6-1ubuntu4.2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libde265-0_1.0.15-1build3_amd64\",\n\t\t\t\t\t\"name\": \"libde265-0\",\n\t\t\t\t\t\"version\": \"1.0.15-1build3\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libheif-plugin-aomdec_1.17.6-1ubuntu4.2_amd64\",\n\t\t\t\t\t\"name\": \"libheif-plugin-aomdec\",\n\t\t\t\t\t\"version\": \"1.17.6-1ubuntu4.2\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libaom3_3.8.2-2ubuntu0.1_amd64\",\n\t\t\t\t\t\"name\": \"libaom3\",\n\t\t\t\t\t\"version\": \"3.8.2-2ubuntu0.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libann0_1.1.2-p-doc-9build1_amd64\",\n\t\t\t\t\t\"name\": \"libann0\",\n\t\t\t\t\t\"version\": \"1.1.2+doc-9build1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"graphviz_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\"name\": \"graphviz\",\n\t\t\t\"sha256\": \"be4e5b1b036babfb1663c095bc645c2fbab79dd57b213fc3b1ab0d48e810dd85\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/graphviz_2.42.2-9ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxt6t64_1-1.2.1-1.2build1_amd64\",\n\t\t\t\"name\": \"libxt6t64\",\n\t\t\t\"sha256\": \"2e4317d5ae1a45e517771ea0b8d93519b5f915ab08983facecd480d64dbd1489\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxt/libxt6t64_1.2.1-1.2build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:1.2.1-1.2build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libx11-6_2-1.8.7-1build1_amd64\",\n\t\t\t\"name\": \"libx11-6\",\n\t\t\t\"sha256\": \"397f84347476a3c5786b39f3ff6f0f82866eb3d8be6d2ad3efeadf019efe5b80\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libx11/libx11-6_1.8.7-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2:1.8.7-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libx11-data_2-1.8.7-1build1_amd64\",\n\t\t\t\"name\": \"libx11-data\",\n\t\t\t\"sha256\": \"9ae01f7747e7f479394c697c55acf11d3baf139e8e54b7c4adfb55ef9c50de08\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libx11/libx11-data_1.8.7-1build1_all.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2:1.8.7-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxcb1_1.15-1ubuntu2_amd64\",\n\t\t\t\"name\": \"libxcb1\",\n\t\t\t\"sha256\": \"e1c6611d11ad7398326f1bf028afc34c3b14c51d917a3426b966ed4b9687fa58\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcb/libxcb1_1.15-1ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.15-1ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxdmcp6_1-1.1.3-0ubuntu6_amd64\",\n\t\t\t\"name\": \"libxdmcp6\",\n\t\t\t\"sha256\": \"bcd336fce11ce2a45f34d0f95e6980af22529f22147e8f98c156e5cee8ee42bb\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxdmcp/libxdmcp6_1.1.3-0ubuntu6_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:1.1.3-0ubuntu6\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libbsd0_0.12.1-1build1.1_amd64\",\n\t\t\t\"name\": \"libbsd0\",\n\t\t\t\"sha256\": \"f3857b0863ac5cfd4263e9bf6cfb1d4be88d5321e4070d5bc2b62b0949e6c86f\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libb/libbsd/libbsd0_0.12.1-1build1.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"0.12.1-1build1.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libmd0_1.1.0-2build1.1_amd64\",\n\t\t\t\"name\": \"libmd0\",\n\t\t\t\"sha256\": \"e5ba01d3c41f256aaf57ec59aa0554857162e3e7f97cdfbff1ed2c0e8d720ee7\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libm/libmd/libmd0_1.1.0-2build1.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.1.0-2build1.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxau6_1-1.0.9-1build6_amd64\",\n\t\t\t\"name\": \"libxau6\",\n\t\t\t\"sha256\": \"e40d29f1d1a62393bacaedebe0da3d9006084152a9f7e5e029293f08ce1c5c80\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxau/libxau6_1.0.9-1build6_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:1.0.9-1build6\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libsm6_2-1.2.3-1build3_amd64\",\n\t\t\t\"name\": \"libsm6\",\n\t\t\t\"sha256\": \"1d27ebc381b499075a28c504be4d7d424c63b6866354736ac7f8d25813860cdf\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libs/libsm/libsm6_1.2.3-1build3_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2:1.2.3-1build3\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libuuid1_2.39.3-9ubuntu6.5_amd64\",\n\t\t\t\"name\": \"libuuid1\",\n\t\t\t\"sha256\": \"2f801582b5f0e7cb0c2d3d3651193e69f4540ee3b7dcb6079a3562f0880b9edf\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/u/util-linux/libuuid1_2.39.3-9ubuntu6.5_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.39.3-9ubuntu6.5\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libice6_2-1.0.10-1build3_amd64\",\n\t\t\t\"name\": \"libice6\",\n\t\t\t\"sha256\": \"ad1edb5303574fee154e487947177e5bd15363aa71b92f78f5a2a7145fdb81c5\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libi/libice/libice6_1.0.10-1build3_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2:1.0.10-1build3\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"x11-common_1-7.7-p-23ubuntu3_amd64\",\n\t\t\t\"name\": \"x11-common\",\n\t\t\t\"sha256\": \"b545fa5196dd7467ba3770d6ce575abcd7941071961ea9f7535a319d9d20fd46\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/x/xorg/x11-common_7.7+23ubuntu3_all.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:7.7+23ubuntu3\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"sysvinit-utils_3.08-6ubuntu3_amd64\",\n\t\t\t\"name\": \"sysvinit-utils\",\n\t\t\t\"sha256\": \"03cf5c9c28a5d186fb7f5214e464f831ff28177695cbb61e66083237267a047e\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/s/sysvinit/sysvinit-utils_3.08-6ubuntu3_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"3.08-6ubuntu3\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxmu6_2-1.1.3-3build2_amd64\",\n\t\t\t\"name\": \"libxmu6\",\n\t\t\t\"sha256\": \"05c13bce0e9a80139dfa72cc8e7a695b4f7d58b1884423929eade62179d99853\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxmu/libxmu6_1.1.3-3build2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2:1.1.3-3build2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxext6_2-1.3.4-1build2_amd64\",\n\t\t\t\"name\": \"libxext6\",\n\t\t\t\"sha256\": \"45783969a9ece9d7b7b733b8c60981584c53c6bc5ee3b42d295d2f80d1285679\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxext/libxext6_1.3.4-1build2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2:1.3.4-1build2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxaw7_2-1.0.14-1build2_amd64\",\n\t\t\t\"name\": \"libxaw7\",\n\t\t\t\"sha256\": \"2a9cdc82ecf566de76e05394efc604c9cced49e12c28b10e1ca101062b47bac0\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxaw/libxaw7_1.0.14-1build2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2:1.0.14-1build2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxpm4_1-3.5.17-1build2_amd64\",\n\t\t\t\"name\": \"libxpm4\",\n\t\t\t\"sha256\": \"ad9d9cb9be156d8b80c12735ecb4aae57e5501280a9f952749e8b71a7e5f9c97\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxpm/libxpm4_3.5.17-1build2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:3.5.17-1build2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"liblab-gamut1_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\"name\": \"liblab-gamut1\",\n\t\t\t\"sha256\": \"b9fa73d671d96b07360dd3b7a4d35814c8b892b5bf4b1e0945d3bc7937c9ee3d\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/liblab-gamut1_2.42.2-9ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgvpr2_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\"name\": \"libgvpr2\",\n\t\t\t\"sha256\": \"1a560080462b80ab56e0fe09af2abd52974a256e25b4f384b9be5872c1073c82\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libgvpr2_2.42.2-9ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libcgraph6_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\"name\": \"libcgraph6\",\n\t\t\t\"sha256\": \"4555e20d8cd068de6343484c5bf825d03830c5048ccf56a9baec9638ca5815b2\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libcgraph6_2.42.2-9ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libcdt5_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\"name\": \"libcdt5\",\n\t\t\t\"sha256\": \"3c49c2624e6a895d729bff5be16c96beeb1347e3282bc40f3d210c715fba07ef\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libcdt5_2.42.2-9ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgvc6_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\"name\": \"libgvc6\",\n\t\t\t\"sha256\": \"dc674afb98d9b64699f63be3a4e92d4f3b563435ebfc70cbaec0c91b8f41fb48\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libgvc6_2.42.2-9ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libwebp7_1.3.2-0.4build3_amd64\",\n\t\t\t\"name\": \"libwebp7\",\n\t\t\t\"sha256\": \"c86f6439f0bbc531f7199794654231f3b29327d6585958511b958795d51c2484\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libw/libwebp/libwebp7_1.3.2-0.4build3_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.3.2-0.4build3\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libsharpyuv0_1.3.2-0.4build3_amd64\",\n\t\t\t\"name\": \"libsharpyuv0\",\n\t\t\t\"sha256\": \"4e1763494e5b9d34192313aa88e38201fc67b3f13ea96ca268d26401a3002791\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libw/libwebp/libsharpyuv0_1.3.2-0.4build3_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.3.2-0.4build3\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libpathplan4_2.42.2-9ubuntu0.1_amd64\",\n\t\t\t\"name\": \"libpathplan4\",\n\t\t\t\"sha256\": \"8cff70c607271662df4719d2e2848731d8f23ab9108e79404066a6f93af9fd23\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/graphviz/libpathplan4_2.42.2-9ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.42.2-9ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libpangoft2-1.0-0_1.52.1-p-ds-1build1_amd64\",\n\t\t\t\"name\": \"libpangoft2-1.0-0\",\n\t\t\t\"sha256\": \"cc6bc9d86ef5f329edacaa4895232d8a9b8f95c5cbc77c3c460a3f64cecc213d\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pango1.0/libpangoft2-1.0-0_1.52.1+ds-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.52.1+ds-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libpango-1.0-0_1.52.1-p-ds-1build1_amd64\",\n\t\t\t\"name\": \"libpango-1.0-0\",\n\t\t\t\"sha256\": \"09dfa5c881ab273ec6bc1830adcefdc407fcd619316e68fb26ca3a23d0fc9f54\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pango1.0/libpango-1.0-0_1.52.1+ds-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.52.1+ds-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libthai0_0.1.29-2build1_amd64\",\n\t\t\t\"name\": \"libthai0\",\n\t\t\t\"sha256\": \"5ada7045c5f84a4b774e6449151800aa6470e8d01dc9d6124ef4a44ae6af5508\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libt/libthai/libthai0_0.1.29-2build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"0.1.29-2build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libdatrie1_0.2.13-3build1_amd64\",\n\t\t\t\"name\": \"libdatrie1\",\n\t\t\t\"sha256\": \"1ae164e40e413eac4fbd38ce1c6ef9591f7a03278ad9076d71fa29258727f447\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libd/libdatrie/libdatrie1_0.2.13-3build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"0.2.13-3build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libthai-data_0.1.29-2build1_amd64\",\n\t\t\t\"name\": \"libthai-data\",\n\t\t\t\"sha256\": \"6035d4a714290b97816bed7f3bf64921c6358ddb84d1dacdddccdc1cf39bffb7\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libt/libthai/libthai-data_0.1.29-2build1_all.deb\"\n\t\t\t],\n\t\t\t\"version\": \"0.1.29-2build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libharfbuzz0b_8.3.0-2build2_amd64\",\n\t\t\t\"name\": \"libharfbuzz0b\",\n\t\t\t\"sha256\": \"d6f7eea2244f98aa0463a056680d4629476bf624767ede301560e24add686b5c\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/h/harfbuzz/libharfbuzz0b_8.3.0-2build2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"8.3.0-2build2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgraphite2-3_1.3.14-2build1_amd64\",\n\t\t\t\"name\": \"libgraphite2-3\",\n\t\t\t\"sha256\": \"e2a91c59a2b26649ed0b331bedab0c92e56cc2021cce27df1080089edaad7aba\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/graphite2/libgraphite2-3_1.3.14-2build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.3.14-2build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libglib2.0-0t64_2.80.0-6ubuntu3.8_amd64\",\n\t\t\t\"name\": \"libglib2.0-0t64\",\n\t\t\t\"sha256\": \"ca4eda0e01c76ba2e5e501e77decce53d98a641234b517eb98fc5d74980f913f\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glib2.0/libglib2.0-0t64_2.80.0-6ubuntu3.8_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.80.0-6ubuntu3.8\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libselinux1_3.5-2ubuntu2.1_amd64\",\n\t\t\t\"name\": \"libselinux1\",\n\t\t\t\"sha256\": \"6abaa6c26f46ef17764c4a753e0e84de1cdadde5634fd2987621fdc617988d19\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libs/libselinux/libselinux1_3.5-2ubuntu2.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"3.5-2ubuntu2.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libpcre2-8-0_10.42-4ubuntu2.1_amd64\",\n\t\t\t\"name\": \"libpcre2-8-0\",\n\t\t\t\"sha256\": \"110a797a57673d3ee497a141cf988199258058c57525799c63194d81822529a0\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pcre2/libpcre2-8-0_10.42-4ubuntu2.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"10.42-4ubuntu2.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libmount1_2.39.3-9ubuntu6.5_amd64\",\n\t\t\t\"name\": \"libmount1\",\n\t\t\t\"sha256\": \"7ca31424fbfc96fbf245e6fe232fd2d2ec74169ca1add968e1987bece5bc0d1f\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/u/util-linux/libmount1_2.39.3-9ubuntu6.5_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.39.3-9ubuntu6.5\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libblkid1_2.39.3-9ubuntu6.5_amd64\",\n\t\t\t\"name\": \"libblkid1\",\n\t\t\t\"sha256\": \"2307a9ed92642a69de0284ac63e233e45ac5a2c3831f04399f7d212379487e82\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/u/util-linux/libblkid1_2.39.3-9ubuntu6.5_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.39.3-9ubuntu6.5\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libffi8_3.4.6-1build1_amd64\",\n\t\t\t\"name\": \"libffi8\",\n\t\t\t\"sha256\": \"637e6a7744de08cd331a41f4efd0d24e6ea9064843dea9d1c6ca87bdb5f038a2\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libf/libffi/libffi8_3.4.6-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"3.4.6-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libfreetype6_2.13.2-p-dfsg-1ubuntu0.1_amd64\",\n\t\t\t\"name\": \"libfreetype6\",\n\t\t\t\"sha256\": \"f6937fd8a77e83001dcfd3857d5a5cfbd4caaeb297c7fdb71ec50514843e48af\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/freetype/libfreetype6_2.13.2+dfsg-1ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.13.2+dfsg-1ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libpng16-16t64_1.6.43-5ubuntu0.5_amd64\",\n\t\t\t\"name\": \"libpng16-16t64\",\n\t\t\t\"sha256\": \"074c954a7a01069c3b3db9146911666f6ac75c1277e034eba901ee274554dadc\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libp/libpng1.6/libpng16-16t64_1.6.43-5ubuntu0.5_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.6.43-5ubuntu0.5\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libbz2-1.0_1.0.8-5.1build0.1_amd64\",\n\t\t\t\"name\": \"libbz2-1.0\",\n\t\t\t\"sha256\": \"d557ab12b42ab370249142099fae3cbb979948934e4dfa58c2ab59bf5bbbda73\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/bzip2/libbz2-1.0_1.0.8-5.1build0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.0.8-5.1build0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libbrotli1_1.1.0-2build2_amd64\",\n\t\t\t\"name\": \"libbrotli1\",\n\t\t\t\"sha256\": \"74492419b8fda803774b8c9acef6afc5d2f9ff31782635aae212906adae7b277\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/brotli/libbrotli1_1.1.0-2build2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.1.0-2build2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libfribidi0_1.0.13-3build1_amd64\",\n\t\t\t\"name\": \"libfribidi0\",\n\t\t\t\"sha256\": \"6cd50259d39ce0dfafee2632c6268538e6a02590e77161a133e9683af346dd1d\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fribidi/libfribidi0_1.0.13-3build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.0.13-3build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"fontconfig_2.15.0-1.1ubuntu2_amd64\",\n\t\t\t\"name\": \"fontconfig\",\n\t\t\t\"sha256\": \"ddf0aace42b13a5bc38904e848b145b787ea254cddf5213d150254bd654db6e5\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fontconfig/fontconfig_2.15.0-1.1ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.15.0-1.1ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"fontconfig-config_2.15.0-1.1ubuntu2_amd64\",\n\t\t\t\"name\": \"fontconfig-config\",\n\t\t\t\"sha256\": \"9df0579cd298a1f2b7a40efd519c849745aecd193a09733cf4d1032284a62bbf\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fontconfig/fontconfig-config_2.15.0-1.1ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.15.0-1.1ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"fonts-dejavu-core_2.37-8_amd64\",\n\t\t\t\"name\": \"fonts-dejavu-core\",\n\t\t\t\"sha256\": \"40049660c194f3b8a2541fc7369efebb10e9f94bdac836a2f38fafedd10fa73a\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fonts-dejavu/fonts-dejavu-core_2.37-8_all.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.37-8\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"fonts-dejavu-mono_2.37-8_amd64\",\n\t\t\t\"name\": \"fonts-dejavu-mono\",\n\t\t\t\"sha256\": \"8a599d6553307db7ecb795d2f0e5a301e03234afc75c7358b0ba43466454c89a\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fonts-dejavu/fonts-dejavu-mono_2.37-8_all.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.37-8\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libfontconfig1_2.15.0-1.1ubuntu2_amd64\",\n\t\t\t\"name\": \"libfontconfig1\",\n\t\t\t\"sha256\": \"a2bc05cfef021fdb84285036f98eda5ebec3c4b7a378f5aa2bdba5c4d3d8d586\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/f/fontconfig/libfontconfig1_2.15.0-1.1ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.15.0-1.1ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libexpat1_2.6.1-2ubuntu0.4_amd64\",\n\t\t\t\"name\": \"libexpat1\",\n\t\t\t\"sha256\": \"126a5612e652bdc2edee19ae8fe4308db72b5b3b0a5581bf885b44a093baf3e5\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/e/expat/libexpat1_2.6.1-2ubuntu0.4_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.6.1-2ubuntu0.4\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libpangocairo-1.0-0_1.52.1-p-ds-1build1_amd64\",\n\t\t\t\"name\": \"libpangocairo-1.0-0\",\n\t\t\t\"sha256\": \"98cf5fc9076c2911fe20dfc1e41cd8f686a2bb5d539b1105fab18105b0db1376\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pango1.0/libpangocairo-1.0-0_1.52.1+ds-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.52.1+ds-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libcairo2_1.18.0-3build1_amd64\",\n\t\t\t\"name\": \"libcairo2\",\n\t\t\t\"sha256\": \"96950b306889ff6e4248bb937b0a3b56e72dacf643fe732214ce375f0f6bbb36\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/c/cairo/libcairo2_1.18.0-3build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.18.0-3build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxrender1_1-0.9.10-1.1build1_amd64\",\n\t\t\t\"name\": \"libxrender1\",\n\t\t\t\"sha256\": \"d70bd831aebe8d4834b5dd2ed98df26dd6bd27f1042c47543bd7f66df1ae22ea\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxrender/libxrender1_0.9.10-1.1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:0.9.10-1.1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxcb-shm0_1.15-1ubuntu2_amd64\",\n\t\t\t\"name\": \"libxcb-shm0\",\n\t\t\t\"sha256\": \"229d1280d459f1ba44c22939d3f9b61d9d20932d9a646b3fe4ce50be4cdf2325\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcb/libxcb-shm0_1.15-1ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.15-1ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libxcb-render0_1.15-1ubuntu2_amd64\",\n\t\t\t\"name\": \"libxcb-render0\",\n\t\t\t\"sha256\": \"7d83a0668b1a693b5ea2e496206e8371fb6beda4c54a2b4b3902005915c272c5\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcb/libxcb-render0_1.15-1ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.15-1ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libpixman-1-0_0.42.2-1build1_amd64\",\n\t\t\t\"name\": \"libpixman-1-0\",\n\t\t\t\"sha256\": \"d9c2931c4c424615eeab9dd5ae08bfc608b84e803c1d3ccddf319270db213421\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/p/pixman/libpixman-1-0_0.42.2-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"0.42.2-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libltdl7_2.4.7-7build1_amd64\",\n\t\t\t\"name\": \"libltdl7\",\n\t\t\t\"sha256\": \"02d0d2d7e1bf73f838a92f8df7a9b07b698c837e82bd89aee6a581cf793cd966\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libt/libtool/libltdl7_2.4.7-7build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.4.7-7build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgts-0.7-5t64_0.7.6-p-darcs121130-5.2build1_amd64\",\n\t\t\t\"name\": \"libgts-0.7-5t64\",\n\t\t\t\"sha256\": \"a6741ed94f3e786eaed0a9fcb69f641327cfe288af4f043e7036b5257e87fc48\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/g/gts/libgts-0.7-5t64_0.7.6+darcs121130-5.2build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"0.7.6+darcs121130-5.2build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgd3_2.3.3-9ubuntu5_amd64\",\n\t\t\t\"name\": \"libgd3\",\n\t\t\t\"sha256\": \"dbef5ad99eec399fd6afeb6d0cc92d81a8b3295a7ac80ac71311ea09c49704ae\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libg/libgd2/libgd3_2.3.3-9ubuntu5_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.3.3-9ubuntu5\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libtiff6_4.5.1-p-git230720-4ubuntu2.5_amd64\",\n\t\t\t\"name\": \"libtiff6\",\n\t\t\t\"sha256\": \"564cdf59dda5e1a826c51b5868ee2785fffa26ee5a7bad95bc5e8c8224c931b8\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/t/tiff/libtiff6_4.5.1+git230720-4ubuntu2.5_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"4.5.1+git230720-4ubuntu2.5\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"liblzma5_5.6.1-p-really5.4.5-1ubuntu0.2_amd64\",\n\t\t\t\"name\": \"liblzma5\",\n\t\t\t\"sha256\": \"4c31cc76391ca47dbb4585edc740728f43fe7b6b090f5b3947fc8072db698aca\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/x/xz-utils/liblzma5_5.6.1+really5.4.5-1ubuntu0.2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"5.6.1+really5.4.5-1ubuntu0.2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"liblerc4_4.0.0-p-ds-4ubuntu2_amd64\",\n\t\t\t\"name\": \"liblerc4\",\n\t\t\t\"sha256\": \"f1b9dbfa28c56564f6b544f3757f5f3f4d549ce9962f39e897812c23b0a5d0e7\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/l/lerc/liblerc4_4.0.0+ds-4ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"4.0.0+ds-4ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libjpeg8_8c-2ubuntu11_amd64\",\n\t\t\t\"name\": \"libjpeg8\",\n\t\t\t\"sha256\": \"4d6ed682972bddf62af158a06d68a0dda497e421423b7ca92fb450b3d9ceaac9\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libj/libjpeg8-empty/libjpeg8_8c-2ubuntu11_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"8c-2ubuntu11\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libjpeg-turbo8_2.1.5-2ubuntu2_amd64\",\n\t\t\t\"name\": \"libjpeg-turbo8\",\n\t\t\t\"sha256\": \"f68b5b23bc8a1688fb787d2aed7e2cdf895a73022f6a5025e183162dac4500b2\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.1.5-2ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.1.5-2ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libjbig0_2.1-6.1ubuntu2_amd64\",\n\t\t\t\"name\": \"libjbig0\",\n\t\t\t\"sha256\": \"074a760a86213a1286ad60f56394c90885cf45af070626a88c480d90b78c175a\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/j/jbigkit/libjbig0_2.1-6.1ubuntu2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.1-6.1ubuntu2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libdeflate0_1.19-1build1.1_amd64\",\n\t\t\t\"name\": \"libdeflate0\",\n\t\t\t\"sha256\": \"8e7dfa9e63a9b5058071b84da48bb2e30dea07de169e80fbc759fe0e68639269\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libd/libdeflate/libdeflate0_1.19-1build1.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.19-1build1.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libheif1_1.17.6-1ubuntu4.2_amd64\",\n\t\t\t\"name\": \"libheif1\",\n\t\t\t\"sha256\": \"c41176242ae4a6a66fa50fab00dc6d951711f0cf8aab32cf13530c4a404a7026\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libh/libheif/libheif1_1.17.6-1ubuntu4.2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.17.6-1ubuntu4.2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libheif-plugin-libde265_1.17.6-1ubuntu4.2_amd64\",\n\t\t\t\"name\": \"libheif-plugin-libde265\",\n\t\t\t\"sha256\": \"f5284ae47a70538c503b15f458783df569ac7b8d5d16aaeac4e04c2d3b8e7341\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libh/libheif/libheif-plugin-libde265_1.17.6-1ubuntu4.2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.17.6-1ubuntu4.2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libde265-0_1.0.15-1build3_amd64\",\n\t\t\t\"name\": \"libde265-0\",\n\t\t\t\"sha256\": \"3b8cb1fb70933261d076d1b2882fb303aa8da1a951ad7614234f890468c90310\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libd/libde265/libde265-0_1.0.15-1build3_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.0.15-1build3\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libheif-plugin-aomdec_1.17.6-1ubuntu4.2_amd64\",\n\t\t\t\"name\": \"libheif-plugin-aomdec\",\n\t\t\t\"sha256\": \"65ce5cb8f2ee27127b7df6b358bde6e58ebcf00e1d9f5c92c32c842cfa260ef6\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libh/libheif/libheif-plugin-aomdec_1.17.6-1ubuntu4.2_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.17.6-1ubuntu4.2\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libaom3_3.8.2-2ubuntu0.1_amd64\",\n\t\t\t\"name\": \"libaom3\",\n\t\t\t\"sha256\": \"1623e49454a2981069ed4801c853630a63a68f77c73056dea11e3289a8467eb9\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/a/aom/libaom3_3.8.2-2ubuntu0.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"3.8.2-2ubuntu0.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libann0_1.1.2-p-doc-9build1_amd64\",\n\t\t\t\"name\": \"libann0\",\n\t\t\t\"sha256\": \"88347025b6292517dc591fd6a51c28b6a91a46c999505e3e54677c99038074e1\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/universe/a/ann/libann0_1.1.2+doc-9build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.1.2+doc-9build1\"\n\t\t}\n\t],\n\t\"version\": 1\n}", + "package_template": "@@rules_distroless+//apt/private:package.BUILD.tmpl" + } + }, + "tooling_sysroot_base-files_13ubuntu10.4_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/base-files/base-files_13ubuntu10.4_amd64.deb" + ], + "sha256": "94daf9ede91f6263d676611623d4c9edab695c728a584ab2fb1d11cabb8479e8" + } + }, + "tooling_sysroot_libcrypt1_1-4.4.36-4build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcrypt/libcrypt1_4.4.36-4build1_amd64.deb" + ], + "sha256": "9474785cd6f398512bf8c305c3901dbb111569dccb6f5832002373c0a8ac5832" + } + }, + "tooling_sysroot_libc6_2.39-0ubuntu8.7_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc6_2.39-0ubuntu8.7_amd64.deb" + ], + "sha256": "955644e8bc2930a9bf8eea5e4c2237c8a118c1e2ac2845b993b6f7f35eefd293" + } + }, + "tooling_sysroot_libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libgcc-s1_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "aa7fadbe33b78bcf99885318040601c550c208929565b179891d9a3cc2aa68cd" + } + }, + "tooling_sysroot_gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/gcc-14-base_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "b95c172411a7fdae70307cf33a9f5320ba5e056b556454543dd5b679d5ce1c4f" + } + }, + "tooling_sysroot_mawk_1.3.4.20240123-1build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/m/mawk/mawk_1.3.4.20240123-1build1_amd64.deb" + ], + "sha256": "dc7f7f4dad4b48f6012ea65de3198d8376604afef39f06d65ec6167740e203c9" + } + }, + "tooling_sysroot_libatomic1_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libatomic1_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "fe49cbbc7be753528380c724a8eef5f1e31dffa9221f692c5069048d81c7449d" + } + }, + "tooling_sysroot_libc6-dev_2.39-0ubuntu8.7_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc6-dev_2.39-0ubuntu8.7_amd64.deb" + ], + "sha256": "bbf5a155039042634961a61276650631ee47b9e721f91f8dbb731b0bbe046df3" + } + }, + "tooling_sysroot_rpcsvc-proto_1.4.2-0ubuntu7_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/r/rpcsvc-proto/rpcsvc-proto_1.4.2-0ubuntu7_amd64.deb" + ], + "sha256": "7eb710fe148d224c159ddec1ceb0ba53ead52a80a6793dcdae1474acf20d8f71" + } + }, + "tooling_sysroot_libcrypt-dev_1-4.4.36-4build1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcrypt/libcrypt-dev_4.4.36-4build1_amd64.deb" + ], + "sha256": "2edff420ef80b4a3f3751e65c33423ef30e563122a58b759e4854ea8d84ba1b1" + } + }, + "tooling_sysroot_linux-libc-dev_6.8.0-107.107_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/l/linux/linux-libc-dev_6.8.0-107.107_amd64.deb" + ], + "sha256": "fcc88782dc3857d661c7ae8b0435174cb41ce0bcda0d2c4c0fbfdadd9f03ff43" + } + }, + "tooling_sysroot_libc-dev-bin_2.39-0ubuntu8.7_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc-dev-bin_2.39-0ubuntu8.7_amd64.deb" + ], + "sha256": "83291a1d9b26262ac8f44a3bb188ce2cb796a0543134aae00e19db066c84dfdd" + } + }, + "tooling_sysroot_libstdc-p--p--13-dev_13.3.0-6ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-13/libstdc++-13-dev_13.3.0-6ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "ee5633e863e19c3381ed97842ce35ed32ede96a3d1ae4e94c051d3036fe21347" + } + }, + "tooling_sysroot_libstdc-p--p-6_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libstdc++6_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "a51f8de7829211db961a31f02158058ad1a95f92ac6d0a5dff6350e2821c54c0" + } + }, + "tooling_sysroot_libgcc-13-dev_13.3.0-6ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-13/libgcc-13-dev_13.3.0-6ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "cd689db2691edaa10f37329307292796bb599e722e0505c79e14caaa1fe9a93a" + } + }, + "tooling_sysroot_libquadmath0_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libquadmath0_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "dc8f0ca542e09d662f29370c8393c016440dd4bc5c996c5fcc19f632b63ce3b0" + } + }, + "tooling_sysroot_libhwasan0_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libhwasan0_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "2195318cfe68fe16b601913ef7b33c9a900372f57861643fdc9ae6fef84534cd" + } + }, + "tooling_sysroot_libubsan1_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libubsan1_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "a16dea3abe2dcac99bcfae27e7e5672fde64573c3c170dcb0cd55631238f9814" + } + }, + "tooling_sysroot_libtsan2_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libtsan2_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "8cbcc9b3ae5ef23b449383d47a9035b27596e307d7dce7df9b83d47a7acd1d91" + } + }, + "tooling_sysroot_liblsan0_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/liblsan0_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "dc0c2a1a053e833ba4d71e1ec2ba4244fe301761ad4a91d6725f927a52d86a14" + } + }, + "tooling_sysroot_libasan8_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libasan8_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "8321aac6230fa1da320e76eb6288b7436164624aec449ed3933ea6c4cc86daac" + } + }, + "tooling_sysroot_libitm1_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libitm1_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "1fca498129dd3510294809d77ee754f72a9de281111200e9b7b9a5adf37faa9f" + } + }, + "tooling_sysroot_libgomp1_14.2.0-4ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libgomp1_14.2.0-4ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "e8a95ec58125b4933597f30ff56c2ae10edf90f287262e366d4b6edea3019144" + } + }, + "tooling_sysroot_gcc-13-base_13.3.0-6ubuntu2_24.04.1_amd64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_distroless//apt/private:deb_postfix.bzl\", \"deb_postfix\")\n\ndeb_postfix(\n name = \"data\",\n srcs = glob([\"data.tar*\"]),\n outs = [\"layer.tar.gz\"],\n mergedusr = False,\n\n visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"control\",\n srcs = glob([\"control.tar.*\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "urls": [ + "https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-13/gcc-13-base_13.3.0-6ubuntu2~24.04.1_amd64.deb" + ], + "sha256": "e859aca26585bb91113a451f1e66bc0e5283cb08797d679aacc1d936ae6dff8e" + } + }, + "tooling_sysroot_resolve": { + "repoRuleId": "@@rules_distroless+//apt/private:deb_resolve.bzl%deb_resolve", + "attributes": { + "manifest": "@@score_tooling+//third_party/tooling_sysroot:manifest.yaml", + "resolve_transitive": true + } + }, + "tooling_sysroot": { + "repoRuleId": "@@rules_distroless+//apt/private:deb_translate_lock.bzl%deb_translate_lock", + "attributes": { + "lock_content": "{\n\t\"packages\": [\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcrypt1_1-4.4.36-4build1_amd64\",\n\t\t\t\t\t\"name\": \"libcrypt1\",\n\t\t\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"mawk_1.3.4.20240123-1build1_amd64\",\n\t\t\t\t\t\"name\": \"mawk\",\n\t\t\t\t\t\"version\": \"1.3.4.20240123-1build1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"base-files_13ubuntu10.4_amd64\",\n\t\t\t\"name\": \"base-files\",\n\t\t\t\"sha256\": \"94daf9ede91f6263d676611623d4c9edab695c728a584ab2fb1d11cabb8479e8\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/b/base-files/base-files_13ubuntu10.4_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"13ubuntu10.4\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libcrypt1_1-4.4.36-4build1_amd64\",\n\t\t\t\"name\": \"libcrypt1\",\n\t\t\t\"sha256\": \"9474785cd6f398512bf8c305c3901dbb111569dccb6f5832002373c0a8ac5832\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcrypt/libcrypt1_4.4.36-4build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\"name\": \"libc6\",\n\t\t\t\"sha256\": \"955644e8bc2930a9bf8eea5e4c2237c8a118c1e2ac2845b993b6f7f35eefd293\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc6_2.39-0ubuntu8.7_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\"sha256\": \"aa7fadbe33b78bcf99885318040601c550c208929565b179891d9a3cc2aa68cd\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libgcc-s1_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\"sha256\": \"b95c172411a7fdae70307cf33a9f5320ba5e056b556454543dd5b679d5ce1c4f\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/gcc-14-base_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"mawk_1.3.4.20240123-1build1_amd64\",\n\t\t\t\"name\": \"mawk\",\n\t\t\t\"sha256\": \"dc7f7f4dad4b48f6012ea65de3198d8376604afef39f06d65ec6167740e203c9\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/m/mawk/mawk_1.3.4.20240123-1build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.3.4.20240123-1build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"libatomic1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libatomic1\",\n\t\t\t\"sha256\": \"fe49cbbc7be753528380c724a8eef5f1e31dffa9221f692c5069048d81c7449d\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libatomic1_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"rpcsvc-proto_1.4.2-0ubuntu7_amd64\",\n\t\t\t\t\t\"name\": \"rpcsvc-proto\",\n\t\t\t\t\t\"version\": \"1.4.2-0ubuntu7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcrypt-dev_1-4.4.36-4build1_amd64\",\n\t\t\t\t\t\"name\": \"libcrypt-dev\",\n\t\t\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcrypt1_1-4.4.36-4build1_amd64\",\n\t\t\t\t\t\"name\": \"libcrypt1\",\n\t\t\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"linux-libc-dev_6.8.0-107.107_amd64\",\n\t\t\t\t\t\"name\": \"linux-libc-dev\",\n\t\t\t\t\t\"version\": \"6.8.0-107.107\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc-dev-bin_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc-dev-bin\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"libc6-dev_2.39-0ubuntu8.7_amd64\",\n\t\t\t\"name\": \"libc6-dev\",\n\t\t\t\"sha256\": \"bbf5a155039042634961a61276650631ee47b9e721f91f8dbb731b0bbe046df3\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc6-dev_2.39-0ubuntu8.7_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"rpcsvc-proto_1.4.2-0ubuntu7_amd64\",\n\t\t\t\"name\": \"rpcsvc-proto\",\n\t\t\t\"sha256\": \"7eb710fe148d224c159ddec1ceb0ba53ead52a80a6793dcdae1474acf20d8f71\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/r/rpcsvc-proto/rpcsvc-proto_1.4.2-0ubuntu7_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1.4.2-0ubuntu7\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libcrypt-dev_1-4.4.36-4build1_amd64\",\n\t\t\t\"name\": \"libcrypt-dev\",\n\t\t\t\"sha256\": \"2edff420ef80b4a3f3751e65c33423ef30e563122a58b759e4854ea8d84ba1b1\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/libx/libxcrypt/libcrypt-dev_4.4.36-4build1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"linux-libc-dev_6.8.0-107.107_amd64\",\n\t\t\t\"name\": \"linux-libc-dev\",\n\t\t\t\"sha256\": \"fcc88782dc3857d661c7ae8b0435174cb41ce0bcda0d2c4c0fbfdadd9f03ff43\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/l/linux/linux-libc-dev_6.8.0-107.107_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"6.8.0-107.107\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libc-dev-bin_2.39-0ubuntu8.7_amd64\",\n\t\t\t\"name\": \"libc-dev-bin\",\n\t\t\t\"sha256\": \"83291a1d9b26262ac8f44a3bb188ce2cb796a0543134aae00e19db066c84dfdd\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/glibc/libc-dev-bin_2.39-0ubuntu8.7_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6-dev_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6-dev\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"rpcsvc-proto_1.4.2-0ubuntu7_amd64\",\n\t\t\t\t\t\"name\": \"rpcsvc-proto\",\n\t\t\t\t\t\"version\": \"1.4.2-0ubuntu7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcrypt-dev_1-4.4.36-4build1_amd64\",\n\t\t\t\t\t\"name\": \"libcrypt-dev\",\n\t\t\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libcrypt1_1-4.4.36-4build1_amd64\",\n\t\t\t\t\t\"name\": \"libcrypt1\",\n\t\t\t\t\t\"version\": \"1:4.4.36-4build1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"linux-libc-dev_6.8.0-107.107_amd64\",\n\t\t\t\t\t\"name\": \"linux-libc-dev\",\n\t\t\t\t\t\"version\": \"6.8.0-107.107\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc-dev-bin_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc-dev-bin\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libstdc-p--p-6_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libstdc++6\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-13-dev_13.3.0-6ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-13-dev\",\n\t\t\t\t\t\"version\": \"13.3.0-6ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libquadmath0_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libquadmath0\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libhwasan0_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libhwasan0\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libubsan1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libubsan1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libtsan2_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libtsan2\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"liblsan0_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"liblsan0\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libasan8_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libasan8\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libatomic1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libatomic1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libitm1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libitm1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgomp1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgomp1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-13-base_13.3.0-6ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-13-base\",\n\t\t\t\t\t\"version\": \"13.3.0-6ubuntu2~24.04.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"libstdc-p--p--13-dev_13.3.0-6ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libstdc++-13-dev\",\n\t\t\t\"sha256\": \"ee5633e863e19c3381ed97842ce35ed32ede96a3d1ae4e94c051d3036fe21347\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-13/libstdc++-13-dev_13.3.0-6ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"13.3.0-6ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libgcc-s1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"libgcc-s1\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"libc6_2.39-0ubuntu8.7_amd64\",\n\t\t\t\t\t\"name\": \"libc6\",\n\t\t\t\t\t\"version\": \"2.39-0ubuntu8.7\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"key\": \"gcc-14-base_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\t\t\"name\": \"gcc-14-base\",\n\t\t\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"key\": \"libstdc-p--p-6_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libstdc++6\",\n\t\t\t\"sha256\": \"a51f8de7829211db961a31f02158058ad1a95f92ac6d0a5dff6350e2821c54c0\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libstdc++6_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgcc-13-dev_13.3.0-6ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libgcc-13-dev\",\n\t\t\t\"sha256\": \"cd689db2691edaa10f37329307292796bb599e722e0505c79e14caaa1fe9a93a\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-13/libgcc-13-dev_13.3.0-6ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"13.3.0-6ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libquadmath0_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libquadmath0\",\n\t\t\t\"sha256\": \"dc8f0ca542e09d662f29370c8393c016440dd4bc5c996c5fcc19f632b63ce3b0\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libquadmath0_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libhwasan0_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libhwasan0\",\n\t\t\t\"sha256\": \"2195318cfe68fe16b601913ef7b33c9a900372f57861643fdc9ae6fef84534cd\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libhwasan0_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libubsan1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libubsan1\",\n\t\t\t\"sha256\": \"a16dea3abe2dcac99bcfae27e7e5672fde64573c3c170dcb0cd55631238f9814\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libubsan1_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libtsan2_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libtsan2\",\n\t\t\t\"sha256\": \"8cbcc9b3ae5ef23b449383d47a9035b27596e307d7dce7df9b83d47a7acd1d91\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libtsan2_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"liblsan0_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"liblsan0\",\n\t\t\t\"sha256\": \"dc0c2a1a053e833ba4d71e1ec2ba4244fe301761ad4a91d6725f927a52d86a14\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/liblsan0_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libasan8_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libasan8\",\n\t\t\t\"sha256\": \"8321aac6230fa1da320e76eb6288b7436164624aec449ed3933ea6c4cc86daac\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libasan8_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libitm1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libitm1\",\n\t\t\t\"sha256\": \"1fca498129dd3510294809d77ee754f72a9de281111200e9b7b9a5adf37faa9f\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libitm1_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"libgomp1_14.2.0-4ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"libgomp1\",\n\t\t\t\"sha256\": \"e8a95ec58125b4933597f30ff56c2ae10edf90f287262e366d4b6edea3019144\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-14/libgomp1_14.2.0-4ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"14.2.0-4ubuntu2~24.04.1\"\n\t\t},\n\t\t{\n\t\t\t\"arch\": \"amd64\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"key\": \"gcc-13-base_13.3.0-6ubuntu2_24.04.1_amd64\",\n\t\t\t\"name\": \"gcc-13-base\",\n\t\t\t\"sha256\": \"e859aca26585bb91113a451f1e66bc0e5283cb08797d679aacc1d936ae6dff8e\",\n\t\t\t\"urls\": [\n\t\t\t\t\"https://snapshot.ubuntu.com/ubuntu/20260401T104001Z/pool/main/g/gcc-13/gcc-13-base_13.3.0-6ubuntu2~24.04.1_amd64.deb\"\n\t\t\t],\n\t\t\t\"version\": \"13.3.0-6ubuntu2~24.04.1\"\n\t\t}\n\t],\n\t\"version\": 1\n}", + "package_template": "@@rules_distroless+//apt/private:package.BUILD.tmpl" + } + } + }, + "moduleExtensionMetadata": { + "explicitRootModuleDirectDeps": [], + "explicitRootModuleDirectDevDeps": [], + "useAllRepos": "NO", + "reproducible": false + }, + "recordedRepoMappingEntries": [ + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "rules_distroless+", + "bazel_features", + "bazel_features+" + ], + [ + "rules_distroless+", + "bazel_lib", + "bazel_lib+" + ], + [ + "rules_distroless+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_distroless+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_distroless+", + "yq_linux_amd64", + "yq.bzl++yq+yq_linux_amd64" + ] + ] + } + }, + "@@rules_foreign_cc+//foreign_cc:extensions.bzl%tools": { + "general": { + "bzlTransitiveDigest": "otSpeDAxv5Qioli4+V+p43+gUyiUU17e2kHR+4wQFAs=", + "usagesDigest": "9LXdVp01HkdYQT8gYPjYLO6VLVJHo9uFfxWaU1ymiRE=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_foreign_cc_framework_toolchain_linux": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:linux" + ] + } + }, + "rules_foreign_cc_framework_toolchain_freebsd": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:freebsd" + ] + } + }, + "rules_foreign_cc_framework_toolchain_windows": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:windows" + ] + } + }, + "rules_foreign_cc_framework_toolchain_macos": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:macos" + ] + } + }, + "rules_foreign_cc_framework_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository_hub", + "attributes": {} + }, + "cmake_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa", + "strip_prefix": "cmake-3.23.2", + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz" + ] + } + }, + "gnumake_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "581f4d4e872da74b3941c874215898a7d35802f03732bdccee1d4a7979105d18", + "strip_prefix": "make-4.4", + "urls": [ + "https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.4.tar.gz", + "http://ftpmirror.gnu.org/gnu/make/make-4.4.tar.gz" + ] + } + }, + "ninja_build_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "31747ae633213f1eda3842686f83c2aa1412e0f5691d1c14dbbcc67fe7400cea", + "strip_prefix": "ninja-1.11.1", + "urls": [ + "https://github.com/ninja-build/ninja/archive/v1.11.1.tar.gz" + ] + } + }, + "meson_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "exports_files([\"meson.py\"])\n\nfilegroup(\n name = \"runtime\",\n srcs = glob([\"mesonbuild/**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "strip_prefix": "meson-1.1.1", + "url": "https://github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz" + } + }, + "glib_dev": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_import(\n name = \"glib_dev\",\n hdrs = glob([\"include/**\"]),\n shared_library = \"@glib_runtime//:bin/libglib-2.0-0.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "bdf18506df304d38be98a4b3f18055b8b8cca81beabecad0eece6ce95319c369", + "urls": [ + "https://download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip" + ] + } + }, + "glib_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\ncc_import(\n name = \"msvc_hdr\",\n hdrs = [\"msvc_recommended_pragmas.h\"],\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "bc96f63112823b7d6c9f06572d2ad626ddac7eb452c04d762592197f6e07898e", + "strip_prefix": "glib-2.26.1", + "urls": [ + "https://download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz" + ] + } + }, + "glib_runtime": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nexports_files(\n [\n \"bin/libgio-2.0-0.dll\",\n \"bin/libglib-2.0-0.dll\",\n \"bin/libgmodule-2.0-0.dll\",\n \"bin/libgobject-2.0-0.dll\",\n \"bin/libgthread-2.0-0.dll\",\n ],\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "88d857087e86f16a9be651ee7021880b3f7ba050d34a1ed9f06113b8799cb973", + "urls": [ + "https://download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip" + ] + } + }, + "gettext_runtime": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\ncc_import(\n name = \"gettext_runtime\",\n shared_library = \"bin/libintl-8.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "1f4269c0e021076d60a54e98da6f978a3195013f6de21674ba0edbc339c5b079", + "urls": [ + "https://download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip" + ] + } + }, + "pkgconfig_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591", + "strip_prefix": "pkg-config-0.29.2", + "patches": [ + "@@rules_foreign_cc+//toolchains:pkgconfig-detectenv.patch", + "@@rules_foreign_cc+//toolchains:pkgconfig-makefile-vc.patch" + ], + "urls": [ + "https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz" + ] + } + }, + "bazel_skylib": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz" + ], + "sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728" + } + }, + "rules_python": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841", + "strip_prefix": "rules_python-0.23.1", + "url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.23.1.tar.gz" + } + }, + "cmake-3.23.2-linux-aarch64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz" + ], + "sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e", + "strip_prefix": "cmake-3.23.2-linux-aarch64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-linux-x86_64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz" + ], + "sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708", + "strip_prefix": "cmake-3.23.2-linux-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-macos-universal": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz" + ], + "sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88", + "strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-windows-i386": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip" + ], + "sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07", + "strip_prefix": "cmake-3.23.2-windows-i386", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-windows-x86_64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip" + ], + "sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0", + "strip_prefix": "cmake-3.23.2-windows-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake_3.23.2_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//toolchains:prebuilt_toolchains_repository.bzl%prebuilt_toolchains_repository", + "attributes": { + "repos": { + "cmake-3.23.2-linux-aarch64": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ], + "cmake-3.23.2-linux-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "cmake-3.23.2-macos-universal": [ + "@platforms//os:macos" + ], + "cmake-3.23.2-windows-i386": [ + "@platforms//cpu:x86_32", + "@platforms//os:windows" + ], + "cmake-3.23.2-windows-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "cmake" + } + }, + "ninja_1.11.1_linux": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-linux.zip" + ], + "sha256": "b901ba96e486dce377f9a070ed4ef3f79deb45f4ffe2938f8e7ddc69cfb3df77", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.11.1_mac": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-mac.zip" + ], + "sha256": "482ecb23c59ae3d4f158029112de172dd96bb0e97549c4b1ca32d8fad11f873e", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.11.1_win": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-win.zip" + ], + "sha256": "524b344a1a9a55005eaf868d991e090ab8ce07fa109f1820d40e74642e289abc", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.11.1_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//toolchains:prebuilt_toolchains_repository.bzl%prebuilt_toolchains_repository", + "attributes": { + "repos": { + "ninja_1.11.1_linux": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "ninja_1.11.1_mac": [ + "@platforms//cpu:x86_64", + "@platforms//os:macos" + ], + "ninja_1.11.1_win": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "ninja" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_foreign_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_foreign_cc+", + "rules_foreign_cc", + "rules_foreign_cc+" + ] + ] + } + }, + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "general": { + "bzlTransitiveDigest": "ipDzEAtocAfIXfJbcbskFj4cGI7OMiupxpyQcliozJ4=", + "usagesDigest": "qTwqmKKUfWcPdvM0waG+CPWrxsbeAWVeUxavm7tEk9E=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_jetbrains_kotlin_git": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", + "attributes": { + "urls": [ + "https://github.com/JetBrains/kotlin/releases/download/v2.1.0/kotlin-compiler-2.1.0.zip" + ], + "sha256": "b6698d5728ad8f9edcdd01617d638073191d8a03139cc538a391b4e3759ad297" + } + }, + "com_github_jetbrains_kotlin": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", + "attributes": { + "git_repository_name": "com_github_jetbrains_kotlin_git", + "compiler_version": "2.1.0" + } + }, + "com_github_google_ksp": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", + "attributes": { + "urls": [ + "https://github.com/google/ksp/releases/download/2.1.0-1.0.28/artifacts.zip" + ], + "sha256": "fc27b08cadc061a4a989af01cbeccb613feef1995f4aad68f2be0f886a3ee251", + "strip_version": "2.1.0-1.0.28" + } + }, + "com_github_pinterest_ktlint": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "sha256": "a9f923be58fbd32670a17f0b729b1df804af882fa57402165741cb26e5440ca1", + "urls": [ + "https://github.com/pinterest/ktlint/releases/download/1.3.1/ktlint" + ], + "executable": true + } + }, + "kotlinx_serialization_core_jvm": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "29c821a8d4e25cbfe4f2ce96cdd4526f61f8f4e69a135f9612a34a81d93b65f1", + "urls": [ + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.6.3/kotlinx-serialization-core-jvm-1.6.3.jar" + ] + } + }, + "kotlinx_serialization_json": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "8c0016890a79ab5980dd520a5ab1a6738023c29aa3b6437c482e0e5fdc06dab1", + "urls": [ + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json/1.6.3/kotlinx-serialization-json-1.6.3.jar" + ] + } + }, + "kotlinx_serialization_json_jvm": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "d3234179bcff1886d53d67c11eca47f7f3cf7b63c349d16965f6db51b7f3dd9a", + "urls": [ + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.6.3/kotlinx-serialization-json-jvm-1.6.3.jar" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_kotlin+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_nodejs+//nodejs:extensions.bzl%node": { + "general": { + "bzlTransitiveDigest": "4pUxCNc22K4I+6+4Nxu52Hur12tFRfa1JMsN5mdDv60=", + "usagesDigest": "GS0hrS+1xGO6wWmKTqytkZ6FWwnLFKCdsYVhZKvKNFA=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "nodejs_linux_amd64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "linux_amd64" + } + }, + "nodejs_linux_arm64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "linux_arm64" + } + }, + "nodejs_linux_s390x": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "linux_s390x" + } + }, + "nodejs_linux_ppc64le": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "linux_ppc64le" + } + }, + "nodejs_darwin_amd64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "darwin_amd64" + } + }, + "nodejs_darwin_arm64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "darwin_arm64" + } + }, + "nodejs_windows_amd64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "windows_amd64" + } + }, + "nodejs_windows_arm64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", + "attributes": { + "node_download_auth": {}, + "node_repositories": {}, + "node_urls": [ + "https://nodejs.org/dist/v{version}/{filename}" + ], + "node_version": "22.22.0", + "include_headers": false, + "platform": "windows_arm64" + } + }, + "nodejs": { + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", + "attributes": { + "user_node_repository_name": "nodejs" + } + }, + "nodejs_host": { + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", + "attributes": { + "user_node_repository_name": "nodejs" + } + }, + "nodejs_toolchains": { + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo", + "attributes": { + "user_node_repository_name": "nodejs" + } + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@rules_python+//python/extensions:config.bzl%config": { + "general": { + "bzlTransitiveDigest": "EcMcbtKZvYmd5Mi1Fpg4EeBBztLHEE5tjO5tLDBYDuU=", + "usagesDigest": "lDbpRfhoWmZCHSaNxwZv/8fF2y0wu2th0G0f/uqX7VM=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_python_internal": { + "repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo", + "attributes": { + "transition_setting_generators": {}, + "transition_settings": [] + } + }, + "pypi__build": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl", + "sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__click": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", + "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__colorama": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", + "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__importlib_metadata": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", + "sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__installer": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl", + "sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__more_itertools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl", + "sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__packaging": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", + "sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pep517": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl", + "sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl", + "sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip_tools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl", + "sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pyproject_hooks": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl", + "sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__setuptools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl", + "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__tomli": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", + "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__wheel": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl", + "sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__zipp": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl", + "sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_python+", + "pypi__build", + "rules_python++config+pypi__build" + ], + [ + "rules_python+", + "pypi__click", + "rules_python++config+pypi__click" + ], + [ + "rules_python+", + "pypi__colorama", + "rules_python++config+pypi__colorama" + ], + [ + "rules_python+", + "pypi__importlib_metadata", + "rules_python++config+pypi__importlib_metadata" + ], + [ + "rules_python+", + "pypi__installer", + "rules_python++config+pypi__installer" + ], + [ + "rules_python+", + "pypi__more_itertools", + "rules_python++config+pypi__more_itertools" + ], + [ + "rules_python+", + "pypi__packaging", + "rules_python++config+pypi__packaging" + ], + [ + "rules_python+", + "pypi__pep517", + "rules_python++config+pypi__pep517" + ], + [ + "rules_python+", + "pypi__pip", + "rules_python++config+pypi__pip" + ], + [ + "rules_python+", + "pypi__pip_tools", + "rules_python++config+pypi__pip_tools" + ], + [ + "rules_python+", + "pypi__pyproject_hooks", + "rules_python++config+pypi__pyproject_hooks" + ], + [ + "rules_python+", + "pypi__setuptools", + "rules_python++config+pypi__setuptools" + ], + [ + "rules_python+", + "pypi__tomli", + "rules_python++config+pypi__tomli" + ], + [ + "rules_python+", + "pypi__wheel", + "rules_python++config+pypi__wheel" + ], + [ + "rules_python+", + "pypi__zipp", + "rules_python++config+pypi__zipp" + ] + ] + } + }, + "@@rules_python+//python/uv:uv.bzl%uv": { + "general": { + "bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=", + "usagesDigest": "r/qVanIsK8SWxQWkJBvpWw5XePYfj9hIHIKL9FTzAdk=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "uv": { + "repoRuleId": "@@rules_python+//python/uv/private:uv_toolchains_repo.bzl%uv_toolchains_repo", + "attributes": { + "toolchain_type": "'@@rules_python+//python/uv:uv_toolchain_type'", + "toolchain_names": [ + "none" + ], + "toolchain_implementations": { + "none": "'@@rules_python+//python:none'" + }, + "toolchain_compatible_with": { + "none": [ + "@platforms//:incompatible" + ] + }, + "toolchain_target_settings": {} + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_python+", + "platforms", + "platforms" + ] + ] + } + }, + "@@rules_rust+//crate_universe:extensions.bzl%crate": { + "general": { + "bzlTransitiveDigest": "wmQpSLxkLUUlomh9A7MYN+AoKG2QJIYABZudGJwn0K4=", + "usagesDigest": "wKDir5lt64+Fed9OkYwXdJMDafXvpjxTmVvguHrnF8g=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": { + "CARGO_BAZEL_DEBUG": null, + "CARGO_BAZEL_GENERATOR_SHA256": null, + "CARGO_BAZEL_GENERATOR_URL": null, + "CARGO_BAZEL_ISOLATED": null, + "CARGO_BAZEL_REPIN": null, + "CARGO_BAZEL_REPIN_ONLY": null, + "CARGO_BAZEL_TIMEOUT": null, + "REPIN": null + }, + "generatedRepoSpecs": { + "crates": { + "repoRuleId": "@@rules_rust+//crate_universe:extensions.bzl%_generate_repo", + "attributes": { + "contents": { + "BUILD.bazel": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files(\n [\n \"cargo-bazel.json\",\n \"crates.bzl\",\n \"defs.bzl\",\n ] + glob(\n allow_empty = True,\n include = [\"*.bazel\"],\n ),\n)\n\nfilegroup(\n name = \"srcs\",\n srcs = glob(\n allow_empty = True,\n include = [\n \"*.bazel\",\n \"*.bzl\",\n ],\n ),\n)\n\n# Workspace Member Dependencies\nalias(\n name = \"assert-json-diff-2.0.2\",\n actual = \"@crates__assert-json-diff-2.0.2//:assert_json_diff\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"assert-json-diff\",\n actual = \"@crates__assert-json-diff-2.0.2//:assert_json_diff\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"clang-1.0.3\",\n actual = \"@crates__clang-1.0.3//:clang\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"clang\",\n actual = \"@crates__clang-1.0.3//:clang\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"clang-sys-1.8.1\",\n actual = \"@crates__clang-sys-1.8.1//:clang_sys\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"clang-sys\",\n actual = \"@crates__clang-sys-1.8.1//:clang_sys\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"clap-4.6.6\",\n actual = \"@crates__clap-4.6.6//:clap\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"clap\",\n actual = \"@crates__clap-4.6.6//:clap\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"env_logger-0.10.2\",\n actual = \"@crates__env_logger-0.10.2//:env_logger\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"env_logger\",\n actual = \"@crates__env_logger-0.10.2//:env_logger\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"flatbuffers-25.12.19\",\n actual = \"@crates__flatbuffers-25.12.19//:flatbuffers\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"flatbuffers\",\n actual = \"@crates__flatbuffers-25.12.19//:flatbuffers\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"libloading-0.9.0\",\n actual = \"@crates__libloading-0.9.0//:libloading\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"libloading\",\n actual = \"@crates__libloading-0.9.0//:libloading\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"log-0.4.34\",\n actual = \"@crates__log-0.4.34//:log\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"log\",\n actual = \"@crates__log-0.4.34//:log\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"once_cell-1.21.4\",\n actual = \"@crates__once_cell-1.21.4//:once_cell\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"once_cell\",\n actual = \"@crates__once_cell-1.21.4//:once_cell\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"pest-2.9.1\",\n actual = \"@crates__pest-2.9.1//:pest\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"pest\",\n actual = \"@crates__pest-2.9.1//:pest\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"pest_derive-2.9.1\",\n actual = \"@crates__pest_derive-2.9.1//:pest_derive\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"pest_derive\",\n actual = \"@crates__pest_derive-2.9.1//:pest_derive\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"serde-1.0.229\",\n actual = \"@crates__serde-1.0.229//:serde\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"serde\",\n actual = \"@crates__serde-1.0.229//:serde\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"serde_json-1.0.151\",\n actual = \"@crates__serde_json-1.0.151//:serde_json\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"serde_json\",\n actual = \"@crates__serde_json-1.0.151//:serde_json\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"serde_yaml-0.9.34+deprecated\",\n actual = \"@crates__serde_yaml-0.9.34-deprecated//:serde_yaml\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"serde_yaml\",\n actual = \"@crates__serde_yaml-0.9.34-deprecated//:serde_yaml\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"strsim-0.11.1\",\n actual = \"@crates__strsim-0.11.1//:strsim\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"strsim\",\n actual = \"@crates__strsim-0.11.1//:strsim\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"thiserror-1.0.69\",\n actual = \"@crates__thiserror-1.0.69//:thiserror\",\n tags = [\"manual\"],\n)\n\nalias(\n name = \"thiserror\",\n actual = \"@crates__thiserror-1.0.69//:thiserror\",\n tags = [\"manual\"],\n)\n", + "alias_rules.bzl": "\"\"\"Alias that transitions its target to `compilation_mode=opt`. Use `transition_alias=\"opt\"` to enable.\"\"\"\n\nload(\"@rules_cc//cc:defs.bzl\", \"CcInfo\")\nload(\"@rules_rust//rust:rust_common.bzl\", \"COMMON_PROVIDERS\")\n\ndef _transition_alias_impl(ctx):\n # `ctx.attr.actual` is a list of 1 item due to the transition\n providers = [ctx.attr.actual[0][provider] for provider in COMMON_PROVIDERS]\n if CcInfo in ctx.attr.actual[0]:\n providers.append(ctx.attr.actual[0][CcInfo])\n return providers\n\ndef _change_compilation_mode(compilation_mode):\n def _change_compilation_mode_impl(_settings, _attr):\n return {\n \"//command_line_option:compilation_mode\": compilation_mode,\n }\n\n return transition(\n implementation = _change_compilation_mode_impl,\n inputs = [],\n outputs = [\n \"//command_line_option:compilation_mode\",\n ],\n )\n\ndef _transition_alias_rule(compilation_mode):\n return rule(\n implementation = _transition_alias_impl,\n provides = COMMON_PROVIDERS,\n attrs = {\n \"actual\": attr.label(\n mandatory = True,\n doc = \"`rust_library()` target to transition to `compilation_mode=opt`.\",\n providers = COMMON_PROVIDERS,\n cfg = _change_compilation_mode(compilation_mode),\n ),\n \"_allowlist_function_transition\": attr.label(\n default = \"@bazel_tools//tools/allowlists/function_transition_allowlist\",\n ),\n },\n doc = \"Transitions a Rust library crate to the `compilation_mode=opt`.\",\n )\n\ntransition_alias_dbg = _transition_alias_rule(\"dbg\")\ntransition_alias_fastbuild = _transition_alias_rule(\"fastbuild\")\ntransition_alias_opt = _transition_alias_rule(\"opt\")\n", + "defs.bzl": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\"\"\"\n# `crates_repository` API\n\n- [aliases](#aliases)\n- [crate_deps](#crate_deps)\n- [all_crate_deps](#all_crate_deps)\n- [crate_repositories](#crate_repositories)\n\n\"\"\"\n\nload(\"@bazel_tools//tools/build_defs/repo:git.bzl\", \"new_git_repository\")\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\nload(\"@bazel_tools//tools/build_defs/repo:utils.bzl\", \"maybe\")\nload(\"@bazel_skylib//lib:selects.bzl\", \"selects\")\nload(\"@rules_rust//crate_universe/private:local_crate_mirror.bzl\", \"local_crate_mirror\")\n\n###############################################################################\n# MACROS API\n###############################################################################\n\n# An identifier that represent common dependencies (unconditional).\n_COMMON_CONDITION = \"\"\n\ndef _flatten_dependency_maps(all_dependency_maps):\n \"\"\"Flatten a list of dependency maps into one dictionary.\n\n Dependency maps have the following structure:\n\n ```python\n DEPENDENCIES_MAP = {\n # The first key in the map is a Bazel package\n # name of the workspace this file is defined in.\n \"workspace_member_package\": {\n\n # Not all dependencies are supported for all platforms.\n # the condition key is the condition required to be true\n # on the host platform.\n \"condition\": {\n\n # An alias to a crate target. # The label of the crate target the\n # Aliases are only crate names. # package name refers to.\n \"package_name\": \"@full//:label\",\n }\n }\n }\n ```\n\n Args:\n all_dependency_maps (list): A list of dicts as described above\n\n Returns:\n dict: A dictionary as described above\n \"\"\"\n dependencies = {}\n\n for workspace_deps_map in all_dependency_maps:\n for pkg_name, conditional_deps_map in workspace_deps_map.items():\n if pkg_name not in dependencies:\n non_frozen_map = dict()\n for key, values in conditional_deps_map.items():\n non_frozen_map.update({key: dict(values.items())})\n dependencies.setdefault(pkg_name, non_frozen_map)\n continue\n\n for condition, deps_map in conditional_deps_map.items():\n # If the condition has not been recorded, do so and continue\n if condition not in dependencies[pkg_name]:\n dependencies[pkg_name].setdefault(condition, dict(deps_map.items()))\n continue\n\n # Alert on any miss-matched dependencies\n inconsistent_entries = []\n for crate_name, crate_label in deps_map.items():\n existing = dependencies[pkg_name][condition].get(crate_name)\n if existing and existing != crate_label:\n inconsistent_entries.append((crate_name, existing, crate_label))\n dependencies[pkg_name][condition].update({crate_name: crate_label})\n\n return dependencies\n\ndef crate_deps(deps, package_name = None):\n \"\"\"Finds the fully qualified label of the requested crates for the package where this macro is called.\n\n Args:\n deps (list): The desired list of crate targets.\n package_name (str, optional): The package name of the set of dependencies to look up.\n Defaults to `native.package_name()`.\n\n Returns:\n list: A list of labels to generated rust targets (str)\n \"\"\"\n\n if not deps:\n return []\n\n if package_name == None:\n package_name = native.package_name()\n\n # Join both sets of dependencies\n dependencies = _flatten_dependency_maps([\n _NORMAL_DEPENDENCIES,\n _NORMAL_DEV_DEPENDENCIES,\n _PROC_MACRO_DEPENDENCIES,\n _PROC_MACRO_DEV_DEPENDENCIES,\n _BUILD_DEPENDENCIES,\n _BUILD_PROC_MACRO_DEPENDENCIES,\n ]).pop(package_name, {})\n\n # Combine all conditional packages so we can easily index over a flat list\n # TODO: Perhaps this should actually return select statements and maintain\n # the conditionals of the dependencies\n flat_deps = {}\n for deps_set in dependencies.values():\n for crate_name, crate_label in deps_set.items():\n flat_deps.update({crate_name: crate_label})\n\n missing_crates = []\n crate_targets = []\n for crate_target in deps:\n if crate_target not in flat_deps:\n missing_crates.append(crate_target)\n else:\n crate_targets.append(flat_deps[crate_target])\n\n if missing_crates:\n fail(\"Could not find crates `{}` among dependencies of `{}`. Available dependencies were `{}`\".format(\n missing_crates,\n package_name,\n dependencies,\n ))\n\n return crate_targets\n\ndef all_crate_deps(\n normal = False, \n normal_dev = False, \n proc_macro = False, \n proc_macro_dev = False,\n build = False,\n build_proc_macro = False,\n package_name = None):\n \"\"\"Finds the fully qualified label of all requested direct crate dependencies \\\n for the package where this macro is called.\n\n If no parameters are set, all normal dependencies are returned. Setting any one flag will\n otherwise impact the contents of the returned list.\n\n Args:\n normal (bool, optional): If True, normal dependencies are included in the\n output list.\n normal_dev (bool, optional): If True, normal dev dependencies will be\n included in the output list.\n proc_macro (bool, optional): If True, proc_macro dependencies are included\n in the output list.\n proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are\n included in the output list.\n build (bool, optional): If True, build dependencies are included\n in the output list.\n build_proc_macro (bool, optional): If True, build proc_macro dependencies are\n included in the output list.\n package_name (str, optional): The package name of the set of dependencies to look up.\n Defaults to `native.package_name()` when unset.\n\n Returns:\n list: A list of labels to generated rust targets (str)\n \"\"\"\n\n if package_name == None:\n package_name = native.package_name()\n\n # Determine the relevant maps to use\n all_dependency_maps = []\n if normal:\n all_dependency_maps.append(_NORMAL_DEPENDENCIES)\n if normal_dev:\n all_dependency_maps.append(_NORMAL_DEV_DEPENDENCIES)\n if proc_macro:\n all_dependency_maps.append(_PROC_MACRO_DEPENDENCIES)\n if proc_macro_dev:\n all_dependency_maps.append(_PROC_MACRO_DEV_DEPENDENCIES)\n if build:\n all_dependency_maps.append(_BUILD_DEPENDENCIES)\n if build_proc_macro:\n all_dependency_maps.append(_BUILD_PROC_MACRO_DEPENDENCIES)\n\n # Default to always using normal dependencies\n if not all_dependency_maps:\n all_dependency_maps.append(_NORMAL_DEPENDENCIES)\n\n dependencies = _flatten_dependency_maps(all_dependency_maps).pop(package_name, None)\n\n if not dependencies:\n if dependencies == None:\n fail(\"Tried to get all_crate_deps for package \" + package_name + \" but that package had no Cargo.toml file\")\n else:\n return []\n\n crate_deps = list(dependencies.pop(_COMMON_CONDITION, {}).values())\n for condition, deps in dependencies.items():\n crate_deps += selects.with_or({\n tuple(_CONDITIONS[condition]): deps.values(),\n \"//conditions:default\": [],\n })\n\n return crate_deps\n\ndef aliases(\n normal = False,\n normal_dev = False,\n proc_macro = False,\n proc_macro_dev = False,\n build = False,\n build_proc_macro = False,\n package_name = None):\n \"\"\"Produces a map of Crate alias names to their original label\n\n If no dependency kinds are specified, `normal` and `proc_macro` are used by default.\n Setting any one flag will otherwise determine the contents of the returned dict.\n\n Args:\n normal (bool, optional): If True, normal dependencies are included in the\n output list.\n normal_dev (bool, optional): If True, normal dev dependencies will be\n included in the output list..\n proc_macro (bool, optional): If True, proc_macro dependencies are included\n in the output list.\n proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are\n included in the output list.\n build (bool, optional): If True, build dependencies are included\n in the output list.\n build_proc_macro (bool, optional): If True, build proc_macro dependencies are\n included in the output list.\n package_name (str, optional): The package name of the set of dependencies to look up.\n Defaults to `native.package_name()` when unset.\n\n Returns:\n dict: The aliases of all associated packages\n \"\"\"\n if package_name == None:\n package_name = native.package_name()\n\n # Determine the relevant maps to use\n all_aliases_maps = []\n if normal:\n all_aliases_maps.append(_NORMAL_ALIASES)\n if normal_dev:\n all_aliases_maps.append(_NORMAL_DEV_ALIASES)\n if proc_macro:\n all_aliases_maps.append(_PROC_MACRO_ALIASES)\n if proc_macro_dev:\n all_aliases_maps.append(_PROC_MACRO_DEV_ALIASES)\n if build:\n all_aliases_maps.append(_BUILD_ALIASES)\n if build_proc_macro:\n all_aliases_maps.append(_BUILD_PROC_MACRO_ALIASES)\n\n # Default to always using normal aliases\n if not all_aliases_maps:\n all_aliases_maps.append(_NORMAL_ALIASES)\n all_aliases_maps.append(_PROC_MACRO_ALIASES)\n\n aliases = _flatten_dependency_maps(all_aliases_maps).pop(package_name, None)\n\n if not aliases:\n return dict()\n\n common_items = aliases.pop(_COMMON_CONDITION, {}).items()\n\n # If there are only common items in the dictionary, immediately return them\n if not len(aliases.keys()) == 1:\n return dict(common_items)\n\n # Build a single select statement where each conditional has accounted for the\n # common set of aliases.\n crate_aliases = {\"//conditions:default\": dict(common_items)}\n for condition, deps in aliases.items():\n condition_triples = _CONDITIONS[condition]\n for triple in condition_triples:\n if triple in crate_aliases:\n crate_aliases[triple].update(deps)\n else:\n crate_aliases.update({triple: dict(deps.items() + common_items)})\n\n return select(crate_aliases)\n\n###############################################################################\n# WORKSPACE MEMBER DEPS AND ALIASES\n###############################################################################\n\n_NORMAL_DEPENDENCIES = {\n \"\": {\n _COMMON_CONDITION: {\n \"assert-json-diff\": Label(\"@crates//:assert-json-diff-2.0.2\"),\n \"clang\": Label(\"@crates//:clang-1.0.3\"),\n \"clang-sys\": Label(\"@crates//:clang-sys-1.8.1\"),\n \"clap\": Label(\"@crates//:clap-4.6.6\"),\n \"env_logger\": Label(\"@crates//:env_logger-0.10.2\"),\n \"flatbuffers\": Label(\"@crates//:flatbuffers-25.12.19\"),\n \"libloading\": Label(\"@crates//:libloading-0.9.0\"),\n \"log\": Label(\"@crates//:log-0.4.34\"),\n \"once_cell\": Label(\"@crates//:once_cell-1.21.4\"),\n \"pest\": Label(\"@crates//:pest-2.9.1\"),\n \"serde\": Label(\"@crates//:serde-1.0.229\"),\n \"serde_json\": Label(\"@crates//:serde_json-1.0.151\"),\n \"serde_yaml\": Label(\"@crates//:serde_yaml-0.9.34+deprecated\"),\n \"strsim\": Label(\"@crates//:strsim-0.11.1\"),\n \"thiserror\": Label(\"@crates//:thiserror-1.0.69\"),\n },\n },\n}\n\n\n_NORMAL_ALIASES = {\n \"\": {\n _COMMON_CONDITION: {\n },\n },\n}\n\n\n_NORMAL_DEV_DEPENDENCIES = {\n \"\": {\n },\n}\n\n\n_NORMAL_DEV_ALIASES = {\n \"\": {\n },\n}\n\n\n_PROC_MACRO_DEPENDENCIES = {\n \"\": {\n _COMMON_CONDITION: {\n \"pest_derive\": Label(\"@crates//:pest_derive-2.9.1\"),\n },\n },\n}\n\n\n_PROC_MACRO_ALIASES = {\n \"\": {\n },\n}\n\n\n_PROC_MACRO_DEV_DEPENDENCIES = {\n \"\": {\n },\n}\n\n\n_PROC_MACRO_DEV_ALIASES = {\n \"\": {\n },\n}\n\n\n_BUILD_DEPENDENCIES = {\n \"\": {\n },\n}\n\n\n_BUILD_ALIASES = {\n \"\": {\n },\n}\n\n\n_BUILD_PROC_MACRO_DEPENDENCIES = {\n \"\": {\n },\n}\n\n\n_BUILD_PROC_MACRO_ALIASES = {\n \"\": {\n },\n}\n\n\n_CONDITIONS = {\n \"aarch64-apple-darwin\": [\"@rules_rust//rust/platform:aarch64-apple-darwin\"],\n \"aarch64-unknown-linux-gnu\": [\"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\"],\n \"cfg(any())\": [],\n \"cfg(any(unix, target_os = \\\"wasi\\\"))\": [\"@rules_rust//rust/platform:aarch64-apple-darwin\",\"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\",\"@rules_rust//rust/platform:wasm32-wasip1\",\"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\",\"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\"],\n \"cfg(target_os = \\\"hermit\\\")\": [],\n \"cfg(unix)\": [\"@rules_rust//rust/platform:aarch64-apple-darwin\",\"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\",\"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\",\"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\"],\n \"cfg(windows)\": [\"@rules_rust//rust/platform:x86_64-pc-windows-msvc\"],\n \"wasm32-unknown-unknown\": [\"@rules_rust//rust/platform:wasm32-unknown-unknown\"],\n \"wasm32-wasip1\": [\"@rules_rust//rust/platform:wasm32-wasip1\"],\n \"x86_64-pc-windows-msvc\": [\"@rules_rust//rust/platform:x86_64-pc-windows-msvc\"],\n \"x86_64-unknown-linux-gnu\": [\"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\"],\n \"x86_64-unknown-nixos-gnu\": [\"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\"],\n}\n\n###############################################################################\n\ndef crate_repositories():\n \"\"\"A macro for defining repositories for all generated crates.\n\n Returns:\n A list of repos visible to the module through the module extension.\n \"\"\"\n maybe(\n http_archive,\n name = \"crates__aho-corasick-1.1.5\",\n sha256 = \"c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/aho-corasick/1.1.5/download\"],\n strip_prefix = \"aho-corasick-1.1.5\",\n build_file = Label(\"@crates//crates:BUILD.aho-corasick-1.1.5.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__anstream-1.0.0\",\n sha256 = \"824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/anstream/1.0.0/download\"],\n strip_prefix = \"anstream-1.0.0\",\n build_file = Label(\"@crates//crates:BUILD.anstream-1.0.0.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__anstyle-1.0.14\",\n sha256 = \"940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/anstyle/1.0.14/download\"],\n strip_prefix = \"anstyle-1.0.14\",\n build_file = Label(\"@crates//crates:BUILD.anstyle-1.0.14.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__anstyle-parse-1.0.0\",\n sha256 = \"52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/anstyle-parse/1.0.0/download\"],\n strip_prefix = \"anstyle-parse-1.0.0\",\n build_file = Label(\"@crates//crates:BUILD.anstyle-parse-1.0.0.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__anstyle-query-1.1.5\",\n sha256 = \"40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/anstyle-query/1.1.5/download\"],\n strip_prefix = \"anstyle-query-1.1.5\",\n build_file = Label(\"@crates//crates:BUILD.anstyle-query-1.1.5.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__anstyle-wincon-3.0.11\",\n sha256 = \"291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/anstyle-wincon/3.0.11/download\"],\n strip_prefix = \"anstyle-wincon-3.0.11\",\n build_file = Label(\"@crates//crates:BUILD.anstyle-wincon-3.0.11.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__assert-json-diff-2.0.2\",\n sha256 = \"47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/assert-json-diff/2.0.2/download\"],\n strip_prefix = \"assert-json-diff-2.0.2\",\n build_file = Label(\"@crates//crates:BUILD.assert-json-diff-2.0.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__bitflags-2.13.1\",\n sha256 = \"b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/bitflags/2.13.1/download\"],\n strip_prefix = \"bitflags-2.13.1\",\n build_file = Label(\"@crates//crates:BUILD.bitflags-2.13.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__cfg-if-1.0.4\",\n sha256 = \"9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/cfg-if/1.0.4/download\"],\n strip_prefix = \"cfg-if-1.0.4\",\n build_file = Label(\"@crates//crates:BUILD.cfg-if-1.0.4.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__clang-1.0.3\",\n sha256 = \"34c6913be3a1c94f52fb975cdec7ef5a7b69de10a55de66dcbc30d7046b85fa1\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/clang/1.0.3/download\"],\n strip_prefix = \"clang-1.0.3\",\n build_file = Label(\"@crates//crates:BUILD.clang-1.0.3.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__clang-sys-1.8.1\",\n sha256 = \"0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/clang-sys/1.8.1/download\"],\n strip_prefix = \"clang-sys-1.8.1\",\n build_file = Label(\"@crates//crates:BUILD.clang-sys-1.8.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__clap-4.6.6\",\n sha256 = \"473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/clap/4.6.6/download\"],\n strip_prefix = \"clap-4.6.6\",\n build_file = Label(\"@crates//crates:BUILD.clap-4.6.6.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__clap_builder-4.6.6\",\n sha256 = \"7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/clap_builder/4.6.6/download\"],\n strip_prefix = \"clap_builder-4.6.6\",\n build_file = Label(\"@crates//crates:BUILD.clap_builder-4.6.6.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__clap_derive-4.6.4\",\n sha256 = \"d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/clap_derive/4.6.4/download\"],\n strip_prefix = \"clap_derive-4.6.4\",\n build_file = Label(\"@crates//crates:BUILD.clap_derive-4.6.4.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__clap_lex-1.1.0\",\n sha256 = \"c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/clap_lex/1.1.0/download\"],\n strip_prefix = \"clap_lex-1.1.0\",\n build_file = Label(\"@crates//crates:BUILD.clap_lex-1.1.0.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__colorchoice-1.0.5\",\n sha256 = \"1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/colorchoice/1.0.5/download\"],\n strip_prefix = \"colorchoice-1.0.5\",\n build_file = Label(\"@crates//crates:BUILD.colorchoice-1.0.5.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__env_logger-0.10.2\",\n sha256 = \"4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/env_logger/0.10.2/download\"],\n strip_prefix = \"env_logger-0.10.2\",\n build_file = Label(\"@crates//crates:BUILD.env_logger-0.10.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__equivalent-1.0.2\",\n sha256 = \"877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/equivalent/1.0.2/download\"],\n strip_prefix = \"equivalent-1.0.2\",\n build_file = Label(\"@crates//crates:BUILD.equivalent-1.0.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__flatbuffers-25.12.19\",\n sha256 = \"35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/flatbuffers/25.12.19/download\"],\n strip_prefix = \"flatbuffers-25.12.19\",\n build_file = Label(\"@crates//crates:BUILD.flatbuffers-25.12.19.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__glob-0.3.4\",\n sha256 = \"e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/glob/0.3.4/download\"],\n strip_prefix = \"glob-0.3.4\",\n build_file = Label(\"@crates//crates:BUILD.glob-0.3.4.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__hashbrown-0.17.1\",\n sha256 = \"ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/hashbrown/0.17.1/download\"],\n strip_prefix = \"hashbrown-0.17.1\",\n build_file = Label(\"@crates//crates:BUILD.hashbrown-0.17.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__heck-0.5.0\",\n sha256 = \"2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/heck/0.5.0/download\"],\n strip_prefix = \"heck-0.5.0\",\n build_file = Label(\"@crates//crates:BUILD.heck-0.5.0.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__hermit-abi-0.5.3\",\n sha256 = \"e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/hermit-abi/0.5.3/download\"],\n strip_prefix = \"hermit-abi-0.5.3\",\n build_file = Label(\"@crates//crates:BUILD.hermit-abi-0.5.3.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__humantime-2.4.0\",\n sha256 = \"15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/humantime/2.4.0/download\"],\n strip_prefix = \"humantime-2.4.0\",\n build_file = Label(\"@crates//crates:BUILD.humantime-2.4.0.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__indexmap-2.14.2\",\n sha256 = \"cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/indexmap/2.14.2/download\"],\n strip_prefix = \"indexmap-2.14.2\",\n build_file = Label(\"@crates//crates:BUILD.indexmap-2.14.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__is-terminal-0.4.17\",\n sha256 = \"3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/is-terminal/0.4.17/download\"],\n strip_prefix = \"is-terminal-0.4.17\",\n build_file = Label(\"@crates//crates:BUILD.is-terminal-0.4.17.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__is_terminal_polyfill-1.70.2\",\n sha256 = \"a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/is_terminal_polyfill/1.70.2/download\"],\n strip_prefix = \"is_terminal_polyfill-1.70.2\",\n build_file = Label(\"@crates//crates:BUILD.is_terminal_polyfill-1.70.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__itoa-1.0.18\",\n sha256 = \"8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/itoa/1.0.18/download\"],\n strip_prefix = \"itoa-1.0.18\",\n build_file = Label(\"@crates//crates:BUILD.itoa-1.0.18.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__libc-0.2.189\",\n sha256 = \"3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/libc/0.2.189/download\"],\n strip_prefix = \"libc-0.2.189\",\n build_file = Label(\"@crates//crates:BUILD.libc-0.2.189.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__libloading-0.9.0\",\n sha256 = \"754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/libloading/0.9.0/download\"],\n strip_prefix = \"libloading-0.9.0\",\n build_file = Label(\"@crates//crates:BUILD.libloading-0.9.0.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__log-0.4.34\",\n sha256 = \"f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/log/0.4.34/download\"],\n strip_prefix = \"log-0.4.34\",\n build_file = Label(\"@crates//crates:BUILD.log-0.4.34.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__memchr-2.8.3\",\n sha256 = \"cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/memchr/2.8.3/download\"],\n strip_prefix = \"memchr-2.8.3\",\n build_file = Label(\"@crates//crates:BUILD.memchr-2.8.3.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__once_cell-1.21.4\",\n sha256 = \"9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/once_cell/1.21.4/download\"],\n strip_prefix = \"once_cell-1.21.4\",\n build_file = Label(\"@crates//crates:BUILD.once_cell-1.21.4.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__once_cell_polyfill-1.70.2\",\n sha256 = \"384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/once_cell_polyfill/1.70.2/download\"],\n strip_prefix = \"once_cell_polyfill-1.70.2\",\n build_file = Label(\"@crates//crates:BUILD.once_cell_polyfill-1.70.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__pest-2.9.1\",\n sha256 = \"6d45aeb61b4bf818e12d4205f2466f8c4748f85f4fce0146d1c03d69d753f0ad\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/pest/2.9.1/download\"],\n strip_prefix = \"pest-2.9.1\",\n build_file = Label(\"@crates//crates:BUILD.pest-2.9.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__pest_derive-2.9.1\",\n sha256 = \"89cc5a242e25ed4e7704d0be240f2cfbe20a8c27e7e252d94835be93d92dc39f\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/pest_derive/2.9.1/download\"],\n strip_prefix = \"pest_derive-2.9.1\",\n build_file = Label(\"@crates//crates:BUILD.pest_derive-2.9.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__pest_generator-2.9.1\",\n sha256 = \"7abf21475cc3820fe4b2ca2dc2142902f67a02189f3b5b3a229f4febc01a43e5\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/pest_generator/2.9.1/download\"],\n strip_prefix = \"pest_generator-2.9.1\",\n build_file = Label(\"@crates//crates:BUILD.pest_generator-2.9.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__pest_meta-2.9.1\",\n sha256 = \"adba4db388f687393c18c51348d44a41d870ca9df71a2c98172ea3035dc6936e\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/pest_meta/2.9.1/download\"],\n strip_prefix = \"pest_meta-2.9.1\",\n build_file = Label(\"@crates//crates:BUILD.pest_meta-2.9.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__proc-macro2-1.0.107\",\n sha256 = \"985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/proc-macro2/1.0.107/download\"],\n strip_prefix = \"proc-macro2-1.0.107\",\n build_file = Label(\"@crates//crates:BUILD.proc-macro2-1.0.107.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__quote-1.0.47\",\n sha256 = \"1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/quote/1.0.47/download\"],\n strip_prefix = \"quote-1.0.47\",\n build_file = Label(\"@crates//crates:BUILD.quote-1.0.47.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__regex-1.13.1\",\n sha256 = \"f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/regex/1.13.1/download\"],\n strip_prefix = \"regex-1.13.1\",\n build_file = Label(\"@crates//crates:BUILD.regex-1.13.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__regex-automata-0.4.18\",\n sha256 = \"ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/regex-automata/0.4.18/download\"],\n strip_prefix = \"regex-automata-0.4.18\",\n build_file = Label(\"@crates//crates:BUILD.regex-automata-0.4.18.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__regex-syntax-0.8.11\",\n sha256 = \"d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/regex-syntax/0.8.11/download\"],\n strip_prefix = \"regex-syntax-0.8.11\",\n build_file = Label(\"@crates//crates:BUILD.regex-syntax-0.8.11.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__rustc_version-0.4.1\",\n sha256 = \"cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/rustc_version/0.4.1/download\"],\n strip_prefix = \"rustc_version-0.4.1\",\n build_file = Label(\"@crates//crates:BUILD.rustc_version-0.4.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__ryu-1.0.23\",\n sha256 = \"9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/ryu/1.0.23/download\"],\n strip_prefix = \"ryu-1.0.23\",\n build_file = Label(\"@crates//crates:BUILD.ryu-1.0.23.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__semver-1.0.28\",\n sha256 = \"8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/semver/1.0.28/download\"],\n strip_prefix = \"semver-1.0.28\",\n build_file = Label(\"@crates//crates:BUILD.semver-1.0.28.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__serde-1.0.229\",\n sha256 = \"4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/serde/1.0.229/download\"],\n strip_prefix = \"serde-1.0.229\",\n build_file = Label(\"@crates//crates:BUILD.serde-1.0.229.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__serde_core-1.0.229\",\n sha256 = \"67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/serde_core/1.0.229/download\"],\n strip_prefix = \"serde_core-1.0.229\",\n build_file = Label(\"@crates//crates:BUILD.serde_core-1.0.229.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__serde_derive-1.0.229\",\n sha256 = \"e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/serde_derive/1.0.229/download\"],\n strip_prefix = \"serde_derive-1.0.229\",\n build_file = Label(\"@crates//crates:BUILD.serde_derive-1.0.229.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__serde_json-1.0.151\",\n sha256 = \"c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/serde_json/1.0.151/download\"],\n strip_prefix = \"serde_json-1.0.151\",\n build_file = Label(\"@crates//crates:BUILD.serde_json-1.0.151.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__serde_yaml-0.9.34-deprecated\",\n sha256 = \"6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/serde_yaml/0.9.34+deprecated/download\"],\n strip_prefix = \"serde_yaml-0.9.34+deprecated\",\n build_file = Label(\"@crates//crates:BUILD.serde_yaml-0.9.34+deprecated.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__strsim-0.11.1\",\n sha256 = \"7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/strsim/0.11.1/download\"],\n strip_prefix = \"strsim-0.11.1\",\n build_file = Label(\"@crates//crates:BUILD.strsim-0.11.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__syn-2.0.119\",\n sha256 = \"872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/syn/2.0.119/download\"],\n strip_prefix = \"syn-2.0.119\",\n build_file = Label(\"@crates//crates:BUILD.syn-2.0.119.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__syn-3.0.5\",\n sha256 = \"12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/syn/3.0.5/download\"],\n strip_prefix = \"syn-3.0.5\",\n build_file = Label(\"@crates//crates:BUILD.syn-3.0.5.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__termcolor-1.4.1\",\n sha256 = \"06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/termcolor/1.4.1/download\"],\n strip_prefix = \"termcolor-1.4.1\",\n build_file = Label(\"@crates//crates:BUILD.termcolor-1.4.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__thiserror-1.0.69\",\n sha256 = \"b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/thiserror/1.0.69/download\"],\n strip_prefix = \"thiserror-1.0.69\",\n build_file = Label(\"@crates//crates:BUILD.thiserror-1.0.69.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__thiserror-impl-1.0.69\",\n sha256 = \"4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/thiserror-impl/1.0.69/download\"],\n strip_prefix = \"thiserror-impl-1.0.69\",\n build_file = Label(\"@crates//crates:BUILD.thiserror-impl-1.0.69.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__ucd-trie-0.1.7\",\n sha256 = \"2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/ucd-trie/0.1.7/download\"],\n strip_prefix = \"ucd-trie-0.1.7\",\n build_file = Label(\"@crates//crates:BUILD.ucd-trie-0.1.7.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__unicode-ident-1.0.24\",\n sha256 = \"e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/unicode-ident/1.0.24/download\"],\n strip_prefix = \"unicode-ident-1.0.24\",\n build_file = Label(\"@crates//crates:BUILD.unicode-ident-1.0.24.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__unsafe-libyaml-0.2.11\",\n sha256 = \"673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/unsafe-libyaml/0.2.11/download\"],\n strip_prefix = \"unsafe-libyaml-0.2.11\",\n build_file = Label(\"@crates//crates:BUILD.unsafe-libyaml-0.2.11.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__utf8parse-0.2.2\",\n sha256 = \"06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/utf8parse/0.2.2/download\"],\n strip_prefix = \"utf8parse-0.2.2\",\n build_file = Label(\"@crates//crates:BUILD.utf8parse-0.2.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__winapi-util-0.1.11\",\n sha256 = \"c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/winapi-util/0.1.11/download\"],\n strip_prefix = \"winapi-util-0.1.11\",\n build_file = Label(\"@crates//crates:BUILD.winapi-util-0.1.11.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__windows-link-0.2.1\",\n sha256 = \"f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/windows-link/0.2.1/download\"],\n strip_prefix = \"windows-link-0.2.1\",\n build_file = Label(\"@crates//crates:BUILD.windows-link-0.2.1.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__windows-sys-0.61.2\",\n sha256 = \"ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/windows-sys/0.61.2/download\"],\n strip_prefix = \"windows-sys-0.61.2\",\n build_file = Label(\"@crates//crates:BUILD.windows-sys-0.61.2.bazel\"),\n )\n\n maybe(\n http_archive,\n name = \"crates__zmij-1.0.23\",\n sha256 = \"29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b\",\n type = \"tar.gz\",\n urls = [\"https://static.crates.io/crates/zmij/1.0.23/download\"],\n strip_prefix = \"zmij-1.0.23\",\n build_file = Label(\"@crates//crates:BUILD.zmij-1.0.23.bazel\"),\n )\n\n return [\n struct(repo=\"crates__assert-json-diff-2.0.2\", is_dev_dep = False),\n struct(repo=\"crates__clang-1.0.3\", is_dev_dep = False),\n struct(repo=\"crates__clang-sys-1.8.1\", is_dev_dep = False),\n struct(repo=\"crates__clap-4.6.6\", is_dev_dep = False),\n struct(repo=\"crates__env_logger-0.10.2\", is_dev_dep = False),\n struct(repo=\"crates__flatbuffers-25.12.19\", is_dev_dep = False),\n struct(repo=\"crates__libloading-0.9.0\", is_dev_dep = False),\n struct(repo=\"crates__log-0.4.34\", is_dev_dep = False),\n struct(repo=\"crates__once_cell-1.21.4\", is_dev_dep = False),\n struct(repo=\"crates__pest-2.9.1\", is_dev_dep = False),\n struct(repo=\"crates__pest_derive-2.9.1\", is_dev_dep = False),\n struct(repo=\"crates__serde-1.0.229\", is_dev_dep = False),\n struct(repo=\"crates__serde_json-1.0.151\", is_dev_dep = False),\n struct(repo=\"crates__serde_yaml-0.9.34-deprecated\", is_dev_dep = False),\n struct(repo=\"crates__strsim-0.11.1\", is_dev_dep = False),\n struct(repo=\"crates__thiserror-1.0.69\", is_dev_dep = False),\n ]\n" + } + } + }, + "crates__aho-corasick-1.1.5": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/aho-corasick/1.1.5/download" + ], + "strip_prefix": "aho-corasick-1.1.5", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"aho_corasick\",\n deps = [\n \"@crates__memchr-2.8.3//:memchr\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"perf-literal\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=aho-corasick\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.1.5\",\n)\n" + } + }, + "crates__anstream-1.0.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstream/1.0.0/download" + ], + "strip_prefix": "anstream-1.0.0", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"anstream\",\n deps = [\n \"@crates__anstyle-1.0.14//:anstyle\",\n \"@crates__anstyle-parse-1.0.0//:anstyle_parse\",\n \"@crates__anstyle-query-1.1.5//:anstyle_query\",\n \"@crates__colorchoice-1.0.5//:colorchoice\",\n \"@crates__is_terminal_polyfill-1.70.2//:is_terminal_polyfill\",\n \"@crates__utf8parse-0.2.2//:utf8parse\",\n ] + select({\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n \"@crates__anstyle-wincon-3.0.11//:anstyle_wincon\", # x86_64-pc-windows-msvc\n ],\n \"//conditions:default\": [],\n }),\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"auto\",\n \"default\",\n \"wincon\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=anstream\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.0\",\n)\n" + } + }, + "crates__anstyle-1.0.14": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle/1.0.14/download" + ], + "strip_prefix": "anstyle-1.0.14", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"anstyle\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=anstyle\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.14\",\n)\n" + } + }, + "crates__anstyle-parse-1.0.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle-parse/1.0.0/download" + ], + "strip_prefix": "anstyle-parse-1.0.0", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"anstyle_parse\",\n deps = [\n \"@crates__utf8parse-0.2.2//:utf8parse\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"utf8\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=anstyle-parse\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.0\",\n)\n" + } + }, + "crates__anstyle-query-1.1.5": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle-query/1.1.5/download" + ], + "strip_prefix": "anstyle-query-1.1.5", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"anstyle_query\",\n deps = select({\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n \"@crates__windows-sys-0.61.2//:windows_sys\", # cfg(windows)\n ],\n \"//conditions:default\": [],\n }),\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=anstyle-query\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.1.5\",\n)\n" + } + }, + "crates__anstyle-wincon-3.0.11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle-wincon/3.0.11/download" + ], + "strip_prefix": "anstyle-wincon-3.0.11", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"anstyle_wincon\",\n deps = [\n \"@crates__anstyle-1.0.14//:anstyle\",\n ] + select({\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n \"@crates__once_cell_polyfill-1.70.2//:once_cell_polyfill\", # cfg(windows)\n \"@crates__windows-sys-0.61.2//:windows_sys\", # cfg(windows)\n ],\n \"//conditions:default\": [],\n }),\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=anstyle-wincon\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"3.0.11\",\n)\n" + } + }, + "crates__assert-json-diff-2.0.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/assert-json-diff/2.0.2/download" + ], + "strip_prefix": "assert-json-diff-2.0.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"assert_json_diff\",\n deps = [\n \"@crates__serde-1.0.229//:serde\",\n \"@crates__serde_json-1.0.151//:serde_json\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2018\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=assert-json-diff\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.0.2\",\n)\n" + } + }, + "crates__bitflags-2.13.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/bitflags/2.13.1/download" + ], + "strip_prefix": "bitflags-2.13.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"bitflags\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=bitflags\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.13.1\",\n)\n" + } + }, + "crates__cfg-if-1.0.4": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/cfg-if/1.0.4/download" + ], + "strip_prefix": "cfg-if-1.0.4", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"cfg_if\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2018\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=cfg-if\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.4\",\n)\n" + } + }, + "crates__clang-1.0.3": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "34c6913be3a1c94f52fb975cdec7ef5a7b69de10a55de66dcbc30d7046b85fa1", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clang/1.0.3/download" + ], + "strip_prefix": "clang-1.0.3", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"clang\",\n deps = [\n \"@crates__clang-sys-1.8.1//:clang_sys\",\n \"@crates__libc-0.2.189//:libc\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"runtime\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2015\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=clang\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.3\",\n)\n" + } + }, + "crates__clang-sys-1.8.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clang-sys/1.8.1/download" + ], + "strip_prefix": "clang-sys-1.8.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"clang_sys\",\n deps = [\n \"@crates//:libloading\",\n \"@crates__clang-sys-1.8.1//:build_script_build\",\n \"@crates__glob-0.3.4//:glob\",\n \"@crates__libc-0.2.189//:libc\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"clang_18_0\",\n \"runtime\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=clang-sys\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.8.1\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"clang_18_0\",\n \"runtime\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n deps = [\n \"@crates__glob-0.3.4//:glob\",\n ],\n edition = \"2021\",\n links = \"clang\",\n pkg_name = \"clang-sys\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=clang-sys\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.8.1\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__clap-4.6.6": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap/4.6.6/download" + ], + "strip_prefix": "clap-4.6.6", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"clap\",\n deps = [\n \"@crates__clap_builder-4.6.6//:clap_builder\",\n ],\n proc_macro_deps = [\n \"@crates__clap_derive-4.6.4//:clap_derive\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"color\",\n \"default\",\n \"derive\",\n \"error-context\",\n \"help\",\n \"std\",\n \"suggestions\",\n \"usage\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2024\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=clap\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"4.6.6\",\n)\n" + } + }, + "crates__clap_builder-4.6.6": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap_builder/4.6.6/download" + ], + "strip_prefix": "clap_builder-4.6.6", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"clap_builder\",\n deps = [\n \"@crates__anstream-1.0.0//:anstream\",\n \"@crates__anstyle-1.0.14//:anstyle\",\n \"@crates__clap_lex-1.1.0//:clap_lex\",\n \"@crates__strsim-0.11.1//:strsim\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"color\",\n \"error-context\",\n \"help\",\n \"std\",\n \"suggestions\",\n \"usage\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2024\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=clap_builder\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"4.6.6\",\n)\n" + } + }, + "crates__clap_derive-4.6.4": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap_derive/4.6.4/download" + ], + "strip_prefix": "clap_derive-4.6.4", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n name = \"clap_derive\",\n deps = [\n \"@crates__heck-0.5.0//:heck\",\n \"@crates__proc-macro2-1.0.107//:proc_macro2\",\n \"@crates__quote-1.0.47//:quote\",\n \"@crates__syn-3.0.5//:syn\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2024\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=clap_derive\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"4.6.4\",\n)\n" + } + }, + "crates__clap_lex-1.1.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap_lex/1.1.0/download" + ], + "strip_prefix": "clap_lex-1.1.0", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"clap_lex\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2024\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=clap_lex\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.1.0\",\n)\n" + } + }, + "crates__colorchoice-1.0.5": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/colorchoice/1.0.5/download" + ], + "strip_prefix": "colorchoice-1.0.5", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"colorchoice\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=colorchoice\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.5\",\n)\n" + } + }, + "crates__env_logger-0.10.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/env_logger/0.10.2/download" + ], + "strip_prefix": "env_logger-0.10.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"env_logger\",\n deps = [\n \"@crates__humantime-2.4.0//:humantime\",\n \"@crates__is-terminal-0.4.17//:is_terminal\",\n \"@crates__log-0.4.34//:log\",\n \"@crates__regex-1.13.1//:regex\",\n \"@crates__termcolor-1.4.1//:termcolor\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"auto-color\",\n \"color\",\n \"default\",\n \"humantime\",\n \"regex\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=env_logger\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.10.2\",\n)\n" + } + }, + "crates__equivalent-1.0.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/equivalent/1.0.2/download" + ], + "strip_prefix": "equivalent-1.0.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"equivalent\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2015\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=equivalent\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.2\",\n)\n" + } + }, + "crates__flatbuffers-25.12.19": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/flatbuffers/25.12.19/download" + ], + "strip_prefix": "flatbuffers-25.12.19", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"flatbuffers\",\n deps = [\n \"@crates__bitflags-2.13.1//:bitflags\",\n \"@crates__flatbuffers-25.12.19//:build_script_build\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2018\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=flatbuffers\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"25.12.19\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n deps = [\n \"@crates__rustc_version-0.4.1//:rustc_version\",\n ],\n edition = \"2018\",\n pkg_name = \"flatbuffers\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=flatbuffers\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"25.12.19\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__glob-0.3.4": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/glob/0.3.4/download" + ], + "strip_prefix": "glob-0.3.4", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"glob\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=glob\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.3.4\",\n)\n" + } + }, + "crates__hashbrown-0.17.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/hashbrown/0.17.1/download" + ], + "strip_prefix": "hashbrown-0.17.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"hashbrown\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2024\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=hashbrown\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.17.1\",\n)\n" + } + }, + "crates__heck-0.5.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/heck/0.5.0/download" + ], + "strip_prefix": "heck-0.5.0", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"heck\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=heck\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.5.0\",\n)\n" + } + }, + "crates__hermit-abi-0.5.3": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/hermit-abi/0.5.3/download" + ], + "strip_prefix": "hermit-abi-0.5.3", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"hermit_abi\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=hermit-abi\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.5.3\",\n)\n" + } + }, + "crates__humantime-2.4.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/humantime/2.4.0/download" + ], + "strip_prefix": "humantime-2.4.0", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"humantime\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=humantime\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.4.0\",\n)\n" + } + }, + "crates__indexmap-2.14.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/indexmap/2.14.2/download" + ], + "strip_prefix": "indexmap-2.14.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"indexmap\",\n deps = [\n \"@crates__equivalent-1.0.2//:equivalent\",\n \"@crates__hashbrown-0.17.1//:hashbrown\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2024\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=indexmap\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.14.2\",\n)\n" + } + }, + "crates__is-terminal-0.4.17": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/is-terminal/0.4.17/download" + ], + "strip_prefix": "is-terminal-0.4.17", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"is_terminal\",\n deps = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [\n \"@crates__libc-0.2.189//:libc\", # cfg(any(unix, target_os = \"wasi\"))\n ],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [\n \"@crates__libc-0.2.189//:libc\", # cfg(any(unix, target_os = \"wasi\"))\n ],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [\n \"@crates__libc-0.2.189//:libc\", # cfg(any(unix, target_os = \"wasi\"))\n ],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n \"@crates__windows-sys-0.61.2//:windows_sys\", # cfg(windows)\n ],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [\n \"@crates__libc-0.2.189//:libc\", # cfg(any(unix, target_os = \"wasi\"))\n ],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [\n \"@crates__libc-0.2.189//:libc\", # cfg(any(unix, target_os = \"wasi\"))\n ],\n \"//conditions:default\": [],\n }),\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2018\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=is-terminal\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.4.17\",\n)\n" + } + }, + "crates__is_terminal_polyfill-1.70.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/is_terminal_polyfill/1.70.2/download" + ], + "strip_prefix": "is_terminal_polyfill-1.70.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"is_terminal_polyfill\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=is_terminal_polyfill\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.70.2\",\n)\n" + } + }, + "crates__itoa-1.0.18": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/itoa/1.0.18/download" + ], + "strip_prefix": "itoa-1.0.18", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"itoa\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=itoa\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.18\",\n)\n" + } + }, + "crates__libc-0.2.189": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/libc/0.2.189/download" + ], + "strip_prefix": "libc-0.2.189", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"libc\",\n deps = [\n \"@crates__libc-0.2.189//:build_script_build\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=libc\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.2.189\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"libc\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=libc\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"0.2.189\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__libloading-0.9.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/libloading/0.9.0/download" + ], + "strip_prefix": "libloading-0.9.0", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"libloading\",\n deps = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [\n \"@crates__cfg-if-1.0.4//:cfg_if\", # cfg(unix)\n ],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [\n \"@crates__cfg-if-1.0.4//:cfg_if\", # cfg(unix)\n ],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n \"@crates__windows-link-0.2.1//:windows_link\", # cfg(windows)\n ],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [\n \"@crates__cfg-if-1.0.4//:cfg_if\", # cfg(unix)\n ],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [\n \"@crates__cfg-if-1.0.4//:cfg_if\", # cfg(unix)\n ],\n \"//conditions:default\": [],\n }),\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=libloading\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.9.0\",\n)\n" + } + }, + "crates__log-0.4.34": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/log/0.4.34/download" + ], + "strip_prefix": "log-0.4.34", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"log\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"alloc\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=log\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.4.34\",\n)\n" + } + }, + "crates__memchr-2.8.3": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/memchr/2.8.3/download" + ], + "strip_prefix": "memchr-2.8.3", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"memchr\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"alloc\",\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=memchr\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.8.3\",\n)\n" + } + }, + "crates__once_cell-1.21.4": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/once_cell/1.21.4/download" + ], + "strip_prefix": "once_cell-1.21.4", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"once_cell\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"alloc\",\n \"default\",\n \"race\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=once_cell\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.21.4\",\n)\n" + } + }, + "crates__once_cell_polyfill-1.70.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/once_cell_polyfill/1.70.2/download" + ], + "strip_prefix": "once_cell_polyfill-1.70.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"once_cell_polyfill\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=once_cell_polyfill\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.70.2\",\n)\n" + } + }, + "crates__pest-2.9.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "6d45aeb61b4bf818e12d4205f2466f8c4748f85f4fce0146d1c03d69d753f0ad", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/pest/2.9.1/download" + ], + "strip_prefix": "pest-2.9.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"pest\",\n deps = [\n \"@crates__memchr-2.8.3//:memchr\",\n \"@crates__ucd-trie-0.1.7//:ucd_trie\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"memchr\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=pest\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.9.1\",\n)\n" + } + }, + "crates__pest_derive-2.9.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "89cc5a242e25ed4e7704d0be240f2cfbe20a8c27e7e252d94835be93d92dc39f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/pest_derive/2.9.1/download" + ], + "strip_prefix": "pest_derive-2.9.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n name = \"pest_derive\",\n deps = [\n \"@crates__pest-2.9.1//:pest\",\n \"@crates__pest_generator-2.9.1//:pest_generator\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=pest_derive\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.9.1\",\n)\n" + } + }, + "crates__pest_generator-2.9.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "7abf21475cc3820fe4b2ca2dc2142902f67a02189f3b5b3a229f4febc01a43e5", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/pest_generator/2.9.1/download" + ], + "strip_prefix": "pest_generator-2.9.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"pest_generator\",\n deps = [\n \"@crates__pest-2.9.1//:pest\",\n \"@crates__pest_meta-2.9.1//:pest_meta\",\n \"@crates__proc-macro2-1.0.107//:proc_macro2\",\n \"@crates__quote-1.0.47//:quote\",\n \"@crates__syn-2.0.119//:syn\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=pest_generator\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.9.1\",\n)\n" + } + }, + "crates__pest_meta-2.9.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "adba4db388f687393c18c51348d44a41d870ca9df71a2c98172ea3035dc6936e", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/pest_meta/2.9.1/download" + ], + "strip_prefix": "pest_meta-2.9.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"pest_meta\",\n deps = [\n \"@crates__pest-2.9.1//:pest\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=pest_meta\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.9.1\",\n)\n" + } + }, + "crates__proc-macro2-1.0.107": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/proc-macro2/1.0.107/download" + ], + "strip_prefix": "proc-macro2-1.0.107", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"proc_macro2\",\n deps = [\n \"@crates__proc-macro2-1.0.107//:build_script_build\",\n \"@crates__unicode-ident-1.0.24//:unicode_ident\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"proc-macro\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=proc-macro2\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.107\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"proc-macro\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"proc-macro2\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=proc-macro2\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.0.107\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__quote-1.0.47": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/quote/1.0.47/download" + ], + "strip_prefix": "quote-1.0.47", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"quote\",\n deps = [\n \"@crates__proc-macro2-1.0.107//:proc_macro2\",\n \"@crates__quote-1.0.47//:build_script_build\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"proc-macro\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=quote\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.47\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"proc-macro\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"quote\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=quote\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.0.47\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__regex-1.13.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/regex/1.13.1/download" + ], + "strip_prefix": "regex-1.13.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"regex\",\n deps = [\n \"@crates__aho-corasick-1.1.5//:aho_corasick\",\n \"@crates__memchr-2.8.3//:memchr\",\n \"@crates__regex-automata-0.4.18//:regex_automata\",\n \"@crates__regex-syntax-0.8.11//:regex_syntax\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"perf\",\n \"perf-backtrack\",\n \"perf-cache\",\n \"perf-dfa\",\n \"perf-inline\",\n \"perf-literal\",\n \"perf-onepass\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=regex\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.13.1\",\n)\n" + } + }, + "crates__regex-automata-0.4.18": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/regex-automata/0.4.18/download" + ], + "strip_prefix": "regex-automata-0.4.18", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"regex_automata\",\n deps = [\n \"@crates__aho-corasick-1.1.5//:aho_corasick\",\n \"@crates__memchr-2.8.3//:memchr\",\n \"@crates__regex-syntax-0.8.11//:regex_syntax\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"alloc\",\n \"dfa-onepass\",\n \"hybrid\",\n \"meta\",\n \"nfa-backtrack\",\n \"nfa-pikevm\",\n \"nfa-thompson\",\n \"perf-inline\",\n \"perf-literal\",\n \"perf-literal-multisubstring\",\n \"perf-literal-substring\",\n \"std\",\n \"syntax\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=regex-automata\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.4.18\",\n)\n" + } + }, + "crates__regex-syntax-0.8.11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/regex-syntax/0.8.11/download" + ], + "strip_prefix": "regex-syntax-0.8.11", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"regex_syntax\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=regex-syntax\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.8.11\",\n)\n" + } + }, + "crates__rustc_version-0.4.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/rustc_version/0.4.1/download" + ], + "strip_prefix": "rustc_version-0.4.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"rustc_version\",\n deps = [\n \"@crates__semver-1.0.28//:semver\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2018\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=rustc_version\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.4.1\",\n)\n" + } + }, + "crates__ryu-1.0.23": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/ryu/1.0.23/download" + ], + "strip_prefix": "ryu-1.0.23", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"ryu\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=ryu\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.23\",\n)\n" + } + }, + "crates__semver-1.0.28": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/semver/1.0.28/download" + ], + "strip_prefix": "semver-1.0.28", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"semver\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=semver\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.28\",\n)\n" + } + }, + "crates__serde-1.0.229": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde/1.0.229/download" + ], + "strip_prefix": "serde-1.0.229", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"serde\",\n deps = [\n \"@crates__serde-1.0.229//:build_script_build\",\n \"@crates__serde_core-1.0.229//:serde_core\",\n ],\n proc_macro_deps = [\n \"@crates__serde_derive-1.0.229//:serde_derive\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"derive\",\n \"rc\",\n \"serde_derive\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.229\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"derive\",\n \"rc\",\n \"serde_derive\",\n \"std\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"serde\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.0.229\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__serde_core-1.0.229": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde_core/1.0.229/download" + ], + "strip_prefix": "serde_core-1.0.229", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"serde_core\",\n deps = [\n \"@crates__serde_core-1.0.229//:build_script_build\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"rc\",\n \"result\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde_core\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.229\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"rc\",\n \"result\",\n \"std\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"serde_core\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde_core\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.0.229\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__serde_derive-1.0.229": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde_derive/1.0.229/download" + ], + "strip_prefix": "serde_derive-1.0.229", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n name = \"serde_derive\",\n deps = [\n \"@crates__proc-macro2-1.0.107//:proc_macro2\",\n \"@crates__quote-1.0.47//:quote\",\n \"@crates__syn-3.0.5//:syn\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde_derive\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.229\",\n)\n" + } + }, + "crates__serde_json-1.0.151": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde_json/1.0.151/download" + ], + "strip_prefix": "serde_json-1.0.151", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"serde_json\",\n deps = [\n \"@crates__itoa-1.0.18//:itoa\",\n \"@crates__memchr-2.8.3//:memchr\",\n \"@crates__serde_core-1.0.229//:serde_core\",\n \"@crates__serde_json-1.0.151//:build_script_build\",\n \"@crates__zmij-1.0.23//:zmij\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde_json\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.151\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n \"std\",\n ],\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"serde_json\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde_json\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.0.151\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__serde_yaml-0.9.34-deprecated": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde_yaml/0.9.34+deprecated/download" + ], + "strip_prefix": "serde_yaml-0.9.34+deprecated", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"serde_yaml\",\n deps = [\n \"@crates__indexmap-2.14.2//:indexmap\",\n \"@crates__itoa-1.0.18//:itoa\",\n \"@crates__ryu-1.0.23//:ryu\",\n \"@crates__serde-1.0.229//:serde\",\n \"@crates__unsafe-libyaml-0.2.11//:unsafe_libyaml\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=serde_yaml\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.9.34+deprecated\",\n)\n" + } + }, + "crates__strsim-0.11.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/strsim/0.11.1/download" + ], + "strip_prefix": "strsim-0.11.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"strsim\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2015\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=strsim\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.11.1\",\n)\n" + } + }, + "crates__syn-2.0.119": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/syn/2.0.119/download" + ], + "strip_prefix": "syn-2.0.119", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"syn\",\n deps = [\n \"@crates__proc-macro2-1.0.107//:proc_macro2\",\n \"@crates__quote-1.0.47//:quote\",\n \"@crates__unicode-ident-1.0.24//:unicode_ident\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"clone-impls\",\n \"default\",\n \"derive\",\n \"parsing\",\n \"printing\",\n \"proc-macro\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=syn\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"2.0.119\",\n)\n" + } + }, + "crates__syn-3.0.5": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/syn/3.0.5/download" + ], + "strip_prefix": "syn-3.0.5", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"syn\",\n deps = [\n \"@crates__proc-macro2-1.0.107//:proc_macro2\",\n \"@crates__quote-1.0.47//:quote\",\n \"@crates__unicode-ident-1.0.24//:unicode_ident\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"clone-impls\",\n \"default\",\n \"derive\",\n \"full\",\n \"parsing\",\n \"printing\",\n \"proc-macro\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=syn\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"3.0.5\",\n)\n" + } + }, + "crates__termcolor-1.4.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/termcolor/1.4.1/download" + ], + "strip_prefix": "termcolor-1.4.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"termcolor\",\n deps = select({\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n \"@crates__winapi-util-0.1.11//:winapi_util\", # cfg(windows)\n ],\n \"//conditions:default\": [],\n }),\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2018\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=termcolor\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.4.1\",\n)\n" + } + }, + "crates__thiserror-1.0.69": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/thiserror/1.0.69/download" + ], + "strip_prefix": "thiserror-1.0.69", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"thiserror\",\n deps = [\n \"@crates__thiserror-1.0.69//:build_script_build\",\n ],\n proc_macro_deps = [\n \"@crates__thiserror-impl-1.0.69//:thiserror_impl\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=thiserror\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.69\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"thiserror\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=thiserror\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.0.69\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + }, + "crates__thiserror-impl-1.0.69": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/thiserror-impl/1.0.69/download" + ], + "strip_prefix": "thiserror-impl-1.0.69", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_proc_macro\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_proc_macro(\n name = \"thiserror_impl\",\n deps = [\n \"@crates__proc-macro2-1.0.107//:proc_macro2\",\n \"@crates__quote-1.0.47//:quote\",\n \"@crates__syn-2.0.119//:syn\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=thiserror-impl\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.69\",\n)\n" + } + }, + "crates__ucd-trie-0.1.7": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/ucd-trie/0.1.7/download" + ], + "strip_prefix": "ucd-trie-0.1.7", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"ucd_trie\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"std\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=ucd-trie\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.1.7\",\n)\n" + } + }, + "crates__unicode-ident-1.0.24": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/unicode-ident/1.0.24/download" + ], + "strip_prefix": "unicode-ident-1.0.24", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"unicode_ident\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=unicode-ident\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.24\",\n)\n" + } + }, + "crates__unsafe-libyaml-0.2.11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/unsafe-libyaml/0.2.11/download" + ], + "strip_prefix": "unsafe-libyaml-0.2.11", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"unsafe_libyaml\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=unsafe-libyaml\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.2.11\",\n)\n" + } + }, + "crates__utf8parse-0.2.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/utf8parse/0.2.2/download" + ], + "strip_prefix": "utf8parse-0.2.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"utf8parse\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"default\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2018\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=utf8parse\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.2.2\",\n)\n" + } + }, + "crates__winapi-util-0.1.11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/winapi-util/0.1.11/download" + ], + "strip_prefix": "winapi-util-0.1.11", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"winapi_util\",\n deps = select({\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [\n \"@crates__windows-sys-0.61.2//:windows_sys\", # cfg(windows)\n ],\n \"//conditions:default\": [],\n }),\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=winapi-util\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.1.11\",\n)\n" + } + }, + "crates__windows-link-0.2.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows-link/0.2.1/download" + ], + "strip_prefix": "windows-link-0.2.1", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"windows_link\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=windows-link\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.2.1\",\n)\n" + } + }, + "crates__windows-sys-0.61.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows-sys/0.61.2/download" + ], + "strip_prefix": "windows-sys-0.61.2", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\"@rules_rust//cargo:defs.bzl\", \"cargo_toml_env_vars\")\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"windows_sys\",\n deps = [\n \"@crates__windows-link-0.2.1//:windows_link\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_features = [\n \"Win32\",\n \"Win32_Foundation\",\n \"Win32_Storage\",\n \"Win32_Storage_FileSystem\",\n \"Win32_System\",\n \"Win32_System_Console\",\n \"Win32_System_SystemInformation\",\n \"default\",\n ],\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=windows-sys\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"0.61.2\",\n)\n" + } + }, + "crates__zmij-1.0.23": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "patch_args": [], + "patch_tool": "", + "patches": [], + "remote_patch_strip": 1, + "sha256": "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/zmij/1.0.23/download" + ], + "strip_prefix": "zmij-1.0.23", + "build_file_content": "###############################################################################\n# @generated\n# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To \n# regenerate this file, run the following:\n#\n# bazel mod show_repo 'score_tooling'\n###############################################################################\n\nload(\n \"@rules_rust//cargo:defs.bzl\",\n \"cargo_build_script\",\n \"cargo_toml_env_vars\",\n)\n\nload(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\n# buildifier: disable=bzl-visibility\nload(\"@rules_rust//crate_universe/private:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncargo_toml_env_vars(\n name = \"cargo_toml_env_vars\",\n src = \"Cargo.toml\",\n)\n\nrust_library(\n name = \"zmij\",\n deps = [\n \"@crates__zmij-1.0.23//:build_script_build\",\n ],\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_root = \"src/lib.rs\",\n edition = \"2021\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=zmij\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n target_compatible_with = select({\n \"@rules_rust//rust/platform:aarch64-apple-darwin\": [],\n \"@rules_rust//rust/platform:aarch64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:wasm32-unknown-unknown\": [],\n \"@rules_rust//rust/platform:wasm32-wasip1\": [],\n \"@rules_rust//rust/platform:x86_64-pc-windows-msvc\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-linux-gnu\": [],\n \"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu\": [],\n \"//conditions:default\": [\"@platforms//:incompatible\"],\n }),\n version = \"1.0.23\",\n)\n\ncargo_build_script(\n name = \"_bs\",\n compile_data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \"**/*.rs\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n crate_name = \"build_script_build\",\n crate_root = \"build.rs\",\n data = glob(\n allow_empty = True,\n include = [\"**\"],\n exclude = [\n \"**/* *\",\n \".tmp_git_root/**/*\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n ],\n ),\n edition = \"2021\",\n pkg_name = \"zmij\",\n rustc_env_files = [\n \":cargo_toml_env_vars\",\n ],\n rustc_flags = [\n \"--cap-lints=allow\",\n ],\n srcs = glob(\n allow_empty = True,\n include = [\"**/*.rs\"],\n ),\n tags = [\n \"cargo-bazel\",\n \"crate-name=zmij\",\n \"manual\",\n \"noclippy\",\n \"norustfmt\",\n ],\n version = \"1.0.23\",\n visibility = [\"//visibility:private\"],\n)\n\nalias(\n name = \"build_script_build\",\n actual = \":_bs\",\n tags = [\"manual\"],\n)\n" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "bazel_features", + "bazel_features+" + ], + [ + "rules_rust+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_rust+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_rust+", + "cargo_bazel_bootstrap", + "rules_rust++cu_nr+cargo_bazel_bootstrap" + ], + [ + "rules_rust+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "rules_rust", + "rules_rust+" + ] + ] + } + }, + "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { + "general": { + "bzlTransitiveDigest": "ExCb8VL1oj/L1Qkv9O9P1KpjLDMpwnpKMeYXxozBVfw=", + "usagesDigest": "1ieIYuafZ1pmP+ncVvISMfB3Em0hv9LCmCRwIH7gL8E=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "cargo_bazel_bootstrap": { + "repoRuleId": "@@rules_rust+//cargo/private:cargo_bootstrap.bzl%cargo_bootstrap_repository", + "attributes": { + "srcs": [ + "@@rules_rust+//crate_universe:src/api.rs", + "@@rules_rust+//crate_universe:src/api/lockfile.rs", + "@@rules_rust+//crate_universe:src/cli.rs", + "@@rules_rust+//crate_universe:src/cli/generate.rs", + "@@rules_rust+//crate_universe:src/cli/query.rs", + "@@rules_rust+//crate_universe:src/cli/render.rs", + "@@rules_rust+//crate_universe:src/cli/splice.rs", + "@@rules_rust+//crate_universe:src/cli/vendor.rs", + "@@rules_rust+//crate_universe:src/config.rs", + "@@rules_rust+//crate_universe:src/context.rs", + "@@rules_rust+//crate_universe:src/context/crate_context.rs", + "@@rules_rust+//crate_universe:src/context/platforms.rs", + "@@rules_rust+//crate_universe:src/lib.rs", + "@@rules_rust+//crate_universe:src/lockfile.rs", + "@@rules_rust+//crate_universe:src/main.rs", + "@@rules_rust+//crate_universe:src/metadata.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_bin.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_resolver.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.bat", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.sh", + "@@rules_rust+//crate_universe:src/metadata/dependency.rs", + "@@rules_rust+//crate_universe:src/metadata/metadata_annotation.rs", + "@@rules_rust+//crate_universe:src/rendering.rs", + "@@rules_rust+//crate_universe:src/rendering/template_engine.rs", + "@@rules_rust+//crate_universe:src/rendering/templates/module_bzl.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/header.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/aliases_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/deps_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_git.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_http.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/vendor_module.j2", + "@@rules_rust+//crate_universe:src/rendering/verbatim/alias_rules.bzl", + "@@rules_rust+//crate_universe:src/select.rs", + "@@rules_rust+//crate_universe:src/splicing.rs", + "@@rules_rust+//crate_universe:src/splicing/cargo_config.rs", + "@@rules_rust+//crate_universe:src/splicing/crate_index_lookup.rs", + "@@rules_rust+//crate_universe:src/splicing/splicer.rs", + "@@rules_rust+//crate_universe:src/test.rs", + "@@rules_rust+//crate_universe:src/utils.rs", + "@@rules_rust+//crate_universe:src/utils/starlark.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/glob.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/label.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_dict.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_list.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_scalar.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_set.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/serialize.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/target_compatible_with.rs", + "@@rules_rust+//crate_universe:src/utils/symlink.rs", + "@@rules_rust+//crate_universe:src/utils/target_triple.rs" + ], + "binary": "cargo-bazel", + "cargo_lockfile": "@@rules_rust+//crate_universe:Cargo.lock", + "cargo_toml": "@@rules_rust+//crate_universe:Cargo.toml", + "version": "1.86.0", + "timeout": 900, + "rust_toolchain_cargo_template": "@rust_host_tools//:bin/{tool}", + "rust_toolchain_rustc_template": "@rust_host_tools//:bin/{tool}", + "compressed_windows_toolchain_names": false + } + } + }, + "moduleExtensionMetadata": { + "explicitRootModuleDirectDeps": [ + "cargo_bazel_bootstrap" + ], + "explicitRootModuleDirectDevDeps": [], + "useAllRepos": "NO", + "reproducible": false + }, + "recordedRepoMappingEntries": [ + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "bazel_features", + "bazel_features+" + ], + [ + "rules_rust+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_rust+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_rust+", + "cargo_bazel_bootstrap", + "rules_rust++cu_nr+cargo_bazel_bootstrap" + ], + [ + "rules_rust+", + "cui", + "rules_rust++cu+cui" + ], + [ + "rules_rust+", + "rrc", + "rules_rust++i2+rrc" + ], + [ + "rules_rust+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "rules_rust", + "rules_rust+" + ] + ] + } + }, + "@@rules_swift+//swift:extensions.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "TdyDy4TBpjOHwrF4hSiQwGdsTAvssvyD6vUJBx+7nt4=", + "usagesDigest": "mhACFnrdMv9Wi0Mt67bxocJqviRkDSV+Ee5Mqdj5akA=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_apple_swift_protobuf": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-protobuf/archive/1.20.2.tar.gz" + ], + "sha256": "3fb50bd4d293337f202d917b6ada22f9548a0a0aed9d9a4d791e6fbd8a246ebb", + "strip_prefix": "swift-protobuf-1.20.2/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_protobuf/BUILD.overlay" + } + }, + "com_github_grpc_grpc_swift": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/grpc/grpc-swift/archive/1.16.0.tar.gz" + ], + "sha256": "58b60431d0064969f9679411264b82e40a217ae6bd34e17096d92cc4e47556a5", + "strip_prefix": "grpc-swift-1.16.0/", + "build_file": "@@rules_swift+//third_party:com_github_grpc_grpc_swift/BUILD.overlay" + } + }, + "com_github_apple_swift_docc_symbolkit": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-docc-symbolkit/archive/refs/tags/swift-5.10-RELEASE.tar.gz" + ], + "sha256": "de1d4b6940468ddb53b89df7aa1a81323b9712775b0e33e8254fa0f6f7469a97", + "strip_prefix": "swift-docc-symbolkit-swift-5.10-RELEASE", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_docc_symbolkit/BUILD.overlay" + } + }, + "com_github_apple_swift_nio": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio/archive/2.42.0.tar.gz" + ], + "sha256": "e3304bc3fb53aea74a3e54bd005ede11f6dc357117d9b1db642d03aea87194a0", + "strip_prefix": "swift-nio-2.42.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_http2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-http2/archive/1.26.0.tar.gz" + ], + "sha256": "f0edfc9d6a7be1d587e5b403f2d04264bdfae59aac1d74f7d974a9022c6d2b25", + "strip_prefix": "swift-nio-http2-1.26.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_http2/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_transport_services": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-transport-services/archive/1.15.0.tar.gz" + ], + "sha256": "f3498dafa633751a52b9b7f741f7ac30c42bcbeb3b9edca6d447e0da8e693262", + "strip_prefix": "swift-nio-transport-services-1.15.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_transport_services/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_extras": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-extras/archive/1.4.0.tar.gz" + ], + "sha256": "4684b52951d9d9937bb3e8ccd6b5daedd777021ef2519ea2f18c4c922843b52b", + "strip_prefix": "swift-nio-extras-1.4.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_extras/BUILD.overlay" + } + }, + "com_github_apple_swift_log": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-log/archive/1.4.4.tar.gz" + ], + "sha256": "48fe66426c784c0c20031f15dc17faf9f4c9037c192bfac2f643f65cb2321ba0", + "strip_prefix": "swift-log-1.4.4/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_log/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_ssl": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-ssl/archive/2.23.0.tar.gz" + ], + "sha256": "4787c63f61dd04d99e498adc3d1a628193387e41efddf8de19b8db04544d016d", + "strip_prefix": "swift-nio-ssl-2.23.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_ssl/BUILD.overlay" + } + }, + "com_github_apple_swift_collections": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-collections/archive/1.0.4.tar.gz" + ], + "sha256": "d9e4c8a91c60fb9c92a04caccbb10ded42f4cb47b26a212bc6b39cc390a4b096", + "strip_prefix": "swift-collections-1.0.4/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_collections/BUILD.overlay" + } + }, + "com_github_apple_swift_atomics": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-atomics/archive/1.1.0.tar.gz" + ], + "sha256": "1bee7f469f7e8dc49f11cfa4da07182fbc79eab000ec2c17bfdce468c5d276fb", + "strip_prefix": "swift-atomics-1.1.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_atomics/BUILD.overlay" + } + }, + "build_bazel_rules_swift_index_import": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@rules_swift+//third_party:build_bazel_rules_swift_index_import/BUILD.overlay", + "canonical_id": "index-import-5.8", + "urls": [ + "https://github.com/MobileNativeFoundation/index-import/releases/download/5.8.0.1/index-import.tar.gz" + ], + "sha256": "28c1ffa39d99e74ed70623899b207b41f79214c498c603915aef55972a851a15" + } + }, + "build_bazel_rules_swift_local_config": { + "repoRuleId": "@@rules_swift+//swift/internal:swift_autoconfiguration.bzl%swift_autoconfiguration", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_swift+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_swift+", + "build_bazel_rules_swift", + "rules_swift+" + ] + ] + } + }, + "@@yq.bzl+//yq:extensions.bzl%yq": { + "general": { + "bzlTransitiveDigest": "61Uz+o5PnlY0jJfPZEUNqsKxnM/UCLeWsn5VVCc8u5Y=", + "usagesDigest": "1CP5kLHwnlFZ3obmnV0eEOp+80JltkcSimeCHXe8/+E=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "yq_darwin_amd64": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "darwin_amd64", + "version": "4.45.1" + } + }, + "yq_darwin_arm64": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "darwin_arm64", + "version": "4.45.1" + } + }, + "yq_linux_amd64": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "linux_amd64", + "version": "4.45.1" + } + }, + "yq_linux_arm64": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "linux_arm64", + "version": "4.45.1" + } + }, + "yq_linux_s390x": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "linux_s390x", + "version": "4.45.1" + } + }, + "yq_linux_riscv64": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "linux_riscv64", + "version": "4.45.1" + } + }, + "yq_linux_ppc64le": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "linux_ppc64le", + "version": "4.45.1" + } + }, + "yq_windows_amd64": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", + "attributes": { + "platform": "windows_amd64", + "version": "4.45.1" + } + }, + "yq_toolchains": { + "repoRuleId": "@@yq.bzl+//yq/toolchain:toolchain.bzl%yq_toolchains_repo", + "attributes": { + "user_repository_name": "yq" + } + } + }, + "recordedRepoMappingEntries": [] + } + } + }, + "facts": {} +} diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..9c933ea --- /dev/null +++ b/NOTICE @@ -0,0 +1,38 @@ + + +# Notices for Eclipse Safe Open Vehicle Core + +This content is produced and maintained by the Eclipse Safe Open Vehicle Core project. + + * Project home: https://projects.eclipse.org/projects/automotive.score + +## Trademarks + +Eclipse, and the Eclipse Logo are registered trademarks of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. +For more information regarding authorship of content, please consult the +listed source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Apache License Version 2.0 which is available at +https://www.apache.org/licenses/LICENSE-2.0. + +SPDX-License-Identifier: Apache-2.0 + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. diff --git a/README.md b/README.md index 180c738..3d0a43b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,346 @@ -# coverage_tool -LLVM source-based code coverage pipeline for Eclipse S-CORE (Bazel module score_coverage) + + +# coverage_tool — Bazel module `score_coverage` + +LLVM source-based code coverage pipeline for Eclipse S-CORE, developed as a +software tool under the S-CORE tool-management process (ISO 26262-8 clause 11). +This repository is the successor of `@score_tooling//coverage`. + +Repository layout: + +- `defs.bzl`, `BUILD` — the public API (`score_coverage_scope`, + `score_coverage_reporter`, `//:merger`, `//:generate_coverage_html`, + `//:enable_llvm_coverage_for_death_tests`). +- `score_coverage/` — the implementation (Python report tooling, Starlark + rules, unit tests). +- `integration_tests/` — a self-contained consumer workspace (C++ + Rust) + exercised end to end by `run_integration_test.sh`; it is also the reference + implementation for the adoption guide below. +- `COVERAGE_GUIDE.md` — how the pipeline works internally. + +# Adoption guide + +Reusable **LLVM source-based coverage pipeline** for S-CORE repositories: + +- **One report for C++ and Rust** (line + branch coverage), produced by + `llvm-cov` directly from covmap instrumentation — no gcov/genhtml. +- **Untested in-scope files appear at exact 0%** — since all targets are + instrumented at build time, the reporter runs `llvm-cov --empty-profile` + over the archives of libraries no test links against. No heuristics; the + line/branch denominators come from the compiler's own coverage map. +- **Justification system**: `COV_JUSTIFIED` in-code markers + a YAML database + turn intentionally-uncovered lines into *justified* lines, tracked in an + **effective coverage** metric with stale-justification detection. +- **Gating**: the report generator exits non-zero when effective coverage is + below `COVERAGE_THRESHOLD` (default 100). + +How the pipeline works internally is documented in +[COVERAGE_GUIDE.md](COVERAGE_GUIDE.md). A complete, working consumer setup is +the [integration_tests/](integration_tests/) workspace — every snippet below +is copied from it. + +## Components + +| Target / file | Purpose | +|---|---| +| `@score_coverage//:merger` | Per-test coverage output generator (profraw → profdata + object metadata). Referenced directly from your bazelrc. | +| `@score_coverage//:reporter` | Final report generator (merged profdata → HTML + LCOV + text summary). Not referenced directly — wrapped by `score_coverage_reporter`. | +| `defs.bzl :: score_coverage_scope` | Declares WHICH targets are in scope; emits the source allowlist + baseline-archive manifest via an aspect. | +| `defs.bzl :: score_coverage_reporter` | Consumer-side wrapper wiring your scope, workspace root and LLVM tools into the reporter. | +| `@score_coverage//:generate_coverage_html` | Orchestration: unpacks the report, runs justifications, enforces the threshold, optionally archives. | +| `@score_coverage//:justify` | Parses the justification YAML + in-code markers into a manifest. | +| `@score_coverage//:effective_coverage` | Post-processes the HTML: restyles justified lines, computes effective coverage, detects stale justifications. | +| `@score_coverage//:coverage_summary` | Renders the markdown job summary from the LCOV data (invoked by `generate_coverage_html` for `--summary-md` / `GITHUB_STEP_SUMMARY`). | +| `@score_coverage//:enable_llvm_coverage_for_death_tests` | `cc_feature` adding `-mllvm -runtime-counter-relocation` (continuous-mode profiling for death tests). | + +## Prerequisites + +1. A Bzlmod workspace (`MODULE.bazel`). +2. Linux x86_64 host (the pipeline runs on the host platform; do not combine + with QNX/cross platform configs). +3. For Rust: a Ferrocene toolchain built by `ferrocene_toolchain_builder` + **>= 1.3.1** (its coverage-tools tarball ships `llvm-cov`/`llvm-profdata` + built from the same LLVM as rustc) wired through `score_toolchains_rust` + **>= 0.10.0**. + +## 1. Depend on score_coverage + +```starlark +bazel_dep(name = "score_coverage", version = "") +``` + +Add one line to your **root** `BUILD` file so the reporter can locate your +workspace root at runtime: + +```starlark +exports_files(["MODULE.bazel"]) +``` + +## 2. Declare the coverage toolchains (MODULE.bazel) + +```starlark +bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) +bazel_dep(name = "toolchains_llvm", version = "1.8.0", dev_dependency = True) + +llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) +llvm.toolchain( + cxx_standard = {"": "c++17"}, + extra_known_features = [ + "@score_coverage//:enable_llvm_coverage_for_death_tests", + ], + llvm_version = "22.1.7", + stdlib = {"": "stdc++"}, +) +use_repo(llvm, "llvm_toolchain", "llvm_toolchain_llvm") +``` + +For Rust, **no coverage-specific toolchain is needed**: the standard +toolchains shipped by score_toolchains_rust >= 0.10.0 already attach +`llvm-cov`/`llvm-profdata` (from the ferrocene_toolchain_builder >= 1.3.1 +coverage-tools tarball, built from the same LLVM as rustc). Just register the +standard toolchain as usual: + +``` +common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu +``` + +rules_rust only instruments crates when the `rust_toolchain` declares +`llvm_cov` — a Ferrocene toolchain from an older score_toolchains_rust (or a +custom instance without `coverage_tools_url`) silently produces no Rust +coverage. + +## 3. Declare scope and reporter (BUILD) + +In e.g. `tools/coverage/BUILD`: + +```starlark +load("@score_coverage//:defs.bzl", "score_coverage_reporter", "score_coverage_scope") + +score_coverage_scope( + name = "coverage_scope", + testonly = True, + deps = [ + "//src/mylib", # cc_library + "//src/rust/mycrate", # rust_library + "//src/rust/tool:tool", # rust_binary + ], +) + +score_coverage_reporter( + name = "reporter_wrapper", + testonly = True, + coverage_scope = ":coverage_scope", + llvm_cov = "@llvm_toolchain//:llvm-cov", + llvm_profdata = "@llvm_toolchain//:llvm-profdata", + llvm_cxxfilt = "@llvm_toolchain_llvm//:bin/llvm-cxxfilt", +) +``` + +The scope aspect walks the listed targets and their transitive in-workspace +deps, collecting source files (allowlist) and compiled archives (baselines). +Everything in scope but untested shows up at 0%; everything outside the scope +(tests, mocks, external deps) is filtered out of the report. + +## 4. Import the bazelrc config + +Copy the `coverage:llvm_cov` block from +[integration_tests/.bazelrc](integration_tests/.bazelrc) into your +repository's bazelrc (directly or via `import`). If your `.bazelrc` ends with +a `try-import %workspace%/user.bazelrc` (or similar local-override file), +place the coverage import BEFORE it — bazelrc conflicts resolve last-wins, +and the local override file must stay last to keep working. The two labels +to adapt: + +``` +coverage:llvm_cov --coverage_output_generator=@score_coverage//:merger +coverage:llvm_cov --coverage_report_generator=//tools/coverage:reporter_wrapper +``` + +The merger reference points into score_coverage as-is; the reporter_wrapper +label is the target you declared in step 3. + +> **Do NOT combine `--config=llvm_cov` with configs that append other +> `--extra_toolchains`** (e.g. a GCC host config): the last toolchain wins +> resolution and a GCC toolchain produces no covmap data. + +## 5. (Optional) Set up justifications + +`tools/coverage/coverage_justifications.yaml`: + +```yaml +version: 1 +justifications: + - id: hw-unreachable-on-x86 + category: platform_specific + platforms: [linux] + reason: | + ARM-only error path; cannot be exercised by x86 CI. +``` + +Mark the code in place: + +```cpp +return false; // COV_JUSTIFIED hw-unreachable-on-x86 + +// or a region: +// COV_JUSTIFIED_START hw-unreachable-on-x86 +if (running_on_arm()) { ... } +// COV_JUSTIFIED_STOP +``` + +Valid categories: `defensive_programming`, `tool_false_positive`, +`platform_specific`, `other`. IDs are kebab-case. Justified lines render +orange in the HTML and count as covered in the *effective* metric; a +justification on a line that is meanwhile covered is flagged as **stale**. + +## 6. Run it + +```bash +bazel coverage --config=llvm_cov //... --build_tests_only + +bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml + +# CI variant: assemble HTML + LCOV + JUnit XMLs for artifact upload, gate at 95%: +COVERAGE_THRESHOLD=95 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml \ + --archive-dir coverage_artifacts +# then: actions/upload-artifact with path: coverage_artifacts +# (upload-artifact zips its input itself — use --archive only when you +# want a local .zip; uploading that zip would nest it in a second zip) +``` + +`--yaml` is optional: without it, justification processing is skipped and the +`COVERAGE_THRESHOLD` gate applies to the **raw** line coverage. Start without +a YAML; add one (`version: 1` + `justifications: []`) when you introduce your +first `COV_JUSTIFIED` marker. + +**GitHub job summary:** inside GitHub Actions no extra flags are needed — when +`GITHUB_STEP_SUMMARY` is set (and `--summary-md` is not given), a markdown +summary is appended to the workflow run page automatically: overall +line/branch/file tables with progress bars, raw-vs-effective when a +justification YAML is in play, a per-directory rollup (worst first), and +collapsible lists of the least-covered and exact-0% files. Outside Actions, +pass `--summary-md ` to write the same summary to a file. The summary is +emitted before the threshold gate decides the exit code, so a failing gate +still leaves it on the run page. No consumer-side LCOV parsing needed. + +`--build_tests_only` matters: without it, coverage builds (not runs) every +target matched by the pattern, including e.g. `manual`-tagged or +platform-incompatible test binaries. + +## Customization knobs + +| Need | Knob | +|---|---| +| Different gate | `COVERAGE_THRESHOLD=` env var (default 100; exit 1 below; gates effective coverage with `--yaml`, raw coverage without) | +| Output directory | positional `output-dir` argument (default `coverage_`) | +| Platform-specific justifications | `--platform linux\|qnx` (default linux) | +| JUnit XMLs subtree in the archive | `--testlogs-subdir ` (default: whole `bazel-testlogs`) | +| Markdown job summary | `--summary-md `; auto-append to `GITHUB_STEP_SUMMARY` when the flag is absent and the variable is set | +| Different LLVM version | your own `llvm.toolchain(...)`; pass its labels in step 3 | +| Rust branch coverage | `-Zcoverage-options=branch` (needs a nightly-based/rolling Ferrocene; drop the flag on stable) | + +## Troubleshooting + +| Symptom | Cause | +|---|---| +| `... is not the LLVM pipeline zip report` | The coverage run used the default lcov path — the `--config=llvm_cov` flags (or your bazelrc import) were not active. | +| No `.rs` files in the report | The Ferrocene toolchain in use has no `llvm_cov` attached (missing `coverage_tools_url`, or a non-coverage toolchain instance won resolution). | +| No C++ files / empty covmap | A GCC toolchain won toolchain resolution — check for conflicting `--extra_toolchains` from another config. | +| `Neither __llvm_profile_counter_bias nor ...` in test logs, no profraw | Continuous mode without runtime counter relocation: the `enable_llvm_coverage_for_death_tests` feature (C++) or the `-Cllvm-args=-runtime-counter-relocation` rustc flag is missing. | +| `no coverage data found` on a Rust archive | Handled automatically (rlib expansion); if you see it, the reporter predates the rlib fix. | +| `error[E0463]: can't find crate for profiler_builtins` | The Ferrocene sysroot lacks profiler_builtins (builder < 1.3.1, or a miri sysroot leaked into coverage builds). | +| `the following arguments are required: --workspace_root` | You pointed `--coverage_report_generator` at `:reporter` directly instead of your `score_coverage_reporter` target. | +| Coverage numbers differ between runs on identical code | Dynamic-linking instrumentation clash — ensure `--dynamic_mode=off` from the bazelrc block is active. | + +## Migration from the removed Ferrocene symbol-report/blanket flow + +`rust_coverage_report`, `//coverage:ferrocene_report` and its helper scripts +were removed. Replace: + +- `bazel run //:rust_coverage` → steps 1–6 above (one report for both + languages, exact untested-file entries, justifications, effective gate). +- `test:ferrocene-coverage --run_under=@score_coverage//:llvm_profile_wrapper` + is no longer needed — Bazel's own coverage collection sets + `LLVM_PROFILE_FILE`. The wrapper target still exists for repositories that + have not migrated yet. + +--- + +## Repository-internal: Combined Rust + Python Coverage + +The `//coverage:combined_report` target generates a single HTML coverage report +for all Rust and Python tools in the repository using Bazel's built-in +coverage support (`bazel coverage`) and `genhtml`. + +### Usage + +```bash +bazel run //coverage:combined_report +``` + +This runs `bazel coverage --config=coverage` for `//plantuml/...`, +`//validation/...` and `//manual_analysis/...`, merges all LCOV data, and +renders the report to `/coverage-html/index.html`. + +Custom output directory: + +```bash +bazel run //coverage:combined_report -- --out-dir /tmp/my-coverage +``` + +Custom target set: + +```bash +bazel run //coverage:combined_report -- --targets "//plantuml/... //validation/core/..." +``` + +### How it works + +1. `bazel coverage --config=coverage` compiles Rust with `-Cinstrument-coverage` + and wraps Python tests with `coverage.py` (via `rules_python`'s built-in + `configure_coverage_tool`). +2. Bazel merges all per-test LCOV files into one `_coverage_report.dat` + (controlled by `--combined_report=lcov`). +3. `--instrumentation_filter` limits instrumentation to the three tool + packages, excluding external dependencies and generated code. +4. Test infrastructure files (`integration_test/`, `tests/`) are excluded from + instrumentation via `--instrumentation_filter`; external Python files are + removed via `lcov --remove`. +5. The HTML report uses a high-coverage threshold of **95 %** (green) and the + default medium threshold of 75 % (yellow). +6. `genhtml` and `lcov` are downloaded hermetically via the `download_utils` + Bazel module (`@lcov_deb`) — no system installation of `lcov` is required. + +### .bazelrc config + +The `coverage:coverage` config in `.bazelrc` provides the required flags: + +``` +coverage:coverage --combined_report=lcov +coverage:coverage --instrumentation_filter=//plantuml,//validation,//manual_analysis,-//plantuml/parser/integration_test,-//validation/core/integration_test +coverage:coverage --@rules_rust//rust/settings:extra_rustc_flag=-Clink-dead-code +coverage:coverage --@rules_rust//rust/settings:extra_rustc_flag=-Ccodegen-units=1 +``` + +You can also run `bazel coverage` directly without the script (requires `genhtml` +from the system `lcov` package): + +```bash +bazel coverage --config=coverage //plantuml/... //validation/... //manual_analysis/... +genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat" \ + --output-directory coverage-html/ +``` + diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 0000000..aeeb922 --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,24 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +version = 1 + +[[annotations]] +path = [ + "**/.bazelversion", + "**/MODULE.bazel.lock", + "score_coverage/requirements.in", + "integration_tests/tools/coverage/coverage_justifications.yaml", +] +SPDX-FileCopyrightText = "Copyright (c) 2026 Contributors to the Eclipse Foundation" +SPDX-License-Identifier = "Apache-2.0" diff --git a/defs.bzl b/defs.bzl new file mode 100644 index 0000000..ce5b882 --- /dev/null +++ b/defs.bzl @@ -0,0 +1,90 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"""Public API of the score_coverage LLVM source-based coverage pipeline. + +Consumers instantiate two targets in their own repository (typically in a +tools/coverage/BUILD file): + + load("@score_coverage//:defs.bzl", + "score_coverage_reporter", "score_coverage_scope") + + score_coverage_scope( + name = "coverage_scope", + testonly = True, + deps = ["//src/mylib", "//src/rust/mycrate"], + ) + + score_coverage_reporter( + name = "reporter_wrapper", + testonly = True, + coverage_scope = ":coverage_scope", + llvm_cov = "@llvm_toolchain//:llvm-cov", + llvm_profdata = "@llvm_toolchain//:llvm-profdata", + llvm_cxxfilt = "@llvm_toolchain_llvm//:bin/llvm-cxxfilt", + ) + +and point Bazel at them from their coverage bazelrc config: + + coverage:llvm_cov --coverage_output_generator=@score_coverage//:merger + coverage:llvm_cov --coverage_report_generator=//tools/coverage:reporter_wrapper + +See README.md for the complete adoption guide (toolchains, bazelrc, +justifications, CI) and COVERAGE_GUIDE.md for how the pipeline works. +""" + +load("//score_coverage:coverage_scope.bzl", _coverage_scope = "coverage_scope") +load("//score_coverage:reporter_wrapper.bzl", _reporter_wrapper = "reporter_wrapper") + +score_coverage_scope = _coverage_scope + +def score_coverage_reporter( + name, + coverage_scope, + llvm_cov, + llvm_profdata, + llvm_cxxfilt = None, + module_bazel = "//:MODULE.bazel", + **kwargs): + """Declare the consumer-side coverage report generator. + + The generated executable is passed to Bazel as + --coverage_report_generator=//:. It wires the consumer's + coverage scope, workspace root and LLVM tools into score_coverage's + reporter. + + Args: + name: Target name, referenced by --coverage_report_generator. + coverage_scope: A score_coverage_scope target listing the production + targets that define the coverage scope. + llvm_cov: Label of the llvm-cov binary (the consumer's LLVM toolchain, + e.g. "@llvm_toolchain//:llvm-cov"). Must come from the same LLVM + major version that produced the coverage instrumentation. + llvm_profdata: Label of the llvm-profdata binary. + llvm_cxxfilt: Optional label of llvm-cxxfilt for symbol demangling + (C++ Itanium and Rust v0/legacy). toolchains_llvm exposes it as + "@llvm_toolchain_llvm//:bin/llvm-cxxfilt". + module_bazel: The consumer's root MODULE.bazel, used at runtime to + locate the real workspace root. Requires + exports_files(["MODULE.bazel"]) in the consumer's root BUILD file. + **kwargs: Common rule attributes (testonly, visibility, tags, ...). + """ + _reporter_wrapper( + name = name, + coverage_scope = coverage_scope, + module_bazel = module_bazel, + llvm_cov = llvm_cov, + llvm_profdata = llvm_profdata, + llvm_cxxfilt = llvm_cxxfilt, + **kwargs + ) diff --git a/integration_tests/.bazelrc b/integration_tests/.bazelrc new file mode 100644 index 0000000..22291f9 --- /dev/null +++ b/integration_tests/.bazelrc @@ -0,0 +1,78 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ +common --registry=https://bcr.bazel.build + +# Rust toolchain for regular builds and coverage alike: score_toolchains_rust's +# standard Ferrocene toolchain ships with llvm-cov/llvm-profdata attached +# (>= 0.10.0), which is what activates Rust coverage instrumentation. +common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu + +test --test_output=errors + +# ============================================================================ +# LLVM source-based coverage (Linux, Clang + Ferrocene) — unified Rust + C++ +# This is the CANONICAL consumer snippet: copy this block (adjusting the +# --coverage_report_generator label) into your repository's bazelrc. +# Use with: bazel coverage --config=llvm_cov //... --build_tests_only +# ============================================================================ + +coverage:llvm_cov --nocache_test_results +coverage:llvm_cov --cxxopt=-O0 +coverage:llvm_cov --combined_report=lcov +coverage:llvm_cov --experimental_fetch_all_coverage_outputs + +# Per-test .so files can carry clashing instrumentation between production +# and test code; the first object loaded wins, causing flaky coverage gaps. +# Static linking avoids this entirely. +coverage:llvm_cov --dynamic_mode=off + +coverage:llvm_cov --experimental_generate_llvm_lcov +# NOTE: --experimental_use_llvm_covmap causes Bazel to instrument ALL targets. +# Source filtering is handled at report time using the allowlist generated by +# the score_coverage_scope target. +coverage:llvm_cov --experimental_use_llvm_covmap +coverage:llvm_cov --extra_toolchains=@llvm_toolchain//:cc-toolchain-x86_64-linux + +# The merger lives in score_coverage (no consumer-specific wiring needed); the +# reporter_wrapper is instantiated in THIS repository (tools/coverage/BUILD) +# because it carries the coverage scope and LLVM tool labels. +coverage:llvm_cov --coverage_output_generator=@score_coverage//:merger +coverage:llvm_cov --coverage_report_generator=//tools/coverage:reporter_wrapper + +# Keep raw profraw files instead of converting to LCOV; the merger handles +# profraw -> profdata directly. +coverage:llvm_cov --test_env=GENERATE_LLVM_LCOV=0 +coverage:llvm_cov --test_env=COVERAGE_GCOV_PATH=/usr/bin/true + +# Continuous-mode profiling: required to cover abnormal termination (death +# tests). Needs runtime counter relocation in both languages. +coverage:llvm_cov --test_env=LLVM_PROFILE_CONTINUOUS_MODE=1 +coverage:llvm_cov --features=enable_llvm_coverage_for_death_tests +coverage:llvm_cov --@rules_rust//rust/settings:extra_rustc_flag=-Cllvm-args=-runtime-counter-relocation + +# Under the LLVM toolchain the `coverage` feature expands to +# -fprofile-instr-generate -fcoverage-mapping — exactly the covmap +# instrumentation this pipeline needs. +coverage:llvm_cov --features=coverage + +# Keep unlinked functions in the coverage map so unreferenced code shows as +# uncovered instead of vanishing. +coverage:llvm_cov --@rules_rust//rust/settings:extra_rustc_flag=-Clink-dead-code + +# Enable branch coverage instrumentation for Rust. NOTE: -Z flags are +# unstable rustc options; they work here because the Ferrocene toolchain in +# use is a rolling (nightly-based) build. If you pin a stable-channel +# Ferrocene release, drop this flag (losing the Rust branch column). +coverage:llvm_cov --@rules_rust//rust/settings:extra_rustc_flag=-Zcoverage-options=branch diff --git a/integration_tests/.bazelversion b/integration_tests/.bazelversion new file mode 100644 index 0000000..df5119e --- /dev/null +++ b/integration_tests/.bazelversion @@ -0,0 +1 @@ +8.7.0 diff --git a/integration_tests/BUILD b/integration_tests/BUILD new file mode 100644 index 0000000..047af1d --- /dev/null +++ b/integration_tests/BUILD @@ -0,0 +1,16 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# Required by score_coverage_reporter: the reporter locates the real +# workspace root at runtime through the MODULE.bazel runfile. +exports_files(["MODULE.bazel"]) diff --git a/integration_tests/MODULE.bazel b/integration_tests/MODULE.bazel new file mode 100644 index 0000000..5d2f65a --- /dev/null +++ b/integration_tests/MODULE.bazel @@ -0,0 +1,68 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# Integration test workspace for the score_coverage LLVM coverage pipeline. +# This module mirrors EXACTLY what a consumer repository has to declare — +# its MODULE.bazel, .bazelrc and tools/coverage/BUILD are the reference +# implementation for the adoption guide in //:README.md. +module(name = "coverage_integration_tests") + +bazel_dep(name = "score_coverage", version = "0.1.0") +local_path_override( + module_name = "score_coverage", + path = "..", +) + +bazel_dep(name = "platforms", version = "1.0.0") +bazel_dep(name = "rules_cc", version = "0.2.17") +bazel_dep(name = "rules_python", version = "1.8.5") +bazel_dep(name = "rules_rust", version = "0.68.2-score") + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + is_default = True, + python_version = "3.12", +) + +# ******************************************************************************* +# Coverage toolchains (only active during `bazel coverage --config=llvm_cov`) +# ******************************************************************************* + +bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) +bazel_dep(name = "toolchains_llvm", version = "1.8.0", dev_dependency = True) + +# C++ LLVM/Clang toolchain used for coverage builds (source-based covmap). +llvm = use_extension( + "@toolchains_llvm//toolchain/extensions:llvm.bzl", + "llvm", + dev_dependency = True, +) +llvm.toolchain( + cxx_standard = {"": "c++17"}, + extra_known_features = [ + # Provided by score_coverage; enables runtime counter relocation for + # continuous-mode profiling (death test coverage). + "@score_coverage//:enable_llvm_coverage_for_death_tests", + ], + llvm_version = "22.1.7", + stdlib = {"": "stdc++"}, +) +use_repo(llvm, "llvm_toolchain", "llvm_toolchain_llvm") + +# NOTE: no custom Ferrocene instance is needed for Rust coverage. The +# standard toolchains shipped by score_toolchains_rust >= 0.10.0 already +# attach llvm-cov/llvm-profdata from the ferrocene_toolchain_builder +# coverage-tools tarball (built from the same LLVM as rustc), and +# rules_rust instruments crates under `bazel coverage` whenever the +# rust_toolchain declares llvm_cov. Registration happens in .bazelrc: +# common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu diff --git a/integration_tests/MODULE.bazel.lock b/integration_tests/MODULE.bazel.lock new file mode 100644 index 0000000..91a3628 --- /dev/null +++ b/integration_tests/MODULE.bazel.lock @@ -0,0 +1,877 @@ +{ + "lockFileVersion": 24, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", + "https://bcr.bazel.build/modules/apple_support/1.17.1/MODULE.bazel": "655c922ab1209978a94ef6ca7d9d43e940cd97d9c172fb55f94d91ac53f8610b", + "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", + "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8", + "https://bcr.bazel.build/modules/apple_support/1.24.1/source.json": "cf725267cbacc5f028ef13bb77e7f2c2e0066923a4dab1025e4a0511b1ed258a", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/source.json": "ffab9254c65ba945f8369297ad97ca0dec213d3adc6e07877e23a48624a8b456", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838", + "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", + "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", + "https://bcr.bazel.build/modules/bazel_features/1.32.0/MODULE.bazel": "095d67022a58cb20f7e20e1aefecfa65257a222c18a938e2914fd257b5f1ccdc", + "https://bcr.bazel.build/modules/bazel_features/1.34.0/MODULE.bazel": "e8475ad7c8965542e0c7aac8af68eb48c4af904be3d614b6aa6274c092c2ea1e", + "https://bcr.bazel.build/modules/bazel_features/1.38.0/MODULE.bazel": "f9b8a9c890ebd216b4049fd12a31d3c2602e3403c7af636b04fbbd7453edc9c9", + "https://bcr.bazel.build/modules/bazel_features/1.38.0/source.json": "31ba776c122b54a2885e23651642e32f087a87bf025465f8040751894b571277", + "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb", + "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8", + "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/source.json": "fa7b512dfcb5eafd90ce3959cf42a2a6fe96144ebbb4b3b3928054895f2afac2", + "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/helly25_bzl/0.4.3/MODULE.bazel": "9c20052fd3f1fb767c48b78c1bbc46f501a4ca69d14558429d1234e68e449f68", + "https://bcr.bazel.build/modules/helly25_bzl/0.4.3/source.json": "e8c54d81e72633fb6f1d23c7e2d44e0b39b1caef2a256141136a2f63e6204b78", + "https://bcr.bazel.build/modules/jq.bzl/0.1.0/MODULE.bazel": "2ce69b1af49952cd4121a9c3055faa679e748ce774c7f1fda9657f936cae902f", + "https://bcr.bazel.build/modules/jq.bzl/0.1.0/source.json": "746bf13cac0860f091df5e4911d0c593971cd8796b5ad4e809b2f8e133eee3d5", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", + "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/package_metadata/0.0.2/MODULE.bazel": "fb8d25550742674d63d7b250063d4580ca530499f045d70748b1b142081ebb92", + "https://bcr.bazel.build/modules/package_metadata/0.0.2/source.json": "e53a759a72488d2c0576f57491ef2da0cf4aab05ac0997314012495935531b73", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", + "https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", + "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4", + "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", + "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", + "https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4", + "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", + "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07", + "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc", + "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", + "https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963", + "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", + "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", + "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", + "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", + "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/1.8.5/MODULE.bazel": "28b2d79ed8368d7d45b34bacc220e3c0b99cbcd9392641961b849e4c3f55dd30", + "https://bcr.bazel.build/modules/rules_python/1.8.5/source.json": "e261b03c8804f2582c9536013f987e1ea105a2b38c238aa2ac8f98fc34c8b18a", + "https://bcr.bazel.build/modules/rules_rust/0.56.0/MODULE.bazel": "3295b00757db397122092322fe1e920be7f5c9fbfb8619138977e820f2cbbbae", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.4.1/MODULE.bazel": "00e501db01bbf4e3e1dd1595959092c2fadf2087b2852d3f553b5370f5633592", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", + "https://bcr.bazel.build/modules/stardoc/0.6.2/MODULE.bazel": "7060193196395f5dd668eda046ccbeacebfd98efc77fed418dbe2b82ffaa39fd", + "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", + "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", + "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", + "https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468", + "https://bcr.bazel.build/modules/tar.bzl/0.6.0/MODULE.bazel": "a3584b4edcfafcabd9b0ef9819808f05b372957bbdff41601429d5fd0aac2e7c", + "https://bcr.bazel.build/modules/tar.bzl/0.6.0/source.json": "4a620381df075a16cb3a7ed57bd1d05f7480222394c64a20fa51bdb636fda658", + "https://bcr.bazel.build/modules/toolchains_llvm/1.8.0/MODULE.bazel": "68259b66e5fb84f94fa37125ad476c3eb8edf602a34bf58377cde9a16dd8fa98", + "https://bcr.bazel.build/modules/toolchains_llvm/1.8.0/source.json": "70d80fe5b626a3fadf812af41dda4a028640a1184f5a3c7e6cb20130d93ed783", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072", + "https://bcr.bazel.build/modules/yq.bzl/0.1.1/source.json": "2d2bad780a9f2b9195a4a370314d2c17ae95eaa745cefc2e12fbc49759b15aa3", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", + "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20210324.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20211102.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230125.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20240116.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.17.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.23.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.24.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.10.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.15.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.17.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.18.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.19.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.21.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.27.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.28.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.30.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.32.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.34.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.38.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.4.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildozer/7.1.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/google_benchmark/1.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/helly25_bzl/0.4.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jq.bzl/0.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jsoncpp/1.9.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/libpfm/4.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/package_metadata/0.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.10/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.11/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/21.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0-rc2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0-rc3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/3.19.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/pybind11_bazel/2.11.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2023-09-01/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_android/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.10/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.13/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.14/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.15/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.16/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.17/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.16/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.17/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_fuzzing/0.5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/4.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/5.3.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.10.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.12.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.3.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.3.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/4.4.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/0.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/4.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/7.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.10.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.23.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.25.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.28.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.31.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.40.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.8.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.56.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.2-score/MODULE.bazel": "37be8dee6df19d666c1d4266e1266d82012aa83bd82de38b3100fd7f641d064b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.2-score/source.json": "f88ad98dd08f296a546677e86ad42b20f61851e41a9fd3e0449971162fcaf784", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/MODULE.bazel": "14c96e378c08705a46abe0799d6236fe3095c342c34f83f8d1b3f6046ce00651", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/source.json": "1a853ab23455388d550a9aecf3d8b53ec73de50e7fe2914d9269a3c698bf3624", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/MODULE.bazel": "535296e6cdef55a506c580ca2ce541aa9ddefb354de1a24ab2bd4addc939282b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/source.json": "8bda773be264da16d2a82a03ebb737421dd4a35855f1e9a5d03d9722d84c1df5", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.6.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/tar.bzl/0.2.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/tar.bzl/0.6.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/toolchains_llvm/1.8.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/yq.bzl/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.2.11/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1/MODULE.bazel": "not found" + }, + "selectedYankedVersions": {}, + "moduleExtensions": { + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "general": { + "bzlTransitiveDigest": "03Qju4tW0vE+0RBuZGuV2A4Hx6AiSkdNahYvworx2aM=", + "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_jetbrains_kotlin_git": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", + "attributes": { + "urls": [ + "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip" + ], + "sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88" + } + }, + "com_github_jetbrains_kotlin": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", + "attributes": { + "git_repository_name": "com_github_jetbrains_kotlin_git", + "compiler_version": "1.9.23" + } + }, + "com_github_google_ksp": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", + "attributes": { + "urls": [ + "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip" + ], + "sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d", + "strip_version": "1.9.23-1.0.20" + } + }, + "com_github_pinterest_ktlint": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", + "urls": [ + "https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint" + ], + "executable": true + } + }, + "rules_android": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", + "strip_prefix": "rules_android-0.1.1", + "urls": [ + "https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_kotlin+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_python+//python/extensions:config.bzl%config": { + "general": { + "bzlTransitiveDigest": "EcMcbtKZvYmd5Mi1Fpg4EeBBztLHEE5tjO5tLDBYDuU=", + "usagesDigest": "lDbpRfhoWmZCHSaNxwZv/8fF2y0wu2th0G0f/uqX7VM=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_python_internal": { + "repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo", + "attributes": { + "transition_setting_generators": {}, + "transition_settings": [] + } + }, + "pypi__build": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl", + "sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__click": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", + "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__colorama": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", + "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__importlib_metadata": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", + "sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__installer": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl", + "sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__more_itertools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl", + "sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__packaging": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", + "sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pep517": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl", + "sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl", + "sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip_tools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl", + "sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pyproject_hooks": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl", + "sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__setuptools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl", + "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__tomli": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", + "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__wheel": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl", + "sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__zipp": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl", + "sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_python+", + "pypi__build", + "rules_python++config+pypi__build" + ], + [ + "rules_python+", + "pypi__click", + "rules_python++config+pypi__click" + ], + [ + "rules_python+", + "pypi__colorama", + "rules_python++config+pypi__colorama" + ], + [ + "rules_python+", + "pypi__importlib_metadata", + "rules_python++config+pypi__importlib_metadata" + ], + [ + "rules_python+", + "pypi__installer", + "rules_python++config+pypi__installer" + ], + [ + "rules_python+", + "pypi__more_itertools", + "rules_python++config+pypi__more_itertools" + ], + [ + "rules_python+", + "pypi__packaging", + "rules_python++config+pypi__packaging" + ], + [ + "rules_python+", + "pypi__pep517", + "rules_python++config+pypi__pep517" + ], + [ + "rules_python+", + "pypi__pip", + "rules_python++config+pypi__pip" + ], + [ + "rules_python+", + "pypi__pip_tools", + "rules_python++config+pypi__pip_tools" + ], + [ + "rules_python+", + "pypi__pyproject_hooks", + "rules_python++config+pypi__pyproject_hooks" + ], + [ + "rules_python+", + "pypi__setuptools", + "rules_python++config+pypi__setuptools" + ], + [ + "rules_python+", + "pypi__tomli", + "rules_python++config+pypi__tomli" + ], + [ + "rules_python+", + "pypi__wheel", + "rules_python++config+pypi__wheel" + ], + [ + "rules_python+", + "pypi__zipp", + "rules_python++config+pypi__zipp" + ] + ] + } + }, + "@@rules_python+//python/uv:uv.bzl%uv": { + "general": { + "bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=", + "usagesDigest": "c4BCoL7WnccEomzDYulDuOys9pd6N93KaNI4mTVbqi0=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "uv": { + "repoRuleId": "@@rules_python+//python/uv/private:uv_toolchains_repo.bzl%uv_toolchains_repo", + "attributes": { + "toolchain_type": "'@@rules_python+//python/uv:uv_toolchain_type'", + "toolchain_names": [ + "none" + ], + "toolchain_implementations": { + "none": "'@@rules_python+//python:none'" + }, + "toolchain_compatible_with": { + "none": [ + "@platforms//:incompatible" + ] + }, + "toolchain_target_settings": {} + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_python+", + "platforms", + "platforms" + ] + ] + } + }, + "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_ext": { + "general": { + "bzlTransitiveDigest": "9k8SLwP4ec9xXjFivq0Kn0FradTQhcEA+j/WsLxfVPM=", + "usagesDigest": "ANsCxJ1KIL4fXiEiWcWT5l5bMcnUM7xjtLQrHfOO6G4=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "ferrocene_x86_64_unknown_linux_gnu": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "sha256": "6fd7c7053a80463b2bfd24202de02e16959b18ed185c55b738148e9caac42eff", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "x86_64-unknown-linux-gnu", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "miri_sysroot_sha256": "143260fe3873249160d57b370717005828e129d3b6782448a32d8cc578384fe0", + "miri_sysroot_strip_prefix": "x86_64-unknown-linux-gnu" + } + }, + "ferrocene_aarch64_unknown_linux_gnu": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-linux-gnu.tar.gz", + "sha256": "06ee88a935083068325b69028e9d4e9adc696542cea9b4322642a55dafcae552", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "aarch64-unknown-linux-gnu", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc", + "-Clink-arg=-lgcc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-linux-gnu.tar.gz", + "miri_sysroot_sha256": "4059e74c79147b942eb595c14a5f998ebdb1063764ea756b4f32c21bf5903a16", + "miri_sysroot_strip_prefix": "aarch64-unknown-linux-gnu" + } + }, + "ferrocene_x86_64_pc_nto_qnx800": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-pc-nto-qnx800.tar.gz", + "sha256": "655ad0d212baf63f4dd03065735ca55cddadc86cf6bd005aeb19689e348a0023", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "x86_64-pc-nto-qnx800", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:qnx" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-pc-nto-qnx800.tar.gz", + "miri_sysroot_sha256": "9684ea089c883a0739f402165fc6aa374a69641e763957c314688779e8124931", + "miri_sysroot_strip_prefix": "x86_64-pc-nto-qnx800" + } + }, + "ferrocene_aarch64_unknown_nto_qnx800": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-nto-qnx800.tar.gz", + "sha256": "a8e80da21c6abebfb31063f3815cd3a4bbb5a1466855a12043d7c243ef152715", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "aarch64-unknown-nto-qnx800", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:qnx" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-nto-qnx800.tar.gz", + "miri_sysroot_sha256": "3077170b7384d6bcf2cf8f53db8b670d48fc7e2237285b4fd799c64e7412bdff", + "miri_sysroot_strip_prefix": "aarch64-unknown-nto-qnx800" + } + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@toolchains_llvm+//toolchain/extensions:distributions.bzl%llvm_distributions": { + "general": { + "bzlTransitiveDigest": "gCdXpBt3HBBc280OILOFheHmO7lXWXJYnPgYiTtyapI=", + "usagesDigest": "VyKGZSw79ryPJ9gt0sqBfIkA7qGOU6tnlDHd7awkb6Q=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "llvm_distributions_data": { + "repoRuleId": "@@toolchains_llvm+//toolchain/internal:distributions_repo.bzl%llvm_distributions_repo", + "attributes": { + "srcs": [ + "@@toolchains_llvm+//toolchain/distributions:pre_github.jsonc", + "@@toolchains_llvm+//toolchain/distributions:github_legacy.jsonc", + "@@toolchains_llvm+//toolchain/distributions:github.jsonc", + "@@toolchains_llvm+//toolchain/distributions:extra.jsonc" + ] + } + } + }, + "recordedRepoMappingEntries": [] + } + } + }, + "facts": {} +} diff --git a/integration_tests/run_integration_test.sh b/integration_tests/run_integration_test.sh new file mode 100755 index 0000000..5b8bf85 --- /dev/null +++ b/integration_tests/run_integration_test.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +# End-to-end test of the score_coverage LLVM coverage pipeline, run against +# this consumer-style workspace. Asserts the properties the pipeline +# guarantees: +# 1. Untested in-scope files (C++ AND Rust) appear at exact 0% in the LCOV. +# 2. The effective-coverage gate fails at threshold 100 and passes at a low +# threshold. +# 3. The justified line raises effective coverage above raw coverage. + +set -euo pipefail +cd "$(dirname "$0")" + +# In GitHub Actions GITHUB_STEP_SUMMARY is set for THIS job; unset it so the +# many generate_coverage_html invocations below don't each append to the real +# run page. The dedicated summary test sets its own target file. +unset GITHUB_STEP_SUMMARY || true + +echo "=== Running coverage build ===" +bazel coverage --config=llvm_cov //... --build_tests_only + +YAML="tools/coverage/coverage_justifications.yaml" + +echo "=== Gate must FAIL at threshold 100 (uncovered fixtures exist) ===" +if COVERAGE_THRESHOLD=100 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml "${YAML}" --archive coverage_artifacts; then + echo "ERROR: coverage gate passed at threshold 100 despite uncovered files" >&2 + exit 1 +fi +echo "OK: gate failed as expected" + +echo "=== Gate must PASS at a low threshold ===" +COVERAGE_THRESHOLD=10 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml "${YAML}" +echo "OK: gate passed as expected" + +echo "=== Without --yaml: HTML still produced, gate applies to RAW coverage ===" +if COVERAGE_THRESHOLD=100 bazel run @score_coverage//:generate_coverage_html; then + echo "ERROR: raw-coverage gate passed at threshold 100" >&2 + exit 1 +fi +COVERAGE_THRESHOLD=10 bazel run @score_coverage//:generate_coverage_html +if [[ ! -f coverage_linux/index.html ]]; then + echo "ERROR: HTML report missing after no-yaml run" >&2 + exit 1 +fi +echo "OK: no-yaml mode works (HTML produced, raw gate enforced)" + +# The following sections all run, in order. Each one deletes summary.md +# before its own generate_coverage_html invocation so a stale file from the +# previous section cannot produce a false pass — in particular, the +# failing-gate section must prove the file was RE-created by THAT run. +echo "=== --summary-md must produce a markdown job summary ===" +rm -f summary.md +COVERAGE_THRESHOLD=10 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml "${YAML}" --summary-md summary.md +for marker in "## Coverage summary" "| Lines |" "Raw vs effective" \ + "Coverage by directory" "Files at exact 0% (2)"; do + if ! grep -qF "${marker}" summary.md; then + echo "ERROR: '${marker}' missing from summary.md" >&2 + exit 1 + fi +done +grep -q "█" summary.md || { echo "ERROR: progress bars missing from summary.md" >&2; exit 1; } +echo "OK: --summary-md works" + +echo "=== Summary must still be written when the gate FAILS ===" +rm -f summary.md +if COVERAGE_THRESHOLD=100 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml "${YAML}" --summary-md summary.md; then + echo "ERROR: gate unexpectedly passed at threshold 100" >&2 + exit 1 +fi +[[ -s summary.md ]] || { echo "ERROR: summary.md missing after failing gate" >&2; exit 1; } +echo "OK: summary survives a failing gate" + +echo "=== GITHUB_STEP_SUMMARY convenience default (no flag) ===" +rm -f step_summary.md +printf '# existing content\n' > step_summary.md +GITHUB_STEP_SUMMARY="$(pwd)/step_summary.md" COVERAGE_THRESHOLD=10 \ + bazel run @score_coverage//:generate_coverage_html -- --yaml "${YAML}" +grep -qF "# existing content" step_summary.md || { echo "ERROR: append mode overwrote the step summary" >&2; exit 1; } +grep -qF "## Coverage summary" step_summary.md || { echo "ERROR: summary not appended to GITHUB_STEP_SUMMARY" >&2; exit 1; } +rm -f summary.md step_summary.md +echo "OK: GITHUB_STEP_SUMMARY convenience works" + +echo "=== --archive-dir must produce an unzipped artifacts tree ===" +COVERAGE_THRESHOLD=10 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml "${YAML}" --archive-dir artifacts_dir +for f in artifacts_dir/coverage_linux/index.html artifacts_dir/coverage_report.dat \ + artifacts_dir/justification_report/summary.txt; do + if [[ ! -f "$f" ]]; then + echo "ERROR: ${f} missing from --archive-dir output" >&2 + exit 1 + fi +done +rm -rf artifacts_dir +echo "OK: --archive-dir works" + +echo "=== Untested files must appear at exact 0% in the LCOV ===" +unzip -p coverage_artifacts.zip artifacts/coverage_report.dat > lcov.dat + +check_zero_coverage() { + local file="$1" + if ! grep -q "SF:.*${file}" lcov.dat; then + echo "ERROR: ${file} missing from LCOV (baseline mechanism broken)" >&2 + exit 1 + fi + # The record for the file must report zero lines hit. + if ! awk -v f="${file}" ' + $0 ~ "^SF:" && $0 ~ f {rec=1} + rec && /^LH:/ {print $0; exit ($0 == "LH:0") ? 0 : 1} + rec && /^end_of_record/ {exit 1}' lcov.dat; then + echo "ERROR: ${file} is present but not at 0% coverage" >&2 + exit 1 + fi + echo "OK: ${file} present at 0%" +} + +check_zero_coverage "src/uncovered.cpp" +check_zero_coverage "rust/main.rs" + +echo "=== Covered files must be present with hits ===" +grep -q "SF:.*src/coverable.cpp" lcov.dat || { echo "ERROR: coverable.cpp missing" >&2; exit 1; } +grep -q "SF:.*rust/lib.rs" lcov.dat || { echo "ERROR: lib.rs missing" >&2; exit 1; } +echo "OK" + +echo "=== Justified line must raise effective coverage above raw ===" +SUMMARY="$(unzip -p coverage_artifacts.zip artifacts/justification_report/summary.txt)" +echo "${SUMMARY}" +JUSTIFIED="$(echo "${SUMMARY}" | grep -oP 'Justified lines:\s+\K[0-9]+')" +if [[ "${JUSTIFIED}" -lt 1 ]]; then + echo "ERROR: expected at least one justified line, got ${JUSTIFIED}" >&2 + exit 1 +fi +RAW="$(echo "${SUMMARY}" | grep -oP 'Raw line coverage:\s+\K[0-9.]+')" +EFFECTIVE="$(echo "${SUMMARY}" | grep -oP 'Effective line coverage:\s+\K[0-9.]+')" +if ! awk "BEGIN {exit (${EFFECTIVE} > ${RAW}) ? 0 : 1}"; then + echo "ERROR: effective coverage ${EFFECTIVE}% not above raw ${RAW}%" >&2 + exit 1 +fi +echo "OK: effective ${EFFECTIVE}% > raw ${RAW}%" + +echo "" +echo "=== All integration checks passed ===" diff --git a/integration_tests/rust/BUILD b/integration_tests/rust/BUILD new file mode 100644 index 0000000..6d05d68 --- /dev/null +++ b/integration_tests/rust/BUILD @@ -0,0 +1,37 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") + +package(default_visibility = ["//visibility:public"]) + +rust_library( + name = "integration_lib", + srcs = ["lib.rs"], + edition = "2021", +) + +rust_test( + name = "integration_lib_test", + crate = ":integration_lib", + edition = "2021", +) + +# No test runs this binary — its main.rs must show up at exact 0% via the +# --empty-profile baseline over the coverage-built executable. +rust_binary( + name = "untested_tool", + srcs = ["main.rs"], + edition = "2021", + deps = [":integration_lib"], +) diff --git a/integration_tests/rust/lib.rs b/integration_tests/rust/lib.rs new file mode 100644 index 0000000..337ba3f --- /dev/null +++ b/integration_tests/rust/lib.rs @@ -0,0 +1,45 @@ +// ******************************************************************************* +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +/// Classifies an integer. Three branches; the unit test exercises only two so +/// the report is guaranteed to contain covered and uncovered Rust branches. +pub fn classify(value: i32) -> &'static str { + if value < 0 { + "negative" + } else if value == 0 { + "zero" + } else { + "positive" + } +} + +/// Never called by any test: must appear as uncovered (0 hits) in the report +/// thanks to -Clink-dead-code keeping it in the coverage map. +pub fn never_called(value: i32) -> i32 { + value * 2 +} + +#[cfg(test)] +mod tests { + use super::classify; + + #[test] + fn classifies_negative() { + assert_eq!(classify(-5), "negative"); + } + + #[test] + fn classifies_zero() { + assert_eq!(classify(0), "zero"); + } +} diff --git a/integration_tests/rust/main.rs b/integration_tests/rust/main.rs new file mode 100644 index 0000000..a36ecb8 --- /dev/null +++ b/integration_tests/rust/main.rs @@ -0,0 +1,19 @@ +// ******************************************************************************* +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +// This binary has no test at all: its source must appear at exactly 0% in the +// coverage report via the --empty-profile baseline over the coverage-built +// executable (rust_binary targets provide no CcInfo archive). +fn main() { + println!("{}", integration_lib::classify(1)); +} diff --git a/integration_tests/src/BUILD b/integration_tests/src/BUILD new file mode 100644 index 0000000..983b6b1 --- /dev/null +++ b/integration_tests/src/BUILD @@ -0,0 +1,37 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "coverable", + srcs = ["coverable.cpp"], + hdrs = ["coverable.h"], +) + +# No test links against this library — it must show up at exact 0% in the +# report via the --empty-profile baseline. +cc_library( + name = "uncovered", + srcs = ["uncovered.cpp"], + hdrs = ["uncovered.h"], +) + +cc_test( + name = "coverable_test", + srcs = ["coverable_test.cpp"], + deps = [":coverable"], +) diff --git a/integration_tests/src/coverable.cpp b/integration_tests/src/coverable.cpp new file mode 100644 index 0000000..f31b997 --- /dev/null +++ b/integration_tests/src/coverable.cpp @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "src/coverable.h" + +namespace coverage_integration { + +const char* classify(int value) { + if (value < 0) { + return "negative"; + } + if (value == 0) { + return "zero"; + } + return "positive"; // COV_JUSTIFIED itest-positive-branch +} + +} // namespace coverage_integration diff --git a/integration_tests/src/coverable.h b/integration_tests/src/coverable.h new file mode 100644 index 0000000..bb43b27 --- /dev/null +++ b/integration_tests/src/coverable.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef COVERAGE_INTEGRATION_TESTS_SRC_COVERABLE_H +#define COVERAGE_INTEGRATION_TESTS_SRC_COVERABLE_H + +namespace coverage_integration { + +// Classifies an integer. Three branches; the test exercises only two so the +// report is guaranteed to contain both covered and uncovered branches. +const char* classify(int value); + +} // namespace coverage_integration + +#endif // COVERAGE_INTEGRATION_TESTS_SRC_COVERABLE_H diff --git a/integration_tests/src/coverable_test.cpp b/integration_tests/src/coverable_test.cpp new file mode 100644 index 0000000..eb22a5e --- /dev/null +++ b/integration_tests/src/coverable_test.cpp @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include + +#include "src/coverable.h" + +// Deliberately exercises only the negative and zero branches; the positive +// branch stays uncovered (and justified via the COV_JUSTIFIED marker). +int main() { + using coverage_integration::classify; + if (std::strcmp(classify(-5), "negative") != 0) { + return 1; + } + if (std::strcmp(classify(0), "zero") != 0) { + return 1; + } + return 0; +} diff --git a/integration_tests/src/uncovered.cpp b/integration_tests/src/uncovered.cpp new file mode 100644 index 0000000..6eaf59d --- /dev/null +++ b/integration_tests/src/uncovered.cpp @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "src/uncovered.h" + +namespace coverage_integration { + +int never_called(int value) { + if (value > 10) { + return value * 2; + } + return value - 1; +} + +} // namespace coverage_integration diff --git a/integration_tests/src/uncovered.h b/integration_tests/src/uncovered.h new file mode 100644 index 0000000..0c5a4a2 --- /dev/null +++ b/integration_tests/src/uncovered.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef COVERAGE_INTEGRATION_TESTS_SRC_UNCOVERED_H +#define COVERAGE_INTEGRATION_TESTS_SRC_UNCOVERED_H + +namespace coverage_integration { + +// Linked into no test on purpose: this library must still appear in the +// coverage report at exactly 0% via the --empty-profile baseline mechanism. +int never_called(int value); + +} // namespace coverage_integration + +#endif // COVERAGE_INTEGRATION_TESTS_SRC_UNCOVERED_H diff --git a/integration_tests/tools/coverage/BUILD b/integration_tests/tools/coverage/BUILD new file mode 100644 index 0000000..e86d935 --- /dev/null +++ b/integration_tests/tools/coverage/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@score_coverage//:defs.bzl", "score_coverage_reporter", "score_coverage_scope") + +# The production targets whose transitive sources define the coverage scope. +# Untested targets listed here (src:uncovered, rust:untested_tool) appear in +# the report at exact 0% via the --empty-profile baseline. +score_coverage_scope( + name = "coverage_scope", + testonly = True, + visibility = ["//visibility:private"], + deps = [ + "//rust:integration_lib", + "//rust:untested_tool", + "//src:coverable", + "//src:uncovered", + ], +) + +score_coverage_reporter( + name = "reporter_wrapper", + testonly = True, + coverage_scope = ":coverage_scope", + llvm_cov = "@llvm_toolchain//:llvm-cov", + llvm_cxxfilt = "@llvm_toolchain_llvm//:bin/llvm-cxxfilt", + llvm_profdata = "@llvm_toolchain//:llvm-profdata", +) diff --git a/integration_tests/tools/coverage/coverage_justifications.yaml b/integration_tests/tools/coverage/coverage_justifications.yaml new file mode 100644 index 0000000..7dadf7c --- /dev/null +++ b/integration_tests/tools/coverage/coverage_justifications.yaml @@ -0,0 +1,25 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +# Coverage justifications for the integration test workspace. +# +# Justified lines count as covered in the EFFECTIVE coverage metric. The line +# is marked in-code with a `COV_JUSTIFIED ` comment (src/coverable.cpp); +# this file provides the id, category and reason. +version: 1 +justifications: + - id: itest-positive-branch + category: other + platforms: [linux] + reason: | + Intentionally uncovered branch: the integration test asserts that a + justified line raises effective coverage above raw coverage. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b2a265d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = ["E", "F", "W"] + +[tool.pylint.format] +max-line-length = 120 + +[tool.pylint."messages control"] +disable = [ + # aspect_rules_lint's pylint aspect action (see + # @aspect_rules_lint//lint:pylint.bzl) only ever passes a target's own + # `srcs` to pylint -- it never forwards `deps` or sets up PYTHONPATH. + # Pylint's static import resolution therefore can never see ANY + # dependency (first-party or third-party) and reports import-error on + # every non-stdlib import, regardless of whether Bazel actually + # resolves it fine at build/run time. This is a structural limitation + # of the sandboxed lint action, not a real code defect, so it can't be + # fixed in userland code -- disabling is the only option. + "import-error", + + # These duplicate checks ruff already runs (`[tool.ruff.lint] select` + # above): verified repo-wide that every finding below was reported by + # BOTH tools for the exact same file/line. Keeping both just reports the + # same diagnostic twice with no added signal, so pylint defers to ruff + # (faster, and already the formatter for this repo). + "unused-import", # == ruff F401 (unused-import) + "undefined-variable", # == ruff F821 (undefined-name) + "line-too-long", # == ruff E501 (line-too-long) + "multiple-imports", # == ruff E401 (multiple-imports-on-one-line) + "reimported", # == ruff F811 (redefined-while-unused) + "f-string-without-interpolation", # == ruff F541 (f-string-missing-placeholders) +] diff --git a/score_coverage/BUILD b/score_coverage/BUILD new file mode 100644 index 0000000..a6f9e70 --- /dev/null +++ b/score_coverage/BUILD @@ -0,0 +1,116 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@pip_score_coverage//:requirements.bzl", "requirement") +load("@rules_python//python:defs.bzl", "py_binary", "py_library") +load("@rules_python//python:pip.bzl", "compile_pip_requirements") +load("@rules_shell//shell:sh_binary.bzl", "sh_binary") + +package(default_visibility = ["//visibility:public"]) + +# ============================================================================= +# LLVM source-based coverage pipeline (C++ + Rust) — implementation package. +# Consumers use the root-level labels (//:merger, //:generate_coverage_html, +# //:defs.bzl, //:enable_llvm_coverage_for_death_tests); see //:BUILD. +# ============================================================================= + +# Per-test coverage output generator (--coverage_output_generator). Locates +# llvm-profdata via the LLVM_PROFDATA / RUST_LLVM_PROFDATA environment +# variables exported by the toolchains, so it needs no consumer-supplied labels. +py_binary( + name = "merger", + srcs = ["merger.py"], +) + +# Final report generator. Not referenced directly by consumers: the +# score_coverage_reporter macro wraps it with the consumer's coverage scope, +# workspace root and LLVM tool labels. +py_binary( + name = "reporter", + srcs = ["reporter.py"], + deps = ["@rules_python//python/runfiles"], +) + +py_binary( + name = "justify", + srcs = ["justify.py"], + deps = [requirement("pyyaml")], +) + +py_binary( + name = "effective_coverage", + srcs = ["effective_coverage.py"], +) + +# Markdown job summary (GITHUB_STEP_SUMMARY / --summary-md), invoked by +# generate_coverage_html.sh. Stdlib only. +py_binary( + name = "coverage_summary", + srcs = ["coverage_summary.py"], +) + +sh_binary( + name = "generate_coverage_html", + srcs = ["generate_coverage_html.sh"], +) + +# Import-only libraries so unit tests can `from score_coverage. import ...`. +py_library( + name = "merger_lib", + srcs = ["merger.py"], + imports = [".."], +) + +py_library( + name = "reporter_lib", + srcs = ["reporter.py"], + imports = [".."], + deps = ["@rules_python//python/runfiles"], +) + +py_library( + name = "justify_lib", + srcs = ["justify.py"], + imports = [".."], + deps = [requirement("pyyaml")], +) + +py_library( + name = "effective_coverage_lib", + srcs = ["effective_coverage.py"], + imports = [".."], +) + +py_library( + name = "coverage_summary_lib", + srcs = ["coverage_summary.py"], + imports = [".."], +) + +# In order to update the requirements, change the `requirements.in` file and run: +# `bazel run //score_coverage:requirements_3_XX.update --@@rules_python+//python/config_settings:python_version=3.XX`. +[ + compile_pip_requirements( + name = "requirements_3_{}".format(version), + src = "requirements.in", + python_version = "3.{}".format(version), + requirements_txt = "requirements_3_{}.txt".format(version), + tags = [ + "manual", + ], + ) + for version in [ + "11", + "12", + ] +] diff --git a/score_coverage/coverage_scope.bzl b/score_coverage/coverage_scope.bzl new file mode 100644 index 0000000..acb722b --- /dev/null +++ b/score_coverage/coverage_scope.bzl @@ -0,0 +1,202 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +""" +Coverage scope rule for deriving file-level allowlists from implementation targets. + +This rule uses an aspect to traverse the build graph starting from the listed +implementation targets (cc_library, rust_library, rust_binary) through their +transitive deps. At each node it collects the actual source files. + +- cc_library (and rust_library, which also provides CcInfo in rules_rust + 0.68.x): source files come from the srcs/hdrs attributes; static archives + (including the rlib exposed as a .a symlink) are collected for baseline + (zero-coverage) reporting. +- rust_binary (no CcInfo): source files come from CrateInfo.srcs; the + coverage-built executable itself serves as the baseline object. + +The resulting allowlist contains one source file path per line (relative to the +workspace root). The coverage reporter uses this to restrict reports to exactly +the files that are part of the covered implementation. +""" + +load("@rules_rust//rust:rust_common.bzl", "CrateInfo") + +# ============================================================================= +# Provider to carry collected source file paths through the aspect +# ============================================================================= + +_CoverageScopeInfo = provider( + doc = "Carries source file paths and object files collected by the coverage scope aspect.", + fields = { + "source_files": "Depset of source file path strings (workspace-relative).", + "object_files": "Depset of compiled archive/executable File objects for baseline coverage.", + }, +) + +# ============================================================================= +# Aspect: traverses library/binary deps to collect files +# ============================================================================= + +def _coverage_scope_aspect_impl(target, ctx): + """Collects source file paths and archive files from the build graph.""" + direct_files = [] + direct_archives = [] + transitive = [] + transitive_archives = [] + + # At cc_library / rust_library targets (rust_library provides CcInfo with + # its rlib exposed as a .a symlink): collect srcs, hdrs, and static archive + if CcInfo in target: + for attr_name in ["srcs", "hdrs"]: + if hasattr(ctx.rule.attr, attr_name): + for src in getattr(ctx.rule.attr, attr_name): + for f in src.files.to_list(): + if not f.path.startswith("external/") and f.is_source: + direct_files.append(f.short_path) + + # Only collect workspace-internal labels and archives + if not str(target.label).startswith("@@") or str(target.label).startswith("@@//"): + # Collect .a archive files for baseline coverage. + for linker_input in target[CcInfo].linking_context.linker_inputs.to_list(): + for lib in linker_input.libraries: + for archive in [lib.static_library, lib.pic_static_library]: + if archive and "/external/" not in archive.path and not archive.path.startswith("external/"): + direct_archives.append(archive) + break + elif CrateInfo in target: + # rust_binary: no CcInfo, collect .rs sources from CrateInfo and use the + # coverage-built executable as the baseline object. + for f in target[CrateInfo].srcs.to_list(): + if not f.path.startswith("external/") and f.is_source: + direct_files.append(f.short_path) + out = target[CrateInfo].output + if out and "/external/" not in out.path and not out.path.startswith("external/"): + direct_archives.append(out) + + # Propagate from children traversed by the aspect + for attr_name in ["components", "implementation", "deps", "implementation_deps", "exported_deps"]: + if hasattr(ctx.rule.attr, attr_name): + for dep in getattr(ctx.rule.attr, attr_name): + if _CoverageScopeInfo in dep: + transitive.append(dep[_CoverageScopeInfo].source_files) + transitive_archives.append(dep[_CoverageScopeInfo].object_files) + + return [_CoverageScopeInfo( + source_files = depset(direct_files, transitive = transitive), + object_files = depset(direct_archives, transitive = transitive_archives), + )] + +_coverage_scope_aspect = aspect( + implementation = _coverage_scope_aspect_impl, + attr_aspects = ["components", "implementation", "deps", "implementation_deps", "exported_deps"], + doc = "Traverses cc_library/rust_library/rust_binary hierarchy to collect implementation source files.", +) + +# ============================================================================= +# Rule: aggregates aspect results into an allowlist file +# ============================================================================= + +def _coverage_scope_impl(ctx): + """Aggregates source file paths from all deps and writes allowlist + baseline objects.""" + all_files = {} + all_objects = [] + + for dep in ctx.attr.deps: + if _CoverageScopeInfo in dep: + for path in dep[_CoverageScopeInfo].source_files.to_list(): + if path: + all_files[path] = True + all_objects.append(dep[_CoverageScopeInfo].object_files) + + sorted_files = sorted(all_files.keys()) + object_depset = depset(transitive = all_objects) + + # Write the allowlist file + output = ctx.actions.declare_file(ctx.attr.name + "_allowlist.txt") + ctx.actions.write( + output = output, + content = "\n".join(sorted_files) + "\n" if sorted_files else "", + ) + + # Write archive file paths for baseline coverage (reporter uses these as --object args) + archive_paths = sorted(set([f.short_path for f in object_depset.to_list()])) + objects_output = ctx.actions.declare_file(ctx.attr.name + "_objects.txt") + ctx.actions.write( + output = objects_output, + content = "\n".join(archive_paths) + "\n" if archive_paths else "", + ) + + return [ + DefaultInfo(files = depset([output, objects_output], transitive = [object_depset])), + OutputGroupInfo( + allowlist = depset([output]), + objects = depset([objects_output]), + object_files = object_depset, + ), + ] + +def _coverage_transition_impl(settings, attr): + # This dictionary modifies the build configuration + return { + "//command_line_option:collect_code_coverage": True, + } + +# Define the transition +coverage_transition = transition( + implementation = _coverage_transition_impl, + inputs = [], + outputs = ["//command_line_option:collect_code_coverage"], +) + +def _coverage_wrapper_impl(ctx): + # Forward the executable or providers from the underlying target + actual_target = ctx.attr.actual[0] + return [actual_target[DefaultInfo]] + +# Define a rule that applies the transition to its 'actual' dependency +coverage_wrapper = rule( + implementation = _coverage_wrapper_impl, + attrs = { + "actual": attr.label( + mandatory = True, + cfg = coverage_transition, # Applying the transition here + ), + # Mandatory attribute needed when a rule uses a transition + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, +) + +coverage_scope = rule( + implementation = _coverage_scope_impl, + doc = """Generates a file-level coverage allowlist from implementation targets. + + Uses an aspect to traverse the listed targets (cc_library, rust_library, + rust_binary) and their transitive deps, collecting all source files + (srcs + hdrs / CrateInfo.srcs). Outputs a text file with one + workspace-relative file path per line. + + This allowlist is consumed by the coverage reporter to restrict coverage + reporting to exactly the source files that are part of the implementation. + """, + attrs = { + "deps": attr.label_list( + mandatory = True, + aspects = [_coverage_scope_aspect], + cfg = coverage_transition, + doc = "Implementation targets whose transitive deps define the coverage scope.", + ), + }, +) diff --git a/score_coverage/coverage_summary.py b/score_coverage/coverage_summary.py new file mode 100644 index 0000000..923a0f4 --- /dev/null +++ b/score_coverage/coverage_summary.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Render a markdown coverage summary from the pipeline's LCOV output. + +Invoked by generate_coverage_html.sh to produce a human-readable summary for +GitHub job summary pages (GITHUB_STEP_SUMMARY) or an arbitrary markdown file +(--summary-md). Standard library only. + +Inputs: + --lcov LCOV trace produced by the coverage reporter + (includes exact-0% baseline records). + --justification-report Optional report.json from effective_coverage.py + (raw vs effective metrics, justified counts). + --output Markdown destination. + --append Append to --output instead of overwriting + (GITHUB_STEP_SUMMARY convention). +""" + +import argparse +import json +import sys +from pathlib import Path +from typing import Dict, List, Optional + +BAR_WIDTH = 10 +LEAST_COVERED_LIMIT = 15 + + +class FileCoverage: + """Line/branch counters for one SF record.""" + + def __init__(self, path: str) -> None: + self.path = path + self.lines_found = 0 + self.lines_hit = 0 + # None means "no branch data in the record" (rendered as an em dash). + self.branches_found: Optional[int] = None + self.branches_hit: Optional[int] = None + + @property + def line_pct(self) -> Optional[float]: + return percent(self.lines_hit, self.lines_found) + + +def percent(hit: int, total: int) -> Optional[float]: + """Percentage, or None when the denominator is zero.""" + if total <= 0: + return None + return 100.0 * hit / total + + +def fmt_pct(pct: Optional[float]) -> str: + return "—" if pct is None else f"{pct:.2f}%" + + +def progress_bar(pct: Optional[float], width: int = BAR_WIDTH) -> str: + """Render a COVERAGE percentage as an inline-code bar, e.g. `███████░░░`. + + Purely a visual aid next to the numeric cell so table rows can be + compared at a glance on the run page — it shows how much of the code is + covered, nothing else. + """ + if pct is None: + return "—" + filled = int(round(pct / 100.0 * width)) + filled = max(0, min(width, filled)) + return "`" + "█" * filled + "░" * (width - filled) + "`" + + +def escape_cell(text: str) -> str: + """Make a path safe inside a markdown table cell.""" + return text.replace("|", "\\|") + + +def parse_lcov(path: Path) -> Optional[List[FileCoverage]]: + """Parse an LCOV trace into per-file counters. + + Returns None when the file does not exist; an empty list when it exists + but contains no records. Branch data prefers BRF/BRH sums and falls back + to counting BRDA entries (taken > 0 counts as hit). + """ + if not path.is_file(): + return None + + files: List[FileCoverage] = [] + current: Optional[FileCoverage] = None + brf = brh = 0 + brda_total = brda_hit = 0 + saw_brf = False + + def flush() -> None: + nonlocal current, brf, brh, brda_total, brda_hit, saw_brf + if current is not None: + if saw_brf: + current.branches_found = brf + current.branches_hit = brh + elif brda_total > 0: + current.branches_found = brda_total + current.branches_hit = brda_hit + files.append(current) + current = None + brf = brh = 0 + brda_total = brda_hit = 0 + saw_brf = False + + # errors="replace" keeps non-UTF8 bytes in paths from crashing the parse. + with open(path, encoding="utf-8", errors="replace") as f: + for raw_line in f: + line = raw_line.strip() + if line.startswith("SF:"): + flush() + current = FileCoverage(line[3:]) + elif current is None: + continue + elif line.startswith("LF:"): + current.lines_found += _int_suffix(line) + elif line.startswith("LH:"): + current.lines_hit += _int_suffix(line) + elif line.startswith("BRF:"): + saw_brf = True + brf += _int_suffix(line) + elif line.startswith("BRH:"): + saw_brf = True + brh += _int_suffix(line) + elif line.startswith("BRDA:"): + # BRDA:,,, — "-" means never + # evaluated, any positive count means taken. + brda_total += 1 + taken = line.rsplit(",", 1)[-1] + if taken not in ("-", "0"): + brda_hit += 1 + elif line == "end_of_record": + flush() + flush() + return files + + +def _int_suffix(line: str) -> int: + try: + return int(line.split(":", 1)[1]) + except (IndexError, ValueError): + return 0 + + +def load_justification_summary(path: Path) -> Optional[Dict]: + """Load the summary block of effective_coverage.py's report.json.""" + try: + with open(path, encoding="utf-8") as f: + report = json.load(f) + except (OSError, json.JSONDecodeError) as e: + print(f"WARNING: could not read justification report {path}: {e}", file=sys.stderr) + return None + summary = report.get("summary") + if not isinstance(summary, dict): + return None + summary = dict(summary) + applied = report.get("applied_justifications") + summary["applied_justification_count"] = len(applied) if isinstance(applied, list) else 0 + return summary + + +def directory_key(path: str) -> str: + """Group by the first one or two path segments (generic, layout-agnostic).""" + parts = path.split("/") + if len(parts) <= 1: + return "(root)" + if len(parts) == 2: + return parts[0] + return "/".join(parts[:2]) + + +def rollup_by_directory(files: List[FileCoverage]) -> List[Dict]: + groups: Dict[str, Dict] = {} + for fc in files: + g = groups.setdefault( + directory_key(fc.path), + {"lines_found": 0, "lines_hit": 0, "files": 0}, + ) + g["lines_found"] += fc.lines_found + g["lines_hit"] += fc.lines_hit + g["files"] += 1 + rows = [ + { + "directory": name, + "pct": percent(g["lines_hit"], g["lines_found"]), + **g, + } + for name, g in groups.items() + ] + # Worst first; groups without countable lines sink to the end. + rows.sort(key=lambda r: (r["pct"] is None, r["pct"], r["directory"])) + return rows + + +def render_markdown(files: List[FileCoverage], justification: Optional[Dict]) -> str: + out: List[str] = ["## Coverage summary", ""] + + if not files: + out.append("_No coverage records found in the LCOV report._") + out.append("") + return "\n".join(out) + + total_lf = sum(f.lines_found for f in files) + total_lh = sum(f.lines_hit for f in files) + branch_files = [f for f in files if f.branches_found is not None] + total_brf = sum(f.branches_found for f in branch_files) if branch_files else 0 + total_brh = sum(f.branches_hit for f in branch_files) if branch_files else 0 + touched = [f for f in files if f.lines_hit > 0] + zero = [f for f in files if f.lines_found > 0 and f.lines_hit == 0] + + line_pct = percent(total_lh, total_lf) + branch_pct = percent(total_brh, total_brf) if branch_files else None + touched_pct = percent(len(touched), len(files)) + + out.append("| Metric | Covered | Total | % | |") + out.append("|---|---:|---:|---:|---|") + out.append(f"| Lines | {total_lh} | {total_lf} | {fmt_pct(line_pct)} | {progress_bar(line_pct)} |") + out.append( + f"| Branches | {total_brh if branch_files else '—'} | " + f"{total_brf if branch_files else '—'} | {fmt_pct(branch_pct)} | {progress_bar(branch_pct)} |" + ) + out.append( + f"| Files with coverage | {len(touched)} | {len(files)} | " + f"{fmt_pct(touched_pct)} | {progress_bar(touched_pct)} |" + ) + out.append(f"| Files at exact 0% | {len(zero)} | {len(files)} | | |") + out.append("") + + if justification is not None: + out.append("### Raw vs effective (justifications applied)") + out.append("") + out.append("| Metric | Raw | Effective |") + out.append("|---|---:|---:|") + out.append( + f"| Line coverage | {justification.get('raw_line_coverage_pct', 0)}% " + f"| {justification.get('effective_line_coverage_pct', 0)}% |" + ) + out.append( + f"| Branch coverage | {justification.get('raw_branch_coverage_pct', 0)}% " + f"| {justification.get('effective_branch_coverage_pct', 0)}% |" + ) + out.append("") + out.append( + f"Justified: {justification.get('justified_lines', 0)} lines, " + f"{justification.get('justified_branches', 0)} branches " + f"({justification.get('applied_justification_count', 0)} justification entries applied, " + f"{justification.get('stale_justifications', 0)} stale)." + ) + out.append("") + + out.append("### Coverage by directory (worst first)") + out.append("") + out.append("| Directory | Files | Lines hit/total | % | |") + out.append("|---|---:|---:|---:|---|") + for row in rollup_by_directory(files): + out.append( + f"| {escape_cell(row['directory'])} | {row['files']} " + f"| {row['lines_hit']}/{row['lines_found']} " + f"| {fmt_pct(row['pct'])} | {progress_bar(row['pct'])} |" + ) + out.append("") + + least = sorted( + (f for f in touched if f.lines_hit < f.lines_found), + key=lambda f: (f.line_pct is None, f.line_pct, f.path), + )[:LEAST_COVERED_LIMIT] + if least: + out.append("
") + out.append(f"Least-covered files with coverage (top {len(least)})") + out.append("") + out.append("| File | Lines hit/total | % | |") + out.append("|---|---:|---:|---|") + for fc in least: + out.append( + f"| {escape_cell(fc.path)} | {fc.lines_hit}/{fc.lines_found} " + f"| {fmt_pct(fc.line_pct)} | {progress_bar(fc.line_pct)} |" + ) + out.append("") + out.append("
") + out.append("") + + if zero: + out.append("
") + out.append(f"Files at exact 0% ({len(zero)})") + out.append("") + for fc in sorted(zero, key=lambda f: f.path): + out.append(f"- `{fc.path}` ({fc.lines_found} lines)") + out.append("") + out.append("
") + out.append("") + + out.append("_Full per-line HTML report: download the coverage artifact of this run._") + out.append("") + return "\n".join(out) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Markdown coverage summary from LCOV") + parser.add_argument("--lcov", type=Path, required=True) + parser.add_argument("--justification-report", type=Path, default=None) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--append", action="store_true") + args = parser.parse_args() + + files = parse_lcov(args.lcov) + if files is None: + print(f"WARNING: LCOV file not found: {args.lcov}", file=sys.stderr) + files = [] + + justification = None + if args.justification_report is not None: + justification = load_justification_summary(args.justification_report) + + markdown = render_markdown(files, justification) + + args.output.parent.mkdir(parents=True, exist_ok=True) + mode = "a" if args.append else "w" + with open(args.output, mode, encoding="utf-8") as f: + f.write(markdown) + print( + f"INFO: coverage summary {'appended to' if args.append else 'written to'} {args.output}", + file=sys.stderr, + ) + + +if __name__ == "__main__": + main() diff --git a/score_coverage/effective_coverage.py b/score_coverage/effective_coverage.py new file mode 100644 index 0000000..1267939 --- /dev/null +++ b/score_coverage/effective_coverage.py @@ -0,0 +1,1180 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Effective coverage calculator and HTML post-processor. + +Takes an HTML coverage report (llvm-cov or gcovr) and the resolved justification +manifest. Modifies the HTML to show justified lines in a distinct color (yellow/orange) +and calculates effective coverage metrics. + +Supports two HTML formats: + - llvm-cov: produced by our custom reporter (Linux) + - gcovr: produced by lcov_to_html.py via gcovr (QNX) + +Usage: + python effective_coverage.py --html-dir --manifest --output [--lcov ] +""" + +import argparse +import json +import math +import os +import re +import sys +from pathlib import Path +from typing import Any, Dict, List, Tuple + + +# Pattern to match a table row in llvm-cov HTML source pages +# Format: ......... +LINE_NUMBER_RE = re.compile(r"") +COVERED_LINE_TD_RE = re.compile(r"") + + +def floor_two_decimals(value: float) -> float: + """Floor a percentage value to two decimal places.""" + return math.floor(value * 100.0) / 100.0 + + +def main() -> None: + """Main entry point.""" + args = parse_args() + + # Load the justification manifest + manifest = load_manifest(args.manifest) + justified_files = manifest.get("justified_files", {}) + + # Find all source HTML files in the report + html_dir = args.html_dir + if not html_dir.exists(): + print(f"ERROR: HTML report directory not found: {html_dir}", file=sys.stderr) + sys.exit(1) + + # Detect HTML format: llvm-cov uses style.css, gcovr uses index...html naming. + html_format = detect_html_format(html_dir) + + if html_format == "gcovr": + _main_gcovr(args, html_dir, justified_files) + else: + _main_llvm_cov(args, html_dir, justified_files) + + +def _main_llvm_cov(args: argparse.Namespace, html_dir: Path, justified_files: Dict) -> None: + """Main logic for llvm-cov HTML format.""" + + # Parse raw coverage totals from the index page (matches llvm-cov exactly). + totals = parse_index_page_totals(html_dir) + raw_covered, raw_total = totals["lines"] + raw_branch_covered, raw_branch_total = totals["branches"] + + # Process each source HTML file (restyle justified lines + count them) + total_justified = 0 + total_stale = 0 + total_justified_branches = 0 + applied_justifications: List[Dict[str, Any]] = [] + stale_justifications: List[Dict[str, Any]] = [] + # Track per-file justification counts for index page updates + per_file_stats: Dict[str, Dict[str, int]] = {} + + source_html_files = find_source_html_files(html_dir) + for html_file in source_html_files: + rel_source_path = extract_source_path_from_html(html_file, html_dir) + if not rel_source_path: + continue + + file_justifications = find_matching_justifications(rel_source_path, justified_files) + + file_stats = process_html_file(html_file, file_justifications, applied_justifications, stale_justifications) + + total_justified += file_stats["justified"] + total_stale += file_stats["stale"] + total_justified_branches += file_stats["justified_branches"] + + if file_stats["justified"] > 0 or file_stats["justified_branches"] > 0: + per_file_stats[rel_source_path] = file_stats + + # Calculate stats using llvm-cov's exact numbers + raw_uncovered = raw_total - raw_covered + unjustified_uncovered = raw_uncovered - total_justified + + effective_branch_covered = raw_branch_covered + total_justified_branches + + stats = { + "total_instrumented_lines": raw_total, + "covered_lines": raw_covered, + "justified_lines": total_justified, + "unjustified_uncovered_lines": max(0, unjustified_uncovered), + "stale_justifications": total_stale, + "raw_line_coverage_pct": floor_two_decimals(100.0 * raw_covered / raw_total) if raw_total > 0 else 0.0, + "effective_line_coverage_pct": floor_two_decimals(100.0 * (raw_covered + total_justified) / raw_total) + if raw_total > 0 + else 0.0, + "total_branches": raw_branch_total, + "covered_branches": raw_branch_covered, + "justified_branches": total_justified_branches, + "raw_branch_coverage_pct": floor_two_decimals(100.0 * raw_branch_covered / raw_branch_total) + if raw_branch_total > 0 + else 0.0, + "effective_branch_coverage_pct": floor_two_decimals(100.0 * effective_branch_covered / raw_branch_total) + if raw_branch_total > 0 + else 0.0, + } + + # Inject CSS for justified lines into style.css + inject_justified_css(html_dir) + + # Update the index page with effective coverage info and per-file stats + update_index_page(html_dir, stats, per_file_stats) + + # Write output report + report = { + "version": 1, + "summary": stats, + "applied_justifications": applied_justifications, + "stale_justifications": stale_justifications, + } + + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w", encoding="utf-8") as f: + json.dump(report, f, indent=2) + + # Write human-readable summary + summary_path = output_path.parent / "summary.txt" + write_summary(summary_path, stats, stale_justifications) + + # Print summary + print( + f"INFO: Effective line coverage: {stats['effective_line_coverage_pct']}% " + f"(raw: {stats['raw_line_coverage_pct']}%, " + f"justified: {stats['justified_lines']} lines, " + f"unjustified uncovered: {stats['unjustified_uncovered_lines']} lines)", + file=sys.stderr, + ) + if stats["justified_branches"] > 0: + print( + f"INFO: Effective branch coverage: {stats['effective_branch_coverage_pct']}% " + f"(raw: {stats['raw_branch_coverage_pct']}%, " + f"justified: {stats['justified_branches']} branches)", + file=sys.stderr, + ) + if stale_justifications: + print( + f"WARNING: {len(stale_justifications)} stale justifications " + f"(lines are actually covered, justification can be removed)", + file=sys.stderr, + ) + + +def process_html_file( + html_file: Path, + justifications: Dict[int, Dict[str, str]], + applied_justifications: List[Dict[str, Any]], + stale_justifications: List[Dict[str, Any]], +) -> Dict[str, int]: + """Process a single source HTML file. Modifies it in-place. + + Restyles justified lines: changes the count cell to show "J" with justified-line + class, and changes red code regions to justified (orange) background. + Also restyles uncovered branches on justified lines. + Only counts justified/stale lines for the justification report — raw coverage + numbers are taken from the index page to match llvm-cov exactly. + """ + file_stats = { + "justified": 0, + "stale": 0, + "justified_branches": 0, + } + + with open(html_file, "r", encoding="utf-8") as f: + content = f.read() + + if not justifications: + return file_stats + + # Determine effective line status (covered if ANY instantiation covers it) + row_pattern = re.compile( + r"
\d+
" + r"" + ) + line_effective_status: Dict[int, str] = {} + for m in row_pattern.finditer(content): + line_num = int(m.group(1)) + line_class = m.group(2) + if line_class == "covered-line": + line_effective_status[line_num] = "covered" + elif line_class == "uncovered-line": + if line_num not in line_effective_status: + line_effective_status[line_num] = "uncovered" + + # Determine which lines have truly uncovered branches (never covered in any instantiation). + # A branch direction is "truly uncovered" if no instantiation covers it. + branch_check_pattern = re.compile( + r"Branch \(" + r"(\d+:\d+)\):\s*\[(.*?)\]" + ) + covered_branch_dirs_check: Dict[str, set] = {} # branch_id → set of covered directions + uncovered_branch_dirs_check: Dict[str, set] = {} # branch_id → set of uncovered directions + branch_line_map: Dict[str, int] = {} # branch_id → line_num + + for m in branch_check_pattern.finditer(content): + line_num = int(m.group(1)) + branch_id = m.group(2) + branch_content = m.group(3) + branch_line_map[branch_id] = line_num + if branch_id not in covered_branch_dirs_check: + covered_branch_dirs_check[branch_id] = set() + uncovered_branch_dirs_check[branch_id] = set() + for direction in ("True", "False"): + if f"class='None'>{direction}" in branch_content: + covered_branch_dirs_check[branch_id].add(direction) + if f"class='red branch'>{direction}" in branch_content: + uncovered_branch_dirs_check[branch_id].add(direction) + + # Lines with truly uncovered branches (uncovered in ALL instantiations) + lines_with_uncovered_branches: set = set() + for branch_id, uncov_dirs in uncovered_branch_dirs_check.items(): + cov_dirs = covered_branch_dirs_check.get(branch_id, set()) + truly_uncovered = uncov_dirs - cov_dirs + if truly_uncovered: + lines_with_uncovered_branches.add(branch_line_map[branch_id]) + + # Determine which justified lines are stale vs applicable. + # A justification is stale only if the line is covered AND has no uncovered branches. + for line_num, justification in justifications.items(): + status = line_effective_status.get(line_num) + has_uncovered_branches = line_num in lines_with_uncovered_branches + if status == "covered" and not has_uncovered_branches: + file_stats["stale"] += 1 + stale_justifications.append( + { + "file": html_file.stem, + "line": line_num, + "id": justification.get("id", ""), + "reason": "Line is already covered and has no uncovered branches — justification is stale", + } + ) + elif status == "uncovered": + file_stats["justified"] += 1 + applied_justifications.append( + { + "file": html_file.stem, + "line": line_num, + "id": justification.get("id", ""), + "category": justification.get("category", ""), + } + ) + elif status == "covered" and has_uncovered_branches: + # Line is covered but has uncovered branches — justification applies to branches only + applied_justifications.append( + { + "file": html_file.stem, + "line": line_num, + "id": justification.get("id", ""), + "category": justification.get("category", ""), + } + ) + + # Restyle justified lines in the HTML (all occurrences including instantiations). + # Full row pattern to capture and replace the entire row: + # ...
0
...
... + full_row_pattern = re.compile( + r"(
\d+
)" + r"(
)\d+(
)" + r"(
)(.*?)(
)" + ) + + modified = False + + def replace_full_row(match: re.Match) -> str: + nonlocal modified + line_num = int(match.group(2)) + if line_num not in justifications: + return match.group(0) + + justification = justifications[line_num] + reason = justification.get("reason", "").replace("'", "'").replace('"', """) + jid = justification.get("id", "") + tooltip = f"Justified [{jid}]: {reason}" + modified = True + + # Rebuild the row with justified styling: + # 1. Line number td (unchanged) + line_td = match.group(1) + # 2. Count td: change class and show "J" instead of "0" + count_td = f"
J{match.group(4)}"
+        # 3. Code td: replace 'region red' spans with 'region justified'
+        code_start = match.group(5)
+        code_content = match.group(6).replace("class='region red'", "class='region justified'")
+        code_end = match.group(7)
+
+        return line_td + count_td + code_start + code_content + code_end
+
+    new_content = full_row_pattern.sub(replace_full_row, content)
+
+    # Restyle branches on justified lines.
+    # Branch format in expansion-view:
+    # Branch (195:17):
+    #   [True: 0, ...]
+    # We find branches at justified line numbers and restyle red branch → justified branch
+    # Counting: A branch direction is "uncovered" only if ALL instantiations show it as red.
+    # (Same as llvm-cov's logic: covered if ANY instantiation covers it.)
+    branch_pattern = re.compile(
+        r"(Branch \("
+        r"(\d+:\d+)\):\s*\[)(.*?\])"
+    )
+
+    # First pass: determine which branch directions are covered in any instantiation
+    covered_branch_dirs: set = set()  # (line:col, direction) that are covered somewhere
+    for m in branch_pattern.finditer(new_content):
+        line_num = int(m.group(2))
+        if line_num not in justifications:
+            continue
+        branch_id = m.group(3)
+        branch_content = m.group(4)
+        # A direction is covered if it does NOT have 'red branch' class
+        for direction in ("True", "False"):
+            # Check if this direction appears as covered (class='None' means covered)
+            covered_marker = f"class='None'>{direction}"
+            if covered_marker in branch_content:
+                covered_branch_dirs.add((branch_id, direction))
+
+    # Second pass: restyle and count only truly uncovered branch directions
+    justified_branch_ids: set = set()  # Track unique uncovered (line:col, direction) pairs
+
+    def replace_branch(match: re.Match) -> str:
+        nonlocal modified
+        line_num = int(match.group(2))
+        if line_num not in justifications:
+            return match.group(0)
+
+        branch_content = match.group(4)
+        if "class='red branch'" not in branch_content:
+            return match.group(0)
+
+        modified = True
+        branch_id = match.group(3)  # e.g. "68:13"
+
+        # Count unique uncovered branch directions that are NEVER covered in any instantiation
+        for direction in ("True", "False"):
+            if f"class='red branch'>{direction}" in branch_content:
+                uid = (branch_id, direction)
+                if uid not in covered_branch_dirs and uid not in justified_branch_ids:
+                    justified_branch_ids.add(uid)
+                    file_stats["justified_branches"] += 1
+
+        # Restyle: red branch → justified-branch, uncovered-line → justified-line
+        branch_content = branch_content.replace("class='red branch'", "class='justified-branch'")
+        branch_content = branch_content.replace("class='uncovered-line'", "class='justified-line'")
+        return match.group(1) + branch_content
+
+    new_content = branch_pattern.sub(replace_branch, new_content)
+
+    if modified:
+        with open(html_file, "w", encoding="utf-8") as f:
+            f.write(new_content)
+
+    return file_stats
+
+
+def parse_index_page_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]:
+    """Parse the TOTALS row from the llvm-cov index.html to get exact coverage numbers.
+
+    Returns dict with 'lines' and 'branches' keys, each (covered, total).
+    The index page TOTALS row format: "93.55% (17565/18777)" — func, line, branch.
+    """
+    index_file = html_dir / "index.html"
+    if not index_file.exists():
+        return {"lines": (0, 0), "branches": (0, 0)}
+
+    with open(index_file, "r", encoding="utf-8") as f:
+        content = f.read()
+
+    pct_pattern = re.compile(r"(\d+\.\d+)%\s*\((\d+)/(\d+)\)")
+    matches = pct_pattern.findall(content)
+
+    result = {"lines": (0, 0), "branches": (0, 0)}
+
+    if len(matches) >= 3:
+        # Last 3 matches are from TOTALS row: func, line, branch
+        totals_matches = matches[-3:]
+        _, line_covered, line_total = totals_matches[1]
+        result["lines"] = (int(line_covered), int(line_total))
+        _, branch_covered, branch_total = totals_matches[2]
+        result["branches"] = (int(branch_covered), int(branch_total))
+
+    if result["lines"] == (0, 0):
+        print("WARNING: Could not parse coverage totals from index.html", file=sys.stderr)
+
+    return result
+
+
+def inject_justified_css(html_dir: Path) -> None:
+    """Add CSS for justified lines to style.css."""
+    style_file = html_dir / "style.css"
+    if not style_file.exists():
+        return
+
+    justified_css = """
+/* Coverage justification styling */
+.justified-line {
+  text-align: right;
+  color: #a60;
+}
+.region.justified {
+  background-color: #fa04;
+}
+.justified-branch {
+  color: #a60;
+  font-weight: bold;
+}
+tr:has(> td.justified-line) > td.code {
+  background-color: #fff3e0;
+}
+@media (prefers-color-scheme: dark) {
+  .justified-line {
+    color: #fa0;
+  }
+  .justified-branch {
+    color: #fa0;
+  }
+  tr:has(> td.justified-line) > td.code {
+    background-color: #3d2800;
+  }
+  .region.justified {
+    background-color: #fa03;
+  }
+}
+"""
+
+    with open(style_file, "a", encoding="utf-8") as f:
+        f.write(justified_css)
+
+
+def update_index_page(html_dir: Path, stats: Dict[str, Any], per_file_stats: Dict[str, Dict[str, int]]) -> None:
+    """Update the index page with effective coverage info and per-file adjusted percentages."""
+    index_file = html_dir / "index.html"
+    if not index_file.exists():
+        return
+
+    with open(index_file, "r", encoding="utf-8") as f:
+        content = f.read()
+
+    # Banner with overall effective coverage (lines + branches)
+    branch_info = ""
+    if stats.get("justified_branches", 0) > 0:
+        branch_info = (
+            f" | Effective Branch Coverage: {stats['effective_branch_coverage_pct']}%"
+            f" (Raw: {stats['raw_branch_coverage_pct']}%, Justified: {stats['justified_branches']} branches)"
+        )
+
+    banner = (
+        f"
" + f"Effective Line Coverage: {stats['effective_line_coverage_pct']}% " + f"(Raw: {stats['raw_line_coverage_pct']}% | " + f"Justified: {stats['justified_lines']} lines | " + f"Unjustified Uncovered: {stats['unjustified_uncovered_lines']} lines)" + f"{branch_info}" + f"
" + ) + + # Insert after the tag or after the first

+ if "

" in content: + content = content.replace("

", banner + "

", 1) + else: + content = content.replace("", f"{banner}", 1) + + # Update per-file rows in the index table. + # For each file with justifications, find its row and update line% and branch% cells. + # Row format:
displayname
+ #
  XX.XX% (covered/total)
← function + #
  XX.XX% (covered/total)
← line + #
  XX.XX% (covered/total)
← branch + # + pct_cell_pattern = re.compile(r"
\s*(\d+\.\d+)%\s*\((\d+)/(\d+)\)
") + + for file_path, fstats in per_file_stats.items(): + justified_lines = fstats.get("justified", 0) + justified_branches = fstats.get("justified_branches", 0) + if justified_lines == 0 and justified_branches == 0: + continue + + # Find the row for this file in the index page + # The href contains the full path to the HTML file + if file_path not in content: + continue + + # Find the containing this file path + file_idx = content.find(file_path) + if file_idx < 0: + continue + row_start = content.rfind("", file_idx) + if row_start < 0 or row_end < 0: + continue + + row = content[row_start : row_end + 5] + + # Find all percentage cells in this row (func, line, branch) + cells = list(pct_cell_pattern.finditer(row)) + if len(cells) < 2: + continue + + new_row = row + # Update line coverage cell (second cell, index 1) + if justified_lines > 0 and len(cells) >= 2: + line_cell = cells[1] + covered = int(line_cell.group(3)) + total = int(line_cell.group(4)) + eff_covered = covered + justified_lines + eff_pct = floor_two_decimals(100.0 * eff_covered / total) if total > 0 else 0.0 + color = _get_coverage_color(eff_pct) + old_cell = line_cell.group(0) + new_cell = f"
{eff_pct:>7.2f}% ({eff_covered}/{total})
" + new_row = new_row.replace(old_cell, new_cell) + + # Update branch coverage cell (third cell, index 2) + if justified_branches > 0 and len(cells) >= 3: + branch_cell = cells[2] + covered = int(branch_cell.group(3)) + total = int(branch_cell.group(4)) + eff_covered = covered + justified_branches + eff_pct = floor_two_decimals(100.0 * eff_covered / total) if total > 0 else 0.0 + color = _get_coverage_color(eff_pct) + old_cell = branch_cell.group(0) + new_cell = f"
{eff_pct:>7.2f}% ({eff_covered}/{total})
" + new_row = new_row.replace(old_cell, new_cell) + + if new_row != row: + content = content.replace(row, new_row) + + # Update the TOTALS row + content = _update_totals_row(content, stats) + + with open(index_file, "w", encoding="utf-8") as f: + f.write(content) + + +def _get_coverage_color(pct: float) -> str: + """Return the llvm-cov color class for a coverage percentage.""" + if pct >= 100.0: + return "green" + elif pct >= 80.0: + return "yellow" + else: + return "red" + + +def _update_totals_row(content: str, stats: Dict[str, Any]) -> str: + """Update the TOTALS row in the index page with effective coverage numbers.""" + # Find the TOTALS row — it's the last row before + totals_idx = content.rfind("Totals") + if totals_idx < 0: + return content + + row_start = content.rfind("", totals_idx) + if row_start < 0 or row_end < 0: + return content + + row = content[row_start : row_end + 5] + + pct_cell_pattern = re.compile(r"
\s*(\d+\.\d+)%\s*\((\d+)/(\d+)\)
") + cells = list(pct_cell_pattern.finditer(row)) + + new_row = row + + # Update line coverage in totals (index 1) + if len(cells) >= 2 and stats.get("justified_lines", 0) > 0: + line_cell = cells[1] + eff_covered = stats["covered_lines"] + stats["justified_lines"] + total = stats["total_instrumented_lines"] + eff_pct = stats["effective_line_coverage_pct"] + color = _get_coverage_color(eff_pct) + old_cell = line_cell.group(0) + new_cell = f"
{eff_pct:>7.2f}% ({eff_covered}/{total})
" + new_row = new_row.replace(old_cell, new_cell) + + # Update branch coverage in totals (index 2) + if len(cells) >= 3 and stats.get("justified_branches", 0) > 0: + branch_cell = cells[2] + eff_covered = stats["covered_branches"] + stats["justified_branches"] + total = stats["total_branches"] + eff_pct = stats["effective_branch_coverage_pct"] + color = _get_coverage_color(eff_pct) + old_cell = branch_cell.group(0) + new_cell = f"
{eff_pct:>7.2f}% ({eff_covered}/{total})
" + new_row = new_row.replace(old_cell, new_cell) + + if new_row != row: + content = content.replace(row, new_row) + + return content + + +def find_source_html_files(html_dir: Path) -> List[Path]: + """Find all per-source HTML files (not index.html, style.css, etc.).""" + coverage_dir = html_dir / "coverage" + if not coverage_dir.exists(): + # Some llvm-cov versions put source files directly in html_dir + coverage_dir = html_dir + + files = [] + for html_file in coverage_dir.rglob("*.html"): + if html_file.name in ("index.html",): + continue + files.append(html_file) + return sorted(files) + + +def extract_source_path_from_html(html_file: Path, html_dir: Path) -> str: + """Extract the relative source file path from the HTML file path. + + llvm-cov creates paths like: html_report/coverage/.html + We need to extract the relative path within the project. + """ + rel = str(html_file.relative_to(html_dir)) + # Remove "coverage/" prefix if present + if rel.startswith("coverage/"): + rel = rel[len("coverage/") :] + # Remove .html suffix + if rel.endswith(".html"): + rel = rel[:-5] + return rel + + +def find_matching_justifications( + source_path: str, justified_files: Dict[str, Dict[str, Dict[str, str]]] +) -> Dict[int, Dict[str, str]]: + """Find justifications that match the given source path. + + The source_path from HTML may be an absolute path or relative. + The justified_files keys are relative to source root. + We match by suffix. + """ + result: Dict[int, Dict[str, str]] = {} + + for justified_path, line_justifications in justified_files.items(): + # Match if the source_path ends with the justified_path + if source_path.endswith(justified_path) or justified_path.endswith(source_path): + for line_str, justification in line_justifications.items(): + result[int(line_str)] = justification + + return result + + +def write_summary(path: Path, stats: Dict[str, Any], stale: List[Dict[str, Any]]) -> None: + """Write human-readable summary.""" + with open(path, "w", encoding="utf-8") as f: + f.write("Coverage Justification Summary\n") + f.write("=" * 40 + "\n\n") + f.write(f"Total instrumented lines: {stats['total_instrumented_lines']}\n") + f.write(f"Covered lines: {stats['covered_lines']}\n") + f.write(f"Justified lines: {stats['justified_lines']}\n") + f.write(f"Unjustified uncovered: {stats['unjustified_uncovered_lines']}\n") + f.write(f"\n") + f.write(f"Raw line coverage: {stats['raw_line_coverage_pct']}%\n") + f.write(f"Effective line coverage: {stats['effective_line_coverage_pct']}%\n") + f.write(f"\n") + if stats.get("total_branches", 0) > 0: + f.write(f"Total branches: {stats['total_branches']}\n") + f.write(f"Covered branches: {stats['covered_branches']}\n") + f.write(f"Justified branches: {stats['justified_branches']}\n") + f.write(f"Raw branch coverage: {stats['raw_branch_coverage_pct']}%\n") + f.write(f"Effective branch coverage: {stats['effective_branch_coverage_pct']}%\n") + f.write(f"\n") + if stale: + f.write(f"Stale justifications ({len(stale)}):\n") + for s in stale: + f.write(f" - {s['file']}:{s['line']} [{s['id']}]\n") + f.write("\n") + + +def load_manifest(path: Path) -> Dict[str, Any]: + """Load the justification manifest JSON.""" + if not path.exists(): + print(f"ERROR: Manifest not found: {path}", file=sys.stderr) + sys.exit(1) + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +def parse_args() -> argparse.Namespace: + """Parse command-line arguments.""" + parser = argparse.ArgumentParser(description="Effective coverage calculator and HTML post-processor") + parser.add_argument( + "--html-dir", + type=Path, + required=True, + help="Path to llvm-cov HTML report directory", + ) + parser.add_argument( + "--manifest", + type=Path, + required=True, + help="Path to resolved justification manifest (from justify.py)", + ) + parser.add_argument( + "--output", + type=Path, + required=True, + help="Output path for justification report (JSON)", + ) + parser.add_argument( + "--lcov", + type=Path, + default=None, + help="Optional path to LCOV data file (used for gcovr format totals)", + ) + return parser.parse_args() + + +# ============================================================================= +# Format detection +# ============================================================================= + + +def detect_html_format(html_dir: Path) -> str: + """Detect whether the HTML report was generated by llvm-cov or gcovr. + + Returns 'llvm_cov' or 'gcovr'. + """ + # gcovr produces files named based on --html-details argument. + # We use index.html, so per-source files are index...html + if any(html_dir.glob("index.*.*.html")): + return "gcovr" + if (html_dir / "coverage_details.html").exists(): + return "gcovr" + return "llvm_cov" + + +def _parse_lcov_totals(lcov_path: Path) -> Dict[str, Tuple[int, int]]: + """Parse coverage totals from an LCOV data file. + + Sums LH/LF (line hit/found) and BRH/BRF (branch hit/found) across all records. + """ + total_lines = 0 + hit_lines = 0 + total_branches = 0 + hit_branches = 0 + + with open(lcov_path, "r", encoding="utf-8", errors="replace") as f: + for line in f: + line = line.strip() + if line.startswith("LF:"): + total_lines += int(line[3:]) + elif line.startswith("LH:"): + hit_lines += int(line[3:]) + elif line.startswith("BRF:"): + total_branches += int(line[4:]) + elif line.startswith("BRH:"): + hit_branches += int(line[4:]) + + return { + "lines": (hit_lines, total_lines), + "branches": (hit_branches, total_branches), + } + + +# ============================================================================= +# gcovr support +# ============================================================================= + + +def _main_gcovr(args: argparse.Namespace, html_dir: Path, justified_files: Dict) -> None: + """Main logic for gcovr HTML format (produced by lcov_to_html.py via gcovr).""" + + # Parse coverage totals from LCOV file or gcovr index page. + if args.lcov and args.lcov.exists(): + totals = _parse_lcov_totals(args.lcov) + else: + totals = _parse_gcovr_index_totals(html_dir) + + raw_covered, raw_total = totals["lines"] + raw_branch_covered, raw_branch_total = totals["branches"] + + # Process each source HTML file + total_justified = 0 + total_stale = 0 + total_justified_branches = 0 + applied_justifications: List[Dict[str, Any]] = [] + stale_justifications: List[Dict[str, Any]] = [] + per_file_stats: Dict[str, Dict[str, int]] = {} + + source_html_files = _find_gcovr_source_files(html_dir) + for html_file in source_html_files: + rel_source_path = _extract_gcovr_source_path(html_file) + if not rel_source_path: + continue + + file_justifications = find_matching_justifications(rel_source_path, justified_files) + + file_stats = _process_gcovr_file(html_file, file_justifications, applied_justifications, stale_justifications) + + total_justified += file_stats["justified"] + total_stale += file_stats["stale"] + total_justified_branches += file_stats["justified_branches"] + + if file_stats["justified"] > 0 or file_stats["justified_branches"] > 0: + per_file_stats[rel_source_path] = file_stats + + # Calculate stats + raw_uncovered = raw_total - raw_covered + unjustified_uncovered = raw_uncovered - total_justified + effective_branch_covered = raw_branch_covered + total_justified_branches + + stats = { + "total_instrumented_lines": raw_total, + "covered_lines": raw_covered, + "justified_lines": total_justified, + "unjustified_uncovered_lines": max(0, unjustified_uncovered), + "stale_justifications": total_stale, + "raw_line_coverage_pct": floor_two_decimals(100.0 * raw_covered / raw_total) if raw_total > 0 else 0.0, + "effective_line_coverage_pct": floor_two_decimals(100.0 * (raw_covered + total_justified) / raw_total) + if raw_total > 0 + else 0.0, + "total_branches": raw_branch_total, + "covered_branches": raw_branch_covered, + "justified_branches": total_justified_branches, + "raw_branch_coverage_pct": floor_two_decimals(100.0 * raw_branch_covered / raw_branch_total) + if raw_branch_total > 0 + else 0.0, + "effective_branch_coverage_pct": floor_two_decimals(100.0 * effective_branch_covered / raw_branch_total) + if raw_branch_total > 0 + else 0.0, + } + + # Inject CSS for justified lines + _inject_gcovr_justified_css(html_dir) + + # Update the gcovr index page with effective coverage banner + _update_gcovr_index_page(html_dir, stats) + + # Write output report + report = { + "version": 1, + "summary": stats, + "applied_justifications": applied_justifications, + "stale_justifications": stale_justifications, + } + + if args.output: + with open(args.output, "w", encoding="utf-8") as f: + json.dump(report, f, indent=2) + print(f"Effective coverage report written to: {args.output}") + else: + print(json.dumps(report, indent=2)) + + +def _parse_gcovr_index_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]: + """Parse coverage totals from gcovr's index page. + + gcovr format shows coverage in the summary header with patterns like: + 75.0% + 30 / 40 + """ + index_file = html_dir / "index.html" + if not index_file.exists(): + # Fallback for older naming + index_file = html_dir / "coverage_details.html" + if not index_file.exists(): + return {"lines": (0, 0), "branches": (0, 0)} + + content = index_file.read_text(encoding="utf-8", errors="replace") + + # gcovr summary table has rows with line/branch stats. + # Look for the summary div with class "summary" containing coverage percentages. + # Pattern: "N / M" in table cells for covered/total + lines_covered, lines_total = 0, 0 + branches_covered, branches_total = 0, 0 + + # gcovr index uses meter elements with data attributes or text like "N / M" + # The summary section shows: Line Coverage: XX.X% (covered / total) + ratio_pattern = re.compile(r"(\d+)\s*/\s*(\d+)") + + # Find the summary section — gcovr puts line/branch/function stats in order. + # Look for the summary-coverage section + summary_match = re.search(r'class="[^"]*summary[^"]*".*?', content, re.DOTALL) + if summary_match: + summary_text = summary_match.group(0) + ratios = ratio_pattern.findall(summary_text) + # gcovr order: lines, functions, branches (if branches enabled) + if len(ratios) >= 1: + lines_covered, lines_total = int(ratios[0][0]), int(ratios[0][1]) + if len(ratios) >= 3: + branches_covered, branches_total = int(ratios[2][0]), int(ratios[2][1]) + elif len(ratios) >= 2: + # Might be lines then branches (no functions section) + branches_covered, branches_total = int(ratios[1][0]), int(ratios[1][1]) + + if lines_total == 0: + # Fallback: scan the whole page for ratio patterns + all_ratios = ratio_pattern.findall(content) + if len(all_ratios) >= 1: + lines_covered, lines_total = int(all_ratios[0][0]), int(all_ratios[0][1]) + if len(all_ratios) >= 3: + branches_covered, branches_total = int(all_ratios[2][0]), int(all_ratios[2][1]) + + return { + "lines": (lines_covered, lines_total), + "branches": (branches_covered, branches_total), + } + + +def _find_gcovr_source_files(html_dir: Path) -> List[Path]: + """Find all per-source HTML files in a gcovr report. + + gcovr --html-details creates files named: + index...html + The index is index.html (no dot-separated parts after "index"). + """ + files = [] + for html_file in sorted(html_dir.glob("index.*.html")): + # Skip function pages (contain ".functions." in name) + if ".functions." in html_file.name: + continue + files.append(html_file) + return files + + +def _extract_gcovr_source_path(html_file: Path) -> str: + """Extract the source file path from a gcovr per-source HTML file. + + gcovr embeds the directory in a "Directory:" table row and the filename + in the Box-header. Combining these gives the full relative source path. + """ + try: + content = html_file.read_text(encoding="utf-8", errors="replace")[:8000] + except OSError: + return "" + + # Extract directory from: Directory:\npath/ + directory = "" + dir_match = re.search(r"Directory:\s*([^<]*)", content) + if dir_match: + directory = dir_match.group(1).strip() + + # Extract filename from the Box-header + filename = "" + name_match = re.search( + r'class="Box-header[^"]*d-flex flex-space-between[^"]*"[^>]*>\s*\n?\s*([^\n<]+)', + content, + ) + if name_match: + filename = name_match.group(1).strip() + + if not filename: + # Fallback: look in + title_match = re.search(r"<title>([^<]+?)(?:\s*-\s*GCC[^<]*)?", content) + if title_match: + filename = title_match.group(1).strip() + + if directory and filename: + return directory + filename + return filename + + +def _process_gcovr_file( + html_file: Path, + justifications: Dict[int, Dict[str, str]], + applied_justifications: List[Dict[str, Any]], + stale_justifications: List[Dict[str, Any]], +) -> Dict[str, int]: + """Process a single gcovr source HTML file. Modifies it in-place. + + gcovr line format: + + {num} + ... + 5 + source code + + + Coverage classes: + - coveredLine: line is covered (count > 0) + - uncoveredLine: line is not covered (count == 0) + - partialCoveredLine: some branches not taken + - excludedLine: excluded from coverage + """ + file_stats = {"justified": 0, "stale": 0, "justified_branches": 0} + + if not justifications: + return file_stats + + with open(html_file, "r", encoding="utf-8") as f: + content = f.read() + + # Parse line coverage status from gcovr HTML. + # Each source line: + # followed by + line_pattern = re.compile( + r']*>.*?' + r' on lines with branch issues + branch_pattern = re.compile( + r']*>.*?(.*?)', + re.DOTALL, + ) + + line_effective_status: Dict[int, str] = {} + lines_with_uncovered_branches: set = set() + + for m in line_pattern.finditer(content): + line_num = int(m.group(1)) + cov_class = m.group(2) + if cov_class == "coveredLine": + line_effective_status[line_num] = "covered" + elif cov_class == "uncoveredLine": + line_effective_status[line_num] = "uncovered" + elif cov_class == "partialCoveredLine": + line_effective_status[line_num] = "covered" + lines_with_uncovered_branches.add(line_num) + + # Also check branch details for not-taken branches + for m in branch_pattern.finditer(content): + line_num = int(m.group(1)) + branch_content = m.group(2) + if "notTakenBranch" in branch_content: + lines_with_uncovered_branches.add(line_num) + + # Determine stale vs applicable justifications + for line_num, justification in justifications.items(): + status = line_effective_status.get(line_num) + has_uncovered_branches = line_num in lines_with_uncovered_branches + if status == "covered" and not has_uncovered_branches: + file_stats["stale"] += 1 + stale_justifications.append( + { + "file": _extract_gcovr_source_path(html_file), + "line": line_num, + "id": justification.get("id", ""), + "reason": "Line is already covered and has no uncovered branches — justification is stale", + } + ) + elif status == "uncovered": + file_stats["justified"] += 1 + applied_justifications.append( + { + "file": _extract_gcovr_source_path(html_file), + "line": line_num, + "id": justification.get("id", ""), + "category": justification.get("category", ""), + } + ) + # Mark the line as justified in the HTML + content = _mark_gcovr_line_justified(content, line_num) + elif status == "covered" and has_uncovered_branches: + file_stats["justified_branches"] += 1 + applied_justifications.append( + { + "file": _extract_gcovr_source_path(html_file), + "line": line_num, + "id": justification.get("id", ""), + "category": justification.get("category", ""), + } + ) + + # Write modified content back + if file_stats["justified"] > 0: + with open(html_file, "w", encoding="utf-8") as f: + f.write(content) + + return file_stats + + +def _mark_gcovr_line_justified(content: str, line_num: int) -> str: + """Replace 'uncoveredLine' with 'justifiedLine' for a specific line in gcovr HTML.""" + # Find the line anchor and replace the coverage class in that row + # Pattern: ...uncoveredLine... (within the same ) + pattern = re.compile( + rf'(]*>.*?)', + re.DOTALL, + ) + match = pattern.search(content) + if match: + original = match.group(0) + modified = original.replace("uncoveredLine", "justifiedLine") + modified = modified.replace("show_uncoveredLine", "show_justifiedLine") + content = content[: match.start()] + modified + content[match.end() :] + return content + + +def _inject_gcovr_justified_css(html_dir: Path) -> None: + """Inject CSS for justified lines into gcovr's stylesheet.""" + # gcovr names its CSS file based on the output filename (coverage_details.css) + css_files = list(html_dir.glob("*.css")) + if not css_files: + return + + justified_css = """ +/* Justified line styling (injected by effective_coverage.py) */ +.justifiedLine { background-color: #ffe4b5 !important; } +.show_justifiedLine { display: table-cell; } +.button_toggle_justifiedLine { background-color: #ffe4b5; border: 1px solid #daa520; } +""" + # Inject into the first CSS file found + with open(css_files[0], "a", encoding="utf-8") as f: + f.write(justified_css) + + +def _update_gcovr_index_page(html_dir: Path, stats: Dict[str, Any]) -> None: + """Update the gcovr index page with an effective coverage banner.""" + index_file = html_dir / "index.html" + if not index_file.exists(): + index_file = html_dir / "coverage_details.html" + if not index_file.exists(): + return + + with open(index_file, "r", encoding="utf-8") as f: + content = f.read() + + branch_info = "" + if stats.get("justified_branches", 0) > 0: + branch_info = ( + f" | Effective Branch Coverage: {stats['effective_branch_coverage_pct']}%" + f" (Raw: {stats['raw_branch_coverage_pct']}%, Justified: {stats['justified_branches']} branches)" + ) + + banner = ( + f'
' + f"Effective Line Coverage: {stats['effective_line_coverage_pct']}% " + f"(Raw: {stats['raw_line_coverage_pct']}% | " + f"Justified: {stats['justified_lines']} lines | " + f"Unjustified Uncovered: {stats['unjustified_uncovered_lines']} lines)" + f"{branch_info}" + f"
" + ) + + # Insert before the file listing + if '
" in content: + content = content.replace("", f"{banner}", 1) + + with open(index_file, "w", encoding="utf-8") as f: + f.write(content) + + +if __name__ == "__main__": + main() diff --git a/score_coverage/generate_coverage_html.sh b/score_coverage/generate_coverage_html.sh new file mode 100755 index 0000000..b847bcc --- /dev/null +++ b/score_coverage/generate_coverage_html.sh @@ -0,0 +1,298 @@ +#!/usr/bin/env bash +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +# Generates an HTML coverage report from `bazel coverage` output produced by +# the LLVM pipeline: the custom reporter produces a zip with html_report/, +# lcov_report/ and text_report/ inside. +# +# Usage (from the CONSUMER workspace): +# bazel coverage --config=llvm_cov //... --build_tests_only +# bazel run @score_coverage//:generate_coverage_html -- \ +# [--yaml ] \ +# [--archive ] [--archive-dir ] \ +# [--platform ] [--testlogs-subdir ] [output-dir] +# +# Arguments: +# --yaml Justification YAML, relative to the workspace +# root. When omitted, justification processing and +# the effective-coverage metric are skipped and the +# threshold gate applies to the RAW line coverage. +# --archive Create a zip archive named .zip +# containing the HTML report, raw LCOV data and JUnit XMLs. +# --archive-dir Assemble the same content as --archive into +# WITHOUT zipping — preferred for CI artifact +# uploads (actions/upload-artifact zips its input +# itself; a pre-zipped file would be zipped twice). +# --platform Target platform for justification filtering +# (default: linux). Also affects the default output +# directory (coverage_). +# --testlogs-subdir Subdirectory of bazel-testlogs to collect JUnit +# XMLs from when archiving (default: entire +# bazel-testlogs tree). +# --summary-md Write a markdown coverage summary (tables, +# per-directory rollup, 0%-file list) to . +# When ABSENT and the GITHUB_STEP_SUMMARY +# environment variable is set (GitHub Actions), +# the summary is appended there automatically; +# when neither is present, no summary is emitted. +# The summary is written before the threshold +# gate decides the exit code, so a failing gate +# still leaves it on the workflow run page. +# output-dir Directory to write the HTML report to +# (default: coverage_) +# +# Environment: +# COVERAGE_THRESHOLD Minimum line coverage percentage (default: 100). +# The script exits non-zero when the gated metric +# (effective coverage with --yaml, raw coverage +# without) is below this threshold. + +set -euo pipefail + +ARCHIVE_NAME="" +ARCHIVE_DIR="" +PLATFORM="linux" +OUTPUT_DIR="" +JUSTIFICATION_YAML_REL="" +TESTLOGS_SUBDIR="" +SUMMARY_MD="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --yaml) + JUSTIFICATION_YAML_REL="${2:?--yaml requires a path argument}" + shift 2 + ;; + --archive) + ARCHIVE_NAME="${2:?--archive requires a name argument}" + shift 2 + ;; + --archive-dir) + ARCHIVE_DIR="${2:?--archive-dir requires a directory argument}" + shift 2 + ;; + --platform) + PLATFORM="${2:?--platform requires a platform argument (e.g. linux or qnx)}" + shift 2 + ;; + --testlogs-subdir) + TESTLOGS_SUBDIR="${2:?--testlogs-subdir requires a path argument}" + shift 2 + ;; + --summary-md) + SUMMARY_MD="${2:?--summary-md requires a path argument}" + shift 2 + ;; + *) + OUTPUT_DIR="$1" + shift + ;; + esac +done + +# --yaml is optional: without it, justification processing and the effective +# coverage metric are skipped and the threshold gate applies to the RAW line +# coverage from llvm-cov's summary instead. + +# Set default output directory based on platform if not explicitly provided. +if [[ -z "${OUTPUT_DIR}" ]]; then + OUTPUT_DIR="coverage_${PLATFORM}" +fi + +# Change to the workspace root so that all subsequent bazel calls and +# relative paths work correctly. +cd "${BUILD_WORKSPACE_DIRECTORY}" + +# Resolve OUTPUT_DIR to absolute path (relative to workspace root). +OUTPUT_DIR="${BUILD_WORKSPACE_DIRECTORY}/${OUTPUT_DIR}" + +# The coverage report is at _coverage_report.dat: a zip containing +# html_report/, lcov_report/ and text_report/ (LLVM pipeline). +COVERAGE_REPORT="${BUILD_WORKSPACE_DIRECTORY}/bazel-out/_coverage/_coverage_report.dat" + +if [[ ! -f "${COVERAGE_REPORT}" ]]; then + echo "ERROR: Coverage report not found at ${COVERAGE_REPORT}" >&2 + echo " Run 'bazel coverage --config=llvm_cov //... --build_tests_only' first." >&2 + exit 1 +fi + +TMPDIR_EXTRACT="${TMPDIR:-/tmp}/coverage_extract_$$" +mkdir -p "${TMPDIR_EXTRACT}" +trap 'rm -rf "${TMPDIR_EXTRACT}"' EXIT + +rm -rf "${OUTPUT_DIR}" + +if ! file -b "${COVERAGE_REPORT}" | grep -q "Zip archive"; then + echo "ERROR: ${COVERAGE_REPORT} is not the LLVM pipeline zip report." >&2 + echo " Run 'bazel coverage --config=llvm_cov //... --build_tests_only' first." >&2 + exit 1 +fi + +# Extract HTML from the zip produced by our custom reporter. +unzip -q -o "${COVERAGE_REPORT}" -d "${TMPDIR_EXTRACT}" + +if [[ -d "${TMPDIR_EXTRACT}/html_report" ]]; then + cp -r "${TMPDIR_EXTRACT}/html_report" "${OUTPUT_DIR}" +else + echo "ERROR: html_report/ not found in ${COVERAGE_REPORT}" >&2 + exit 1 +fi + +echo "Coverage report written to: ${OUTPUT_DIR}" + +# --------------------------------------------------------------------------- +# Run coverage justification processing (only when --yaml was given) and +# enforce the coverage threshold. +# --------------------------------------------------------------------------- +THRESHOLD="${COVERAGE_THRESHOLD:-100}" +JUSTIFICATION_DIR="" + +if [[ -n "${JUSTIFICATION_YAML_REL}" ]]; then + JUSTIFICATION_YAML="${BUILD_WORKSPACE_DIRECTORY}/${JUSTIFICATION_YAML_REL}" + + if [[ ! -f "${JUSTIFICATION_YAML}" ]]; then + echo "ERROR: ${JUSTIFICATION_YAML} not found." >&2 + exit 1 + fi + + echo "" + echo "Running coverage justification processing..." + + JUSTIFICATION_DIR="${TMPDIR_EXTRACT}/justification_report" + mkdir -p "${JUSTIFICATION_DIR}" + + # Run justify.py / effective_coverage.py via nested bazel invocations from the + # consumer workspace. This deliberately avoids runfiles resolution across + # module boundaries (canonical repo names vary between Bazel versions). + bazel run @score_coverage//:justify -- \ + --yaml "${JUSTIFICATION_YAML}" \ + --source-root "${BUILD_WORKSPACE_DIRECTORY}" \ + --platform "${PLATFORM}" \ + --output "${JUSTIFICATION_DIR}/manifest.json" + + bazel run @score_coverage//:effective_coverage -- \ + --html-dir "${OUTPUT_DIR}" \ + --manifest "${JUSTIFICATION_DIR}/manifest.json" \ + --output "${JUSTIFICATION_DIR}/report.json" + + # Display effective coverage summary and enforce the threshold. + if [[ ! -f "${JUSTIFICATION_DIR}/summary.txt" ]]; then + echo "ERROR: Effective coverage summary was not produced." >&2 + exit 1 + fi + + echo "" + cat "${JUSTIFICATION_DIR}/summary.txt" + + # Extract effective coverage percentage for threshold check. + GATE_PCT=$(grep -oP 'Effective line coverage:\s+\K[0-9.]+' \ + "${JUSTIFICATION_DIR}/summary.txt" 2>/dev/null || echo "0") + GATE_KIND="Effective" +else + # No justification YAML: gate on the raw line coverage computed from the + # LCOV data. Deliberately NOT llvm-cov's text summary TOTAL — that summary + # omits baseline-only files (in-scope files no test links against), which + # would let untested files escape the gate. The LCOV includes them. + echo "" + echo "INFO: no --yaml given; justification processing skipped, gating on raw line coverage." + if [[ ! -f "${TMPDIR_EXTRACT}/lcov_report/lcov.dat" ]]; then + echo "ERROR: lcov_report/lcov.dat not found in ${COVERAGE_REPORT}" >&2 + exit 1 + fi + GATE_PCT=$(awk -F: '/^LF:/ {lf += $2} /^LH:/ {lh += $2} + END { if (lf > 0) printf "%.2f", lh * 100 / lf; }' \ + "${TMPDIR_EXTRACT}/lcov_report/lcov.dat") + if [[ -z "${GATE_PCT}" ]]; then + echo "ERROR: could not compute raw line coverage from lcov_report/lcov.dat" >&2 + exit 1 + fi + echo "Raw line coverage: ${GATE_PCT}%" + GATE_KIND="Raw" +fi + +# --------------------------------------------------------------------------- +# Optional markdown summary (--summary-md, or GITHUB_STEP_SUMMARY when the +# flag is absent). Emitted BEFORE the threshold gate so a failing gate still +# leaves the summary on the workflow run page. +# --------------------------------------------------------------------------- +if [[ -n "${SUMMARY_MD}" || -n "${GITHUB_STEP_SUMMARY:-}" ]]; then + SUMMARY_ARGS=(--lcov "${TMPDIR_EXTRACT}/lcov_report/lcov.dat") + if [[ -n "${JUSTIFICATION_DIR}" && -f "${JUSTIFICATION_DIR}/report.json" ]]; then + SUMMARY_ARGS+=(--justification-report "${JUSTIFICATION_DIR}/report.json") + fi + if [[ -n "${SUMMARY_MD}" ]]; then + case "${SUMMARY_MD}" in + /*) : ;; + *) SUMMARY_MD="${BUILD_WORKSPACE_DIRECTORY}/${SUMMARY_MD}" ;; + esac + bazel run @score_coverage//:coverage_summary -- \ + "${SUMMARY_ARGS[@]}" --output "${SUMMARY_MD}" + echo "Coverage summary written to: ${SUMMARY_MD}" + else + bazel run @score_coverage//:coverage_summary -- \ + "${SUMMARY_ARGS[@]}" --output "${GITHUB_STEP_SUMMARY}" --append + echo "Coverage summary appended to GITHUB_STEP_SUMMARY" + fi +fi + +# Threshold check (default: 100%). Fails the run when below. +if ! awk "BEGIN {exit (${GATE_PCT} >= ${THRESHOLD}) ? 0 : 1}"; then + echo "ERROR: ${GATE_KIND} coverage ${GATE_PCT}% is below threshold ${THRESHOLD}%" >&2 + RC=1 +else + RC=0 +fi + +# --------------------------------------------------------------------------- +# Optional: assemble the HTML report, raw LCOV data, justification report and +# JUnit XML test results into an artifacts tree. +# --archive zip the tree into .zip (and remove the tree) +# --archive-dir keep the tree at — preferred for CI artifact +# uploads, since actions/upload-artifact zips its input +# anyway (a pre-zipped file would be zipped twice) +# --------------------------------------------------------------------------- +assemble_artifacts() { + local dest="$1" + mkdir -p "${dest}" + + # Copy JUnit XML test results preserving directory structure. + find "bazel-testlogs/${TESTLOGS_SUBDIR}" -name 'test.xml' -exec cp --parents {} "${dest}/" \; + + # Copy the HTML coverage report + cp -r "${OUTPUT_DIR}" "${dest}/" + + # Include the LCOV .dat file from the reporter zip. + if [[ -f "${TMPDIR_EXTRACT}/lcov_report/lcov.dat" ]]; then + cp "${TMPDIR_EXTRACT}/lcov_report/lcov.dat" "${dest}/coverage_report.dat" + fi + + # Include the justification report (manifest + effective coverage json). + if [[ -n "${JUSTIFICATION_DIR}" && -d "${JUSTIFICATION_DIR}" ]]; then + cp -r "${JUSTIFICATION_DIR}" "${dest}/" + fi +} + +if [[ -n "${ARCHIVE_DIR}" ]]; then + rm -rf "${ARCHIVE_DIR}" + assemble_artifacts "${ARCHIVE_DIR}" + echo "Coverage artifacts written to: ${ARCHIVE_DIR}/" +fi + +if [[ -n "${ARCHIVE_NAME}" ]]; then + assemble_artifacts artifacts + zip -r "${ARCHIVE_NAME}.zip" artifacts/ + rm -rf artifacts/ + echo "Coverage archive written to: ${ARCHIVE_NAME}.zip" +fi + +exit "${RC}" diff --git a/score_coverage/justify.py b/score_coverage/justify.py new file mode 100644 index 0000000..488b7c5 --- /dev/null +++ b/score_coverage/justify.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Coverage justification processor. + +Parses the YAML justification database and source files for COV_JUSTIFIED markers. +Resolves all justified lines and produces a manifest mapping file:line → justification. + +Usage: + python justify.py --yaml --source-root --output + +Supports two ways to specify justified lines: +1. YAML locations: directly specify file + line ranges in the YAML +2. In-code markers: COV_JUSTIFIED , COV_JUSTIFIED_START / COV_JUSTIFIED_STOP +""" + +import argparse +import json +import re +import sys +from pathlib import Path +from typing import Any, Dict, List, Set, Tuple + +import yaml + + +# Marker patterns +COV_JUSTIFIED_LINE_RE = re.compile(r"COV_JUSTIFIED\s+([\w-]+)") +COV_JUSTIFIED_START_RE = re.compile(r"COV_JUSTIFIED_START\s+([\w-]+)") +COV_JUSTIFIED_STOP_RE = re.compile(r"COV_JUSTIFIED_STOP") + +VALID_CATEGORIES = { + "defensive_programming", + "tool_false_positive", + "platform_specific", + "other", +} + +VALID_PLATFORMS = { + "linux", + "qnx", +} + + +def main() -> None: + """Main entry point.""" + args = parse_args() + + justifications_data = load_yaml(args.yaml) + validate_yaml(justifications_data) + + # Build lookup: id -> justification entry + justifications_by_id: Dict[str, Dict[str, Any]] = {} + for entry in justifications_data.get("justifications", []): + justifications_by_id[entry["id"]] = entry + + # Filter justifications by platform if --platform is specified. + if args.platform: + justifications_by_id = { + jid: entry for jid, entry in justifications_by_id.items() if _matches_platform(entry, args.platform) + } + + # Resolve all justified lines + resolved: Dict[str, Dict[int, Dict[str, str]]] = {} + warnings: List[str] = [] + errors: List[str] = [] + + # 1. Process YAML direct locations + for jid, entry in justifications_by_id.items(): + for location in entry.get("locations", []): + file_path = location["file"] + full_path = Path(args.source_root) / file_path + + if not full_path.exists(): + errors.append(f"File not found for justification '{entry['id']}': {file_path}") + continue + + lines = resolve_location_lines(location) + if file_path not in resolved: + resolved[file_path] = {} + for line in lines: + resolved[file_path][line] = { + "id": entry["id"], + "category": entry["category"], + "reason": entry["reason"].strip(), + } + + # 2. Scan source files for in-code COV_JUSTIFIED markers + source_files = collect_source_files(args.source_root, args.file_filter) + for source_file in source_files: + rel_path = str(source_file.relative_to(args.source_root)) + scan_warnings, scan_lines = scan_file_for_markers(source_file, rel_path, justifications_by_id) + warnings.extend(scan_warnings) + + if scan_lines: + if rel_path not in resolved: + resolved[rel_path] = {} + for line_num, justification_info in scan_lines.items(): + resolved[rel_path][line_num] = justification_info + + # Output manifest + manifest = { + "version": 1, + "source_root": str(args.source_root), + "justified_files": { + filepath: {str(k): v for k, v in lines.items()} for filepath, lines in sorted(resolved.items()) + }, + "warnings": warnings, + "errors": errors, + } + + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w", encoding="utf-8") as f: + json.dump(manifest, f, indent=2) + + # Print diagnostics + total_justified_lines = sum(len(lines) for lines in resolved.values()) + print( + f"INFO: Resolved {total_justified_lines} justified lines across {len(resolved)} files.", + file=sys.stderr, + ) + if warnings: + for w in warnings: + print(f"WARNING: {w}", file=sys.stderr) + if errors: + for e in errors: + print(f"ERROR: {e}", file=sys.stderr) + sys.exit(1) + + +def resolve_location_lines(location: Dict[str, Any]) -> List[int]: + """Resolve line numbers from a YAML location entry.""" + if "lines" in location: + return location["lines"] + elif "line_start" in location and "line_end" in location: + return list(range(location["line_start"], location["line_end"] + 1)) + elif "line" in location: + return [location["line"]] + return [] + + +def _matches_platform(entry: Dict[str, Any], platform: str) -> bool: + """Check if a justification entry applies to the given platform. + + The ``platforms`` field is mandatory and validated by ``validate_yaml``. + """ + platforms = entry.get("platforms", []) + return platform in platforms + + +def scan_file_for_markers( + file_path: Path, + rel_path: str, + justifications_by_id: Dict[str, Dict[str, Any]], +) -> Tuple[List[str], Dict[int, Dict[str, str]]]: + """Scan a source file for COV_JUSTIFIED markers.""" + warnings = [] + justified_lines: Dict[int, Dict[str, str]] = {} + + try: + with open(file_path, "r", encoding="utf-8", errors="replace") as f: + lines = f.readlines() + except (IOError, OSError): + return warnings, justified_lines + + region_stack: List[Tuple[int, str]] = [] # (start_line, justification_id) + + for line_num, line in enumerate(lines, start=1): + # Check for COV_JUSTIFIED_START + start_match = COV_JUSTIFIED_START_RE.search(line) + if start_match: + jid = start_match.group(1) + if jid not in justifications_by_id: + warnings.append(f"{rel_path}:{line_num}: COV_JUSTIFIED_START references unknown ID '{jid}'") + else: + region_stack.append((line_num, jid)) + continue + + # Check for COV_JUSTIFIED_STOP + stop_match = COV_JUSTIFIED_STOP_RE.search(line) + if stop_match: + if not region_stack: + warnings.append(f"{rel_path}:{line_num}: COV_JUSTIFIED_STOP without matching START") + else: + start_line, jid = region_stack.pop() + if jid in justifications_by_id: + entry = justifications_by_id[jid] + for ln in range(start_line + 1, line_num): + justified_lines[ln] = { + "id": jid, + "category": entry["category"], + "reason": entry["reason"].strip(), + } + continue + + # Check for single-line COV_JUSTIFIED (but not START/STOP) + if "COV_JUSTIFIED_START" not in line and "COV_JUSTIFIED_STOP" not in line: + line_match = COV_JUSTIFIED_LINE_RE.search(line) + if line_match: + jid = line_match.group(1) + if jid not in justifications_by_id: + warnings.append(f"{rel_path}:{line_num}: COV_JUSTIFIED references unknown ID '{jid}'") + else: + entry = justifications_by_id[jid] + justified_lines[line_num] = { + "id": jid, + "category": entry["category"], + "reason": entry["reason"].strip(), + } + + # Check for unclosed regions + for start_line, jid in region_stack: + warnings.append(f"{rel_path}:{start_line}: COV_JUSTIFIED_START '{jid}' without matching STOP") + + return warnings, justified_lines + + +def collect_source_files(source_root: Path, file_filter: str) -> List[Path]: + """Collect source files to scan for markers.""" + extensions = file_filter.split(",") if file_filter else ["cpp", "h", "hpp", "cc", "rs"] + files = [] + for ext in extensions: + for path in source_root.rglob(f"*.{ext.strip()}"): + # Skip Bazel convenience symlinks (bazel-bin, bazel-out, bazel-, ...) + # so the marker scan does not descend into build outputs. + rel_parts = path.relative_to(source_root).parts + if rel_parts and rel_parts[0].startswith("bazel-"): + continue + files.append(path) + return sorted(files) + + +def load_yaml(yaml_path: Path) -> Dict[str, Any]: + """Load YAML justification database.""" + if not yaml_path.exists(): + print(f"ERROR: Justification YAML not found: {yaml_path}", file=sys.stderr) + sys.exit(1) + + with open(yaml_path, "r", encoding="utf-8") as f: + content = f.read() + + return yaml.safe_load(content) + + +def validate_yaml(data: Dict[str, Any]) -> None: + """Validate the justification YAML structure and types.""" + try: + errors = [] + + if not isinstance(data, dict): + print("ERROR: YAML validation: root must be a mapping", file=sys.stderr) + sys.exit(1) + + if "version" not in data: + errors.append("Missing 'version' field") + elif not isinstance(data["version"], int): + errors.append(f"'version' must be an integer, got {type(data['version']).__name__}") + + if "justifications" not in data: + errors.append("Missing 'justifications' field") + for e in errors: + print(f"ERROR: {e}", file=sys.stderr) + sys.exit(1) + + if not isinstance(data["justifications"], list): + errors.append(f"'justifications' must be a list, got {type(data['justifications']).__name__}") + for e in errors: + print(f"ERROR: YAML validation: {e}", file=sys.stderr) + sys.exit(1) + + seen_ids: Set[str] = set() + for i, entry in enumerate(data["justifications"]): + prefix = f"justifications[{i}]" + + if not isinstance(entry, dict): + errors.append(f"{prefix}: must be a mapping, got {type(entry).__name__}") + continue + + if "id" not in entry: + errors.append(f"{prefix}: missing 'id'") + continue + + jid = entry["id"] + if not isinstance(jid, str): + errors.append(f"{prefix}: 'id' must be a string, got {type(jid).__name__}") + continue + + if jid in seen_ids: + errors.append(f"{prefix}: duplicate ID '{jid}'") + seen_ids.add(jid) + + if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", jid): + errors.append(f"{prefix}: ID '{jid}' must be kebab-case") + + if "category" not in entry: + errors.append(f"{prefix}: missing 'category'") + elif not isinstance(entry["category"], str): + errors.append(f"{prefix}: 'category' must be a string, got {type(entry['category']).__name__}") + elif entry["category"] not in VALID_CATEGORIES: + errors.append( + f"{prefix}: invalid category '{entry['category']}'. Must be one of: {sorted(VALID_CATEGORIES)}" + ) + + if "platforms" not in entry: + errors.append(f"{prefix}: missing 'platforms'") + elif not isinstance(entry["platforms"], list): + errors.append(f"{prefix}: 'platforms' must be a list, got {type(entry['platforms']).__name__}") + elif not entry["platforms"]: + errors.append(f"{prefix}: 'platforms' must not be empty") + else: + for p in entry["platforms"]: + if not isinstance(p, str): + errors.append(f"{prefix}: 'platforms' entries must be strings, got {type(p).__name__}") + elif p not in VALID_PLATFORMS: + errors.append(f"{prefix}: invalid platform '{p}'. Must be one of: {sorted(VALID_PLATFORMS)}") + + if "reason" not in entry: + errors.append(f"{prefix}: missing 'reason'") + elif not isinstance(entry["reason"], str): + errors.append(f"{prefix}: 'reason' must be a string, got {type(entry['reason']).__name__}") + elif not entry["reason"].strip(): + errors.append(f"{prefix}: 'reason' must not be empty") + + if "locations" in entry: + if not isinstance(entry["locations"], list): + errors.append(f"{prefix}: 'locations' must be a list, got {type(entry['locations']).__name__}") + else: + for j, loc in enumerate(entry["locations"]): + loc_prefix = f"{prefix}.locations[{j}]" + if not isinstance(loc, dict): + errors.append(f"{loc_prefix}: must be a mapping, got {type(loc).__name__}") + continue + if "file" not in loc: + errors.append(f"{loc_prefix}: missing 'file'") + elif not isinstance(loc["file"], str): + errors.append(f"{loc_prefix}: 'file' must be a string, got {type(loc['file']).__name__}") + for int_field in ("line", "line_start", "line_end"): + if int_field in loc and not isinstance(loc[int_field], int): + errors.append( + f"{loc_prefix}: '{int_field}' must be an integer, " + f"got {type(loc[int_field]).__name__}" + ) + if "lines" in loc: + if not isinstance(loc["lines"], list): + errors.append( + f"{loc_prefix}: 'lines' must be a list, got {type(loc['lines']).__name__}" + ) + elif not all(isinstance(ln, int) for ln in loc["lines"]): + errors.append(f"{loc_prefix}: 'lines' must contain only integers") + + if errors: + for e in errors: + print(f"ERROR: YAML validation: {e}", file=sys.stderr) + sys.exit(1) + except Exception as error: + print(f"ERROR: YAML validation: {error}", file=sys.stderr) + sys.exit(1) + + +def parse_args() -> argparse.Namespace: + """Parse command-line arguments.""" + parser = argparse.ArgumentParser(description="Coverage justification processor") + parser.add_argument( + "--yaml", + type=Path, + required=True, + help="Path to coverage_justifications.yaml", + ) + parser.add_argument( + "--source-root", + type=Path, + required=True, + help="Root directory of source files", + ) + parser.add_argument( + "--output", + type=Path, + required=True, + help="Output path for resolved justification manifest (JSON)", + ) + parser.add_argument( + "--file-filter", + type=str, + default="cpp,h,hpp,cc,rs", + help="Comma-separated file extensions to scan (default: cpp,h,hpp,cc,rs)", + ) + parser.add_argument( + "--platform", + type=str, + default=None, + choices=sorted(VALID_PLATFORMS), + help="Target platform for filtering justifications (default: all platforms apply)", + ) + return parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/score_coverage/merger.py b/score_coverage/merger.py new file mode 100644 index 0000000..fc508f6 --- /dev/null +++ b/score_coverage/merger.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Per-test coverage output generator using llvm-cov. + +This script is invoked by Bazel as the --coverage_output_generator for each test. +It receives profraw files from test execution, merges them into profdata, generates +an HTML coverage report using llvm-cov show, and packages everything into a zip file +that the reporter can later aggregate. + +Expected Bazel interface (from collect_coverage.sh): + --coverage_dir= Directory containing *.profraw files + --output_file= Where to write the output (zip) + --source_file_manifest= File listing instrumented sources and object files + --filter_sources= Source path regexes to exclude (repeatable) + [--sources_to_replace_file=] Optional source mapping file +""" + +import argparse +import json +import os +import subprocess +import sys +import zipfile +from pathlib import Path +from typing import List, Set + + +def main() -> None: + args = parse_args() + + # Get object files from the manifest. + object_files = get_object_files_from_manifest(args.source_file_manifest) + if not object_files: + print("INFO: No instrumented object files found, skipping coverage.", file=sys.stderr) + cleanup_dangling_symlinks(args.coverage_dir) + sys.exit(0) + + # Find profraw files. + profraw_files = sorted(args.coverage_dir.glob("*.profraw")) + if not profraw_files: + print("INFO: No *.profraw files found, skipping coverage.", file=sys.stderr) + cleanup_dangling_symlinks(args.coverage_dir) + sys.exit(0) + + llvm_profdata = find_llvm_profdata() + if not llvm_profdata: + print( + "ERROR: llvm-profdata not found (neither LLVM_PROFDATA nor " + "RUST_LLVM_PROFDATA resolved to an existing binary).", + file=sys.stderr, + ) + sys.exit(1) + + # Merge profraw → profdata. + profdata_dir = args.coverage_dir / "profdata" + profdata_dir.mkdir(exist_ok=True) + profdata_file = profdata_dir / "target.profdata" + + run_command( + [ + llvm_profdata, + "merge", + "--sparse", + "--output", + str(profdata_file), + ] + + [str(f) for f in profraw_files] + ) + + # Create meta.json with object files for the reporter. + meta_dir = args.coverage_dir / "meta" + meta_dir.mkdir(exist_ok=True) + meta = { + "object_files": [os.path.realpath(f) for f in sorted(object_files)], + } + with open(meta_dir / "meta.json", "w", encoding="utf-8") as f: + json.dump(meta, f) + + # Package into zip at output_file. + create_zip( + root=args.coverage_dir, + directories=[profdata_dir, meta_dir], + output_file=args.output_file, + ) + + # Clean up dangling symlinks in coverage_dir that would cause Bazel tree + # artifact validation to fail (e.g. the 'gcov' symlink created by + # collect_cc_coverage.sh's init_gcov() pointing into the destroyed sandbox). + cleanup_dangling_symlinks(args.coverage_dir) + + target = os.environ.get("TEST_TARGET", "unknown") + print(f"INFO: Coverage merger completed for '{target}'", file=sys.stderr) + + +def find_llvm_profdata() -> str: + """Locate the llvm-profdata binary. + + C++ tests: Bazel exports LLVM_PROFDATA from the cc toolchain. + Rust tests: rules_rust exports RUST_LLVM_PROFDATA (an execroot-relative + path to the rust_toolchain's llvm_profdata) instead; resolve it against + the current directory, ROOT and the runfiles dir. + """ + direct = os.environ.get("LLVM_PROFDATA") + if direct and Path(direct).exists(): + return direct + + rust = os.environ.get("RUST_LLVM_PROFDATA") + if rust: + exec_root = Path(os.environ.get("ROOT", ".")) + runfiles_dir = Path(os.environ.get("RUNFILES_DIR", "")) / os.environ.get("TEST_WORKSPACE", "_main") + for candidate in [Path(rust), exec_root / rust, runfiles_dir / rust]: + if candidate.exists(): + return str(candidate) + + return "" + + +def cleanup_dangling_symlinks(directory: Path) -> None: + """Remove symlinks in the coverage directory that would become dangling. + + Bazel's tree artifact validation rejects directories containing dangling + symlinks. The 'gcov' symlink created by collect_cc_coverage.sh's init_gcov() + points into the sandbox which is torn down before validation runs. Since we + use llvm-cov directly, this symlink is not needed. + """ + gcov_link = directory / "gcov" + if gcov_link.is_symlink(): + gcov_link.unlink() + + # Also remove any other symlinks pointing into sandbox paths. + for entry in directory.iterdir(): + if entry.is_symlink(): + target = os.readlink(entry) + if "sandbox" in target: + entry.unlink() + + +def get_object_files_from_manifest(source_file_manifest: Path) -> Set[str]: + """Parse the coverage manifest to find instrumented object files.""" + runfiles_dir = Path(os.environ.get("RUNFILES_DIR", "")) / os.environ.get("TEST_WORKSPACE", "_main") + root = os.environ.get("ROOT") + if not root: + # Bazel's coverage collection exports ROOT (the exec root) to the + # LCOV merger action; without it manifest paths cannot be resolved. + print( + "ERROR: ROOT environment variable is not set; the merger must be " + "invoked by Bazel's coverage collection (--coverage_output_generator).", + file=sys.stderr, + ) + sys.exit(1) + exec_root = Path(root) + + object_files = set() + with open(source_file_manifest, encoding="utf-8") as f: + manifests = [line.strip() for line in f.readlines()] + + for manifest in manifests: + if "objects_list.txt" in manifest: + with open(manifest, encoding="utf-8") as f: + for line in f: + obj_path = line.strip() + if not obj_path: + continue + # Try runfiles first, then exec_root. + candidate = runfiles_dir / obj_path + if candidate.exists(): + object_files.add(str(candidate)) + else: + object_files.add(str(exec_root / obj_path)) + else: + # Rust tests: rules_rust lists the instrumented test executable + # itself in the manifest (via coverage metadata_files) instead of + # an objects_list.txt. Pick up manifest entries that are ELF + # binaries directly. Skip external/ entries: the rust_toolchain + # also lists its llvm-cov/llvm-profdata binaries as metadata + # files, and those are not instrumented objects. + if manifest.startswith("external/") or "/external/" in manifest: + continue + for candidate in [runfiles_dir / manifest, exec_root / manifest, Path(manifest)]: + if candidate.is_file() and is_elf(candidate): + object_files.add(str(candidate)) + break + + return object_files + + +def is_elf(path: Path) -> bool: + """Return True if the file at path is an ELF binary.""" + try: + with open(path, "rb") as f: + return f.read(4) == b"\x7fELF" + except OSError: + return False + + +def run_command(cmd: List[str]) -> subprocess.CompletedProcess: + """Run a command and exit on failure.""" + try: + return subprocess.run( + cmd, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + except subprocess.CalledProcessError as e: + print(f"ERROR: Command failed with code {e.returncode}:", file=sys.stderr) + print(f" {' '.join(cmd)}", file=sys.stderr) + if e.stdout: + print(e.stdout, file=sys.stderr) + sys.exit(1) + + +def create_zip(root: Path, directories: List[Path], output_file: Path) -> None: + """Create a zip file from the given directories relative to root.""" + with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf: + for directory in directories: + if not directory.exists(): + continue + for dirpath, _, files in os.walk(directory): + for filename in files: + file_path = Path(dirpath) / filename + arcname = file_path.relative_to(root) + zf.write(file_path, arcname) + + +def parse_args() -> argparse.Namespace: + """Parse command-line arguments matching the Bazel LCOV_MERGER interface.""" + parser = argparse.ArgumentParser(description="LLVM coverage merger for Bazel") + parser.add_argument("--coverage_dir", type=Path, required=True) + parser.add_argument("--output_file", type=Path, required=True) + parser.add_argument("--source_file_manifest", type=Path, required=True) + parser.add_argument("--filter_sources", action="append", default=[]) + parser.add_argument("--sources_to_replace_file", type=str, default=None) + return parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/score_coverage/reporter.py b/score_coverage/reporter.py new file mode 100644 index 0000000..e743a5a --- /dev/null +++ b/score_coverage/reporter.py @@ -0,0 +1,775 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Final coverage report generator using llvm-cov. + +This script is invoked by Bazel as the --coverage_report_generator after all tests +complete. It reads the per-test zip files produced by the merger, merges all profdata +into one, and generates the final combined HTML report. + +Expected Bazel interface: + --reports_file= Text file listing paths to all per-test coverage outputs + --output_file= Where to write the final report (zip) +""" + +import argparse +import json +import os +import re +import subprocess +import sys +import zipfile +from pathlib import Path +from typing import List, Optional, Set, Tuple +from python.runfiles import Runfiles + + +def main() -> None: + """Main entry point.""" + args = parse_args() + r = Runfiles.Create() + + # Read the list of per-test report files. + reports = read_reports_file(args.reports_file) + if not reports: + print("INFO: No coverage reports listed; writing empty output.", file=sys.stderr) + write_empty_output(args.output_file) + return + + # Extract profdata and object files from each per-test zip. + valid_profdata_files, valid_object_files = extract_reports(reports) + + if not valid_profdata_files or not valid_object_files: + print("INFO: No valid profdata or object files found; writing empty output.", file=sys.stderr) + write_empty_output(args.output_file) + return + + sorted_objects = sorted(valid_object_files) + + # Resolve the llvm tools. The reporter_wrapper passes explicit rlocation + # paths for the consumer-supplied toolchain labels; the bare + # "llvm_toolchain/..." forms remain as a fallback for in-repo setups. + llvm_bin_path = resolve_tool(r, args.llvm_cov, "llvm_toolchain/llvm-cov") + llvm_profdata_path = resolve_tool(r, args.llvm_profdata, "llvm_toolchain/llvm-profdata") + if not llvm_bin_path or not llvm_profdata_path: + print( + "ERROR: llvm-cov/llvm-profdata not found in runfiles. Pass --llvm_cov " + "and --llvm_profdata (the score_coverage_reporter macro does this).", + file=sys.stderr, + ) + sys.exit(1) + + # Merge all per-test profdata files. + merged_profdata = Path.cwd() / "merged_coverage.profdata" + merge_inputs = sorted(set(valid_profdata_files)) + run_command( + [ + str(llvm_profdata_path), + "merge", + "--output", + str(merged_profdata), + ] + + merge_inputs + ) + + # Load baseline objects (production library archives) for zero-coverage baseline. + baseline_objects = load_baseline_objects(r, args.baseline_objects, args.workspace_root) + + # Rust rlib archives (exposed as .a symlinks by rules_rust) start with a + # lib.rmeta member, which makes llvm-cov reject the whole archive with + # "no coverage data found" even though the .o members carry the covmap. + # Expand such archives into their object members. + baseline_objects = expand_rlib_archives(baseline_objects, Path.cwd() / "rlib_baseline_objects") + + # Determine filter regexes: prefer allowlist-based filtering, fall back to manual regexes. + workspace_root = args.workspace_root + allowlist_files = [] + filter_regexes = [] + baseline_only_archives = [] + baseline_only_files = set() + + if args.coverage_allowlist: + allowlist_files = load_coverage_allowlist(r, args.coverage_allowlist) + if allowlist_files: + print(f"INFO: Using coverage allowlist with {len(allowlist_files)} source files.", file=sys.stderr) + allowlist_set = set(allowlist_files) + + # Get files covered by test binaries. + test_covered_files = get_covered_files(llvm_bin_path, sorted_objects, str(merged_profdata), workspace_root) + print(f"INFO: Test binaries cover {len(test_covered_files)} files.", file=sys.stderr) + + # Get files from baseline archives via a SEPARATE llvm-cov run. + # Combining archives with test binaries in a single llvm-cov invocation + # causes some files to vanish (suspected llvm-cov deduplication issue). + # Some archives may have oversized coverage mappings ("malformed coverage + # data"), so we iteratively remove bad ones. + baseline_files = set() + if baseline_objects: + baseline_files = get_covered_files(llvm_bin_path, baseline_objects, None, workspace_root) + print(f"INFO: Baseline archives contain {len(baseline_files)} files.", file=sys.stderr) + + # Files only in baseline archives (not in any test binary). + baseline_only_files = (baseline_files & allowlist_set) - test_covered_files + baseline_only_archives = [] + if baseline_only_files: + print( + f"INFO: {len(baseline_only_files)} allowlisted files only in baseline " + f"(e.g., {sorted(baseline_only_files)[:5]})", + file=sys.stderr, + ) + # Use all valid baseline archives for LCOV generation. + # The _filter_lcov function will filter to only baseline-only files. + baseline_only_archives = list(baseline_objects) + + # Union of test + baseline for exclude-set calculation. + all_covered_files = test_covered_files | baseline_files + files_to_exclude = all_covered_files - allowlist_set + filter_regexes = [re.escape(f) + "$" for f in sorted(files_to_exclude)] + print(f"INFO: Excluding {len(filter_regexes)} files not in allowlist.", file=sys.stderr) + else: + print("ERROR: Coverage allowlist is empty, falling back to filter_regexes.txt.", file=sys.stderr) + sys.exit(-1) + cxxfilt = find_cxxfilt(llvm_bin_path, r, args.llvm_cxxfilt) + common_args = { + "llvm_bin_path": llvm_bin_path, + "objects": sorted_objects, + "instr_profile": str(merged_profdata), + "filter_regexes": sorted(filter_regexes), + "workspace_root": workspace_root, + } + + # Generate HTML report including baseline-only files when valid archives are available. + html_report_dir = Path.cwd() / "html_report" + if baseline_only_archives: + all_html_objects = sorted_objects + baseline_only_archives + html_args = { + **common_args, + "objects": all_html_objects, + } + try: + run_llvm_cov_show( + **html_args, + output_format="html", + html_report_dir=html_report_dir, + cxxfilt=cxxfilt, + ) + except SystemExit: + # Some baseline archives caused llvm-cov show to fail; retry with test binaries only. + print( + "WARNING: HTML generation with baseline archives failed; falling back to test-only HTML.", + file=sys.stderr, + ) + run_llvm_cov_show( + **common_args, + output_format="html", + html_report_dir=html_report_dir, + cxxfilt=cxxfilt, + ) + else: + run_llvm_cov_show( + **common_args, + output_format="html", + html_report_dir=html_report_dir, + cxxfilt=cxxfilt, + ) + + # Rewrite absolute workspace paths in the HTML pages so unpacked report + # archives remain browsable outside the machine that produced them. + _make_html_paths_relative(html_report_dir, workspace_root) + + # Generate LCOV report from test binaries. + lcov_report_dir = Path.cwd() / "lcov_report" + lcov_report_dir.mkdir(exist_ok=True) + lcov_result = run_llvm_cov_export(**common_args) + lcov_content = lcov_result.stdout + + # If there are baseline-only files, generate a separate baseline LCOV and merge. + if baseline_only_archives: + baseline_lcov_args = { + "llvm_bin_path": llvm_bin_path, + "objects": baseline_only_archives, + "instr_profile": None, + "filter_regexes": [], # No filtering — we only have the needed archives. + "workspace_root": workspace_root, + } + baseline_lcov = run_llvm_cov_export(**baseline_lcov_args) + if baseline_lcov.stdout: + # Filter baseline LCOV to only include baseline-only files. + filtered_baseline = _filter_lcov(baseline_lcov.stdout, baseline_only_files) + if filtered_baseline: + lcov_content += filtered_baseline + print(f"INFO: Merged baseline LCOV for {len(baseline_only_files)} files.", file=sys.stderr) + + # Strip the absolute workspace root from SF: records so the LCOV file is + # portable (IDE gutters, SonarQube, reports produced inside containers). + lcov_content = _make_lcov_paths_relative(lcov_content, workspace_root) + + with open(lcov_report_dir / "lcov.dat", "w", encoding="utf-8") as f: + f.write(lcov_content) + + # Generate text summary. + text_report_dir = Path.cwd() / "text_report" + text_report_dir.mkdir(exist_ok=True) + summary = run_llvm_cov_report(**common_args) + with open(text_report_dir / "summary.txt", "w", encoding="utf-8") as f: + f.write(summary.stdout) + print(summary.stdout, file=sys.stderr) + + # Package everything into the output zip. + directories = [html_report_dir, lcov_report_dir, text_report_dir] + create_zip( + root=Path.cwd(), + directories=directories, + output_file=args.output_file, + ) + + print(f"INFO: Coverage reporter completed. Output: {args.output_file}", file=sys.stderr) + + +def _make_lcov_paths_relative(lcov_content: str, workspace_root: str) -> str: + """Rewrite absolute SF: paths under workspace_root to workspace-relative ones. + + Paths outside the workspace (external deps that survived filtering) are + left unchanged. + """ + prefix = workspace_root if workspace_root.endswith("/") else workspace_root + "/" + sf_prefix = "SF:" + prefix + lines = [] + for line in lcov_content.splitlines(keepends=True): + if line.startswith(sf_prefix): + lines.append("SF:" + line[len(sf_prefix) :]) + else: + lines.append(line) + return "".join(lines) + + +_SOURCE_TITLE_RE = re.compile(r"(
)([^<]*)(
)") + + +def _make_html_paths_relative(html_dir: Path, workspace_root: str) -> None: + """Rewrite absolute workspace paths in llvm-cov HTML page titles. + + Only the source-name-title header text is touched — hrefs and the on-disk + page layout embed the same path components without a leading slash, and a + blanket text replacement would corrupt them. + """ + if not html_dir.exists(): + return + prefix = workspace_root if workspace_root.endswith("/") else workspace_root + "/" + + def _repl(match: "re.Match") -> str: + title = match.group(2) + if title.startswith(prefix): + title = title[len(prefix) :] + return match.group(1) + title + match.group(3) + + for page in html_dir.rglob("*.html"): + text = page.read_text(encoding="utf-8", errors="replace") + new_text = _SOURCE_TITLE_RE.sub(_repl, text) + if new_text != text: + page.write_text(new_text, encoding="utf-8") + + +def _filter_lcov(lcov_content: str, target_files: set) -> str: + """Filter LCOV content to only include records for target files. + + LCOV format: SF: starts a record, end_of_record ends it. + """ + result = [] + current_record = [] + include = False + + for line in lcov_content.splitlines(keepends=True): + if line.startswith("SF:"): + current_record = [line] + filepath = line[3:].strip() + # Check if the file path (or its suffix) matches any target file. + include = any(filepath.endswith(f) for f in target_files) + elif line.strip() == "end_of_record": + current_record.append(line) + if include: + result.extend(current_record) + current_record = [] + include = False + else: + current_record.append(line) + + return "".join(result) + + +def get_covered_files( + llvm_bin_path: Path, + objects: List[str], + instr_profile: Optional[str], + workspace_root: str, +) -> set: + """Run a quick llvm-cov report to discover all files with coverage data. + + Returns a set of workspace-relative file paths. + """ + cmd = [ + str(llvm_bin_path), + "report", + f"--path-equivalence=/proc/self/cwd/,{workspace_root}", + ] + if instr_profile is None: + cmd.append("--empty-profile") + else: + cmd.extend(["--instr-profile", instr_profile]) + cmd.append(objects[0]) + for obj in objects[1:]: + cmd.extend(["--object", obj]) + + result = run_command(cmd) + if result.returncode != 0: + return set() + + files = set() + in_files = False + for line in result.stdout.splitlines(): + if line.startswith("---"): + in_files = True + continue + if line.startswith("TOTAL"): + break + if not in_files: + continue + # Extract filename (everything before first multi-space + digit sequence) + match = re.match(r"^(.+?)\s{2,}\d+", line) + if match: + filename = match.group(1).strip() + # Normalize to workspace-relative form. llvm-cov report prints the + # raw covmap path: for C++ that is the recorded compilation dir + # /proc/self/cwd/, --path-equivalence does not rewrite the + # DISPLAYED path. Rust covmap paths are already exec-root relative. + for prefix in (workspace_root, "/proc/self/cwd/"): + if filename.startswith(prefix): + filename = filename[len(prefix) :] + break + files.add(filename) + + return files + + +def run_llvm_cov_show( + llvm_bin_path: Path, + objects: List[str], + instr_profile: Optional[str], + filter_regexes: List[str], + workspace_root: str, + output_format: str, + html_report_dir: Path = None, + cxxfilt: str = "", +) -> subprocess.CompletedProcess: + """Run llvm-cov show.""" + cmd = [ + str(llvm_bin_path), + "show", + f"--format={output_format}", + f"--path-equivalence=/proc/self/cwd/,{workspace_root}", + f"--compilation-dir={workspace_root}", + "--show-branches=count", + "--show-region-summary=0", + ] + + if cxxfilt: + cmd.append(f"--Xdemangler={cxxfilt}") + + for regex in filter_regexes: + cmd.append(f"--ignore-filename-regex={regex}") + + if html_report_dir: + cmd.append(f"--output-dir={html_report_dir}") + cmd.append("--coverage-watermark=100,50") + cmd.append("--show-expansions") + + if instr_profile is None: + cmd.append("--empty-profile") + else: + cmd.extend(["--instr-profile", instr_profile]) + cmd.append(objects[0]) + for obj in objects[1:]: + cmd.extend(["--object", obj]) + + return run_command(cmd) + + +def run_llvm_cov_export( + llvm_bin_path: Path, + objects: List[str], + instr_profile: Optional[str], + filter_regexes: List[str], + workspace_root: str, +) -> subprocess.CompletedProcess: + """Run llvm-cov export to produce LCOV format.""" + cmd = [ + str(llvm_bin_path), + "export", + "--format=lcov", + f"--path-equivalence=/proc/self/cwd/,{workspace_root}", + f"--compilation-dir={workspace_root}", + ] + + for regex in filter_regexes: + cmd.append(f"--ignore-filename-regex={regex}") + + if instr_profile is None: + cmd.append("--empty-profile") + else: + cmd.extend(["--instr-profile", instr_profile]) + cmd.append(objects[0]) + for obj in objects[1:]: + cmd.extend(["--object", obj]) + + # Keep stderr separate: llvm-cov warnings must not end up in the LCOV data. + return run_command(cmd, separate_stderr=True) + + +def run_llvm_cov_report( + llvm_bin_path: Path, + objects: List[str], + instr_profile: Optional[str], + filter_regexes: List[str], + workspace_root: str, +) -> subprocess.CompletedProcess: + """Run llvm-cov report for a text summary.""" + cmd = [ + str(llvm_bin_path), + "report", + f"--path-equivalence=/proc/self/cwd/,{workspace_root}", + "--show-region-summary=0", + "--show-branch-summary=1", + ] + + for regex in filter_regexes: + cmd.append(f"--ignore-filename-regex={regex}") + + if instr_profile is None: + cmd.append("--empty-profile") + else: + cmd.extend(["--instr-profile", instr_profile]) + cmd.append(objects[0]) + for obj in objects[1:]: + cmd.extend(["--object", obj]) + + return run_command(cmd) + + +def extract_reports(reports: List[str]) -> Tuple[Set[str], Set[str]]: + """Extract profdata and object files from per-test zip files.""" + valid_profdata_files = set() + valid_object_files = set() + + for i, report_path in enumerate(reports): + # Skip baseline_coverage files (LCOV format, not our zip). + if "baseline_coverage" in report_path: + continue + + report = Path(report_path) + if not report.exists() or report.stat().st_size == 0: + continue + + # Check if it's a valid zip. + if not zipfile.is_zipfile(report): + continue + + profdata_name = f"coverage_report_{i:08d}.profdata" + + try: + with zipfile.ZipFile(report, "r") as archive: + # Extract meta. + meta_json = archive.read("meta/meta.json") + target_meta = json.loads(meta_json) + + # Extract profdata. + profdata_content = archive.read("profdata/target.profdata") + profdata_path = Path.cwd() / profdata_name + with open(profdata_path, "wb") as f: + f.write(profdata_content) + + valid_profdata_files.add(str(profdata_path)) + + # Collect object files. + for obj in target_meta.get("object_files", []): + if obj and Path(obj).exists(): + valid_object_files.add(os.path.realpath(obj)) + + except (zipfile.BadZipFile, KeyError, json.JSONDecodeError) as e: + print(f"WARNING: Skipping invalid report {report_path}: {e}", file=sys.stderr) + continue + + return valid_profdata_files, valid_object_files + + +def read_reports_file(reports_file: Path) -> List[str]: + """Read the reports file listing all per-test coverage outputs.""" + with open(reports_file, encoding="utf-8") as f: + return [line.strip() for line in f if line.strip()] + + +def _read_ar_members(path: str) -> List[tuple]: + """Parse a Unix ar archive, returning (name, data_offset, size) tuples. + + Handles the GNU long-name table ("//" member with "/" references). + Returns an empty list when the file is not an ar archive. + """ + members = [] + longnames = b"" + with open(path, "rb") as f: + if f.read(8) != b"!\n": + return [] + while True: + header = f.read(60) + if len(header) < 60: + break + name = header[0:16].decode(errors="replace").rstrip() + try: + size = int(header[48:58].decode().strip() or "0") + except ValueError: + break + data_offset = f.tell() + if name == "//": + longnames = f.read(size) + else: + if name.startswith("/") and name[1:].isdigit(): + start = int(name[1:]) + end = longnames.find(b"\n", start) + name = longnames[start:end].decode(errors="replace").rstrip("/") + elif name.endswith("/"): + name = name[:-1] + members.append((name, data_offset, size)) + f.seek(size, 1) + if size % 2 == 1: + f.seek(1, 1) + return members + + +def expand_rlib_archives(objects: List[str], workdir: Path) -> List[str]: + """Replace Rust rlib archives with their extracted object members. + + llvm-cov rejects rlib archives ("no coverage data found") because of the + leading lib.rmeta member, even though the .o members carry the coverage + mapping. Non-rlib entries (C++ .a archives, executables) pass through + unchanged. + """ + result = [] + extracted = 0 + for obj in objects: + members = _read_ar_members(obj) if obj.endswith((".a", ".rlib")) else [] + if not any(name == "lib.rmeta" for name, _, _ in members): + result.append(obj) + continue + workdir.mkdir(parents=True, exist_ok=True) + with open(obj, "rb") as f: + for index, (name, offset, size) in enumerate(members): + if not name.endswith(".o"): + continue + f.seek(offset) + out_path = workdir / f"{Path(obj).stem}.{index}.o" + out_path.write_bytes(f.read(size)) + result.append(str(out_path)) + extracted += 1 + if extracted: + print(f"INFO: Expanded {extracted} object(s) from Rust rlib baseline archives.", file=sys.stderr) + return result + + +def resolve_tool( + runfiles: Optional[Runfiles], + flag_value: Optional[str], + fallback_rlocation: str, +) -> Optional[Path]: + """Resolve an llvm tool path. + + Preference order: the explicit rlocation path passed by the + reporter_wrapper (consumer-supplied toolchain label), then the legacy + "llvm_toolchain/..." runfiles location, then the raw value as a plain path. + """ + for candidate in (flag_value, fallback_rlocation): + if not candidate: + continue + if runfiles: + location = runfiles.Rlocation(candidate) + if location and Path(location).exists(): + return Path(location) + if Path(candidate).exists(): + return Path(candidate) + return None + + +def find_cxxfilt( + llvm_bin_path: Path, + runfiles: Optional[Runfiles] = None, + explicit: Optional[str] = None, +) -> str: + """Locate llvm-cxxfilt for demangling (C++ Itanium and Rust v0/legacy symbols). + + Tries the explicit rlocation path from the reporter_wrapper first, then the + directory of llvm-cov, then the @llvm_toolchain_llvm distribution via + runfiles (toolchains_llvm declares no alias for llvm-cxxfilt). + Returns an empty string when unavailable (demangling is cosmetic). + """ + if explicit: + resolved = resolve_tool(runfiles, explicit, "") + if resolved: + return str(resolved) + sibling = llvm_bin_path.parent / "llvm-cxxfilt" + if sibling.exists(): + return str(sibling) + r = runfiles or Runfiles.Create() + if r: + location = r.Rlocation("llvm_toolchain_llvm/bin/llvm-cxxfilt") + if location and Path(location).exists(): + return location + return "" + + +def load_coverage_allowlist(runfiles: Runfiles, rlocation_path: str) -> List[str]: + """Load coverage allowlist (package paths) from a file via Bazel runfiles.""" + path = runfiles.Rlocation(rlocation_path) + if not path or not Path(path).exists(): + return [] + + lines = Path(path).read_text(encoding="utf-8").splitlines() + return [line.strip() for line in lines if line.strip() and not line.strip().startswith("#")] + + +def load_baseline_objects( + runfiles: Runfiles, + rlocation_path: str, + workspace_root: str, +) -> List[str]: + """Load baseline object archive paths and resolve them to absolute paths. + + The objects manifest lists relative paths to .a files. When the reporter runs + in the exec config, the manifest paths use the exec config dir + (e.g., k8-opt-exec-*). + """ + if not rlocation_path: + return [] + + path = runfiles.Rlocation(rlocation_path) + if not path or not Path(path).exists(): + print(f"WARNING: Baseline objects manifest not found: {rlocation_path}", file=sys.stderr) + return [] + + lines = Path(path).read_text(encoding="utf-8").splitlines() + resolved = [] + for line in lines: + line = line.strip() + if not line or line.startswith("#"): + continue + # The manifest lists short_paths of files built by the CONSUMER + # repository, which is always the root module ("_main") in a coverage + # run. Do NOT use runfiles.CurrentRepository() here: this script lives + # in score_coverage, so that would resolve against the wrong repo. + path = runfiles.Rlocation(os.path.join("_main", line)) + if os.path.exists(path): + resolved.append(path) + else: + print(f"ERROR: Baseline object not found: {line}", file=sys.stderr) + sys.exit(-1) + return sorted(resolved) + + +def run_command(cmd: List[str], separate_stderr: bool = False) -> subprocess.CompletedProcess: + """Run a command and exit on failure. + + With separate_stderr the child's stderr is captured separately and + forwarded to our stderr — required when stdout is machine-consumed data + (LCOV) that llvm-cov warnings must not corrupt. + """ + try: + result = subprocess.run( + cmd, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE if separate_stderr else subprocess.STDOUT, + text=True, + ) + if separate_stderr and result.stderr: + print(result.stderr, file=sys.stderr) + return result + except subprocess.CalledProcessError as e: + print(f"ERROR: Command failed with code {e.returncode}:", file=sys.stderr) + print(f" {' '.join(cmd[:10])}{'...' if len(cmd) > 10 else ''}", file=sys.stderr) + if e.stdout: + print(e.stdout, file=sys.stderr) + if e.stderr: + print(e.stderr, file=sys.stderr) + sys.exit(1) + + +def write_empty_output(output_file: Path) -> None: + """Write an empty (but valid) zip so Bazel's coverage action still succeeds. + + Matches Bazel's own behaviour for runs that produce no coverage data + (e.g. a coverage invocation whose tests were all filtered out). + """ + with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED): + pass + + +def create_zip(root: Path, directories: List[Path], output_file: Path) -> None: + """Create a zip file from the given directories relative to root.""" + with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf: + for directory in directories: + if not directory.exists(): + continue + for dirpath, _, files in os.walk(directory): + for filename in files: + file_path = Path(dirpath) / filename + arcname = file_path.relative_to(root) + zf.write(file_path, arcname) + + +def parse_args() -> argparse.Namespace: + """Parse command-line arguments matching the Bazel coverage_report_generator interface.""" + parser = argparse.ArgumentParser(description="LLVM coverage reporter for Bazel") + parser.add_argument("--output_file", type=Path, required=True) + parser.add_argument("--reports_file", type=Path, required=True) + parser.add_argument( + "--coverage_allowlist", + type=str, + default=None, + help="Rlocation path to the coverage allowlist file (preferred over filter_regexes)", + ) + parser.add_argument( + "--baseline_objects", + type=str, + default=None, + help="Rlocation path to the baseline objects manifest (archive .a files)", + ) + parser.add_argument( + "--workspace_root", type=str, required=True, help="Real workspace root path for source path mapping" + ) + parser.add_argument( + "--llvm_cov", type=str, default=None, help="Rlocation path to llvm-cov (supplied by score_coverage_reporter)" + ) + parser.add_argument( + "--llvm_profdata", + type=str, + default=None, + help="Rlocation path to llvm-profdata (supplied by score_coverage_reporter)", + ) + parser.add_argument( + "--llvm_cxxfilt", + type=str, + default=None, + help="Rlocation path to llvm-cxxfilt (supplied by score_coverage_reporter)", + ) + return parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/score_coverage/reporter_wrapper.bzl b/score_coverage/reporter_wrapper.bzl new file mode 100644 index 0000000..204bf2a --- /dev/null +++ b/score_coverage/reporter_wrapper.bzl @@ -0,0 +1,155 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"""Executable wrapper rule for the coverage reporter. + +Instantiated in the CONSUMER repository (via the score_coverage_reporter +macro) so that the consumer's coverage_scope, MODULE.bazel and LLVM tool +labels can be wired into the reporter, which itself lives in score_coverage. +""" + +def _rlocation_path(ctx, file): + """Return the Runfiles.Rlocation()-compatible path for a Bazel File. + + External-repo files have short_path = "..//" — strip the + "../". Main-workspace files have short_path = "/" — prepend + the workspace name. Required because this rule mixes files from the + consumer repo (_main) and from score_coverage / toolchain repos. + """ + if file.short_path.startswith("../"): + return file.short_path[3:] + return ctx.workspace_name + "/" + file.short_path + +def _reporter_wrapper_impl(ctx): + launcher = ctx.actions.declare_file(ctx.label.name + ".sh") + + reporter = ctx.executable.reporter + module_bazel = ctx.file.module_bazel + coverage_scope = ctx.attr.coverage_scope + allowlist_group = coverage_scope[OutputGroupInfo].allowlist.to_list() + objects_group = coverage_scope[OutputGroupInfo].objects.to_list() + object_files = coverage_scope[OutputGroupInfo].object_files + + if len(allowlist_group) != 1: + fail("coverage_scope must provide exactly one allowlist file") + if len(objects_group) != 1: + fail("coverage_scope must provide exactly one objects manifest file") + + allowlist = allowlist_group[0] + baseline_objects = objects_group[0] + + cxxfilt_line = "" + if ctx.file.llvm_cxxfilt: + cxxfilt_line = ' --llvm_cxxfilt="{}" \\\n'.format( + _rlocation_path(ctx, ctx.file.llvm_cxxfilt), + ) + + # Bazel invokes the coverage report generator from within its coverage + # machinery, where an inherited RUNFILES_DIR may point at ANOTHER tool's + # runfiles tree. Derive our own runfiles directory from $0 first and only + # fall back to the inherited value. + script = """#!/usr/bin/env bash +set -euo pipefail +SELF_RUNFILES_DIR="$(cd "$(dirname "$0")" && pwd)/$(basename "$0").runfiles" +if [[ -d "${{SELF_RUNFILES_DIR}}" ]]; then + RUNFILES_DIR="${{SELF_RUNFILES_DIR}}" +elif [[ -z "${{RUNFILES_DIR:-}}" || ! -d "${{RUNFILES_DIR}}" ]]; then + echo "ERROR: could not locate the reporter_wrapper runfiles directory" >&2 + exit 1 +fi +export RUNFILES_DIR +WORKSPACE_ROOT="$(cd "$(dirname "$(readlink -f "${{RUNFILES_DIR}}/{module_bazel}")")" && pwd)/" +exec "${{RUNFILES_DIR}}/{reporter}" \\ + --coverage_allowlist="{allowlist}" \\ + --baseline_objects="{baseline_objects}" \\ + --workspace_root="${{WORKSPACE_ROOT}}" \\ + --llvm_cov="{llvm_cov}" \\ + --llvm_profdata="{llvm_profdata}" \\ +{cxxfilt_line} "$@" +""".format( + module_bazel = _rlocation_path(ctx, module_bazel), + reporter = _rlocation_path(ctx, reporter), + allowlist = _rlocation_path(ctx, allowlist), + baseline_objects = _rlocation_path(ctx, baseline_objects), + llvm_cov = _rlocation_path(ctx, ctx.file.llvm_cov), + llvm_profdata = _rlocation_path(ctx, ctx.file.llvm_profdata), + cxxfilt_line = cxxfilt_line, + ) + + ctx.actions.write( + output = launcher, + content = script, + is_executable = True, + ) + + direct_files = [ + reporter, + allowlist, + baseline_objects, + module_bazel, + ctx.file.llvm_cov, + ctx.file.llvm_profdata, + ] + if ctx.file.llvm_cxxfilt: + direct_files.append(ctx.file.llvm_cxxfilt) + + runfiles = ctx.runfiles( + files = direct_files, + transitive_files = object_files, + ).merge(ctx.attr.reporter[DefaultInfo].default_runfiles) + for tool in (ctx.attr.llvm_cov, ctx.attr.llvm_profdata, ctx.attr.llvm_cxxfilt): + if tool: + runfiles = runfiles.merge(tool[DefaultInfo].default_runfiles) + + return [DefaultInfo( + executable = launcher, + runfiles = runfiles, + )] + +reporter_wrapper = rule( + implementation = _reporter_wrapper_impl, + executable = True, + attrs = { + "reporter": attr.label( + executable = True, + cfg = "exec", + default = Label("//score_coverage:reporter"), + doc = "The coverage reporter binary (defaults to score_coverage's).", + ), + "coverage_scope": attr.label( + cfg = "target", + mandatory = True, + doc = "A score_coverage_scope target defining the in-scope sources/archives.", + ), + "module_bazel": attr.label( + allow_single_file = True, + mandatory = True, + doc = "The consumer's root MODULE.bazel; used to locate the real workspace root.", + ), + "llvm_cov": attr.label( + allow_single_file = True, + mandatory = True, + doc = "llvm-cov binary, e.g. @llvm_toolchain//:llvm-cov.", + ), + "llvm_profdata": attr.label( + allow_single_file = True, + mandatory = True, + doc = "llvm-profdata binary, e.g. @llvm_toolchain//:llvm-profdata.", + ), + "llvm_cxxfilt": attr.label( + allow_single_file = True, + doc = "Optional llvm-cxxfilt for demangling, e.g. " + + "@llvm_toolchain_llvm//:bin/llvm-cxxfilt.", + ), + }, +) diff --git a/score_coverage/requirements.in b/score_coverage/requirements.in new file mode 100644 index 0000000..c3726e8 --- /dev/null +++ b/score_coverage/requirements.in @@ -0,0 +1 @@ +pyyaml diff --git a/score_coverage/requirements_3_11.txt b/score_coverage/requirements_3_11.txt new file mode 100644 index 0000000..5017dc3 --- /dev/null +++ b/score_coverage/requirements_3_11.txt @@ -0,0 +1,81 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //coverage:requirements_3_11.update +# +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via -r coverage/requirements.in diff --git a/score_coverage/requirements_3_12.txt b/score_coverage/requirements_3_12.txt new file mode 100644 index 0000000..1e80040 --- /dev/null +++ b/score_coverage/requirements_3_12.txt @@ -0,0 +1,81 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# bazel run //coverage:requirements_3_12.update +# +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via -r coverage/requirements.in diff --git a/score_coverage/tests/BUILD b/score_coverage/tests/BUILD new file mode 100644 index 0000000..f49ab02 --- /dev/null +++ b/score_coverage/tests/BUILD @@ -0,0 +1,32 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_python//python:defs.bzl", "py_test") + +py_test( + name = "merger_test", + srcs = ["merger_test.py"], + deps = ["//score_coverage:merger_lib"], +) + +py_test( + name = "reporter_test", + srcs = ["reporter_test.py"], + deps = ["//score_coverage:reporter_lib"], +) + +py_test( + name = "coverage_summary_test", + srcs = ["coverage_summary_test.py"], + deps = ["//score_coverage:coverage_summary_lib"], +) diff --git a/score_coverage/tests/coverage_summary_test.py b/score_coverage/tests/coverage_summary_test.py new file mode 100644 index 0000000..327cc1c --- /dev/null +++ b/score_coverage/tests/coverage_summary_test.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Unit tests for the markdown coverage summary.""" + +import json +import tempfile +import unittest +from pathlib import Path + +from score_coverage.coverage_summary import ( + directory_key, + load_justification_summary, + parse_lcov, + percent, + progress_bar, + render_markdown, + rollup_by_directory, +) + +LCOV_TWO_FILES = ( + "SF:src/foo/a.cpp\n" + "DA:1,1\nDA:2,0\n" + "BRF:4\nBRH:3\n" + "LF:2\nLH:1\n" + "end_of_record\n" + "SF:rust/main.rs\n" + "DA:1,0\n" + "LF:3\nLH:0\n" + "end_of_record\n" +) + + +def _write(tmp: str, name: str, content: str) -> Path: + p = Path(tmp) / name + p.write_text(content, encoding="utf-8") + return p + + +class ParseLcovTest(unittest.TestCase): + def test_missing_file_returns_none(self): + self.assertIsNone(parse_lcov(Path("/nonexistent/lcov.dat"))) + + def test_empty_file_returns_empty_list(self): + with tempfile.TemporaryDirectory() as tmp: + self.assertEqual(parse_lcov(_write(tmp, "e.dat", "")), []) + + def test_line_and_branch_counters(self): + with tempfile.TemporaryDirectory() as tmp: + files = parse_lcov(_write(tmp, "l.dat", LCOV_TWO_FILES)) + self.assertEqual(len(files), 2) + a, main_rs = files + self.assertEqual((a.lines_hit, a.lines_found), (1, 2)) + self.assertEqual((a.branches_hit, a.branches_found), (3, 4)) + self.assertEqual((main_rs.lines_hit, main_rs.lines_found), (0, 3)) + + def test_lf_without_brf_yields_no_branch_data(self): + with tempfile.TemporaryDirectory() as tmp: + files = parse_lcov(_write(tmp, "l.dat", "SF:a.cpp\nLF:5\nLH:2\nend_of_record\n")) + self.assertIsNone(files[0].branches_found) + + def test_brda_fallback_when_no_brf(self): + lcov = "SF:a.cpp\nBRDA:1,0,0,3\nBRDA:1,0,1,-\nBRDA:2,0,0,0\nLF:2\nLH:2\nend_of_record\n" + with tempfile.TemporaryDirectory() as tmp: + files = parse_lcov(_write(tmp, "l.dat", lcov)) + self.assertEqual((files[0].branches_hit, files[0].branches_found), (1, 3)) + + def test_record_without_end_of_record_is_flushed(self): + with tempfile.TemporaryDirectory() as tmp: + files = parse_lcov(_write(tmp, "l.dat", "SF:a.cpp\nLF:1\nLH:1\n")) + self.assertEqual(len(files), 1) + + def test_non_utf8_bytes_do_not_crash(self): + with tempfile.TemporaryDirectory() as tmp: + p = Path(tmp) / "l.dat" + p.write_bytes(b"SF:src/\xff\xfe.cpp\nLF:1\nLH:0\nend_of_record\n") + files = parse_lcov(p) + self.assertEqual(len(files), 1) + self.assertEqual(files[0].lines_found, 1) + + +class MathHelpersTest(unittest.TestCase): + def test_percent_zero_denominator_is_none(self): + self.assertIsNone(percent(0, 0)) + self.assertIsNone(percent(5, 0)) + + def test_progress_bar_bounds(self): + self.assertEqual(progress_bar(0.0), "`" + "░" * 10 + "`") + self.assertEqual(progress_bar(100.0), "`" + "█" * 10 + "`") + self.assertEqual(progress_bar(None), "—") + + def test_directory_key_grouping(self): + self.assertEqual(directory_key("a.cpp"), "(root)") + self.assertEqual(directory_key("src/a.cpp"), "src") + self.assertEqual(directory_key("src/foo/a.cpp"), "src/foo") + self.assertEqual(directory_key("src/foo/bar/a.cpp"), "src/foo") + + +class RollupTest(unittest.TestCase): + def test_worst_directory_first(self): + with tempfile.TemporaryDirectory() as tmp: + files = parse_lcov(_write(tmp, "l.dat", LCOV_TWO_FILES)) + rows = rollup_by_directory(files) + self.assertEqual(rows[0]["directory"], "rust") + self.assertEqual(rows[0]["pct"], 0.0) + self.assertEqual(rows[1]["directory"], "src/foo") + + +class RenderTest(unittest.TestCase): + def _render(self, justification=None): + with tempfile.TemporaryDirectory() as tmp: + files = parse_lcov(_write(tmp, "l.dat", LCOV_TWO_FILES)) + return render_markdown(files, justification) + + def test_empty_input_renders_note(self): + md = render_markdown([], None) + self.assertIn("No coverage records", md) + + def test_overall_table_and_zero_section(self): + md = self._render() + self.assertIn("| Lines | 1 | 5 | 20.00% |", md) + self.assertIn("| Branches | 3 | 4 | 75.00% |", md) + self.assertIn("Files at exact 0% (1)", md) + self.assertIn("`rust/main.rs` (3 lines)", md) + self.assertIn("█", md) # progress bars present + self.assertIn("
", md) + + def test_justification_section(self): + justification = { + "raw_line_coverage_pct": 20.0, + "effective_line_coverage_pct": 40.0, + "raw_branch_coverage_pct": 75.0, + "effective_branch_coverage_pct": 75.0, + "justified_lines": 1, + "justified_branches": 0, + "stale_justifications": 0, + "applied_justification_count": 1, + } + md = self._render(justification) + self.assertIn("Raw vs effective", md) + self.assertIn("| Line coverage | 20.0% | 40.0% |", md) + self.assertIn("1 justification entries applied", md) + + def test_branch_dash_when_no_branch_data(self): + with tempfile.TemporaryDirectory() as tmp: + files = parse_lcov(_write(tmp, "l.dat", "SF:a.cpp\nLF:2\nLH:1\nend_of_record\n")) + md = render_markdown(files, None) + self.assertIn("| Branches | — | — | — | — |", md) + + +class JustificationReportTest(unittest.TestCase): + def test_loads_summary_and_counts_applied(self): + report = { + "version": 1, + "summary": {"raw_line_coverage_pct": 50.0, "justified_lines": 2}, + "applied_justifications": [{"id": "a"}, {"id": "b"}], + } + with tempfile.TemporaryDirectory() as tmp: + p = _write(tmp, "report.json", json.dumps(report)) + summary = load_justification_summary(p) + self.assertEqual(summary["applied_justification_count"], 2) + self.assertEqual(summary["justified_lines"], 2) + + def test_malformed_json_returns_none(self): + with tempfile.TemporaryDirectory() as tmp: + p = _write(tmp, "report.json", "{not json") + self.assertIsNone(load_justification_summary(p)) + + +if __name__ == "__main__": + unittest.main() diff --git a/score_coverage/tests/merger_test.py b/score_coverage/tests/merger_test.py new file mode 100644 index 0000000..733f6fe --- /dev/null +++ b/score_coverage/tests/merger_test.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Unit tests for the per-test coverage merger.""" + +import os +import tempfile +import unittest +from pathlib import Path +from unittest import mock + +from score_coverage.merger import find_llvm_profdata, get_object_files_from_manifest, is_elf + + +class IsElfTest(unittest.TestCase): + def test_elf_magic_is_detected(self): + with tempfile.NamedTemporaryFile(suffix=".bin") as f: + f.write(b"\x7fELF" + b"\x00" * 12) + f.flush() + self.assertTrue(is_elf(Path(f.name))) + + def test_text_file_is_not_elf(self): + with tempfile.NamedTemporaryFile(suffix=".txt") as f: + f.write(b"just some text") + f.flush() + self.assertFalse(is_elf(Path(f.name))) + + def test_missing_file_is_not_elf(self): + self.assertFalse(is_elf(Path("/nonexistent/path/binary"))) + + +class FindLlvmProfdataTest(unittest.TestCase): + def test_llvm_profdata_env_wins(self): + with tempfile.NamedTemporaryFile() as f: + with mock.patch.dict(os.environ, {"LLVM_PROFDATA": f.name}, clear=True): + self.assertEqual(find_llvm_profdata(), f.name) + + def test_rust_llvm_profdata_resolved_against_root(self): + with tempfile.TemporaryDirectory() as root: + tool = Path(root) / "bin" / "llvm-profdata" + tool.parent.mkdir() + tool.write_bytes(b"\x7fELF") + env = {"RUST_LLVM_PROFDATA": "bin/llvm-profdata", "ROOT": root} + with mock.patch.dict(os.environ, env, clear=True): + self.assertEqual(find_llvm_profdata(), str(tool)) + + def test_returns_empty_when_nothing_resolves(self): + env = {"RUST_LLVM_PROFDATA": "does/not/exist"} + with mock.patch.dict(os.environ, env, clear=True): + self.assertEqual(find_llvm_profdata(), "") + + +class GetObjectFilesFromManifestTest(unittest.TestCase): + def test_missing_root_env_is_a_hard_error(self): + """Without ROOT the merger cannot resolve manifest paths — must exit.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as manifest: + manifest.write("some/path\n") + manifest.flush() + with mock.patch.dict(os.environ, {}, clear=True): + with self.assertRaises(SystemExit): + get_object_files_from_manifest(Path(manifest.name)) + + def test_rust_elf_manifest_entry_is_collected(self): + """rules_rust lists the instrumented test executable in the manifest.""" + with tempfile.TemporaryDirectory() as root: + binary = Path(root) / "pkg" / "my_test" + binary.parent.mkdir() + binary.write_bytes(b"\x7fELF" + b"\x00" * 12) + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as manifest: + manifest.write("pkg/my_test\n") + manifest.flush() + with mock.patch.dict(os.environ, {"ROOT": root}, clear=True): + objects = get_object_files_from_manifest(Path(manifest.name)) + self.assertEqual(objects, {str(binary)}) + + def test_external_manifest_entries_are_skipped(self): + """Toolchain-provided metadata files (external/) are not instrumented objects.""" + with tempfile.TemporaryDirectory() as root: + binary = Path(root) / "external" / "tool" + binary.parent.mkdir() + binary.write_bytes(b"\x7fELF" + b"\x00" * 12) + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as manifest: + manifest.write("external/tool\n") + manifest.flush() + with mock.patch.dict(os.environ, {"ROOT": root}, clear=True): + objects = get_object_files_from_manifest(Path(manifest.name)) + self.assertEqual(objects, set()) + + def test_objects_list_entries_are_resolved(self): + """C++ tests provide an objects_list.txt with one object path per line.""" + with tempfile.TemporaryDirectory() as root: + obj = Path(root) / "bazel-out" / "lib.a" + obj.parent.mkdir() + obj.write_bytes(b"!\n") + objects_list = Path(root) / "objects_list.txt" + objects_list.write_text("bazel-out/lib.a\n") + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as manifest: + manifest.write(f"{objects_list}\n") + manifest.flush() + with mock.patch.dict(os.environ, {"ROOT": root}, clear=True): + objects = get_object_files_from_manifest(Path(manifest.name)) + self.assertEqual(objects, {str(obj)}) + + +if __name__ == "__main__": + unittest.main() diff --git a/score_coverage/tests/reporter_test.py b/score_coverage/tests/reporter_test.py new file mode 100644 index 0000000..e3c1a15 --- /dev/null +++ b/score_coverage/tests/reporter_test.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Unit tests for the final coverage reporter.""" + +import tempfile +import unittest +import zipfile +from pathlib import Path + +from score_coverage.reporter import ( + _filter_lcov, + _make_html_paths_relative, + _make_lcov_paths_relative, + _read_ar_members, + expand_rlib_archives, + write_empty_output, +) + + +def _ar_header(name: str, size: int) -> bytes: + """Build a 60-byte Unix ar member header.""" + return (f"{name:<16}{'0':<12}{'0':<6}{'0':<6}{'100644':<8}{size:<10}`\n").encode() + + +def _make_archive(members) -> bytes: + """Build a Unix ar archive from (name, data) tuples.""" + blob = b"!\n" + for name, data in members: + blob += _ar_header(name, len(data)) + data + if len(data) % 2 == 1: + blob += b"\n" + return blob + + +class ReadArMembersTest(unittest.TestCase): + def test_non_archive_returns_empty(self): + with tempfile.NamedTemporaryFile(suffix=".a") as f: + f.write(b"\x7fELF not an archive") + f.flush() + self.assertEqual(_read_ar_members(f.name), []) + + def test_members_are_listed_with_sizes(self): + blob = _make_archive([("lib.rmeta/", b"META"), ("foo.o/", b"OBJDATA")]) + with tempfile.NamedTemporaryFile(suffix=".a") as f: + f.write(blob) + f.flush() + members = _read_ar_members(f.name) + self.assertEqual([(m[0], m[2]) for m in members], [("lib.rmeta", 4), ("foo.o", 7)]) + + def test_gnu_long_name_table_is_resolved(self): + longnames = b"a_very_long_object_file_name.o/\n" + blob = _make_archive([("//", longnames), ("/0", b"LONGOBJ")]) + with tempfile.NamedTemporaryFile(suffix=".a") as f: + f.write(blob) + f.flush() + members = _read_ar_members(f.name) + self.assertEqual([m[0] for m in members], ["a_very_long_object_file_name.o"]) + + +class ExpandRlibArchivesTest(unittest.TestCase): + def test_rlib_is_expanded_to_object_members(self): + """Archives with a lib.rmeta member are replaced by their .o members.""" + blob = _make_archive([("lib.rmeta/", b"META"), ("crate.o/", b"OBJ1"), ("notes.txt/", b"TXT")]) + with tempfile.TemporaryDirectory() as tmp: + rlib = Path(tmp) / "libcrate.a" + rlib.write_bytes(blob) + workdir = Path(tmp) / "extracted" + result = expand_rlib_archives([str(rlib)], workdir) + self.assertEqual(len(result), 1) + self.assertTrue(result[0].endswith(".o")) + self.assertEqual(Path(result[0]).read_bytes(), b"OBJ1") + + def test_plain_cc_archive_passes_through(self): + blob = _make_archive([("mylib.o/", b"OBJ1")]) + with tempfile.TemporaryDirectory() as tmp: + archive = Path(tmp) / "libcc.a" + archive.write_bytes(blob) + result = expand_rlib_archives([str(archive)], Path(tmp) / "x") + self.assertEqual(result, [str(archive)]) + + def test_executable_passes_through(self): + with tempfile.TemporaryDirectory() as tmp: + binary = Path(tmp) / "my_tool" + binary.write_bytes(b"\x7fELF" + b"\x00" * 12) + result = expand_rlib_archives([str(binary)], Path(tmp) / "x") + self.assertEqual(result, [str(binary)]) + + +class FilterLcovTest(unittest.TestCase): + LCOV = "SF:src/foo.cpp\nDA:1,1\nLF:1\nLH:1\nend_of_record\nSF:src/bar.cpp\nDA:1,0\nLF:1\nLH:0\nend_of_record\n" + + def test_only_target_records_survive(self): + result = _filter_lcov(self.LCOV, {"src/bar.cpp"}) + self.assertIn("SF:src/bar.cpp", result) + self.assertNotIn("SF:src/foo.cpp", result) + + def test_suffix_matching(self): + result = _filter_lcov("SF:/abs/prefix/src/foo.cpp\nend_of_record\n", {"src/foo.cpp"}) + self.assertIn("SF:/abs/prefix/src/foo.cpp", result) + + +class MakeLcovPathsRelativeTest(unittest.TestCase): + def test_workspace_paths_become_relative(self): + lcov = "SF:/ws/root/src/foo.cpp\nDA:1,1\nend_of_record\n" + result = _make_lcov_paths_relative(lcov, "/ws/root/") + self.assertEqual(result, "SF:src/foo.cpp\nDA:1,1\nend_of_record\n") + + def test_workspace_root_without_trailing_slash(self): + lcov = "SF:/ws/root/src/foo.cpp\nend_of_record\n" + result = _make_lcov_paths_relative(lcov, "/ws/root") + self.assertEqual(result, "SF:src/foo.cpp\nend_of_record\n") + + def test_external_paths_are_unchanged(self): + lcov = "SF:/other/place/dep.cpp\nend_of_record\n" + self.assertEqual(_make_lcov_paths_relative(lcov, "/ws/root/"), lcov) + + def test_non_sf_lines_are_preserved(self): + lcov = "TN:test\nSF:/ws/root/a.cpp\nDA:5,0\nLF:1\nLH:0\nend_of_record\n" + result = _make_lcov_paths_relative(lcov, "/ws/root/") + self.assertIn("TN:test\n", result) + self.assertIn("DA:5,0\n", result) + + +class MakeHtmlPathsRelativeTest(unittest.TestCase): + def test_source_title_is_rewritten_and_hrefs_untouched(self): + html = ( + "
/ws/root/src/foo.cpp
" + "
link" + ) + with tempfile.TemporaryDirectory() as tmp: + page = Path(tmp) / "page.html" + page.write_text(html) + _make_html_paths_relative(Path(tmp), "/ws/root/") + result = page.read_text() + self.assertIn("
src/foo.cpp
", result) + # The href embeds the path components without a leading slash and must + # never be rewritten. + self.assertIn("href='coverage/ws/root/src/foo.cpp.html'", result) + + def test_missing_dir_is_a_noop(self): + _make_html_paths_relative(Path("/nonexistent/html_dir"), "/ws/root/") + + +class WriteEmptyOutputTest(unittest.TestCase): + def test_produces_valid_empty_zip(self): + with tempfile.TemporaryDirectory() as tmp: + out = Path(tmp) / "out.zip" + write_empty_output(out) + self.assertTrue(zipfile.is_zipfile(out)) + with zipfile.ZipFile(out) as zf: + self.assertEqual(zf.namelist(), []) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/BUILD b/tools/BUILD new file mode 100644 index 0000000..316b8c6 --- /dev/null +++ b/tools/BUILD @@ -0,0 +1,49 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@score_tooling//:defs.bzl", "copyright_checker") +load("@score_tooling//third_party/format:macros.bzl", "use_format_targets") + +# Repository hygiene (copyright headers, formatting). Kept out of the root +# package on purpose: these load from dev dependencies, which consumers of +# @score_coverage do not have. + +copyright_checker( + name = "copyright", + srcs = [ + ".bazelrc", + ".github", + "BUILD", + "COVERAGE_GUIDE.md", + "MODULE.bazel", + "README.md", + "REUSE.toml", + "defs.bzl", + "integration_tests", + "pyproject.toml", + "score_coverage", + "tools", + ], + config = "@score_tooling//cr_checker/resources:config", + template = "@score_tooling//cr_checker/resources:templates", + visibility = ["//visibility:public"], +) + +# C++ and Rust exist only as fixtures inside the nested integration_tests +# workspace, which has its own toolchains; the main module holds Python, +# Starlark and YAML. +use_format_targets(languages = [ + "python", + "starlark", + "yaml", +]) From 0322ad0baa297ec13e6f72aebaa5c7d24cf4b794 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 15:46:31 +0300 Subject: [PATCH 02/17] Port generate_coverage_html from shell to Python The orchestrator holds the COVERAGE_THRESHOLD gate, the one decision downstream verification reports rely on, and as a shell script it could neither be unit tested nor covered. It is now score_coverage/ generate_coverage_html.py with the same CLI (--yaml, --archive, --archive-dir, --platform, --testlogs-subdir, --summary-md, output-dir) and the same observable behaviour, verified by the unchanged integration test (all 9 checks pass). Changes in behaviour, all in the "fail loud, never fail green" direction: - justify / effective_coverage / coverage_summary are called in-process instead of via nested `bazel run`; the three modules take an argv parameter for that. - Exit codes: 0 gate passed, 1 gate failed, 2 no verdict possible (missing or invalid report, bad threshold, justification errors). Previously a tool failure and a failed gate were both exit 1. - The gate compares the unrounded percentage; the shell compared the value after printf "%.2f", so 99.995% passed a threshold of 100. - Non-numeric or out-of-range COVERAGE_THRESHOLD is an error instead of a shell arithmetic failure. - Effective coverage is read from report.json instead of grepping summary.txt; corrupt LCOV data (LH > LF, malformed records) is an error. Unit tests (score_coverage/tests/generate_coverage_html_test.py, 40 cases) cover the gate primitives, argument parsing and the end-to-end flow on a synthetic workspace, including the fail-loud paths. rules_shell is no longer a dependency. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- BUILD | 3 +- COVERAGE_GUIDE.md | 4 +- MODULE.bazel | 1 - score_coverage/BUILD | 26 +- score_coverage/coverage_summary.py | 7 +- score_coverage/effective_coverage.py | 14 +- score_coverage/generate_coverage_html.py | 403 +++++++++++++++ score_coverage/generate_coverage_html.sh | 298 ----------- score_coverage/justify.py | 14 +- score_coverage/tests/BUILD | 6 + .../tests/generate_coverage_html_test.py | 470 ++++++++++++++++++ 11 files changed, 923 insertions(+), 323 deletions(-) create mode 100644 score_coverage/generate_coverage_html.py delete mode 100755 score_coverage/generate_coverage_html.sh create mode 100644 score_coverage/tests/generate_coverage_html_test.py diff --git a/BUILD b/BUILD index f7c2622..9ddfc0c 100644 --- a/BUILD +++ b/BUILD @@ -46,7 +46,8 @@ alias( actual = "//score_coverage:generate_coverage_html", ) -# Used by generate_coverage_html.sh through nested `bazel run` invocations. +# Standalone entry points of the justification layer (generate_coverage_html +# calls them in-process; these aliases keep them runnable on their own). alias( name = "justify", actual = "//score_coverage:justify", diff --git a/COVERAGE_GUIDE.md b/COVERAGE_GUIDE.md index 72cdab7..da5447a 100644 --- a/COVERAGE_GUIDE.md +++ b/COVERAGE_GUIDE.md @@ -145,7 +145,7 @@ bazel run @score_coverage//:generate_coverage_html -- \ --yaml tools/coverage/coverage_justifications.yaml ``` -`generate_coverage_html.sh` unpacks the HTML from the zip, then applies the +`generate_coverage_html.py` unpacks the HTML from the zip, then applies the **justification system**: - `justify.py` reads the consumer's `coverage_justifications.yaml` plus @@ -229,7 +229,7 @@ The split follows directly: | `reporter.py` | final merge + llvm-cov show/export/report + allowlist filtering + `--empty-profile` baselines + rlib expansion | | `coverage_scope.bzl` | the scope aspect/rule (CcInfo + CrateInfo) | | `reporter_wrapper.bzl` + `defs.bzl` | the consumer-facing `score_coverage_scope` / `score_coverage_reporter` API | -| `justify.py`, `effective_coverage.py`, `generate_coverage_html.sh` | justification + gating layer | +| `justify.py`, `effective_coverage.py`, `generate_coverage_html.py` | justification + gating layer | | `enable_llvm_coverage_for_death_tests` | cc_feature for continuous-mode profiling | **Lives in the consumer repository:** diff --git a/MODULE.bazel b/MODULE.bazel index e268882..1d1ec5c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -22,7 +22,6 @@ module(name = "score_coverage") bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "rules_cc", version = "0.2.16") # cc_args/cc_feature for the death-test feature bazel_dep(name = "rules_python", version = "1.8.5") -bazel_dep(name = "rules_shell", version = "0.6.1") # CrateInfo provider consumed by the coverage-scope aspect (coverage_scope.bzl). bazel_dep(name = "rules_rust", version = "0.68.2-score") diff --git a/score_coverage/BUILD b/score_coverage/BUILD index a6f9e70..a4d35a3 100644 --- a/score_coverage/BUILD +++ b/score_coverage/BUILD @@ -14,7 +14,6 @@ load("@pip_score_coverage//:requirements.bzl", "requirement") load("@rules_python//python:defs.bzl", "py_binary", "py_library") load("@rules_python//python:pip.bzl", "compile_pip_requirements") -load("@rules_shell//shell:sh_binary.bzl", "sh_binary") package(default_visibility = ["//visibility:public"]) @@ -53,15 +52,23 @@ py_binary( ) # Markdown job summary (GITHUB_STEP_SUMMARY / --summary-md), invoked by -# generate_coverage_html.sh. Stdlib only. +# generate_coverage_html. Stdlib only. py_binary( name = "coverage_summary", srcs = ["coverage_summary.py"], ) -sh_binary( +# Orchestrator + threshold gate: unpacks the reporter zip, runs justify / +# effective_coverage / coverage_summary in-process and enforces +# COVERAGE_THRESHOLD. Consumers run it as @score_coverage//:generate_coverage_html. +py_binary( name = "generate_coverage_html", - srcs = ["generate_coverage_html.sh"], + srcs = ["generate_coverage_html.py"], + deps = [ + ":coverage_summary_lib", + ":effective_coverage_lib", + ":justify_lib", + ], ) # Import-only libraries so unit tests can `from score_coverage. import ...`. @@ -97,6 +104,17 @@ py_library( imports = [".."], ) +py_library( + name = "generate_coverage_html_lib", + srcs = ["generate_coverage_html.py"], + imports = [".."], + deps = [ + ":coverage_summary_lib", + ":effective_coverage_lib", + ":justify_lib", + ], +) + # In order to update the requirements, change the `requirements.in` file and run: # `bazel run //score_coverage:requirements_3_XX.update --@@rules_python+//python/config_settings:python_version=3.XX`. [ diff --git a/score_coverage/coverage_summary.py b/score_coverage/coverage_summary.py index 923a0f4..79602ef 100644 --- a/score_coverage/coverage_summary.py +++ b/score_coverage/coverage_summary.py @@ -13,7 +13,7 @@ # ******************************************************************************* """Render a markdown coverage summary from the pipeline's LCOV output. -Invoked by generate_coverage_html.sh to produce a human-readable summary for +Invoked by generate_coverage_html to produce a human-readable summary for GitHub job summary pages (GITHUB_STEP_SUMMARY) or an arbitrary markdown file (--summary-md). Standard library only. @@ -305,13 +305,14 @@ def render_markdown(files: List[FileCoverage], justification: Optional[Dict]) -> return "\n".join(out) -def main() -> None: +def main(argv: Optional[List[str]] = None) -> None: + """Entry point. ``argv`` defaults to ``sys.argv[1:]``.""" parser = argparse.ArgumentParser(description="Markdown coverage summary from LCOV") parser.add_argument("--lcov", type=Path, required=True) parser.add_argument("--justification-report", type=Path, default=None) parser.add_argument("--output", type=Path, required=True) parser.add_argument("--append", action="store_true") - args = parser.parse_args() + args = parser.parse_args(argv) files = parse_lcov(args.lcov) if files is None: diff --git a/score_coverage/effective_coverage.py b/score_coverage/effective_coverage.py index 1267939..6fdf5a5 100644 --- a/score_coverage/effective_coverage.py +++ b/score_coverage/effective_coverage.py @@ -32,7 +32,7 @@ import re import sys from pathlib import Path -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List, Optional, Tuple # Pattern to match a table row in llvm-cov HTML source pages @@ -47,9 +47,9 @@ def floor_two_decimals(value: float) -> float: return math.floor(value * 100.0) / 100.0 -def main() -> None: - """Main entry point.""" - args = parse_args() +def main(argv: Optional[List[str]] = None) -> None: + """Main entry point. ``argv`` defaults to ``sys.argv[1:]``.""" + args = parse_args(argv) # Load the justification manifest manifest = load_manifest(args.manifest) @@ -711,8 +711,8 @@ def load_manifest(path: Path) -> Dict[str, Any]: return json.load(f) -def parse_args() -> argparse.Namespace: - """Parse command-line arguments.""" +def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace: + """Parse command-line arguments (``argv`` defaults to ``sys.argv[1:]``).""" parser = argparse.ArgumentParser(description="Effective coverage calculator and HTML post-processor") parser.add_argument( "--html-dir", @@ -738,7 +738,7 @@ def parse_args() -> argparse.Namespace: default=None, help="Optional path to LCOV data file (used for gcovr format totals)", ) - return parser.parse_args() + return parser.parse_args(argv) # ============================================================================= diff --git a/score_coverage/generate_coverage_html.py b/score_coverage/generate_coverage_html.py new file mode 100644 index 0000000..0e757cd --- /dev/null +++ b/score_coverage/generate_coverage_html.py @@ -0,0 +1,403 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Generate the HTML coverage report from `bazel coverage` output and gate it. + +The LLVM pipeline's reporter leaves a zip at bazel-out/_coverage/_coverage_report.dat +containing html_report/, lcov_report/ and text_report/. This tool + +1. extracts the HTML report into the output directory, +2. optionally applies coverage justifications (--yaml) and computes the + effective-coverage metric, +3. writes an optional markdown summary (--summary-md or GITHUB_STEP_SUMMARY), +4. optionally assembles an artifacts tree / zip (--archive-dir / --archive), +5. enforces COVERAGE_THRESHOLD on the gated metric: effective line coverage + when --yaml is given, raw line coverage from the LCOV data otherwise. + +Usage (from the CONSUMER workspace, after `bazel coverage --config=llvm_cov`): + + bazel run @score_coverage//:generate_coverage_html -- \\ + [--yaml ] \\ + [--archive ] [--archive-dir ] \\ + [--platform ] [--testlogs-subdir ] \\ + [--summary-md ] [output-dir] + +Exit codes: + 0 gate passed + 1 gate failed (gated coverage below COVERAGE_THRESHOLD) + 2 the run could not be evaluated (missing/invalid inputs, bad threshold, + justification errors). This is deliberately distinct from 0: a report + that cannot be produced must never look like a passing gate. + +Design note: the threshold gate is the one decision downstream safety +arguments rely on, which is why it lives in small pure functions here +(``parse_threshold``, ``raw_line_coverage_from_lcov``, +``effective_line_coverage_from_report``, ``gate_passes``) that are unit tested +independently of Bazel. The comparison uses the UNROUNDED percentage: a +value printed as "100.00" but actually 99.995 must not pass a threshold of +100. +""" + +from __future__ import annotations + +import argparse +import json +import os +import shutil +import sys +import tempfile +import zipfile +from dataclasses import dataclass +from pathlib import Path +from typing import List, Optional, Sequence + +from score_coverage import coverage_summary, effective_coverage, justify + +EXIT_OK = 0 +EXIT_GATE_FAILED = 1 +EXIT_ERROR = 2 + +DEFAULT_THRESHOLD = 100.0 +COVERAGE_REPORT_REL = Path("bazel-out/_coverage/_coverage_report.dat") + + +class GenerateError(Exception): + """A condition under which no verdict can be produced (exit code 2).""" + + +@dataclass +class Options: + """Parsed command line.""" + + yaml: Optional[str] + archive: Optional[str] + archive_dir: Optional[str] + platform: str + testlogs_subdir: str + summary_md: Optional[str] + output_dir: Optional[str] + + +def parse_args(argv: Optional[Sequence[str]] = None) -> Options: + """Parse the command line (``argv`` defaults to ``sys.argv[1:]``).""" + parser = argparse.ArgumentParser( + prog="generate_coverage_html", + description="Extract the LLVM coverage HTML report, apply justifications and gate on the threshold.", + ) + parser.add_argument("--yaml", metavar="PATH", help="Justification YAML, relative to the workspace root.") + parser.add_argument("--archive", metavar="NAME", help="Write an artifacts zip named NAME.zip.") + parser.add_argument("--archive-dir", metavar="DIR", help="Assemble the artifacts tree into DIR (not zipped).") + parser.add_argument( + "--platform", + default="linux", + choices=sorted(justify.VALID_PLATFORMS), + help="Target platform for justification filtering (default: linux).", + ) + parser.add_argument( + "--testlogs-subdir", + default="", + metavar="SUBDIR", + help="Subdirectory of bazel-testlogs to collect JUnit XMLs from when archiving.", + ) + parser.add_argument("--summary-md", metavar="PATH", help="Write the markdown coverage summary to PATH.") + parser.add_argument( + "output_dir", + nargs="?", + help="Directory for the HTML report (default: coverage_).", + ) + ns = parser.parse_args(argv) + return Options( + yaml=ns.yaml, + archive=ns.archive, + archive_dir=ns.archive_dir, + platform=ns.platform, + testlogs_subdir=ns.testlogs_subdir, + summary_md=ns.summary_md, + output_dir=ns.output_dir, + ) + + +# ----------------------------------------------------------------------------- +# Gate primitives (pure, unit tested) +# ----------------------------------------------------------------------------- + + +def parse_threshold(value: Optional[str]) -> float: + """Return the coverage threshold in percent. + + ``None`` or an empty string means the default (100). Anything that is not + a number in [0, 100] is an error: a misspelt threshold must never turn + into a permissive gate. + """ + if value is None or value.strip() == "": + return DEFAULT_THRESHOLD + try: + threshold = float(value) + except ValueError as exc: + raise GenerateError(f"COVERAGE_THRESHOLD must be a number, got {value!r}") from exc + if threshold != threshold or not 0.0 <= threshold <= 100.0: # NaN or out of range + raise GenerateError(f"COVERAGE_THRESHOLD must be within [0, 100], got {value!r}") + return threshold + + +def raw_line_coverage_from_lcov(lcov_path: Path) -> float: + """Sum LF/LH over all records of an LCOV file and return the percentage. + + The LCOV file (not llvm-cov's text summary) is used on purpose: it + includes the baseline-only records of in-scope files no test links + against, which the text summary omits. A file with no instrumented + lines at all (LF total 0) yields no verdict and is an error. + """ + if not lcov_path.is_file(): + raise GenerateError(f"lcov_report/lcov.dat not found at {lcov_path}") + lines_found = 0 + lines_hit = 0 + with open(lcov_path, "r", encoding="utf-8") as f: + for line in f: + line = line.rstrip("\n") + if line.startswith("LF:"): + lines_found += _lcov_int(line) + elif line.startswith("LH:"): + lines_hit += _lcov_int(line) + if lines_found <= 0: + raise GenerateError(f"could not compute raw line coverage from {lcov_path}: no instrumented lines") + if lines_hit > lines_found: + raise GenerateError(f"corrupt LCOV data in {lcov_path}: LH total {lines_hit} exceeds LF total {lines_found}") + return 100.0 * lines_hit / lines_found + + +def _lcov_int(record: str) -> int: + key, _, value = record.partition(":") + try: + number = int(value) + except ValueError as exc: + raise GenerateError(f"malformed LCOV record {record!r}") from exc + if number < 0: + raise GenerateError(f"malformed LCOV record {record!r}: negative {key}") + return number + + +def effective_line_coverage_from_report(report_path: Path) -> float: + """Read the effective line coverage percentage from effective_coverage's report.json.""" + if not report_path.is_file(): + raise GenerateError(f"effective coverage report was not produced: {report_path}") + try: + with open(report_path, "r", encoding="utf-8") as f: + report = json.load(f) + value = report["summary"]["effective_line_coverage_pct"] + except (json.JSONDecodeError, KeyError, TypeError) as exc: + raise GenerateError( + f"effective coverage report {report_path} is missing summary.effective_line_coverage_pct" + ) from exc + if not isinstance(value, (int, float)) or isinstance(value, bool): + raise GenerateError(f"effective_line_coverage_pct in {report_path} is not a number: {value!r}") + return float(value) + + +def gate_passes(coverage_pct: float, threshold_pct: float) -> bool: + """The gate passes when the (unrounded) coverage reaches the threshold.""" + return coverage_pct >= threshold_pct + + +# ----------------------------------------------------------------------------- +# Steps +# ----------------------------------------------------------------------------- + + +def extract_report(report_zip: Path, extract_dir: Path) -> None: + """Unpack the reporter's zip; it must contain html_report/.""" + if not report_zip.is_file(): + raise GenerateError( + f"Coverage report not found at {report_zip}\n" + " Run 'bazel coverage --config=llvm_cov //... --build_tests_only' first." + ) + if not zipfile.is_zipfile(report_zip): + raise GenerateError( + f"{report_zip} is not the LLVM pipeline zip report.\n" + " Run 'bazel coverage --config=llvm_cov //... --build_tests_only' first." + ) + with zipfile.ZipFile(report_zip) as zf: + zf.extractall(extract_dir) + if not (extract_dir / "html_report").is_dir(): + raise GenerateError(f"html_report/ not found in {report_zip}") + + +def run_justifications( + workspace: Path, yaml_rel: str, platform: str, output_dir: Path, justification_dir: Path +) -> float: + """Resolve justifications, post-process the HTML and return the effective line coverage.""" + yaml_path = workspace / yaml_rel + if not yaml_path.is_file(): + raise GenerateError(f"{yaml_path} not found.") + print("\nRunning coverage justification processing...") + justification_dir.mkdir(parents=True, exist_ok=True) + manifest = justification_dir / "manifest.json" + report = justification_dir / "report.json" + # In-process calls. Both tools call sys.exit(1) on their own errors; that + # is turned into a GenerateError so it exits with EXIT_ERROR (2) and can + # never be mistaken for the gate verdict (0/1). + _call_tool( + "justify", + justify.main, + ["--yaml", str(yaml_path), "--source-root", str(workspace), "--platform", platform, "--output", str(manifest)], + ) + _call_tool( + "effective_coverage", + effective_coverage.main, + ["--html-dir", str(output_dir), "--manifest", str(manifest), "--output", str(report)], + ) + summary_txt = justification_dir / "summary.txt" + if not summary_txt.is_file(): + raise GenerateError("Effective coverage summary was not produced.") + print() + print(summary_txt.read_text(encoding="utf-8"), end="") + return effective_line_coverage_from_report(report) + + +def _call_tool(name: str, entry, argv: List[str]) -> None: + try: + entry(argv) + except SystemExit as exc: + if exc.code not in (None, 0): + raise GenerateError(f"{name} failed with exit code {exc.code}") from exc + + +def write_summary( + workspace: Path, + lcov: Path, + justification_dir: Optional[Path], + summary_md: Optional[str], + step_summary: Optional[str], +) -> None: + """Emit the markdown summary to --summary-md or, failing that, GITHUB_STEP_SUMMARY.""" + args: List[str] = ["--lcov", str(lcov)] + if justification_dir is not None and (justification_dir / "report.json").is_file(): + args += ["--justification-report", str(justification_dir / "report.json")] + if summary_md: + target = Path(summary_md) + if not target.is_absolute(): + target = workspace / target + coverage_summary.main(args + ["--output", str(target)]) + print(f"Coverage summary written to: {target}") + elif step_summary: + coverage_summary.main(args + ["--output", step_summary, "--append"]) + print("Coverage summary appended to GITHUB_STEP_SUMMARY") + + +def assemble_artifacts( + dest: Path, + workspace: Path, + testlogs_subdir: str, + output_dir: Path, + lcov: Path, + justification_dir: Optional[Path], +) -> None: + """Copy JUnit XMLs (tree preserved), the HTML report, the LCOV and the justification report.""" + dest.mkdir(parents=True, exist_ok=True) + testlogs = workspace / "bazel-testlogs" / testlogs_subdir if testlogs_subdir else workspace / "bazel-testlogs" + if not testlogs.is_dir(): + raise GenerateError(f"test logs directory not found: {testlogs}") + for xml in sorted(testlogs.rglob("test.xml")): + rel = xml.relative_to(workspace) + target = dest / rel + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(xml, target) + shutil.copytree(output_dir, dest / output_dir.name, dirs_exist_ok=True) + if lcov.is_file(): + shutil.copy2(lcov, dest / "coverage_report.dat") + if justification_dir is not None and justification_dir.is_dir(): + shutil.copytree(justification_dir, dest / justification_dir.name, dirs_exist_ok=True) + + +# ----------------------------------------------------------------------------- +# Orchestration +# ----------------------------------------------------------------------------- + + +def run(opts: Options, workspace: Path, environ: Optional[dict] = None) -> int: + """Execute the full flow from ``workspace`` and return the exit code.""" + env = os.environ if environ is None else environ + threshold = parse_threshold(env.get("COVERAGE_THRESHOLD")) + + output_dir = workspace / (opts.output_dir or f"coverage_{opts.platform}") + report_zip = workspace / COVERAGE_REPORT_REL + + with tempfile.TemporaryDirectory(prefix="coverage_extract_") as tmp: + extract_dir = Path(tmp) + extract_report(report_zip, extract_dir) + if output_dir.exists(): + shutil.rmtree(output_dir) + shutil.copytree(extract_dir / "html_report", output_dir) + print(f"Coverage report written to: {output_dir}") + + lcov = extract_dir / "lcov_report" / "lcov.dat" + justification_dir: Optional[Path] = None + if opts.yaml: + justification_dir = extract_dir / "justification_report" + gate_pct = run_justifications(workspace, opts.yaml, opts.platform, output_dir, justification_dir) + gate_kind = "Effective" + else: + print("\nINFO: no --yaml given; justification processing skipped, gating on raw line coverage.") + gate_pct = raw_line_coverage_from_lcov(lcov) + print(f"Raw line coverage: {gate_pct:.2f}%") + gate_kind = "Raw" + + # The summary is emitted BEFORE the gate decides, so a failing gate + # still leaves it on the workflow run page. + write_summary(workspace, lcov, justification_dir, opts.summary_md, env.get("GITHUB_STEP_SUMMARY")) + + if gate_passes(gate_pct, threshold): + rc = EXIT_OK + else: + print( + f"ERROR: {gate_kind} coverage {gate_pct:.2f}% is below threshold {threshold:g}%", + file=sys.stderr, + ) + rc = EXIT_GATE_FAILED + + if opts.archive_dir: + archive_dir = workspace / opts.archive_dir + if archive_dir.exists(): + shutil.rmtree(archive_dir) + assemble_artifacts(archive_dir, workspace, opts.testlogs_subdir, output_dir, lcov, justification_dir) + print(f"Coverage artifacts written to: {opts.archive_dir}/") + + if opts.archive: + tree = workspace / "artifacts" + if tree.exists(): + shutil.rmtree(tree) + assemble_artifacts(tree, workspace, opts.testlogs_subdir, output_dir, lcov, justification_dir) + shutil.make_archive(str(workspace / opts.archive), "zip", root_dir=workspace, base_dir="artifacts") + shutil.rmtree(tree) + print(f"Coverage archive written to: {opts.archive}.zip") + + return rc + + +def main(argv: Optional[Sequence[str]] = None) -> int: + """CLI entry point; returns the process exit code.""" + opts = parse_args(argv) + workspace_env = os.environ.get("BUILD_WORKSPACE_DIRECTORY") + if not workspace_env: + print("ERROR: BUILD_WORKSPACE_DIRECTORY is not set; run this tool via `bazel run`.", file=sys.stderr) + return EXIT_ERROR + workspace = Path(workspace_env) + os.chdir(workspace) + try: + return run(opts, workspace) + except GenerateError as exc: + print(f"ERROR: {exc}", file=sys.stderr) + return EXIT_ERROR + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/score_coverage/generate_coverage_html.sh b/score_coverage/generate_coverage_html.sh deleted file mode 100755 index b847bcc..0000000 --- a/score_coverage/generate_coverage_html.sh +++ /dev/null @@ -1,298 +0,0 @@ -#!/usr/bin/env bash -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* -# Generates an HTML coverage report from `bazel coverage` output produced by -# the LLVM pipeline: the custom reporter produces a zip with html_report/, -# lcov_report/ and text_report/ inside. -# -# Usage (from the CONSUMER workspace): -# bazel coverage --config=llvm_cov //... --build_tests_only -# bazel run @score_coverage//:generate_coverage_html -- \ -# [--yaml ] \ -# [--archive ] [--archive-dir ] \ -# [--platform ] [--testlogs-subdir ] [output-dir] -# -# Arguments: -# --yaml Justification YAML, relative to the workspace -# root. When omitted, justification processing and -# the effective-coverage metric are skipped and the -# threshold gate applies to the RAW line coverage. -# --archive Create a zip archive named .zip -# containing the HTML report, raw LCOV data and JUnit XMLs. -# --archive-dir Assemble the same content as --archive into -# WITHOUT zipping — preferred for CI artifact -# uploads (actions/upload-artifact zips its input -# itself; a pre-zipped file would be zipped twice). -# --platform Target platform for justification filtering -# (default: linux). Also affects the default output -# directory (coverage_). -# --testlogs-subdir Subdirectory of bazel-testlogs to collect JUnit -# XMLs from when archiving (default: entire -# bazel-testlogs tree). -# --summary-md Write a markdown coverage summary (tables, -# per-directory rollup, 0%-file list) to . -# When ABSENT and the GITHUB_STEP_SUMMARY -# environment variable is set (GitHub Actions), -# the summary is appended there automatically; -# when neither is present, no summary is emitted. -# The summary is written before the threshold -# gate decides the exit code, so a failing gate -# still leaves it on the workflow run page. -# output-dir Directory to write the HTML report to -# (default: coverage_) -# -# Environment: -# COVERAGE_THRESHOLD Minimum line coverage percentage (default: 100). -# The script exits non-zero when the gated metric -# (effective coverage with --yaml, raw coverage -# without) is below this threshold. - -set -euo pipefail - -ARCHIVE_NAME="" -ARCHIVE_DIR="" -PLATFORM="linux" -OUTPUT_DIR="" -JUSTIFICATION_YAML_REL="" -TESTLOGS_SUBDIR="" -SUMMARY_MD="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --yaml) - JUSTIFICATION_YAML_REL="${2:?--yaml requires a path argument}" - shift 2 - ;; - --archive) - ARCHIVE_NAME="${2:?--archive requires a name argument}" - shift 2 - ;; - --archive-dir) - ARCHIVE_DIR="${2:?--archive-dir requires a directory argument}" - shift 2 - ;; - --platform) - PLATFORM="${2:?--platform requires a platform argument (e.g. linux or qnx)}" - shift 2 - ;; - --testlogs-subdir) - TESTLOGS_SUBDIR="${2:?--testlogs-subdir requires a path argument}" - shift 2 - ;; - --summary-md) - SUMMARY_MD="${2:?--summary-md requires a path argument}" - shift 2 - ;; - *) - OUTPUT_DIR="$1" - shift - ;; - esac -done - -# --yaml is optional: without it, justification processing and the effective -# coverage metric are skipped and the threshold gate applies to the RAW line -# coverage from llvm-cov's summary instead. - -# Set default output directory based on platform if not explicitly provided. -if [[ -z "${OUTPUT_DIR}" ]]; then - OUTPUT_DIR="coverage_${PLATFORM}" -fi - -# Change to the workspace root so that all subsequent bazel calls and -# relative paths work correctly. -cd "${BUILD_WORKSPACE_DIRECTORY}" - -# Resolve OUTPUT_DIR to absolute path (relative to workspace root). -OUTPUT_DIR="${BUILD_WORKSPACE_DIRECTORY}/${OUTPUT_DIR}" - -# The coverage report is at _coverage_report.dat: a zip containing -# html_report/, lcov_report/ and text_report/ (LLVM pipeline). -COVERAGE_REPORT="${BUILD_WORKSPACE_DIRECTORY}/bazel-out/_coverage/_coverage_report.dat" - -if [[ ! -f "${COVERAGE_REPORT}" ]]; then - echo "ERROR: Coverage report not found at ${COVERAGE_REPORT}" >&2 - echo " Run 'bazel coverage --config=llvm_cov //... --build_tests_only' first." >&2 - exit 1 -fi - -TMPDIR_EXTRACT="${TMPDIR:-/tmp}/coverage_extract_$$" -mkdir -p "${TMPDIR_EXTRACT}" -trap 'rm -rf "${TMPDIR_EXTRACT}"' EXIT - -rm -rf "${OUTPUT_DIR}" - -if ! file -b "${COVERAGE_REPORT}" | grep -q "Zip archive"; then - echo "ERROR: ${COVERAGE_REPORT} is not the LLVM pipeline zip report." >&2 - echo " Run 'bazel coverage --config=llvm_cov //... --build_tests_only' first." >&2 - exit 1 -fi - -# Extract HTML from the zip produced by our custom reporter. -unzip -q -o "${COVERAGE_REPORT}" -d "${TMPDIR_EXTRACT}" - -if [[ -d "${TMPDIR_EXTRACT}/html_report" ]]; then - cp -r "${TMPDIR_EXTRACT}/html_report" "${OUTPUT_DIR}" -else - echo "ERROR: html_report/ not found in ${COVERAGE_REPORT}" >&2 - exit 1 -fi - -echo "Coverage report written to: ${OUTPUT_DIR}" - -# --------------------------------------------------------------------------- -# Run coverage justification processing (only when --yaml was given) and -# enforce the coverage threshold. -# --------------------------------------------------------------------------- -THRESHOLD="${COVERAGE_THRESHOLD:-100}" -JUSTIFICATION_DIR="" - -if [[ -n "${JUSTIFICATION_YAML_REL}" ]]; then - JUSTIFICATION_YAML="${BUILD_WORKSPACE_DIRECTORY}/${JUSTIFICATION_YAML_REL}" - - if [[ ! -f "${JUSTIFICATION_YAML}" ]]; then - echo "ERROR: ${JUSTIFICATION_YAML} not found." >&2 - exit 1 - fi - - echo "" - echo "Running coverage justification processing..." - - JUSTIFICATION_DIR="${TMPDIR_EXTRACT}/justification_report" - mkdir -p "${JUSTIFICATION_DIR}" - - # Run justify.py / effective_coverage.py via nested bazel invocations from the - # consumer workspace. This deliberately avoids runfiles resolution across - # module boundaries (canonical repo names vary between Bazel versions). - bazel run @score_coverage//:justify -- \ - --yaml "${JUSTIFICATION_YAML}" \ - --source-root "${BUILD_WORKSPACE_DIRECTORY}" \ - --platform "${PLATFORM}" \ - --output "${JUSTIFICATION_DIR}/manifest.json" - - bazel run @score_coverage//:effective_coverage -- \ - --html-dir "${OUTPUT_DIR}" \ - --manifest "${JUSTIFICATION_DIR}/manifest.json" \ - --output "${JUSTIFICATION_DIR}/report.json" - - # Display effective coverage summary and enforce the threshold. - if [[ ! -f "${JUSTIFICATION_DIR}/summary.txt" ]]; then - echo "ERROR: Effective coverage summary was not produced." >&2 - exit 1 - fi - - echo "" - cat "${JUSTIFICATION_DIR}/summary.txt" - - # Extract effective coverage percentage for threshold check. - GATE_PCT=$(grep -oP 'Effective line coverage:\s+\K[0-9.]+' \ - "${JUSTIFICATION_DIR}/summary.txt" 2>/dev/null || echo "0") - GATE_KIND="Effective" -else - # No justification YAML: gate on the raw line coverage computed from the - # LCOV data. Deliberately NOT llvm-cov's text summary TOTAL — that summary - # omits baseline-only files (in-scope files no test links against), which - # would let untested files escape the gate. The LCOV includes them. - echo "" - echo "INFO: no --yaml given; justification processing skipped, gating on raw line coverage." - if [[ ! -f "${TMPDIR_EXTRACT}/lcov_report/lcov.dat" ]]; then - echo "ERROR: lcov_report/lcov.dat not found in ${COVERAGE_REPORT}" >&2 - exit 1 - fi - GATE_PCT=$(awk -F: '/^LF:/ {lf += $2} /^LH:/ {lh += $2} - END { if (lf > 0) printf "%.2f", lh * 100 / lf; }' \ - "${TMPDIR_EXTRACT}/lcov_report/lcov.dat") - if [[ -z "${GATE_PCT}" ]]; then - echo "ERROR: could not compute raw line coverage from lcov_report/lcov.dat" >&2 - exit 1 - fi - echo "Raw line coverage: ${GATE_PCT}%" - GATE_KIND="Raw" -fi - -# --------------------------------------------------------------------------- -# Optional markdown summary (--summary-md, or GITHUB_STEP_SUMMARY when the -# flag is absent). Emitted BEFORE the threshold gate so a failing gate still -# leaves the summary on the workflow run page. -# --------------------------------------------------------------------------- -if [[ -n "${SUMMARY_MD}" || -n "${GITHUB_STEP_SUMMARY:-}" ]]; then - SUMMARY_ARGS=(--lcov "${TMPDIR_EXTRACT}/lcov_report/lcov.dat") - if [[ -n "${JUSTIFICATION_DIR}" && -f "${JUSTIFICATION_DIR}/report.json" ]]; then - SUMMARY_ARGS+=(--justification-report "${JUSTIFICATION_DIR}/report.json") - fi - if [[ -n "${SUMMARY_MD}" ]]; then - case "${SUMMARY_MD}" in - /*) : ;; - *) SUMMARY_MD="${BUILD_WORKSPACE_DIRECTORY}/${SUMMARY_MD}" ;; - esac - bazel run @score_coverage//:coverage_summary -- \ - "${SUMMARY_ARGS[@]}" --output "${SUMMARY_MD}" - echo "Coverage summary written to: ${SUMMARY_MD}" - else - bazel run @score_coverage//:coverage_summary -- \ - "${SUMMARY_ARGS[@]}" --output "${GITHUB_STEP_SUMMARY}" --append - echo "Coverage summary appended to GITHUB_STEP_SUMMARY" - fi -fi - -# Threshold check (default: 100%). Fails the run when below. -if ! awk "BEGIN {exit (${GATE_PCT} >= ${THRESHOLD}) ? 0 : 1}"; then - echo "ERROR: ${GATE_KIND} coverage ${GATE_PCT}% is below threshold ${THRESHOLD}%" >&2 - RC=1 -else - RC=0 -fi - -# --------------------------------------------------------------------------- -# Optional: assemble the HTML report, raw LCOV data, justification report and -# JUnit XML test results into an artifacts tree. -# --archive zip the tree into .zip (and remove the tree) -# --archive-dir keep the tree at — preferred for CI artifact -# uploads, since actions/upload-artifact zips its input -# anyway (a pre-zipped file would be zipped twice) -# --------------------------------------------------------------------------- -assemble_artifacts() { - local dest="$1" - mkdir -p "${dest}" - - # Copy JUnit XML test results preserving directory structure. - find "bazel-testlogs/${TESTLOGS_SUBDIR}" -name 'test.xml' -exec cp --parents {} "${dest}/" \; - - # Copy the HTML coverage report - cp -r "${OUTPUT_DIR}" "${dest}/" - - # Include the LCOV .dat file from the reporter zip. - if [[ -f "${TMPDIR_EXTRACT}/lcov_report/lcov.dat" ]]; then - cp "${TMPDIR_EXTRACT}/lcov_report/lcov.dat" "${dest}/coverage_report.dat" - fi - - # Include the justification report (manifest + effective coverage json). - if [[ -n "${JUSTIFICATION_DIR}" && -d "${JUSTIFICATION_DIR}" ]]; then - cp -r "${JUSTIFICATION_DIR}" "${dest}/" - fi -} - -if [[ -n "${ARCHIVE_DIR}" ]]; then - rm -rf "${ARCHIVE_DIR}" - assemble_artifacts "${ARCHIVE_DIR}" - echo "Coverage artifacts written to: ${ARCHIVE_DIR}/" -fi - -if [[ -n "${ARCHIVE_NAME}" ]]; then - assemble_artifacts artifacts - zip -r "${ARCHIVE_NAME}.zip" artifacts/ - rm -rf artifacts/ - echo "Coverage archive written to: ${ARCHIVE_NAME}.zip" -fi - -exit "${RC}" diff --git a/score_coverage/justify.py b/score_coverage/justify.py index 488b7c5..ddf7d20 100644 --- a/score_coverage/justify.py +++ b/score_coverage/justify.py @@ -29,7 +29,7 @@ import re import sys from pathlib import Path -from typing import Any, Dict, List, Set, Tuple +from typing import Any, Dict, List, Optional, Set, Tuple import yaml @@ -52,9 +52,9 @@ } -def main() -> None: - """Main entry point.""" - args = parse_args() +def main(argv: Optional[List[str]] = None) -> None: + """Main entry point. ``argv`` defaults to ``sys.argv[1:]``.""" + args = parse_args(argv) justifications_data = load_yaml(args.yaml) validate_yaml(justifications_data) @@ -368,8 +368,8 @@ def validate_yaml(data: Dict[str, Any]) -> None: sys.exit(1) -def parse_args() -> argparse.Namespace: - """Parse command-line arguments.""" +def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace: + """Parse command-line arguments (``argv`` defaults to ``sys.argv[1:]``).""" parser = argparse.ArgumentParser(description="Coverage justification processor") parser.add_argument( "--yaml", @@ -402,7 +402,7 @@ def parse_args() -> argparse.Namespace: choices=sorted(VALID_PLATFORMS), help="Target platform for filtering justifications (default: all platforms apply)", ) - return parser.parse_args() + return parser.parse_args(argv) if __name__ == "__main__": diff --git a/score_coverage/tests/BUILD b/score_coverage/tests/BUILD index f49ab02..9500ea8 100644 --- a/score_coverage/tests/BUILD +++ b/score_coverage/tests/BUILD @@ -30,3 +30,9 @@ py_test( srcs = ["coverage_summary_test.py"], deps = ["//score_coverage:coverage_summary_lib"], ) + +py_test( + name = "generate_coverage_html_test", + srcs = ["generate_coverage_html_test.py"], + deps = ["//score_coverage:generate_coverage_html_lib"], +) diff --git a/score_coverage/tests/generate_coverage_html_test.py b/score_coverage/tests/generate_coverage_html_test.py new file mode 100644 index 0000000..e83cbc4 --- /dev/null +++ b/score_coverage/tests/generate_coverage_html_test.py @@ -0,0 +1,470 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Unit tests for generate_coverage_html: the gate primitives and the end-to-end flow. + +The end-to-end tests run against a synthetic consumer workspace: a fake +reporter zip (html_report/ + lcov_report/lcov.dat) under +bazel-out/_coverage/ and a fake bazel-testlogs tree. The justification tools +are replaced by fakes where the HTML post-processing itself is out of scope. +""" + +import io +import json +import tempfile +import unittest +import zipfile +from contextlib import redirect_stderr, redirect_stdout +from pathlib import Path +from unittest import mock + +from score_coverage import generate_coverage_html as gch + +LCOV_25_PERCENT = ( + "SF:src/covered.cpp\nDA:1,1\nDA:2,1\nLF:10\nLH:5\nend_of_record\n" + "SF:src/uncovered.cpp\nDA:1,0\nLF:10\nLH:0\nend_of_record\n" +) + + +def _write(path: Path, content: str) -> Path: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + return path + + +def _make_workspace( + root: Path, lcov: str = LCOV_25_PERCENT, with_html: bool = True, with_testlogs: bool = True +) -> Path: + """Create a fake consumer workspace with a reporter zip and test logs.""" + report = root / gch.COVERAGE_REPORT_REL + report.parent.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(report, "w") as zf: + if with_html: + zf.writestr("html_report/index.html", "report") + zf.writestr("html_report/style.css", "body {}") + zf.writestr("lcov_report/lcov.dat", lcov) + zf.writestr("text_report/summary.txt", "TOTAL 50%") + if with_testlogs: + _write(root / "bazel-testlogs" / "pkg" / "some_test" / "test.xml", "") + _write(root / "bazel-testlogs" / "pkg" / "some_test" / "test.log", "log") + return root + + +def _run(root: Path, argv, environ) -> tuple: + """Run the tool quietly, returning (rc, stdout, stderr).""" + out, err = io.StringIO(), io.StringIO() + with redirect_stdout(out), redirect_stderr(err): + rc = gch.run(gch.parse_args(argv), root, environ) + return rc, out.getvalue(), err.getvalue() + + +class ParseThresholdTest(unittest.TestCase): + def test_default_is_100(self): + self.assertEqual(gch.parse_threshold(None), 100.0) + self.assertEqual(gch.parse_threshold(""), 100.0) + self.assertEqual(gch.parse_threshold(" "), 100.0) + + def test_numbers(self): + self.assertEqual(gch.parse_threshold("85"), 85.0) + self.assertEqual(gch.parse_threshold("99.5"), 99.5) + self.assertEqual(gch.parse_threshold("0"), 0.0) + self.assertEqual(gch.parse_threshold("100"), 100.0) + + def test_garbage_is_an_error_not_a_permissive_gate(self): + for bad in ["abc", "85%", "1e999", "nan", "-1", "100.01", "inf"]: + with self.subTest(bad=bad): + with self.assertRaises(gch.GenerateError): + gch.parse_threshold(bad) + + +class RawLineCoverageTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + + def tearDown(self): + self.tmp.cleanup() + + def test_sums_all_records(self): + lcov = _write(self.root / "lcov.dat", LCOV_25_PERCENT) + self.assertAlmostEqual(gch.raw_line_coverage_from_lcov(lcov), 25.0) + + def test_baseline_only_records_count_towards_the_denominator(self): + lcov = _write(self.root / "lcov.dat", "SF:a\nLF:1\nLH:1\nend_of_record\nSF:b\nLF:1\nLH:0\nend_of_record\n") + self.assertAlmostEqual(gch.raw_line_coverage_from_lcov(lcov), 50.0) + + def test_result_is_not_rounded(self): + lcov = _write(self.root / "lcov.dat", "SF:a\nLF:200000\nLH:199999\nend_of_record\n") + pct = gch.raw_line_coverage_from_lcov(lcov) + self.assertLess(pct, 100.0) + self.assertEqual(f"{pct:.2f}", "100.00") # the printed value rounds up, the gate value must not + + def test_missing_file(self): + with self.assertRaises(gch.GenerateError): + gch.raw_line_coverage_from_lcov(self.root / "nope.dat") + + def test_no_instrumented_lines_is_an_error(self): + lcov = _write(self.root / "lcov.dat", "SF:a\nLF:0\nLH:0\nend_of_record\n") + with self.assertRaises(gch.GenerateError): + gch.raw_line_coverage_from_lcov(lcov) + empty = _write(self.root / "empty.dat", "") + with self.assertRaises(gch.GenerateError): + gch.raw_line_coverage_from_lcov(empty) + + def test_corrupt_records_are_errors(self): + for content in ["SF:a\nLF:1\nLH:2\nend_of_record\n", "SF:a\nLF:x\nLH:0\n", "SF:a\nLF:5\nLH:-1\n"]: + with self.subTest(content=content): + lcov = _write(self.root / "lcov.dat", content) + with self.assertRaises(gch.GenerateError): + gch.raw_line_coverage_from_lcov(lcov) + + +class EffectiveLineCoverageTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + + def tearDown(self): + self.tmp.cleanup() + + def _report(self, payload) -> Path: + return _write(self.root / "report.json", json.dumps(payload)) + + def test_reads_summary_value(self): + path = self._report({"summary": {"effective_line_coverage_pct": 61.76}}) + self.assertEqual(gch.effective_line_coverage_from_report(path), 61.76) + + def test_integer_is_accepted(self): + path = self._report({"summary": {"effective_line_coverage_pct": 100}}) + self.assertEqual(gch.effective_line_coverage_from_report(path), 100.0) + + def test_missing_report(self): + with self.assertRaises(gch.GenerateError): + gch.effective_line_coverage_from_report(self.root / "missing.json") + + def test_malformed_reports(self): + for payload in [ + {}, + {"summary": {}}, + {"summary": {"effective_line_coverage_pct": "95"}}, + {"summary": {"effective_line_coverage_pct": None}}, + {"summary": {"effective_line_coverage_pct": True}}, + ]: + with self.subTest(payload=payload): + with self.assertRaises(gch.GenerateError): + gch.effective_line_coverage_from_report(self._report(payload)) + bad_json = _write(self.root / "report.json", "{not json") + with self.assertRaises(gch.GenerateError): + gch.effective_line_coverage_from_report(bad_json) + + +class GateTest(unittest.TestCase): + def test_boundaries(self): + self.assertTrue(gch.gate_passes(100.0, 100.0)) + self.assertTrue(gch.gate_passes(85.0, 85.0)) + self.assertTrue(gch.gate_passes(0.0, 0.0)) + self.assertFalse(gch.gate_passes(99.995, 100.0)) + self.assertFalse(gch.gate_passes(84.999, 85.0)) + + +class ParseArgsTest(unittest.TestCase): + def test_defaults(self): + opts = gch.parse_args([]) + self.assertIsNone(opts.yaml) + self.assertIsNone(opts.archive) + self.assertIsNone(opts.archive_dir) + self.assertEqual(opts.platform, "linux") + self.assertEqual(opts.testlogs_subdir, "") + self.assertIsNone(opts.summary_md) + self.assertIsNone(opts.output_dir) + + def test_all_flags(self): + opts = gch.parse_args( + [ + "--yaml", + "j.yaml", + "--archive", + "cov", + "--archive-dir", + "d", + "--platform", + "qnx", + "--testlogs-subdir", + "score", + "--summary-md", + "s.md", + "out", + ] + ) + self.assertEqual((opts.yaml, opts.archive, opts.archive_dir), ("j.yaml", "cov", "d")) + self.assertEqual( + (opts.platform, opts.testlogs_subdir, opts.summary_md, opts.output_dir), ("qnx", "score", "s.md", "out") + ) + + def test_unknown_platform_rejected(self): + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + gch.parse_args(["--platform", "windows"]) + + +class RunWithoutYamlTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = _make_workspace(Path(self.tmp.name)) + + def tearDown(self): + self.tmp.cleanup() + + def test_gate_passes_at_low_threshold_and_writes_html(self): + rc, out, _ = _run(self.root, [], {"COVERAGE_THRESHOLD": "10"}) + self.assertEqual(rc, gch.EXIT_OK) + self.assertTrue((self.root / "coverage_linux" / "index.html").is_file()) + self.assertIn("Raw line coverage: 25.00%", out) + + def test_gate_fails_at_default_threshold(self): + rc, _, err = _run(self.root, [], {}) + self.assertEqual(rc, gch.EXIT_GATE_FAILED) + self.assertIn("Raw coverage 25.00% is below threshold 100%", err) + + def test_gate_fails_exactly_below_threshold(self): + rc, _, _ = _run(self.root, [], {"COVERAGE_THRESHOLD": "25.01"}) + self.assertEqual(rc, gch.EXIT_GATE_FAILED) + rc, _, _ = _run(self.root, [], {"COVERAGE_THRESHOLD": "25"}) + self.assertEqual(rc, gch.EXIT_OK) + + def test_invalid_threshold_is_an_error_before_any_output(self): + with self.assertRaises(gch.GenerateError): + _run(self.root, [], {"COVERAGE_THRESHOLD": "high"}) + self.assertFalse((self.root / "coverage_linux").exists()) + + def test_custom_output_dir_and_platform_default(self): + rc, _, _ = _run(self.root, ["--platform", "qnx"], {"COVERAGE_THRESHOLD": "0"}) + self.assertEqual(rc, gch.EXIT_OK) + self.assertTrue((self.root / "coverage_qnx" / "index.html").is_file()) + rc, _, _ = _run(self.root, ["custom_out"], {"COVERAGE_THRESHOLD": "0"}) + self.assertEqual(rc, gch.EXIT_OK) + self.assertTrue((self.root / "custom_out" / "index.html").is_file()) + + def test_stale_output_dir_is_replaced(self): + _write(self.root / "coverage_linux" / "stale.html", "old") + _run(self.root, [], {"COVERAGE_THRESHOLD": "0"}) + self.assertFalse((self.root / "coverage_linux" / "stale.html").exists()) + + def test_summary_md_is_written_even_when_gate_fails(self): + rc, out, _ = _run(self.root, ["--summary-md", "summary.md"], {}) + self.assertEqual(rc, gch.EXIT_GATE_FAILED) + summary = (self.root / "summary.md").read_text(encoding="utf-8") + self.assertIn("## Coverage summary", summary) + self.assertIn("Coverage summary written to:", out) + + def test_github_step_summary_is_appended_not_overwritten(self): + step = _write(self.root / "step.md", "# existing content\n") + rc, out, _ = _run(self.root, [], {"COVERAGE_THRESHOLD": "0", "GITHUB_STEP_SUMMARY": str(step)}) + self.assertEqual(rc, gch.EXIT_OK) + text = step.read_text(encoding="utf-8") + self.assertTrue(text.startswith("# existing content\n")) + self.assertIn("## Coverage summary", text) + self.assertIn("appended to GITHUB_STEP_SUMMARY", out) + + def test_explicit_summary_md_wins_over_step_summary(self): + step = _write(self.root / "step.md", "# existing content\n") + _run(self.root, ["--summary-md", "s.md"], {"COVERAGE_THRESHOLD": "0", "GITHUB_STEP_SUMMARY": str(step)}) + self.assertEqual(step.read_text(encoding="utf-8"), "# existing content\n") + self.assertTrue((self.root / "s.md").is_file()) + + def test_archive_dir_layout(self): + rc, out, _ = _run( + self.root, ["--archive-dir", "artifacts_dir", "--testlogs-subdir", "pkg"], {"COVERAGE_THRESHOLD": "0"} + ) + self.assertEqual(rc, gch.EXIT_OK) + base = self.root / "artifacts_dir" + for rel in ["coverage_linux/index.html", "coverage_report.dat", "bazel-testlogs/pkg/some_test/test.xml"]: + with self.subTest(rel=rel): + self.assertTrue((base / rel).is_file()) + self.assertFalse((base / "bazel-testlogs/pkg/some_test/test.log").exists()) + self.assertEqual((base / "coverage_report.dat").read_text(encoding="utf-8"), LCOV_25_PERCENT) + self.assertIn("Coverage artifacts written to: artifacts_dir/", out) + + def test_archive_zip_layout(self): + rc, _, _ = _run(self.root, ["--archive", "coverage_artifacts"], {"COVERAGE_THRESHOLD": "0"}) + self.assertEqual(rc, gch.EXIT_OK) + with zipfile.ZipFile(self.root / "coverage_artifacts.zip") as zf: + names = set(zf.namelist()) + self.assertIn("artifacts/coverage_report.dat", names) + self.assertIn("artifacts/coverage_linux/index.html", names) + self.assertIn("artifacts/bazel-testlogs/pkg/some_test/test.xml", names) + self.assertFalse((self.root / "artifacts").exists()) + + def test_archive_is_still_produced_when_gate_fails(self): + rc, _, _ = _run(self.root, ["--archive-dir", "out"], {}) + self.assertEqual(rc, gch.EXIT_GATE_FAILED) + self.assertTrue((self.root / "out" / "coverage_report.dat").is_file()) + + def test_missing_testlogs_when_archiving_is_an_error(self): + with self.assertRaises(gch.GenerateError): + _run( + self.root, ["--archive-dir", "out", "--testlogs-subdir", "does_not_exist"], {"COVERAGE_THRESHOLD": "0"} + ) + + +class RunInputValidationTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + + def tearDown(self): + self.tmp.cleanup() + + def test_missing_report(self): + with self.assertRaises(gch.GenerateError): + _run(self.root, [], {"COVERAGE_THRESHOLD": "0"}) + + def test_report_is_not_a_zip(self): + _write(self.root / gch.COVERAGE_REPORT_REL, "TN:\nSF:foo\nend_of_record\n") + with self.assertRaises(gch.GenerateError): + _run(self.root, [], {"COVERAGE_THRESHOLD": "0"}) + + def test_zip_without_html_report(self): + _make_workspace(self.root, with_html=False) + with self.assertRaises(gch.GenerateError): + _run(self.root, [], {"COVERAGE_THRESHOLD": "0"}) + + def test_lcov_with_no_instrumented_lines_yields_no_verdict(self): + _make_workspace(self.root, lcov="SF:a\nLF:0\nLH:0\nend_of_record\n") + with self.assertRaises(gch.GenerateError): + _run(self.root, [], {"COVERAGE_THRESHOLD": "0"}) + + +class RunWithYamlTest(unittest.TestCase): + """The justification layer is faked: these tests cover the orchestration around it.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = _make_workspace(Path(self.tmp.name)) + _write(self.root / "tools" / "coverage" / "coverage_justifications.yaml", "version: 1\njustifications: []\n") + self.calls = [] + + def tearDown(self): + self.tmp.cleanup() + + def _fake_effective(self, pct): + def fake(argv): + self.calls.append(("effective_coverage", argv)) + output = Path(argv[argv.index("--output") + 1]) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text( + json.dumps( + { + "summary": { + "effective_line_coverage_pct": pct, + "raw_line_coverage_pct": 25.0, + "justified_lines": 3, + } + } + ), + encoding="utf-8", + ) + (output.parent / "summary.txt").write_text(f"Effective line coverage: {pct}%\n", encoding="utf-8") + + return fake + + def _fake_justify(self, argv): + self.calls.append(("justify", argv)) + Path(argv[argv.index("--output") + 1]).parent.mkdir(parents=True, exist_ok=True) + Path(argv[argv.index("--output") + 1]).write_text("{}", encoding="utf-8") + + def _run_yaml(self, extra, environ, pct=90.0): + with ( + mock.patch.object(gch.justify, "main", side_effect=self._fake_justify), + mock.patch.object(gch.effective_coverage, "main", side_effect=self._fake_effective(pct)), + ): + return _run(self.root, ["--yaml", "tools/coverage/coverage_justifications.yaml"] + extra, environ) + + def test_gates_on_effective_coverage(self): + rc, out, _ = self._run_yaml([], {"COVERAGE_THRESHOLD": "85"}) + self.assertEqual(rc, gch.EXIT_OK) + self.assertIn("Effective line coverage: 90.0%", out) + rc, _, err = self._run_yaml([], {"COVERAGE_THRESHOLD": "95"}) + self.assertEqual(rc, gch.EXIT_GATE_FAILED) + self.assertIn("Effective coverage 90.00% is below threshold 95%", err) + + def test_tools_receive_workspace_relative_inputs(self): + self._run_yaml(["--platform", "qnx"], {"COVERAGE_THRESHOLD": "0"}) + names = [c[0] for c in self.calls] + self.assertEqual(names, ["justify", "effective_coverage"]) + justify_argv = self.calls[0][1] + self.assertEqual(justify_argv[justify_argv.index("--platform") + 1], "qnx") + self.assertEqual(justify_argv[justify_argv.index("--source-root") + 1], str(self.root)) + self.assertTrue(justify_argv[justify_argv.index("--yaml") + 1].endswith("coverage_justifications.yaml")) + eff_argv = self.calls[1][1] + self.assertEqual(eff_argv[eff_argv.index("--html-dir") + 1], str(self.root / "coverage_qnx")) + + def test_missing_yaml_is_an_error(self): + with mock.patch.object(gch.justify, "main") as justify_main: + with self.assertRaises(gch.GenerateError): + _run(self.root, ["--yaml", "nope.yaml"], {"COVERAGE_THRESHOLD": "0"}) + justify_main.assert_not_called() + + def test_justify_failure_is_an_error_not_a_verdict(self): + def failing(argv): + raise SystemExit(1) + + with mock.patch.object(gch.justify, "main", side_effect=failing): + with self.assertRaises(gch.GenerateError): + _run(self.root, ["--yaml", "tools/coverage/coverage_justifications.yaml"], {"COVERAGE_THRESHOLD": "0"}) + + def test_missing_summary_is_an_error(self): + def no_summary(argv): + output = Path(argv[argv.index("--output") + 1]) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps({"summary": {"effective_line_coverage_pct": 99.0}}), encoding="utf-8") + + with ( + mock.patch.object(gch.justify, "main", side_effect=self._fake_justify), + mock.patch.object(gch.effective_coverage, "main", side_effect=no_summary), + ): + with self.assertRaises(gch.GenerateError): + _run(self.root, ["--yaml", "tools/coverage/coverage_justifications.yaml"], {"COVERAGE_THRESHOLD": "0"}) + + def test_archive_includes_justification_report(self): + rc, _, _ = self._run_yaml(["--archive-dir", "out"], {"COVERAGE_THRESHOLD": "0"}) + self.assertEqual(rc, gch.EXIT_OK) + self.assertTrue((self.root / "out" / "justification_report" / "summary.txt").is_file()) + self.assertTrue((self.root / "out" / "justification_report" / "report.json").is_file()) + + +class MainTest(unittest.TestCase): + def test_requires_build_workspace_directory(self): + with mock.patch.dict("os.environ", {}, clear=True), redirect_stderr(io.StringIO()): + self.assertEqual(gch.main([]), gch.EXIT_ERROR) + + def test_generate_error_maps_to_exit_error(self): + with tempfile.TemporaryDirectory() as tmp: + env = {"BUILD_WORKSPACE_DIRECTORY": tmp, "COVERAGE_THRESHOLD": "0"} + with mock.patch.dict("os.environ", env, clear=True), redirect_stderr(io.StringIO()): + self.assertEqual(gch.main([]), gch.EXIT_ERROR) # no report in an empty workspace + + def test_end_to_end_exit_codes(self): + with tempfile.TemporaryDirectory() as tmp: + _make_workspace(Path(tmp)) + with mock.patch.dict( + "os.environ", {"BUILD_WORKSPACE_DIRECTORY": tmp, "COVERAGE_THRESHOLD": "10"}, clear=True + ): + with redirect_stdout(io.StringIO()): + self.assertEqual(gch.main([]), gch.EXIT_OK) + with mock.patch.dict("os.environ", {"BUILD_WORKSPACE_DIRECTORY": tmp}, clear=True): + with redirect_stdout(io.StringIO()), redirect_stderr(io.StringIO()): + self.assertEqual(gch.main([]), gch.EXIT_GATE_FAILED) + + +if __name__ == "__main__": + unittest.main() From f0d359f2f9e91671511351ed960322052abed03a Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 15:51:20 +0300 Subject: [PATCH 03/17] Add unit tests for justify and effective_coverage The two largest modules of the justification layer had no tests. This adds 72 cases: - justify_test (41): YAML validation (every rule of validate_yaml, all errors reported together), location resolution, platform filtering, marker scanning (single-line, START/STOP regions, nesting, unknown ids, unbalanced markers, non-UTF-8 input), source collection (bazel-* skipped) and the end-to-end manifest for linux/qnx/unfiltered runs including the fail paths (missing location file, invalid YAML). - effective_coverage_test (31): flooring arithmetic, index totals parsing, path matching, llvm-cov row/branch post-processing (justified, stale, covered-in-any-instantiation, branch-only justifications), index page and CSS updates, and the end-to-end report/summary on a synthetic llvm-cov report. Two defects found while writing them, both fixed: - find_matching_justifications matched by plain string suffix, so a justification for bar.cpp also applied to foobar.cpp and could inflate the effective coverage. Matching now requires a path-component boundary. - parse_index_page_totals returned 0/0 silently when index.html was missing (the unparseable case warned); it now warns too. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- score_coverage/effective_coverage.py | 20 +- score_coverage/tests/BUILD | 12 + .../tests/effective_coverage_test.py | 438 ++++++++++++++++++ score_coverage/tests/justify_test.py | 407 ++++++++++++++++ 4 files changed, 875 insertions(+), 2 deletions(-) create mode 100644 score_coverage/tests/effective_coverage_test.py create mode 100644 score_coverage/tests/justify_test.py diff --git a/score_coverage/effective_coverage.py b/score_coverage/effective_coverage.py index 6fdf5a5..ad93803 100644 --- a/score_coverage/effective_coverage.py +++ b/score_coverage/effective_coverage.py @@ -396,6 +396,8 @@ def parse_index_page_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]: """ index_file = html_dir / "index.html" if not index_file.exists(): + # Zero totals make every percentage 0.0 and fail the gate; say why. + print(f"WARNING: {index_file} not found; coverage totals default to 0/0", file=sys.stderr) return {"lines": (0, 0), "branches": (0, 0)} with open(index_file, "r", encoding="utf-8") as f: @@ -667,14 +669,28 @@ def find_matching_justifications( result: Dict[int, Dict[str, str]] = {} for justified_path, line_justifications in justified_files.items(): - # Match if the source_path ends with the justified_path - if source_path.endswith(justified_path) or justified_path.endswith(source_path): + if _same_file(source_path, justified_path): for line_str, justification in line_justifications.items(): result[int(line_str)] = justification return result +def _same_file(source_path: str, justified_path: str) -> bool: + """True when one path equals the other or ends with it at a path-component boundary. + + A plain string suffix test would let a justification for ``bar.cpp`` apply + to ``foobar.cpp`` and inflate the effective coverage; the shorter path + must therefore be preceded by ``/`` (or be the whole path). + """ + if source_path == justified_path: + return True + for longer, shorter in ((source_path, justified_path), (justified_path, source_path)): + if shorter and longer.endswith("/" + shorter): + return True + return False + + def write_summary(path: Path, stats: Dict[str, Any], stale: List[Dict[str, Any]]) -> None: """Write human-readable summary.""" with open(path, "w", encoding="utf-8") as f: diff --git a/score_coverage/tests/BUILD b/score_coverage/tests/BUILD index 9500ea8..188c825 100644 --- a/score_coverage/tests/BUILD +++ b/score_coverage/tests/BUILD @@ -36,3 +36,15 @@ py_test( srcs = ["generate_coverage_html_test.py"], deps = ["//score_coverage:generate_coverage_html_lib"], ) + +py_test( + name = "justify_test", + srcs = ["justify_test.py"], + deps = ["//score_coverage:justify_lib"], +) + +py_test( + name = "effective_coverage_test", + srcs = ["effective_coverage_test.py"], + deps = ["//score_coverage:effective_coverage_lib"], +) diff --git a/score_coverage/tests/effective_coverage_test.py b/score_coverage/tests/effective_coverage_test.py new file mode 100644 index 0000000..9979c05 --- /dev/null +++ b/score_coverage/tests/effective_coverage_test.py @@ -0,0 +1,438 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Unit tests for effective_coverage: arithmetic, llvm-cov HTML post-processing and the report.""" + +import io +import json +import tempfile +import unittest +from contextlib import redirect_stderr, redirect_stdout +from pathlib import Path + +from score_coverage import effective_coverage as ec + + +def _row(line: int, status: str, count: str, code: str) -> str: + """One llvm-cov source table row in the exact shape the module parses.""" + return ( + f"
{line}
" + f"
{count}
" + f"
{code}
\n" + ) + + +def _branch(line: int, col: int, true_covered: bool, false_covered: bool) -> str: + def side(name, covered): + if covered: + return f"{name}: 3" + return f"{name}: 0" + + return ( + f"Branch ({line}:{col}): " + f"[{side('True', true_covered)}, {side('False', false_covered)}]\n" + ) + + +def _pct_cell(covered: int, total: int, color: str = "red") -> str: + pct = 100.0 * covered / total if total else 0.0 + return f"
{pct:>7.2f}% ({covered}/{total})
" + + +def _index_page(files, totals) -> str: + """Minimal llvm-cov index.html: one row per (path, (fc, ft), (lc, lt), (bc, bt)) plus Totals.""" + rows = [] + for path, func, line, branch in files: + rows.append( + f"
{path}
" + f"{_pct_cell(*func)}{_pct_cell(*line)}{_pct_cell(*branch)}\n" + ) + func, line, branch = totals + rows.append( + f"
Totals
{_pct_cell(*func)}{_pct_cell(*line)}{_pct_cell(*branch)}\n" + ) + return "

Coverage Report

" + "".join(rows) + "
" + + +class FloorTwoDecimalsTest(unittest.TestCase): + def test_never_rounds_up(self): + self.assertEqual(ec.floor_two_decimals(61.7647), 61.76) + self.assertEqual(ec.floor_two_decimals(99.999), 99.99) + self.assertEqual(ec.floor_two_decimals(100.0), 100.0) + self.assertEqual(ec.floor_two_decimals(0.0), 0.0) + + +class ParseIndexPageTotalsTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.html = Path(self.tmp.name) + + def tearDown(self): + self.tmp.cleanup() + + def test_reads_totals_row(self): + (self.html / "index.html").write_text( + _index_page([("src/a.cpp", (1, 2), (10, 20), (2, 4))], ((5, 6), (17, 34), (6, 10))), encoding="utf-8" + ) + totals = ec.parse_index_page_totals(self.html) + self.assertEqual(totals["lines"], (17, 34)) + self.assertEqual(totals["branches"], (6, 10)) + + def test_missing_index_yields_zero_totals_and_warns(self): + err = io.StringIO() + with redirect_stderr(err): + totals = ec.parse_index_page_totals(self.html) + self.assertEqual(totals, {"lines": (0, 0), "branches": (0, 0)}) + self.assertIn("WARNING", err.getvalue()) + + def test_unparseable_index_yields_zero_totals(self): + (self.html / "index.html").write_text("nothing here", encoding="utf-8") + with redirect_stderr(io.StringIO()): + totals = ec.parse_index_page_totals(self.html) + self.assertEqual(totals["lines"], (0, 0)) + + +class PathHelpersTest(unittest.TestCase): + def test_extract_source_path(self): + html_dir = Path("/r/html") + self.assertEqual( + ec.extract_source_path_from_html(Path("/r/html/coverage/src/a.cpp.html"), html_dir), "src/a.cpp" + ) + self.assertEqual(ec.extract_source_path_from_html(Path("/r/html/src/a.cpp.html"), html_dir), "src/a.cpp") + self.assertEqual( + ec.extract_source_path_from_html(Path("/r/html/coverage/home/u/ws/src/a.cpp.html"), html_dir), + "home/u/ws/src/a.cpp", + ) + + def test_find_source_html_files(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + for rel in [ + "index.html", + "style.css", + "coverage/src/a.cpp.html", + "coverage/src/index.html", + "coverage/rust/lib.rs.html", + ]: + p = root / rel + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text("", encoding="utf-8") + found = [str(f.relative_to(root)) for f in ec.find_source_html_files(root)] + self.assertEqual(found, ["coverage/rust/lib.rs.html", "coverage/src/a.cpp.html"]) + + +class FindMatchingJustificationsTest(unittest.TestCase): + JUSTIFIED = { + "src/bar.cpp": {"5": {"id": "bar-five"}}, + "lib.rs": {"1": {"id": "lib-one"}}, + } + + def test_exact_and_component_suffix_match(self): + self.assertEqual(ec.find_matching_justifications("src/bar.cpp", self.JUSTIFIED), {5: {"id": "bar-five"}}) + self.assertEqual( + ec.find_matching_justifications("home/u/ws/src/bar.cpp", self.JUSTIFIED), {5: {"id": "bar-five"}} + ) + self.assertEqual(ec.find_matching_justifications("rust/lib.rs", self.JUSTIFIED), {1: {"id": "lib-one"}}) + + def test_shorter_html_path_matches_at_component_boundary(self): + self.assertEqual(ec.find_matching_justifications("bar.cpp", self.JUSTIFIED), {5: {"id": "bar-five"}}) + + def test_partial_basename_does_not_match(self): + # A justification for bar.cpp must never leak into foobar.cpp (would inflate effective coverage). + self.assertEqual(ec.find_matching_justifications("src/foobar.cpp", self.JUSTIFIED), {}) + self.assertEqual(ec.find_matching_justifications("mylib.rs", self.JUSTIFIED), {}) + self.assertEqual(ec.find_matching_justifications("other/xsrc/bar.cpp", self.JUSTIFIED), {}) + + def test_line_keys_become_integers(self): + result = ec.find_matching_justifications("src/bar.cpp", self.JUSTIFIED) + self.assertEqual(list(result), [5]) + + +class ProcessHtmlFileTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.html = Path(self.tmp.name) / "a.cpp.html" + self.applied = [] + self.stale = [] + self.j = {"id": "j-1", "category": "other", "reason": 'it\'s "fine"'} + + def tearDown(self): + self.tmp.cleanup() + + def _process(self, content, justifications): + self.html.write_text(content, encoding="utf-8") + stats = ec.process_html_file(self.html, justifications, self.applied, self.stale) + return stats, self.html.read_text(encoding="utf-8") + + def test_no_justifications_leaves_file_untouched(self): + content = _row(1, "uncovered-line", "0", "x") + stats, after = self._process(content, {}) + self.assertEqual(stats, {"justified": 0, "stale": 0, "justified_branches": 0}) + self.assertEqual(after, content) + + def test_uncovered_justified_line_is_counted_and_restyled(self): + content = _row(1, "covered-line", "3", "a") + _row( + 2, "uncovered-line", "0", "b" + ) + stats, after = self._process(content, {2: self.j}) + self.assertEqual(stats["justified"], 1) + self.assertEqual(stats["stale"], 0) + self.assertIn( + "
J
", after + ) + self.assertIn("class='region justified'", after) + self.assertNotIn("class='region red'", after) + self.assertEqual(self.applied, [{"file": "a.cpp", "line": 2, "id": "j-1", "category": "other"}]) + # Line 1 is untouched. + self.assertIn(_row(1, "covered-line", "3", "a"), after) + + def test_covered_justified_line_is_stale(self): + content = _row(4, "covered-line", "2", "a") + stats, after = self._process(content, {4: self.j}) + self.assertEqual(stats["stale"], 1) + self.assertEqual(stats["justified"], 0) + self.assertEqual(after, content) + self.assertEqual(self.stale[0]["line"], 4) + self.assertEqual(self.stale[0]["id"], "j-1") + + def test_justified_line_not_in_file_is_ignored(self): + content = _row(1, "uncovered-line", "0", "x") + stats, _ = self._process(content, {99: self.j}) + self.assertEqual(stats, {"justified": 0, "stale": 0, "justified_branches": 0}) + self.assertEqual(self.applied, []) + self.assertEqual(self.stale, []) + + def test_covered_in_any_instantiation_counts_as_covered(self): + content = _row(3, "uncovered-line", "0", "t") + _row(3, "covered-line", "1", "t") + stats, _ = self._process(content, {3: self.j}) + self.assertEqual(stats["stale"], 1) + self.assertEqual(stats["justified"], 0) + + def test_uncovered_branch_on_justified_line(self): + content = _row(5, "covered-line", "3", "if (x)") + _branch(5, 9, True, False) + stats, after = self._process(content, {5: self.j}) + self.assertEqual(stats["justified_branches"], 1) + self.assertEqual(stats["stale"], 0) + self.assertEqual(stats["justified"], 0) # the line itself is covered + self.assertIn("class='justified-branch'>False: 0", after) + self.assertEqual(len(self.applied), 1) + + def test_branch_covered_in_one_instantiation_is_not_justified(self): + content = _row(5, "covered-line", "3", "if (x)") + _branch(5, 9, True, False) + _branch(5, 9, True, True) + stats, _ = self._process(content, {5: self.j}) + self.assertEqual(stats["justified_branches"], 0) + self.assertEqual(stats["stale"], 1) # covered line, no truly uncovered branch: nothing left to justify + + def test_both_directions_uncovered_count_two_branches(self): + content = _row(5, "uncovered-line", "0", "if (x)") + _branch(5, 9, False, False) + stats, _ = self._process(content, {5: self.j}) + self.assertEqual(stats["justified"], 1) + self.assertEqual(stats["justified_branches"], 2) + + def test_branches_on_unjustified_lines_untouched(self): + content = _row(5, "covered-line", "3", "if (x)") + _branch(5, 9, True, False) + stats, after = self._process(content, {7: self.j}) + self.assertEqual(stats["justified_branches"], 0) + self.assertIn("class='red branch'>False", after) + + +class UpdateIndexPageTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.html = Path(self.tmp.name) + (self.html / "index.html").write_text( + _index_page( + [("src/a.cpp", (1, 2), (5, 10), (1, 4)), ("src/b.cpp", (2, 2), (10, 10), (4, 4))], + ((3, 4), (15, 20), (5, 8)), + ), + encoding="utf-8", + ) + (self.html / "style.css").write_text("body {}\n", encoding="utf-8") + + def tearDown(self): + self.tmp.cleanup() + + def test_banner_per_file_and_totals_updated(self): + stats = { + "total_instrumented_lines": 20, + "covered_lines": 15, + "justified_lines": 3, + "unjustified_uncovered_lines": 2, + "raw_line_coverage_pct": 75.0, + "effective_line_coverage_pct": 90.0, + "total_branches": 8, + "covered_branches": 5, + "justified_branches": 1, + "raw_branch_coverage_pct": 62.5, + "effective_branch_coverage_pct": 75.0, + } + ec.update_index_page(self.html, stats, {"src/a.cpp": {"justified": 3, "stale": 0, "justified_branches": 1}}) + content = (self.html / "index.html").read_text(encoding="utf-8") + self.assertIn("Effective Line Coverage: 90.0%", content) + self.assertIn("Effective Branch Coverage: 75.0%", content) + # per-file row: 5+3 of 10 lines, 1+1 of 4 branches + self.assertIn("
  80.00% (8/10)
", content) + self.assertIn("
  50.00% (2/4)
", content) + # totals row uses the stats' effective numbers + self.assertIn("
  90.00% (18/20)
", content) + self.assertIn("
  75.00% (6/8)
", content) + # untouched file keeps its cell + self.assertIn(_pct_cell(10, 10), content) + + def test_css_injection(self): + ec.inject_justified_css(self.html) + css = (self.html / "style.css").read_text(encoding="utf-8") + self.assertTrue(css.startswith("body {}\n")) + self.assertIn(".justified-line", css) + self.assertIn(".justified-branch", css) + + def test_color_thresholds(self): + self.assertEqual(ec._get_coverage_color(100.0), "green") + self.assertEqual(ec._get_coverage_color(99.99), "yellow") + self.assertEqual(ec._get_coverage_color(80.0), "yellow") + self.assertEqual(ec._get_coverage_color(79.99), "red") + + +class MainLlvmCovTest(unittest.TestCase): + """End-to-end on a synthetic llvm-cov report: report.json, summary.txt and HTML edits.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + root = Path(self.tmp.name) + self.html = root / "html_report" + (self.html / "coverage" / "src").mkdir(parents=True) + (self.html / "index.html").write_text( + _index_page([("src/a.cpp", (1, 1), (3, 5), (1, 2))], ((1, 1), (3, 5), (1, 2))), encoding="utf-8" + ) + (self.html / "style.css").write_text("body {}\n", encoding="utf-8") + (self.html / "coverage" / "src" / "a.cpp.html").write_text( + _row(1, "covered-line", "1", "a") + + _row(2, "covered-line", "1", "b") + + _row(3, "covered-line", "1", "c") + + _row(4, "uncovered-line", "0", "d") + + _row(5, "uncovered-line", "0", "e"), + encoding="utf-8", + ) + self.manifest = root / "manifest.json" + self.report = root / "just" / "report.json" + + def tearDown(self): + self.tmp.cleanup() + + def _run(self, justified_files): + self.manifest.write_text(json.dumps({"version": 1, "justified_files": justified_files}), encoding="utf-8") + with redirect_stderr(io.StringIO()), redirect_stdout(io.StringIO()): + ec.main(["--html-dir", str(self.html), "--manifest", str(self.manifest), "--output", str(self.report)]) + return json.loads(self.report.read_text(encoding="utf-8")) + + def test_one_justified_line(self): + report = self._run({"src/a.cpp": {"4": {"id": "j-4", "category": "other", "reason": "r"}}}) + s = report["summary"] + self.assertEqual((s["total_instrumented_lines"], s["covered_lines"], s["justified_lines"]), (5, 3, 1)) + self.assertEqual(s["unjustified_uncovered_lines"], 1) + self.assertEqual(s["raw_line_coverage_pct"], 60.0) + self.assertEqual(s["effective_line_coverage_pct"], 80.0) + self.assertEqual(s["total_branches"], 2) + self.assertEqual(report["applied_justifications"][0]["line"], 4) + self.assertEqual(report["stale_justifications"], []) + summary = (self.report.parent / "summary.txt").read_text(encoding="utf-8") + self.assertIn("Justified lines: 1", summary) + self.assertIn("Raw line coverage: 60.0%", summary) + self.assertIn("Effective line coverage: 80.0%", summary) + index = (self.html / "index.html").read_text(encoding="utf-8") + self.assertIn("Effective Line Coverage: 80.0%", index) + self.assertIn("(4/5)", index) + self.assertIn(".justified-line", (self.html / "style.css").read_text(encoding="utf-8")) + page = (self.html / "coverage" / "src" / "a.cpp.html").read_text(encoding="utf-8") + self.assertIn("
J
", page) + + def test_no_justifications_effective_equals_raw(self): + report = self._run({}) + s = report["summary"] + self.assertEqual(s["justified_lines"], 0) + self.assertEqual(s["effective_line_coverage_pct"], s["raw_line_coverage_pct"]) + + def test_stale_justification_is_reported_not_counted(self): + report = self._run({"src/a.cpp": {"1": {"id": "stale-1", "category": "other", "reason": "r"}}}) + s = report["summary"] + self.assertEqual(s["justified_lines"], 0) + self.assertEqual(s["stale_justifications"], 1) + self.assertEqual(s["effective_line_coverage_pct"], 60.0) + self.assertEqual(report["stale_justifications"][0]["id"], "stale-1") + summary = (self.report.parent / "summary.txt").read_text(encoding="utf-8") + self.assertIn("Stale justifications (1):", summary) + + def test_justification_for_other_file_does_not_apply(self): + report = self._run({"src/foo_a.cpp": {"4": {"id": "x", "category": "other", "reason": "r"}}}) + self.assertEqual(report["summary"]["justified_lines"], 0) + + def test_effective_coverage_is_floored(self): + # 3 covered + 1 justified of 5 = 80.0 exactly; use 4/5 -> 3 covered of 5 = 60.0; craft 2/3 case instead + (self.html / "index.html").write_text( + _index_page([("src/a.cpp", (1, 1), (1, 3), (0, 0))], ((1, 1), (1, 3), (0, 0))), encoding="utf-8" + ) + report = self._run({"src/a.cpp": {"4": {"id": "j", "category": "other", "reason": "r"}}}) + s = report["summary"] + self.assertEqual(s["raw_line_coverage_pct"], 33.33) + self.assertEqual(s["effective_line_coverage_pct"], 66.66) # 66.666.. floored, never 66.67 + + def test_missing_manifest_exits(self): + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + ec.main( + [ + "--html-dir", + str(self.html), + "--manifest", + str(self.manifest / "nope"), + "--output", + str(self.report), + ] + ) + + def test_missing_html_dir_exits(self): + self.manifest.write_text(json.dumps({"justified_files": {}}), encoding="utf-8") + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + ec.main( + [ + "--html-dir", + str(self.html / "missing"), + "--manifest", + str(self.manifest), + "--output", + str(self.report), + ] + ) + + +class FormatDetectionAndLcovTest(unittest.TestCase): + def test_detect_html_format(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + (root / "index.html").write_text("", encoding="utf-8") + self.assertEqual(ec.detect_html_format(root), "llvm_cov") + (root / "index.a_cpp.0123abcd.html").write_text("", encoding="utf-8") + self.assertEqual(ec.detect_html_format(root), "gcovr") + + def test_parse_lcov_totals(self): + with tempfile.TemporaryDirectory() as tmp: + lcov = Path(tmp) / "lcov.dat" + lcov.write_text( + "SF:a\nLF:10\nLH:4\nBRF:6\nBRH:2\nend_of_record\nSF:b\nLF:5\nLH:5\nend_of_record\n", encoding="utf-8" + ) + totals = ec._parse_lcov_totals(lcov) + self.assertEqual(totals, {"lines": (9, 15), "branches": (2, 6)}) + + +if __name__ == "__main__": + unittest.main() diff --git a/score_coverage/tests/justify_test.py b/score_coverage/tests/justify_test.py new file mode 100644 index 0000000..4fcd68a --- /dev/null +++ b/score_coverage/tests/justify_test.py @@ -0,0 +1,407 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Unit tests for justify: YAML validation, marker scanning and manifest generation.""" + +import io +import json +import os +import tempfile +import unittest +from contextlib import redirect_stderr +from pathlib import Path + +from score_coverage import justify + +VALID_ENTRY = { + "id": "defensive-null-check", + "category": "defensive_programming", + "platforms": ["linux"], + "reason": "Cannot be reached: the pointer is validated by the caller.", +} + + +def _valid_yaml(**overrides): + entry = dict(VALID_ENTRY) + entry.update(overrides) + return {"version": 1, "justifications": [entry]} + + +def _validate(data): + """Run validate_yaml quietly; return None on success or the SystemExit code.""" + with redirect_stderr(io.StringIO()): + try: + justify.validate_yaml(data) + except SystemExit as exc: + return exc.code + return None + + +class ValidateYamlTest(unittest.TestCase): + def test_valid_document(self): + self.assertIsNone(_validate(_valid_yaml())) + + def test_valid_with_locations(self): + locations = [ + {"file": "src/a.cpp", "line": 3}, + {"file": "src/a.cpp", "line_start": 10, "line_end": 12}, + {"file": "src/b.rs", "lines": [1, 2, 3]}, + ] + self.assertIsNone(_validate(_valid_yaml(locations=locations))) + + def test_empty_justifications_is_valid(self): + self.assertIsNone(_validate({"version": 1, "justifications": []})) + + def test_root_must_be_mapping(self): + self.assertEqual(_validate(["not", "a", "mapping"]), 1) + self.assertEqual(_validate(None), 1) + + def test_version_field(self): + self.assertEqual(_validate({"justifications": []}), 1) + self.assertEqual(_validate({"version": "1", "justifications": []}), 1) + + def test_justifications_field(self): + self.assertEqual(_validate({"version": 1}), 1) + self.assertEqual(_validate({"version": 1, "justifications": {"a": 1}}), 1) + + def test_entry_shape(self): + self.assertEqual(_validate({"version": 1, "justifications": ["string"]}), 1) + entry = dict(VALID_ENTRY) + del entry["id"] + self.assertEqual(_validate({"version": 1, "justifications": [entry]}), 1) + self.assertEqual(_validate(_valid_yaml(id=42)), 1) + + def test_duplicate_ids(self): + data = {"version": 1, "justifications": [dict(VALID_ENTRY), dict(VALID_ENTRY)]} + self.assertEqual(_validate(data), 1) + + def test_id_must_be_kebab_case(self): + for bad in ["Defensive", "has_underscore", "trailing-", "-leading", "double--dash", "with space", ""]: + with self.subTest(bad=bad): + self.assertEqual(_validate(_valid_yaml(id=bad)), 1) + for good in ["a", "a1", "abc-def-123"]: + with self.subTest(good=good): + self.assertIsNone(_validate(_valid_yaml(id=good))) + + def test_category(self): + entry = dict(VALID_ENTRY) + del entry["category"] + self.assertEqual(_validate({"version": 1, "justifications": [entry]}), 1) + self.assertEqual(_validate(_valid_yaml(category=3)), 1) + self.assertEqual(_validate(_valid_yaml(category="unknown")), 1) + for cat in sorted(justify.VALID_CATEGORIES): + with self.subTest(cat=cat): + self.assertIsNone(_validate(_valid_yaml(category=cat))) + + def test_platforms(self): + entry = dict(VALID_ENTRY) + del entry["platforms"] + self.assertEqual(_validate({"version": 1, "justifications": [entry]}), 1) + self.assertEqual(_validate(_valid_yaml(platforms="linux")), 1) + self.assertEqual(_validate(_valid_yaml(platforms=[])), 1) + self.assertEqual(_validate(_valid_yaml(platforms=[1])), 1) + self.assertEqual(_validate(_valid_yaml(platforms=["windows"])), 1) + self.assertIsNone(_validate(_valid_yaml(platforms=["linux", "qnx"]))) + + def test_reason(self): + entry = dict(VALID_ENTRY) + del entry["reason"] + self.assertEqual(_validate({"version": 1, "justifications": [entry]}), 1) + self.assertEqual(_validate(_valid_yaml(reason=5)), 1) + self.assertEqual(_validate(_valid_yaml(reason=" ")), 1) + + def test_locations(self): + self.assertEqual(_validate(_valid_yaml(locations={"file": "a"})), 1) + self.assertEqual(_validate(_valid_yaml(locations=["a"])), 1) + self.assertEqual(_validate(_valid_yaml(locations=[{"line": 1}])), 1) + self.assertEqual(_validate(_valid_yaml(locations=[{"file": 1, "line": 1}])), 1) + self.assertEqual(_validate(_valid_yaml(locations=[{"file": "a", "line": "1"}])), 1) + self.assertEqual(_validate(_valid_yaml(locations=[{"file": "a", "line_start": "1", "line_end": 2}])), 1) + self.assertEqual(_validate(_valid_yaml(locations=[{"file": "a", "lines": 3}])), 1) + self.assertEqual(_validate(_valid_yaml(locations=[{"file": "a", "lines": [1, "2"]}])), 1) + + def test_all_errors_are_reported_together(self): + err = io.StringIO() + entry = {"id": "Bad_Id", "category": "nope", "platforms": [], "reason": ""} + with redirect_stderr(err): + with self.assertRaises(SystemExit): + justify.validate_yaml({"version": 1, "justifications": [entry]}) + text = err.getvalue() + for fragment in ["kebab-case", "invalid category", "must not be empty", "'reason' must not be empty"]: + self.assertIn(fragment, text) + + +class ResolveLocationLinesTest(unittest.TestCase): + def test_explicit_lines(self): + self.assertEqual(justify.resolve_location_lines({"lines": [3, 1, 2]}), [3, 1, 2]) + + def test_range_is_inclusive(self): + self.assertEqual(justify.resolve_location_lines({"line_start": 10, "line_end": 12}), [10, 11, 12]) + + def test_single_line(self): + self.assertEqual(justify.resolve_location_lines({"line": 7}), [7]) + + def test_no_line_information(self): + self.assertEqual(justify.resolve_location_lines({"file": "a"}), []) + + def test_lines_take_precedence(self): + self.assertEqual(justify.resolve_location_lines({"lines": [1], "line": 5}), [1]) + + +class MatchesPlatformTest(unittest.TestCase): + def test_platform_membership(self): + entry = {"platforms": ["linux"]} + self.assertTrue(justify._matches_platform(entry, "linux")) + self.assertFalse(justify._matches_platform(entry, "qnx")) + self.assertFalse(justify._matches_platform({}, "linux")) + + +class ScanFileForMarkersTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + self.by_id = { + "reason-a": {"id": "reason-a", "category": "other", "reason": " because A "}, + "reason-b": {"id": "reason-b", "category": "platform_specific", "reason": "because B"}, + } + + def tearDown(self): + self.tmp.cleanup() + + def _scan(self, text): + path = self.root / "f.cpp" + path.write_text(text, encoding="utf-8") + return justify.scan_file_for_markers(path, "f.cpp", self.by_id) + + def test_single_line_marker(self): + warnings, lines = self._scan("int a;\nfoo(); // COV_JUSTIFIED reason-a\nint b;\n") + self.assertEqual(warnings, []) + self.assertEqual(lines, {2: {"id": "reason-a", "category": "other", "reason": "because A"}}) + + def test_region_excludes_marker_lines(self): + text = "a\n// COV_JUSTIFIED_START reason-b\nx\ny\n// COV_JUSTIFIED_STOP\nz\n" + warnings, lines = self._scan(text) + self.assertEqual(warnings, []) + self.assertEqual(sorted(lines), [3, 4]) + self.assertTrue(all(v["id"] == "reason-b" for v in lines.values())) + + def test_empty_region_justifies_nothing(self): + warnings, lines = self._scan("// COV_JUSTIFIED_START reason-a\n// COV_JUSTIFIED_STOP\n") + self.assertEqual(warnings, []) + self.assertEqual(lines, {}) + + def test_nested_regions_inner_wins(self): + text = ( + "// COV_JUSTIFIED_START reason-a\n" # 1 + "a\n" # 2 -> reason-a + "// COV_JUSTIFIED_START reason-b\n" # 3 + "b\n" # 4 -> reason-b, then overwritten by outer STOP? No: outer range re-assigns. + "// COV_JUSTIFIED_STOP\n" # 5 + "c\n" # 6 -> reason-a + "// COV_JUSTIFIED_STOP\n" # 7 + ) + warnings, lines = self._scan(text) + self.assertEqual(warnings, []) + self.assertEqual(sorted(lines), [2, 3, 4, 5, 6]) + # The outer region is closed last and covers lines 2..6, so it overwrites the inner assignment. + self.assertEqual(lines[4]["id"], "reason-a") + + def test_unknown_ids_warn_and_do_not_justify(self): + text = "x // COV_JUSTIFIED nope\n// COV_JUSTIFIED_START nope2\ny\n// COV_JUSTIFIED_STOP\n" + warnings, lines = self._scan(text) + self.assertEqual(lines, {}) + self.assertEqual(len(warnings), 3) + self.assertIn("f.cpp:1: COV_JUSTIFIED references unknown ID 'nope'", warnings[0]) + self.assertIn("f.cpp:2: COV_JUSTIFIED_START references unknown ID 'nope2'", warnings[1]) + self.assertIn("f.cpp:4: COV_JUSTIFIED_STOP without matching START", warnings[2]) + + def test_unclosed_region_warns(self): + warnings, lines = self._scan("// COV_JUSTIFIED_START reason-a\nx\n") + self.assertEqual(lines, {}) + self.assertEqual(warnings, ["f.cpp:1: COV_JUSTIFIED_START 'reason-a' without matching STOP"]) + + def test_stop_without_start_warns(self): + warnings, lines = self._scan("x\n// COV_JUSTIFIED_STOP\n") + self.assertEqual(lines, {}) + self.assertEqual(warnings, ["f.cpp:2: COV_JUSTIFIED_STOP without matching START"]) + + def test_marker_id_characters(self): + warnings, lines = self._scan("x // COV_JUSTIFIED reason-a; trailing text\n") + self.assertEqual(sorted(lines), [1]) + + def test_unreadable_file_yields_nothing(self): + warnings, lines = justify.scan_file_for_markers(self.root / "missing.cpp", "missing.cpp", self.by_id) + self.assertEqual((warnings, lines), ([], {})) + + def test_non_utf8_content_is_tolerated(self): + path = self.root / "f.cpp" + path.write_bytes(b"\xff\xfe junk\nfoo(); // COV_JUSTIFIED reason-a\n") + warnings, lines = justify.scan_file_for_markers(path, "f.cpp", self.by_id) + self.assertEqual(sorted(lines), [2]) + + +class CollectSourceFilesTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + for rel in ["src/a.cpp", "src/b.h", "src/c.rs", "src/d.py", "bazel-out/e.cpp", "bazel-bin/f.rs", "docs/g.hpp"]: + p = self.root / rel + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text("", encoding="utf-8") + + def tearDown(self): + self.tmp.cleanup() + + def _rel(self, files): + return sorted(str(f.relative_to(self.root)) for f in files) + + def test_default_filter_and_bazel_dirs_skipped(self): + files = justify.collect_source_files(self.root, "cpp,h,hpp,cc,rs") + self.assertEqual(self._rel(files), ["docs/g.hpp", "src/a.cpp", "src/b.h", "src/c.rs"]) + + def test_custom_filter(self): + files = justify.collect_source_files(self.root, "py, rs") + self.assertEqual(self._rel(files), ["src/c.rs", "src/d.py"]) + + def test_empty_filter_uses_defaults(self): + files = justify.collect_source_files(self.root, "") + self.assertIn("src/a.cpp", self._rel(files)) + self.assertNotIn("src/d.py", self._rel(files)) + + +class LoadYamlTest(unittest.TestCase): + def test_missing_file_exits(self): + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + justify.load_yaml(Path("/nonexistent/justifications.yaml")) + + def test_loads_document(self): + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "j.yaml" + path.write_text("version: 1\njustifications: []\n", encoding="utf-8") + self.assertEqual(justify.load_yaml(path), {"version": 1, "justifications": []}) + + +class MainTest(unittest.TestCase): + """End-to-end: YAML locations + in-code markers -> manifest.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + (self.root / "src").mkdir() + (self.root / "src" / "a.cpp").write_text( + "int a;\nfoo(); // COV_JUSTIFIED marker-linux\nbar(); // COV_JUSTIFIED marker-qnx\nbaz();\n", + encoding="utf-8", + ) + (self.root / "src" / "b.rs").write_text("fn b() {}\nfn c() {}\n", encoding="utf-8") + self.yaml = self.root / "j.yaml" + self.yaml.write_text( + "\n".join( + [ + "version: 1", + "justifications:", + " - id: marker-linux", + " category: defensive_programming", + " platforms: [linux]", + " reason: linux only marker", + " - id: marker-qnx", + " category: platform_specific", + " platforms: [qnx]", + " reason: qnx only marker", + " - id: yaml-loc", + " category: other", + " platforms: [linux, qnx]", + " reason: ' located via yaml '", + " locations:", + " - file: src/b.rs", + " line_start: 1", + " line_end: 2", + "", + ] + ), + encoding="utf-8", + ) + self.out = self.root / "out" / "manifest.json" + + def tearDown(self): + self.tmp.cleanup() + + def _run(self, platform): + argv = ["--yaml", str(self.yaml), "--source-root", str(self.root), "--output", str(self.out)] + if platform: + argv += ["--platform", platform] + err = io.StringIO() + code = None + with redirect_stderr(err): + try: + justify.main(argv) + except SystemExit as exc: + code = exc.code + manifest = json.loads(self.out.read_text(encoding="utf-8")) if self.out.exists() else None + return code, manifest, err.getvalue() + + def test_linux_manifest(self): + code, manifest, err = self._run("linux") + self.assertIsNone(code) + self.assertEqual(manifest["version"], 1) + self.assertEqual(manifest["source_root"], str(self.root)) + self.assertEqual(sorted(manifest["justified_files"]), ["src/a.cpp", "src/b.rs"]) + a = manifest["justified_files"]["src/a.cpp"] + self.assertEqual(list(a), ["2"]) # the qnx-only marker is filtered out; keys are strings + self.assertEqual( + a["2"], {"id": "marker-linux", "category": "defensive_programming", "reason": "linux only marker"} + ) + b = manifest["justified_files"]["src/b.rs"] + self.assertEqual(sorted(b), ["1", "2"]) + self.assertEqual(b["1"]["reason"], "located via yaml") + # The filtered-out marker is reported as unknown for this platform. + self.assertTrue(any("marker-qnx" in w for w in manifest["warnings"])) + self.assertEqual(manifest["errors"], []) + self.assertIn("Resolved 3 justified lines across 2 files", err) + + def test_qnx_manifest(self): + code, manifest, _ = self._run("qnx") + self.assertIsNone(code) + self.assertEqual(list(manifest["justified_files"]["src/a.cpp"]), ["3"]) + + def test_no_platform_keeps_all(self): + code, manifest, _ = self._run(None) + self.assertIsNone(code) + self.assertEqual(sorted(manifest["justified_files"]["src/a.cpp"]), ["2", "3"]) + + def test_missing_location_file_is_an_error(self): + self.yaml.write_text( + "version: 1\njustifications:\n - id: gone\n category: other\n platforms: [linux]\n" + " reason: r\n locations:\n - file: src/missing.cpp\n line: 1\n", + encoding="utf-8", + ) + code, manifest, err = self._run("linux") + self.assertEqual(code, 1) + self.assertIn("File not found for justification 'gone'", err) + self.assertEqual(len(manifest["errors"]), 1) # the manifest is still written for diagnosis + + def test_invalid_yaml_exits_before_scanning(self): + self.yaml.write_text("version: 1\n", encoding="utf-8") + code, manifest, _ = self._run("linux") + self.assertEqual(code, 1) + self.assertIsNone(manifest) + + def test_bazel_symlinks_are_not_scanned(self): + (self.root / "bazel-out").mkdir() + (self.root / "bazel-out" / "gen.cpp").write_text("x // COV_JUSTIFIED marker-linux\n", encoding="utf-8") + code, manifest, _ = self._run("linux") + self.assertIsNone(code) + self.assertNotIn("bazel-out/gen.cpp", manifest["justified_files"]) + + +if __name__ == "__main__": + unittest.main() From 8c4493528438f24a5c998994798ab3d2075816a3 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 15:59:00 +0300 Subject: [PATCH 04/17] Add analysis tests, ground truth, fault injection, self-coverage gate Starlark: rules_testing analysis tests for the coverage_scope rule/aspect (score_coverage/tests/starlark): transitive deps and implementation_deps are collected, shared deps are listed once, header-only libraries yield no baseline archive, generated sources are excluded, output groups are as documented. The fixtures are tagged manual. Integration test: the LCOV produced for the fixture workspace is now compared record by record against integration_tests/expected_lcov.dat, a hand-derived ground truth (line and branch counts explained in the file header). Three fault-injection checks were added: a corrupt report and a non-numeric threshold must exit 2, and a misspelt COV_JUSTIFIED id must be reported and must not raise the effective coverage. Self coverage: tools/self_coverage_gate.py reads the coverage.py LCOV that `bazel coverage --combined_report=lcov` produces for this repository's own Python, prints the per-file C0/C1 table and fails below the thresholds. CI runs it after the unit tests; the current floor is 69% lines / 63% branches (reporter.py 31%, merger.py 48%, effective_coverage.py 68%, the rest above 96%) and is meant to be ratcheted up. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/workflows/tests.yml | 10 +- .gitignore | 5 + MODULE.bazel | 3 + MODULE.bazel.lock | 5 + integration_tests/expected_lcov.dat | 97 +++++++++ integration_tests/run_integration_test.sh | 69 +++++++ score_coverage/tests/starlark/BUILD | 16 ++ .../tests/starlark/coverage_scope_tests.bzl | 148 ++++++++++++++ score_coverage/tests/starlark/fixtures/BUILD | 64 ++++++ .../tests/starlark/fixtures/leaf.cpp | 14 ++ score_coverage/tests/starlark/fixtures/leaf.h | 13 ++ .../tests/starlark/fixtures/mid.cpp | 14 ++ score_coverage/tests/starlark/fixtures/mid.h | 13 ++ score_coverage/tests/starlark/fixtures/only.h | 13 ++ .../tests/starlark/fixtures/user.cpp | 14 ++ tools/BUILD | 18 ++ tools/self_coverage_gate.py | 191 ++++++++++++++++++ tools/self_coverage_gate_test.py | 114 +++++++++++ 18 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 integration_tests/expected_lcov.dat create mode 100644 score_coverage/tests/starlark/BUILD create mode 100644 score_coverage/tests/starlark/coverage_scope_tests.bzl create mode 100644 score_coverage/tests/starlark/fixtures/BUILD create mode 100644 score_coverage/tests/starlark/fixtures/leaf.cpp create mode 100644 score_coverage/tests/starlark/fixtures/leaf.h create mode 100644 score_coverage/tests/starlark/fixtures/mid.cpp create mode 100644 score_coverage/tests/starlark/fixtures/mid.h create mode 100644 score_coverage/tests/starlark/fixtures/only.h create mode 100644 score_coverage/tests/starlark/fixtures/user.cpp create mode 100644 tools/self_coverage_gate.py create mode 100644 tools/self_coverage_gate_test.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20cd228..e968893 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,8 +46,14 @@ jobs: cache-save: ${{ github.ref == 'refs/heads/main' }} - name: Build everything run: bazel build --lockfile_mode=error //... - - name: Run unit tests - run: bazel test --lockfile_mode=error //score_coverage/tests:all + - name: Run unit and Starlark analysis tests + run: bazel test --lockfile_mode=error //score_coverage/... //tools/... + - name: Measure structural coverage of the tool itself (coverage.py) + run: bazel coverage --lockfile_mode=error --combined_report=lcov //score_coverage/tests:all + - name: "Gate structural coverage (ratchet, raise as tests land; target 100/100)" + run: | + bazel run --lockfile_mode=error //tools:self_coverage_gate -- \ + --min-lines 69 --min-branches 63 --summary-md "$GITHUB_STEP_SUMMARY" - name: Ensure the lockfile is up to date run: | bazel mod deps --lockfile_mode=update diff --git a/.gitignore b/.gitignore index d0f3b70..f9d1900 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,8 @@ integration_tests/coverage_linux/ integration_tests/summary.md integration_tests/step_summary.md integration_tests/coverage_artifact/ +integration_tests/actual_normalised.dat +integration_tests/expected_normalised.dat +integration_tests/typo_dir/ +integration_tests/typo.log +integration_tests/report.backup diff --git a/MODULE.bazel b/MODULE.bazel index 1d1ec5c..8e83bfd 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -74,3 +74,6 @@ bazel_dep(name = "score_tooling", version = "2.2.0", dev_dependency = True) # repo mapping, so the root has to declare them itself. bazel_dep(name = "aspect_rules_lint", version = "2.7.1", dev_dependency = True) bazel_dep(name = "buildifier_prebuilt", version = "8.5.1", dev_dependency = True) + +# Starlark analysis tests for the coverage_scope rule/aspect. +bazel_dep(name = "rules_testing", version = "0.9.0", dev_dependency = True) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index ef5ed46..8912c0f 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -351,6 +351,7 @@ "https://bcr.bazel.build/modules/rules_kotlin/2.1.3/MODULE.bazel": "ce7def6d576aa8d3a9c6d10e13b4d157296229674371f67dbf788dae0afae3d5", "https://bcr.bazel.build/modules/rules_kotlin/2.1.3/source.json": "0b0dc9400f14b5fbb13d278ad3bf0413cdbaf0da0db337e055b855e35b878a3b", "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.4/MODULE.bazel": "6a88dd22800cf1f9f79ba32cacad0d3a423ed28efa2c2ed5582eaa78dd3ac1e5", "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", @@ -415,6 +416,8 @@ "https://bcr.bazel.build/modules/rules_swift/1.18.0/MODULE.bazel": "a6aba73625d0dc64c7b4a1e831549b6e375fbddb9d2dde9d80c9de6ec45b24c9", "https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046", "https://bcr.bazel.build/modules/rules_swift/2.1.1/source.json": "40fc69dfaac64deddbb75bd99cdac55f4427d9ca0afbe408576a65428427a186", + "https://bcr.bazel.build/modules/rules_testing/0.9.0/MODULE.bazel": "d4d9a7367978b5c589437f01dba1dc80ac6f389e9991e1c1edb3c8207526ca83", + "https://bcr.bazel.build/modules/rules_testing/0.9.0/source.json": "2a943853f3480f42a9a5205cc4881a147fecdcf7c8ee87750bbeffff9c9a1877", "https://bcr.bazel.build/modules/stardoc/0.5.0/MODULE.bazel": "f9f1f46ba8d9c3362648eea571c6f9100680efc44913618811b58cc9c02cd678", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", @@ -737,6 +740,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.6/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/2.1.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.4/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.7/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/1.0.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multirun/0.9.0/MODULE.bazel": "not found", @@ -793,6 +797,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/1.16.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/1.18.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/2.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_testing/0.9.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/MODULE.bazel": "ade2bad4a331b02d9b7e7d9842e8de8c6fded6186486e02c4f7db5cd4b71d34d", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/source.json": "fbcbc738e652b0c68d5d28dd1db09f2e643dc111f5739b2f6af7ec56c2e88043", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_tooling/2.2.0/MODULE.bazel": "178ba4862246b6ba2bbcd96b7e9e728299b19fb94bcf23d315dc2299aabf7178", diff --git a/integration_tests/expected_lcov.dat b/integration_tests/expected_lcov.dat new file mode 100644 index 0000000..18fbe5c --- /dev/null +++ b/integration_tests/expected_lcov.dat @@ -0,0 +1,97 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +# Ground truth for the integration workspace, derived BY HAND from the fixture +# sources and the tests that run against them (validation evidence for the +# coverage pipeline, ISO 26262-8 11.4.9). Function records (FN*, FNF, FNH) are +# not compared; the pipeline gates on lines and branches. +# +# rust/lib.rs classify() called with -5 and 0 by two tests (each test +# binary runs both #[test] fns => entry lines hit 2x), the +# `positive` arm (22) and never_called (28-30) at 0; the test +# module's own lines (37-44) are instrumented and hit once. +# Branches: 17 (value < 0) both ways, 19 (value == 0) true +# only => 3 of 4. +# src/coverable.cpp classify() called with -5 and 0 => line 24 (`positive`) +# at 0, branch 21 false direction never taken => 3 of 4. +# rust/main.rs no test at all => every line 0 via the empty-profile +# baseline; no branches. +# src/uncovered.cpp no test links against it => all lines 0, both directions +# of the branch on line 18 never executed ('-'). +SF:rust/lib.rs +DA:16,2 +DA:17,2 +DA:18,1 +DA:19,1 +DA:20,1 +DA:22,0 +DA:24,2 +DA:28,0 +DA:29,0 +DA:30,0 +DA:37,1 +DA:38,1 +DA:39,1 +DA:42,1 +DA:43,1 +DA:44,1 +BRDA:17,0,0,1 +BRDA:17,0,1,1 +BRDA:19,0,0,1 +BRDA:19,0,1,0 +BRF:4 +BRH:3 +LF:16 +LH:12 +end_of_record +SF:rust/main.rs +DA:17,0 +DA:18,0 +DA:19,0 +BRF:0 +BRH:0 +LF:3 +LH:0 +end_of_record +SF:src/coverable.cpp +DA:17,2 +DA:18,2 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,0 +DA:25,1 +BRDA:18,0,0,1 +BRDA:18,0,1,1 +BRDA:21,0,0,1 +BRDA:21,0,1,0 +BRF:4 +BRH:3 +LF:9 +LH:8 +end_of_record +SF:src/uncovered.cpp +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +BRDA:18,0,0,- +BRDA:18,0,1,- +BRF:2 +BRH:0 +LF:6 +LH:0 +end_of_record diff --git a/integration_tests/run_integration_test.sh b/integration_tests/run_integration_test.sh index 5b8bf85..73e0693 100755 --- a/integration_tests/run_integration_test.sh +++ b/integration_tests/run_integration_test.sh @@ -131,6 +131,25 @@ check_zero_coverage() { check_zero_coverage "src/uncovered.cpp" check_zero_coverage "rust/main.rs" +echo "=== LCOV must match the hand-verified ground truth exactly ===" +# Normalise: drop function records, keep one record per file sorted by SF, so +# the comparison is independent of record order and of symbol names. +normalise_lcov() { + grep -v '^FN' "$1" | awk ' + /^SF:/ { key = $0; rec = "" } + { rec = rec $0 "\n" } + /^end_of_record/ { records[key] = rec } + END { n = asorti(records, keys); for (i = 1; i <= n; i++) printf "%s", records[keys[i]] }' +} +normalise_lcov lcov.dat > actual_normalised.dat +grep -v '^#' expected_lcov.dat | normalise_lcov /dev/stdin > expected_normalised.dat +if ! diff -u expected_normalised.dat actual_normalised.dat; then + echo "ERROR: coverage data differs from expected_lcov.dat (see diff above)" >&2 + exit 1 +fi +rm -f actual_normalised.dat expected_normalised.dat +echo "OK: LCOV matches the ground truth" + echo "=== Covered files must be present with hits ===" grep -q "SF:.*src/coverable.cpp" lcov.dat || { echo "ERROR: coverable.cpp missing" >&2; exit 1; } grep -q "SF:.*rust/lib.rs" lcov.dat || { echo "ERROR: lib.rs missing" >&2; exit 1; } @@ -152,5 +171,55 @@ if ! awk "BEGIN {exit (${EFFECTIVE} > ${RAW}) ? 0 : 1}"; then fi echo "OK: effective ${EFFECTIVE}% > raw ${RAW}%" +echo "=== Fault injection: a broken report must yield NO verdict (exit 2), never a pass ===" +REPORT="bazel-out/_coverage/_coverage_report.dat" +cp "${REPORT}" report.backup +chmod u+w "${REPORT}" +printf 'this is not a zip archive' > "${REPORT}" +set +e +COVERAGE_THRESHOLD=0 bazel run @score_coverage//:generate_coverage_html > /dev/null 2>&1 +rc=$? +set -e +cp report.backup "${REPORT}" +rm -f report.backup +if [[ "${rc}" -ne 2 ]]; then + echo "ERROR: corrupt report gave exit code ${rc}, expected 2" >&2 + exit 1 +fi +echo "OK: corrupt report is rejected with exit 2" + +echo "=== Fault injection: a non-numeric threshold must be rejected (exit 2) ===" +set +e +COVERAGE_THRESHOLD=lenient bazel run @score_coverage//:generate_coverage_html > /dev/null 2>&1 +rc=$? +set -e +if [[ "${rc}" -ne 2 ]]; then + echo "ERROR: bad threshold gave exit code ${rc}, expected 2" >&2 + exit 1 +fi +echo "OK: invalid threshold is rejected with exit 2" + +echo "=== Fault injection: an unknown justification id must not count as covered ===" +sed -i 's/itest-positive-branch/itest-typo-branch/' src/coverable.cpp +set +e +COVERAGE_THRESHOLD=10 bazel run @score_coverage//:generate_coverage_html -- --yaml "${YAML}" --archive-dir typo_dir > typo.log 2>&1 +rc=$? +set -e +sed -i 's/itest-typo-branch/itest-positive-branch/' src/coverable.cpp +if [[ "${rc}" -ne 0 ]]; then + cat typo.log + echo "ERROR: run with an unknown marker id failed unexpectedly (${rc})" >&2 + exit 1 +fi +grep -q "references unknown ID 'itest-typo-branch'" typo.log || { echo "ERROR: unknown marker id was not reported" >&2; exit 1; } +TYPO_EFFECTIVE="$(grep -oP 'Effective line coverage:\s+\K[0-9.]+' typo_dir/justification_report/summary.txt)" +TYPO_RAW="$(grep -oP 'Raw line coverage:\s+\K[0-9.]+' typo_dir/justification_report/summary.txt)" +if [[ "${TYPO_EFFECTIVE}" != "${TYPO_RAW}" ]]; then + echo "ERROR: unknown marker id still raised effective (${TYPO_EFFECTIVE}) above raw (${TYPO_RAW})" >&2 + exit 1 +fi +rm -rf typo_dir typo.log +echo "OK: unknown justification id is reported and does not count" + echo "" echo "=== All integration checks passed ===" diff --git a/score_coverage/tests/starlark/BUILD b/score_coverage/tests/starlark/BUILD new file mode 100644 index 0000000..25f46cf --- /dev/null +++ b/score_coverage/tests/starlark/BUILD @@ -0,0 +1,16 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load(":coverage_scope_tests.bzl", "coverage_scope_test_suite") + +coverage_scope_test_suite(name = "coverage_scope_tests") diff --git a/score_coverage/tests/starlark/coverage_scope_tests.bzl b/score_coverage/tests/starlark/coverage_scope_tests.bzl new file mode 100644 index 0000000..d439689 --- /dev/null +++ b/score_coverage/tests/starlark/coverage_scope_tests.bzl @@ -0,0 +1,148 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"""Analysis tests for the coverage_scope rule and its aspect. + +The rule writes two text files; rules_testing lets us read the content of +the FileWrite actions without executing anything, so these tests pin down +exactly which source files and archives end up in the coverage scope. +""" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite") +load("@rules_testing//lib:truth.bzl", "matching") +load("//score_coverage:coverage_scope.bzl", "coverage_scope") + +_FIX = "//score_coverage/tests/starlark/fixtures" +_PKG = "score_coverage/tests/starlark" + +def _allowlist(env, target): + return env.expect.that_target(target).action_generating( + _PKG + "/" + target.label.name + "_allowlist.txt", + ).content() + +def _objects(env, target): + return env.expect.that_target(target).action_generating( + _PKG + "/" + target.label.name + "_objects.txt", + ).content() + +# --- transitive deps ------------------------------------------------------- + +def _test_transitive_deps_are_collected(name): + coverage_scope(name = name + "_subject", testonly = True, deps = [_FIX + ":mid"]) + analysis_test(name = name, impl = _test_transitive_deps_are_collected_impl, target = name + "_subject") + +def _test_transitive_deps_are_collected_impl(env, target): + _allowlist(env, target).equals( + "\n".join([ + _PKG + "/fixtures/leaf.cpp", + _PKG + "/fixtures/leaf.h", + _PKG + "/fixtures/mid.cpp", + _PKG + "/fixtures/mid.h", + ]) + "\n", + ) + objects = _objects(env, target) + objects.contains("libleaf.a") + objects.contains("libmid.a") + +# --- implementation_deps --------------------------------------------------- + +def _test_implementation_deps_are_followed(name): + coverage_scope(name = name + "_subject", testonly = True, deps = [_FIX + ":impl_dep_user"]) + analysis_test(name = name, impl = _test_implementation_deps_are_followed_impl, target = name + "_subject") + +def _test_implementation_deps_are_followed_impl(env, target): + _allowlist(env, target).equals( + "\n".join([ + _PKG + "/fixtures/leaf.cpp", + _PKG + "/fixtures/leaf.h", + _PKG + "/fixtures/user.cpp", + ]) + "\n", + ) + +# --- deduplication across roots ------------------------------------------- + +def _test_shared_dependency_listed_once(name): + coverage_scope( + name = name + "_subject", + testonly = True, + deps = [_FIX + ":mid", _FIX + ":impl_dep_user", _FIX + ":leaf"], + ) + analysis_test(name = name, impl = _test_shared_dependency_listed_once_impl, target = name + "_subject") + +def _test_shared_dependency_listed_once_impl(env, target): + _allowlist(env, target).equals( + "\n".join([ + _PKG + "/fixtures/leaf.cpp", + _PKG + "/fixtures/leaf.h", + _PKG + "/fixtures/mid.cpp", + _PKG + "/fixtures/mid.h", + _PKG + "/fixtures/user.cpp", + ]) + "\n", + ) + +# --- header-only library --------------------------------------------------- + +def _test_header_only_library_has_no_archive(name): + coverage_scope(name = name + "_subject", testonly = True, deps = [_FIX + ":header_only"]) + analysis_test(name = name, impl = _test_header_only_library_has_no_archive_impl, target = name + "_subject") + +def _test_header_only_library_has_no_archive_impl(env, target): + _allowlist(env, target).equals(_PKG + "/fixtures/only.h\n") + _objects(env, target).equals("") + +# --- generated sources are excluded --------------------------------------- + +def _test_generated_sources_are_excluded(name): + coverage_scope(name = name + "_subject", testonly = True, deps = [_FIX + ":with_generated"]) + analysis_test(name = name, impl = _test_generated_sources_are_excluded_impl, target = name + "_subject") + +def _test_generated_sources_are_excluded_impl(env, target): + # Exactly the checked-in sources: generated.cpp is absent. + _allowlist(env, target).equals( + "\n".join([ + _PKG + "/fixtures/leaf.cpp", + _PKG + "/fixtures/leaf.h", + ]) + "\n", + ) + + # The archive of the library with the generated source is still a baseline object. + _objects(env, target).contains("libwith_generated.a") + +# --- providers / output groups -------------------------------------------- + +def _test_output_groups(name): + coverage_scope(name = name + "_subject", testonly = True, deps = [_FIX + ":mid"]) + analysis_test(name = name, impl = _test_output_groups_impl, target = name + "_subject") + +def _test_output_groups_impl(env, target): + subject = env.expect.that_target(target) + subject.output_group("allowlist").contains_exactly([_PKG + "/" + target.label.name + "_allowlist.txt"]) + subject.output_group("objects").contains_exactly([_PKG + "/" + target.label.name + "_objects.txt"]) + subject.output_group("object_files").contains_predicate(matching.file_basename_equals("libleaf.a")) + subject.default_outputs().contains_at_least([ + _PKG + "/" + target.label.name + "_allowlist.txt", + _PKG + "/" + target.label.name + "_objects.txt", + ]) + +def coverage_scope_test_suite(name): + test_suite( + name = name, + tests = [ + _test_transitive_deps_are_collected, + _test_implementation_deps_are_followed, + _test_shared_dependency_listed_once, + _test_header_only_library_has_no_archive, + _test_generated_sources_are_excluded, + _test_output_groups, + ], + ) diff --git a/score_coverage/tests/starlark/fixtures/BUILD b/score_coverage/tests/starlark/fixtures/BUILD new file mode 100644 index 0000000..90ab467 --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/BUILD @@ -0,0 +1,64 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +# Fixtures for the coverage_scope analysis tests. They are only analyzed, +# never built: `manual` keeps them out of wildcard builds. +package(default_visibility = ["//score_coverage/tests/starlark:__subpackages__"]) + +MANUAL = ["manual"] + +cc_library( + name = "leaf", + srcs = ["leaf.cpp"], + hdrs = ["leaf.h"], + tags = MANUAL, +) + +cc_library( + name = "mid", + srcs = ["mid.cpp"], + hdrs = ["mid.h"], + tags = MANUAL, + deps = [":leaf"], +) + +cc_library( + name = "impl_dep_user", + srcs = ["user.cpp"], + implementation_deps = [":leaf"], + tags = MANUAL, +) + +cc_library( + name = "header_only", + hdrs = ["only.h"], + tags = MANUAL, +) + +# A generated source must NOT enter the allowlist (only checked-in sources +# are coverage subjects). +genrule( + name = "gen_source", + outs = ["generated.cpp"], + cmd = "echo 'int g() { return 1; }' > $@", + tags = MANUAL, +) + +cc_library( + name = "with_generated", + srcs = ["generated.cpp"], + tags = MANUAL, + deps = [":leaf"], +) diff --git a/score_coverage/tests/starlark/fixtures/leaf.cpp b/score_coverage/tests/starlark/fixtures/leaf.cpp new file mode 100644 index 0000000..0252db5 --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/leaf.cpp @@ -0,0 +1,14 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +// Fixture for the coverage_scope analysis tests; only analyzed. +int leaf() { return 1; } diff --git a/score_coverage/tests/starlark/fixtures/leaf.h b/score_coverage/tests/starlark/fixtures/leaf.h new file mode 100644 index 0000000..fb28115 --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/leaf.h @@ -0,0 +1,13 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +// Fixture for the coverage_scope analysis tests; never compiled. diff --git a/score_coverage/tests/starlark/fixtures/mid.cpp b/score_coverage/tests/starlark/fixtures/mid.cpp new file mode 100644 index 0000000..e57ce2d --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/mid.cpp @@ -0,0 +1,14 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +// Fixture for the coverage_scope analysis tests; only analyzed. +int mid() { return 2; } diff --git a/score_coverage/tests/starlark/fixtures/mid.h b/score_coverage/tests/starlark/fixtures/mid.h new file mode 100644 index 0000000..fb28115 --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/mid.h @@ -0,0 +1,13 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +// Fixture for the coverage_scope analysis tests; never compiled. diff --git a/score_coverage/tests/starlark/fixtures/only.h b/score_coverage/tests/starlark/fixtures/only.h new file mode 100644 index 0000000..fb28115 --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/only.h @@ -0,0 +1,13 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +// Fixture for the coverage_scope analysis tests; never compiled. diff --git a/score_coverage/tests/starlark/fixtures/user.cpp b/score_coverage/tests/starlark/fixtures/user.cpp new file mode 100644 index 0000000..50e0191 --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/user.cpp @@ -0,0 +1,14 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +// Fixture for the coverage_scope analysis tests; only analyzed. +int user() { return 3; } diff --git a/tools/BUILD b/tools/BUILD index 316b8c6..c7f180a 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_python//python:defs.bzl", "py_binary", "py_test") load("@score_tooling//:defs.bzl", "copyright_checker") load("@score_tooling//third_party/format:macros.bzl", "use_format_targets") @@ -47,3 +48,20 @@ use_format_targets(languages = [ "starlark", "yaml", ]) + +# Structural coverage of this repository's own Python (coverage.py via +# `bazel coverage --combined_report=lcov //score_coverage/tests:all`). +py_binary( + name = "self_coverage_gate", + srcs = ["self_coverage_gate.py"], +) + +py_test( + name = "self_coverage_gate_test", + srcs = [ + "self_coverage_gate.py", + "self_coverage_gate_test.py", + ], + imports = ["."], + main = "self_coverage_gate_test.py", +) diff --git a/tools/self_coverage_gate.py b/tools/self_coverage_gate.py new file mode 100644 index 0000000..172b5a3 --- /dev/null +++ b/tools/self_coverage_gate.py @@ -0,0 +1,191 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Structural-coverage gate for score_coverage's OWN Python code. + +The pipeline this repository ships measures C++ and Rust; its own code is +Python and is measured by coverage.py through `bazel coverage` (rules_python, +configure_coverage_tool = True). This script reads the combined LCOV that +Bazel writes, prints the per-file C0 (line) and C1 (branch) table the +verification report needs, and fails when the totals are below the thresholds. + +Usage: + bazel coverage --combined_report=lcov //score_coverage/tests:all + bazel run //tools:self_coverage_gate -- --min-lines 69 --min-branches 63 \\ + [--lcov bazel-out/_coverage/_coverage_report.dat] [--summary-md out.md] + +Only files under score_coverage/ (excluding tests/) count. +""" + +from __future__ import annotations + +import argparse +import os +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import Dict, List, Optional, Sequence + +DEFAULT_LCOV = Path("bazel-out/_coverage/_coverage_report.dat") +SCOPE_PREFIX = "score_coverage/" +EXCLUDED_PREFIX = "score_coverage/tests/" + + +@dataclass +class FileCoverage: + path: str + lines_found: int = 0 + lines_hit: int = 0 + branches_found: int = 0 + branches_hit: int = 0 + + def add(self, other: "FileCoverage") -> None: + self.lines_found += other.lines_found + self.lines_hit += other.lines_hit + self.branches_found += other.branches_found + self.branches_hit += other.branches_hit + + +@dataclass +class Totals: + files: List[FileCoverage] = field(default_factory=list) + + @property + def lines_found(self) -> int: + return sum(f.lines_found for f in self.files) + + @property + def lines_hit(self) -> int: + return sum(f.lines_hit for f in self.files) + + @property + def branches_found(self) -> int: + return sum(f.branches_found for f in self.files) + + @property + def branches_hit(self) -> int: + return sum(f.branches_hit for f in self.files) + + +def pct(hit: int, found: int) -> Optional[float]: + """Percentage, or None when nothing was found (no verdict).""" + return None if found == 0 else 100.0 * hit / found + + +def in_scope(path: str) -> bool: + return path.startswith(SCOPE_PREFIX) and not path.startswith(EXCLUDED_PREFIX) + + +def parse_lcov(path: Path) -> Totals: + """Aggregate LF/LH/BRF/BRH per in-scope source file (records may repeat per test).""" + if not path.is_file(): + raise FileNotFoundError(f"LCOV file not found: {path}") + per_file: Dict[str, FileCoverage] = {} + current: Optional[FileCoverage] = None + with open(path, "r", encoding="utf-8") as f: + for raw in f: + line = raw.rstrip("\n") + if line.startswith("SF:"): + current = FileCoverage(path=line[3:]) + elif line == "end_of_record": + if current is not None and in_scope(current.path): + per_file.setdefault(current.path, FileCoverage(path=current.path)).add(current) + current = None + elif current is not None: + key, _, value = line.partition(":") + if key in ("LF", "LH", "BRF", "BRH"): + number = int(value) + if number < 0: + raise ValueError(f"negative {key} in record for {current.path}") + attr = {"LF": "lines_found", "LH": "lines_hit", "BRF": "branches_found", "BRH": "branches_hit"}[key] + setattr(current, attr, getattr(current, attr) + number) + for fc in per_file.values(): + if fc.lines_hit > fc.lines_found or fc.branches_hit > fc.branches_found: + raise ValueError(f"corrupt LCOV record for {fc.path}: hit count exceeds found count") + return Totals(files=sorted(per_file.values(), key=lambda fc: fc.path)) + + +def fmt(value: Optional[float]) -> str: + return " n/a " if value is None else f"{value:6.2f}" + + +def render_table(totals: Totals, markdown: bool) -> str: + rows = [(f.path, f.lines_hit, f.lines_found, f.branches_hit, f.branches_found) for f in totals.files] + rows.append(("TOTAL", totals.lines_hit, totals.lines_found, totals.branches_hit, totals.branches_found)) + if markdown: + out = ["| File | Lines (C0) | Branches (C1) |", "|---|---:|---:|"] + for path, lh, lf, bh, bf in rows: + out.append( + f"| `{path}` | {fmt(pct(lh, lf)).strip()}% ({lh}/{lf}) | {fmt(pct(bh, bf)).strip()}% ({bh}/{bf}) |" + ) + return "\n".join(out) + "\n" + width = max(len(r[0]) for r in rows) + out = [f"{'File':<{width}} {'Lines (C0)':>22} {'Branches (C1)':>22}"] + for path, lh, lf, bh, bf in rows: + out.append(f"{path:<{width}} {fmt(pct(lh, lf))}% ({lh:>4}/{lf:>4}) {fmt(pct(bh, bf))}% ({bh:>4}/{bf:>4})") + return "\n".join(out) + "\n" + + +def evaluate(totals: Totals, min_lines: float, min_branches: float) -> List[str]: + """Return the list of gate violations (empty when the gate passes).""" + problems = [] + line_pct = pct(totals.lines_hit, totals.lines_found) + branch_pct = pct(totals.branches_hit, totals.branches_found) + if line_pct is None: + problems.append("no instrumented lines found in scope; nothing to gate on") + elif line_pct < min_lines: + problems.append(f"line coverage {line_pct:.2f}% is below the minimum of {min_lines:g}%") + if branch_pct is None: + problems.append("no branch data found in scope; nothing to gate on") + elif branch_pct < min_branches: + problems.append(f"branch coverage {branch_pct:.2f}% is below the minimum of {min_branches:g}%") + return problems + + +def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--lcov", type=Path, default=None, help=f"Combined LCOV (default: {DEFAULT_LCOV})") + parser.add_argument("--min-lines", type=float, required=True, help="Minimum line coverage in percent") + parser.add_argument("--min-branches", type=float, required=True, help="Minimum branch coverage in percent") + parser.add_argument("--summary-md", type=Path, default=None, help="Append a markdown table to this file") + return parser.parse_args(argv) + + +def main(argv: Optional[Sequence[str]] = None) -> int: + args = parse_args(argv) + workspace = Path(os.environ.get("BUILD_WORKSPACE_DIRECTORY", ".")) + lcov = args.lcov if args.lcov is not None else workspace / DEFAULT_LCOV + if not lcov.is_absolute(): + lcov = workspace / lcov + for threshold in (args.min_lines, args.min_branches): + if not 0.0 <= threshold <= 100.0: + print(f"ERROR: thresholds must be within [0, 100], got {threshold}", file=sys.stderr) + return 2 + try: + totals = parse_lcov(lcov) + except (FileNotFoundError, ValueError) as exc: + print(f"ERROR: {exc}", file=sys.stderr) + return 2 + print(render_table(totals, markdown=False)) + if args.summary_md is not None: + target = args.summary_md if args.summary_md.is_absolute() else workspace / args.summary_md + with open(target, "a", encoding="utf-8") as f: + f.write("## Structural coverage of score_coverage (coverage.py)\n\n") + f.write(render_table(totals, markdown=True)) + problems = evaluate(totals, args.min_lines, args.min_branches) + for problem in problems: + print(f"ERROR: {problem}", file=sys.stderr) + return 1 if problems else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/self_coverage_gate_test.py b/tools/self_coverage_gate_test.py new file mode 100644 index 0000000..f9c54f0 --- /dev/null +++ b/tools/self_coverage_gate_test.py @@ -0,0 +1,114 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Tests for the repository's own coverage gate.""" + +import io +import tempfile +import unittest +from contextlib import redirect_stderr, redirect_stdout +from pathlib import Path + +import self_coverage_gate as gate + +LCOV = ( + "SF:score_coverage/a.py\nLF:10\nLH:8\nBRF:4\nBRH:2\nend_of_record\n" + "SF:score_coverage/a.py\nLF:10\nLH:9\nBRF:4\nBRH:3\nend_of_record\n" # same file from a second test + "SF:score_coverage/tests/a_test.py\nLF:50\nLH:50\nBRF:0\nBRH:0\nend_of_record\n" # excluded + "SF:external/other.py\nLF:5\nLH:0\nend_of_record\n" # out of scope + "SF:score_coverage/b.py\nLF:10\nLH:0\nBRF:2\nBRH:0\nend_of_record\n" +) + + +class ParseLcovTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.lcov = Path(self.tmp.name) / "lcov.dat" + self.lcov.write_text(LCOV, encoding="utf-8") + + def tearDown(self): + self.tmp.cleanup() + + def test_scope_and_aggregation(self): + totals = gate.parse_lcov(self.lcov) + self.assertEqual([f.path for f in totals.files], ["score_coverage/a.py", "score_coverage/b.py"]) + a = totals.files[0] + self.assertEqual((a.lines_found, a.lines_hit, a.branches_found, a.branches_hit), (20, 17, 8, 5)) + self.assertEqual((totals.lines_found, totals.lines_hit), (30, 17)) + self.assertEqual((totals.branches_found, totals.branches_hit), (10, 5)) + + def test_missing_file(self): + with self.assertRaises(FileNotFoundError): + gate.parse_lcov(self.lcov.parent / "missing") + + def test_corrupt_record(self): + self.lcov.write_text("SF:score_coverage/a.py\nLF:1\nLH:2\nend_of_record\n", encoding="utf-8") + with self.assertRaises(ValueError): + gate.parse_lcov(self.lcov) + + +class EvaluateTest(unittest.TestCase): + def _totals(self, lh, lf, bh, bf): + return gate.Totals(files=[gate.FileCoverage("score_coverage/x.py", lf, lh, bf, bh)]) + + def test_pass_and_fail(self): + self.assertEqual(gate.evaluate(self._totals(17, 30, 5, 10), 50, 50), []) + problems = gate.evaluate(self._totals(17, 30, 5, 10), 60, 51) + self.assertEqual(len(problems), 2) + self.assertIn("line coverage 56.67%", problems[0]) + self.assertIn("branch coverage 50.00%", problems[1]) + + def test_no_data_is_a_failure(self): + self.assertEqual(len(gate.evaluate(gate.Totals(), 0, 0)), 2) + + +class MainTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + (self.root / "lcov.dat").write_text(LCOV, encoding="utf-8") + + def tearDown(self): + self.tmp.cleanup() + + def _main(self, argv): + out, err = io.StringIO(), io.StringIO() + with redirect_stdout(out), redirect_stderr(err): + rc = gate.main(argv) + return rc, out.getvalue(), err.getvalue() + + def test_exit_codes_and_summary(self): + lcov = str(self.root / "lcov.dat") + rc, out, _ = self._main(["--lcov", lcov, "--min-lines", "50", "--min-branches", "50"]) + self.assertEqual(rc, 0) + self.assertIn("TOTAL", out) + self.assertIn("score_coverage/b.py", out) + rc, _, err = self._main(["--lcov", lcov, "--min-lines", "90", "--min-branches", "50"]) + self.assertEqual(rc, 1) + self.assertIn("below the minimum", err) + md = self.root / "summary.md" + md.write_text("# before\n", encoding="utf-8") + rc, _, _ = self._main(["--lcov", lcov, "--min-lines", "0", "--min-branches", "0", "--summary-md", str(md)]) + self.assertEqual(rc, 0) + text = md.read_text(encoding="utf-8") + self.assertTrue(text.startswith("# before\n")) + self.assertIn("| `score_coverage/a.py` | 85.00% (17/20) | 62.50% (5/8) |", text) + + def test_bad_inputs(self): + rc, _, _ = self._main(["--lcov", str(self.root / "nope"), "--min-lines", "0", "--min-branches", "0"]) + self.assertEqual(rc, 2) + rc, _, _ = self._main(["--lcov", str(self.root / "lcov.dat"), "--min-lines", "101", "--min-branches", "0"]) + self.assertEqual(rc, 2) + + +if __name__ == "__main__": + unittest.main() From 1bc1ec85fd5c49203177ad56c7ca10f100c5c0c1 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:02:52 +0300 Subject: [PATCH 05/17] Cover merger and reporter end to end with fake llvm tools merger_test (+10 cases): main() against a fake Bazel coverage directory and a fake llvm-profdata (profraw merge order, meta.json content, zip layout, dangling gcov symlink cleanup, RUST_LLVM_PROFDATA fallback) plus the fail paths (no objects / no profraw exit 0 without output, missing or failing llvm-profdata exit 1). reporter_test (+19 cases): reports file, per-test zip extraction with every invalid-input variant, tool resolution order, cxxfilt lookup, allowlist and baseline manifests (missing baseline object is a hard error), run_command stderr separation, the exact llvm-cov report/show/export command lines via a logging fake, and main() end to end: merged profdata, allowlist-driven exclusion regexes, workspace-relative SF paths, HTML title rewrite, empty zip for no/invalid reports, missing tools and empty allowlist as errors. merger.main and reporter.main take an argv parameter like the other tools. Note: the first version of these additions sat after the unittest.main() guard and silently never ran; the guard now closes both files. Self coverage moves from 69.6/63.4 to 86.9% lines / 79.4% branches (merger 98.4%, reporter 91.6%); the CI ratchet is raised to 86/79. The remaining gap is the gcovr (QNX) backend of effective_coverage.py. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/workflows/tests.yml | 2 +- score_coverage/merger.py | 11 +- score_coverage/reporter.py | 10 +- score_coverage/tests/merger_test.py | 182 ++++++++++++ score_coverage/tests/reporter_test.py | 411 ++++++++++++++++++++++++++ 5 files changed, 605 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e968893..67b17a6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,7 +53,7 @@ jobs: - name: "Gate structural coverage (ratchet, raise as tests land; target 100/100)" run: | bazel run --lockfile_mode=error //tools:self_coverage_gate -- \ - --min-lines 69 --min-branches 63 --summary-md "$GITHUB_STEP_SUMMARY" + --min-lines 86 --min-branches 79 --summary-md "$GITHUB_STEP_SUMMARY" - name: Ensure the lockfile is up to date run: | bazel mod deps --lockfile_mode=update diff --git a/score_coverage/merger.py b/score_coverage/merger.py index fc508f6..17f159b 100644 --- a/score_coverage/merger.py +++ b/score_coverage/merger.py @@ -33,11 +33,12 @@ import sys import zipfile from pathlib import Path -from typing import List, Set +from typing import List, Optional, Set -def main() -> None: - args = parse_args() +def main(argv: Optional[List[str]] = None) -> None: + """Entry point. ``argv`` defaults to ``sys.argv[1:]``.""" + args = parse_args(argv) # Get object files from the manifest. object_files = get_object_files_from_manifest(args.source_file_manifest) @@ -235,7 +236,7 @@ def create_zip(root: Path, directories: List[Path], output_file: Path) -> None: zf.write(file_path, arcname) -def parse_args() -> argparse.Namespace: +def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace: """Parse command-line arguments matching the Bazel LCOV_MERGER interface.""" parser = argparse.ArgumentParser(description="LLVM coverage merger for Bazel") parser.add_argument("--coverage_dir", type=Path, required=True) @@ -243,7 +244,7 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--source_file_manifest", type=Path, required=True) parser.add_argument("--filter_sources", action="append", default=[]) parser.add_argument("--sources_to_replace_file", type=str, default=None) - return parser.parse_args() + return parser.parse_args(argv) if __name__ == "__main__": diff --git a/score_coverage/reporter.py b/score_coverage/reporter.py index e743a5a..8005258 100644 --- a/score_coverage/reporter.py +++ b/score_coverage/reporter.py @@ -34,9 +34,9 @@ from python.runfiles import Runfiles -def main() -> None: - """Main entry point.""" - args = parse_args() +def main(argv: Optional[List[str]] = None) -> None: + """Main entry point. ``argv`` defaults to ``sys.argv[1:]``.""" + args = parse_args(argv) r = Runfiles.Create() # Read the list of per-test report files. @@ -733,7 +733,7 @@ def create_zip(root: Path, directories: List[Path], output_file: Path) -> None: zf.write(file_path, arcname) -def parse_args() -> argparse.Namespace: +def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace: """Parse command-line arguments matching the Bazel coverage_report_generator interface.""" parser = argparse.ArgumentParser(description="LLVM coverage reporter for Bazel") parser.add_argument("--output_file", type=Path, required=True) @@ -768,7 +768,7 @@ def parse_args() -> argparse.Namespace: default=None, help="Rlocation path to llvm-cxxfilt (supplied by score_coverage_reporter)", ) - return parser.parse_args() + return parser.parse_args(argv) if __name__ == "__main__": diff --git a/score_coverage/tests/merger_test.py b/score_coverage/tests/merger_test.py index 733f6fe..d74a8e8 100644 --- a/score_coverage/tests/merger_test.py +++ b/score_coverage/tests/merger_test.py @@ -112,5 +112,187 @@ def test_objects_list_entries_are_resolved(self): self.assertEqual(objects, {str(obj)}) +# --------------------------------------------------------------------------- +# Added for the score_coverage qualification: end-to-end behaviour of main() +# with a fake llvm-profdata, plus the helpers that were not covered. +# --------------------------------------------------------------------------- + +import io # noqa: E402 +import json # noqa: E402 +import stat # noqa: E402 +import sys # noqa: E402 +import zipfile # noqa: E402 +from contextlib import redirect_stderr # noqa: E402 + +from score_coverage import merger # noqa: E402 + + +def _fake_profdata(path: Path, fail: bool = False) -> Path: + """A stand-in llvm-profdata that concatenates its inputs into --output.""" + body = "#!/usr/bin/env python3\nimport sys\n" + if fail: + body += "print('boom'); sys.exit(3)\n" + else: + body += ( + "args = sys.argv[1:]\n" + "assert args[0] == 'merge' and '--sparse' in args, args\n" + "out = args[args.index('--output') + 1]\n" + "inputs = args[args.index('--output') + 2:]\n" + "with open(out, 'wb') as o:\n" + " for i in inputs:\n" + " o.write(open(i, 'rb').read())\n" + ) + path.write_text(body, encoding="utf-8") + path.chmod(path.stat().st_mode | stat.S_IXUSR) + return path + + +class CleanupDanglingSymlinksTest(unittest.TestCase): + def test_gcov_and_sandbox_links_removed_others_kept(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + (root / "keep.txt").write_text("x", encoding="utf-8") + (root / "gcov").symlink_to("/nonexistent/sandbox/gcov") + (root / "into_sandbox").symlink_to("/tmp/bazel-sandbox/123/thing") + (root / "other_link").symlink_to("/usr/bin") + merger.cleanup_dangling_symlinks(root) + self.assertFalse((root / "gcov").is_symlink()) + self.assertFalse((root / "into_sandbox").is_symlink()) + self.assertTrue((root / "other_link").is_symlink()) + self.assertTrue((root / "keep.txt").is_file()) + + +class CreateZipTest(unittest.TestCase): + def test_only_listed_directories_relative_to_root(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + (root / "a" / "sub").mkdir(parents=True) + (root / "a" / "sub" / "f.txt").write_text("f", encoding="utf-8") + (root / "b").mkdir() + (root / "b" / "g.txt").write_text("g", encoding="utf-8") + (root / "c").mkdir() + (root / "c" / "h.txt").write_text("h", encoding="utf-8") + out = root / "out.zip" + merger.create_zip(root, [root / "a", root / "b", root / "missing"], out) + with zipfile.ZipFile(out) as zf: + self.assertEqual(sorted(zf.namelist()), ["a/sub/f.txt", "b/g.txt"]) + + +class RunCommandTest(unittest.TestCase): + def test_failure_exits_with_1(self): + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit) as ctx: + merger.run_command([sys.executable, "-c", "import sys; print('bad'); sys.exit(7)"]) + self.assertEqual(ctx.exception.code, 1) + + def test_success_returns_output(self): + result = merger.run_command([sys.executable, "-c", "print('ok')"]) + self.assertEqual(result.stdout.strip(), "ok") + + +class MergerMainTest(unittest.TestCase): + """main() against a fake Bazel coverage directory and a fake llvm-profdata.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + self.coverage_dir = self.root / "coverage_dir" + self.coverage_dir.mkdir() + # An "instrumented object" listed through objects_list.txt. + self.exec_root = self.root / "execroot" + (self.exec_root / "bazel-out" / "k8" / "bin").mkdir(parents=True) + self.obj = self.exec_root / "bazel-out" / "k8" / "bin" / "libfoo.a" + self.obj.write_bytes(b"!\n") + objects_list = self.root / "objects_list.txt" + objects_list.write_text("bazel-out/k8/bin/libfoo.a\n\n", encoding="utf-8") + self.manifest = self.root / "manifest.txt" + self.manifest.write_text(f"{objects_list}\n", encoding="utf-8") + self.output = self.root / "coverage.zip" + self.profdata = _fake_profdata(self.root / "llvm-profdata") + self.env = { + "ROOT": str(self.exec_root), + "RUNFILES_DIR": str(self.root / "no_runfiles"), + "TEST_WORKSPACE": "_main", + "LLVM_PROFDATA": str(self.profdata), + "TEST_TARGET": "//pkg:t", + "PATH": os.environ.get("PATH", ""), + } + + def tearDown(self): + self.tmp.cleanup() + + def _argv(self): + return [ + "--coverage_dir", + str(self.coverage_dir), + "--output_file", + str(self.output), + "--source_file_manifest", + str(self.manifest), + "--filter_sources", + "external/.*", + ] + + def _run(self): + err = io.StringIO() + with mock.patch.dict(os.environ, self.env, clear=True), redirect_stderr(err): + try: + merger.main(self._argv()) + except SystemExit as exc: + return exc.code, err.getvalue() + return None, err.getvalue() + + def test_merges_profraw_and_packages_meta(self): + (self.coverage_dir / "b.profraw").write_bytes(b"BBBB") + (self.coverage_dir / "a.profraw").write_bytes(b"AAAA") + (self.coverage_dir / "gcov").symlink_to("/sandbox/gone/gcov") + code, err = self._run() + self.assertIsNone(code, err) + self.assertIn("Coverage merger completed for '//pkg:t'", err) + with zipfile.ZipFile(self.output) as zf: + names = sorted(zf.namelist()) + self.assertEqual(names, ["meta/meta.json", "profdata/target.profdata"]) + self.assertEqual(zf.read("profdata/target.profdata"), b"AAAABBBB") # sorted input order + meta = json.loads(zf.read("meta/meta.json")) + self.assertEqual(meta, {"object_files": [os.path.realpath(self.obj)]}) + self.assertFalse((self.coverage_dir / "gcov").is_symlink()) + + def test_no_profraw_skips_quietly_with_exit_0(self): + code, err = self._run() + self.assertEqual(code, 0) + self.assertIn("No *.profraw files found", err) + self.assertFalse(self.output.exists()) + + def test_no_objects_skips_quietly_with_exit_0(self): + self.manifest.write_text("", encoding="utf-8") + (self.coverage_dir / "a.profraw").write_bytes(b"A") + code, err = self._run() + self.assertEqual(code, 0) + self.assertIn("No instrumented object files found", err) + + def test_missing_llvm_profdata_is_an_error(self): + (self.coverage_dir / "a.profraw").write_bytes(b"A") + self.env["LLVM_PROFDATA"] = str(self.root / "does_not_exist") + code, err = self._run() + self.assertEqual(code, 1) + self.assertIn("llvm-profdata not found", err) + + def test_failing_llvm_profdata_is_an_error(self): + (self.coverage_dir / "a.profraw").write_bytes(b"A") + _fake_profdata(self.profdata, fail=True) + code, err = self._run() + self.assertEqual(code, 1) + self.assertIn("Command failed with code 3", err) + self.assertFalse(self.output.exists()) + + def test_rust_llvm_profdata_fallback_is_used(self): + (self.coverage_dir / "a.profraw").write_bytes(b"A") + del self.env["LLVM_PROFDATA"] + self.env["RUST_LLVM_PROFDATA"] = str(self.profdata) + code, _ = self._run() + self.assertIsNone(code) + self.assertTrue(self.output.exists()) + + if __name__ == "__main__": unittest.main() diff --git a/score_coverage/tests/reporter_test.py b/score_coverage/tests/reporter_test.py index e3c1a15..099d938 100644 --- a/score_coverage/tests/reporter_test.py +++ b/score_coverage/tests/reporter_test.py @@ -162,5 +162,416 @@ def test_produces_valid_empty_zip(self): self.assertEqual(zf.namelist(), []) +# --------------------------------------------------------------------------- +# Added for the score_coverage qualification: helpers around llvm-cov and an +# end-to-end run of main() against fake llvm tools. +# --------------------------------------------------------------------------- + +import io # noqa: E402 +import json # noqa: E402 +import os # noqa: E402 +import stat # noqa: E402 +import sys # noqa: E402 +from contextlib import redirect_stderr # noqa: E402 +from unittest import mock # noqa: E402 + +from score_coverage import reporter # noqa: E402 + + +class _FakeRunfiles: + """Minimal stand-in for python.runfiles.Runfiles: maps rlocation paths to files.""" + + def __init__(self, mapping): + self.mapping = mapping + + def Rlocation(self, path): # noqa: N802 (mirrors the real API) + if os.path.isabs(path): + return path + return self.mapping.get(path) + + +def _write_tool(path: Path, body: str) -> Path: + path.write_text("#!/usr/bin/env python3\nimport sys, os\n" + body, encoding="utf-8") + path.chmod(path.stat().st_mode | stat.S_IXUSR) + return path + + +REPORT_TABLE = """Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover +------------------------------------------------------------------------------------------------------------------------------------------------------- +/proc/self/cwd/src/a.cpp 4 1 75.00% 1 0 100.00% 10 2 80.00% +/ws/src/b.cpp 2 2 0.00% 1 1 0.00% 5 5 0.00% +rust/lib.rs 3 0 100.00% 2 0 100.00% 8 0 100.00% +------------------------------------------------------------------------------------------------------------------------------------------------------- +TOTAL 9 3 66.67% 4 1 75.00% 23 7 69.57% +""" + + +def _fake_llvm_cov(path: Path) -> Path: + """Fake llvm-cov: records argv, answers report/show/export plausibly.""" + body = f""" +args = sys.argv[1:] +with open(os.environ["FAKE_LLVM_COV_LOG"], "a") as log: + log.write(" ".join(args) + "\\n") +sub = args[0] +if sub == "report": + sys.stdout.write({REPORT_TABLE!r}) +elif sub == "export": + empty = "--empty-profile" in args + sys.stderr.write("warning: something cosmetic\\n") + if empty: + sys.stdout.write("SF:/ws/src/b.cpp\\nDA:1,0\\nLF:1\\nLH:0\\nend_of_record\\n") + else: + sys.stdout.write("SF:/ws/src/a.cpp\\nDA:1,1\\nLF:1\\nLH:1\\nend_of_record\\n") +elif sub == "show": + out = [a for a in args if a.startswith("--output-dir=")][0].split("=", 1)[1] + os.makedirs(os.path.join(out, "coverage", "ws", "src"), exist_ok=True) + open(os.path.join(out, "index.html"), "w").write("index") + open(os.path.join(out, "style.css"), "w").write("body {{}}") + open(os.path.join(out, "coverage", "ws", "src", "a.cpp.html"), "w").write( + "
/ws/src/a.cpp
") +else: + sys.exit(2) +""" + return _write_tool(path, body) + + +def _fake_llvm_profdata(path: Path) -> Path: + return _write_tool( + path, + "args = sys.argv[1:]\nout = args[args.index('--output') + 1]\n" + "open(out, 'wb').write(b''.join(open(i, 'rb').read() for i in args[args.index('--output') + 2:]))\n", + ) + + +class ReadReportsFileTest(unittest.TestCase): + def test_blank_lines_dropped(self): + with tempfile.TemporaryDirectory() as tmp: + f = Path(tmp) / "reports.txt" + f.write_text("a.zip\n\n b.zip \n", encoding="utf-8") + self.assertEqual(reporter.read_reports_file(f), ["a.zip", "b.zip"]) + + +class ExtractReportsTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + self.obj = self.root / "libx.a" + self.obj.write_bytes(b"!\n") + self.cwd = os.getcwd() + os.chdir(self.root) + + def tearDown(self): + os.chdir(self.cwd) + self.tmp.cleanup() + + def _zip(self, name, meta=None, profdata=b"PROF"): + path = self.root / name + with zipfile.ZipFile(path, "w") as zf: + if meta is not None: + zf.writestr("meta/meta.json", json.dumps(meta)) + if profdata is not None: + zf.writestr("profdata/target.profdata", profdata) + return str(path) + + def test_valid_reports_are_extracted(self): + good = self._zip("good.zip", {"object_files": [str(self.obj), str(self.root / "missing.a"), ""]}) + with redirect_stderr(io.StringIO()): + profdata, objects = reporter.extract_reports([good]) + self.assertEqual(len(profdata), 1) + self.assertTrue(Path(next(iter(profdata))).read_bytes() == b"PROF") + self.assertEqual(objects, {os.path.realpath(self.obj)}) + + def test_invalid_inputs_are_skipped(self): + empty = self.root / "empty.zip" + empty.write_bytes(b"") + notzip = self.root / "notzip.zip" + notzip.write_text("nope", encoding="utf-8") + no_meta = self._zip("nometa.zip", meta=None) + no_prof = self._zip("noprof.zip", meta={"object_files": []}, profdata=None) + bad_json = self.root / "badjson.zip" + with zipfile.ZipFile(bad_json, "w") as zf: + zf.writestr("meta/meta.json", "{bad") + zf.writestr("profdata/target.profdata", b"P") + err = io.StringIO() + with redirect_stderr(err): + profdata, objects = reporter.extract_reports( + [ + str(empty), + str(notzip), + no_meta, + no_prof, + str(bad_json), + str(self.root / "missing.zip"), + str(self.root / "baseline_coverage.dat"), + ] + ) + self.assertEqual((profdata, objects), (set(), set())) + self.assertEqual(err.getvalue().count("WARNING: Skipping invalid report"), 3) + + +class ResolveToolTest(unittest.TestCase): + def test_preference_order(self): + with tempfile.TemporaryDirectory() as tmp: + real = Path(tmp) / "llvm-cov" + real.write_text("", encoding="utf-8") + fallback = Path(tmp) / "fallback-cov" + fallback.write_text("", encoding="utf-8") + rf = _FakeRunfiles({"toolchain/llvm-cov": str(real), "llvm_toolchain/llvm-cov": str(fallback)}) + self.assertEqual(reporter.resolve_tool(rf, "toolchain/llvm-cov", "llvm_toolchain/llvm-cov"), real) + self.assertEqual(reporter.resolve_tool(rf, None, "llvm_toolchain/llvm-cov"), fallback) + self.assertEqual(reporter.resolve_tool(rf, str(real), ""), real) # plain path + self.assertIsNone(reporter.resolve_tool(rf, "unknown", "also-unknown")) + self.assertIsNone(reporter.resolve_tool(None, "unknown", "")) + + +class FindCxxfiltTest(unittest.TestCase): + def test_explicit_then_sibling_then_none(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + (root / "bin").mkdir() + cov = root / "bin" / "llvm-cov" + cov.write_text("", encoding="utf-8") + explicit = root / "explicit-cxxfilt" + explicit.write_text("", encoding="utf-8") + rf = _FakeRunfiles({"x/llvm-cxxfilt": str(explicit)}) + self.assertEqual(reporter.find_cxxfilt(cov, rf, "x/llvm-cxxfilt"), str(explicit)) + sibling = root / "bin" / "llvm-cxxfilt" + sibling.write_text("", encoding="utf-8") + self.assertEqual(reporter.find_cxxfilt(cov, rf, None), str(sibling)) + sibling.unlink() + self.assertEqual(reporter.find_cxxfilt(cov, rf, None), "") + + +class LoadAllowlistAndBaselineTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + + def tearDown(self): + self.tmp.cleanup() + + def test_allowlist_comments_and_blank_lines_ignored(self): + f = self.root / "allow.txt" + f.write_text("# comment\nsrc/a.cpp\n\n src/b.h \n", encoding="utf-8") + rf = _FakeRunfiles({"main/allow.txt": str(f)}) + self.assertEqual(reporter.load_coverage_allowlist(rf, "main/allow.txt"), ["src/a.cpp", "src/b.h"]) + self.assertEqual(reporter.load_coverage_allowlist(rf, "missing"), []) + + def test_baseline_objects_resolved_via_main_repo(self): + obj = self.root / "bazel-out" / "bin" / "libz.a" + obj.parent.mkdir(parents=True) + obj.write_bytes(b"") + manifest = self.root / "objects.txt" + manifest.write_text("# c\nbazel-out/bin/libz.a\n", encoding="utf-8") + rf = _FakeRunfiles({"main/objects.txt": str(manifest), "_main/bazel-out/bin/libz.a": str(obj)}) + self.assertEqual(reporter.load_baseline_objects(rf, "main/objects.txt", "/ws"), [str(obj)]) + self.assertEqual(reporter.load_baseline_objects(rf, None, "/ws"), []) + with redirect_stderr(io.StringIO()): + self.assertEqual(reporter.load_baseline_objects(rf, "missing", "/ws"), []) + + def test_missing_baseline_object_is_a_hard_error(self): + manifest = self.root / "objects.txt" + manifest.write_text("bazel-out/bin/gone.a\n", encoding="utf-8") + rf = _FakeRunfiles({"main/objects.txt": str(manifest), "_main/bazel-out/bin/gone.a": str(self.root / "gone")}) + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + reporter.load_baseline_objects(rf, "main/objects.txt", "/ws") + + +class RunCommandTest(unittest.TestCase): + def test_separate_stderr_keeps_stdout_clean(self): + err = io.StringIO() + with redirect_stderr(err): + result = reporter.run_command( + [sys.executable, "-c", "import sys; print('data'); print('warn', file=sys.stderr)"], + separate_stderr=True, + ) + self.assertEqual(result.stdout, "data\n") + self.assertIn("warn", err.getvalue()) + + def test_failure_exits(self): + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + reporter.run_command([sys.executable, "-c", "import sys; sys.exit(4)"]) + + +class LlvmCovInvocationsTest(unittest.TestCase): + """The exact llvm-cov command lines, checked through the fake tool's log.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + self.cov = _fake_llvm_cov(self.root / "llvm-cov") + self.log = self.root / "log.txt" + self.env = mock.patch.dict(os.environ, {"FAKE_LLVM_COV_LOG": str(self.log)}) + self.env.start() + + def tearDown(self): + self.env.stop() + self.tmp.cleanup() + + def _last(self): + return self.log.read_text(encoding="utf-8").splitlines()[-1].split(" ") + + def test_get_covered_files_normalises_paths(self): + with redirect_stderr(io.StringIO()): + files = reporter.get_covered_files(self.cov, ["/o/a.a", "/o/b.a"], None, "/ws/") + self.assertEqual(files, {"src/a.cpp", "src/b.cpp", "rust/lib.rs"}) + argv = self._last() + self.assertEqual(argv[:3], ["report", "--path-equivalence=/proc/self/cwd/,/ws/", "--empty-profile"]) + self.assertEqual(argv[3:], ["/o/a.a", "--object", "/o/b.a"]) + + def test_show_html_flags(self): + out = self.root / "html" + with redirect_stderr(io.StringIO()): + reporter.run_llvm_cov_show( + self.cov, ["/o/a.a"], "/p/merged.profdata", ["src/x\\.cpp$"], "/ws", "html", out, cxxfilt="/bin/cxxfilt" + ) + argv = self._last() + for flag in [ + "show", + "--format=html", + "--compilation-dir=/ws", + "--show-branches=count", + "--Xdemangler=/bin/cxxfilt", + "--ignore-filename-regex=src/x\\.cpp$", + f"--output-dir={out}", + "--coverage-watermark=100,50", + "--show-expansions", + "--instr-profile", + "/p/merged.profdata", + ]: + self.assertIn(flag, argv) + self.assertTrue((out / "index.html").is_file()) + + def test_export_separates_stderr(self): + err = io.StringIO() + with redirect_stderr(err): + result = reporter.run_llvm_cov_export(self.cov, ["/o/a.a"], "/p/m.profdata", [], "/ws") + self.assertTrue(result.stdout.startswith("SF:")) + self.assertNotIn("warning", result.stdout) + self.assertIn("warning: something cosmetic", err.getvalue()) + self.assertIn("--format=lcov", self._last()) + + def test_report_flags(self): + with redirect_stderr(io.StringIO()): + reporter.run_llvm_cov_report(self.cov, ["/o/a.a"], None, ["r$"], "/ws") + argv = self._last() + self.assertEqual(argv[0], "report") + for flag in [ + "--show-region-summary=0", + "--show-branch-summary=1", + "--ignore-filename-regex=r$", + "--empty-profile", + ]: + self.assertIn(flag, argv) + + +class ReporterMainTest(unittest.TestCase): + """main() end to end with fake llvm tools and two per-test reports.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + self.cov = _fake_llvm_cov(self.root / "llvm-cov") + self.profdata = _fake_llvm_profdata(self.root / "llvm-profdata") + self.log = self.root / "log.txt" + self.obj = self.root / "test_bin" + self.obj.write_bytes(b"\x7fELF") + reports = [] + for i in range(2): + z = self.root / f"t{i}.zip" + with zipfile.ZipFile(z, "w") as zf: + zf.writestr("meta/meta.json", json.dumps({"object_files": [str(self.obj)]})) + zf.writestr("profdata/target.profdata", f"P{i}".encode()) + reports.append(str(z)) + self.reports_file = self.root / "reports.txt" + self.reports_file.write_text("\n".join(reports) + "\n", encoding="utf-8") + self.allowlist = self.root / "allow.txt" + self.allowlist.write_text("src/a.cpp\nsrc/b.cpp\n", encoding="utf-8") + self.output = self.root / "out.zip" + self.workdir = self.root / "work" + self.workdir.mkdir() + self.cwd = os.getcwd() + os.chdir(self.workdir) + self.env = mock.patch.dict(os.environ, {"FAKE_LLVM_COV_LOG": str(self.log)}) + self.env.start() + + def tearDown(self): + self.env.stop() + os.chdir(self.cwd) + self.tmp.cleanup() + + def _argv(self, **extra): + argv = [ + "--output_file", + str(self.output), + "--reports_file", + str(self.reports_file), + "--workspace_root", + "/ws", + "--llvm_cov", + str(self.cov), + "--llvm_profdata", + str(self.profdata), + "--coverage_allowlist", + str(self.allowlist), + ] + for k, v in extra.items(): + argv += [f"--{k}", str(v)] + return argv + + def test_full_report(self): + err = io.StringIO() + with redirect_stderr(err): + reporter.main(self._argv()) + with zipfile.ZipFile(self.output) as zf: + names = set(zf.namelist()) + lcov = zf.read("lcov_report/lcov.dat").decode() + summary = zf.read("text_report/summary.txt").decode() + page = zf.read("html_report/coverage/ws/src/a.cpp.html").decode() + self.assertIn("html_report/index.html", names) + self.assertIn("html_report/style.css", names) + # SF paths are made workspace-relative, llvm-cov's stderr warning is not in the data. + self.assertIn("SF:src/a.cpp\n", lcov) + self.assertNotIn("warning", lcov) + self.assertIn("TOTAL", summary) + # HTML title rewritten to a relative path. + self.assertIn("
src/a.cpp
", page) + # The merged profdata combined both per-test inputs. + self.assertEqual((self.workdir / "merged_coverage.profdata").read_bytes(), b"P0P1") + # rust/lib.rs is not in the allowlist and must be excluded from the report. + log = self.log.read_text(encoding="utf-8") + self.assertIn("--ignore-filename-regex=rust/lib\\.rs$", log) + self.assertIn("Using coverage allowlist with 2 source files", err.getvalue()) + + def test_no_reports_writes_empty_zip(self): + self.reports_file.write_text("", encoding="utf-8") + with redirect_stderr(io.StringIO()): + reporter.main(self._argv()) + with zipfile.ZipFile(self.output) as zf: + self.assertEqual(zf.namelist(), []) + + def test_invalid_reports_write_empty_zip(self): + (self.root / "t0.zip").write_text("junk", encoding="utf-8") + (self.root / "t1.zip").write_text("junk", encoding="utf-8") + with redirect_stderr(io.StringIO()): + reporter.main(self._argv()) + with zipfile.ZipFile(self.output) as zf: + self.assertEqual(zf.namelist(), []) + + def test_missing_llvm_tools_is_an_error(self): + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit) as ctx: + reporter.main(self._argv(llvm_cov=str(self.root / "nope"))) + self.assertEqual(ctx.exception.code, 1) + + def test_empty_allowlist_is_an_error(self): + self.allowlist.write_text("# nothing\n", encoding="utf-8") + with redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + reporter.main(self._argv()) + + if __name__ == "__main__": unittest.main() From cf107bb58c7cdb63c1f25145d8f5efcc767fe276 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:32:26 +0300 Subject: [PATCH 06/17] Test the gcovr (QNX) backend of effective_coverage against real markup The gcovr backend post-processes the HTML that communication's lcov_to_html.py produces through gcovr 8.6 for QNX C++ coverage. It had no tests. The fixtures in effective_coverage_test reproduce the exact markup of a real gcovr 8.6 --html-details report generated from the integration workspace's LCOV (index summary rows, Directory cell, Box-header, source rows with linebranch details). 21 new cases cover format detection, page discovery, path extraction, totals parsing, line/branch/stale processing, in-place restyling, CSS and banner injection, and main() end to end with and without --lcov. Three defects found and fixed: - _parse_gcovr_index_totals read gcovr's "Exec / Excl / Total" triple "20 / 0 / 34" as 20 of 0, so without --lcov every percentage collapsed to 0. The summary rows are now parsed by label; the old two-number heuristics remain as a fallback for other gcovr versions. - _main_gcovr never wrote summary.txt (the llvm-cov path did), which generate_coverage_html requires, and crashed when the output directory did not exist. Both backends now share _write_outputs. - The unused "print JSON to stdout when --output is missing" branch is gone; --output is mandatory. Self coverage: 95.6% lines / 87.1% branches (effective_coverage.py from 68.5% to 95.2%). CI ratchet raised to 95/87. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/workflows/tests.yml | 2 +- score_coverage/effective_coverage.py | 92 +++-- .../tests/effective_coverage_test.py | 336 ++++++++++++++++++ 3 files changed, 399 insertions(+), 31 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 67b17a6..5f6cfbb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,7 +53,7 @@ jobs: - name: "Gate structural coverage (ratchet, raise as tests land; target 100/100)" run: | bazel run --lockfile_mode=error //tools:self_coverage_gate -- \ - --min-lines 86 --min-branches 79 --summary-md "$GITHUB_STEP_SUMMARY" + --min-lines 95 --min-branches 87 --summary-md "$GITHUB_STEP_SUMMARY" - name: Ensure the lockfile is up to date run: | bazel mod deps --lockfile_mode=update diff --git a/score_coverage/effective_coverage.py b/score_coverage/effective_coverage.py index ad93803..66e17b7 100644 --- a/score_coverage/effective_coverage.py +++ b/score_coverage/effective_coverage.py @@ -137,22 +137,7 @@ def _main_llvm_cov(args: argparse.Namespace, html_dir: Path, justified_files: Di # Update the index page with effective coverage info and per-file stats update_index_page(html_dir, stats, per_file_stats) - # Write output report - report = { - "version": 1, - "summary": stats, - "applied_justifications": applied_justifications, - "stale_justifications": stale_justifications, - } - - output_path = Path(args.output) - output_path.parent.mkdir(parents=True, exist_ok=True) - with open(output_path, "w", encoding="utf-8") as f: - json.dump(report, f, indent=2) - - # Write human-readable summary - summary_path = output_path.parent / "summary.txt" - write_summary(summary_path, stats, stale_justifications) + _write_outputs(Path(args.output), stats, applied_justifications, stale_justifications) # Print summary print( @@ -691,6 +676,29 @@ def _same_file(source_path: str, justified_path: str) -> bool: return False +def _write_outputs( + output_path: Path, + stats: Dict[str, Any], + applied: List[Dict[str, Any]], + stale: List[Dict[str, Any]], +) -> None: + """Write report.json and the human-readable summary.txt next to it. + + Both HTML backends produce exactly these two files; generate_coverage_html + reads the effective percentage from report.json and displays summary.txt. + """ + report = { + "version": 1, + "summary": stats, + "applied_justifications": applied, + "stale_justifications": stale, + } + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w", encoding="utf-8") as f: + json.dump(report, f, indent=2) + write_summary(output_path.parent / "summary.txt", stats, stale) + + def write_summary(path: Path, stats: Dict[str, Any], stale: List[Dict[str, Any]]) -> None: """Write human-readable summary.""" with open(path, "w", encoding="utf-8") as f: @@ -878,20 +886,20 @@ def _main_gcovr(args: argparse.Namespace, html_dir: Path, justified_files: Dict) # Update the gcovr index page with effective coverage banner _update_gcovr_index_page(html_dir, stats) - # Write output report - report = { - "version": 1, - "summary": stats, - "applied_justifications": applied_justifications, - "stale_justifications": stale_justifications, - } - - if args.output: - with open(args.output, "w", encoding="utf-8") as f: - json.dump(report, f, indent=2) - print(f"Effective coverage report written to: {args.output}") - else: - print(json.dumps(report, indent=2)) + _write_outputs(Path(args.output), stats, applied_justifications, stale_justifications) + print( + f"INFO: Effective line coverage: {stats['effective_line_coverage_pct']}% " + f"(raw: {stats['raw_line_coverage_pct']}%, " + f"justified: {stats['justified_lines']} lines, " + f"unjustified uncovered: {stats['unjustified_uncovered_lines']} lines)", + file=sys.stderr, + ) + if stale_justifications: + print( + f"WARNING: {len(stale_justifications)} stale justifications " + f"(lines are actually covered, justification can be removed)", + file=sys.stderr, + ) def _parse_gcovr_index_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]: @@ -910,6 +918,13 @@ def _parse_gcovr_index_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]: content = index_file.read_text(encoding="utf-8", errors="replace") + # gcovr >= 6 renders the summary as rows "Lines: | 58.8% | 20 / 0 / 34" + # (Exec / Excl / Total). Read those rows first; the older two-number + # heuristics below stay as a fallback for other gcovr versions. + row_totals = _parse_gcovr_summary_rows(content) + if row_totals is not None: + return row_totals + # gcovr summary table has rows with line/branch stats. # Look for the summary div with class "summary" containing coverage percentages. # Pattern: "N / M" in table cells for covered/total @@ -949,6 +964,23 @@ def _parse_gcovr_index_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]: } +_GCOVR_SUMMARY_ROW_RE = ( + r'{label}:\s*]*>[^<]*\s*]*>\s*(\d+)\s*/\s*(\d+)\s*/\s*(\d+)\s*' +) + + +def _parse_gcovr_summary_rows(content: str) -> Optional[Dict[str, Tuple[int, int]]]: + """Parse gcovr's "Exec / Excl / Total" summary rows; None when absent.""" + lines = re.search(_GCOVR_SUMMARY_ROW_RE.format(label="Lines"), content) + if not lines: + return None + branches = re.search(_GCOVR_SUMMARY_ROW_RE.format(label="Branches"), content) + return { + "lines": (int(lines.group(1)), int(lines.group(3))), + "branches": (int(branches.group(1)), int(branches.group(3))) if branches else (0, 0), + } + + def _find_gcovr_source_files(html_dir: Path) -> List[Path]: """Find all per-source HTML files in a gcovr report. diff --git a/score_coverage/tests/effective_coverage_test.py b/score_coverage/tests/effective_coverage_test.py index 9979c05..8bad710 100644 --- a/score_coverage/tests/effective_coverage_test.py +++ b/score_coverage/tests/effective_coverage_test.py @@ -434,5 +434,341 @@ def test_parse_lcov_totals(self): self.assertEqual(totals, {"lines": (9, 15), "branches": (2, 6)}) +# --------------------------------------------------------------------------- +# gcovr backend (QNX path). The fixtures reproduce the markup gcovr 8.6 emits +# for lcov_to_html.py's --html-details output, taken from a real report of the +# integration workspace. +# --------------------------------------------------------------------------- + + +def _gcovr_index(lines=(20, 0, 34), functions=(4, 0, 7), branches=(6, 0, 10), with_summary=True) -> str: + def row(label, triple): + e, x, t = triple + pct = f"{100.0 * e / t:.1f}%" if t else "-%" + return ( + f' \n {label}:\n' + f' {pct}\n' + f' {e} / {x} / {t}\n \n' + ) + + summary = "" + if with_summary: + summary = ( + '
\n
\n \n' + ' \n
Directory:./
\n
\n
\n' + ' \n \n \n \n' + ' \n \n' + + row("Lines", lines) + + row("Functions", functions) + + row("Branches", branches) + + "
CoverageExec / Excl / Total
\n
\n
\n" + ) + return ( + "\n\n\n
\n" + summary + "
\n" + '
\n
\n' + '
\n' + ' 75.0%\n 12 / 0 / 16\n
\n
\n
\n' + "\n\n" + ) + + +def _gcovr_row(line: int, status: str, count: str, code: str, branches=None) -> str: + """One gcovr source row. ``branches`` is a list of (taken: bool) per branch direction.""" + if branches: + taken = sum(1 for b in branches if b) + details = ( + '
\n' + f' {taken}/{len(branches)}\n' + '
\n' + ) + for i, ok in enumerate(branches): + if ok: + details += ( + f'
✓ Branch 0 → {i} taken 1 time.
\n' + ) + else: + details += ( + f'
✗ Branch 0 → {i} not taken.
\n' + ) + details += "
\n
\n" + else: + details = "" + return ( + ' \n' + f' {line}\n' + f' \n{details} \n' + f' {count}\n' + f' {code}\n' + " \n" + ) + + +def _gcovr_source_page(path: str, rows: str, directory: str = "./") -> str: + return ( + "\n\nGCC Code Coverage Report\n\n" + '\n \n \n' + f" \n \n
Directory:{directory}
\n" + '
\n
\n' + '
Function (File:Line)
\n' + "
\n
\n" + '
\n
\n' + f" {path}\n" + '
\n
\n' + ' \n' + rows + "
\n
\n\n\n" + ) + + +COVERABLE_ROWS = ( + _gcovr_row(17, "coveredLine", "2", "const char* classify(int value) {") + + _gcovr_row(18, "coveredLine", "2", "if (value < 0) {", branches=[True, True]) + + _gcovr_row(19, "coveredLine", "1", 'return "negative";') + + _gcovr_row(20, "coveredLine", "1", "}") + + _gcovr_row(21, "coveredLine", "1", "if (value == 0) {", branches=[True, False]) + + _gcovr_row(22, "coveredLine", "1", 'return "zero";') + + _gcovr_row(23, "coveredLine", "1", "}") + + _gcovr_row(24, "uncoveredLine", "✗", 'return "positive"; // COV_JUSTIFIED itest-positive-branch') + + _gcovr_row(25, "coveredLine", "1", "}") +) + + +class GcovrFixtureMixin: + def _make_gcovr_report(self, with_summary=True): + self.tmp = tempfile.TemporaryDirectory() + self.html = Path(self.tmp.name) / "cpp_coverage_qnx" + self.html.mkdir() + (self.html / "index.html").write_text(_gcovr_index(with_summary=with_summary), encoding="utf-8") + (self.html / "index.css").write_text("body { color: black; }\n", encoding="utf-8") + (self.html / "index.js").write_text("// js\n", encoding="utf-8") + (self.html / "index.functions.html").write_text("functions", encoding="utf-8") + self.page = self.html / "index.coverable.cpp.adc3e3ccca5ac003a7efea58a6f0e095.html" + self.page.write_text(_gcovr_source_page("src/coverable.cpp", COVERABLE_ROWS), encoding="utf-8") + self.uncovered = self.html / "index.uncovered.cpp.e4ac329bbb251afd99e0ac248d5dd32f.html" + self.uncovered.write_text( + _gcovr_source_page( + "src/uncovered.cpp", + _gcovr_row(17, "uncoveredLine", "✗", "int never_called(int value) {") + + _gcovr_row(18, "uncoveredLine", "✗", "if (value > 10) {", branches=[False, False]), + ), + encoding="utf-8", + ) + + +class GcovrDetectionAndParsingTest(GcovrFixtureMixin, unittest.TestCase): + def setUp(self): + self._make_gcovr_report() + + def tearDown(self): + self.tmp.cleanup() + + def test_format_detected_by_per_source_naming(self): + self.assertEqual(ec.detect_html_format(self.html), "gcovr") + + def test_source_files_skip_index_and_functions_pages(self): + names = [p.name for p in ec._find_gcovr_source_files(self.html)] + self.assertEqual(names, [self.page.name, self.uncovered.name]) + + def test_source_path_from_directory_and_header(self): + self.assertEqual(ec._extract_gcovr_source_path(self.page), "./src/coverable.cpp") + + def test_source_path_falls_back_to_title(self): + page = self.html / "index.x.cpp.0123.html" + page.write_text( + "x.cpp - GCC Code Coverage Report", encoding="utf-8" + ) + self.assertEqual(ec._extract_gcovr_source_path(page), "x.cpp") + self.assertEqual(ec._extract_gcovr_source_path(self.html / "missing.html"), "") + + def test_index_totals_from_exec_excl_total_rows(self): + totals = ec._parse_gcovr_index_totals(self.html) + self.assertEqual(totals, {"lines": (20, 34), "branches": (6, 10)}) + + def test_index_totals_without_summary_block(self): + (self.html / "index.html").write_text(_gcovr_index(with_summary=False), encoding="utf-8") + totals = ec._parse_gcovr_index_totals(self.html) + # Fallback heuristics see the "12 / 0 / 16" file row: whatever they yield must not be + # a plausible-looking wrong total; the LCOV route (--lcov) is authoritative. + self.assertIn(totals["lines"][1], (0, 16)) + + def test_index_totals_missing_index(self): + (self.html / "index.html").unlink() + self.assertEqual(ec._parse_gcovr_index_totals(self.html), {"lines": (0, 0), "branches": (0, 0)}) + + def test_matching_ignores_leading_dot_slash(self): + result = ec.find_matching_justifications("./src/coverable.cpp", {"src/coverable.cpp": {"24": {"id": "j"}}}) + self.assertEqual(list(result), [24]) + + +class ProcessGcovrFileTest(GcovrFixtureMixin, unittest.TestCase): + def setUp(self): + self._make_gcovr_report() + self.applied = [] + self.stale = [] + + def tearDown(self): + self.tmp.cleanup() + + def _j(self, jid): + return {"id": jid, "category": "other", "reason": "r"} + + def test_uncovered_line_is_justified_and_restyled(self): + before = self.page.read_text(encoding="utf-8") + stats = ec._process_gcovr_file(self.page, {24: self._j("pos")}, self.applied, self.stale) + self.assertEqual(stats, {"justified": 1, "stale": 0, "justified_branches": 0}) + after = self.page.read_text(encoding="utf-8") + self.assertNotEqual(before, after) + self.assertIn('✗', after) + self.assertIn('', after) + self.assertEqual(after.count("uncoveredLine"), 0) + self.assertEqual(self.applied, [{"file": "./src/coverable.cpp", "line": 24, "id": "pos", "category": "other"}]) + + def test_covered_line_without_branch_gap_is_stale(self): + stats = ec._process_gcovr_file(self.page, {19: self._j("stale")}, self.applied, self.stale) + self.assertEqual(stats["stale"], 1) + self.assertEqual(self.stale[0]["line"], 19) + self.assertEqual(self.stale[0]["file"], "./src/coverable.cpp") + + def test_not_taken_branch_makes_justification_branch_only(self): + stats = ec._process_gcovr_file(self.page, {21: self._j("br")}, self.applied, self.stale) + self.assertEqual(stats, {"justified": 0, "stale": 0, "justified_branches": 1}) + self.assertEqual(self.applied[0]["line"], 21) + + def test_fully_taken_branch_line_is_stale(self): + stats = ec._process_gcovr_file(self.page, {18: self._j("x")}, self.applied, self.stale) + self.assertEqual(stats["stale"], 1) + + def test_partial_covered_line_class(self): + page = self.html / "index.p.cpp.0000.html" + page.write_text( + _gcovr_source_page("p.cpp", _gcovr_row(5, "partialCoveredLine", "1", "if (a || b)")), encoding="utf-8" + ) + stats = ec._process_gcovr_file(page, {5: self._j("p")}, self.applied, self.stale) + self.assertEqual(stats["justified_branches"], 1) + + def test_no_justifications_leaves_page_untouched(self): + before = self.page.read_text(encoding="utf-8") + stats = ec._process_gcovr_file(self.page, {}, self.applied, self.stale) + self.assertEqual(stats, {"justified": 0, "stale": 0, "justified_branches": 0}) + self.assertEqual(self.page.read_text(encoding="utf-8"), before) + + def test_unknown_line_is_ignored(self): + stats = ec._process_gcovr_file(self.page, {99: self._j("nope")}, self.applied, self.stale) + self.assertEqual(stats, {"justified": 0, "stale": 0, "justified_branches": 0}) + self.assertEqual((self.applied, self.stale), ([], [])) + + def test_only_the_named_line_is_marked(self): + ec._process_gcovr_file(self.uncovered, {18: self._j("u")}, self.applied, self.stale) + after = self.uncovered.read_text(encoding="utf-8") + self.assertIn('id="l17"', after) + # line 17 stays uncovered, line 18 is justified + row17 = after[after.index('id="l17"') : after.index('id="l18"')] + row18 = after[after.index('id="l18"') :] + self.assertIn("uncoveredLine", row17) + self.assertNotIn("justifiedLine", row17) + self.assertIn("justifiedLine", row18) + + +class GcovrIndexAndCssTest(GcovrFixtureMixin, unittest.TestCase): + def setUp(self): + self._make_gcovr_report() + + def tearDown(self): + self.tmp.cleanup() + + def test_css_injected_into_first_stylesheet(self): + ec._inject_gcovr_justified_css(self.html) + css = (self.html / "index.css").read_text(encoding="utf-8") + self.assertTrue(css.startswith("body { color: black; }\n")) + self.assertIn(".justifiedLine", css) + (self.html / "index.css").unlink() + ec._inject_gcovr_justified_css(self.html) # no stylesheet: no-op + + def test_banner_inserted_before_file_list(self): + stats = { + "effective_line_coverage_pct": 64.7, + "raw_line_coverage_pct": 58.82, + "justified_lines": 2, + "unjustified_uncovered_lines": 12, + "justified_branches": 1, + "effective_branch_coverage_pct": 70.0, + "raw_branch_coverage_pct": 60.0, + } + ec._update_gcovr_index_page(self.html, stats) + content = (self.html / "index.html").read_text(encoding="utf-8") + banner_at = content.index("Effective Line Coverage: 64.7%") + self.assertLess(banner_at, content.index('
Date: Mon, 7 Sep 2026 17:48:59 +0300 Subject: [PATCH 07/17] Add static analysis (ruff, pylint, ty) and Rust analysis-test fixtures Static analysis follows score_tooling 2.x: tools/linters.bzl instantiates the aspect_rules_lint aspects from @score_tooling//third_party/lint (ruff with the python_basics rule set, pylint via score_tooling's binary, ty), wired as `bazel build --config=lint` with fail_on_violation. CI runs it after the tests. docs-as-code's pre-commit script targets score_tooling 1.x tool binaries that no longer exist in 2.x, so the aspect route is used instead, and unlike docs-as-code's continue-on-error job it is blocking. Policy, documented in pyproject.toml: McCabe complexity ceiling 15 (the process limit is LoC-based; ruff's default 10 is a style preference), pylint's design counters disabled in favour of that single complexity measure, module size limit 2000 lines per the process, test modules exempt from docstring/protected-access/TemporaryDirectory rules. Bringing the code to zero findings: - 252 auto-fixable modernisations (typing builtins, X | None, isort). - Four functions split to stay under complexity 15: validate_yaml (40 -> per-field helpers), process_html_file (30 -> status, branch, classify and restyle helpers), coverage_summary.parse_lcov (16 -> _LcovRecord), justify.main (16 -> resolve/scan/write helpers). Behaviour unchanged; all 214 unit cases and the 15 integration checks pass. - Type fixes found by ty: Runfiles.Create() may return None (now an error), RunfilesLike Protocol so tests can pass fakes, Path | None defaults, Optional narrowing in tests, llvm-cov calls with explicit arguments instead of **dict kwargs. The unused workspace_root parameter of load_baseline_objects is gone. The NaN check uses math.isnan. Rust fixtures: score_toolchains_rust 0.10.0 (dev) with the standard Ferrocene toolchain registered in .bazelrc as in persistency; the analysis tests now cover rust_library (CcInfo archive) and rust_binary (CrateInfo sources plus the executable as baseline object). Self coverage after the refactors: 95.6% lines / 87.2% branches. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .bazelrc | 10 + .github/workflows/tests.yml | 2 + MODULE.bazel | 4 +- MODULE.bazel.lock | 171 ++++++++ pyproject.toml | 41 +- score_coverage/coverage_summary.py | 125 +++--- score_coverage/effective_coverage.py | 381 ++++++++---------- score_coverage/generate_coverage_html.py | 41 +- score_coverage/justify.py | 371 +++++++++-------- score_coverage/merger.py | 11 +- score_coverage/reporter.py | 138 +++---- score_coverage/tests/coverage_summary_test.py | 14 + .../tests/effective_coverage_test.py | 60 +-- .../tests/generate_coverage_html_test.py | 48 ++- score_coverage/tests/justify_test.py | 20 +- score_coverage/tests/merger_test.py | 38 +- score_coverage/tests/reporter_test.py | 67 ++- .../tests/starlark/coverage_scope_tests.bzl | 29 ++ score_coverage/tests/starlark/fixtures/BUILD | 16 + score_coverage/tests/starlark/fixtures/lib.rs | 16 + .../tests/starlark/fixtures/main.rs | 16 + tools/linters.bzl | 31 ++ tools/self_coverage_gate.py | 38 +- tools/self_coverage_gate_test.py | 5 + 24 files changed, 1031 insertions(+), 662 deletions(-) create mode 100644 score_coverage/tests/starlark/fixtures/lib.rs create mode 100644 score_coverage/tests/starlark/fixtures/main.rs create mode 100644 tools/linters.bzl diff --git a/.bazelrc b/.bazelrc index 2880dd4..cb3cdaa 100644 --- a/.bazelrc +++ b/.bazelrc @@ -20,5 +20,15 @@ common --repo_env=ANDROID_HOME= test --test_output=errors +# Rust toolchain for the analysis-test fixtures (same registration as the +# consumer repos; the standard Ferrocene toolchain of score_toolchains_rust). +common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu + +# Static analysis of the Python (ruff, pylint, ty) as aspects; findings fail +# the build. Usage: bazel build --config=lint //score_coverage/... //tools/... +build:lint --aspects=//tools:linters.bzl%ruff,//tools:linters.bzl%pylint,//tools:linters.bzl%ty +build:lint --output_groups=+rules_lint_human +build:lint --@aspect_rules_lint//lint:fail_on_violation + # Per-developer overrides (disk cache, etc.). Must stay last: bazelrc is last-wins. try-import %workspace%/user.bazelrc diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f6cfbb..e60d54d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,6 +48,8 @@ jobs: run: bazel build --lockfile_mode=error //... - name: Run unit and Starlark analysis tests run: bazel test --lockfile_mode=error //score_coverage/... //tools/... + - name: Static analysis of the Python (ruff, pylint, ty; findings fail the build) + run: bazel build --lockfile_mode=error --config=lint //score_coverage/... //tools/... - name: Measure structural coverage of the tool itself (coverage.py) run: bazel coverage --lockfile_mode=error --combined_report=lcov //score_coverage/tests:all - name: "Gate structural coverage (ratchet, raise as tests land; target 100/100)" diff --git a/MODULE.bazel b/MODULE.bazel index 8e83bfd..0526e41 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -75,5 +75,7 @@ bazel_dep(name = "score_tooling", version = "2.2.0", dev_dependency = True) bazel_dep(name = "aspect_rules_lint", version = "2.7.1", dev_dependency = True) bazel_dep(name = "buildifier_prebuilt", version = "8.5.1", dev_dependency = True) -# Starlark analysis tests for the coverage_scope rule/aspect. +# Starlark analysis tests for the coverage_scope rule/aspect, incl. Rust fixtures +# built with the standard Ferrocene toolchain (registered in .bazelrc). bazel_dep(name = "rules_testing", version = "0.9.0", dev_dependency = True) +bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 8912c0f..0c1748d 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -404,6 +404,7 @@ "https://bcr.bazel.build/modules/rules_python_gazelle_plugin/1.5.1/source.json": "c52e4d2229fbd92b658bf60a7638e79b96525e8f7ed6c59036b4827cade9e430", "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "d44fec647d0aeb67b9f3b980cf68ba634976f3ae7ccd6c07d790b59b87a4f251", "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/source.json": "37c10335f2361c337c5c1f34ed36d2da70534c23088062b33a8bdaab68aa9dea", + "https://bcr.bazel.build/modules/rules_rust/0.56.0/MODULE.bazel": "3295b00757db397122092322fe1e920be7f5c9fbfb8619138977e820f2cbbbae", "https://bcr.bazel.build/modules/rules_rust/0.67.0/MODULE.bazel": "87c3816c4321352dcfd9e9e26b58e84efc5b21351ae3ef8fb5d0d57bde7237f5", "https://bcr.bazel.build/modules/rules_shell/0.1.2/MODULE.bazel": "66e4ca3ce084b04af0b9ff05ff14cab4e5df7503973818bb91cbc6cda08d32fc", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", @@ -784,6 +785,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.8.5/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python_gazelle_plugin/1.5.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.56.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.67.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.1-score/MODULE.bazel": "dc1c87d74ef6d32190e65c3c8aabfa7e7764e457bf9888312e0313c3c11fdb69", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.2-score/MODULE.bazel": "37be8dee6df19d666c1d4266e1266d82012aa83bd82de38b3100fd7f641d064b", @@ -798,8 +800,12 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/1.18.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/2.1.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_testing/0.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/MODULE.bazel": "14c96e378c08705a46abe0799d6236fe3095c342c34f83f8d1b3f6046ce00651", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/source.json": "1a853ab23455388d550a9aecf3d8b53ec73de50e7fe2914d9269a3c698bf3624", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/MODULE.bazel": "ade2bad4a331b02d9b7e7d9842e8de8c6fded6186486e02c4f7db5cd4b71d34d", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/source.json": "fbcbc738e652b0c68d5d28dd1db09f2e643dc111f5739b2f6af7ec56c2e88043", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/MODULE.bazel": "535296e6cdef55a506c580ca2ce541aa9ddefb354de1a24ab2bd4addc939282b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/source.json": "8bda773be264da16d2a82a03ebb737421dd4a35855f1e9a5d03d9722d84c1df5", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_tooling/2.2.0/MODULE.bazel": "178ba4862246b6ba2bbcd96b7e9e728299b19fb94bcf23d315dc2299aabf7178", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_tooling/2.2.0/source.json": "a76f2d093cff26d5b256ce531bb2f69b6c667c968f99a156327fd194d4f36e61", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.0/MODULE.bazel": "not found", @@ -5579,6 +5585,171 @@ ] } }, + "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_ext": { + "general": { + "bzlTransitiveDigest": "9k8SLwP4ec9xXjFivq0Kn0FradTQhcEA+j/WsLxfVPM=", + "usagesDigest": "ANsCxJ1KIL4fXiEiWcWT5l5bMcnUM7xjtLQrHfOO6G4=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "ferrocene_x86_64_unknown_linux_gnu": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "sha256": "6fd7c7053a80463b2bfd24202de02e16959b18ed185c55b738148e9caac42eff", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "x86_64-unknown-linux-gnu", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "miri_sysroot_sha256": "143260fe3873249160d57b370717005828e129d3b6782448a32d8cc578384fe0", + "miri_sysroot_strip_prefix": "x86_64-unknown-linux-gnu" + } + }, + "ferrocene_aarch64_unknown_linux_gnu": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-linux-gnu.tar.gz", + "sha256": "06ee88a935083068325b69028e9d4e9adc696542cea9b4322642a55dafcae552", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "aarch64-unknown-linux-gnu", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc", + "-Clink-arg=-lgcc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-linux-gnu.tar.gz", + "miri_sysroot_sha256": "4059e74c79147b942eb595c14a5f998ebdb1063764ea756b4f32c21bf5903a16", + "miri_sysroot_strip_prefix": "aarch64-unknown-linux-gnu" + } + }, + "ferrocene_x86_64_pc_nto_qnx800": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-pc-nto-qnx800.tar.gz", + "sha256": "655ad0d212baf63f4dd03065735ca55cddadc86cf6bd005aeb19689e348a0023", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "x86_64-pc-nto-qnx800", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:qnx" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-pc-nto-qnx800.tar.gz", + "miri_sysroot_sha256": "9684ea089c883a0739f402165fc6aa374a69641e763957c314688779e8124931", + "miri_sysroot_strip_prefix": "x86_64-pc-nto-qnx800" + } + }, + "ferrocene_aarch64_unknown_nto_qnx800": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_repo", + "attributes": { + "url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/ferrocene-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-nto-qnx800.tar.gz", + "sha256": "a8e80da21c6abebfb31063f3815cd3a4bbb5a1466855a12043d7c243ef152715", + "strip_prefix": "", + "toolchain_name": "rust_ferrocene", + "target_triple": "aarch64-unknown-nto-qnx800", + "exec_triple": "x86_64-unknown-linux-gnu", + "staticlib_ext": ".a", + "dylib_ext": ".so", + "binary_ext": "", + "default_edition": "2021", + "stdlib_linkflags": [], + "extra_rustc_flags": [ + "-Clink-arg=-Wl,--no-as-needed", + "-Clink-arg=-lc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc" + ], + "extra_exec_rustc_flags": [], + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:qnx" + ], + "coverage_tools_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/coverage-tools-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-x86_64-unknown-linux-gnu.tar.gz", + "coverage_tools_sha256": "9cf5d76b2e505bf2a8b2b47c60f191ac1273f87851ed387e53080b8e5d14dedf", + "coverage_tools_strip_prefix": "779fbed05ae9e9fe2a04137929d99cc9b3d516fd/x86_64-unknown-linux-gnu", + "miri_sysroot_url": "https://github.com/eclipse-score/ferrocene_toolchain_builder/releases/download/1.3.1/miri-sysroot-779fbed05ae9e9fe2a04137929d99cc9b3d516fd-aarch64-unknown-nto-qnx800.tar.gz", + "miri_sysroot_sha256": "3077170b7384d6bcf2cf8f53db8b670d48fc7e2237285b4fd799c64e7412bdff", + "miri_sysroot_strip_prefix": "aarch64-unknown-nto-qnx800" + } + } + }, + "recordedRepoMappingEntries": [] + } + }, "@@yq.bzl+//yq:extensions.bzl%yq": { "general": { "bzlTransitiveDigest": "61Uz+o5PnlY0jJfPZEUNqsKxnM/UCLeWsn5VVCc8u5Y=", diff --git a/pyproject.toml b/pyproject.toml index b2a265d..4b104cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,15 +13,54 @@ [tool.ruff] line-length = 120 +target-version = "py311" # oldest interpreter the module supports (see MODULE.bazel) +extend-exclude = ["__pycache__", ".*", "bazel-*", "integration_tests"] [tool.ruff.lint] -select = ["E", "F", "W"] +# Rule set of score_tooling's python_basics/pyproject.toml (S-CORE Python guideline). +select = [ + "E", # pycodestyle + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C90", # mccabe complexity + "UP", # pyupgrade + "SIM", # flake8-simplify + "RET", # flake8-return +] + +[tool.ruff.lint.isort] +known-first-party = ["score_coverage"] + +[tool.ruff.lint.mccabe] +# The S-CORE complexity requirement (gd_req__impl_complexity_analysis) is +# LoC-based: 100 lines per function for safety-related code. McCabe 15 is this +# module's additional ceiling; ruff's default of 10 is the python_basics style +# preference and would force splitting straightforward validation code. +max-complexity = 15 + +[tool.ty.environment] +python-version = "3.11" [tool.pylint.format] max-line-length = 120 +# S-CORE process limit for safety-related code (gd_req__impl_complexity_analysis). +max-module-lines = 2000 [tool.pylint."messages control"] disable = [ + # Size/complexity is governed by ruff's C901 (max 15, see [tool.ruff.lint.mccabe]) + # and the process LoC limits; pylint's design counters would add a second, + # unrelated set of thresholds for the same property. + "too-many-locals", + "too-many-branches", + "too-many-statements", + "too-many-nested-blocks", + "too-many-arguments", + "too-many-positional-arguments", + # Small dataclasses and test fakes legitimately have one or no public method. + "too-few-public-methods", # aspect_rules_lint's pylint aspect action (see # @aspect_rules_lint//lint:pylint.bzl) only ever passes a target's own # `srcs` to pylint -- it never forwards `deps` or sets up PYTHONPATH. diff --git a/score_coverage/coverage_summary.py b/score_coverage/coverage_summary.py index 79602ef..fc4d435 100644 --- a/score_coverage/coverage_summary.py +++ b/score_coverage/coverage_summary.py @@ -31,7 +31,6 @@ import json import sys from pathlib import Path -from typing import Dict, List, Optional BAR_WIDTH = 10 LEAST_COVERED_LIMIT = 15 @@ -45,26 +44,28 @@ def __init__(self, path: str) -> None: self.lines_found = 0 self.lines_hit = 0 # None means "no branch data in the record" (rendered as an em dash). - self.branches_found: Optional[int] = None - self.branches_hit: Optional[int] = None + self.branches_found: int | None = None + self.branches_hit: int | None = None @property - def line_pct(self) -> Optional[float]: + def line_pct(self) -> float | None: + """Line coverage percentage of this file, or None without instrumented lines.""" return percent(self.lines_hit, self.lines_found) -def percent(hit: int, total: int) -> Optional[float]: +def percent(hit: int, total: int) -> float | None: """Percentage, or None when the denominator is zero.""" if total <= 0: return None return 100.0 * hit / total -def fmt_pct(pct: Optional[float]) -> str: +def fmt_pct(pct: float | None) -> str: + """Render a percentage for a table cell (n/a for None).""" return "—" if pct is None else f"{pct:.2f}%" -def progress_bar(pct: Optional[float], width: int = BAR_WIDTH) -> str: +def progress_bar(pct: float | None, width: int = BAR_WIDTH) -> str: """Render a COVERAGE percentage as an inline-code bar, e.g. `███████░░░`. Purely a visual aid next to the numeric cell so table rows can be @@ -83,7 +84,48 @@ def escape_cell(text: str) -> str: return text.replace("|", "\\|") -def parse_lcov(path: Path) -> Optional[List[FileCoverage]]: +class _LcovRecord: + """Accumulates one ``SF:`` record. BRF/BRH sums win over counted BRDA entries.""" + + def __init__(self, path: str) -> None: + self.file = FileCoverage(path) + self.brf = 0 + self.brh = 0 + self.brda_total = 0 + self.brda_hit = 0 + self.saw_brf = False + + def feed(self, line: str) -> None: + """Consume one LCOV line belonging to this record.""" + if line.startswith("LF:"): + self.file.lines_found += _int_suffix(line) + elif line.startswith("LH:"): + self.file.lines_hit += _int_suffix(line) + elif line.startswith("BRF:"): + self.saw_brf = True + self.brf += _int_suffix(line) + elif line.startswith("BRH:"): + self.saw_brf = True + self.brh += _int_suffix(line) + elif line.startswith("BRDA:"): + # BRDA:,,, — "-" means never + # evaluated, any positive count means taken. + self.brda_total += 1 + if line.rsplit(",", 1)[-1] not in ("-", "0"): + self.brda_hit += 1 + + def finish(self) -> FileCoverage: + """Apply the branch counters and return the completed record.""" + if self.saw_brf: + self.file.branches_found = self.brf + self.file.branches_hit = self.brh + elif self.brda_total > 0: + self.file.branches_found = self.brda_total + self.file.branches_hit = self.brda_hit + return self.file + + +def parse_lcov(path: Path) -> list[FileCoverage] | None: """Parse an LCOV trace into per-file counters. Returns None when the file does not exist; an empty list when it exists @@ -93,56 +135,25 @@ def parse_lcov(path: Path) -> Optional[List[FileCoverage]]: if not path.is_file(): return None - files: List[FileCoverage] = [] - current: Optional[FileCoverage] = None - brf = brh = 0 - brda_total = brda_hit = 0 - saw_brf = False - - def flush() -> None: - nonlocal current, brf, brh, brda_total, brda_hit, saw_brf - if current is not None: - if saw_brf: - current.branches_found = brf - current.branches_hit = brh - elif brda_total > 0: - current.branches_found = brda_total - current.branches_hit = brda_hit - files.append(current) - current = None - brf = brh = 0 - brda_total = brda_hit = 0 - saw_brf = False - + files: list[FileCoverage] = [] + current: _LcovRecord | None = None # errors="replace" keeps non-UTF8 bytes in paths from crashing the parse. with open(path, encoding="utf-8", errors="replace") as f: for raw_line in f: line = raw_line.strip() if line.startswith("SF:"): - flush() - current = FileCoverage(line[3:]) + if current is not None: + files.append(current.finish()) + current = _LcovRecord(line[3:]) elif current is None: continue - elif line.startswith("LF:"): - current.lines_found += _int_suffix(line) - elif line.startswith("LH:"): - current.lines_hit += _int_suffix(line) - elif line.startswith("BRF:"): - saw_brf = True - brf += _int_suffix(line) - elif line.startswith("BRH:"): - saw_brf = True - brh += _int_suffix(line) - elif line.startswith("BRDA:"): - # BRDA:,,, — "-" means never - # evaluated, any positive count means taken. - brda_total += 1 - taken = line.rsplit(",", 1)[-1] - if taken not in ("-", "0"): - brda_hit += 1 elif line == "end_of_record": - flush() - flush() + files.append(current.finish()) + current = None + else: + current.feed(line) + if current is not None: + files.append(current.finish()) return files @@ -153,7 +164,7 @@ def _int_suffix(line: str) -> int: return 0 -def load_justification_summary(path: Path) -> Optional[Dict]: +def load_justification_summary(path: Path) -> dict | None: """Load the summary block of effective_coverage.py's report.json.""" try: with open(path, encoding="utf-8") as f: @@ -180,8 +191,9 @@ def directory_key(path: str) -> str: return "/".join(parts[:2]) -def rollup_by_directory(files: List[FileCoverage]) -> List[Dict]: - groups: Dict[str, Dict] = {} +def rollup_by_directory(files: list[FileCoverage]) -> list[dict]: + """Aggregate per-file counters into one row per top-level directory.""" + groups: dict[str, dict] = {} for fc in files: g = groups.setdefault( directory_key(fc.path), @@ -203,8 +215,9 @@ def rollup_by_directory(files: List[FileCoverage]) -> List[Dict]: return rows -def render_markdown(files: List[FileCoverage], justification: Optional[Dict]) -> str: - out: List[str] = ["## Coverage summary", ""] +def render_markdown(files: list[FileCoverage], justification: dict | None) -> str: + """Render the full markdown summary (totals, raw vs effective, per-directory rollup, 0% files).""" + out: list[str] = ["## Coverage summary", ""] if not files: out.append("_No coverage records found in the LCOV report._") @@ -305,7 +318,7 @@ def render_markdown(files: List[FileCoverage], justification: Optional[Dict]) -> return "\n".join(out) -def main(argv: Optional[List[str]] = None) -> None: +def main(argv: list[str] | None = None) -> None: """Entry point. ``argv`` defaults to ``sys.argv[1:]``.""" parser = argparse.ArgumentParser(description="Markdown coverage summary from LCOV") parser.add_argument("--lcov", type=Path, required=True) diff --git a/score_coverage/effective_coverage.py b/score_coverage/effective_coverage.py index 66e17b7..0871704 100644 --- a/score_coverage/effective_coverage.py +++ b/score_coverage/effective_coverage.py @@ -28,12 +28,10 @@ import argparse import json import math -import os import re import sys from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple - +from typing import Any # Pattern to match a table row in llvm-cov HTML source pages # Format: ......... @@ -47,7 +45,7 @@ def floor_two_decimals(value: float) -> float: return math.floor(value * 100.0) / 100.0 -def main(argv: Optional[List[str]] = None) -> None: +def main(argv: list[str] | None = None) -> None: """Main entry point. ``argv`` defaults to ``sys.argv[1:]``.""" args = parse_args(argv) @@ -70,7 +68,7 @@ def main(argv: Optional[List[str]] = None) -> None: _main_llvm_cov(args, html_dir, justified_files) -def _main_llvm_cov(args: argparse.Namespace, html_dir: Path, justified_files: Dict) -> None: +def _main_llvm_cov(args: argparse.Namespace, html_dir: Path, justified_files: dict) -> None: """Main logic for llvm-cov HTML format.""" # Parse raw coverage totals from the index page (matches llvm-cov exactly). @@ -82,10 +80,10 @@ def _main_llvm_cov(args: argparse.Namespace, html_dir: Path, justified_files: Di total_justified = 0 total_stale = 0 total_justified_branches = 0 - applied_justifications: List[Dict[str, Any]] = [] - stale_justifications: List[Dict[str, Any]] = [] + applied_justifications: list[dict[str, Any]] = [] + stale_justifications: list[dict[str, Any]] = [] # Track per-file justification counts for index page updates - per_file_stats: Dict[str, Dict[str, int]] = {} + per_file_stats: dict[str, dict[str, int]] = {} source_html_files = find_source_html_files(html_dir) for html_file in source_html_files: @@ -162,87 +160,81 @@ def _main_llvm_cov(args: argparse.Namespace, html_dir: Path, justified_files: Di ) -def process_html_file( - html_file: Path, - justifications: Dict[int, Dict[str, str]], - applied_justifications: List[Dict[str, Any]], - stale_justifications: List[Dict[str, Any]], -) -> Dict[str, int]: - """Process a single source HTML file. Modifies it in-place. - - Restyles justified lines: changes the count cell to show "J" with justified-line - class, and changes red code regions to justified (orange) background. - Also restyles uncovered branches on justified lines. - Only counts justified/stale lines for the justification report — raw coverage - numbers are taken from the index page to match llvm-cov exactly. - """ - file_stats = { - "justified": 0, - "stale": 0, - "justified_branches": 0, - } - - with open(html_file, "r", encoding="utf-8") as f: - content = f.read() +_ROW_STATUS_RE = re.compile( + r"
\d+
" + r"" +) +# Full row, captured for rewriting: +# ...
0
+#
...
... +_FULL_ROW_RE = re.compile( + r"(
\d+
)" + r"(
)\d+(
)" + r"(
)(.*?)(
)" +) +# Branch line in expansion view: +# Branch (195:17): +# [True: 0, ...] +_BRANCH_RE = re.compile( + r"(Branch \(" + r"(\d+:\d+)\):\s*\[)(.*?\])" +) +_DIRECTIONS = ("True", "False") - if not justifications: - return file_stats - # Determine effective line status (covered if ANY instantiation covers it) - row_pattern = re.compile( - r"
\d+
" - r"" - ) - line_effective_status: Dict[int, str] = {} - for m in row_pattern.finditer(content): +def _line_statuses(content: str) -> dict[int, str]: + """Effective status per line: covered if ANY instantiation covers it.""" + statuses: dict[int, str] = {} + for m in _ROW_STATUS_RE.finditer(content): line_num = int(m.group(1)) line_class = m.group(2) if line_class == "covered-line": - line_effective_status[line_num] = "covered" - elif line_class == "uncovered-line": - if line_num not in line_effective_status: - line_effective_status[line_num] = "uncovered" - - # Determine which lines have truly uncovered branches (never covered in any instantiation). - # A branch direction is "truly uncovered" if no instantiation covers it. - branch_check_pattern = re.compile( - r"Branch \(" - r"(\d+:\d+)\):\s*\[(.*?)\]" - ) - covered_branch_dirs_check: Dict[str, set] = {} # branch_id → set of covered directions - uncovered_branch_dirs_check: Dict[str, set] = {} # branch_id → set of uncovered directions - branch_line_map: Dict[str, int] = {} # branch_id → line_num - - for m in branch_check_pattern.finditer(content): - line_num = int(m.group(1)) - branch_id = m.group(2) - branch_content = m.group(3) - branch_line_map[branch_id] = line_num - if branch_id not in covered_branch_dirs_check: - covered_branch_dirs_check[branch_id] = set() - uncovered_branch_dirs_check[branch_id] = set() - for direction in ("True", "False"): - if f"class='None'>{direction}" in branch_content: - covered_branch_dirs_check[branch_id].add(direction) - if f"class='red branch'>{direction}" in branch_content: - uncovered_branch_dirs_check[branch_id].add(direction) - - # Lines with truly uncovered branches (uncovered in ALL instantiations) - lines_with_uncovered_branches: set = set() - for branch_id, uncov_dirs in uncovered_branch_dirs_check.items(): - cov_dirs = covered_branch_dirs_check.get(branch_id, set()) - truly_uncovered = uncov_dirs - cov_dirs - if truly_uncovered: - lines_with_uncovered_branches.add(branch_line_map[branch_id]) - - # Determine which justified lines are stale vs applicable. - # A justification is stale only if the line is covered AND has no uncovered branches. + statuses[line_num] = "covered" + elif line_class == "uncovered-line" and line_num not in statuses: + statuses[line_num] = "uncovered" + return statuses + + +def _lines_with_uncovered_branches(content: str) -> set[int]: + """Lines having a branch direction that no instantiation ever covered.""" + covered: dict[str, set] = {} + uncovered: dict[str, set] = {} + branch_line: dict[str, int] = {} + for m in _BRANCH_RE.finditer(content): + branch_id = m.group(3) + branch_line[branch_id] = int(m.group(2)) + covered.setdefault(branch_id, set()) + uncovered.setdefault(branch_id, set()) + for direction in _DIRECTIONS: + if f"class='None'>{direction}" in m.group(4): + covered[branch_id].add(direction) + if f"class='red branch'>{direction}" in m.group(4): + uncovered[branch_id].add(direction) + return {branch_line[bid] for bid, dirs in uncovered.items() if dirs - covered.get(bid, set())} + + +def _classify_justifications( + html_file: Path, + justifications: dict[int, dict[str, str]], + statuses: dict[int, str], + branch_lines: set[int], + applied: list[dict[str, Any]], + stale: list[dict[str, Any]], + file_stats: dict[str, int], +) -> None: + """Sort each justified line into justified / branch-only / stale.""" for line_num, justification in justifications.items(): - status = line_effective_status.get(line_num) - has_uncovered_branches = line_num in lines_with_uncovered_branches + status = statuses.get(line_num) + has_uncovered_branches = line_num in branch_lines + entry = { + "file": html_file.stem, + "line": line_num, + "id": justification.get("id", ""), + "category": justification.get("category", ""), + } if status == "covered" and not has_uncovered_branches: file_stats["stale"] += 1 - stale_justifications.append( + stale.append( { "file": html_file.stem, "line": line_num, @@ -252,34 +244,14 @@ def process_html_file( ) elif status == "uncovered": file_stats["justified"] += 1 - applied_justifications.append( - { - "file": html_file.stem, - "line": line_num, - "id": justification.get("id", ""), - "category": justification.get("category", ""), - } - ) + applied.append(entry) elif status == "covered" and has_uncovered_branches: - # Line is covered but has uncovered branches — justification applies to branches only - applied_justifications.append( - { - "file": html_file.stem, - "line": line_num, - "id": justification.get("id", ""), - "category": justification.get("category", ""), - } - ) + # Covered line with uncovered branches: the justification applies to the branches only. + applied.append(entry) - # Restyle justified lines in the HTML (all occurrences including instantiations). - # Full row pattern to capture and replace the entire row: - # ...
0
...
... - full_row_pattern = re.compile( - r"(
\d+
)" - r"(
)\d+(
)" - r"(
)(.*?)(
)" - ) +def _restyle_rows(content: str, justifications: dict[int, dict[str, str]]) -> tuple[str, bool]: + """Turn the count cell of justified uncovered rows into 'J' and recolor red regions.""" modified = False def replace_full_row(match: re.Match) -> str: @@ -287,93 +259,92 @@ def replace_full_row(match: re.Match) -> str: line_num = int(match.group(2)) if line_num not in justifications: return match.group(0) - justification = justifications[line_num] reason = justification.get("reason", "").replace("'", "'").replace('"', """) - jid = justification.get("id", "") - tooltip = f"Justified [{jid}]: {reason}" + tooltip = f"Justified [{justification.get('id', '')}]: {reason}" modified = True - - # Rebuild the row with justified styling: - # 1. Line number td (unchanged) - line_td = match.group(1) - # 2. Count td: change class and show "J" instead of "0" count_td = f"
J{match.group(4)}"
-        # 3. Code td: replace 'region red' spans with 'region justified'
-        code_start = match.group(5)
         code_content = match.group(6).replace("class='region red'", "class='region justified'")
-        code_end = match.group(7)
+        return match.group(1) + count_td + match.group(5) + code_content + match.group(7)
 
-        return line_td + count_td + code_start + code_content + code_end
+    return _FULL_ROW_RE.sub(replace_full_row, content), modified
 
-    new_content = full_row_pattern.sub(replace_full_row, content)
 
-    # Restyle branches on justified lines.
-    # Branch format in expansion-view:
-    # Branch (195:17):
-    #   [True: 0, ...]
-    # We find branches at justified line numbers and restyle red branch → justified branch
-    # Counting: A branch direction is "uncovered" only if ALL instantiations show it as red.
-    # (Same as llvm-cov's logic: covered if ANY instantiation covers it.)
-    branch_pattern = re.compile(
-        r"(Branch \("
-        r"(\d+:\d+)\):\s*\[)(.*?\])"
-    )
-
-    # First pass: determine which branch directions are covered in any instantiation
-    covered_branch_dirs: set = set()  # (line:col, direction) that are covered somewhere
-    for m in branch_pattern.finditer(new_content):
-        line_num = int(m.group(2))
-        if line_num not in justifications:
+def _restyle_branches(
+    content: str, justifications: dict[int, dict[str, str]], file_stats: dict[str, int]
+) -> tuple[str, bool]:
+    """Restyle red branches on justified lines and count the truly uncovered directions once."""
+    # A direction is covered if ANY instantiation covers it (llvm-cov's own rule).
+    covered_dirs: set = set()
+    for m in _BRANCH_RE.finditer(content):
+        if int(m.group(2)) not in justifications:
             continue
-        branch_id = m.group(3)
-        branch_content = m.group(4)
-        # A direction is covered if it does NOT have 'red branch' class
-        for direction in ("True", "False"):
-            # Check if this direction appears as covered (class='None' means covered)
-            covered_marker = f"class='None'>{direction}"
-            if covered_marker in branch_content:
-                covered_branch_dirs.add((branch_id, direction))
+        for direction in _DIRECTIONS:
+            if f"class='None'>{direction}" in m.group(4):
+                covered_dirs.add((m.group(3), direction))
 
-    # Second pass: restyle and count only truly uncovered branch directions
-    justified_branch_ids: set = set()  # Track unique uncovered (line:col, direction) pairs
+    modified = False
+    counted: set = set()
 
     def replace_branch(match: re.Match) -> str:
         nonlocal modified
         line_num = int(match.group(2))
-        if line_num not in justifications:
-            return match.group(0)
-
         branch_content = match.group(4)
-        if "class='red branch'" not in branch_content:
+        if line_num not in justifications or "class='red branch'" not in branch_content:
             return match.group(0)
-
         modified = True
-        branch_id = match.group(3)  # e.g. "68:13"
-
-        # Count unique uncovered branch directions that are NEVER covered in any instantiation
-        for direction in ("True", "False"):
-            if f"class='red branch'>{direction}" in branch_content:
-                uid = (branch_id, direction)
-                if uid not in covered_branch_dirs and uid not in justified_branch_ids:
-                    justified_branch_ids.add(uid)
-                    file_stats["justified_branches"] += 1
-
-        # Restyle: red branch → justified-branch, uncovered-line → justified-line
+        for direction in _DIRECTIONS:
+            uid = (match.group(3), direction)
+            is_red = f"class='red branch'>{direction}" in branch_content
+            if is_red and uid not in covered_dirs and uid not in counted:
+                counted.add(uid)
+                file_stats["justified_branches"] += 1
         branch_content = branch_content.replace("class='red branch'", "class='justified-branch'")
         branch_content = branch_content.replace("class='uncovered-line'", "class='justified-line'")
         return match.group(1) + branch_content
 
-    new_content = branch_pattern.sub(replace_branch, new_content)
+    return _BRANCH_RE.sub(replace_branch, content), modified
+
 
-    if modified:
+def process_html_file(
+    html_file: Path,
+    justifications: dict[int, dict[str, str]],
+    applied_justifications: list[dict[str, Any]],
+    stale_justifications: list[dict[str, Any]],
+) -> dict[str, int]:
+    """Process a single source HTML file. Modifies it in-place.
+
+    Restyles justified lines: changes the count cell to show "J" with justified-line
+    class, and changes red code regions to justified (orange) background.
+    Also restyles uncovered branches on justified lines.
+    Only counts justified/stale lines for the justification report — raw coverage
+    numbers are taken from the index page to match llvm-cov exactly.
+    """
+    file_stats = {"justified": 0, "stale": 0, "justified_branches": 0}
+
+    with open(html_file, encoding="utf-8") as f:
+        content = f.read()
+
+    if not justifications:
+        return file_stats
+
+    statuses = _line_statuses(content)
+    branch_lines = _lines_with_uncovered_branches(content)
+    _classify_justifications(
+        html_file, justifications, statuses, branch_lines, applied_justifications, stale_justifications, file_stats
+    )
+
+    new_content, rows_modified = _restyle_rows(content, justifications)
+    new_content, branches_modified = _restyle_branches(new_content, justifications, file_stats)
+
+    if rows_modified or branches_modified:
         with open(html_file, "w", encoding="utf-8") as f:
             f.write(new_content)
 
     return file_stats
 
 
-def parse_index_page_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]:
+def parse_index_page_totals(html_dir: Path) -> dict[str, tuple[int, int]]:
     """Parse the TOTALS row from the llvm-cov index.html to get exact coverage numbers.
 
     Returns dict with 'lines' and 'branches' keys, each (covered, total).
@@ -385,7 +356,7 @@ def parse_index_page_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]:
         print(f"WARNING: {index_file} not found; coverage totals default to 0/0", file=sys.stderr)
         return {"lines": (0, 0), "branches": (0, 0)}
 
-    with open(index_file, "r", encoding="utf-8") as f:
+    with open(index_file, encoding="utf-8") as f:
         content = f.read()
 
     pct_pattern = re.compile(r"(\d+\.\d+)%\s*\((\d+)/(\d+)\)")
@@ -449,13 +420,13 @@ def inject_justified_css(html_dir: Path) -> None:
         f.write(justified_css)
 
 
-def update_index_page(html_dir: Path, stats: Dict[str, Any], per_file_stats: Dict[str, Dict[str, int]]) -> None:
+def update_index_page(html_dir: Path, stats: dict[str, Any], per_file_stats: dict[str, dict[str, int]]) -> None:
     """Update the index page with effective coverage info and per-file adjusted percentages."""
     index_file = html_dir / "index.html"
     if not index_file.exists():
         return
 
-    with open(index_file, "r", encoding="utf-8") as f:
+    with open(index_file, encoding="utf-8") as f:
         content = f.read()
 
     # Banner with overall effective coverage (lines + branches)
@@ -558,13 +529,12 @@ def _get_coverage_color(pct: float) -> str:
     """Return the llvm-cov color class for a coverage percentage."""
     if pct >= 100.0:
         return "green"
-    elif pct >= 80.0:
+    if pct >= 80.0:
         return "yellow"
-    else:
-        return "red"
+    return "red"
 
 
-def _update_totals_row(content: str, stats: Dict[str, Any]) -> str:
+def _update_totals_row(content: str, stats: dict[str, Any]) -> str:
     """Update the TOTALS row in the index page with effective coverage numbers."""
     # Find the TOTALS row — it's the last row before 
     totals_idx = content.rfind("Totals")
@@ -611,7 +581,7 @@ def _update_totals_row(content: str, stats: Dict[str, Any]) -> str:
     return content
 
 
-def find_source_html_files(html_dir: Path) -> List[Path]:
+def find_source_html_files(html_dir: Path) -> list[Path]:
     """Find all per-source HTML files (not index.html, style.css, etc.)."""
     coverage_dir = html_dir / "coverage"
     if not coverage_dir.exists():
@@ -643,15 +613,15 @@ def extract_source_path_from_html(html_file: Path, html_dir: Path) -> str:
 
 
 def find_matching_justifications(
-    source_path: str, justified_files: Dict[str, Dict[str, Dict[str, str]]]
-) -> Dict[int, Dict[str, str]]:
+    source_path: str, justified_files: dict[str, dict[str, dict[str, str]]]
+) -> dict[int, dict[str, str]]:
     """Find justifications that match the given source path.
 
     The source_path from HTML may be an absolute path or relative.
     The justified_files keys are relative to source root.
     We match by suffix.
     """
-    result: Dict[int, Dict[str, str]] = {}
+    result: dict[int, dict[str, str]] = {}
 
     for justified_path, line_justifications in justified_files.items():
         if _same_file(source_path, justified_path):
@@ -678,9 +648,9 @@ def _same_file(source_path: str, justified_path: str) -> bool:
 
 def _write_outputs(
     output_path: Path,
-    stats: Dict[str, Any],
-    applied: List[Dict[str, Any]],
-    stale: List[Dict[str, Any]],
+    stats: dict[str, Any],
+    applied: list[dict[str, Any]],
+    stale: list[dict[str, Any]],
 ) -> None:
     """Write report.json and the human-readable summary.txt next to it.
 
@@ -699,7 +669,7 @@ def _write_outputs(
     write_summary(output_path.parent / "summary.txt", stats, stale)
 
 
-def write_summary(path: Path, stats: Dict[str, Any], stale: List[Dict[str, Any]]) -> None:
+def write_summary(path: Path, stats: dict[str, Any], stale: list[dict[str, Any]]) -> None:
     """Write human-readable summary."""
     with open(path, "w", encoding="utf-8") as f:
         f.write("Coverage Justification Summary\n")
@@ -708,17 +678,17 @@ def write_summary(path: Path, stats: Dict[str, Any], stale: List[Dict[str, Any]]
         f.write(f"Covered lines:            {stats['covered_lines']}\n")
         f.write(f"Justified lines:          {stats['justified_lines']}\n")
         f.write(f"Unjustified uncovered:    {stats['unjustified_uncovered_lines']}\n")
-        f.write(f"\n")
+        f.write("\n")
         f.write(f"Raw line coverage:        {stats['raw_line_coverage_pct']}%\n")
         f.write(f"Effective line coverage:  {stats['effective_line_coverage_pct']}%\n")
-        f.write(f"\n")
+        f.write("\n")
         if stats.get("total_branches", 0) > 0:
             f.write(f"Total branches:           {stats['total_branches']}\n")
             f.write(f"Covered branches:         {stats['covered_branches']}\n")
             f.write(f"Justified branches:       {stats['justified_branches']}\n")
             f.write(f"Raw branch coverage:      {stats['raw_branch_coverage_pct']}%\n")
             f.write(f"Effective branch coverage: {stats['effective_branch_coverage_pct']}%\n")
-            f.write(f"\n")
+            f.write("\n")
         if stale:
             f.write(f"Stale justifications ({len(stale)}):\n")
             for s in stale:
@@ -726,16 +696,16 @@ def write_summary(path: Path, stats: Dict[str, Any], stale: List[Dict[str, Any]]
             f.write("\n")
 
 
-def load_manifest(path: Path) -> Dict[str, Any]:
+def load_manifest(path: Path) -> dict[str, Any]:
     """Load the justification manifest JSON."""
     if not path.exists():
         print(f"ERROR: Manifest not found: {path}", file=sys.stderr)
         sys.exit(1)
-    with open(path, "r", encoding="utf-8") as f:
+    with open(path, encoding="utf-8") as f:
         return json.load(f)
 
 
-def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
+def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
     """Parse command-line arguments (``argv`` defaults to ``sys.argv[1:]``)."""
     parser = argparse.ArgumentParser(description="Effective coverage calculator and HTML post-processor")
     parser.add_argument(
@@ -784,7 +754,7 @@ def detect_html_format(html_dir: Path) -> str:
     return "llvm_cov"
 
 
-def _parse_lcov_totals(lcov_path: Path) -> Dict[str, Tuple[int, int]]:
+def _parse_lcov_totals(lcov_path: Path) -> dict[str, tuple[int, int]]:
     """Parse coverage totals from an LCOV data file.
 
     Sums LH/LF (line hit/found) and BRH/BRF (branch hit/found) across all records.
@@ -794,7 +764,7 @@ def _parse_lcov_totals(lcov_path: Path) -> Dict[str, Tuple[int, int]]:
     total_branches = 0
     hit_branches = 0
 
-    with open(lcov_path, "r", encoding="utf-8", errors="replace") as f:
+    with open(lcov_path, encoding="utf-8", errors="replace") as f:
         for line in f:
             line = line.strip()
             if line.startswith("LF:"):
@@ -817,14 +787,11 @@ def _parse_lcov_totals(lcov_path: Path) -> Dict[str, Tuple[int, int]]:
 # =============================================================================
 
 
-def _main_gcovr(args: argparse.Namespace, html_dir: Path, justified_files: Dict) -> None:
+def _main_gcovr(args: argparse.Namespace, html_dir: Path, justified_files: dict) -> None:
     """Main logic for gcovr HTML format (produced by lcov_to_html.py via gcovr)."""
 
     # Parse coverage totals from LCOV file or gcovr index page.
-    if args.lcov and args.lcov.exists():
-        totals = _parse_lcov_totals(args.lcov)
-    else:
-        totals = _parse_gcovr_index_totals(html_dir)
+    totals = _parse_lcov_totals(args.lcov) if args.lcov and args.lcov.exists() else _parse_gcovr_index_totals(html_dir)
 
     raw_covered, raw_total = totals["lines"]
     raw_branch_covered, raw_branch_total = totals["branches"]
@@ -833,9 +800,9 @@ def _main_gcovr(args: argparse.Namespace, html_dir: Path, justified_files: Dict)
     total_justified = 0
     total_stale = 0
     total_justified_branches = 0
-    applied_justifications: List[Dict[str, Any]] = []
-    stale_justifications: List[Dict[str, Any]] = []
-    per_file_stats: Dict[str, Dict[str, int]] = {}
+    applied_justifications: list[dict[str, Any]] = []
+    stale_justifications: list[dict[str, Any]] = []
+    per_file_stats: dict[str, dict[str, int]] = {}
 
     source_html_files = _find_gcovr_source_files(html_dir)
     for html_file in source_html_files:
@@ -902,7 +869,7 @@ def _main_gcovr(args: argparse.Namespace, html_dir: Path, justified_files: Dict)
         )
 
 
-def _parse_gcovr_index_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]:
+def _parse_gcovr_index_totals(html_dir: Path) -> dict[str, tuple[int, int]]:
     """Parse coverage totals from gcovr's index page.
 
     gcovr format shows coverage in the summary header with patterns like:
@@ -969,7 +936,7 @@ def _parse_gcovr_index_totals(html_dir: Path) -> Dict[str, Tuple[int, int]]:
 )
 
 
-def _parse_gcovr_summary_rows(content: str) -> Optional[Dict[str, Tuple[int, int]]]:
+def _parse_gcovr_summary_rows(content: str) -> dict[str, tuple[int, int]] | None:
     """Parse gcovr's "Exec / Excl / Total" summary rows; None when absent."""
     lines = re.search(_GCOVR_SUMMARY_ROW_RE.format(label="Lines"), content)
     if not lines:
@@ -981,7 +948,7 @@ def _parse_gcovr_summary_rows(content: str) -> Optional[Dict[str, Tuple[int, int
     }
 
 
-def _find_gcovr_source_files(html_dir: Path) -> List[Path]:
+def _find_gcovr_source_files(html_dir: Path) -> list[Path]:
     """Find all per-source HTML files in a gcovr report.
 
     gcovr --html-details creates files named:
@@ -1036,10 +1003,10 @@ def _extract_gcovr_source_path(html_file: Path) -> str:
 
 def _process_gcovr_file(
     html_file: Path,
-    justifications: Dict[int, Dict[str, str]],
-    applied_justifications: List[Dict[str, Any]],
-    stale_justifications: List[Dict[str, Any]],
-) -> Dict[str, int]:
+    justifications: dict[int, dict[str, str]],
+    applied_justifications: list[dict[str, Any]],
+    stale_justifications: list[dict[str, Any]],
+) -> dict[str, int]:
     """Process a single gcovr source HTML file. Modifies it in-place.
 
     gcovr line format:
@@ -1061,7 +1028,7 @@ def _process_gcovr_file(
     if not justifications:
         return file_stats
 
-    with open(html_file, "r", encoding="utf-8") as f:
+    with open(html_file, encoding="utf-8") as f:
         content = f.read()
 
     # Parse line coverage status from gcovr HTML.
@@ -1080,7 +1047,7 @@ def _process_gcovr_file(
         re.DOTALL,
     )
 
-    line_effective_status: Dict[int, str] = {}
+    line_effective_status: dict[int, str] = {}
     lines_with_uncovered_branches: set = set()
 
     for m in line_pattern.finditer(content):
@@ -1181,7 +1148,7 @@ def _inject_gcovr_justified_css(html_dir: Path) -> None:
         f.write(justified_css)
 
 
-def _update_gcovr_index_page(html_dir: Path, stats: Dict[str, Any]) -> None:
+def _update_gcovr_index_page(html_dir: Path, stats: dict[str, Any]) -> None:
     """Update the gcovr index page with an effective coverage banner."""
     index_file = html_dir / "index.html"
     if not index_file.exists():
@@ -1189,7 +1156,7 @@ def _update_gcovr_index_page(html_dir: Path, stats: Dict[str, Any]) -> None:
     if not index_file.exists():
         return
 
-    with open(index_file, "r", encoding="utf-8") as f:
+    with open(index_file, encoding="utf-8") as f:
         content = f.read()
 
     branch_info = ""
diff --git a/score_coverage/generate_coverage_html.py b/score_coverage/generate_coverage_html.py
index 0e757cd..5704233 100644
--- a/score_coverage/generate_coverage_html.py
+++ b/score_coverage/generate_coverage_html.py
@@ -51,14 +51,15 @@
 
 import argparse
 import json
+import math
 import os
 import shutil
 import sys
 import tempfile
 import zipfile
+from collections.abc import Sequence
 from dataclasses import dataclass
 from pathlib import Path
-from typing import List, Optional, Sequence
 
 from score_coverage import coverage_summary, effective_coverage, justify
 
@@ -78,16 +79,16 @@ class GenerateError(Exception):
 class Options:
     """Parsed command line."""
 
-    yaml: Optional[str]
-    archive: Optional[str]
-    archive_dir: Optional[str]
+    yaml: str | None
+    archive: str | None
+    archive_dir: str | None
     platform: str
     testlogs_subdir: str
-    summary_md: Optional[str]
-    output_dir: Optional[str]
+    summary_md: str | None
+    output_dir: str | None
 
 
-def parse_args(argv: Optional[Sequence[str]] = None) -> Options:
+def parse_args(argv: Sequence[str] | None = None) -> Options:
     """Parse the command line (``argv`` defaults to ``sys.argv[1:]``)."""
     parser = argparse.ArgumentParser(
         prog="generate_coverage_html",
@@ -131,7 +132,7 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Options:
 # -----------------------------------------------------------------------------
 
 
-def parse_threshold(value: Optional[str]) -> float:
+def parse_threshold(value: str | None) -> float:
     """Return the coverage threshold in percent.
 
     ``None`` or an empty string means the default (100). Anything that is not
@@ -144,7 +145,7 @@ def parse_threshold(value: Optional[str]) -> float:
         threshold = float(value)
     except ValueError as exc:
         raise GenerateError(f"COVERAGE_THRESHOLD must be a number, got {value!r}") from exc
-    if threshold != threshold or not 0.0 <= threshold <= 100.0:  # NaN or out of range
+    if math.isnan(threshold) or not 0.0 <= threshold <= 100.0:
         raise GenerateError(f"COVERAGE_THRESHOLD must be within [0, 100], got {value!r}")
     return threshold
 
@@ -161,7 +162,7 @@ def raw_line_coverage_from_lcov(lcov_path: Path) -> float:
         raise GenerateError(f"lcov_report/lcov.dat not found at {lcov_path}")
     lines_found = 0
     lines_hit = 0
-    with open(lcov_path, "r", encoding="utf-8") as f:
+    with open(lcov_path, encoding="utf-8") as f:
         for line in f:
             line = line.rstrip("\n")
             if line.startswith("LF:"):
@@ -191,7 +192,7 @@ def effective_line_coverage_from_report(report_path: Path) -> float:
     if not report_path.is_file():
         raise GenerateError(f"effective coverage report was not produced: {report_path}")
     try:
-        with open(report_path, "r", encoding="utf-8") as f:
+        with open(report_path, encoding="utf-8") as f:
             report = json.load(f)
         value = report["summary"]["effective_line_coverage_pct"]
     except (json.JSONDecodeError, KeyError, TypeError) as exc:
@@ -263,7 +264,7 @@ def run_justifications(
     return effective_line_coverage_from_report(report)
 
 
-def _call_tool(name: str, entry, argv: List[str]) -> None:
+def _call_tool(name: str, entry, argv: list[str]) -> None:
     try:
         entry(argv)
     except SystemExit as exc:
@@ -274,12 +275,12 @@ def _call_tool(name: str, entry, argv: List[str]) -> None:
 def write_summary(
     workspace: Path,
     lcov: Path,
-    justification_dir: Optional[Path],
-    summary_md: Optional[str],
-    step_summary: Optional[str],
+    justification_dir: Path | None,
+    summary_md: str | None,
+    step_summary: str | None,
 ) -> None:
     """Emit the markdown summary to --summary-md or, failing that, GITHUB_STEP_SUMMARY."""
-    args: List[str] = ["--lcov", str(lcov)]
+    args: list[str] = ["--lcov", str(lcov)]
     if justification_dir is not None and (justification_dir / "report.json").is_file():
         args += ["--justification-report", str(justification_dir / "report.json")]
     if summary_md:
@@ -299,7 +300,7 @@ def assemble_artifacts(
     testlogs_subdir: str,
     output_dir: Path,
     lcov: Path,
-    justification_dir: Optional[Path],
+    justification_dir: Path | None,
 ) -> None:
     """Copy JUnit XMLs (tree preserved), the HTML report, the LCOV and the justification report."""
     dest.mkdir(parents=True, exist_ok=True)
@@ -323,7 +324,7 @@ def assemble_artifacts(
 # -----------------------------------------------------------------------------
 
 
-def run(opts: Options, workspace: Path, environ: Optional[dict] = None) -> int:
+def run(opts: Options, workspace: Path, environ: dict | None = None) -> int:
     """Execute the full flow from ``workspace`` and return the exit code."""
     env = os.environ if environ is None else environ
     threshold = parse_threshold(env.get("COVERAGE_THRESHOLD"))
@@ -340,7 +341,7 @@ def run(opts: Options, workspace: Path, environ: Optional[dict] = None) -> int:
         print(f"Coverage report written to: {output_dir}")
 
         lcov = extract_dir / "lcov_report" / "lcov.dat"
-        justification_dir: Optional[Path] = None
+        justification_dir: Path | None = None
         if opts.yaml:
             justification_dir = extract_dir / "justification_report"
             gate_pct = run_justifications(workspace, opts.yaml, opts.platform, output_dir, justification_dir)
@@ -383,7 +384,7 @@ def run(opts: Options, workspace: Path, environ: Optional[dict] = None) -> int:
     return rc
 
 
-def main(argv: Optional[Sequence[str]] = None) -> int:
+def main(argv: Sequence[str] | None = None) -> int:
     """CLI entry point; returns the process exit code."""
     opts = parse_args(argv)
     workspace_env = os.environ.get("BUILD_WORKSPACE_DIRECTORY")
diff --git a/score_coverage/justify.py b/score_coverage/justify.py
index ddf7d20..39f9014 100644
--- a/score_coverage/justify.py
+++ b/score_coverage/justify.py
@@ -29,11 +29,10 @@
 import re
 import sys
 from pathlib import Path
-from typing import Any, Dict, List, Optional, Set, Tuple
+from typing import Any, cast
 
 import yaml
 
-
 # Marker patterns
 COV_JUSTIFIED_LINE_RE = re.compile(r"COV_JUSTIFIED\s+([\w-]+)")
 COV_JUSTIFIED_START_RE = re.compile(r"COV_JUSTIFIED_START\s+([\w-]+)")
@@ -52,105 +51,113 @@
 }
 
 
-def main(argv: Optional[List[str]] = None) -> None:
+def main(argv: list[str] | None = None) -> None:
     """Main entry point. ``argv`` defaults to ``sys.argv[1:]``."""
     args = parse_args(argv)
 
     justifications_data = load_yaml(args.yaml)
     validate_yaml(justifications_data)
+    justifications_by_id = _justifications_by_id(justifications_data, args.platform)
+
+    resolved, errors = _resolve_yaml_locations(justifications_by_id, Path(args.source_root))
+    warnings = _scan_sources(Path(args.source_root), args.file_filter, justifications_by_id, resolved)
+    _write_manifest(Path(args.output), Path(args.source_root), resolved, warnings, errors)
+
+    # Print diagnostics
+    total_justified_lines = sum(len(lines) for lines in resolved.values())
+    print(
+        f"INFO: Resolved {total_justified_lines} justified lines across {len(resolved)} files.",
+        file=sys.stderr,
+    )
+    for w in warnings:
+        print(f"WARNING: {w}", file=sys.stderr)
+    if errors:
+        for e in errors:
+            print(f"ERROR: {e}", file=sys.stderr)
+        sys.exit(1)
 
-    # Build lookup: id -> justification entry
-    justifications_by_id: Dict[str, Dict[str, Any]] = {}
-    for entry in justifications_data.get("justifications", []):
-        justifications_by_id[entry["id"]] = entry
 
-    # Filter justifications by platform if --platform is specified.
-    if args.platform:
-        justifications_by_id = {
-            jid: entry for jid, entry in justifications_by_id.items() if _matches_platform(entry, args.platform)
-        }
+def _justifications_by_id(data: dict[str, Any], platform: str | None) -> dict[str, dict[str, Any]]:
+    """Index the entries by id, keeping only those that apply to ``platform`` (if given)."""
+    by_id: dict[str, dict[str, Any]] = {entry["id"]: entry for entry in data.get("justifications", [])}
+    if platform:
+        by_id = {jid: entry for jid, entry in by_id.items() if _matches_platform(entry, platform)}
+    return by_id
 
-    # Resolve all justified lines
-    resolved: Dict[str, Dict[int, Dict[str, str]]] = {}
-    warnings: List[str] = []
-    errors: List[str] = []
 
-    # 1. Process YAML direct locations
-    for jid, entry in justifications_by_id.items():
+def _resolve_yaml_locations(
+    justifications_by_id: dict[str, dict[str, Any]], source_root: Path
+) -> tuple[dict[str, dict[int, dict[str, str]]], list[str]]:
+    """Resolve the explicit ``locations`` of the YAML entries to file -> line -> justification."""
+    resolved: dict[str, dict[int, dict[str, str]]] = {}
+    errors: list[str] = []
+    for entry in justifications_by_id.values():
         for location in entry.get("locations", []):
             file_path = location["file"]
-            full_path = Path(args.source_root) / file_path
-
-            if not full_path.exists():
+            if not (source_root / file_path).exists():
                 errors.append(f"File not found for justification '{entry['id']}': {file_path}")
                 continue
-
-            lines = resolve_location_lines(location)
-            if file_path not in resolved:
-                resolved[file_path] = {}
-            for line in lines:
-                resolved[file_path][line] = {
+            lines = resolved.setdefault(file_path, {})
+            for line in resolve_location_lines(location):
+                lines[line] = {
                     "id": entry["id"],
                     "category": entry["category"],
                     "reason": entry["reason"].strip(),
                 }
-
-    # 2. Scan source files for in-code COV_JUSTIFIED markers
-    source_files = collect_source_files(args.source_root, args.file_filter)
-    for source_file in source_files:
-        rel_path = str(source_file.relative_to(args.source_root))
+    return resolved, errors
+
+
+def _scan_sources(
+    source_root: Path,
+    file_filter: str,
+    justifications_by_id: dict[str, dict[str, Any]],
+    resolved: dict[str, dict[int, dict[str, str]]],
+) -> list[str]:
+    """Scan the source tree for COV_JUSTIFIED markers, merging results into ``resolved``."""
+    warnings: list[str] = []
+    for source_file in collect_source_files(source_root, file_filter):
+        rel_path = str(source_file.relative_to(source_root))
         scan_warnings, scan_lines = scan_file_for_markers(source_file, rel_path, justifications_by_id)
         warnings.extend(scan_warnings)
-
         if scan_lines:
-            if rel_path not in resolved:
-                resolved[rel_path] = {}
-            for line_num, justification_info in scan_lines.items():
-                resolved[rel_path][line_num] = justification_info
-
-    # Output manifest
+            resolved.setdefault(rel_path, {}).update(scan_lines)
+    return warnings
+
+
+def _write_manifest(
+    output_path: Path,
+    source_root: Path,
+    resolved: dict[str, dict[int, dict[str, str]]],
+    warnings: list[str],
+    errors: list[str],
+) -> None:
+    """Write the manifest consumed by effective_coverage (line keys as strings, files sorted)."""
     manifest = {
         "version": 1,
-        "source_root": str(args.source_root),
+        "source_root": str(source_root),
         "justified_files": {
             filepath: {str(k): v for k, v in lines.items()} for filepath, lines in sorted(resolved.items())
         },
         "warnings": warnings,
         "errors": errors,
     }
-
-    output_path = Path(args.output)
     output_path.parent.mkdir(parents=True, exist_ok=True)
     with open(output_path, "w", encoding="utf-8") as f:
         json.dump(manifest, f, indent=2)
 
-    # Print diagnostics
-    total_justified_lines = sum(len(lines) for lines in resolved.values())
-    print(
-        f"INFO: Resolved {total_justified_lines} justified lines across {len(resolved)} files.",
-        file=sys.stderr,
-    )
-    if warnings:
-        for w in warnings:
-            print(f"WARNING: {w}", file=sys.stderr)
-    if errors:
-        for e in errors:
-            print(f"ERROR: {e}", file=sys.stderr)
-        sys.exit(1)
-
 
-def resolve_location_lines(location: Dict[str, Any]) -> List[int]:
+def resolve_location_lines(location: dict[str, Any]) -> list[int]:
     """Resolve line numbers from a YAML location entry."""
     if "lines" in location:
         return location["lines"]
-    elif "line_start" in location and "line_end" in location:
+    if "line_start" in location and "line_end" in location:
         return list(range(location["line_start"], location["line_end"] + 1))
-    elif "line" in location:
+    if "line" in location:
         return [location["line"]]
     return []
 
 
-def _matches_platform(entry: Dict[str, Any], platform: str) -> bool:
+def _matches_platform(entry: dict[str, Any], platform: str) -> bool:
     """Check if a justification entry applies to the given platform.
 
     The ``platforms`` field is mandatory and validated by ``validate_yaml``.
@@ -162,19 +169,19 @@ def _matches_platform(entry: Dict[str, Any], platform: str) -> bool:
 def scan_file_for_markers(
     file_path: Path,
     rel_path: str,
-    justifications_by_id: Dict[str, Dict[str, Any]],
-) -> Tuple[List[str], Dict[int, Dict[str, str]]]:
+    justifications_by_id: dict[str, dict[str, Any]],
+) -> tuple[list[str], dict[int, dict[str, str]]]:
     """Scan a source file for COV_JUSTIFIED markers."""
     warnings = []
-    justified_lines: Dict[int, Dict[str, str]] = {}
+    justified_lines: dict[int, dict[str, str]] = {}
 
     try:
-        with open(file_path, "r", encoding="utf-8", errors="replace") as f:
+        with open(file_path, encoding="utf-8", errors="replace") as f:
             lines = f.readlines()
-    except (IOError, OSError):
+    except OSError:
         return warnings, justified_lines
 
-    region_stack: List[Tuple[int, str]] = []  # (start_line, justification_id)
+    region_stack: list[tuple[int, str]] = []  # (start_line, justification_id)
 
     for line_num, line in enumerate(lines, start=1):
         # Check for COV_JUSTIFIED_START
@@ -226,7 +233,7 @@ def scan_file_for_markers(
     return warnings, justified_lines
 
 
-def collect_source_files(source_root: Path, file_filter: str) -> List[Path]:
+def collect_source_files(source_root: Path, file_filter: str) -> list[Path]:
     """Collect source files to scan for markers."""
     extensions = file_filter.split(",") if file_filter else ["cpp", "h", "hpp", "cc", "rs"]
     files = []
@@ -241,134 +248,148 @@ def collect_source_files(source_root: Path, file_filter: str) -> List[Path]:
     return sorted(files)
 
 
-def load_yaml(yaml_path: Path) -> Dict[str, Any]:
+def load_yaml(yaml_path: Path) -> dict[str, Any]:
     """Load YAML justification database."""
     if not yaml_path.exists():
         print(f"ERROR: Justification YAML not found: {yaml_path}", file=sys.stderr)
         sys.exit(1)
 
-    with open(yaml_path, "r", encoding="utf-8") as f:
+    with open(yaml_path, encoding="utf-8") as f:
         content = f.read()
 
     return yaml.safe_load(content)
 
 
-def validate_yaml(data: Dict[str, Any]) -> None:
-    """Validate the justification YAML structure and types."""
+def validate_yaml(data: Any) -> None:
+    """Validate the justification YAML structure and types; exit(1) with all findings on failure."""
     try:
-        errors = []
-
-        if not isinstance(data, dict):
-            print("ERROR: YAML validation: root must be a mapping", file=sys.stderr)
-            sys.exit(1)
-
-        if "version" not in data:
-            errors.append("Missing 'version' field")
-        elif not isinstance(data["version"], int):
-            errors.append(f"'version' must be an integer, got {type(data['version']).__name__}")
-
-        if "justifications" not in data:
-            errors.append("Missing 'justifications' field")
-            for e in errors:
-                print(f"ERROR: {e}", file=sys.stderr)
-            sys.exit(1)
-
-        if not isinstance(data["justifications"], list):
-            errors.append(f"'justifications' must be a list, got {type(data['justifications']).__name__}")
-            for e in errors:
-                print(f"ERROR: YAML validation: {e}", file=sys.stderr)
-            sys.exit(1)
-
-        seen_ids: Set[str] = set()
-        for i, entry in enumerate(data["justifications"]):
-            prefix = f"justifications[{i}]"
-
-            if not isinstance(entry, dict):
-                errors.append(f"{prefix}: must be a mapping, got {type(entry).__name__}")
-                continue
+        errors = _validate_document(data)
+    except Exception as error:  # pylint: disable=broad-exception-caught
+        # Any malformed shape must end in a validation failure, never in a traceback.
+        print(f"ERROR: YAML validation: {error}", file=sys.stderr)
+        sys.exit(1)
+    if errors:
+        for e in errors:
+            print(f"ERROR: YAML validation: {e}", file=sys.stderr)
+        sys.exit(1)
 
-            if "id" not in entry:
-                errors.append(f"{prefix}: missing 'id'")
-                continue
 
-            jid = entry["id"]
-            if not isinstance(jid, str):
-                errors.append(f"{prefix}: 'id' must be a string, got {type(jid).__name__}")
-                continue
+def _validate_document(data: Any) -> list[str]:
+    """Return all validation errors of the document (empty when valid)."""
+    if not isinstance(data, dict):
+        return ["root must be a mapping"]
+    errors: list[str] = []
+    if "version" not in data:
+        errors.append("Missing 'version' field")
+    elif not isinstance(data["version"], int):
+        errors.append(f"'version' must be an integer, got {type(data['version']).__name__}")
+    if "justifications" not in data:
+        errors.append("Missing 'justifications' field")
+        return errors
+    justifications = data["justifications"]
+    if not isinstance(justifications, list):
+        errors.append(f"'justifications' must be a list, got {type(justifications).__name__}")
+        return errors
+    seen_ids: set[str] = set()
+    for i, entry in enumerate(justifications):
+        errors.extend(_validate_entry(f"justifications[{i}]", entry, seen_ids))
+    return errors
+
+
+def _validate_entry(prefix: str, entry: Any, seen_ids: set[str]) -> list[str]:
+    """Validate one justification entry."""
+    if not isinstance(entry, dict):
+        return [f"{prefix}: must be a mapping, got {type(entry).__name__}"]
+    if "id" not in entry:
+        return [f"{prefix}: missing 'id'"]
+    jid = entry["id"]
+    if not isinstance(jid, str):
+        return [f"{prefix}: 'id' must be a string, got {type(jid).__name__}"]
+    errors: list[str] = []
+    if jid in seen_ids:
+        errors.append(f"{prefix}: duplicate ID '{jid}'")
+    seen_ids.add(jid)
+    if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", jid):
+        errors.append(f"{prefix}: ID '{jid}' must be kebab-case")
+    errors.extend(_validate_choice(prefix, entry, "category", VALID_CATEGORIES))
+    errors.extend(_validate_platforms(prefix, entry))
+    errors.extend(_validate_reason(prefix, entry))
+    if "locations" in entry:
+        errors.extend(_validate_locations(prefix, entry["locations"]))
+    return errors
+
+
+def _validate_choice(prefix: str, entry: dict[str, Any], field: str, valid: set[str]) -> list[str]:
+    """A mandatory string field restricted to ``valid`` values."""
+    if field not in entry:
+        return [f"{prefix}: missing '{field}'"]
+    value = entry[field]
+    if not isinstance(value, str):
+        return [f"{prefix}: '{field}' must be a string, got {type(value).__name__}"]
+    if value not in valid:
+        return [f"{prefix}: invalid {field} '{value}'. Must be one of: {sorted(valid)}"]
+    return []
 
-            if jid in seen_ids:
-                errors.append(f"{prefix}: duplicate ID '{jid}'")
-            seen_ids.add(jid)
 
-            if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", jid):
-                errors.append(f"{prefix}: ID '{jid}' must be kebab-case")
+def _validate_platforms(prefix: str, entry: dict[str, Any]) -> list[str]:
+    """``platforms``: a non-empty list of known platform names."""
+    if "platforms" not in entry:
+        return [f"{prefix}: missing 'platforms'"]
+    platforms = entry["platforms"]
+    if not isinstance(platforms, list):
+        return [f"{prefix}: 'platforms' must be a list, got {type(platforms).__name__}"]
+    if not platforms:
+        return [f"{prefix}: 'platforms' must not be empty"]
+    errors: list[str] = []
+    for p in platforms:
+        if not isinstance(p, str):
+            errors.append(f"{prefix}: 'platforms' entries must be strings, got {type(p).__name__}")
+        elif p not in VALID_PLATFORMS:
+            errors.append(f"{prefix}: invalid platform '{p}'. Must be one of: {sorted(VALID_PLATFORMS)}")
+    return errors
+
+
+def _validate_reason(prefix: str, entry: dict[str, Any]) -> list[str]:
+    """``reason``: a non-blank string."""
+    if "reason" not in entry:
+        return [f"{prefix}: missing 'reason'"]
+    reason = entry["reason"]
+    if not isinstance(reason, str):
+        return [f"{prefix}: 'reason' must be a string, got {type(reason).__name__}"]
+    if not reason.strip():
+        return [f"{prefix}: 'reason' must not be empty"]
+    return []
+
 
-            if "category" not in entry:
-                errors.append(f"{prefix}: missing 'category'")
-            elif not isinstance(entry["category"], str):
-                errors.append(f"{prefix}: 'category' must be a string, got {type(entry['category']).__name__}")
-            elif entry["category"] not in VALID_CATEGORIES:
+def _validate_locations(prefix: str, locations: Any) -> list[str]:
+    """``locations``: a list of mappings with a ``file`` and integer line selectors."""
+    if not isinstance(locations, list):
+        return [f"{prefix}: 'locations' must be a list, got {type(locations).__name__}"]
+    errors: list[str] = []
+    for j, loc in enumerate(locations):
+        loc_prefix = f"{prefix}.locations[{j}]"
+        if not isinstance(loc, dict):
+            errors.append(f"{loc_prefix}: must be a mapping, got {type(loc).__name__}")
+            continue
+        loc_map = cast(dict[str, Any], loc)  # ty cannot narrow Any through isinstance
+        if "file" not in loc_map:
+            errors.append(f"{loc_prefix}: missing 'file'")
+        elif not isinstance(loc_map["file"], str):
+            errors.append(f"{loc_prefix}: 'file' must be a string, got {type(loc_map['file']).__name__}")
+        for int_field in ("line", "line_start", "line_end"):
+            if int_field in loc and not isinstance(loc_map[int_field], int):
                 errors.append(
-                    f"{prefix}: invalid category '{entry['category']}'. Must be one of: {sorted(VALID_CATEGORIES)}"
+                    f"{loc_prefix}: '{int_field}' must be an integer, got {type(loc_map[int_field]).__name__}"
                 )
-
-            if "platforms" not in entry:
-                errors.append(f"{prefix}: missing 'platforms'")
-            elif not isinstance(entry["platforms"], list):
-                errors.append(f"{prefix}: 'platforms' must be a list, got {type(entry['platforms']).__name__}")
-            elif not entry["platforms"]:
-                errors.append(f"{prefix}: 'platforms' must not be empty")
-            else:
-                for p in entry["platforms"]:
-                    if not isinstance(p, str):
-                        errors.append(f"{prefix}: 'platforms' entries must be strings, got {type(p).__name__}")
-                    elif p not in VALID_PLATFORMS:
-                        errors.append(f"{prefix}: invalid platform '{p}'. Must be one of: {sorted(VALID_PLATFORMS)}")
-
-            if "reason" not in entry:
-                errors.append(f"{prefix}: missing 'reason'")
-            elif not isinstance(entry["reason"], str):
-                errors.append(f"{prefix}: 'reason' must be a string, got {type(entry['reason']).__name__}")
-            elif not entry["reason"].strip():
-                errors.append(f"{prefix}: 'reason' must not be empty")
-
-            if "locations" in entry:
-                if not isinstance(entry["locations"], list):
-                    errors.append(f"{prefix}: 'locations' must be a list, got {type(entry['locations']).__name__}")
-                else:
-                    for j, loc in enumerate(entry["locations"]):
-                        loc_prefix = f"{prefix}.locations[{j}]"
-                        if not isinstance(loc, dict):
-                            errors.append(f"{loc_prefix}: must be a mapping, got {type(loc).__name__}")
-                            continue
-                        if "file" not in loc:
-                            errors.append(f"{loc_prefix}: missing 'file'")
-                        elif not isinstance(loc["file"], str):
-                            errors.append(f"{loc_prefix}: 'file' must be a string, got {type(loc['file']).__name__}")
-                        for int_field in ("line", "line_start", "line_end"):
-                            if int_field in loc and not isinstance(loc[int_field], int):
-                                errors.append(
-                                    f"{loc_prefix}: '{int_field}' must be an integer, "
-                                    f"got {type(loc[int_field]).__name__}"
-                                )
-                        if "lines" in loc:
-                            if not isinstance(loc["lines"], list):
-                                errors.append(
-                                    f"{loc_prefix}: 'lines' must be a list, got {type(loc['lines']).__name__}"
-                                )
-                            elif not all(isinstance(ln, int) for ln in loc["lines"]):
-                                errors.append(f"{loc_prefix}: 'lines' must contain only integers")
-
-        if errors:
-            for e in errors:
-                print(f"ERROR: YAML validation: {e}", file=sys.stderr)
-            sys.exit(1)
-    except Exception as error:
-        print(f"ERROR: YAML validation: {error}", file=sys.stderr)
-        sys.exit(1)
+        if "lines" in loc:
+            if not isinstance(loc_map["lines"], list):
+                errors.append(f"{loc_prefix}: 'lines' must be a list, got {type(loc_map['lines']).__name__}")
+            elif not all(isinstance(ln, int) for ln in loc_map["lines"]):
+                errors.append(f"{loc_prefix}: 'lines' must contain only integers")
+    return errors
 
 
-def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
+def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
     """Parse command-line arguments (``argv`` defaults to ``sys.argv[1:]``)."""
     parser = argparse.ArgumentParser(description="Coverage justification processor")
     parser.add_argument(
diff --git a/score_coverage/merger.py b/score_coverage/merger.py
index 17f159b..def16b8 100644
--- a/score_coverage/merger.py
+++ b/score_coverage/merger.py
@@ -33,10 +33,9 @@
 import sys
 import zipfile
 from pathlib import Path
-from typing import List, Optional, Set
 
 
-def main(argv: Optional[List[str]] = None) -> None:
+def main(argv: list[str] | None = None) -> None:
     """Entry point. ``argv`` defaults to ``sys.argv[1:]``."""
     args = parse_args(argv)
 
@@ -147,7 +146,7 @@ def cleanup_dangling_symlinks(directory: Path) -> None:
                 entry.unlink()
 
 
-def get_object_files_from_manifest(source_file_manifest: Path) -> Set[str]:
+def get_object_files_from_manifest(source_file_manifest: Path) -> set[str]:
     """Parse the coverage manifest to find instrumented object files."""
     runfiles_dir = Path(os.environ.get("RUNFILES_DIR", "")) / os.environ.get("TEST_WORKSPACE", "_main")
     root = os.environ.get("ROOT")
@@ -205,7 +204,7 @@ def is_elf(path: Path) -> bool:
         return False
 
 
-def run_command(cmd: List[str]) -> subprocess.CompletedProcess:
+def run_command(cmd: list[str]) -> subprocess.CompletedProcess:
     """Run a command and exit on failure."""
     try:
         return subprocess.run(
@@ -223,7 +222,7 @@ def run_command(cmd: List[str]) -> subprocess.CompletedProcess:
         sys.exit(1)
 
 
-def create_zip(root: Path, directories: List[Path], output_file: Path) -> None:
+def create_zip(root: Path, directories: list[Path], output_file: Path) -> None:
     """Create a zip file from the given directories relative to root."""
     with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf:
         for directory in directories:
@@ -236,7 +235,7 @@ def create_zip(root: Path, directories: List[Path], output_file: Path) -> None:
                     zf.write(file_path, arcname)
 
 
-def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
+def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
     """Parse command-line arguments matching the Bazel LCOV_MERGER interface."""
     parser = argparse.ArgumentParser(description="LLVM coverage merger for Bazel")
     parser.add_argument("--coverage_dir", type=Path, required=True)
diff --git a/score_coverage/reporter.py b/score_coverage/reporter.py
index 8005258..7e9eb29 100644
--- a/score_coverage/reporter.py
+++ b/score_coverage/reporter.py
@@ -30,14 +30,25 @@
 import sys
 import zipfile
 from pathlib import Path
-from typing import List, Optional, Set, Tuple
+from typing import Protocol
+
 from python.runfiles import Runfiles
 
 
-def main(argv: Optional[List[str]] = None) -> None:
+class RunfilesLike(Protocol):
+    """The part of ``python.runfiles.Runfiles`` this module uses; tests provide fakes."""
+
+    def Rlocation(self, path: str) -> str | None:  # noqa: N802  # pylint: disable=invalid-name
+        """Resolve a runfiles path to an absolute path, or None."""
+
+
+def main(argv: list[str] | None = None) -> None:
     """Main entry point. ``argv`` defaults to ``sys.argv[1:]``."""
     args = parse_args(argv)
     r = Runfiles.Create()
+    if r is None:
+        print("ERROR: runfiles are unavailable; the reporter must run as a Bazel coverage action.", file=sys.stderr)
+        sys.exit(1)
 
     # Read the list of per-test report files.
     reports = read_reports_file(args.reports_file)
@@ -83,7 +94,7 @@ def main(argv: Optional[List[str]] = None) -> None:
     )
 
     # Load baseline objects (production library archives) for zero-coverage baseline.
-    baseline_objects = load_baseline_objects(r, args.baseline_objects, args.workspace_root)
+    baseline_objects = load_baseline_objects(r, args.baseline_objects)
 
     # Rust rlib archives (exposed as .a symlinks by rules_rust) start with a
     # lib.rmeta member, which makes llvm-cov reject the whole archive with
@@ -140,48 +151,35 @@ def main(argv: Optional[List[str]] = None) -> None:
             print("ERROR: Coverage allowlist is empty, falling back to filter_regexes.txt.", file=sys.stderr)
             sys.exit(-1)
     cxxfilt = find_cxxfilt(llvm_bin_path, r, args.llvm_cxxfilt)
-    common_args = {
-        "llvm_bin_path": llvm_bin_path,
-        "objects": sorted_objects,
-        "instr_profile": str(merged_profdata),
-        "filter_regexes": sorted(filter_regexes),
-        "workspace_root": workspace_root,
-    }
+    profile = str(merged_profdata)
+    regexes = sorted(filter_regexes)
+
+    def show_html(objects: list[str]) -> None:
+        run_llvm_cov_show(
+            llvm_bin_path,
+            objects,
+            profile,
+            regexes,
+            workspace_root,
+            output_format="html",
+            html_report_dir=html_report_dir,
+            cxxfilt=cxxfilt,
+        )
 
     # Generate HTML report including baseline-only files when valid archives are available.
     html_report_dir = Path.cwd() / "html_report"
     if baseline_only_archives:
-        all_html_objects = sorted_objects + baseline_only_archives
-        html_args = {
-            **common_args,
-            "objects": all_html_objects,
-        }
         try:
-            run_llvm_cov_show(
-                **html_args,
-                output_format="html",
-                html_report_dir=html_report_dir,
-                cxxfilt=cxxfilt,
-            )
+            show_html(sorted_objects + baseline_only_archives)
         except SystemExit:
             # Some baseline archives caused llvm-cov show to fail; retry with test binaries only.
             print(
                 "WARNING: HTML generation with baseline archives failed; falling back to test-only HTML.",
                 file=sys.stderr,
             )
-            run_llvm_cov_show(
-                **common_args,
-                output_format="html",
-                html_report_dir=html_report_dir,
-                cxxfilt=cxxfilt,
-            )
+            show_html(sorted_objects)
     else:
-        run_llvm_cov_show(
-            **common_args,
-            output_format="html",
-            html_report_dir=html_report_dir,
-            cxxfilt=cxxfilt,
-        )
+        show_html(sorted_objects)
 
     # Rewrite absolute workspace paths in the HTML pages so unpacked report
     # archives remain browsable outside the machine that produced them.
@@ -190,19 +188,12 @@ def main(argv: Optional[List[str]] = None) -> None:
     # Generate LCOV report from test binaries.
     lcov_report_dir = Path.cwd() / "lcov_report"
     lcov_report_dir.mkdir(exist_ok=True)
-    lcov_result = run_llvm_cov_export(**common_args)
-    lcov_content = lcov_result.stdout
+    lcov_content = run_llvm_cov_export(llvm_bin_path, sorted_objects, profile, regexes, workspace_root).stdout
 
     # If there are baseline-only files, generate a separate baseline LCOV and merge.
     if baseline_only_archives:
-        baseline_lcov_args = {
-            "llvm_bin_path": llvm_bin_path,
-            "objects": baseline_only_archives,
-            "instr_profile": None,
-            "filter_regexes": [],  # No filtering — we only have the needed archives.
-            "workspace_root": workspace_root,
-        }
-        baseline_lcov = run_llvm_cov_export(**baseline_lcov_args)
+        # No filtering: only the needed archives are passed.
+        baseline_lcov = run_llvm_cov_export(llvm_bin_path, baseline_only_archives, None, [], workspace_root)
         if baseline_lcov.stdout:
             # Filter baseline LCOV to only include baseline-only files.
             filtered_baseline = _filter_lcov(baseline_lcov.stdout, baseline_only_files)
@@ -220,7 +211,7 @@ def main(argv: Optional[List[str]] = None) -> None:
     # Generate text summary.
     text_report_dir = Path.cwd() / "text_report"
     text_report_dir.mkdir(exist_ok=True)
-    summary = run_llvm_cov_report(**common_args)
+    summary = run_llvm_cov_report(llvm_bin_path, sorted_objects, profile, regexes, workspace_root)
     with open(text_report_dir / "summary.txt", "w", encoding="utf-8") as f:
         f.write(summary.stdout)
     print(summary.stdout, file=sys.stderr)
@@ -309,8 +300,8 @@ def _filter_lcov(lcov_content: str, target_files: set) -> str:
 
 def get_covered_files(
     llvm_bin_path: Path,
-    objects: List[str],
-    instr_profile: Optional[str],
+    objects: list[str],
+    instr_profile: str | None,
     workspace_root: str,
 ) -> set:
     """Run a quick llvm-cov report to discover all files with coverage data.
@@ -363,12 +354,12 @@ def get_covered_files(
 
 def run_llvm_cov_show(
     llvm_bin_path: Path,
-    objects: List[str],
-    instr_profile: Optional[str],
-    filter_regexes: List[str],
+    objects: list[str],
+    instr_profile: str | None,
+    filter_regexes: list[str],
     workspace_root: str,
     output_format: str,
-    html_report_dir: Path = None,
+    html_report_dir: Path | None = None,
     cxxfilt: str = "",
 ) -> subprocess.CompletedProcess:
     """Run llvm-cov show."""
@@ -406,9 +397,9 @@ def run_llvm_cov_show(
 
 def run_llvm_cov_export(
     llvm_bin_path: Path,
-    objects: List[str],
-    instr_profile: Optional[str],
-    filter_regexes: List[str],
+    objects: list[str],
+    instr_profile: str | None,
+    filter_regexes: list[str],
     workspace_root: str,
 ) -> subprocess.CompletedProcess:
     """Run llvm-cov export to produce LCOV format."""
@@ -437,9 +428,9 @@ def run_llvm_cov_export(
 
 def run_llvm_cov_report(
     llvm_bin_path: Path,
-    objects: List[str],
-    instr_profile: Optional[str],
-    filter_regexes: List[str],
+    objects: list[str],
+    instr_profile: str | None,
+    filter_regexes: list[str],
     workspace_root: str,
 ) -> subprocess.CompletedProcess:
     """Run llvm-cov report for a text summary."""
@@ -465,7 +456,7 @@ def run_llvm_cov_report(
     return run_command(cmd)
 
 
-def extract_reports(reports: List[str]) -> Tuple[Set[str], Set[str]]:
+def extract_reports(reports: list[str]) -> tuple[set[str], set[str]]:
     """Extract profdata and object files from per-test zip files."""
     valid_profdata_files = set()
     valid_object_files = set()
@@ -511,13 +502,13 @@ def extract_reports(reports: List[str]) -> Tuple[Set[str], Set[str]]:
     return valid_profdata_files, valid_object_files
 
 
-def read_reports_file(reports_file: Path) -> List[str]:
+def read_reports_file(reports_file: Path) -> list[str]:
     """Read the reports file listing all per-test coverage outputs."""
     with open(reports_file, encoding="utf-8") as f:
         return [line.strip() for line in f if line.strip()]
 
 
-def _read_ar_members(path: str) -> List[tuple]:
+def _read_ar_members(path: str) -> list[tuple]:
     """Parse a Unix ar archive, returning (name, data_offset, size) tuples.
 
     Handles the GNU long-name table ("//" member with "/" references).
@@ -554,7 +545,7 @@ def _read_ar_members(path: str) -> List[tuple]:
     return members
 
 
-def expand_rlib_archives(objects: List[str], workdir: Path) -> List[str]:
+def expand_rlib_archives(objects: list[str], workdir: Path) -> list[str]:
     """Replace Rust rlib archives with their extracted object members.
 
     llvm-cov rejects rlib archives ("no coverage data found") because of the
@@ -585,10 +576,10 @@ def expand_rlib_archives(objects: List[str], workdir: Path) -> List[str]:
 
 
 def resolve_tool(
-    runfiles: Optional[Runfiles],
-    flag_value: Optional[str],
+    runfiles: RunfilesLike | None,
+    flag_value: str | None,
     fallback_rlocation: str,
-) -> Optional[Path]:
+) -> Path | None:
     """Resolve an llvm tool path.
 
     Preference order: the explicit rlocation path passed by the
@@ -609,8 +600,8 @@ def resolve_tool(
 
 def find_cxxfilt(
     llvm_bin_path: Path,
-    runfiles: Optional[Runfiles] = None,
-    explicit: Optional[str] = None,
+    runfiles: RunfilesLike | None = None,
+    explicit: str | None = None,
 ) -> str:
     """Locate llvm-cxxfilt for demangling (C++ Itanium and Rust v0/legacy symbols).
 
@@ -634,7 +625,7 @@ def find_cxxfilt(
     return ""
 
 
-def load_coverage_allowlist(runfiles: Runfiles, rlocation_path: str) -> List[str]:
+def load_coverage_allowlist(runfiles: RunfilesLike, rlocation_path: str) -> list[str]:
     """Load coverage allowlist (package paths) from a file via Bazel runfiles."""
     path = runfiles.Rlocation(rlocation_path)
     if not path or not Path(path).exists():
@@ -645,10 +636,9 @@ def load_coverage_allowlist(runfiles: Runfiles, rlocation_path: str) -> List[str
 
 
 def load_baseline_objects(
-    runfiles: Runfiles,
-    rlocation_path: str,
-    workspace_root: str,
-) -> List[str]:
+    runfiles: RunfilesLike,
+    rlocation_path: str | None,
+) -> list[str]:
     """Load baseline object archive paths and resolve them to absolute paths.
 
     The objects manifest lists relative paths to .a files. When the reporter runs
@@ -674,7 +664,7 @@ def load_baseline_objects(
         # run. Do NOT use runfiles.CurrentRepository() here: this script lives
         # in score_coverage, so that would resolve against the wrong repo.
         path = runfiles.Rlocation(os.path.join("_main", line))
-        if os.path.exists(path):
+        if path and os.path.exists(path):
             resolved.append(path)
         else:
             print(f"ERROR: Baseline object not found: {line}", file=sys.stderr)
@@ -682,7 +672,7 @@ def load_baseline_objects(
     return sorted(resolved)
 
 
-def run_command(cmd: List[str], separate_stderr: bool = False) -> subprocess.CompletedProcess:
+def run_command(cmd: list[str], separate_stderr: bool = False) -> subprocess.CompletedProcess:
     """Run a command and exit on failure.
 
     With separate_stderr the child's stderr is captured separately and
@@ -720,7 +710,7 @@ def write_empty_output(output_file: Path) -> None:
         pass
 
 
-def create_zip(root: Path, directories: List[Path], output_file: Path) -> None:
+def create_zip(root: Path, directories: list[Path], output_file: Path) -> None:
     """Create a zip file from the given directories relative to root."""
     with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf:
         for directory in directories:
@@ -733,7 +723,7 @@ def create_zip(root: Path, directories: List[Path], output_file: Path) -> None:
                     zf.write(file_path, arcname)
 
 
-def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
+def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
     """Parse command-line arguments matching the Bazel coverage_report_generator interface."""
     parser = argparse.ArgumentParser(description="LLVM coverage reporter for Bazel")
     parser.add_argument("--output_file", type=Path, required=True)
diff --git a/score_coverage/tests/coverage_summary_test.py b/score_coverage/tests/coverage_summary_test.py
index 327cc1c..8d02859 100644
--- a/score_coverage/tests/coverage_summary_test.py
+++ b/score_coverage/tests/coverage_summary_test.py
@@ -12,6 +12,11 @@
 # SPDX-License-Identifier: Apache-2.0
 # *******************************************************************************
 """Unit tests for the markdown coverage summary."""
+# Test modules: docstrings on every test method add nothing, tests exercise
+# private helpers on purpose, TemporaryDirectory is closed in tearDown, and setUp
+# fixtures are attributes.
+# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,consider-using-with
+# pylint: disable=too-many-instance-attributes
 
 import json
 import tempfile
@@ -58,6 +63,7 @@ def test_empty_file_returns_empty_list(self):
     def test_line_and_branch_counters(self):
         with tempfile.TemporaryDirectory() as tmp:
             files = parse_lcov(_write(tmp, "l.dat", LCOV_TWO_FILES))
+            assert files is not None
         self.assertEqual(len(files), 2)
         a, main_rs = files
         self.assertEqual((a.lines_hit, a.lines_found), (1, 2))
@@ -67,17 +73,20 @@ def test_line_and_branch_counters(self):
     def test_lf_without_brf_yields_no_branch_data(self):
         with tempfile.TemporaryDirectory() as tmp:
             files = parse_lcov(_write(tmp, "l.dat", "SF:a.cpp\nLF:5\nLH:2\nend_of_record\n"))
+            assert files is not None
         self.assertIsNone(files[0].branches_found)
 
     def test_brda_fallback_when_no_brf(self):
         lcov = "SF:a.cpp\nBRDA:1,0,0,3\nBRDA:1,0,1,-\nBRDA:2,0,0,0\nLF:2\nLH:2\nend_of_record\n"
         with tempfile.TemporaryDirectory() as tmp:
             files = parse_lcov(_write(tmp, "l.dat", lcov))
+            assert files is not None
         self.assertEqual((files[0].branches_hit, files[0].branches_found), (1, 3))
 
     def test_record_without_end_of_record_is_flushed(self):
         with tempfile.TemporaryDirectory() as tmp:
             files = parse_lcov(_write(tmp, "l.dat", "SF:a.cpp\nLF:1\nLH:1\n"))
+            assert files is not None
         self.assertEqual(len(files), 1)
 
     def test_non_utf8_bytes_do_not_crash(self):
@@ -85,6 +94,7 @@ def test_non_utf8_bytes_do_not_crash(self):
             p = Path(tmp) / "l.dat"
             p.write_bytes(b"SF:src/\xff\xfe.cpp\nLF:1\nLH:0\nend_of_record\n")
             files = parse_lcov(p)
+            assert files is not None
         self.assertEqual(len(files), 1)
         self.assertEqual(files[0].lines_found, 1)
 
@@ -110,6 +120,7 @@ class RollupTest(unittest.TestCase):
     def test_worst_directory_first(self):
         with tempfile.TemporaryDirectory() as tmp:
             files = parse_lcov(_write(tmp, "l.dat", LCOV_TWO_FILES))
+            assert files is not None
         rows = rollup_by_directory(files)
         self.assertEqual(rows[0]["directory"], "rust")
         self.assertEqual(rows[0]["pct"], 0.0)
@@ -120,6 +131,7 @@ class RenderTest(unittest.TestCase):
     def _render(self, justification=None):
         with tempfile.TemporaryDirectory() as tmp:
             files = parse_lcov(_write(tmp, "l.dat", LCOV_TWO_FILES))
+            assert files is not None
         return render_markdown(files, justification)
 
     def test_empty_input_renders_note(self):
@@ -154,6 +166,7 @@ def test_justification_section(self):
     def test_branch_dash_when_no_branch_data(self):
         with tempfile.TemporaryDirectory() as tmp:
             files = parse_lcov(_write(tmp, "l.dat", "SF:a.cpp\nLF:2\nLH:1\nend_of_record\n"))
+            assert files is not None
         md = render_markdown(files, None)
         self.assertIn("| Branches | — | — | — | — |", md)
 
@@ -168,6 +181,7 @@ def test_loads_summary_and_counts_applied(self):
         with tempfile.TemporaryDirectory() as tmp:
             p = _write(tmp, "report.json", json.dumps(report))
             summary = load_justification_summary(p)
+        assert summary is not None
         self.assertEqual(summary["applied_justification_count"], 2)
         self.assertEqual(summary["justified_lines"], 2)
 
diff --git a/score_coverage/tests/effective_coverage_test.py b/score_coverage/tests/effective_coverage_test.py
index 8bad710..9361d63 100644
--- a/score_coverage/tests/effective_coverage_test.py
+++ b/score_coverage/tests/effective_coverage_test.py
@@ -11,6 +11,11 @@
 # SPDX-License-Identifier: Apache-2.0
 # *******************************************************************************
 """Unit tests for effective_coverage: arithmetic, llvm-cov HTML post-processing and the report."""
+# Test modules: docstrings on every test method add nothing, tests exercise
+# private helpers on purpose, TemporaryDirectory is closed in tearDown, and setUp
+# fixtures are attributes.
+# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,consider-using-with
+# pylint: disable=too-many-instance-attributes
 
 import io
 import json
@@ -58,7 +63,8 @@ def _index_page(files, totals) -> str:
         )
     func, line, branch = totals
     rows.append(
-        f"
Totals
{_pct_cell(*func)}{_pct_cell(*line)}{_pct_cell(*branch)}\n" + "
Totals
" + f"{_pct_cell(*func)}{_pct_cell(*line)}{_pct_cell(*branch)}\n" ) return "

Coverage Report

" + "".join(rows) + "
" @@ -386,33 +392,31 @@ def test_effective_coverage_is_floored(self): self.assertEqual(s["effective_line_coverage_pct"], 66.66) # 66.666.. floored, never 66.67 def test_missing_manifest_exits(self): - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit): - ec.main( - [ - "--html-dir", - str(self.html), - "--manifest", - str(self.manifest / "nope"), - "--output", - str(self.report), - ] - ) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): + ec.main( + [ + "--html-dir", + str(self.html), + "--manifest", + str(self.manifest / "nope"), + "--output", + str(self.report), + ] + ) def test_missing_html_dir_exits(self): self.manifest.write_text(json.dumps({"justified_files": {}}), encoding="utf-8") - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit): - ec.main( - [ - "--html-dir", - str(self.html / "missing"), - "--manifest", - str(self.manifest), - "--output", - str(self.report), - ] - ) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): + ec.main( + [ + "--html-dir", + str(self.html / "missing"), + "--manifest", + str(self.manifest), + "--output", + str(self.report), + ] + ) class FormatDetectionAndLcovTest(unittest.TestCase): @@ -466,8 +470,10 @@ def row(label, triple): return ( "\n\n\n
\n" + summary + "
\n" '
\n
\n' - '
\n' - ' 75.0%\n 12 / 0 / 16\n
\n
\n
\n' + '
\n' + ' 75.0%\n 12 / 0 / 16\n' + "
\n
\n
\n" "\n\n" ) diff --git a/score_coverage/tests/generate_coverage_html_test.py b/score_coverage/tests/generate_coverage_html_test.py index e83cbc4..43d597d 100644 --- a/score_coverage/tests/generate_coverage_html_test.py +++ b/score_coverage/tests/generate_coverage_html_test.py @@ -17,6 +17,11 @@ bazel-out/_coverage/ and a fake bazel-testlogs tree. The justification tools are replaced by fakes where the HTML post-processing itself is out of scope. """ +# Test modules: docstrings on every test method add nothing, tests exercise +# private helpers on purpose, TemporaryDirectory is closed in tearDown, and setUp +# fixtures are attributes. +# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,consider-using-with +# pylint: disable=too-many-instance-attributes import io import json @@ -81,9 +86,8 @@ def test_numbers(self): def test_garbage_is_an_error_not_a_permissive_gate(self): for bad in ["abc", "85%", "1e999", "nan", "-1", "100.01", "inf"]: - with self.subTest(bad=bad): - with self.assertRaises(gch.GenerateError): - gch.parse_threshold(bad) + with self.subTest(bad=bad), self.assertRaises(gch.GenerateError): + gch.parse_threshold(bad) class RawLineCoverageTest(unittest.TestCase): @@ -159,9 +163,8 @@ def test_malformed_reports(self): {"summary": {"effective_line_coverage_pct": None}}, {"summary": {"effective_line_coverage_pct": True}}, ]: - with self.subTest(payload=payload): - with self.assertRaises(gch.GenerateError): - gch.effective_line_coverage_from_report(self._report(payload)) + with self.subTest(payload=payload), self.assertRaises(gch.GenerateError): + gch.effective_line_coverage_from_report(self._report(payload)) bad_json = _write(self.root / "report.json", "{not json") with self.assertRaises(gch.GenerateError): gch.effective_line_coverage_from_report(bad_json) @@ -211,9 +214,8 @@ def test_all_flags(self): ) def test_unknown_platform_rejected(self): - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit): - gch.parse_args(["--platform", "windows"]) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): + gch.parse_args(["--platform", "windows"]) class RunWithoutYamlTest(unittest.TestCase): @@ -418,9 +420,8 @@ def test_justify_failure_is_an_error_not_a_verdict(self): def failing(argv): raise SystemExit(1) - with mock.patch.object(gch.justify, "main", side_effect=failing): - with self.assertRaises(gch.GenerateError): - _run(self.root, ["--yaml", "tools/coverage/coverage_justifications.yaml"], {"COVERAGE_THRESHOLD": "0"}) + with mock.patch.object(gch.justify, "main", side_effect=failing), self.assertRaises(gch.GenerateError): + _run(self.root, ["--yaml", "tools/coverage/coverage_justifications.yaml"], {"COVERAGE_THRESHOLD": "0"}) def test_missing_summary_is_an_error(self): def no_summary(argv): @@ -431,9 +432,9 @@ def no_summary(argv): with ( mock.patch.object(gch.justify, "main", side_effect=self._fake_justify), mock.patch.object(gch.effective_coverage, "main", side_effect=no_summary), + self.assertRaises(gch.GenerateError), ): - with self.assertRaises(gch.GenerateError): - _run(self.root, ["--yaml", "tools/coverage/coverage_justifications.yaml"], {"COVERAGE_THRESHOLD": "0"}) + _run(self.root, ["--yaml", "tools/coverage/coverage_justifications.yaml"], {"COVERAGE_THRESHOLD": "0"}) def test_archive_includes_justification_report(self): rc, _, _ = self._run_yaml(["--archive-dir", "out"], {"COVERAGE_THRESHOLD": "0"}) @@ -456,14 +457,19 @@ def test_generate_error_maps_to_exit_error(self): def test_end_to_end_exit_codes(self): with tempfile.TemporaryDirectory() as tmp: _make_workspace(Path(tmp)) - with mock.patch.dict( - "os.environ", {"BUILD_WORKSPACE_DIRECTORY": tmp, "COVERAGE_THRESHOLD": "10"}, clear=True + with ( + mock.patch.dict( + "os.environ", {"BUILD_WORKSPACE_DIRECTORY": tmp, "COVERAGE_THRESHOLD": "10"}, clear=True + ), + redirect_stdout(io.StringIO()), + ): + self.assertEqual(gch.main([]), gch.EXIT_OK) + with ( + mock.patch.dict("os.environ", {"BUILD_WORKSPACE_DIRECTORY": tmp}, clear=True), + redirect_stdout(io.StringIO()), + redirect_stderr(io.StringIO()), ): - with redirect_stdout(io.StringIO()): - self.assertEqual(gch.main([]), gch.EXIT_OK) - with mock.patch.dict("os.environ", {"BUILD_WORKSPACE_DIRECTORY": tmp}, clear=True): - with redirect_stdout(io.StringIO()), redirect_stderr(io.StringIO()): - self.assertEqual(gch.main([]), gch.EXIT_GATE_FAILED) + self.assertEqual(gch.main([]), gch.EXIT_GATE_FAILED) if __name__ == "__main__": diff --git a/score_coverage/tests/justify_test.py b/score_coverage/tests/justify_test.py index 4fcd68a..4d501a1 100644 --- a/score_coverage/tests/justify_test.py +++ b/score_coverage/tests/justify_test.py @@ -11,10 +11,14 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* """Unit tests for justify: YAML validation, marker scanning and manifest generation.""" +# Test modules: docstrings on every test method add nothing, tests exercise +# private helpers on purpose, TemporaryDirectory is closed in tearDown, and setUp +# fixtures are attributes. +# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,consider-using-with +# pylint: disable=too-many-instance-attributes import io import json -import os import tempfile import unittest from contextlib import redirect_stderr @@ -132,9 +136,8 @@ def test_locations(self): def test_all_errors_are_reported_together(self): err = io.StringIO() entry = {"id": "Bad_Id", "category": "nope", "platforms": [], "reason": ""} - with redirect_stderr(err): - with self.assertRaises(SystemExit): - justify.validate_yaml({"version": 1, "justifications": [entry]}) + with redirect_stderr(err), self.assertRaises(SystemExit): + justify.validate_yaml({"version": 1, "justifications": [entry]}) text = err.getvalue() for fragment in ["kebab-case", "invalid category", "must not be empty", "'reason' must not be empty"]: self.assertIn(fragment, text) @@ -235,7 +238,7 @@ def test_stop_without_start_warns(self): self.assertEqual(warnings, ["f.cpp:2: COV_JUSTIFIED_STOP without matching START"]) def test_marker_id_characters(self): - warnings, lines = self._scan("x // COV_JUSTIFIED reason-a; trailing text\n") + _warnings, lines = self._scan("x // COV_JUSTIFIED reason-a; trailing text\n") self.assertEqual(sorted(lines), [1]) def test_unreadable_file_yields_nothing(self): @@ -245,7 +248,7 @@ def test_unreadable_file_yields_nothing(self): def test_non_utf8_content_is_tolerated(self): path = self.root / "f.cpp" path.write_bytes(b"\xff\xfe junk\nfoo(); // COV_JUSTIFIED reason-a\n") - warnings, lines = justify.scan_file_for_markers(path, "f.cpp", self.by_id) + _warnings, lines = justify.scan_file_for_markers(path, "f.cpp", self.by_id) self.assertEqual(sorted(lines), [2]) @@ -280,9 +283,8 @@ def test_empty_filter_uses_defaults(self): class LoadYamlTest(unittest.TestCase): def test_missing_file_exits(self): - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit): - justify.load_yaml(Path("/nonexistent/justifications.yaml")) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): + justify.load_yaml(Path("/nonexistent/justifications.yaml")) def test_loads_document(self): with tempfile.TemporaryDirectory() as tmp: diff --git a/score_coverage/tests/merger_test.py b/score_coverage/tests/merger_test.py index d74a8e8..8a8bc87 100644 --- a/score_coverage/tests/merger_test.py +++ b/score_coverage/tests/merger_test.py @@ -12,13 +12,25 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* """Unit tests for the per-test coverage merger.""" - +# Test modules: docstrings on every test method add nothing, tests exercise +# private helpers on purpose, TemporaryDirectory is closed in tearDown, and setUp +# fixtures are attributes. +# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,consider-using-with +# pylint: disable=too-many-instance-attributes + +import io +import json import os +import stat +import sys import tempfile import unittest +import zipfile +from contextlib import redirect_stderr from pathlib import Path from unittest import mock +from score_coverage import merger from score_coverage.merger import find_llvm_profdata, get_object_files_from_manifest, is_elf @@ -41,9 +53,8 @@ def test_missing_file_is_not_elf(self): class FindLlvmProfdataTest(unittest.TestCase): def test_llvm_profdata_env_wins(self): - with tempfile.NamedTemporaryFile() as f: - with mock.patch.dict(os.environ, {"LLVM_PROFDATA": f.name}, clear=True): - self.assertEqual(find_llvm_profdata(), f.name) + with tempfile.NamedTemporaryFile() as f, mock.patch.dict(os.environ, {"LLVM_PROFDATA": f.name}, clear=True): + self.assertEqual(find_llvm_profdata(), f.name) def test_rust_llvm_profdata_resolved_against_root(self): with tempfile.TemporaryDirectory() as root: @@ -66,9 +77,8 @@ def test_missing_root_env_is_a_hard_error(self): with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as manifest: manifest.write("some/path\n") manifest.flush() - with mock.patch.dict(os.environ, {}, clear=True): - with self.assertRaises(SystemExit): - get_object_files_from_manifest(Path(manifest.name)) + with mock.patch.dict(os.environ, {}, clear=True), self.assertRaises(SystemExit): + get_object_files_from_manifest(Path(manifest.name)) def test_rust_elf_manifest_entry_is_collected(self): """rules_rust lists the instrumented test executable in the manifest.""" @@ -117,15 +127,6 @@ def test_objects_list_entries_are_resolved(self): # with a fake llvm-profdata, plus the helpers that were not covered. # --------------------------------------------------------------------------- -import io # noqa: E402 -import json # noqa: E402 -import stat # noqa: E402 -import sys # noqa: E402 -import zipfile # noqa: E402 -from contextlib import redirect_stderr # noqa: E402 - -from score_coverage import merger # noqa: E402 - def _fake_profdata(path: Path, fail: bool = False) -> Path: """A stand-in llvm-profdata that concatenates its inputs into --output.""" @@ -180,9 +181,8 @@ def test_only_listed_directories_relative_to_root(self): class RunCommandTest(unittest.TestCase): def test_failure_exits_with_1(self): - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit) as ctx: - merger.run_command([sys.executable, "-c", "import sys; print('bad'); sys.exit(7)"]) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit) as ctx: + merger.run_command([sys.executable, "-c", "import sys; print('bad'); sys.exit(7)"]) self.assertEqual(ctx.exception.code, 1) def test_success_returns_output(self): diff --git a/score_coverage/tests/reporter_test.py b/score_coverage/tests/reporter_test.py index 099d938..24aeea8 100644 --- a/score_coverage/tests/reporter_test.py +++ b/score_coverage/tests/reporter_test.py @@ -12,12 +12,25 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* """Unit tests for the final coverage reporter.""" - +# Test modules: docstrings on every test method add nothing, tests exercise +# private helpers on purpose, TemporaryDirectory is closed in tearDown, and setUp +# fixtures are attributes. +# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,consider-using-with +# pylint: disable=too-many-instance-attributes + +import io +import json +import os +import stat +import sys import tempfile import unittest import zipfile +from contextlib import redirect_stderr from pathlib import Path +from unittest import mock +from score_coverage import reporter from score_coverage.reporter import ( _filter_lcov, _make_html_paths_relative, @@ -167,16 +180,6 @@ def test_produces_valid_empty_zip(self): # end-to-end run of main() against fake llvm tools. # --------------------------------------------------------------------------- -import io # noqa: E402 -import json # noqa: E402 -import os # noqa: E402 -import stat # noqa: E402 -import sys # noqa: E402 -from contextlib import redirect_stderr # noqa: E402 -from unittest import mock # noqa: E402 - -from score_coverage import reporter # noqa: E402 - class _FakeRunfiles: """Minimal stand-in for python.runfiles.Runfiles: maps rlocation paths to files.""" @@ -184,7 +187,7 @@ class _FakeRunfiles: def __init__(self, mapping): self.mapping = mapping - def Rlocation(self, path): # noqa: N802 (mirrors the real API) + def Rlocation(self, path): # noqa: N802 # pylint: disable=invalid-name if os.path.isabs(path): return path return self.mapping.get(path) @@ -196,13 +199,13 @@ def _write_tool(path: Path, body: str) -> Path: return path -REPORT_TABLE = """Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover -------------------------------------------------------------------------------------------------------------------------------------------------------- -/proc/self/cwd/src/a.cpp 4 1 75.00% 1 0 100.00% 10 2 80.00% -/ws/src/b.cpp 2 2 0.00% 1 1 0.00% 5 5 0.00% -rust/lib.rs 3 0 100.00% 2 0 100.00% 8 0 100.00% -------------------------------------------------------------------------------------------------------------------------------------------------------- -TOTAL 9 3 66.67% 4 1 75.00% 23 7 69.57% +REPORT_TABLE = """Filename Regions Missed Cover Functions Missed Executed Lines Missed Cover +------------------------------------------------------------------------------------------------------- +/proc/self/cwd/src/a.cpp 4 1 75.00% 1 0 100.00% 10 2 80.00% +/ws/src/b.cpp 2 2 0.00% 1 1 0.00% 5 5 0.00% +rust/lib.rs 3 0 100.00% 2 0 100.00% 8 0 100.00% +------------------------------------------------------------------------------------------------------- +TOTAL 9 3 66.67% 4 1 75.00% 23 7 69.57% """ @@ -364,18 +367,17 @@ def test_baseline_objects_resolved_via_main_repo(self): manifest = self.root / "objects.txt" manifest.write_text("# c\nbazel-out/bin/libz.a\n", encoding="utf-8") rf = _FakeRunfiles({"main/objects.txt": str(manifest), "_main/bazel-out/bin/libz.a": str(obj)}) - self.assertEqual(reporter.load_baseline_objects(rf, "main/objects.txt", "/ws"), [str(obj)]) - self.assertEqual(reporter.load_baseline_objects(rf, None, "/ws"), []) + self.assertEqual(reporter.load_baseline_objects(rf, "main/objects.txt"), [str(obj)]) + self.assertEqual(reporter.load_baseline_objects(rf, None), []) with redirect_stderr(io.StringIO()): - self.assertEqual(reporter.load_baseline_objects(rf, "missing", "/ws"), []) + self.assertEqual(reporter.load_baseline_objects(rf, "missing"), []) def test_missing_baseline_object_is_a_hard_error(self): manifest = self.root / "objects.txt" manifest.write_text("bazel-out/bin/gone.a\n", encoding="utf-8") rf = _FakeRunfiles({"main/objects.txt": str(manifest), "_main/bazel-out/bin/gone.a": str(self.root / "gone")}) - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit): - reporter.load_baseline_objects(rf, "main/objects.txt", "/ws") + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): + reporter.load_baseline_objects(rf, "main/objects.txt") class RunCommandTest(unittest.TestCase): @@ -390,9 +392,8 @@ def test_separate_stderr_keeps_stdout_clean(self): self.assertIn("warn", err.getvalue()) def test_failure_exits(self): - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit): - reporter.run_command([sys.executable, "-c", "import sys; sys.exit(4)"]) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): + reporter.run_command([sys.executable, "-c", "import sys; sys.exit(4)"]) class LlvmCovInvocationsTest(unittest.TestCase): @@ -561,16 +562,14 @@ def test_invalid_reports_write_empty_zip(self): self.assertEqual(zf.namelist(), []) def test_missing_llvm_tools_is_an_error(self): - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit) as ctx: - reporter.main(self._argv(llvm_cov=str(self.root / "nope"))) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit) as ctx: + reporter.main(self._argv(llvm_cov=str(self.root / "nope"))) self.assertEqual(ctx.exception.code, 1) def test_empty_allowlist_is_an_error(self): self.allowlist.write_text("# nothing\n", encoding="utf-8") - with redirect_stderr(io.StringIO()): - with self.assertRaises(SystemExit): - reporter.main(self._argv()) + with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): + reporter.main(self._argv()) if __name__ == "__main__": diff --git a/score_coverage/tests/starlark/coverage_scope_tests.bzl b/score_coverage/tests/starlark/coverage_scope_tests.bzl index d439689..1e3be2d 100644 --- a/score_coverage/tests/starlark/coverage_scope_tests.bzl +++ b/score_coverage/tests/starlark/coverage_scope_tests.bzl @@ -118,6 +118,33 @@ def _test_generated_sources_are_excluded_impl(env, target): # The archive of the library with the generated source is still a baseline object. _objects(env, target).contains("libwith_generated.a") +# --- Rust: rust_library (CcInfo) and rust_binary (CrateInfo only) ----------- + +def _test_rust_library_sources_and_archive(name): + coverage_scope(name = name + "_subject", testonly = True, deps = [_FIX + ":rlib"]) + analysis_test(name = name, impl = _test_rust_library_sources_and_archive_impl, target = name + "_subject") + +def _test_rust_library_sources_and_archive_impl(env, target): + _allowlist(env, target).equals(_PKG + "/fixtures/lib.rs\n") + + # rules_rust exposes the rlib through CcInfo as a static library; that is the baseline object. + _objects(env, target).contains("rlib") + +def _test_rust_binary_collects_crate_sources_and_executable(name): + coverage_scope(name = name + "_subject", testonly = True, deps = [_FIX + ":rbin"]) + analysis_test(name = name, impl = _test_rust_binary_collects_crate_sources_and_executable_impl, target = name + "_subject") + +def _test_rust_binary_collects_crate_sources_and_executable_impl(env, target): + _allowlist(env, target).equals( + "\n".join([ + _PKG + "/fixtures/lib.rs", + _PKG + "/fixtures/main.rs", + ]) + "\n", + ) + objects = _objects(env, target) + objects.contains(_PKG + "/fixtures/rbin") # the coverage-built executable itself + objects.contains("rlib") + # --- providers / output groups -------------------------------------------- def _test_output_groups(name): @@ -143,6 +170,8 @@ def coverage_scope_test_suite(name): _test_shared_dependency_listed_once, _test_header_only_library_has_no_archive, _test_generated_sources_are_excluded, + _test_rust_library_sources_and_archive, + _test_rust_binary_collects_crate_sources_and_executable, _test_output_groups, ], ) diff --git a/score_coverage/tests/starlark/fixtures/BUILD b/score_coverage/tests/starlark/fixtures/BUILD index 90ab467..1ba42de 100644 --- a/score_coverage/tests/starlark/fixtures/BUILD +++ b/score_coverage/tests/starlark/fixtures/BUILD @@ -12,6 +12,7 @@ # ******************************************************************************* load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") # Fixtures for the coverage_scope analysis tests. They are only analyzed, # never built: `manual` keeps them out of wildcard builds. @@ -62,3 +63,18 @@ cc_library( tags = MANUAL, deps = [":leaf"], ) + +rust_library( + name = "rlib", + srcs = ["lib.rs"], + edition = "2021", + tags = MANUAL, +) + +rust_binary( + name = "rbin", + srcs = ["main.rs"], + edition = "2021", + tags = MANUAL, + deps = [":rlib"], +) diff --git a/score_coverage/tests/starlark/fixtures/lib.rs b/score_coverage/tests/starlark/fixtures/lib.rs new file mode 100644 index 0000000..338536f --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/lib.rs @@ -0,0 +1,16 @@ +// ******************************************************************************* +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* +// Fixture for the coverage_scope analysis tests; only analyzed. +pub fn lib() -> i32 { + 1 +} diff --git a/score_coverage/tests/starlark/fixtures/main.rs b/score_coverage/tests/starlark/fixtures/main.rs new file mode 100644 index 0000000..cdfa2db --- /dev/null +++ b/score_coverage/tests/starlark/fixtures/main.rs @@ -0,0 +1,16 @@ +// ******************************************************************************* +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* +// Fixture for the coverage_scope analysis tests; only analyzed. +fn main() { + println!("{}", rlib::lib()); +} diff --git a/tools/linters.bzl b/tools/linters.bzl new file mode 100644 index 0000000..d53d6e1 --- /dev/null +++ b/tools/linters.bzl @@ -0,0 +1,31 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"""Static-analysis aspects for this repository's Python (ruff, pylint, ty). + +Declared here rather than in score_tooling so the config label resolves +against this repo. Wired through the `lint` config in .bazelrc: + + bazel build --config=lint //score_coverage/... //tools/... +""" + +load("@score_tooling//third_party/lint:macros.bzl", "pylint_lint_aspect", "ruff_lint_aspect", "ty_lint_aspect") + +ruff = ruff_lint_aspect(config = Label("//:pyproject.toml")) + +pylint = pylint_lint_aspect( + binary = Label("@score_tooling//third_party/lint:pylint"), + config = Label("//:pyproject.toml"), +) + +ty = ty_lint_aspect(config = Label("//:pyproject.toml")) diff --git a/tools/self_coverage_gate.py b/tools/self_coverage_gate.py index 172b5a3..7c5a192 100644 --- a/tools/self_coverage_gate.py +++ b/tools/self_coverage_gate.py @@ -31,9 +31,9 @@ import argparse import os import sys +from collections.abc import Sequence from dataclasses import dataclass, field from pathlib import Path -from typing import Dict, List, Optional, Sequence DEFAULT_LCOV = Path("bazel-out/_coverage/_coverage_report.dat") SCOPE_PREFIX = "score_coverage/" @@ -42,13 +42,16 @@ @dataclass class FileCoverage: + """Line and branch counters of one source file.""" + path: str lines_found: int = 0 lines_hit: int = 0 branches_found: int = 0 branches_hit: int = 0 - def add(self, other: "FileCoverage") -> None: + def add(self, other: FileCoverage) -> None: + """Accumulate another record of the same file (one per test target).""" self.lines_found += other.lines_found self.lines_hit += other.lines_hit self.branches_found += other.branches_found @@ -57,31 +60,38 @@ def add(self, other: "FileCoverage") -> None: @dataclass class Totals: - files: List[FileCoverage] = field(default_factory=list) + """All in-scope files plus their sums.""" + + files: list[FileCoverage] = field(default_factory=list) @property def lines_found(self) -> int: + """Sum of ``lines_found`` over all files.""" return sum(f.lines_found for f in self.files) @property def lines_hit(self) -> int: + """Sum of ``lines_hit`` over all files.""" return sum(f.lines_hit for f in self.files) @property def branches_found(self) -> int: + """Sum of ``branches_found`` over all files.""" return sum(f.branches_found for f in self.files) @property def branches_hit(self) -> int: + """Sum of ``branches_hit`` over all files.""" return sum(f.branches_hit for f in self.files) -def pct(hit: int, found: int) -> Optional[float]: +def pct(hit: int, found: int) -> float | None: """Percentage, or None when nothing was found (no verdict).""" return None if found == 0 else 100.0 * hit / found def in_scope(path: str) -> bool: + """True for the tool's own sources (score_coverage/, excluding tests/).""" return path.startswith(SCOPE_PREFIX) and not path.startswith(EXCLUDED_PREFIX) @@ -89,9 +99,9 @@ def parse_lcov(path: Path) -> Totals: """Aggregate LF/LH/BRF/BRH per in-scope source file (records may repeat per test).""" if not path.is_file(): raise FileNotFoundError(f"LCOV file not found: {path}") - per_file: Dict[str, FileCoverage] = {} - current: Optional[FileCoverage] = None - with open(path, "r", encoding="utf-8") as f: + per_file: dict[str, FileCoverage] = {} + current: FileCoverage | None = None + with open(path, encoding="utf-8") as f: for raw in f: line = raw.rstrip("\n") if line.startswith("SF:"): @@ -114,11 +124,13 @@ def parse_lcov(path: Path) -> Totals: return Totals(files=sorted(per_file.values(), key=lambda fc: fc.path)) -def fmt(value: Optional[float]) -> str: +def fmt(value: float | None) -> str: + """Fixed-width percentage, or n/a.""" return " n/a " if value is None else f"{value:6.2f}" def render_table(totals: Totals, markdown: bool) -> str: + """Per-file C0/C1 table plus a TOTAL row, as plain text or markdown.""" rows = [(f.path, f.lines_hit, f.lines_found, f.branches_hit, f.branches_found) for f in totals.files] rows.append(("TOTAL", totals.lines_hit, totals.lines_found, totals.branches_hit, totals.branches_found)) if markdown: @@ -135,7 +147,7 @@ def render_table(totals: Totals, markdown: bool) -> str: return "\n".join(out) + "\n" -def evaluate(totals: Totals, min_lines: float, min_branches: float) -> List[str]: +def evaluate(totals: Totals, min_lines: float, min_branches: float) -> list[str]: """Return the list of gate violations (empty when the gate passes).""" problems = [] line_pct = pct(totals.lines_hit, totals.lines_found) @@ -151,8 +163,9 @@ def evaluate(totals: Totals, min_lines: float, min_branches: float) -> List[str] return problems -def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace: - parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) +def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: + """Parse the command line (``argv`` defaults to ``sys.argv[1:]``).""" + parser = argparse.ArgumentParser(description=(__doc__ or "").splitlines()[0]) parser.add_argument("--lcov", type=Path, default=None, help=f"Combined LCOV (default: {DEFAULT_LCOV})") parser.add_argument("--min-lines", type=float, required=True, help="Minimum line coverage in percent") parser.add_argument("--min-branches", type=float, required=True, help="Minimum branch coverage in percent") @@ -160,7 +173,8 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace: return parser.parse_args(argv) -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: + """Entry point; returns the process exit code (0 pass, 1 below threshold, 2 no verdict).""" args = parse_args(argv) workspace = Path(os.environ.get("BUILD_WORKSPACE_DIRECTORY", ".")) lcov = args.lcov if args.lcov is not None else workspace / DEFAULT_LCOV diff --git a/tools/self_coverage_gate_test.py b/tools/self_coverage_gate_test.py index f9c54f0..5e5d4f8 100644 --- a/tools/self_coverage_gate_test.py +++ b/tools/self_coverage_gate_test.py @@ -11,6 +11,11 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* """Tests for the repository's own coverage gate.""" +# Test modules: docstrings on every test method add nothing, tests exercise +# private helpers on purpose, TemporaryDirectory is closed in tearDown, and setUp +# fixtures are attributes. +# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,consider-using-with +# pylint: disable=too-many-instance-attributes import io import tempfile From 06c9678442b9f9e6d61b3b8fea8bab586bceb44d Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:06:22 +0300 Subject: [PATCH 08/17] Add the docs-as-code tree (WP4) Documentation built with score_docs_as_code 8.1.1 against the S-CORE process needs (score_process_description 2.1.2), from docs/BUILD so the root package keeps loading only runtime dependencies. `bazel run //docs:docs` and `//docs:docs_check` pass with zero metamodel warnings; 42 needs. - manual/: adoption guide (six steps, from the former README), command reference, constraints of use CSTR-01..10 each mapped to the potential error it mitigates, known problems (with upstream llvm-cov references and the QNX limitation, tooling#427). - requirements/: six use cases as stakeholder requirements (stkh_req__coverage__uc_*), the potential-error analysis ERR-01..10 with HazOp guide words, impact, detection and mitigations, and 32 tool requirements (tool_req__coverage_*) with `satisfies` links to the use cases and to gd_req__verification_reporting / _report_archiving, tagged with the errors they mitigate. The analysis concludes tool impact yes, detection no for ERR-02/03/07, hence expected TCL LOW. - architecture/: two-phase pipeline with a PlantUML diagram, module/consumer split, design decisions (report-time filtering, fail loud, gate on LCOV, in-process tool calls). Replaces COVERAGE_GUIDE.md. - verification/: verification report with test inventory (7 suites, 230 cases), hand-maintained requirement coverage, structural coverage table (95.6 % / 87.2 %), static analysis, end-to-end validation, deviations. - release/: release notes for 0.1.0 relative to score_tooling 2.2.x. README.md is now a short pointer to the docs; docs.yml publishes them through the cicd-workflows docs job. The generated ubproject.toml and docs/_build are ignored. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/workflows/docs.yml | 37 ++ .gitignore | 4 + COVERAGE_GUIDE.md | 341 ---------------- MODULE.bazel | 5 + MODULE.bazel.lock | 18 +- README.md | 356 ++--------------- docs/BUILD | 24 ++ docs/architecture/index.rst | 169 ++++++++ docs/index.rst | 50 +++ docs/manual/constraints.rst | 118 ++++++ docs/manual/index.rst | 23 ++ docs/manual/known_problems.rst | 68 ++++ docs/manual/user_manual.rst | 268 +++++++++++++ docs/release/index.rst | 21 + docs/release/release_notes.rst | 47 +++ docs/requirements/index.rst | 27 ++ docs/requirements/potential_errors.rst | 128 ++++++ docs/requirements/tool_requirements.rst | 459 ++++++++++++++++++++++ docs/requirements/use_cases.rst | 101 +++++ docs/verification/index.rst | 21 + docs/verification/verification_report.rst | 166 ++++++++ tools/BUILD | 2 +- 22 files changed, 1796 insertions(+), 657 deletions(-) create mode 100644 .github/workflows/docs.yml delete mode 100644 COVERAGE_GUIDE.md create mode 100644 docs/BUILD create mode 100644 docs/architecture/index.rst create mode 100644 docs/index.rst create mode 100644 docs/manual/constraints.rst create mode 100644 docs/manual/index.rst create mode 100644 docs/manual/known_problems.rst create mode 100644 docs/manual/user_manual.rst create mode 100644 docs/release/index.rst create mode 100644 docs/release/release_notes.rst create mode 100644 docs/requirements/index.rst create mode 100644 docs/requirements/potential_errors.rst create mode 100644 docs/requirements/tool_requirements.rst create mode 100644 docs/requirements/use_cases.rst create mode 100644 docs/verification/index.rst create mode 100644 docs/verification/verification_report.rst diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..335c127 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,37 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: Docs / Build & Deploy +permissions: + contents: write + pages: write + pull-requests: write + id-token: write +on: + pull_request_target: + types: [opened, reopened, synchronize] + push: + branches: + - main + merge_group: + types: [checks_requested] +jobs: + docs-build: + uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@93aac16ada7d247bbb6ae926509ddea74cf5213a # v0.0.2 + permissions: + contents: write + pages: write + pull-requests: write + id-token: write + with: + bazel-target: "//docs:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" + retention-days: 3 diff --git a/.gitignore b/.gitignore index f9d1900..973bfdd 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,7 @@ integration_tests/expected_normalised.dat integration_tests/typo_dir/ integration_tests/typo.log integration_tests/report.backup + +# Generated by docs-as-code on every docs build +ubproject.toml +_build/ diff --git a/COVERAGE_GUIDE.md b/COVERAGE_GUIDE.md deleted file mode 100644 index da5447a..0000000 --- a/COVERAGE_GUIDE.md +++ /dev/null @@ -1,341 +0,0 @@ - - -# Unified Code Coverage (LLVM) — how the pipeline works - -This document explains, from first principles, how the unified Rust + C++ -coverage pipeline in `@score_coverage` works: the mechanism, the -module/consumer split, the non-obvious pitfalls, and where the pipeline comes -from. For the step-by-step consumer setup see [README.md](README.md); for a -working consumer workspace see [integration_tests/](integration_tests/). - ---- - -## 1. Background concepts - -### 1.1 What "code coverage" means here - -When we run the test suite, we want to know **which lines of production code -were actually executed**. The compiler helps: it can *instrument* the code — -insert tiny counters at every branch and statement. When an instrumented test -binary runs, it writes the counter values to a file. Tooling then maps those -counters back to source lines and produces a report: green = executed, -red = never executed. - -### 1.2 Bazel in three paragraphs - -Bazel is the build system used by S-CORE. Code is organized into **targets** -(a library, a binary, a test), declared in files named `BUILD`. Targets -reference each other by **labels** like `//src/rust/mycrate:tests` -(`//path/to/package:target_name`). - -External dependencies (compilers, libraries) are declared in `MODULE.bazel`. -A **toolchain** is Bazel's packaging of a compiler + flags; you can register -several and select one per build. Command-line defaults live in `.bazelrc`, -grouped into named **configs**: `bazel test --config=foo` applies all lines -starting with `test:foo`. - -Bazel has a built-in coverage mode: `bazel coverage ` builds the -targets with instrumentation, runs the tests, and post-processes the results. -Two hooks matter for us: `--coverage_output_generator` (a tool that processes -each test's raw coverage output) and `--coverage_report_generator` (a tool -that combines everything into the final report). **This pipeline replaces -both with its own tools** — that is its core. - -### 1.3 Rust in two paragraphs - -Rust code is organized into **crates** (≈ libraries/binaries). The Rust -compiler is `rustc`; S-CORE uses **Ferrocene**, a safety-certified Rust -toolchain distribution. Bazel builds Rust via the `rules_rust` plugin. - -Crucially, `rustc` is built on the same compiler backend as Clang (**LLVM**). -That means Rust and C++ can use the *same* coverage instrumentation format — -which is what makes a single unified report possible. - -### 1.4 The LLVM coverage toolchain - -The pipeline is built on LLVM's "source-based coverage": - -| Artifact | Produced by | Contains | -|---|---|---| -| instrumented binary | Clang (C++) / rustc (Rust) with coverage flags | counters + a "covmap" mapping counters → source lines | -| `.profraw` | running the instrumented test | raw counter values | -| `.profdata` | `llvm-profdata merge` | merged, indexed counters | -| HTML / LCOV / text report | `llvm-cov show/export/report` | human- and machine-readable coverage | - ---- - -## 2. How the pipeline works - -There are two phases. - -### 2.1 Phase 1 — collection (`bazel coverage`) - -``` -bazel coverage --config=llvm_cov //... --build_tests_only -``` - -The `llvm_cov` config (the consumer copies it from -[integration_tests/.bazelrc](integration_tests/.bazelrc)) does four things: - -1. **Swaps the compilers.** C++ is compiled with a hermetic Clang/LLVM - toolchain (`@llvm_toolchain`) instead of GCC; Rust with a Ferrocene - toolchain that has LLVM coverage tools attached — wired in automatically - by score_toolchains_rust >= 0.9.2 from the coverage-tools tarball, see §5. - Both emit the same covmap format. -2. **Turns on instrumentation.** `--experimental_use_llvm_covmap` plus the - `coverage` feature for C++; `rules_rust` adds `-Cinstrument-coverage` to - rustc automatically once the toolchain declares coverage tools. An extra - flag (`-Cllvm-args=-runtime-counter-relocation`) enables "continuous - mode" so coverage survives even if a test terminates abnormally (same - purpose as the `enable_llvm_coverage_for_death_tests` cc_feature on the - C++ side). **Branch coverage** needs one more flag per language: Clang - emits branch regions by default, but rustc only does so with - `-Zcoverage-options=branch` — an unstable option that works on - *rolling* (nightly-based) Ferrocene builds. On a stable-channel - Ferrocene the flag must be dropped (Rust branch columns revert to `-`) - until rustc stabilizes it. -3. **Installs the per-test tool** - (`--coverage_output_generator=@score_coverage//:merger`). After - each test runs, `merger.py` finds the test's `.profraw` files, merges - them into one `.profdata` with `llvm-profdata`, records which - instrumented binary was involved, and zips both up as the test's - `coverage.dat`. -4. **Installs the final tool** (`--coverage_report_generator=` the - consumer's `score_coverage_reporter` target). After all tests finish, - `reporter.py` merges every per-test `.profdata` into one, then runs - `llvm-cov` three times: `show` → HTML report, `export` → LCOV data (for - dashboards), `report` → text summary. All three are zipped into - `bazel-out/_coverage/_coverage_report.dat`. - -**Scope — which files appear in the report.** LLVM covmap instruments -*everything*, including test code and third-party libraries. Filtering -happens at report time using an **allowlist** generated by the consumer's -`score_coverage_scope` target (`coverage_scope.bzl`): an aspect walks the -dependency graph starting from the listed production targets and collects -every in-workspace source file they own. The reporter excludes everything -else (test sources, googletest, external deps, ...). - -**Baseline — files with no tests at all.** A file that no test executes -produces no coverage data, so naive tooling silently omits it — it looks -like there is no problem when in fact coverage is 0%. The scope aspect -therefore also collects the compiled libraries/binaries (`.a` archives for -cc_library/rust_library, the coverage-built executable for rust_binary), and -the reporter runs `llvm-cov --empty-profile` over them so untested files -show up with **exact** 0% line and branch entries — the denominators come -from the compiler's own coverage map, not from any source-text heuristic. -Rust rlib archives need special handling here: their leading `lib.rmeta` -member makes llvm-cov reject the whole archive, so the reporter expands them -into their `.o` members first. - -### 2.2 Phase 2 — report generation & gating - -``` -bazel run @score_coverage//:generate_coverage_html -- \ - --yaml tools/coverage/coverage_justifications.yaml -``` - -`generate_coverage_html.py` unpacks the HTML from the zip, then applies the -**justification system**: - -- `justify.py` reads the consumer's `coverage_justifications.yaml` plus - in-code markers and produces a manifest of "argued" lines. A justification - says: *this line cannot reasonably be covered by a test, and here is why* - (e.g. defensive code for conditions that cannot occur). Markers work in - both languages: - - ```rust - unreachable!(); // COV_JUSTIFIED my-justification-id - ``` - ```cpp - default: return Error; // COV_JUSTIFIED my-justification-id - ``` - - Every marker id must exist in the YAML with a category and a written - reason — a justification is a reviewed engineering argument, not an - opt-out. - -- `effective_coverage.py` recolors justified lines **orange** in the HTML - (with the reason as tooltip) and computes: - - ``` - raw coverage = covered / total - effective coverage = (covered + justified) / total - ``` - - It also flags **stale** justifications — lines that are justified but - meanwhile covered by a test — so the database stays clean. - -- Finally the script compares effective line coverage against the - `COVERAGE_THRESHOLD` environment variable (default **100**) and **fails - (exit 1)** when below. The model: every uncovered line must eventually be - either tested or justified; the threshold is ratcheted up as gaps close. - -- Optionally a **markdown job summary** is emitted (`--summary-md `, or - appended to `GITHUB_STEP_SUMMARY` automatically inside GitHub Actions when - the flag is absent): overall/per-directory tables computed from the LCOV - data (which includes the exact-0% baseline records), raw-vs-effective - numbers when justifications ran, and collapsible least-covered/0% file - lists. It is written before the gate decides the exit code, so a failing - gate still leaves the summary on the run page. - -### 2.3 Day-to-day commands - -```bash -# collect coverage (Rust + C++, one run) -bazel coverage --config=llvm_cov //... --build_tests_only - -# report without gating -COVERAGE_THRESHOLD=0 bazel run @score_coverage//:generate_coverage_html -- \ - --yaml tools/coverage/coverage_justifications.yaml - -# open it -xdg-open coverage_linux/index.html - -# CI-style archive (HTML + LCOV + justification report + JUnit XMLs) -bazel run @score_coverage//:generate_coverage_html -- \ - --yaml tools/coverage/coverage_justifications.yaml --archive my-report -``` - -> **Do not** combine `--config=llvm_cov` with configs that register other -> C++ toolchains (e.g. a GCC host config): the last `--extra_toolchains` -> wins resolution, GCC cannot produce covmap data, and the report script -> fails loudly on the resulting non-zip report. - ---- - -## 3. The module/consumer split - -Almost everything in the pipeline is generic. What is repo-specific is -exactly three things: **(a)** the list of production targets in the scope, -**(b)** the justification YAML, **(c)** the toolchain pins in MODULE.bazel. -The split follows directly: - -**Lives in `@score_coverage` (shared):** - -| File | Role | -|---|---| -| `merger.py` | per-test profraw → profdata (C++ `objects_list.txt` and Rust ELF-manifest discovery) | -| `reporter.py` | final merge + llvm-cov show/export/report + allowlist filtering + `--empty-profile` baselines + rlib expansion | -| `coverage_scope.bzl` | the scope aspect/rule (CcInfo + CrateInfo) | -| `reporter_wrapper.bzl` + `defs.bzl` | the consumer-facing `score_coverage_scope` / `score_coverage_reporter` API | -| `justify.py`, `effective_coverage.py`, `generate_coverage_html.py` | justification + gating layer | -| `enable_llvm_coverage_for_death_tests` | cc_feature for continuous-mode profiling | - -**Lives in the consumer repository:** - -| Piece | Why it cannot move | -|---|---| -| `score_coverage_scope(deps = [...])` | names the repo's production targets | -| `score_coverage_reporter(...)` | carries the repo's LLVM tool labels and workspace root | -| `coverage_justifications.yaml` | reviewed, repo-specific engineering arguments | -| MODULE.bazel toolchain blocks | LLVM + Ferrocene pins are per-repo decisions | -| the `coverage:llvm_cov` bazelrc block | bazelrc cannot be imported across modules; copied from the canonical snippet | - -Two wiring details make the external hosting work, both easy to get wrong: - -- **Runfiles paths span repositories.** The reporter wrapper mixes files - from `_main` (the consumer), `score_coverage` and toolchain repos, so every - path in the generated launcher uses rlocation form (`../repo/...` → - `repo/...`), and the launcher derives its own `RUNFILES_DIR` from `$0` — - the inherited value points at the *test's* runfiles tree, not ours. -- **The baseline manifest lists consumer files.** The reporter resolves - manifest entries against `_main` explicitly; using the runfiles library's - "current repository" would resolve against `score_coverage` and find - nothing. - ---- - -## 4. Non-obvious pitfalls (why these lines exist) - -These are the "landmines" discovered while bringing the pipeline up in -communication and persistency: - -1. **Warnings-as-errors under Clang.** Repos whose deps request the - `treat_warnings_as_errors` feature may need - `--features=-treat_warnings_as_errors` **and** - `--host_features=-treat_warnings_as_errors` in the coverage config: - Clang emits warnings GCC doesn't, and Bazel builds the coverage reporter - (and hence the scope's libraries) a second time "as a tool" in a separate - configuration — that's what the `--host_features` variant covers. -2. **Never disable the `coverage` feature** under the LLVM toolchain — it - *is* the instrumentation (`-fprofile-instr-generate - -fcoverage-mapping`). -3. **`-Cllvm-args=-runtime-counter-relocation`** for Rust — without it, - continuous-mode profiling errors out and Rust tests write no `.profraw`. -4. **`llvm-cov report` prints raw covmap paths** (`/proc/self/cwd/...`) — - `--path-equivalence` does not rewrite *displayed* paths; the reporter - normalizes them, otherwise the allowlist silently excludes all C++ - files. -5. **The Rust toolchain lists its own `llvm-cov`/`llvm-profdata` binaries as - coverage metadata** — the merger must skip `external/` entries or the - final merge emits "mismatched data" warnings. -6. **Rust branch coverage is opt-in and channel-dependent** — llvm-cov only - renders branch data that the compiler wrote into the covmap; stable rustc - writes none. `-Zcoverage-options=branch` enables it on nightly-based - toolchains (like the Ferrocene rolling build). Verify with - `llvm-cov export`: the `branches` arrays must be non-empty for Rust - files. - ---- - -## 5. Toolchain provisioning (score_toolchains_rust + ferrocene_toolchain_builder) - -Solved at the source, no consumer configuration needed: - -- `ferrocene_toolchain_builder` >= **1.3.1** ships `llvm-cov`, - `llvm-profdata` and `llvm-cxxfilt` in the coverage-tools tarball, built - from the same LLVM tree as rustc (so the tools can always read the - profraw/covmap the compiler emits). 1.3.1 also rebuilds ALL artifacts from - a single tree — toolchain tarballs, miri-sysroots (now including - `libprofiler_builtins`) and coverage tools are ABI-consistent. -- `score_toolchains_rust` >= **0.9.2** (current: 0.10.0) auto-wires the - tools into the generated `rust_toolchain` whenever the coverage-tools - tarball contains them. rules_rust then instruments crates under - `bazel coverage` and exports `RUST_LLVM_COV`/`RUST_LLVM_PROFDATA` to the - coverage runner. - -Consequently consumers need no coverage-specific Rust toolchain at all: the -**standard** toolchains declared in score_toolchains_rust's own MODULE.bazel -(e.g. `@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu`) -already pin the coverage-tools tarball, so the toolchain registered for -regular builds is the one that produces coverage — same rustc, same LLVM. - ---- - -## 6. Origins and validation evidence - -The pipeline core was built in the `communication` repository -(eclipse-score/communication, Rust support merged with PR #772 and visible -in its nightly coverage reports), then ported to `persistency` and finally -centralized here. During the persistency port, the previous Rust mechanism -(Ferrocene `symbol-report` + `blanket`, per-target 90% gate — the flow this -module's removed `rust_coverage_report` rule drove) was run against the new -pipeline **on the same commit**. Percentages agreed within a few points on -most files (the two tools attribute lines differently: blanket is -symbol-oriented, llvm-cov counts every executable region), with one headline -difference: - -> A 311-line, 0%-covered Rust binary (`kvs_tool.rs`, no tests) was -> **invisible** to the old pipeline while its 90% gate passed. The new -> baseline mechanism reports it at exact 0% and the effective-coverage gate -> accounts for it. - -That gap — untested files silently missing from reports — is the main -correctness argument for this pipeline, alongside unified C++ + Rust -reporting and branch coverage for Rust. - -Planned next step for the ecosystem: a reusable GitHub Actions workflow in -`eclipse-score/cicd-workflows` wrapping the collection + report + artifact -steps, so consumer repos add one `uses:` block instead of a hand-written -job. diff --git a/MODULE.bazel b/MODULE.bazel index 0526e41..c956b00 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -79,3 +79,8 @@ bazel_dep(name = "buildifier_prebuilt", version = "8.5.1", dev_dependency = True # built with the standard Ferrocene toolchain (registered in .bazelrc). bazel_dep(name = "rules_testing", version = "0.9.0", dev_dependency = True) bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) + +# Documentation (docs-as-code with the S-CORE process needs for `satisfies` / +# `realizes` links). Used only by //docs. +bazel_dep(name = "score_docs_as_code", version = "8.1.1", dev_dependency = True) +bazel_dep(name = "score_process_description", version = "2.1.2", dev_dependency = True) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 0c1748d..1cc40fc 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -358,8 +358,9 @@ "https://bcr.bazel.build/modules/rules_multirun/0.9.0/MODULE.bazel": "32d628ef586b5b23f67e55886b7bc38913ea4160420d66ae90521dda2ff37df0", "https://bcr.bazel.build/modules/rules_multirun/0.9.0/source.json": "e882ba77962fa6c5fe68619e5c7d0374ec9a219fb8d03c42eadaf6d0243771bd", "https://bcr.bazel.build/modules/rules_multitool/0.11.0/MODULE.bazel": "8d9dda78d2398e136300d3ef4fbcc89ede7c32c158d8c016fa7d032df41c4aaf", + "https://bcr.bazel.build/modules/rules_multitool/1.11.1/MODULE.bazel": "f826d2d394e8d964e44ebb4a75ebcfe4e9cd4eb150e2ddcd60398ffeb939696a", + "https://bcr.bazel.build/modules/rules_multitool/1.11.1/source.json": "201f43de1d35bd17f25a4fed3ba5a2ec500ef5e08b7d4b341bb9fd39cef0cbc6", "https://bcr.bazel.build/modules/rules_multitool/1.9.0/MODULE.bazel": "8a042b0dbf35e4aaa94c28ad69efa75c9e673e9ea4bd5c0fb70bab75ef9c636b", - "https://bcr.bazel.build/modules/rules_multitool/1.9.0/source.json": "d9a01604a8b5c4a0e9430824dd34ca5b1b3f5b25277b755e8f3ae91f2c9362a3", "https://bcr.bazel.build/modules/rules_nodejs/5.8.2/MODULE.bazel": "6bc03c8f37f69401b888023bf511cb6ee4781433b0cb56236b2e55a21e3a026a", "https://bcr.bazel.build/modules/rules_nodejs/6.2.0/MODULE.bazel": "ec27907f55eb34705adb4e8257952162a2d4c3ed0f0b3b4c3c1aad1fac7be35e", "https://bcr.bazel.build/modules/rules_nodejs/6.3.0/MODULE.bazel": "45345e4aba35dd6e4701c1eebf5a4e67af4ed708def9ebcdc6027585b34ee52d", @@ -368,7 +369,8 @@ "https://bcr.bazel.build/modules/rules_nodejs/6.7.3/source.json": "a3f966f4415a8a6545e560ee5449eac95cc633f96429d08e87c87775c72f5e09", "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", - "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_pkg/1.1.0/MODULE.bazel": "9db8031e71b6ef32d1846106e10dd0ee2deac042bd9a2de22b4761b0c3036453", + "https://bcr.bazel.build/modules/rules_pkg/1.1.0/source.json": "fef768df13a92ce6067e1cd0cdc47560dace01354f1d921cfb1d632511f7d608", "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483", @@ -419,6 +421,8 @@ "https://bcr.bazel.build/modules/rules_swift/2.1.1/source.json": "40fc69dfaac64deddbb75bd99cdac55f4427d9ca0afbe408576a65428427a186", "https://bcr.bazel.build/modules/rules_testing/0.9.0/MODULE.bazel": "d4d9a7367978b5c589437f01dba1dc80ac6f389e9991e1c1edb3c8207526ca83", "https://bcr.bazel.build/modules/rules_testing/0.9.0/source.json": "2a943853f3480f42a9a5205cc4881a147fecdcf7c8ee87750bbeffff9c9a1877", + "https://bcr.bazel.build/modules/sphinxdocs/2.2.0/MODULE.bazel": "e046c573919d72605d62c352a08d9223a10aafef3a7cb70d0fe253ebdd97019e", + "https://bcr.bazel.build/modules/sphinxdocs/2.2.0/source.json": "b1da19a3d14a1dd8aa6a9ccaedc42bbe0313c8160a77ba5cca336cca1315298d", "https://bcr.bazel.build/modules/stardoc/0.5.0/MODULE.bazel": "f9f1f46ba8d9c3362648eea571c6f9100680efc44913618811b58cc9c02cd678", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", @@ -746,6 +750,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/1.0.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multirun/0.9.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/0.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/1.11.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/1.9.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/5.8.2/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/6.2.0/MODULE.bazel": "not found", @@ -754,6 +759,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/6.7.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/0.7.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.1.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/4.0.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "not found", @@ -802,12 +808,20 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_testing/0.9.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/MODULE.bazel": "14c96e378c08705a46abe0799d6236fe3095c342c34f83f8d1b3f6046ce00651", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/source.json": "1a853ab23455388d550a9aecf3d8b53ec73de50e7fe2914d9269a3c698bf3624", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_devcontainer/1.11.0/MODULE.bazel": "fb6041c96e949a151307b4690ee81cf5d9254d6bf79540184b16567b04397171", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_devcontainer/1.11.0/source.json": "b379a2499cf06f16de65c2981665c3723afba9e858ed16b453f044882067f56f", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/8.0.1/MODULE.bazel": "446b0462f893021e3133b2f95672ae37dfe2ade434b6052c91530043e4d89993", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/8.1.1/MODULE.bazel": "4740f5e4229753b2ec023df5ce9c24c3472ad1e6d700a970e3e96f7347e65a1b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/8.1.1/source.json": "189c748d57a732e0653af377bb7635253ed7fc135f33c2f107c4d8a4a17259c5", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process_description/2.1.2/MODULE.bazel": "1076b36d2d05ab1a18df0746f6a545869eec6927019d651af99d99ef056e2023", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process_description/2.1.2/source.json": "f76099f076243e2641803971e2a95ed27f24756c9bdda5d10a808c664996f0fc", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/MODULE.bazel": "ade2bad4a331b02d9b7e7d9842e8de8c6fded6186486e02c4f7db5cd4b71d34d", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/source.json": "fbcbc738e652b0c68d5d28dd1db09f2e643dc111f5739b2f6af7ec56c2e88043", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/MODULE.bazel": "535296e6cdef55a506c580ca2ce541aa9ddefb354de1a24ab2bd4addc939282b", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/source.json": "8bda773be264da16d2a82a03ebb737421dd4a35855f1e9a5d03d9722d84c1df5", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_tooling/2.2.0/MODULE.bazel": "178ba4862246b6ba2bbcd96b7e9e728299b19fb94bcf23d315dc2299aabf7178", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_tooling/2.2.0/source.json": "a76f2d093cff26d5b256ce531bb2f69b6c667c968f99a156327fd194d4f36e61", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/sphinxdocs/2.2.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.3/MODULE.bazel": "not found", diff --git a/README.md b/README.md index 3d0a43b..b7484c1 100644 --- a/README.md +++ b/README.md @@ -13,334 +13,64 @@ # coverage_tool — Bazel module `score_coverage` -LLVM source-based code coverage pipeline for Eclipse S-CORE, developed as a -software tool under the S-CORE tool-management process (ISO 26262-8 clause 11). -This repository is the successor of `@score_tooling//coverage`. +LLVM source-based code coverage pipeline for Eclipse S-CORE: one +`bazel coverage` run gives one line and branch coverage report for C++ and +Rust, untested in-scope files at exact 0 %, reviewed justifications with an +effective-coverage metric, and a CI threshold gate. -Repository layout: +The tool is developed as a software tool under the S-CORE tool-management +process (ISO 26262-8 clause 11). It is the successor of +`@score_tooling//coverage`. -- `defs.bzl`, `BUILD` — the public API (`score_coverage_scope`, - `score_coverage_reporter`, `//:merger`, `//:generate_coverage_html`, - `//:enable_llvm_coverage_for_death_tests`). -- `score_coverage/` — the implementation (Python report tooling, Starlark - rules, unit tests). -- `integration_tests/` — a self-contained consumer workspace (C++ + Rust) - exercised end to end by `run_integration_test.sh`; it is also the reference - implementation for the adoption guide below. -- `COVERAGE_GUIDE.md` — how the pipeline works internally. - -# Adoption guide - -Reusable **LLVM source-based coverage pipeline** for S-CORE repositories: - -- **One report for C++ and Rust** (line + branch coverage), produced by - `llvm-cov` directly from covmap instrumentation — no gcov/genhtml. -- **Untested in-scope files appear at exact 0%** — since all targets are - instrumented at build time, the reporter runs `llvm-cov --empty-profile` - over the archives of libraries no test links against. No heuristics; the - line/branch denominators come from the compiler's own coverage map. -- **Justification system**: `COV_JUSTIFIED` in-code markers + a YAML database - turn intentionally-uncovered lines into *justified* lines, tracked in an - **effective coverage** metric with stale-justification detection. -- **Gating**: the report generator exits non-zero when effective coverage is - below `COVERAGE_THRESHOLD` (default 100). - -How the pipeline works internally is documented in -[COVERAGE_GUIDE.md](COVERAGE_GUIDE.md). A complete, working consumer setup is -the [integration_tests/](integration_tests/) workspace — every snippet below -is copied from it. - -## Components - -| Target / file | Purpose | -|---|---| -| `@score_coverage//:merger` | Per-test coverage output generator (profraw → profdata + object metadata). Referenced directly from your bazelrc. | -| `@score_coverage//:reporter` | Final report generator (merged profdata → HTML + LCOV + text summary). Not referenced directly — wrapped by `score_coverage_reporter`. | -| `defs.bzl :: score_coverage_scope` | Declares WHICH targets are in scope; emits the source allowlist + baseline-archive manifest via an aspect. | -| `defs.bzl :: score_coverage_reporter` | Consumer-side wrapper wiring your scope, workspace root and LLVM tools into the reporter. | -| `@score_coverage//:generate_coverage_html` | Orchestration: unpacks the report, runs justifications, enforces the threshold, optionally archives. | -| `@score_coverage//:justify` | Parses the justification YAML + in-code markers into a manifest. | -| `@score_coverage//:effective_coverage` | Post-processes the HTML: restyles justified lines, computes effective coverage, detects stale justifications. | -| `@score_coverage//:coverage_summary` | Renders the markdown job summary from the LCOV data (invoked by `generate_coverage_html` for `--summary-md` / `GITHUB_STEP_SUMMARY`). | -| `@score_coverage//:enable_llvm_coverage_for_death_tests` | `cc_feature` adding `-mllvm -runtime-counter-relocation` (continuous-mode profiling for death tests). | - -## Prerequisites - -1. A Bzlmod workspace (`MODULE.bazel`). -2. Linux x86_64 host (the pipeline runs on the host platform; do not combine - with QNX/cross platform configs). -3. For Rust: a Ferrocene toolchain built by `ferrocene_toolchain_builder` - **>= 1.3.1** (its coverage-tools tarball ships `llvm-cov`/`llvm-profdata` - built from the same LLVM as rustc) wired through `score_toolchains_rust` - **>= 0.10.0**. - -## 1. Depend on score_coverage - -```starlark -bazel_dep(name = "score_coverage", version = "") -``` - -Add one line to your **root** `BUILD` file so the reporter can locate your -workspace root at runtime: - -```starlark -exports_files(["MODULE.bazel"]) -``` - -## 2. Declare the coverage toolchains (MODULE.bazel) - -```starlark -bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) -bazel_dep(name = "toolchains_llvm", version = "1.8.0", dev_dependency = True) - -llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) -llvm.toolchain( - cxx_standard = {"": "c++17"}, - extra_known_features = [ - "@score_coverage//:enable_llvm_coverage_for_death_tests", - ], - llvm_version = "22.1.7", - stdlib = {"": "stdc++"}, -) -use_repo(llvm, "llvm_toolchain", "llvm_toolchain_llvm") -``` - -For Rust, **no coverage-specific toolchain is needed**: the standard -toolchains shipped by score_toolchains_rust >= 0.10.0 already attach -`llvm-cov`/`llvm-profdata` (from the ferrocene_toolchain_builder >= 1.3.1 -coverage-tools tarball, built from the same LLVM as rustc). Just register the -standard toolchain as usual: - -``` -common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu -``` - -rules_rust only instruments crates when the `rust_toolchain` declares -`llvm_cov` — a Ferrocene toolchain from an older score_toolchains_rust (or a -custom instance without `coverage_tools_url`) silently produces no Rust -coverage. - -## 3. Declare scope and reporter (BUILD) - -In e.g. `tools/coverage/BUILD`: - -```starlark -load("@score_coverage//:defs.bzl", "score_coverage_reporter", "score_coverage_scope") - -score_coverage_scope( - name = "coverage_scope", - testonly = True, - deps = [ - "//src/mylib", # cc_library - "//src/rust/mycrate", # rust_library - "//src/rust/tool:tool", # rust_binary - ], -) - -score_coverage_reporter( - name = "reporter_wrapper", - testonly = True, - coverage_scope = ":coverage_scope", - llvm_cov = "@llvm_toolchain//:llvm-cov", - llvm_profdata = "@llvm_toolchain//:llvm-profdata", - llvm_cxxfilt = "@llvm_toolchain_llvm//:bin/llvm-cxxfilt", -) -``` - -The scope aspect walks the listed targets and their transitive in-workspace -deps, collecting source files (allowlist) and compiled archives (baselines). -Everything in scope but untested shows up at 0%; everything outside the scope -(tests, mocks, external deps) is filtered out of the report. - -## 4. Import the bazelrc config - -Copy the `coverage:llvm_cov` block from -[integration_tests/.bazelrc](integration_tests/.bazelrc) into your -repository's bazelrc (directly or via `import`). If your `.bazelrc` ends with -a `try-import %workspace%/user.bazelrc` (or similar local-override file), -place the coverage import BEFORE it — bazelrc conflicts resolve last-wins, -and the local override file must stay last to keep working. The two labels -to adapt: - -``` -coverage:llvm_cov --coverage_output_generator=@score_coverage//:merger -coverage:llvm_cov --coverage_report_generator=//tools/coverage:reporter_wrapper -``` - -The merger reference points into score_coverage as-is; the reporter_wrapper -label is the target you declared in step 3. - -> **Do NOT combine `--config=llvm_cov` with configs that append other -> `--extra_toolchains`** (e.g. a GCC host config): the last toolchain wins -> resolution and a GCC toolchain produces no covmap data. - -## 5. (Optional) Set up justifications +## Documentation -`tools/coverage/coverage_justifications.yaml`: +The documentation is docs-as-code under [`docs/`](docs/) and published at +: -```yaml -version: 1 -justifications: - - id: hw-unreachable-on-x86 - category: platform_specific - platforms: [linux] - reason: | - ARM-only error path; cannot be exercised by x86 CI. -``` - -Mark the code in place: - -```cpp -return false; // COV_JUSTIFIED hw-unreachable-on-x86 +- **User manual** — adoption guide (six steps), command reference, + [constraints of use](docs/manual/constraints.rst) and + [known problems](docs/manual/known_problems.rst). +- **Requirements** — use cases, potential errors (HazOp style) and the tool + requirements the tests verify. +- **Architecture** — the two-phase pipeline and the module/consumer split. +- **Verification report** — test inventory, structural coverage of the tool, + static analysis, end-to-end validation, deviations. -// or a region: -// COV_JUSTIFIED_START hw-unreachable-on-x86 -if (running_on_arm()) { ... } -// COV_JUSTIFIED_STOP -``` - -Valid categories: `defensive_programming`, `tool_false_positive`, -`platform_specific`, `other`. IDs are kebab-case. Justified lines render -orange in the HTML and count as covered in the *effective* metric; a -justification on a line that is meanwhile covered is flagged as **stale**. +Build it locally with `bazel run //docs:docs` (output in `docs/_build/`) or +`bazel run //docs:live_preview`. -## 6. Run it +## Quick start ```bash bazel coverage --config=llvm_cov //... --build_tests_only - bazel run @score_coverage//:generate_coverage_html -- \ - --yaml tools/coverage/coverage_justifications.yaml - -# CI variant: assemble HTML + LCOV + JUnit XMLs for artifact upload, gate at 95%: -COVERAGE_THRESHOLD=95 bazel run @score_coverage//:generate_coverage_html -- \ - --yaml tools/coverage/coverage_justifications.yaml \ - --archive-dir coverage_artifacts -# then: actions/upload-artifact with path: coverage_artifacts -# (upload-artifact zips its input itself — use --archive only when you -# want a local .zip; uploading that zip would nest it in a second zip) + --yaml tools/coverage/coverage_justifications.yaml --archive-dir coverage_artifact ``` -`--yaml` is optional: without it, justification processing is skipped and the -`COVERAGE_THRESHOLD` gate applies to the **raw** line coverage. Start without -a YAML; add one (`version: 1` + `justifications: []`) when you introduce your -first `COV_JUSTIFIED` marker. - -**GitHub job summary:** inside GitHub Actions no extra flags are needed — when -`GITHUB_STEP_SUMMARY` is set (and `--summary-md` is not given), a markdown -summary is appended to the workflow run page automatically: overall -line/branch/file tables with progress bars, raw-vs-effective when a -justification YAML is in play, a per-directory rollup (worst first), and -collapsible lists of the least-covered and exact-0% files. Outside Actions, -pass `--summary-md ` to write the same summary to a file. The summary is -emitted before the threshold gate decides the exit code, so a failing gate -still leaves it on the run page. No consumer-side LCOV parsing needed. - -`--build_tests_only` matters: without it, coverage builds (not runs) every -target matched by the pattern, including e.g. `manual`-tagged or -platform-incompatible test binaries. - -## Customization knobs - -| Need | Knob | -|---|---| -| Different gate | `COVERAGE_THRESHOLD=` env var (default 100; exit 1 below; gates effective coverage with `--yaml`, raw coverage without) | -| Output directory | positional `output-dir` argument (default `coverage_`) | -| Platform-specific justifications | `--platform linux\|qnx` (default linux) | -| JUnit XMLs subtree in the archive | `--testlogs-subdir ` (default: whole `bazel-testlogs`) | -| Markdown job summary | `--summary-md `; auto-append to `GITHUB_STEP_SUMMARY` when the flag is absent and the variable is set | -| Different LLVM version | your own `llvm.toolchain(...)`; pass its labels in step 3 | -| Rust branch coverage | `-Zcoverage-options=branch` (needs a nightly-based/rolling Ferrocene; drop the flag on stable) | +Exit codes: `0` gate passed, `1` gate failed, `2` no verdict possible. -## Troubleshooting +## Repository layout -| Symptom | Cause | -|---|---| -| `... is not the LLVM pipeline zip report` | The coverage run used the default lcov path — the `--config=llvm_cov` flags (or your bazelrc import) were not active. | -| No `.rs` files in the report | The Ferrocene toolchain in use has no `llvm_cov` attached (missing `coverage_tools_url`, or a non-coverage toolchain instance won resolution). | -| No C++ files / empty covmap | A GCC toolchain won toolchain resolution — check for conflicting `--extra_toolchains` from another config. | -| `Neither __llvm_profile_counter_bias nor ...` in test logs, no profraw | Continuous mode without runtime counter relocation: the `enable_llvm_coverage_for_death_tests` feature (C++) or the `-Cllvm-args=-runtime-counter-relocation` rustc flag is missing. | -| `no coverage data found` on a Rust archive | Handled automatically (rlib expansion); if you see it, the reporter predates the rlib fix. | -| `error[E0463]: can't find crate for profiler_builtins` | The Ferrocene sysroot lacks profiler_builtins (builder < 1.3.1, or a miri sysroot leaked into coverage builds). | -| `the following arguments are required: --workspace_root` | You pointed `--coverage_report_generator` at `:reporter` directly instead of your `score_coverage_reporter` target. | -| Coverage numbers differ between runs on identical code | Dynamic-linking instrumentation clash — ensure `--dynamic_mode=off` from the bazelrc block is active. | - -## Migration from the removed Ferrocene symbol-report/blanket flow - -`rust_coverage_report`, `//coverage:ferrocene_report` and its helper scripts -were removed. Replace: - -- `bazel run //:rust_coverage` → steps 1–6 above (one report for both - languages, exact untested-file entries, justifications, effective gate). -- `test:ferrocene-coverage --run_under=@score_coverage//:llvm_profile_wrapper` - is no longer needed — Bazel's own coverage collection sets - `LLVM_PROFILE_FILE`. The wrapper target still exists for repositories that - have not migrated yet. - ---- - -## Repository-internal: Combined Rust + Python Coverage - -The `//coverage:combined_report` target generates a single HTML coverage report -for all Rust and Python tools in the repository using Bazel's built-in -coverage support (`bazel coverage`) and `genhtml`. - -### Usage - -```bash -bazel run //coverage:combined_report -``` - -This runs `bazel coverage --config=coverage` for `//plantuml/...`, -`//validation/...` and `//manual_analysis/...`, merges all LCOV data, and -renders the report to `/coverage-html/index.html`. - -Custom output directory: - -```bash -bazel run //coverage:combined_report -- --out-dir /tmp/my-coverage -``` - -Custom target set: - -```bash -bazel run //coverage:combined_report -- --targets "//plantuml/... //validation/core/..." -``` - -### How it works - -1. `bazel coverage --config=coverage` compiles Rust with `-Cinstrument-coverage` - and wraps Python tests with `coverage.py` (via `rules_python`'s built-in - `configure_coverage_tool`). -2. Bazel merges all per-test LCOV files into one `_coverage_report.dat` - (controlled by `--combined_report=lcov`). -3. `--instrumentation_filter` limits instrumentation to the three tool - packages, excluding external dependencies and generated code. -4. Test infrastructure files (`integration_test/`, `tests/`) are excluded from - instrumentation via `--instrumentation_filter`; external Python files are - removed via `lcov --remove`. -5. The HTML report uses a high-coverage threshold of **95 %** (green) and the - default medium threshold of 75 % (yellow). -6. `genhtml` and `lcov` are downloaded hermetically via the `download_utils` - Bazel module (`@lcov_deb`) — no system installation of `lcov` is required. - -### .bazelrc config - -The `coverage:coverage` config in `.bazelrc` provides the required flags: - -``` -coverage:coverage --combined_report=lcov -coverage:coverage --instrumentation_filter=//plantuml,//validation,//manual_analysis,-//plantuml/parser/integration_test,-//validation/core/integration_test -coverage:coverage --@rules_rust//rust/settings:extra_rustc_flag=-Clink-dead-code -coverage:coverage --@rules_rust//rust/settings:extra_rustc_flag=-Ccodegen-units=1 -``` +- `defs.bzl`, `BUILD` — the public API (`score_coverage_scope`, + `score_coverage_reporter`, `//:merger`, `//:generate_coverage_html`, + `//:enable_llvm_coverage_for_death_tests`). The root package loads only + runtime dependencies so it stays loadable for consumers. +- `score_coverage/` — implementation (Python report tooling, Starlark rules) and + unit tests, including Starlark analysis tests. +- `integration_tests/` — a self-contained consumer workspace (C++ + Rust) + exercised end to end by `run_integration_test.sh` against a hand-derived + ground truth (`expected_lcov.dat`); also the reference for the adoption guide. +- `tools/` — repository hygiene (copyright, format, lint aspects) and the + self-coverage gate. +- `docs/` — the docs-as-code tree. -You can also run `bazel coverage` directly without the script (requires `genhtml` -from the system `lcov` package): +## Development ```bash -bazel coverage --config=coverage //plantuml/... //validation/... //manual_analysis/... -genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat" \ - --output-directory coverage-html/ +bazel test //score_coverage/... //tools/... # unit + analysis tests +bazel build --config=lint //score_coverage/... //tools/... # ruff, pylint, ty +bazel coverage --combined_report=lcov //score_coverage/tests:all +bazel run //tools:self_coverage_gate -- --min-lines 95 --min-branches 87 +integration_tests/run_integration_test.sh # end-to-end (downloads LLVM + Ferrocene) +bazel run //tools:format.fix && bazel run //tools:copyright.check ``` - diff --git a/docs/BUILD b/docs/BUILD new file mode 100644 index 0000000..c33db90 --- /dev/null +++ b/docs/BUILD @@ -0,0 +1,24 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@score_docs_as_code//:docs.bzl", "docs") + +# Documentation (docs-as-code). Lives in its own package on purpose: the root +# package must not load from dev dependencies (consumers do not have them). +# Targets: //docs:docs, //docs:incremental, //docs:live_preview, //docs:ide_support. +docs( + external_needs = ["@score_process_description//:needs_json"], + project = "score_coverage", + project_url = "https://eclipse-score.github.io/coverage_tool", + source_dir = ".", +) diff --git a/docs/architecture/index.rst b/docs/architecture/index.rst new file mode 100644 index 0000000..1353b6a --- /dev/null +++ b/docs/architecture/index.rst @@ -0,0 +1,169 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Architecture +============ + +.. document:: score_coverage architecture + :id: doc__coverage_architecture + :version: 1 + :status: draft + :safety: ASIL_B + :security: NO + :realizes: wp__sw_implementation + +The pipeline replaces Bazel's two coverage hooks, ``--coverage_output_generator`` +and ``--coverage_report_generator``, with its own tools and adds a +justification and gating layer on top. It has two phases. + +.. uml:: + + @startuml + skinparam componentStyle rectangle + package "Phase 1: bazel coverage --config=llvm_cov" { + [Clang / rustc\ncovmap instrumentation] --> [test binaries] + [test binaries] --> (profraw per test) + (profraw per test) --> [merger.py\n--coverage_output_generator] + [merger.py\n--coverage_output_generator] --> (coverage.dat zip\nprofdata + meta.json) + [score_coverage_scope\naspect] --> (allowlist.txt\nobjects.txt) + (coverage.dat zip\nprofdata + meta.json) --> [reporter.py\n--coverage_report_generator] + (allowlist.txt\nobjects.txt) --> [reporter.py\n--coverage_report_generator] + [reporter.py\n--coverage_report_generator] --> (_coverage_report.dat zip\nhtml_report, lcov_report, text_report) + } + package "Phase 2: bazel run //:generate_coverage_html" { + (_coverage_report.dat zip\nhtml_report, lcov_report, text_report) --> [generate_coverage_html.py] + [generate_coverage_html.py] --> [justify.py] + [justify.py] --> (manifest.json) + (manifest.json) --> [effective_coverage.py] + [effective_coverage.py] --> (report.json\nsummary.txt) + [generate_coverage_html.py] --> [coverage_summary.py] + [generate_coverage_html.py] --> (gate verdict\nexit 0 / 1 / 2) + [generate_coverage_html.py] --> (archive dir) + } + @enduml + +Phase 1: collection +------------------- + +The ``llvm_cov`` bazelrc config, copied by the consumer from the integration +workspace, does four things: + +1. **Swaps the compilers.** C++ is compiled with a hermetic Clang/LLVM toolchain + instead of GCC; Rust with the Ferrocene toolchain of ``score_toolchains_rust``, + which has LLVM coverage tools attached. Both emit the same covmap format. +2. **Turns on instrumentation.** ``--experimental_use_llvm_covmap`` plus the + ``coverage`` feature for C++; ``rules_rust`` adds ``-Cinstrument-coverage`` to + rustc once the toolchain declares coverage tools. Runtime counter relocation + (``-mllvm -runtime-counter-relocation`` via the ``cc_feature``, + ``-Cllvm-args=-runtime-counter-relocation`` for Rust) enables continuous mode + so coverage survives abnormal termination. Rust branch regions need + ``-Zcoverage-options=branch`` on a rolling Ferrocene. +3. **Installs the per-test tool.** ``merger.py`` merges the test's ``profraw`` + files with ``llvm-profdata``, records the instrumented objects, and zips both + as the test's ``coverage.dat``. +4. **Installs the final tool.** The consumer's ``score_coverage_reporter`` target + wraps ``reporter.py`` with the scope, workspace root and LLVM tool labels. + The reporter merges all per-test profiles and runs ``llvm-cov`` three times: + ``show`` (HTML), ``export`` (LCOV), ``report`` (text). + +**Scope.** Covmap instruments everything. Filtering happens at report time +through the allowlist written by ``score_coverage_scope``: an aspect walks the +dependency graph from the listed production targets and collects every +in-workspace source file they own. Everything else, test sources, googletest, +external dependencies, is excluded. + +**Baseline.** A file that no test executes produces no profile data. The scope +aspect therefore also collects the compiled archives and executables, and the +reporter runs ``llvm-cov --empty-profile`` over them, so untested files show up +with exact 0 % entries whose denominators come from the compiler's coverage +map. Rust rlibs are expanded into their object members first because their +leading ``lib.rmeta`` member makes ``llvm-cov`` reject the archive. + +Phase 2: report generation and gate +----------------------------------- + +``generate_coverage_html.py`` unpacks the HTML from the zip and, when a +justification YAML is given, calls ``justify.py`` (YAML plus in-code markers to +a manifest of justified lines) and ``effective_coverage.py`` (recolours justified +lines, computes raw and effective figures, flags stale justifications, writes +``report.json`` and ``summary.txt``). The markdown summary is written next, then +the gate compares the unrounded gated percentage against ``COVERAGE_THRESHOLD``. +Optionally the HTML, LCOV, justification report and JUnit XMLs are assembled +into an artifacts tree. + +Exit code 2 is reserved for runs without a verdict, so a broken report, a bad +threshold or a tool failure can never look like a pass. + +Module and consumer split +------------------------- + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Lives in ``@score_coverage`` + - Role + * - ``score_coverage/merger.py`` + - per-test profraw to profdata; C++ ``objects_list.txt`` and Rust ELF + manifest discovery + * - ``score_coverage/reporter.py`` + - final merge, llvm-cov show/export/report, allowlist filtering, + ``--empty-profile`` baselines, rlib expansion, path normalisation + * - ``score_coverage/coverage_scope.bzl`` + - the scope aspect and rule (CcInfo and CrateInfo) + * - ``score_coverage/reporter_wrapper.bzl``, ``defs.bzl`` + - the consumer-facing ``score_coverage_scope`` / ``score_coverage_reporter`` + API + * - ``score_coverage/justify.py``, ``effective_coverage.py``, + ``coverage_summary.py``, ``generate_coverage_html.py`` + - justification, summary and gating layer + * - ``//:enable_llvm_coverage_for_death_tests`` + - ``cc_feature`` for continuous-mode profiling + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Lives in the consumer repository + - Why it cannot move + * - ``score_coverage_scope(deps = [...])`` + - names the repository's production targets + * - ``score_coverage_reporter(...)`` + - carries the repository's LLVM tool labels and workspace root + * - ``coverage_justifications.yaml`` + - reviewed, repository-specific engineering arguments + * - MODULE.bazel toolchain blocks + - LLVM and Ferrocene pins are per-repository decisions + * - the ``coverage:llvm_cov`` bazelrc block + - bazelrc cannot be imported across modules + +Two wiring details make the external hosting work: every path in the generated +reporter launcher uses rlocation form because it mixes files from the consumer +(``_main``), ``score_coverage`` and toolchain repositories, and the baseline +manifest is resolved against ``_main`` explicitly because its entries are +consumer files. + +Design decisions +---------------- + +- **Report-time filtering, not instrumentation filtering.** Instrumenting + everything and filtering by allowlist is what makes exact 0 % baselines + possible; ``--instrumentation_filter`` would hide untested files. +- **Fail loud, never fail green.** Every input problem ends in exit 2. The gate + compares unrounded values and floors displayed percentages. +- **Gate on the LCOV, not on llvm-cov's text summary.** The text summary omits + baseline-only files; the LCOV includes them. +- **In-process tool calls.** ``generate_coverage_html`` imports the justification + tools instead of nesting ``bazel run``; this keeps one process, one exit code + and testable seams. diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..9911690 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,50 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +score_coverage +============== + +``score_coverage`` is the LLVM source-based code coverage pipeline of Eclipse +S-CORE: one ``bazel coverage`` run produces one line and branch coverage report +for C++ and Rust, lists every in-scope file that no test executes at exactly +0 %, tracks reviewed justifications in an effective-coverage metric and gates +CI on a threshold. + +The tool produces the structural-coverage evidence (statement and branch +coverage, C0 and C1) that S-CORE module verification reports rely on. It is +therefore developed and documented as a software tool under the S-CORE +tool-management process (ISO 26262-8, clause 11); its Tool Verification Report +lives in the S-CORE platform documentation and links here. + +.. toctree:: + :maxdepth: 2 + :caption: Contents + + manual/index + requirements/index + architecture/index + verification/index + release/index + +Quick reference +--------------- + +.. code-block:: bash + + bazel coverage --config=llvm_cov //... --build_tests_only + bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml --archive-dir coverage_artifact + +Exit codes of ``generate_coverage_html``: ``0`` gate passed, ``1`` gate failed, +``2`` no verdict possible (broken input, invalid threshold, tool failure). diff --git a/docs/manual/constraints.rst b/docs/manual/constraints.rst new file mode 100644 index 0000000..e9e8db8 --- /dev/null +++ b/docs/manual/constraints.rst @@ -0,0 +1,118 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +.. _coverage_constraints: + +Constraints of use +================== + +The correctness of the coverage figures depends on the environment and on how +the tool is invoked. Each constraint below mitigates one of the potential errors +in :doc:`../requirements/potential_errors`; the Tool Verification Report refers +to them by their anchors. + +.. _cstr_coverage_environment: + +CSTR-01 Qualified environment +----------------------------- + +Use the tool only in the environment it was validated in: Linux x86_64 host, +Bazel 8.x, ``toolchains_llvm`` 1.8.0 with LLVM 22.1.7 for C++, the standard +Ferrocene toolchain of ``score_toolchains_rust`` 0.10.0 or newer (built by +``ferrocene_toolchain_builder`` 1.3.1 or newer) for Rust. QNX on-target coverage +is outside this environment. Mitigates ERR-08. + +.. _cstr_coverage_scope_list: + +CSTR-02 Maintain the scope list +------------------------------- + +List every shipped production library or binary in ``score_coverage_scope``. +A target missing from the list is not reported at all, not even at 0 %. Add +new libraries when they are created; document exclusions in writing next to the +target. For large repositories, run a completeness check comparing the +reachable universe against the scope. Mitigates ERR-01. + +.. _cstr_coverage_build_tests_only: + +CSTR-03 Run coverage with ``--build_tests_only`` +------------------------------------------------ + +Always invoke ``bazel coverage --config=llvm_cov --build_tests_only``. +Without the flag Bazel builds manual-tagged or incompatible test binaries, which +fails or pollutes the run. Mitigates ERR-08. + +.. _cstr_coverage_toolchain_config: + +CSTR-04 Do not combine with other toolchain configs +--------------------------------------------------- + +Never combine ``--config=llvm_cov`` with a config that registers another C++ +toolchain (for example a GCC host config). The last ``--extra_toolchains`` wins +resolution; a GCC toolchain produces no covmap data and the run is rejected +with exit 2. Mitigates ERR-08. + +.. _cstr_coverage_dynamic_mode: + +CSTR-05 Keep ``--dynamic_mode=off`` +----------------------------------- + +The canonical ``llvm_cov`` config disables dynamic linking. Per-test shared +objects can carry clashing instrumentation between production and test code, +and the first object loaded wins, causing flaky coverage gaps. Mitigates ERR-02. + +.. _cstr_coverage_lockfile: + +CSTR-06 Pin dependencies +------------------------ + +Run CI with ``--lockfile_mode=error`` so the LLVM and Ferrocene versions that +produced the evidence cannot drift silently. Mitigates ERR-08 and ERR-09. + +.. _cstr_coverage_review_justifications: + +CSTR-07 Review justifications +----------------------------- + +A justification is a reviewed engineering argument. Every entry in the YAML +needs an id, a category, the platforms it applies to and a written reason, and +every change to the YAML or to ``COV_JUSTIFIED`` markers is reviewed like code. +Remove justifications the report flags as stale. Mitigates ERR-04 and ERR-05. + +.. _cstr_coverage_check_baselines: + +CSTR-08 Check the baseline entries +---------------------------------- + +When a file is known to be untested, confirm it appears in the LCOV +(``coverage_report.dat``) with an ``SF:`` record and ``LH:0``. If an expected +file is missing entirely, the baseline mechanism is broken; stop and diagnose +instead of accepting the percentage. Mitigates ERR-01 and ERR-07. + +.. _cstr_coverage_archive: + +CSTR-09 Archive the report with the verification report +------------------------------------------------------- + +Use ``--archive-dir`` and upload the directory as the CI artifact. The archived +``coverage_report.dat`` and ``justification_report/`` are the evidence a module +verification report cites; the HTML alone is not sufficient. Mitigates ERR-09. + +.. _cstr_coverage_exit_codes: + +CSTR-10 Treat exit code 2 as a failed run +----------------------------------------- + +Exit code 2 means the tool could not produce a verdict. CI must fail on it +exactly like on exit code 1. Never map it to a pass. Mitigates ERR-03. diff --git a/docs/manual/index.rst b/docs/manual/index.rst new file mode 100644 index 0000000..faeec19 --- /dev/null +++ b/docs/manual/index.rst @@ -0,0 +1,23 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +User manual +=========== + +.. toctree:: + :maxdepth: 2 + + user_manual + constraints + known_problems diff --git a/docs/manual/known_problems.rst b/docs/manual/known_problems.rst new file mode 100644 index 0000000..c418afc --- /dev/null +++ b/docs/manual/known_problems.rst @@ -0,0 +1,68 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Known problems +============== + +Known problems of the current release, with detection and workaround. Entries +are removed when a release fixes them; problems in the underlying LLVM tools +stay listed with their upstream references. + +.. list-table:: + :header-rows: 1 + :widths: 25 35 40 + + * - Problem + - Detection + - Workaround / status + * - **Per-instantiation counting in llvm-cov.** Template-heavy C++ files can + show lower line coverage than under gcov; ``LF``/``LH`` totals may + disagree with the file's own ``DA`` rows. + - Compare the per-file summary with the ``DA`` rows of the LCOV. + - Upstream behaviour (llvm-project #93843, #111743, #119299). The error is + towards **under**-reporting; numbers are reported as llvm-cov produces + them. + * - **Rust branch coverage needs an unstable rustc flag.** + ``-Zcoverage-options=branch`` works on rolling (nightly-based) Ferrocene + builds only. + - Rust rows show ``-`` in the branch columns. + - On a stable-channel Ferrocene drop the flag; Rust branch coverage is then + not available. + * - **Clang warns where GCC does not.** Repositories with + ``treat_warnings_as_errors`` fail to compile under the coverage config. + - ``-Werror`` failures only under ``--config=llvm_cov``. + - Add ``--features=-treat_warnings_as_errors`` and + ``--host_features=-treat_warnings_as_errors`` to the coverage config. + * - **Containerised tests produce no coverage.** Instrumented binaries inside + stock containers lack runtime dependencies and profraw files never reach + the host. + - Exit 127 in the test log; no profraw. + - Exclude containerised or system tests from the coverage run; they keep + running in the regular test jobs. + * - **QNX on-target coverage is not supported by this tool.** The + orchestrator accepts only the LLVM zip report. The gcovr-based HTML + post-processing exists but is reachable only through the consumer-side + flow of the ``communication`` repository (tooling issue #427). + - Exit 2 with ``is not the LLVM pipeline zip report`` on a gcov run. + - Use the Linux host pipeline; QNX centralisation is tracked in tooling + issue #427. + * - **Rust rlib archives are rejected by llvm-cov** because of the leading + ``lib.rmeta`` member. + - ``no coverage data found`` on a Rust archive. + - Handled since the pipeline expands rlibs into their object members; if + seen, the installed version predates the fix. + * - **Instrumentation filter appears ignored.** + - ``--instrumentation_filter`` has no visible effect. + - Expected: ``--experimental_use_llvm_covmap`` instruments everything; + filtering happens at report time through the scope allowlist. diff --git a/docs/manual/user_manual.rst b/docs/manual/user_manual.rst new file mode 100644 index 0000000..39eedc1 --- /dev/null +++ b/docs/manual/user_manual.rst @@ -0,0 +1,268 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Adoption guide +============== + +.. document:: score_coverage user manual + :id: doc__coverage_user_manual + :version: 1 + :status: draft + :safety: ASIL_B + :security: NO + :realizes: wp__sw_development_plan + +What the pipeline provides +-------------------------- + +- **One report for C++ and Rust** (line and branch coverage), produced by + ``llvm-cov`` directly from covmap instrumentation, without gcov or genhtml. +- **Untested in-scope files appear at exact 0 %**: all targets are instrumented + at build time, and the reporter runs ``llvm-cov --empty-profile`` over the + archives of libraries no test links against. The denominators come from the + compiler's own coverage map, not from a source-text heuristic. +- **Justifications**: ``COV_JUSTIFIED`` in-code markers plus a YAML database turn + intentionally uncovered lines into *justified* lines, tracked in an + **effective coverage** metric with stale-justification detection. +- **Gating**: the report generator exits non-zero when the gated coverage is + below ``COVERAGE_THRESHOLD`` (default 100). + +A complete working consumer setup is the ``integration_tests/`` workspace of the +repository; every snippet below is taken from it. + +Components +---------- + +.. list-table:: + :header-rows: 1 + :widths: 35 65 + + * - Target + - Purpose + * - ``@score_coverage//:merger`` + - Per-test coverage output generator (profraw to profdata plus object + metadata). Referenced directly from the consumer's bazelrc. + * - ``score_coverage_scope`` (``defs.bzl``) + - Declares which targets are in scope; emits the source allowlist and the + baseline-archive manifest through an aspect. + * - ``score_coverage_reporter`` (``defs.bzl``) + - Consumer-side wrapper wiring scope, workspace root and LLVM tools into + the final report generator. + * - ``@score_coverage//:generate_coverage_html`` + - Orchestration: unpacks the report, applies justifications, writes the + summary, enforces the threshold, optionally archives. + * - ``@score_coverage//:justify``, ``//:effective_coverage``, ``//:coverage_summary`` + - Standalone entry points of the justification and summary layer (called + in-process by ``generate_coverage_html``). + * - ``@score_coverage//:enable_llvm_coverage_for_death_tests`` + - ``cc_feature`` adding ``-mllvm -runtime-counter-relocation`` (continuous + mode profiling for death tests). + +Prerequisites +------------- + +1. A Bzlmod workspace (``MODULE.bazel``). +2. A Linux x86_64 host. The pipeline runs on the host platform; do not combine + it with QNX or other cross-platform configs. +3. For Rust: a Ferrocene toolchain built by ``ferrocene_toolchain_builder`` + 1.3.1 or newer, wired through ``score_toolchains_rust`` 0.10.0 or newer. + Its coverage-tools tarball ships ``llvm-cov`` and ``llvm-profdata`` built + from the same LLVM as ``rustc``. + +Step 1: depend on score_coverage +-------------------------------- + +.. code-block:: starlark + + bazel_dep(name = "score_coverage", version = "") + +Add one line to the **root** ``BUILD`` file so the reporter can locate the +workspace root at runtime: + +.. code-block:: starlark + + exports_files(["MODULE.bazel"]) + +Step 2: declare the coverage toolchains +--------------------------------------- + +.. code-block:: starlark + + bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) + bazel_dep(name = "toolchains_llvm", version = "1.8.0", dev_dependency = True) + + llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) + llvm.toolchain( + cxx_standard = {"": "c++17"}, + extra_known_features = ["@score_coverage//:enable_llvm_coverage_for_death_tests"], + llvm_version = "22.1.7", + stdlib = {"": "stdc++"}, + ) + use_repo(llvm, "llvm_toolchain", "llvm_toolchain_llvm") + +For Rust no coverage-specific toolchain is needed. Register the standard +Ferrocene toolchain as usual: + +.. code-block:: text + + common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu + +``rules_rust`` only instruments crates when the ``rust_toolchain`` declares +``llvm_cov``. A Ferrocene toolchain from an older ``score_toolchains_rust``, or a +custom instance without ``coverage_tools_url``, silently produces no Rust +coverage (see :ref:`coverage_constraints`). + +Step 3: declare scope and reporter +---------------------------------- + +In ``tools/coverage/BUILD``: + +.. code-block:: starlark + + load("@score_coverage//:defs.bzl", "score_coverage_reporter", "score_coverage_scope") + + score_coverage_scope( + name = "coverage_scope", + testonly = True, + deps = [ + "//src/mylib", # cc_library + "//src/rust/mycrate", # rust_library + "//src/rust/tool:tool", # rust_binary + ], + ) + + score_coverage_reporter( + name = "reporter_wrapper", + testonly = True, + coverage_scope = ":coverage_scope", + llvm_cov = "@llvm_toolchain//:llvm-cov", + llvm_profdata = "@llvm_toolchain//:llvm-profdata", + llvm_cxxfilt = "@llvm_toolchain_llvm//:bin/llvm-cxxfilt", + ) + +The scope aspect walks the listed targets and their transitive in-workspace +dependencies, collecting source files (allowlist) and compiled archives +(baselines). Everything in scope but untested shows up at 0 %; everything +outside the scope (tests, mocks, external dependencies) is filtered out of the +report. The scope list is the written record of what is covered: a production +library missing from it silently vanishes from the report, so every new +library must be added, and exclusions need a written decision. + +Step 4: import the bazelrc config +--------------------------------- + +Copy the ``coverage:llvm_cov`` block from ``integration_tests/.bazelrc`` into the +repository's bazelrc, directly or via ``import``. Place the import **before** any +``try-import %workspace%/user.bazelrc``: bazelrc resolves last-wins and the local +override file must stay last. The two labels to adapt: + +.. code-block:: text + + coverage:llvm_cov --coverage_output_generator=@score_coverage//:merger + coverage:llvm_cov --coverage_report_generator=//tools/coverage:reporter_wrapper + +Do **not** combine ``--config=llvm_cov`` with configs that append other +``--extra_toolchains`` (for example a GCC host config): the last toolchain wins +resolution and a GCC toolchain produces no covmap data. + +Step 5 (optional): justifications +--------------------------------- + +``tools/coverage/coverage_justifications.yaml``: + +.. code-block:: yaml + + version: 1 + justifications: + - id: hw-unreachable-on-x86 + category: platform_specific + platforms: [linux] + reason: | + ARM-only error path; cannot be exercised by x86 CI. + +Mark the code in place: + +.. code-block:: cpp + + return false; // COV_JUSTIFIED hw-unreachable-on-x86 + + // or a region: + // COV_JUSTIFIED_START hw-unreachable-on-x86 + if (running_on_arm()) { ... } + // COV_JUSTIFIED_STOP + +Valid categories: ``defensive_programming``, ``tool_false_positive``, +``platform_specific``, ``other``. Ids are kebab-case. Justified lines render +orange in the HTML and count as covered in the *effective* metric. A +justification on a line that is meanwhile covered is flagged as **stale**. A +marker whose id is unknown is reported as a warning and does not count. + +Step 6: run it +-------------- + +.. code-block:: bash + + bazel coverage --config=llvm_cov //... --build_tests_only + + bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml + + # CI variant: HTML + LCOV + JUnit XMLs for artifact upload, gate at 95 % + COVERAGE_THRESHOLD=95 bazel run @score_coverage//:generate_coverage_html -- \ + --yaml tools/coverage/coverage_justifications.yaml \ + --archive-dir coverage_artifacts + +``--yaml`` is optional: without it, justification processing is skipped and the +gate applies to the **raw** line coverage computed from the LCOV data. +``--build_tests_only`` is mandatory: without it, coverage builds every target +matched by the pattern, including manual-tagged or platform-incompatible test +binaries. + +Inside GitHub Actions a markdown summary is appended to ``GITHUB_STEP_SUMMARY`` +automatically when ``--summary-md`` is absent. The summary is written before the +gate decides the exit code, so a failing gate still leaves it on the run page. + +Command reference +----------------- + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Option + - Effect + * - ``COVERAGE_THRESHOLD=`` + - Minimum gated coverage in percent (default 100). Effective line coverage + with ``--yaml``, raw line coverage without. Must be a number in + ``[0, 100]``; anything else is rejected with exit 2. + * - ``--yaml `` + - Justification YAML, relative to the workspace root. + * - ``--archive-dir `` + - Assemble HTML report, ``coverage_report.dat`` (LCOV), justification + report and JUnit XMLs into ```` for artifact upload. + * - ``--archive `` + - Same content as a local ``.zip`` (do not upload it: upload-artifact + zips again). + * - ``--testlogs-subdir `` + - Subtree of ``bazel-testlogs`` whose ``test.xml`` files are archived. + * - ``--platform linux|qnx`` + - Platform filter for justifications and default output directory + ``coverage_``. + * - ``--summary-md `` + - Write the markdown summary to ```` instead of the step summary. + * - ``output-dir`` + - HTML output directory (default ``coverage_``). + +Exit codes: ``0`` gate passed, ``1`` gate failed, ``2`` no verdict possible +(missing or non-zip report, invalid threshold, justification or tool failure). diff --git a/docs/release/index.rst b/docs/release/index.rst new file mode 100644 index 0000000..9fc326e --- /dev/null +++ b/docs/release/index.rst @@ -0,0 +1,21 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Release +======= + +.. toctree:: + :maxdepth: 2 + + release_notes diff --git a/docs/release/release_notes.rst b/docs/release/release_notes.rst new file mode 100644 index 0000000..6fb298d --- /dev/null +++ b/docs/release/release_notes.rst @@ -0,0 +1,47 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Release notes +============= + +.. document:: score_coverage release notes + :id: doc__coverage_release_notes + :version: 1 + :status: draft + :safety: ASIL_B + :security: NO + :realizes: wp__module_sw_release_note + +0.1.0 (unreleased) +------------------ + +First release as a standalone module, extracted from ``@score_tooling//coverage`` +(tooling commit ``9a61f42``). + +Changes relative to the pipeline in score_tooling 2.2.x: + +- Consumer labels move to the module root: ``@score_coverage//:defs.bzl``, + ``//:merger``, ``//:generate_coverage_html``, + ``//:enable_llvm_coverage_for_death_tests``. +- ``generate_coverage_html`` is Python and returns exit code 2 for runs without + a verdict; the gate compares unrounded percentages; malformed thresholds and + corrupt LCOV data are rejected. +- Justifications match files at path-component boundaries (a justification for + ``bar.cpp`` no longer applies to ``foobar.cpp``). +- gcovr reports: the index totals of gcovr 8.x are read correctly without + ``--lcov``; ``summary.txt`` is written for gcovr reports too. +- The repository-bound ``combined_report`` and ``llvm_profile_wrapper`` helpers + are not part of the module. + +Known problems: see :doc:`../manual/known_problems`. diff --git a/docs/requirements/index.rst b/docs/requirements/index.rst new file mode 100644 index 0000000..712474a --- /dev/null +++ b/docs/requirements/index.rst @@ -0,0 +1,27 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Requirements +============ + +The tool requirements follow the chain the S-CORE tool-management process asks +for: use cases, the potential errors derived from them, and the tool +requirements that mitigate those errors. Tests link to the tool requirements. + +.. toctree:: + :maxdepth: 2 + + use_cases + potential_errors + tool_requirements diff --git a/docs/requirements/potential_errors.rst b/docs/requirements/potential_errors.rst new file mode 100644 index 0000000..1dc06bd --- /dev/null +++ b/docs/requirements/potential_errors.rst @@ -0,0 +1,128 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Potential errors +================ + +Potential errors of the tool, derived from the use cases with the HazOp guide +words *invalid*, *changed*, *more* and *less* applied to inputs, outputs and +actions. The dangerous direction for a coverage tool is always **more coverage +than real**: an error in that direction hides a verification gap. Errors +towards less coverage cost effort but no safety. + +Tool impact is *high* when the error can make a violated structural-coverage +requirement go undetected. *Detection before qualification* describes what a +user could notice without the mitigations; the last column lists the tool +requirements and constraints of use that mitigate the error. + +.. list-table:: + :header-rows: 1 + :widths: 8 32 8 12 40 + + * - Id + - Error + - Impact + - Detection + - Mitigation + * - ERR-01 + - An in-scope file is silently missing from the report (the scope aspect + misses a dependency, a ``select`` branch, or visibility hides a target). + - high + - weak + - :need:`tool_req__coverage_scope_transitive`, + :need:`tool_req__coverage_scope_baseline_objects`, + :need:`tool_req__coverage_report_baseline_zero`, + :ref:`CSTR-02 `, + :ref:`CSTR-08 ` + * - ERR-02 + - A line or branch is reported covered although it was never executed + (stale profile data, wrong object, wrong test merged, clashing + instrumentation of shared objects). + - high + - none + - :need:`tool_req__coverage_merge_profraw`, + :need:`tool_req__coverage_report_merged_profile`, + :need:`tool_req__coverage_validation_ground_truth`, + :ref:`CSTR-05 ` + * - ERR-03 + - The gate passes although the threshold is not met (rounding, parsing, + unset or malformed threshold, tool failure mistaken for a pass). + - high + - none + - :need:`tool_req__coverage_gate_threshold`, + :need:`tool_req__coverage_gate_unrounded`, + :need:`tool_req__coverage_gate_exit_codes`, + :need:`tool_req__coverage_gate_no_verdict`, + :ref:`CSTR-10 ` + * - ERR-04 + - A justification is applied to the wrong lines, to a file with the same + name, or a stale marker is counted as covered. + - high + - weak + - :need:`tool_req__coverage_just_markers`, + :need:`tool_req__coverage_just_unknown_id`, + :need:`tool_req__coverage_eff_stale`, + :need:`tool_req__coverage_eff_path_match`, + :ref:`CSTR-07 ` + * - ERR-05 + - The effective metric overstates coverage (justified and covered lines + double counted, rounding up). + - medium + - weak + - :need:`tool_req__coverage_eff_metric`, + :need:`tool_req__coverage_eff_branch_only` + * - ERR-06 + - Path normalisation maps two source files onto one record. + - medium + - weak + - :need:`tool_req__coverage_report_relative_paths`, + :need:`tool_req__coverage_eff_path_match` + * - ERR-07 + - A Rust rlib is skipped, so its crate appears complete by absence. + - high + - none + - :need:`tool_req__coverage_report_rlib_expansion`, + :need:`tool_req__coverage_validation_ground_truth`, + :ref:`CSTR-08 ` + * - ERR-08 + - The wrong toolchain wins resolution; gcov data is silently mixed in or + dropped. + - high + - weak + - :need:`tool_req__coverage_gate_no_verdict`, + :ref:`CSTR-01 `, + :ref:`CSTR-03 `, + :ref:`CSTR-04 ` + * - ERR-09 + - The report is regenerated from stale data or unpinned tool versions. + - low + - good + - :need:`tool_req__coverage_artifacts`, + :ref:`CSTR-06 `, + :ref:`CSTR-09 ` + * - ERR-10 + - Coverage is reported too low (false gaps). + - none + - good + - Informational; costs review effort only. + +Classification +-------------- + +Tool impact: **yes**. An error in the *more coverage than real* direction lets a +violation of the structural-coverage verification requirement go undetected. +Tool error detection before qualification: **no** for ERR-02, ERR-03 and +ERR-07. The expected tool confidence level is therefore **TCL LOW**, and the +qualification method of the S-CORE process, validation of the software tool, +applies. The evaluation itself is recorded in the Tool Verification Report. diff --git a/docs/requirements/tool_requirements.rst b/docs/requirements/tool_requirements.rst new file mode 100644 index 0000000..c034027 --- /dev/null +++ b/docs/requirements/tool_requirements.rst @@ -0,0 +1,459 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Tool requirements +================= + +Every requirement satisfies a use case or a process requirement and carries the +potential errors it mitigates as tags. The verification report lists the tests +that verify each requirement. + +.. needtable:: Tool requirements overview + :types: tool_req + :columns: id;title;tags;implemented + :style: table + +Scope +----- + +.. tool_req:: Transitive in-workspace sources define the scope + :id: tool_req__coverage_scope_transitive + :version: 1 + :implemented: YES + :tags: scope, ERR-01 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_scope_completeness + + ``score_coverage_scope`` shall collect, for the listed targets and their + transitive in-workspace dependencies reached through ``deps``, + ``implementation_deps``, ``exported_deps``, ``components`` and + ``implementation``, the checked-in source and header files of ``cc_library`` + and ``rust_library`` targets (``srcs``, ``hdrs``) and the ``CrateInfo`` + sources of ``rust_binary`` targets, and shall write them sorted and + deduplicated, one workspace-relative path per line, to the allowlist file. + +.. tool_req:: External and generated sources are excluded from the scope + :id: tool_req__coverage_scope_excludes + :version: 1 + :implemented: YES + :tags: scope + :safety: QM + :satisfies: stkh_req__coverage__uc_scope_completeness + + ``score_coverage_scope`` shall not list files from external repositories or + generated files in the allowlist. + +.. tool_req:: Baseline objects accompany the scope + :id: tool_req__coverage_scope_baseline_objects + :version: 1 + :implemented: YES + :tags: scope, ERR-01 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_scope_completeness + + ``score_coverage_scope`` shall list the static archives of in-workspace + ``cc_library`` and ``rust_library`` targets and the coverage-built executable + of ``rust_binary`` targets in the objects file, so the reporter can produce + zero-coverage baselines for files no test links against. + +Collection +---------- + +.. tool_req:: Per-test profiles are merged with llvm-profdata + :id: tool_req__coverage_merge_profraw + :version: 1 + :implemented: YES + :tags: collection, ERR-02 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_unified_report + + For each test, the merger shall merge all ``*.profraw`` files of the test into + one ``profdata`` file with ``llvm-profdata merge --sparse``, record the real + paths of the instrumented objects of the test in ``meta/meta.json``, and + package both into the test's coverage output. Object files come from the + ``objects_list.txt`` entries of the coverage manifest (C++) or from ELF + binaries listed in the manifest (Rust); entries under ``external/`` are + skipped. + +.. tool_req:: A test without instrumentation produces no coverage output + :id: tool_req__coverage_merge_no_data + :version: 1 + :implemented: YES + :tags: collection + :safety: QM + :satisfies: stkh_req__coverage__uc_unified_report + + When a test has no instrumented objects or no ``profraw`` files, the merger + shall exit 0 without writing an output file and shall say so on stderr, so + non-instrumented tests (for example Python tests) do not fail the run. + +.. tool_req:: A missing or failing llvm-profdata fails the test's collection + :id: tool_req__coverage_merge_tool_error + :version: 1 + :implemented: YES + :tags: collection, ERR-02 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_gate + + When ``llvm-profdata`` cannot be located through ``LLVM_PROFDATA`` or + ``RUST_LLVM_PROFDATA``, or exits non-zero, the merger shall exit 1 without + writing an output file. + +Report +------ + +.. tool_req:: One merged profile for all tests + :id: tool_req__coverage_report_merged_profile + :version: 1 + :implemented: YES + :tags: report, ERR-02 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_unified_report + + The reporter shall extract the ``profdata`` and object lists of all valid + per-test outputs, skip invalid or empty ones with a warning, merge the + profiles into one, and produce the HTML, LCOV and text reports from that + merged profile and the union of the instrumented objects. + +.. tool_req:: The report is restricted to the scope allowlist + :id: tool_req__coverage_report_allowlist + :version: 1 + :implemented: YES + :tags: report + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_scope_completeness + + The reporter shall exclude every file with coverage data that is not in the + allowlist from all three report formats. An empty allowlist shall be an + error (exit non-zero), not an empty report. + +.. tool_req:: Untested in-scope files appear at exact 0 % + :id: tool_req__coverage_report_baseline_zero + :version: 1 + :implemented: YES + :tags: report, ERR-01 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_scope_completeness + + For every allowlisted file that appears in the baseline objects but in no + test binary, the reporter shall run ``llvm-cov`` with ``--empty-profile`` over + the baseline objects and shall include the file in the HTML and LCOV output + with all instrumented lines and branches at zero hits, so that the LCOV + record shows ``LH:0``. + +.. tool_req:: Rust rlib archives are expanded into object members + :id: tool_req__coverage_report_rlib_expansion + :version: 1 + :implemented: YES + :tags: report, ERR-07 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_scope_completeness + + Before passing baseline archives to ``llvm-cov``, the reporter shall detect + archives with a ``lib.rmeta`` member and replace them by their ``.o`` + members, so that Rust libraries are not rejected as having no coverage data. + +.. tool_req:: A missing baseline object is an error + :id: tool_req__coverage_report_missing_baseline + :version: 1 + :implemented: YES + :tags: report, ERR-01 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_scope_completeness + + When an entry of the baseline objects manifest cannot be resolved to an + existing file, the reporter shall exit non-zero instead of silently dropping + the object. + +.. tool_req:: Report paths are workspace-relative + :id: tool_req__coverage_report_relative_paths + :version: 1 + :implemented: YES + :tags: report, ERR-06 + :safety: QM + :satisfies: stkh_req__coverage__uc_archive + + The reporter shall rewrite the absolute workspace root and the compiler's + ``/proc/self/cwd/`` prefix in LCOV ``SF:`` records and in HTML page titles to + workspace-relative paths, so that the archived report is portable and file + identity does not depend on the machine. + +.. tool_req:: Report contents + :id: tool_req__coverage_report_outputs + :version: 1 + :implemented: YES + :tags: report + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_unified_report + + The reporter's output shall be a zip containing ``html_report/`` (llvm-cov + HTML with branch counts), ``lcov_report/lcov.dat`` (LCOV with line and + branch records) and ``text_report/summary.txt`` (llvm-cov text summary), with + ``llvm-cov`` warnings kept out of the LCOV data. When no valid per-test + output exists, the output shall be an empty zip. + +Justifications +-------------- + +.. tool_req:: Justification YAML is validated + :id: tool_req__coverage_just_yaml + :version: 1 + :implemented: YES + :tags: justification, ERR-04 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + The justification processor shall reject, with exit 1 and a message per + finding, a YAML that is not a mapping with an integer ``version`` and a + ``justifications`` list, or that contains an entry without a kebab-case + string ``id``, a known ``category``, a non-empty list of known ``platforms`` + or a non-blank ``reason``, or with malformed ``locations``. Duplicate ids + shall be rejected. + +.. tool_req:: In-code markers + :id: tool_req__coverage_just_markers + :version: 1 + :implemented: YES + :tags: justification, ERR-04 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + The justification processor shall resolve ``COV_JUSTIFIED `` to the + marker's line and ``COV_JUSTIFIED_START `` / ``COV_JUSTIFIED_STOP`` to the + lines strictly between the markers, in checked-in sources with the + configured extensions, skipping Bazel output directories, and shall combine + them with the explicit ``locations`` of the YAML into a manifest keyed by + workspace-relative file and line. + +.. tool_req:: Unknown marker ids do not justify anything + :id: tool_req__coverage_just_unknown_id + :version: 1 + :implemented: YES + :tags: justification, ERR-04 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + A marker whose id is not in the (platform-filtered) YAML, a ``STOP`` without + ``START`` and a ``START`` without ``STOP`` shall be reported as warnings and + shall justify no line. + +.. tool_req:: Platform filter + :id: tool_req__coverage_just_platform + :version: 1 + :implemented: YES + :tags: justification + :safety: QM + :satisfies: stkh_req__coverage__uc_justifications + + When a platform is given, only justifications listing that platform shall + apply. + +.. tool_req:: Justified locations exist + :id: tool_req__coverage_just_missing_file + :version: 1 + :implemented: YES + :tags: justification, ERR-04 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + A YAML location whose file does not exist shall be reported as an error and + the processor shall exit 1 after writing the manifest. + +Effective coverage +------------------ + +.. tool_req:: Effective coverage metric + :id: tool_req__coverage_eff_metric + :version: 1 + :implemented: YES + :tags: effective, ERR-05 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + Effective line coverage shall be ``(covered + justified) / total`` and + effective branch coverage ``(covered + justified branches) / total branches``, + with the totals taken from the report's own totals (llvm-cov index page, or + the LCOV for gcovr reports), both floored to two decimals, never rounded up. + Raw figures shall be reported alongside. + +.. tool_req:: Stale justifications are reported and not counted + :id: tool_req__coverage_eff_stale + :version: 1 + :implemented: YES + :tags: effective, ERR-04 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + A justified line that is covered in any instantiation and has no uncovered + branch shall be reported as stale and shall not increase the effective + coverage. + +.. tool_req:: Branch-only justifications + :id: tool_req__coverage_eff_branch_only + :version: 1 + :implemented: YES + :tags: effective, ERR-05 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + A justified line that is covered but has a branch direction no instantiation + covers shall count its truly uncovered directions once as justified branches + and shall not count as a justified line. + +.. tool_req:: Justifications match files at path-component boundaries + :id: tool_req__coverage_eff_path_match + :version: 1 + :implemented: YES + :tags: effective, ERR-04, ERR-06 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_justifications + + A justification for a file shall apply to a report page only when the page's + source path equals the justified path or ends with it at a path-component + boundary; ``bar.cpp`` shall not apply to ``foobar.cpp``. + +.. tool_req:: Justified lines are visible in the HTML + :id: tool_req__coverage_eff_html + :version: 1 + :implemented: YES + :tags: effective + :safety: QM + :satisfies: stkh_req__coverage__uc_justifications + + Justified lines shall be restyled in the HTML report with a ``J`` marker, the + justification id and reason as tooltip and a distinct colour, the index page + shall show the effective figures, and stale justifications shall be listed in + ``summary.txt``. + +.. tool_req:: gcovr HTML reports are supported + :id: tool_req__coverage_eff_gcovr + :version: 1 + :implemented: YES + :tags: effective + :safety: QM + :satisfies: stkh_req__coverage__uc_justifications + + The post-processor shall detect gcovr ``--html-details`` reports and apply + the same justification logic to them, reading totals from the LCOV file when + given and from the summary rows of the index page otherwise. + +Gate +---- + +.. tool_req:: Threshold from the environment + :id: tool_req__coverage_gate_threshold + :version: 1 + :implemented: YES + :tags: gate, ERR-03 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_gate + + The threshold shall be read from ``COVERAGE_THRESHOLD`` and default to 100 %. + A value that is not a number in ``[0, 100]`` shall be rejected before any + report is produced (exit 2). + +.. tool_req:: Gated metric + :id: tool_req__coverage_gate_metric + :version: 1 + :implemented: YES + :tags: gate, ERR-01 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_gate + + With ``--yaml`` the gate shall use the effective line coverage from the + justification report; without it the raw line coverage summed over all + ``LF``/``LH`` records of the LCOV data, so that baseline-only files count. + The llvm-cov text summary, which omits baseline files, shall not be used. + +.. tool_req:: Unrounded comparison + :id: tool_req__coverage_gate_unrounded + :version: 1 + :implemented: YES + :tags: gate, ERR-03 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_gate + + The gate shall compare the unrounded percentage with the threshold; a value + that prints as 100.00 but is below 100 shall fail a threshold of 100. + +.. tool_req:: Exit codes + :id: tool_req__coverage_gate_exit_codes + :version: 1 + :implemented: YES + :tags: gate, ERR-03 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_gate + + ``generate_coverage_html`` shall exit 0 when the gate passes, 1 when it fails, + and 2 when no verdict is possible. A tool failure shall never end in exit 0. + +.. tool_req:: Broken input yields no verdict + :id: tool_req__coverage_gate_no_verdict + :version: 1 + :implemented: YES + :tags: gate, ERR-03, ERR-08 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_gate + + A missing or non-zip coverage report, a report without ``html_report/``, LCOV + data without instrumented lines, ``LH`` exceeding ``LF``, a missing + justification YAML, a failing justification tool, or a missing + ``summary.txt`` shall end the run with exit 2. + +Summary and archive +------------------- + +.. tool_req:: Summary is written before the verdict + :id: tool_req__coverage_summary_first + :version: 1 + :implemented: YES + :tags: summary + :safety: QM + :satisfies: stkh_req__coverage__uc_summary, gd_req__verification_reporting + + The markdown summary (``--summary-md``, or appended to ``GITHUB_STEP_SUMMARY`` + when the flag is absent) shall be written before the gate decides, so a + failing gate still leaves the summary; the explicit flag shall take + precedence over the environment variable. + +.. tool_req:: Artifacts tree + :id: tool_req__coverage_artifacts + :version: 1 + :implemented: YES + :tags: archive, ERR-09 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_archive, gd_req__verification_report_archiving + + With ``--archive-dir`` the tool shall assemble the HTML report, the LCOV data + as ``coverage_report.dat``, the justification report directory and the + ``test.xml`` files of the selected ``bazel-testlogs`` subtree with their + paths preserved, also when the gate fails; a missing test-logs directory + shall be an error. + +Validation +---------- + +.. tool_req:: Ground-truth validation + :id: tool_req__coverage_validation_ground_truth + :version: 1 + :implemented: YES + :tags: validation, ERR-02, ERR-07 + :safety: ASIL_B + :satisfies: stkh_req__coverage__uc_unified_report, stkh_req__coverage__uc_scope_completeness + + The pipeline shall be validated end to end against a fixture workspace with + C++ and Rust units whose line and branch counts are derived by hand: the + produced LCOV shall match the expected records exactly (``DA``, ``BRDA``, + ``LF``, ``LH``, ``BRF``, ``BRH`` per file), including exact-0 % records for an + untested C++ library and an untested Rust binary. diff --git a/docs/requirements/use_cases.rst b/docs/requirements/use_cases.rst new file mode 100644 index 0000000..1d5531f --- /dev/null +++ b/docs/requirements/use_cases.rst @@ -0,0 +1,101 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Use cases +========= + +The use cases are modelled as stakeholder requirements of the tool user, a +module CI pipeline or a developer on a Linux host. Tool requirements satisfy +them. + +.. stkh_req:: UC1 One report for C++ and Rust + :id: stkh_req__coverage__uc_unified_report + :version: 1 + :valid_from: v0.1.0 + :reqtype: Functional + :safety: ASIL_B + :security: NO + :status: valid + :rationale: Module verification reports need one C0/C1 figure per unit regardless of language. + + The user shall obtain, from one ``bazel coverage`` run, one report with line + and branch coverage for the C++ and Rust units of the module. + +.. stkh_req:: UC2 Untested code is visible + :id: stkh_req__coverage__uc_scope_completeness + :version: 1 + :valid_from: v0.1.0 + :reqtype: Functional + :safety: ASIL_B + :security: NO + :status: valid + :rationale: A file no test executes must not disappear from the evidence; it must count with 0 %. + + The user shall see every source file of the declared coverage scope in the + report, including files that no test links against, with exact 0 % line and + branch coverage for those. + +.. stkh_req:: UC3 Reviewed justifications + :id: stkh_req__coverage__uc_justifications + :version: 1 + :valid_from: v0.1.0 + :reqtype: Functional + :safety: ASIL_B + :security: NO + :status: valid + :rationale: Intentionally uncovered code needs a written, reviewable argument instead of a lowered threshold. + + The user shall be able to justify intentionally uncovered lines with a + reviewed argument, and obtain an effective coverage figure that counts + justified lines as covered while reporting the raw figure alongside. + +.. stkh_req:: UC4 CI gate + :id: stkh_req__coverage__uc_gate + :version: 1 + :valid_from: v0.1.0 + :reqtype: Functional + :safety: ASIL_B + :security: NO + :status: valid + :rationale: The platform quality criteria (85 % QM, 100 % safety) are enforced automatically. + + The user shall be able to fail a CI job when the gated coverage is below a + configured threshold, and shall be able to tell a failed gate from a run + that could not be evaluated. + +.. stkh_req:: UC5 Archived evidence + :id: stkh_req__coverage__uc_archive + :version: 1 + :valid_from: v0.1.0 + :reqtype: Functional + :safety: ASIL_B + :security: NO + :status: valid + :rationale: The module verification report cites the archived LCOV and justification report. + + The user shall be able to archive the HTML report, the LCOV data, the + justification report and the JUnit results of a run as one CI artifact. + +.. stkh_req:: UC6 Job summary + :id: stkh_req__coverage__uc_summary + :version: 1 + :valid_from: v0.1.0 + :reqtype: Functional + :safety: QM + :security: NO + :status: valid + :rationale: Reviewers read the numbers on the workflow page without downloading artifacts. + + The user shall obtain a markdown summary of the run, on the GitHub Actions + step summary or in a file, even when the gate fails. diff --git a/docs/verification/index.rst b/docs/verification/index.rst new file mode 100644 index 0000000..9ff043f --- /dev/null +++ b/docs/verification/index.rst @@ -0,0 +1,21 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Verification +============ + +.. toctree:: + :maxdepth: 2 + + verification_report diff --git a/docs/verification/verification_report.rst b/docs/verification/verification_report.rst new file mode 100644 index 0000000..77b3a51 --- /dev/null +++ b/docs/verification/verification_report.rst @@ -0,0 +1,166 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Verification report +=================== + +.. document:: score_coverage verification report + :id: doc__coverage_verification_report + :version: 1 + :status: draft + :safety: ASIL_B + :security: NO + :realizes: wp__verification_module_ver_report + +This report is regenerated with every release. It doubles as the qualification +verification report of the tool: the Tool Verification Report in the S-CORE +platform documentation refers to it as the evidence of the validation. + +Scope and environment +--------------------- + +Validated environment: Linux x86_64, Bazel 8.7.0, ``toolchains_llvm`` 1.8.0 +with LLVM 22.1.7, ``score_toolchains_rust`` 0.10.0 (Ferrocene built by +``ferrocene_toolchain_builder`` 1.3.1), Python 3.11 and 3.12 (``rules_python`` +1.8.5), ``rules_rust`` 0.68.2-score. + +Test inventory +-------------- + +.. list-table:: + :header-rows: 1 + :widths: 45 15 40 + + * - Test target + - Cases + - Verifies + * - ``//score_coverage/tests:merger_test`` + - 20 + - merge_profraw, merge_no_data, merge_tool_error + * - ``//score_coverage/tests:reporter_test`` + - 34 + - report_merged_profile, report_allowlist, report_rlib_expansion, + report_missing_baseline, report_relative_paths, report_outputs + * - ``//score_coverage/tests:justify_test`` + - 41 + - just_yaml, just_markers, just_unknown_id, just_platform, + just_missing_file + * - ``//score_coverage/tests:effective_coverage_test`` + - 52 + - eff_metric, eff_stale, eff_branch_only, eff_path_match, eff_html, + eff_gcovr + * - ``//score_coverage/tests:generate_coverage_html_test`` + - 43 + - gate_threshold, gate_metric, gate_unrounded, gate_exit_codes, + gate_no_verdict, summary_first, artifacts + * - ``//score_coverage/tests:coverage_summary_test`` + - 17 + - summary_first + * - ``//score_coverage/tests/starlark:coverage_scope_tests`` (8 analysis tests) + - 8 + - scope_transitive, scope_excludes, scope_baseline_objects + * - ``integration_tests/run_integration_test.sh`` (15 end-to-end checks) + - 15 + - validation_ground_truth, report_baseline_zero, gate_exit_codes, + gate_no_verdict, just_unknown_id, artifacts, summary_first + +Requirement coverage +-------------------- + +Every tool requirement is verified by at least one test listed above. The +machine-readable link from test case to requirement (``Verifies`` test +properties) is planned for the next iteration; until then the mapping above is +maintained by hand and reviewed with each change. + +.. needtable:: Requirements and their verification state + :types: tool_req + :columns: id;title;implemented;testcovered + :style: table + +Structural coverage of the tool +------------------------------- + +Measured with coverage.py through ``bazel coverage --combined_report=lcov`` and +gated in CI by ``//tools:self_coverage_gate`` (current ratchet 95 % lines, +87 % branches; target 100 % with documented deviations). + +.. list-table:: + :header-rows: 1 + :widths: 40 30 30 + + * - File + - Lines (C0) + - Branches (C1) + * - ``score_coverage/coverage_summary.py`` + - 95.65 % (198/207) + - 88.78 % (87/98) + * - ``score_coverage/effective_coverage.py`` + - 95.09 % (523/550) + - 81.16 % (224/276) + * - ``score_coverage/generate_coverage_html.py`` + - 98.56 % (205/208) + - 92.59 % (75/81) + * - ``score_coverage/justify.py`` + - 98.35 % (238/242) + - 94.56 % (139/147) + * - ``score_coverage/merger.py`` + - 98.35 % (119/121) + - 93.65 % (59/63) + * - ``score_coverage/reporter.py`` + - 91.95 % (354/385) + - 84.97 % (164/193) + * - **Total** + - **95.56 % (1637/1713)** + - **87.18 % (748/858)** + +Static analysis +--------------- + +ruff (rule set of the S-CORE Python guideline: E, W, F, I, B, C90, UP, SIM, +RET; McCabe ceiling 15), pylint and ty run as Bazel aspects with findings +failing the build. Current state: zero findings. buildifier checks the Starlark, +yamlfmt the workflows; copyright headers are checked on every file. + +End-to-end validation +--------------------- + +``integration_tests/run_integration_test.sh`` builds a consumer workspace with a +tested and an untested C++ library, a tested Rust library and an untested Rust +binary, one justified line, and asserts: + +1. the gate fails at 100 % and passes at 10 % (effective and raw mode); +2. the HTML, the summary and the archive tree are produced, the summary also + when the gate fails; +3. the untested C++ file and the untested Rust binary appear with ``LH:0``; +4. the LCOV matches ``expected_lcov.dat``, a hand-derived ground truth, record + by record; +5. the justified line raises effective above raw coverage; +6. fault injection: a corrupt report and a non-numeric threshold exit 2, and a + misspelt justification id is reported and does not raise the effective + coverage. + +Deviations +---------- + +- Structural coverage of the Python is below 100 %. The remaining lines are + error-handling and llvm-cov fallback paths in ``reporter.py`` and + ``effective_coverage.py``; they are covered by the fault-injection checks of + the integration test where they are reachable and will be closed or justified + before the first qualified release. +- Starlark (``coverage_scope.bzl``, ``reporter_wrapper.bzl``) has no structural + coverage tooling. The rule and aspect are verified by eight analysis tests + and by the end-to-end run. +- The gcovr backend of ``effective_coverage.py`` is unit-tested against real + gcovr 8.6 markup but is not reachable through ``generate_coverage_html`` in + this release (QNX flow, tooling issue #427). diff --git a/tools/BUILD b/tools/BUILD index c7f180a..95a9119 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -25,11 +25,11 @@ copyright_checker( ".bazelrc", ".github", "BUILD", - "COVERAGE_GUIDE.md", "MODULE.bazel", "README.md", "REUSE.toml", "defs.bzl", + "docs", "integration_tests", "pyproject.toml", "score_coverage", From 10b5476b6aaf996dd4f320b8ace9e1936885116e Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:16:51 +0300 Subject: [PATCH 09/17] Generate the test-to-requirement links (WP3/WP4 leftover) Unit tests now run through score_tooling's score_py_pytest (pytest with the attribute plugin, --junitxml=$XML_OUTPUT_FILE). A small class decorator, score_coverage/tests/traceability.py::verifies, applies add_test_properties to every test_* method of a TestCase class, so each JUnit testcase carries PartiallyVerifies (tool_req ids), TestType and DerivationTechnique; missing descriptions are derived from the test name, explicit docstrings win. Outside Bazel the decorator is a no-op and the tests remain plain unittest. All 54 test classes (207 cases) are annotated against the 32 tool requirements. docs() gets test_sources = ["score_coverage/tests"], and the verification report's hand-maintained mapping is replaced by a generated needtable with the testlink column (test name plus execution result) and a result pie. 28 of 32 requirements carry generated links; the three scope requirements (Starlark analysis tests, no properties) and the ground-truth validation (shell integration test) are listed as verified outside pytest. CI: the unit-test job copies the score_coverage test.xml files into tests-report/ and uploads them as artifact "tests-report"; the docs build moves into tests.yml as a job depending on it and passes the artifact to the reusable docs workflow, so the published docs show the links. The separate docs.yml is removed. Coverage under pytest is unchanged (95.6 % / 87.2 %); lint stays at zero findings. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/workflows/docs.yml | 37 -------- .github/workflows/tests.yml | 29 +++++- .gitignore | 1 + docs/BUILD | 3 + docs/verification/verification_report.rst | 36 ++++++-- score_coverage/tests/BUILD | 56 +++++++++--- score_coverage/tests/coverage_summary_test.py | 6 ++ .../tests/effective_coverage_test.py | 13 +++ .../tests/generate_coverage_html_test.py | 20 ++++ score_coverage/tests/justify_test.py | 13 +++ score_coverage/tests/merger_test.py | 8 ++ score_coverage/tests/reporter_test.py | 24 +++++ score_coverage/tests/traceability.py | 91 +++++++++++++++++++ 13 files changed, 279 insertions(+), 58 deletions(-) delete mode 100644 .github/workflows/docs.yml create mode 100644 score_coverage/tests/traceability.py diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 335c127..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,37 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* -name: Docs / Build & Deploy -permissions: - contents: write - pages: write - pull-requests: write - id-token: write -on: - pull_request_target: - types: [opened, reopened, synchronize] - push: - branches: - - main - merge_group: - types: [checks_requested] -jobs: - docs-build: - uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@93aac16ada7d247bbb6ae926509ddea74cf5213a # v0.0.2 - permissions: - contents: write - pages: write - pull-requests: write - id-token: write - with: - bazel-target: "//docs:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" - retention-days: 3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e60d54d..daf728d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,10 @@ concurrency: group: tests-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: - contents: read + contents: write + pages: write + pull-requests: write + id-token: write env: ANDROID_HOME: "" ANDROID_SDK_ROOT: "" @@ -60,6 +63,18 @@ jobs: run: | bazel mod deps --lockfile_mode=update git diff --exit-code MODULE.bazel.lock + - name: Collect JUnit results for the documentation build + # docs-as-code links testcases to requirements from these files + # (tests-report//test.xml). + run: | + mkdir -p tests-report + (cd bazel-testlogs && find -L . -name test.xml -path "./score_coverage/*" -exec cp --parents {} ../tests-report/ \;) + find tests-report -name test.xml | wc -l + - uses: actions/upload-artifact@v4 + with: + name: tests-report + path: tests-report + retention-days: 3 integration_tests: runs-on: ubuntu-24.04 steps: @@ -86,3 +101,15 @@ jobs: path: integration_tests/coverage_artifact if-no-files-found: ignore retention-days: 10 + docs: + needs: unit_tests + uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@93aac16ada7d247bbb6ae926509ddea74cf5213a # v0.0.2 + permissions: + contents: write + pages: write + pull-requests: write + id-token: write + with: + bazel-target: "//docs:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" + tests-report-artifact: tests-report + retention-days: 3 diff --git a/.gitignore b/.gitignore index 973bfdd..50f17db 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ integration_tests/report.backup # Generated by docs-as-code on every docs build ubproject.toml _build/ +tests-report/ diff --git a/docs/BUILD b/docs/BUILD index c33db90..55c6dd1 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -21,4 +21,7 @@ docs( project = "score_coverage", project_url = "https://eclipse-score.github.io/coverage_tool", source_dir = ".", + # Only these test.xml files become testcase needs (JUnit results under + # bazel-testlogs/ or tests-report/ at the workspace root). + test_sources = ["score_coverage/tests"], ) diff --git a/docs/verification/verification_report.rst b/docs/verification/verification_report.rst index 77b3a51..ec8aae7 100644 --- a/docs/verification/verification_report.rst +++ b/docs/verification/verification_report.rst @@ -78,16 +78,38 @@ Test inventory Requirement coverage -------------------- -Every tool requirement is verified by at least one test listed above. The -machine-readable link from test case to requirement (``Verifies`` test -properties) is planned for the next iteration; until then the mapping above is -maintained by hand and reviewed with each change. - -.. needtable:: Requirements and their verification state +The links from test cases to requirements are generated: every unit test class +carries ``@verifies()``, which writes ``PartiallyVerifies``, +``TestType`` and ``DerivationTechnique`` into the JUnit XML of the test run, and +docs-as-code turns the results into ``testcase`` needs with back-links on the +requirements (``testlink`` column below, with the execution result of each +case). The links reflect the test run that preceded the documentation build. + +.. needtable:: Requirements and the tests that verify them :types: tool_req - :columns: id;title;implemented;testcovered + :columns: id;title;testlink :style: table +Four requirements are verified outside the pytest suites and therefore carry no +generated link: + +- :need:`tool_req__coverage_scope_transitive`, + :need:`tool_req__coverage_scope_excludes` and + :need:`tool_req__coverage_scope_baseline_objects` are verified by the eight + Starlark analysis tests in ``score_coverage/tests/starlark`` (rules_testing + produces no test properties). +- :need:`tool_req__coverage_validation_ground_truth` is verified by the + end-to-end run ``integration_tests/run_integration_test.sh`` (golden LCOV + comparison, see below). + +.. needpie:: Test results of the linked test cases + :labels: passed, failed, skipped + :colors: green, red, orange + + type == 'testcase' and result == 'passed' + type == 'testcase' and result == 'failed' + type == 'testcase' and result == 'skipped' + Structural coverage of the tool ------------------------------- diff --git a/score_coverage/tests/BUILD b/score_coverage/tests/BUILD index 188c825..8fe2220 100644 --- a/score_coverage/tests/BUILD +++ b/score_coverage/tests/BUILD @@ -11,40 +11,70 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@rules_python//python:defs.bzl", "py_test") +load("@rules_python//python:defs.bzl", "py_library") +load("@score_tooling//:defs.bzl", "score_py_pytest") -py_test( +# Requirement-to-test links: the unittest classes carry @verifies(...) which +# writes PartiallyVerifies/TestType/DerivationTechnique into the JUnit XML that +# docs-as-code turns into testcase needs (gd_req__verification_link_tests_python). +# score_py_pytest runs the unittest classes under pytest with score_tooling's +# attribute plugin and --junitxml=$XML_OUTPUT_FILE. +py_library( + name = "traceability", + srcs = ["traceability.py"], + imports = ["../.."], +) + +score_py_pytest( name = "merger_test", srcs = ["merger_test.py"], - deps = ["//score_coverage:merger_lib"], + deps = [ + ":traceability", + "//score_coverage:merger_lib", + ], ) -py_test( +score_py_pytest( name = "reporter_test", srcs = ["reporter_test.py"], - deps = ["//score_coverage:reporter_lib"], + deps = [ + ":traceability", + "//score_coverage:reporter_lib", + ], ) -py_test( +score_py_pytest( name = "coverage_summary_test", srcs = ["coverage_summary_test.py"], - deps = ["//score_coverage:coverage_summary_lib"], + deps = [ + ":traceability", + "//score_coverage:coverage_summary_lib", + ], ) -py_test( +score_py_pytest( name = "generate_coverage_html_test", srcs = ["generate_coverage_html_test.py"], - deps = ["//score_coverage:generate_coverage_html_lib"], + deps = [ + ":traceability", + "//score_coverage:generate_coverage_html_lib", + ], ) -py_test( +score_py_pytest( name = "justify_test", srcs = ["justify_test.py"], - deps = ["//score_coverage:justify_lib"], + deps = [ + ":traceability", + "//score_coverage:justify_lib", + ], ) -py_test( +score_py_pytest( name = "effective_coverage_test", srcs = ["effective_coverage_test.py"], - deps = ["//score_coverage:effective_coverage_lib"], + deps = [ + ":traceability", + "//score_coverage:effective_coverage_lib", + ], ) diff --git a/score_coverage/tests/coverage_summary_test.py b/score_coverage/tests/coverage_summary_test.py index 8d02859..bba82f7 100644 --- a/score_coverage/tests/coverage_summary_test.py +++ b/score_coverage/tests/coverage_summary_test.py @@ -32,6 +32,7 @@ render_markdown, rollup_by_directory, ) +from score_coverage.tests.traceability import verifies LCOV_TWO_FILES = ( "SF:src/foo/a.cpp\n" @@ -52,6 +53,7 @@ def _write(tmp: str, name: str, content: str) -> Path: return p +@verifies("tool_req__coverage_summary_first") class ParseLcovTest(unittest.TestCase): def test_missing_file_returns_none(self): self.assertIsNone(parse_lcov(Path("/nonexistent/lcov.dat"))) @@ -99,6 +101,7 @@ def test_non_utf8_bytes_do_not_crash(self): self.assertEqual(files[0].lines_found, 1) +@verifies("tool_req__coverage_summary_first", derivation="boundary-values") class MathHelpersTest(unittest.TestCase): def test_percent_zero_denominator_is_none(self): self.assertIsNone(percent(0, 0)) @@ -116,6 +119,7 @@ def test_directory_key_grouping(self): self.assertEqual(directory_key("src/foo/bar/a.cpp"), "src/foo") +@verifies("tool_req__coverage_summary_first") class RollupTest(unittest.TestCase): def test_worst_directory_first(self): with tempfile.TemporaryDirectory() as tmp: @@ -127,6 +131,7 @@ def test_worst_directory_first(self): self.assertEqual(rows[1]["directory"], "src/foo") +@verifies("tool_req__coverage_summary_first") class RenderTest(unittest.TestCase): def _render(self, justification=None): with tempfile.TemporaryDirectory() as tmp: @@ -171,6 +176,7 @@ def test_branch_dash_when_no_branch_data(self): self.assertIn("| Branches | — | — | — | — |", md) +@verifies("tool_req__coverage_summary_first") class JustificationReportTest(unittest.TestCase): def test_loads_summary_and_counts_applied(self): report = { diff --git a/score_coverage/tests/effective_coverage_test.py b/score_coverage/tests/effective_coverage_test.py index 9361d63..0486971 100644 --- a/score_coverage/tests/effective_coverage_test.py +++ b/score_coverage/tests/effective_coverage_test.py @@ -25,6 +25,7 @@ from pathlib import Path from score_coverage import effective_coverage as ec +from score_coverage.tests.traceability import verifies def _row(line: int, status: str, count: str, code: str) -> str: @@ -69,6 +70,7 @@ def _index_page(files, totals) -> str: return "

Coverage Report

" + "".join(rows) + "
" +@verifies("tool_req__coverage_eff_metric", derivation="boundary-values") class FloorTwoDecimalsTest(unittest.TestCase): def test_never_rounds_up(self): self.assertEqual(ec.floor_two_decimals(61.7647), 61.76) @@ -77,6 +79,7 @@ def test_never_rounds_up(self): self.assertEqual(ec.floor_two_decimals(0.0), 0.0) +@verifies("tool_req__coverage_eff_metric") class ParseIndexPageTotalsTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -107,6 +110,7 @@ def test_unparseable_index_yields_zero_totals(self): self.assertEqual(totals["lines"], (0, 0)) +@verifies("tool_req__coverage_eff_path_match") class PathHelpersTest(unittest.TestCase): def test_extract_source_path(self): html_dir = Path("/r/html") @@ -136,6 +140,7 @@ def test_find_source_html_files(self): self.assertEqual(found, ["coverage/rust/lib.rs.html", "coverage/src/a.cpp.html"]) +@verifies("tool_req__coverage_eff_path_match", derivation="equivalence-classes") class FindMatchingJustificationsTest(unittest.TestCase): JUSTIFIED = { "src/bar.cpp": {"5": {"id": "bar-five"}}, @@ -163,6 +168,7 @@ def test_line_keys_become_integers(self): self.assertEqual(list(result), [5]) +@verifies("tool_req__coverage_eff_stale", "tool_req__coverage_eff_branch_only", "tool_req__coverage_eff_html") class ProcessHtmlFileTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -251,6 +257,7 @@ def test_branches_on_unjustified_lines_untouched(self): self.assertIn("class='red branch'>False", after) +@verifies("tool_req__coverage_eff_html") class UpdateIndexPageTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -308,6 +315,7 @@ def test_color_thresholds(self): self.assertEqual(ec._get_coverage_color(79.99), "red") +@verifies("tool_req__coverage_eff_metric", "tool_req__coverage_eff_stale", "tool_req__coverage_eff_html") class MainLlvmCovTest(unittest.TestCase): """End-to-end on a synthetic llvm-cov report: report.json, summary.txt and HTML edits.""" @@ -419,6 +427,7 @@ def test_missing_html_dir_exits(self): ) +@verifies("tool_req__coverage_eff_gcovr") class FormatDetectionAndLcovTest(unittest.TestCase): def test_detect_html_format(self): with tempfile.TemporaryDirectory() as tmp: @@ -559,6 +568,7 @@ def _make_gcovr_report(self, with_summary=True): ) +@verifies("tool_req__coverage_eff_gcovr") class GcovrDetectionAndParsingTest(GcovrFixtureMixin, unittest.TestCase): def setUp(self): self._make_gcovr_report() @@ -604,6 +614,7 @@ def test_matching_ignores_leading_dot_slash(self): self.assertEqual(list(result), [24]) +@verifies("tool_req__coverage_eff_gcovr", "tool_req__coverage_eff_stale", "tool_req__coverage_eff_branch_only") class ProcessGcovrFileTest(GcovrFixtureMixin, unittest.TestCase): def setUp(self): self._make_gcovr_report() @@ -673,6 +684,7 @@ def test_only_the_named_line_is_marked(self): self.assertIn("justifiedLine", row18) +@verifies("tool_req__coverage_eff_gcovr", "tool_req__coverage_eff_html") class GcovrIndexAndCssTest(GcovrFixtureMixin, unittest.TestCase): def setUp(self): self._make_gcovr_report() @@ -707,6 +719,7 @@ def test_banner_inserted_before_file_list(self): ec._update_gcovr_index_page(self.html, stats) # missing index: no-op +@verifies("tool_req__coverage_eff_gcovr", "tool_req__coverage_eff_metric") class MainGcovrTest(GcovrFixtureMixin, unittest.TestCase): """End-to-end through main(): the gcovr backend must produce the same two output files.""" diff --git a/score_coverage/tests/generate_coverage_html_test.py b/score_coverage/tests/generate_coverage_html_test.py index 43d597d..d3f8574 100644 --- a/score_coverage/tests/generate_coverage_html_test.py +++ b/score_coverage/tests/generate_coverage_html_test.py @@ -33,6 +33,7 @@ from unittest import mock from score_coverage import generate_coverage_html as gch +from score_coverage.tests.traceability import verifies LCOV_25_PERCENT = ( "SF:src/covered.cpp\nDA:1,1\nDA:2,1\nLF:10\nLH:5\nend_of_record\n" @@ -72,6 +73,7 @@ def _run(root: Path, argv, environ) -> tuple: return rc, out.getvalue(), err.getvalue() +@verifies("tool_req__coverage_gate_threshold", derivation="boundary-values") class ParseThresholdTest(unittest.TestCase): def test_default_is_100(self): self.assertEqual(gch.parse_threshold(None), 100.0) @@ -90,6 +92,7 @@ def test_garbage_is_an_error_not_a_permissive_gate(self): gch.parse_threshold(bad) +@verifies("tool_req__coverage_gate_metric", "tool_req__coverage_gate_no_verdict") class RawLineCoverageTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -132,6 +135,7 @@ def test_corrupt_records_are_errors(self): gch.raw_line_coverage_from_lcov(lcov) +@verifies("tool_req__coverage_gate_metric", "tool_req__coverage_gate_no_verdict") class EffectiveLineCoverageTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -170,6 +174,7 @@ def test_malformed_reports(self): gch.effective_line_coverage_from_report(bad_json) +@verifies("tool_req__coverage_gate_unrounded", derivation="boundary-values") class GateTest(unittest.TestCase): def test_boundaries(self): self.assertTrue(gch.gate_passes(100.0, 100.0)) @@ -179,6 +184,7 @@ def test_boundaries(self): self.assertFalse(gch.gate_passes(84.999, 85.0)) +@verifies("tool_req__coverage_summary_first", "tool_req__coverage_artifacts") class ParseArgsTest(unittest.TestCase): def test_defaults(self): opts = gch.parse_args([]) @@ -218,6 +224,12 @@ def test_unknown_platform_rejected(self): gch.parse_args(["--platform", "windows"]) +@verifies( + "tool_req__coverage_gate_metric", + "tool_req__coverage_gate_exit_codes", + "tool_req__coverage_summary_first", + "tool_req__coverage_artifacts", +) class RunWithoutYamlTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -318,6 +330,7 @@ def test_missing_testlogs_when_archiving_is_an_error(self): ) +@verifies("tool_req__coverage_gate_no_verdict", test_type="fault-injection", derivation="error-guessing") class RunInputValidationTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -346,6 +359,12 @@ def test_lcov_with_no_instrumented_lines_yields_no_verdict(self): _run(self.root, [], {"COVERAGE_THRESHOLD": "0"}) +@verifies( + "tool_req__coverage_gate_metric", + "tool_req__coverage_gate_exit_codes", + "tool_req__coverage_gate_no_verdict", + "tool_req__coverage_artifacts", +) class RunWithYamlTest(unittest.TestCase): """The justification layer is faked: these tests cover the orchestration around it.""" @@ -443,6 +462,7 @@ def test_archive_includes_justification_report(self): self.assertTrue((self.root / "out" / "justification_report" / "report.json").is_file()) +@verifies("tool_req__coverage_gate_exit_codes") class MainTest(unittest.TestCase): def test_requires_build_workspace_directory(self): with mock.patch.dict("os.environ", {}, clear=True), redirect_stderr(io.StringIO()): diff --git a/score_coverage/tests/justify_test.py b/score_coverage/tests/justify_test.py index 4d501a1..3ac4f22 100644 --- a/score_coverage/tests/justify_test.py +++ b/score_coverage/tests/justify_test.py @@ -25,6 +25,7 @@ from pathlib import Path from score_coverage import justify +from score_coverage.tests.traceability import verifies VALID_ENTRY = { "id": "defensive-null-check", @@ -50,6 +51,7 @@ def _validate(data): return None +@verifies("tool_req__coverage_just_yaml", derivation="equivalence-classes") class ValidateYamlTest(unittest.TestCase): def test_valid_document(self): self.assertIsNone(_validate(_valid_yaml())) @@ -143,6 +145,7 @@ def test_all_errors_are_reported_together(self): self.assertIn(fragment, text) +@verifies("tool_req__coverage_just_markers") class ResolveLocationLinesTest(unittest.TestCase): def test_explicit_lines(self): self.assertEqual(justify.resolve_location_lines({"lines": [3, 1, 2]}), [3, 1, 2]) @@ -160,6 +163,7 @@ def test_lines_take_precedence(self): self.assertEqual(justify.resolve_location_lines({"lines": [1], "line": 5}), [1]) +@verifies("tool_req__coverage_just_platform") class MatchesPlatformTest(unittest.TestCase): def test_platform_membership(self): entry = {"platforms": ["linux"]} @@ -168,6 +172,7 @@ def test_platform_membership(self): self.assertFalse(justify._matches_platform({}, "linux")) +@verifies("tool_req__coverage_just_markers", "tool_req__coverage_just_unknown_id") class ScanFileForMarkersTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -252,6 +257,7 @@ def test_non_utf8_content_is_tolerated(self): self.assertEqual(sorted(lines), [2]) +@verifies("tool_req__coverage_just_markers") class CollectSourceFilesTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -281,6 +287,7 @@ def test_empty_filter_uses_defaults(self): self.assertNotIn("src/d.py", self._rel(files)) +@verifies("tool_req__coverage_just_yaml") class LoadYamlTest(unittest.TestCase): def test_missing_file_exits(self): with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): @@ -293,6 +300,12 @@ def test_loads_document(self): self.assertEqual(justify.load_yaml(path), {"version": 1, "justifications": []}) +@verifies( + "tool_req__coverage_just_markers", + "tool_req__coverage_just_platform", + "tool_req__coverage_just_missing_file", + "tool_req__coverage_just_yaml", +) class MainTest(unittest.TestCase): """End-to-end: YAML locations + in-code markers -> manifest.""" diff --git a/score_coverage/tests/merger_test.py b/score_coverage/tests/merger_test.py index 8a8bc87..a46af5e 100644 --- a/score_coverage/tests/merger_test.py +++ b/score_coverage/tests/merger_test.py @@ -32,8 +32,10 @@ from score_coverage import merger from score_coverage.merger import find_llvm_profdata, get_object_files_from_manifest, is_elf +from score_coverage.tests.traceability import verifies +@verifies("tool_req__coverage_merge_profraw") class IsElfTest(unittest.TestCase): def test_elf_magic_is_detected(self): with tempfile.NamedTemporaryFile(suffix=".bin") as f: @@ -51,6 +53,7 @@ def test_missing_file_is_not_elf(self): self.assertFalse(is_elf(Path("/nonexistent/path/binary"))) +@verifies("tool_req__coverage_merge_tool_error") class FindLlvmProfdataTest(unittest.TestCase): def test_llvm_profdata_env_wins(self): with tempfile.NamedTemporaryFile() as f, mock.patch.dict(os.environ, {"LLVM_PROFDATA": f.name}, clear=True): @@ -71,6 +74,7 @@ def test_returns_empty_when_nothing_resolves(self): self.assertEqual(find_llvm_profdata(), "") +@verifies("tool_req__coverage_merge_profraw") class GetObjectFilesFromManifestTest(unittest.TestCase): def test_missing_root_env_is_a_hard_error(self): """Without ROOT the merger cannot resolve manifest paths — must exit.""" @@ -148,6 +152,7 @@ def _fake_profdata(path: Path, fail: bool = False) -> Path: return path +@verifies("tool_req__coverage_merge_profraw") class CleanupDanglingSymlinksTest(unittest.TestCase): def test_gcov_and_sandbox_links_removed_others_kept(self): with tempfile.TemporaryDirectory() as tmp: @@ -163,6 +168,7 @@ def test_gcov_and_sandbox_links_removed_others_kept(self): self.assertTrue((root / "keep.txt").is_file()) +@verifies("tool_req__coverage_merge_profraw") class CreateZipTest(unittest.TestCase): def test_only_listed_directories_relative_to_root(self): with tempfile.TemporaryDirectory() as tmp: @@ -179,6 +185,7 @@ def test_only_listed_directories_relative_to_root(self): self.assertEqual(sorted(zf.namelist()), ["a/sub/f.txt", "b/g.txt"]) +@verifies("tool_req__coverage_merge_tool_error", test_type="fault-injection", derivation="error-guessing") class RunCommandTest(unittest.TestCase): def test_failure_exits_with_1(self): with redirect_stderr(io.StringIO()), self.assertRaises(SystemExit) as ctx: @@ -190,6 +197,7 @@ def test_success_returns_output(self): self.assertEqual(result.stdout.strip(), "ok") +@verifies("tool_req__coverage_merge_profraw", "tool_req__coverage_merge_no_data", "tool_req__coverage_merge_tool_error") class MergerMainTest(unittest.TestCase): """main() against a fake Bazel coverage directory and a fake llvm-profdata.""" diff --git a/score_coverage/tests/reporter_test.py b/score_coverage/tests/reporter_test.py index 24aeea8..ab21d9f 100644 --- a/score_coverage/tests/reporter_test.py +++ b/score_coverage/tests/reporter_test.py @@ -39,6 +39,7 @@ expand_rlib_archives, write_empty_output, ) +from score_coverage.tests.traceability import verifies def _ar_header(name: str, size: int) -> bytes: @@ -56,6 +57,7 @@ def _make_archive(members) -> bytes: return blob +@verifies("tool_req__coverage_report_rlib_expansion") class ReadArMembersTest(unittest.TestCase): def test_non_archive_returns_empty(self): with tempfile.NamedTemporaryFile(suffix=".a") as f: @@ -81,6 +83,7 @@ def test_gnu_long_name_table_is_resolved(self): self.assertEqual([m[0] for m in members], ["a_very_long_object_file_name.o"]) +@verifies("tool_req__coverage_report_rlib_expansion") class ExpandRlibArchivesTest(unittest.TestCase): def test_rlib_is_expanded_to_object_members(self): """Archives with a lib.rmeta member are replaced by their .o members.""" @@ -110,6 +113,7 @@ def test_executable_passes_through(self): self.assertEqual(result, [str(binary)]) +@verifies("tool_req__coverage_report_baseline_zero") class FilterLcovTest(unittest.TestCase): LCOV = "SF:src/foo.cpp\nDA:1,1\nLF:1\nLH:1\nend_of_record\nSF:src/bar.cpp\nDA:1,0\nLF:1\nLH:0\nend_of_record\n" @@ -123,6 +127,7 @@ def test_suffix_matching(self): self.assertIn("SF:/abs/prefix/src/foo.cpp", result) +@verifies("tool_req__coverage_report_relative_paths") class MakeLcovPathsRelativeTest(unittest.TestCase): def test_workspace_paths_become_relative(self): lcov = "SF:/ws/root/src/foo.cpp\nDA:1,1\nend_of_record\n" @@ -145,6 +150,7 @@ def test_non_sf_lines_are_preserved(self): self.assertIn("DA:5,0\n", result) +@verifies("tool_req__coverage_report_relative_paths") class MakeHtmlPathsRelativeTest(unittest.TestCase): def test_source_title_is_rewritten_and_hrefs_untouched(self): html = ( @@ -165,6 +171,7 @@ def test_missing_dir_is_a_noop(self): _make_html_paths_relative(Path("/nonexistent/html_dir"), "/ws/root/") +@verifies("tool_req__coverage_report_outputs") class WriteEmptyOutputTest(unittest.TestCase): def test_produces_valid_empty_zip(self): with tempfile.TemporaryDirectory() as tmp: @@ -246,6 +253,7 @@ def _fake_llvm_profdata(path: Path) -> Path: ) +@verifies("tool_req__coverage_report_merged_profile") class ReadReportsFileTest(unittest.TestCase): def test_blank_lines_dropped(self): with tempfile.TemporaryDirectory() as tmp: @@ -254,6 +262,7 @@ def test_blank_lines_dropped(self): self.assertEqual(reporter.read_reports_file(f), ["a.zip", "b.zip"]) +@verifies("tool_req__coverage_report_merged_profile", derivation="error-guessing") class ExtractReportsTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -312,6 +321,7 @@ def test_invalid_inputs_are_skipped(self): self.assertEqual(err.getvalue().count("WARNING: Skipping invalid report"), 3) +@verifies("tool_req__coverage_report_merged_profile") class ResolveToolTest(unittest.TestCase): def test_preference_order(self): with tempfile.TemporaryDirectory() as tmp: @@ -327,6 +337,7 @@ def test_preference_order(self): self.assertIsNone(reporter.resolve_tool(None, "unknown", "")) +@verifies("tool_req__coverage_report_outputs") class FindCxxfiltTest(unittest.TestCase): def test_explicit_then_sibling_then_none(self): with tempfile.TemporaryDirectory() as tmp: @@ -345,6 +356,7 @@ def test_explicit_then_sibling_then_none(self): self.assertEqual(reporter.find_cxxfilt(cov, rf, None), "") +@verifies("tool_req__coverage_report_allowlist", "tool_req__coverage_report_missing_baseline") class LoadAllowlistAndBaselineTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() @@ -380,6 +392,7 @@ def test_missing_baseline_object_is_a_hard_error(self): reporter.load_baseline_objects(rf, "main/objects.txt") +@verifies("tool_req__coverage_report_outputs", test_type="fault-injection", derivation="error-guessing") class RunCommandTest(unittest.TestCase): def test_separate_stderr_keeps_stdout_clean(self): err = io.StringIO() @@ -396,6 +409,11 @@ def test_failure_exits(self): reporter.run_command([sys.executable, "-c", "import sys; sys.exit(4)"]) +@verifies( + "tool_req__coverage_report_allowlist", + "tool_req__coverage_report_baseline_zero", + "tool_req__coverage_report_outputs", +) class LlvmCovInvocationsTest(unittest.TestCase): """The exact llvm-cov command lines, checked through the fake tool's log.""" @@ -468,6 +486,12 @@ def test_report_flags(self): self.assertIn(flag, argv) +@verifies( + "tool_req__coverage_report_merged_profile", + "tool_req__coverage_report_allowlist", + "tool_req__coverage_report_relative_paths", + "tool_req__coverage_report_outputs", +) class ReporterMainTest(unittest.TestCase): """main() end to end with fake llvm tools and two per-test reports.""" diff --git a/score_coverage/tests/traceability.py b/score_coverage/tests/traceability.py new file mode 100644 index 0000000..f8b4761 --- /dev/null +++ b/score_coverage/tests/traceability.py @@ -0,0 +1,91 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Link test classes to the tool requirements they verify. + +``@verifies(...)`` applies score_tooling's ``add_test_properties`` to every +``test_*`` method of a ``unittest.TestCase`` class. Under ``score_py_pytest`` the +properties (``PartiallyVerifies`` / ``FullyVerifies``, ``TestType``, +``DerivationTechnique``) end up in the JUnit XML that docs-as-code turns into +``testcase`` needs and ``testlink`` back-references on the requirements +(``gd_req__verification_link_tests_python``). + +Outside Bazel (plain ``python -m unittest``) the plugin is absent and the +decorator is a no-op, so the tests stay runnable from an IDE. +""" + +from __future__ import annotations + +from collections.abc import Callable +from typing import Any, Literal + +try: + from attribute_plugin import add_test_properties # type: ignore[import-not-found] # ty: ignore[unresolved-import] +except ImportError: # pragma: no cover - only outside of score_py_pytest + + def add_test_properties(**_kwargs: Any) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + """No-op replacement when the pytest plugin is not available.""" + + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + return func + + return decorator + + +TestType = Literal["fault-injection", "interface-test", "requirements-based", "resource-usage"] +Derivation = Literal[ + "requirements-analysis", + "design-analysis", + "boundary-values", + "equivalence-classes", + "fuzz-testing", + "error-guessing", + "explorative-testing", +] + + +def _description_from_name(name: str) -> str: + """``test_gate_fails_at_default_threshold`` -> ``Gate fails at default threshold.``""" + words = name.removeprefix("test_").replace("_", " ").strip() + return (words[:1].upper() + words[1:] + ".") if words else name + + +def verifies( + *partially: str, + fully: tuple[str, ...] = (), + test_type: TestType = "requirements-based", + derivation: Derivation = "requirements-analysis", +) -> Callable[[type], type]: + """Class decorator: every ``test_*`` method verifies the given requirement ids. + + Methods without a docstring get a description derived from their name; an + explicit docstring always wins. + """ + if not partially and not fully: + raise ValueError("verifies() needs at least one requirement id") + + def decorate(cls: type) -> type: + for name, member in list(vars(cls).items()): + if not name.startswith("test_") or not callable(member): + continue + if not (member.__doc__ or "").strip(): + member.__doc__ = _description_from_name(name) + annotated = add_test_properties( + partially_verifies=list(partially) or None, + fully_verifies=list(fully) or None, + test_type=test_type, + derivation_technique=derivation, + )(member) + setattr(cls, name, annotated) + return cls + + return decorate From 87fc38af2c1ae536312543fc1094e864ce5d701a Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:28:32 +0300 Subject: [PATCH 10/17] Add gitlint configuration aligned with eclipse-score/score Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .gitlint | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .gitlint diff --git a/.gitlint b/.gitlint new file mode 100644 index 0000000..7cb86ee --- /dev/null +++ b/.gitlint @@ -0,0 +1,22 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +# Commit message rules, aligned with eclipse-score/score: 72-character titles, +# body lines unrestricted (B1) so DCO sign-off lines with long addresses pass. +[general] +ignore=T3,T5,B1,B5,B6,B7 +regex-style-search=true + +[title-max-length] +line-length=72 + +[body-first-line-empty] From 64581de722b1afb4ae73fe6ff145968e4326b029 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:35:24 +0300 Subject: [PATCH 11/17] Refresh MODULE.bazel.lock for the score_toolchains_rust extensions bazel mod deps --lockfile_mode=update records the ferrocene_rules_rust_miri extension of score_toolchains_rust, which the CI lockfile check flagged. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- MODULE.bazel.lock | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 1cc40fc..ff8b2c3 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -5599,6 +5599,82 @@ ] } }, + "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_ext": { + "general": { + "bzlTransitiveDigest": "9k8SLwP4ec9xXjFivq0Kn0FradTQhcEA+j/WsLxfVPM=", + "usagesDigest": "1G+0hoRkZx33aQUiAxx8GdGErOPGCHHQXn5lO7fZxgI=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "ferrocene_x86_64_unknown_linux_gnu_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_x86_64_unknown_linux_gnu", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ] + } + }, + "ferrocene_aarch64_unknown_linux_gnu_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_aarch64_unknown_linux_gnu", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ] + } + }, + "ferrocene_x86_64_pc_nto_qnx800_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_x86_64_pc_nto_qnx800", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:qnx" + ] + } + }, + "ferrocene_aarch64_unknown_nto_qnx800_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_aarch64_unknown_nto_qnx800", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:qnx" + ] + } + } + }, + "recordedRepoMappingEntries": [] + } + }, "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_ext": { "general": { "bzlTransitiveDigest": "9k8SLwP4ec9xXjFivq0Kn0FradTQhcEA+j/WsLxfVPM=", From c227e727cd1583e4de6f09682f708b27e9d821a0 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:42:22 +0300 Subject: [PATCH 12/17] Fix collection of JUnit results for the docs build The cd into the bazel-testlogs symlink made ../tests-report resolve outside the workspace, so no file was copied and the empty artifact upload passed silently (if-no-files-found: warn). Paths are now rewritten without changing directory and an empty upload is an error. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/workflows/tests.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index daf728d..bc5a1ad 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -66,14 +66,21 @@ jobs: - name: Collect JUnit results for the documentation build # docs-as-code links testcases to requirements from these files # (tests-report//test.xml). + # bazel-testlogs is a symlink into the output tree, so paths are + # rewritten here instead of cd-ing into it. run: | - mkdir -p tests-report - (cd bazel-testlogs && find -L . -name test.xml -path "./score_coverage/*" -exec cp --parents {} ../tests-report/ \;) - find tests-report -name test.xml | wc -l + rm -rf tests-report + find -L bazel-testlogs/score_coverage -name test.xml | while read -r f; do + dest="tests-report/${f#bazel-testlogs/}" + mkdir -p "$(dirname "$dest")" + cp "$f" "$dest" + done + echo "collected $(find tests-report -name test.xml | wc -l) test.xml files" - uses: actions/upload-artifact@v4 with: name: tests-report path: tests-report + if-no-files-found: error retention-days: 3 integration_tests: runs-on: ubuntu-24.04 From 39516c8bb721300d5cf006dd7581b479125d9c8b Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:46:46 +0300 Subject: [PATCH 13/17] Move the docs output to _build for the reusable docs workflow docs() runs from //docs, so the CLI writes docs/_build while the cicd-workflows docs job archives _build at the workspace root. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/workflows/tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bc5a1ad..a86d9a4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -117,6 +117,8 @@ jobs: pull-requests: write id-token: write with: - bazel-target: "//docs:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" + # docs() lives in //docs (the root package must not load dev deps), so + # the output is docs/_build; the reusable workflow archives _build. + bazel-target: "//docs:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }} && mv docs/_build _build" tests-report-artifact: tests-report retention-days: 3 From f06cc5562be714a789c87e8c400617fcc9a3fd9e Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Tue, 8 Sep 2026 09:03:14 +0300 Subject: [PATCH 14/17] Add CODEOWNERS (infrastructure maintainers, as in eclipse-score/tools) Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .github/CODEOWNERS | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..8db10ee --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,10 @@ +# 👋 Code owners help maintain this repository and keep it aligned with our technical vision. +# You're responsible for reviewing changes, ensuring quality, and guiding contributors. +# You're also encouraged to help triage issues and keep discussions constructive and focused. +# Ownership can be shared, delegated, or updated as the project evolves. + +# For more information about CODEOWNERS, see: +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# https://github.com/orgs/eclipse-score/teams/infrastructure-maintainers +* @AlexanderLanin @MaximilianSoerenPollak @dcalavrezo-qorix @nradakovic From 021fb6d8ac4d336458498316e5e5435b746b8729 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Tue, 8 Sep 2026 09:15:48 +0300 Subject: [PATCH 15/17] Make the docs modules regular (non-dev) dependencies Consumers (reference_integration, the Tool Verification Report in eclipse-score/score) consume //docs:needs_json and the docs bundle of this module, which only resolves when score_docs_as_code and score_process_description are not dev dependencies. Same pattern as persistency and time. Review feedback on PR #1. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- MODULE.bazel | 10 +- integration_tests/MODULE.bazel.lock | 500 +++++++++++++++++++++++++++- 2 files changed, 505 insertions(+), 5 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index c956b00..f7ef32d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -80,7 +80,11 @@ bazel_dep(name = "buildifier_prebuilt", version = "8.5.1", dev_dependency = True bazel_dep(name = "rules_testing", version = "0.9.0", dev_dependency = True) bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) +############################################################################### # Documentation (docs-as-code with the S-CORE process needs for `satisfies` / -# `realizes` links). Used only by //docs. -bazel_dep(name = "score_docs_as_code", version = "8.1.1", dev_dependency = True) -bazel_dep(name = "score_process_description", version = "2.1.2", dev_dependency = True) +# `realizes` links). NOT dev dependencies: reference_integration and the Tool +# Verification Report in eclipse-score/score consume //docs:needs_json and the +# docs bundle of this module, which requires these to resolve from a consumer. +############################################################################### +bazel_dep(name = "score_docs_as_code", version = "8.1.1") +bazel_dep(name = "score_process_description", version = "2.1.2") diff --git a/integration_tests/MODULE.bazel.lock b/integration_tests/MODULE.bazel.lock index 91a3628..4eb6a6d 100644 --- a/integration_tests/MODULE.bazel.lock +++ b/integration_tests/MODULE.bazel.lock @@ -15,9 +15,12 @@ "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8", "https://bcr.bazel.build/modules/apple_support/1.24.1/source.json": "cf725267cbacc5f028ef13bb77e7f2c2e0066923a4dab1025e4a0511b1ed258a", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.16.0/MODULE.bazel": "852f9ebbda017572a7c113a2434592dd3b2f55cd9a0faea3d4be5a09a59e4900", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/source.json": "ffab9254c65ba945f8369297ad97ca0dec213d3adc6e07877e23a48624a8b456", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838", + "https://bcr.bazel.build/modules/aspect_rules_py/1.4.0/MODULE.bazel": "6fd29b93207a31445d5d3ab9d9882fd5511e43c95e8e82e7492872663720fd44", + "https://bcr.bazel.build/modules/aspect_rules_py/1.4.0/source.json": "fb1ba946478fb6dbb26d49307d756b0fd2ff88be339af23c39c0397d59143d2c", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", @@ -36,6 +39,8 @@ "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "d6e00979a98ac14ada5e31c8794708b41434d461e7e7ca39b59b765e6d233b18", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0-rc.0/source.json": "7051768079aa19302df2b9446ad0889839fd08b7d59851eba2c99234d665c9ba", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", @@ -47,8 +52,12 @@ "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb", + "https://bcr.bazel.build/modules/buildifier_prebuilt/7.3.1/MODULE.bazel": "537faf0ad9f5892910074b8e43b4c91c96f1d5d86b6ed04bdbe40cf68aa48b68", + "https://bcr.bazel.build/modules/buildifier_prebuilt/8.2.0.2/MODULE.bazel": "a9b689711d5b69f9db741649b218c119b9fdf82924ba390415037e09798edd03", + "https://bcr.bazel.build/modules/buildifier_prebuilt/8.2.0.2/source.json": "51eb0a4b38aaaeab7fa64361576d616c4d8bfd0f17a0a10184aeab7084d79f8e", "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8", @@ -124,7 +133,8 @@ "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", - "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", + "https://bcr.bazel.build/modules/rules_java/8.15.1/MODULE.bazel": "5071eebf0fd602ab0617f846e0e0d8f388d66c961513c736e0ac4a1dcde3ff2c", + "https://bcr.bazel.build/modules/rules_java/8.15.1/source.json": "e48286d5819767bc5b3d457539ae7f94e28a9b3e55d092d5c47176cb6a2a289b", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", @@ -141,9 +151,12 @@ "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_multitool/1.11.1/MODULE.bazel": "f826d2d394e8d964e44ebb4a75ebcfe4e9cd4eb150e2ddcd60398ffeb939696a", + "https://bcr.bazel.build/modules/rules_multitool/1.11.1/source.json": "201f43de1d35bd17f25a4fed3ba5a2ec500ef5e08b7d4b341bb9fd39cef0cbc6", "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", - "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_pkg/1.1.0/MODULE.bazel": "9db8031e71b6ef32d1846106e10dd0ee2deac042bd9a2de22b4761b0c3036453", + "https://bcr.bazel.build/modules/rules_pkg/1.1.0/source.json": "fef768df13a92ce6067e1cd0cdc47560dace01354f1d921cfb1d632511f7d608", "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", @@ -153,9 +166,11 @@ "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.29.0/MODULE.bazel": "2ac8cd70524b4b9ec49a0b8284c79e4cd86199296f82f6e0d5da3f783d660c82", "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", "https://bcr.bazel.build/modules/rules_python/1.8.5/MODULE.bazel": "28b2d79ed8368d7d45b34bacc220e3c0b99cbcd9392641961b849e4c3f55dd30", "https://bcr.bazel.build/modules/rules_python/1.8.5/source.json": "e261b03c8804f2582c9536013f987e1ea105a2b38c238aa2ac8f98fc34c8b18a", "https://bcr.bazel.build/modules/rules_rust/0.56.0/MODULE.bazel": "3295b00757db397122092322fe1e920be7f5c9fbfb8619138977e820f2cbbbae", @@ -164,6 +179,8 @@ "https://bcr.bazel.build/modules/rules_shell/0.4.1/MODULE.bazel": "00e501db01bbf4e3e1dd1595959092c2fadf2087b2852d3f553b5370f5633592", "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c", + "https://bcr.bazel.build/modules/sphinxdocs/2.2.0/MODULE.bazel": "e046c573919d72605d62c352a08d9223a10aafef3a7cb70d0fe253ebdd97019e", + "https://bcr.bazel.build/modules/sphinxdocs/2.2.0/source.json": "b1da19a3d14a1dd8aa6a9ccaedc42bbe0313c8160a77ba5cca336cca1315298d", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", @@ -196,8 +213,10 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.23.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.24.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.16.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_py/1.4.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.1.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.10.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.11.0/MODULE.bazel": "not found", @@ -215,6 +234,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.4.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.0.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.1.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.0/MODULE.bazel": "not found", @@ -226,7 +246,10 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.6.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.8.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/7.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/8.2.0.2/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildozer/7.1.2/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/google_benchmark/1.8.2/MODULE.bazel": "not found", @@ -288,6 +311,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.3.2/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.6.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.15.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.3.2/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.5.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/4.4.2/MODULE.bazel": "not found", @@ -301,8 +325,10 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.7/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/1.11.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/0.7.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.1.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/4.0.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.2/MODULE.bazel": "not found", @@ -311,9 +337,11 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.23.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.25.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.28.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.29.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.31.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.4.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.40.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.0.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.8.5/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.56.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.68.2-score/MODULE.bazel": "37be8dee6df19d666c1d4266e1266d82012aa83bd82de38b3100fd7f641d064b", @@ -324,8 +352,16 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.6.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/MODULE.bazel": "14c96e378c08705a46abe0799d6236fe3095c342c34f83f8d1b3f6046ce00651", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.0.3/source.json": "1a853ab23455388d550a9aecf3d8b53ec73de50e7fe2914d9269a3c698bf3624", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_devcontainer/1.11.0/MODULE.bazel": "fb6041c96e949a151307b4690ee81cf5d9254d6bf79540184b16567b04397171", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_devcontainer/1.11.0/source.json": "b379a2499cf06f16de65c2981665c3723afba9e858ed16b453f044882067f56f", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/8.0.1/MODULE.bazel": "446b0462f893021e3133b2f95672ae37dfe2ade434b6052c91530043e4d89993", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/8.1.1/MODULE.bazel": "4740f5e4229753b2ec023df5ce9c24c3472ad1e6d700a970e3e96f7347e65a1b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/8.1.1/source.json": "189c748d57a732e0653af377bb7635253ed7fc135f33c2f107c4d8a4a17259c5", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process_description/2.1.2/MODULE.bazel": "1076b36d2d05ab1a18df0746f6a545869eec6927019d651af99d99ef056e2023", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process_description/2.1.2/source.json": "f76099f076243e2641803971e2a95ed27f24756c9bdda5d10a808c664996f0fc", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/MODULE.bazel": "535296e6cdef55a506c580ca2ce541aa9ddefb354de1a24ab2bd4addc939282b", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_rust/0.10.0/source.json": "8bda773be264da16d2a82a03ebb737421dd4a35855f1e9a5d03d9722d84c1df5", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/sphinxdocs/2.2.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.6/MODULE.bazel": "not found", @@ -344,6 +380,233 @@ }, "selectedYankedVersions": {}, "moduleExtensions": { + "@@aspect_rules_py+//py:extensions.bzl%py_tools": { + "general": { + "bzlTransitiveDigest": "SnsNf8tZ93pztlEcTNvkFJ6dCCRMBCipDvH33ozbho0=", + "usagesDigest": "NC1b49l5tenTBVWEUGzzC0j5Kg1GH+l5lBw5JRCldIU=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "bsd_tar_darwin_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "darwin_amd64" + } + }, + "bsd_tar_darwin_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "darwin_arm64" + } + }, + "bsd_tar_linux_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "linux_amd64" + } + }, + "bsd_tar_linux_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "linux_arm64" + } + }, + "bsd_tar_windows_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "windows_amd64" + } + }, + "bsd_tar_windows_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "windows_arm64" + } + }, + "bsd_tar_toolchains": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%tar_toolchains_repo", + "attributes": { + "user_repository_name": "bsd_tar" + } + }, + "rules_py_tools.darwin_amd64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "darwin_amd64" + } + }, + "rules_py_tools.darwin_arm64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "darwin_arm64" + } + }, + "rules_py_tools.linux_amd64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "linux_amd64" + } + }, + "rules_py_tools.linux_arm64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "linux_arm64" + } + }, + "rules_py_tools": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:repo.bzl%toolchains_repo", + "attributes": { + "user_repository_name": "rules_py_tools" + } + }, + "rules_py_pex_2_3_1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "urls": [ + "https://files.pythonhosted.org/packages/e7/d0/fbda2a4d41d62d86ce53f5ae4fbaaee8c34070f75bb7ca009090510ae874/pex-2.3.1-py2.py3-none-any.whl" + ], + "sha256": "64692a5bf6f298403aab930d22f0d836ae4736c5bc820e262e9092fe8c56f830", + "downloaded_file_path": "pex-2.3.1-py2.py3-none-any.whl" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "aspect_bazel_lib+", + "bazel_tools", + "bazel_tools" + ], + [ + "aspect_rules_py+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "aspect_rules_py+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@pybind11_bazel+//:python_configure.bzl%extension": { + "general": { + "bzlTransitiveDigest": "VhEtmxw1yzb9rBZVsKTdti7p+nDM/Fv1p9TmKdO45+Q=", + "usagesDigest": "fycyB39YnXIJkfWCIXLUKJMZzANcuLy9ZE73hRucjFk=", + "recordedFileInputs": { + "@@pybind11_bazel+//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" + }, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_python": { + "repoRuleId": "@@pybind11_bazel+//:python_configure.bzl%python_configure", + "attributes": {} + }, + "pybind11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@pybind11_bazel+//:pybind11.BUILD", + "strip_prefix": "pybind11-2.11.1", + "urls": [ + "https://github.com/pybind/pybind11/archive/v2.11.1.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "pybind11_bazel+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_fuzzing+//fuzzing/private:extensions.bzl%non_module_dependencies": { + "general": { + "bzlTransitiveDigest": "CYUiFDCnL2VGx3uotIu/VuGabgObnZra2zzRUJCX0sU=", + "usagesDigest": "wy6ISK6UOcBEjj/mvJ/S3WeXoO67X+1llb9yPyFtPgc=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "platforms": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", + "https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz" + ], + "sha256": "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74" + } + }, + "rules_python": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "d70cd72a7a4880f0000a6346253414825c19cdd40a28289bdf67b8e6480edff8", + "strip_prefix": "rules_python-0.28.0", + "url": "https://github.com/bazelbuild/rules_python/releases/download/0.28.0/rules_python-0.28.0.tar.gz" + } + }, + "bazel_skylib": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz" + ] + } + }, + "com_google_absl": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/abseil/abseil-cpp/archive/refs/tags/20240116.1.zip" + ], + "strip_prefix": "abseil-cpp-20240116.1", + "integrity": "sha256-7capMWOvWyoYbUaHF/b+I2U6XLMaHmky8KugWvfXYuk=" + } + }, + "rules_fuzzing_oss_fuzz": { + "repoRuleId": "@@rules_fuzzing+//fuzzing/private/oss_fuzz:repository.bzl%oss_fuzz_repository", + "attributes": {} + }, + "honggfuzz": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@rules_fuzzing+//:honggfuzz.BUILD", + "sha256": "6b18ba13bc1f36b7b950c72d80f19ea67fbadc0ac0bb297ec89ad91f2eaa423e", + "url": "https://github.com/google/honggfuzz/archive/2.5.zip", + "strip_prefix": "honggfuzz-2.5" + } + }, + "rules_fuzzing_jazzer": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "ee6feb569d88962d59cb59e8a31eb9d007c82683f3ebc64955fd5b96f277eec2", + "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer/0.20.1/jazzer-0.20.1.jar" + } + }, + "rules_fuzzing_jazzer_api": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "f5a60242bc408f7fa20fccf10d6c5c5ea1fcb3c6f44642fec5af88373ae7aa1b", + "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer-api/0.20.1/jazzer-api-0.20.1.jar" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_fuzzing+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { "bzlTransitiveDigest": "03Qju4tW0vE+0RBuZGuV2A4Hx6AiSkdNahYvworx2aM=", @@ -684,6 +947,239 @@ ] } }, + "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { + "general": { + "bzlTransitiveDigest": "OwuVsAcKBjxuWv8V5VDzHh0ceFQgRzkZo6GnN62QRYM=", + "usagesDigest": "1ieIYuafZ1pmP+ncVvISMfB3Em0hv9LCmCRwIH7gL8E=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "cargo_bazel_bootstrap": { + "repoRuleId": "@@rules_rust+//cargo/private:cargo_bootstrap.bzl%cargo_bootstrap_repository", + "attributes": { + "srcs": [ + "@@rules_rust+//crate_universe:src/api.rs", + "@@rules_rust+//crate_universe:src/api/lockfile.rs", + "@@rules_rust+//crate_universe:src/cli.rs", + "@@rules_rust+//crate_universe:src/cli/generate.rs", + "@@rules_rust+//crate_universe:src/cli/query.rs", + "@@rules_rust+//crate_universe:src/cli/render.rs", + "@@rules_rust+//crate_universe:src/cli/splice.rs", + "@@rules_rust+//crate_universe:src/cli/vendor.rs", + "@@rules_rust+//crate_universe:src/config.rs", + "@@rules_rust+//crate_universe:src/context.rs", + "@@rules_rust+//crate_universe:src/context/crate_context.rs", + "@@rules_rust+//crate_universe:src/context/platforms.rs", + "@@rules_rust+//crate_universe:src/lib.rs", + "@@rules_rust+//crate_universe:src/lockfile.rs", + "@@rules_rust+//crate_universe:src/main.rs", + "@@rules_rust+//crate_universe:src/metadata.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_bin.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_resolver.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.bat", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.sh", + "@@rules_rust+//crate_universe:src/metadata/dependency.rs", + "@@rules_rust+//crate_universe:src/metadata/metadata_annotation.rs", + "@@rules_rust+//crate_universe:src/rendering.rs", + "@@rules_rust+//crate_universe:src/rendering/template_engine.rs", + "@@rules_rust+//crate_universe:src/rendering/templates/module_bzl.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/header.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/aliases_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/deps_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_git.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_http.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/vendor_module.j2", + "@@rules_rust+//crate_universe:src/rendering/verbatim/alias_rules.bzl", + "@@rules_rust+//crate_universe:src/select.rs", + "@@rules_rust+//crate_universe:src/splicing.rs", + "@@rules_rust+//crate_universe:src/splicing/cargo_config.rs", + "@@rules_rust+//crate_universe:src/splicing/crate_index_lookup.rs", + "@@rules_rust+//crate_universe:src/splicing/splicer.rs", + "@@rules_rust+//crate_universe:src/test.rs", + "@@rules_rust+//crate_universe:src/utils.rs", + "@@rules_rust+//crate_universe:src/utils/starlark.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/glob.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/label.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_dict.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_list.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_scalar.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_set.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/serialize.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/target_compatible_with.rs", + "@@rules_rust+//crate_universe:src/utils/symlink.rs", + "@@rules_rust+//crate_universe:src/utils/target_triple.rs" + ], + "binary": "cargo-bazel", + "cargo_lockfile": "@@rules_rust+//crate_universe:Cargo.lock", + "cargo_toml": "@@rules_rust+//crate_universe:Cargo.toml", + "version": "1.86.0", + "timeout": 900, + "rust_toolchain_cargo_template": "@rust_host_tools//:bin/{tool}", + "rust_toolchain_rustc_template": "@rust_host_tools//:bin/{tool}", + "compressed_windows_toolchain_names": false + } + } + }, + "moduleExtensionMetadata": { + "explicitRootModuleDirectDeps": [ + "cargo_bazel_bootstrap" + ], + "explicitRootModuleDirectDevDeps": [], + "useAllRepos": "NO", + "reproducible": false + }, + "recordedRepoMappingEntries": [ + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "bazel_features", + "bazel_features+" + ], + [ + "rules_rust+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_rust+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_rust+", + "cargo_bazel_bootstrap", + "rules_rust++cu_nr+cargo_bazel_bootstrap" + ], + [ + "rules_rust+", + "cui", + "rules_rust++cu+cui" + ], + [ + "rules_rust+", + "rrc", + "rules_rust++i2+rrc" + ], + [ + "rules_rust+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "rules_rust", + "rules_rust+" + ] + ] + } + }, + "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_ext": { + "general": { + "bzlTransitiveDigest": "9k8SLwP4ec9xXjFivq0Kn0FradTQhcEA+j/WsLxfVPM=", + "usagesDigest": "1G+0hoRkZx33aQUiAxx8GdGErOPGCHHQXn5lO7fZxgI=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "ferrocene_x86_64_unknown_linux_gnu_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_x86_64_unknown_linux_gnu", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ] + } + }, + "ferrocene_aarch64_unknown_linux_gnu_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_aarch64_unknown_linux_gnu", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ] + } + }, + "ferrocene_x86_64_pc_nto_qnx800_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_x86_64_pc_nto_qnx800", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:qnx" + ] + } + }, + "ferrocene_aarch64_unknown_nto_qnx800_rules_rust_miri": { + "repoRuleId": "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_rules_rust_miri_toolchain_repo", + "attributes": { + "ferrocene_repo_name": "ferrocene_aarch64_unknown_nto_qnx800", + "toolchain_name": "rust_ferrocene", + "env": {}, + "exec_compatible_with": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "target_compatible_with": [ + "@platforms//cpu:aarch64", + "@platforms//os:qnx" + ] + } + } + }, + "recordedRepoMappingEntries": [] + } + }, "@@score_toolchains_rust+//extensions:ferrocene_toolchain_ext.bzl%ferrocene_toolchain_ext": { "general": { "bzlTransitiveDigest": "9k8SLwP4ec9xXjFivq0Kn0FradTQhcEA+j/WsLxfVPM=", From b13e350492ec54d455c68bc07ea3a80e565a5e67 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:56:59 +0300 Subject: [PATCH 16/17] Address review: Python 3.12, Bazel 8.6.0, drop REUSE, rsync collection - Python: every S-CORE consumer defaults to 3.12, so the 3.11 toolchain, pip hub and lock are dropped; ruff and ty target 3.12. One interpreter version also keeps the qualified environment unambiguous. - Bazel 8.6.0 in both workspaces, matching score, persistency, time, kyron, orchestrator, baselibs and config_management. - REUSE.toml removed: the copyright checker does not need it and no other S-CORE module besides devcontainer uses REUSE. - tests.yml: JUnit results collected with rsync as suggested; the no-op --github_user/--github_repo arguments of the docs CLI are gone. - Verification report and constraints state the new environment. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- .bazelversion | 2 +- .github/workflows/tests.yml | 12 +--- MODULE.bazel | 32 +++------ MODULE.bazel.lock | 34 +++++----- REUSE.toml | 24 ------- docs/manual/constraints.rst | 2 +- docs/verification/verification_report.rst | 5 +- integration_tests/.bazelversion | 2 +- integration_tests/MODULE.bazel.lock | 12 ++-- pyproject.toml | 4 +- score_coverage/BUILD | 24 +++---- score_coverage/requirements_3_11.txt | 81 ----------------------- tools/BUILD | 1 - 13 files changed, 52 insertions(+), 183 deletions(-) delete mode 100644 REUSE.toml delete mode 100644 score_coverage/requirements_3_11.txt diff --git a/.bazelversion b/.bazelversion index df5119e..acd405b 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -8.7.0 +8.6.0 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a86d9a4..ac943c4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -66,15 +66,9 @@ jobs: - name: Collect JUnit results for the documentation build # docs-as-code links testcases to requirements from these files # (tests-report//test.xml). - # bazel-testlogs is a symlink into the output tree, so paths are - # rewritten here instead of cd-ing into it. run: | - rm -rf tests-report - find -L bazel-testlogs/score_coverage -name test.xml | while read -r f; do - dest="tests-report/${f#bazel-testlogs/}" - mkdir -p "$(dirname "$dest")" - cp "$f" "$dest" - done + rm -rf tests-report && mkdir -p tests-report/score_coverage + rsync -amL --include='*/' --include='test.xml' --exclude='*' bazel-testlogs/score_coverage/ tests-report/score_coverage/ echo "collected $(find tests-report -name test.xml | wc -l) test.xml files" - uses: actions/upload-artifact@v4 with: @@ -119,6 +113,6 @@ jobs: with: # docs() lives in //docs (the root package must not load dev deps), so # the output is docs/_build; the reusable workflow archives _build. - bazel-target: "//docs:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }} && mv docs/_build _build" + bazel-target: "//docs:docs && mv docs/_build _build" tests-report-artifact: tests-report retention-days: 3 diff --git a/MODULE.bazel b/MODULE.bazel index f7ef32d..f40ce2a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -29,17 +29,15 @@ bazel_dep(name = "rules_rust", version = "0.68.2-score") ############################################################################### # Python toolchain ############################################################################### -DEFAULT_PYTHON_VERSION = "3.12" +# Python 3.12 only: every S-CORE consumer defaults to 3.12, and the qualified +# environment pins one interpreter version. +PYTHON_VERSION = "3.12" python = use_extension("@rules_python//python/extensions:python.bzl", "python") -python.toolchain( - configure_coverage_tool = True, - python_version = "3.11", -) python.toolchain( configure_coverage_tool = True, is_default = True, - python_version = DEFAULT_PYTHON_VERSION, + python_version = PYTHON_VERSION, ) ############################################################################### @@ -48,21 +46,13 @@ python.toolchain( # score_tooling < 3. ############################################################################### pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") - -[ - pip.parse( - envsubst = ["PIP_INDEX_URL"], - extra_pip_args = ["--index-url=${PIP_INDEX_URL:-https://pypi.org/simple/}"], - hub_name = "pip_score_coverage", - python_version = "3.{}".format(version), - requirements_lock = "//score_coverage:requirements_3_{}.txt".format(version), - ) - for version in [ - "11", - "12", - ] -] - +pip.parse( + envsubst = ["PIP_INDEX_URL"], + extra_pip_args = ["--index-url=${PIP_INDEX_URL:-https://pypi.org/simple/}"], + hub_name = "pip_score_coverage", + python_version = PYTHON_VERSION, + requirements_lock = "//score_coverage:requirements_3_12.txt", +) use_repo(pip, "pip_score_coverage") ############################################################################### diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index ff8b2c3..cc17feb 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -857,7 +857,7 @@ "moduleExtensions": { "@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": { "general": { - "bzlTransitiveDigest": "uvKBzzynYgAz3E6uzLKr7emNyi+uIepPAiUlbUilv2w=", + "bzlTransitiveDigest": "qh4ujJcmdcm37/781UtOijzSUpQ4dDmcmRF/yI/+H2I=", "usagesDigest": "sj4kz7yaVclWMuWhUhSLq0bVH7+HrkWyMdODMeA7Zhw=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1024,7 +1024,7 @@ }, "@@aspect_rules_js+//npm:extensions.bzl%pnpm": { "general": { - "bzlTransitiveDigest": "8vuF34437k2v0cxa1rCNL0YtLzocf2Q3v6QZpSjOiS0=", + "bzlTransitiveDigest": "6CnPmgfrbmQ1ksmozHPuP2j3+BnRuCzw55p9IL41/kI=", "usagesDigest": "kbjSw2REjlSC0HtTZDf2p+l/dmiMt3NHLoiWEXYAoQI=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1154,7 +1154,7 @@ }, "@@aspect_rules_py+//py:extensions.bzl%py_tools": { "general": { - "bzlTransitiveDigest": "13DN9p/N8uOU1SMHBh9nZIlptJtS3qP63UkXe+NH8SQ=", + "bzlTransitiveDigest": "VfJYy1B5v3oMq9SNoz4hGTKf+uJgqbxHJYD8rdhivHo=", "usagesDigest": "NC1b49l5tenTBVWEUGzzC0j5Kg1GH+l5lBw5JRCldIU=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1264,7 +1264,7 @@ }, "@@aspect_rules_ts+//ts:extensions.bzl%ext": { "general": { - "bzlTransitiveDigest": "LW1zC7Im6EhnBiPOn9FBRp3lYTjaUmo9dSdS2dJvXH0=", + "bzlTransitiveDigest": "U195YDrpWzfnEHQ+ASO5i6qLqeDF2eBcqVDryoKZRKY=", "usagesDigest": "caXVbnxEUN71ZxydJbg6pZ8NaFPVDbNp12wS9TMC824=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1330,7 +1330,7 @@ }, "@@bazel_jar_jar+//internal:non_module_deps.bzl%non_module_deps": { "general": { - "bzlTransitiveDigest": "IfOiDBJwnddmiy546C4iX/vpN6o6p7joO6VC37WBYhw=", + "bzlTransitiveDigest": "VBviAE+Wh71Wczft5g7JzY9S5ZeH0iPXQyKjCeb5V1I=", "usagesDigest": "P1uSZ4XnqOp90Mkh0A4Nrjx12Bgz/iceROnSbTsawGk=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1446,7 +1446,7 @@ }, "@@cel-spec+//:extensions.bzl%non_module_dependencies": { "general": { - "bzlTransitiveDigest": "43iembF10pz9FJIRseDrjWB9kp/ODYb+7FBYAtvSt3Q=", + "bzlTransitiveDigest": "49v9UE4eBOMtW8ATb+3VHdFaCc//dFbCS+ZQW0HIKNE=", "usagesDigest": "HFQJtQrL9nKaFZEjgwaHVMHALMW+cafu696xy7J4ueM=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1643,7 +1643,7 @@ }, "@@envoy_api+//bazel:repositories.bzl%non_module_deps": { "general": { - "bzlTransitiveDigest": "6K7ebXGqru7CSFs/38zH8GGvDgewYM/qjlz7Lqm2XXY=", + "bzlTransitiveDigest": "VzLskQI4CUM3XVC+wpI1FIlKua2ToSOGRJ8G+0HfaV8=", "usagesDigest": "cxAa0VVo9d210JBUBw6wpuGp8jg+ltqw3tzH0tPtIEg=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1865,7 +1865,7 @@ }, "@@pybind11_bazel+//:internal_configure.bzl%internal_configure_extension": { "general": { - "bzlTransitiveDigest": "qQEChI+4ZOV4xn6LulonXjQy5+Z0z1gELJDmBl1ePy4=", + "bzlTransitiveDigest": "doxdX2drQaP4mqY6tnLSIGABgYuXR5Hpp6Wpfyb23Rg=", "usagesDigest": "tVQNvLoXMWAbiK39am3yovKGpwINdftfn7RpDyN+JZc=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1892,7 +1892,7 @@ }, "@@rules_android+//bzlmod_extensions:apksig.bzl%apksig_extension": { "general": { - "bzlTransitiveDigest": "c6mHv2L0P+tWDe7lh/mpzTEedvukBq17Gr9gP16UdxU=", + "bzlTransitiveDigest": "By9qVNN7G4oL1vYOJXye7Dp/CbR2ar9oxAW8WXAVcVw=", "usagesDigest": "xq6OVkELeJvOgYo3oY/sUBsGFbcqdV+9BYiNgSPV/po=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1917,7 +1917,7 @@ }, "@@rules_android+//bzlmod_extensions:com_android_dex.bzl%com_android_dex_extension": { "general": { - "bzlTransitiveDigest": "BbM3I4LvjudqQ6/IwaP/LaJXdj2EPfqPleTjiFG4Lg0=", + "bzlTransitiveDigest": "rvWbJQc8jInfIAaXIMhSOqUlwM9HVeLey6q0ISvg08Y=", "usagesDigest": "toF8IFMu98H/VU2p1sfVC5fVXVYJunpbbmtM6tOsQXY=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -2050,7 +2050,7 @@ }, "@@rules_apple+//apple:extensions.bzl%non_module_deps": { "general": { - "bzlTransitiveDigest": "ul8vHGy74hBD66XzLuB09UmLKPv7O6yFI8pwN7jMWdc=", + "bzlTransitiveDigest": "4xtddSlWIQdtVNVuvOI62fJfQVETHZCVWFvYYwQHMR4=", "usagesDigest": "M3VqFpeTCo4qmrNKGZw0dxBHvTYDrfV3cscGzlSAhQ4=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -3348,7 +3348,7 @@ }, "@@rules_foreign_cc+//foreign_cc:extensions.bzl%tools": { "general": { - "bzlTransitiveDigest": "otSpeDAxv5Qioli4+V+p43+gUyiUU17e2kHR+4wQFAs=", + "bzlTransitiveDigest": "Zyhrp9ZD1OkHXRpL1OG37IUnUmkiGpNNHJeqFTQ8aAM=", "usagesDigest": "9LXdVp01HkdYQT8gYPjYLO6VLVJHo9uFfxWaU1ymiRE=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -3662,7 +3662,7 @@ }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "ipDzEAtocAfIXfJbcbskFj4cGI7OMiupxpyQcliozJ4=", + "bzlTransitiveDigest": "HJP3wKFbPhB1mSYjJS6kbXEiP+OQxvsBdqpvyJN6I3s=", "usagesDigest": "qTwqmKKUfWcPdvM0waG+CPWrxsbeAWVeUxavm7tEk9E=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -3877,7 +3877,7 @@ }, "@@rules_python+//python/extensions:config.bzl%config": { "general": { - "bzlTransitiveDigest": "EcMcbtKZvYmd5Mi1Fpg4EeBBztLHEE5tjO5tLDBYDuU=", + "bzlTransitiveDigest": "TRGIl0CDmorwyNiblOYyhWuyKzi/kWFHT2uIofq7o9Y=", "usagesDigest": "lDbpRfhoWmZCHSaNxwZv/8fF2y0wu2th0G0f/uqX7VM=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -4153,7 +4153,7 @@ }, "@@rules_rust+//crate_universe:extensions.bzl%crate": { "general": { - "bzlTransitiveDigest": "wmQpSLxkLUUlomh9A7MYN+AoKG2QJIYABZudGJwn0K4=", + "bzlTransitiveDigest": "8QfSf1kvCWSgBnQ9KgYFpBqDkBtjBBPru5O7fUT2Wyw=", "usagesDigest": "wKDir5lt64+Fed9OkYwXdJMDafXvpjxTmVvguHrnF8g=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -5285,7 +5285,7 @@ }, "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { "general": { - "bzlTransitiveDigest": "ExCb8VL1oj/L1Qkv9O9P1KpjLDMpwnpKMeYXxozBVfw=", + "bzlTransitiveDigest": "Z7p+lTI47yj/ZpgLpEKob+c3lW+QLrMSpbG4Qo89LIo=", "usagesDigest": "1ieIYuafZ1pmP+ncVvISMfB3Em0hv9LCmCRwIH7gL8E=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -5442,7 +5442,7 @@ }, "@@rules_swift+//swift:extensions.bzl%non_module_deps": { "general": { - "bzlTransitiveDigest": "TdyDy4TBpjOHwrF4hSiQwGdsTAvssvyD6vUJBx+7nt4=", + "bzlTransitiveDigest": "6axDCXf6fQoPav8hojnUBxGA0FAMqLvtpC1cRsisCdw=", "usagesDigest": "mhACFnrdMv9Wi0Mt67bxocJqviRkDSV+Ee5Mqdj5akA=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/REUSE.toml b/REUSE.toml deleted file mode 100644 index aeeb922..0000000 --- a/REUSE.toml +++ /dev/null @@ -1,24 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -version = 1 - -[[annotations]] -path = [ - "**/.bazelversion", - "**/MODULE.bazel.lock", - "score_coverage/requirements.in", - "integration_tests/tools/coverage/coverage_justifications.yaml", -] -SPDX-FileCopyrightText = "Copyright (c) 2026 Contributors to the Eclipse Foundation" -SPDX-License-Identifier = "Apache-2.0" diff --git a/docs/manual/constraints.rst b/docs/manual/constraints.rst index e9e8db8..714024a 100644 --- a/docs/manual/constraints.rst +++ b/docs/manual/constraints.rst @@ -28,7 +28,7 @@ CSTR-01 Qualified environment ----------------------------- Use the tool only in the environment it was validated in: Linux x86_64 host, -Bazel 8.x, ``toolchains_llvm`` 1.8.0 with LLVM 22.1.7 for C++, the standard +Bazel 8.6, ``toolchains_llvm`` 1.8.0 with LLVM 22.1.7 for C++, the standard Ferrocene toolchain of ``score_toolchains_rust`` 0.10.0 or newer (built by ``ferrocene_toolchain_builder`` 1.3.1 or newer) for Rust. QNX on-target coverage is outside this environment. Mitigates ERR-08. diff --git a/docs/verification/verification_report.rst b/docs/verification/verification_report.rst index ec8aae7..5f8edb8 100644 --- a/docs/verification/verification_report.rst +++ b/docs/verification/verification_report.rst @@ -30,10 +30,9 @@ platform documentation refers to it as the evidence of the validation. Scope and environment --------------------- -Validated environment: Linux x86_64, Bazel 8.7.0, ``toolchains_llvm`` 1.8.0 +Validated environment: Linux x86_64, Bazel 8.6.0, ``toolchains_llvm`` 1.8.0 with LLVM 22.1.7, ``score_toolchains_rust`` 0.10.0 (Ferrocene built by -``ferrocene_toolchain_builder`` 1.3.1), Python 3.11 and 3.12 (``rules_python`` -1.8.5), ``rules_rust`` 0.68.2-score. +``ferrocene_toolchain_builder`` 1.3.1), Python 3.12 (``rules_python`` 1.8.5), ``rules_rust`` 0.68.2-score. Test inventory -------------- diff --git a/integration_tests/.bazelversion b/integration_tests/.bazelversion index df5119e..acd405b 100644 --- a/integration_tests/.bazelversion +++ b/integration_tests/.bazelversion @@ -1 +1 @@ -8.7.0 +8.6.0 diff --git a/integration_tests/MODULE.bazel.lock b/integration_tests/MODULE.bazel.lock index 4eb6a6d..2d25a5a 100644 --- a/integration_tests/MODULE.bazel.lock +++ b/integration_tests/MODULE.bazel.lock @@ -382,7 +382,7 @@ "moduleExtensions": { "@@aspect_rules_py+//py:extensions.bzl%py_tools": { "general": { - "bzlTransitiveDigest": "SnsNf8tZ93pztlEcTNvkFJ6dCCRMBCipDvH33ozbho0=", + "bzlTransitiveDigest": "uh/OnBspuZDYzeAio/cE3jPFXYyt5/s0JxMJ1pkhoMo=", "usagesDigest": "NC1b49l5tenTBVWEUGzzC0j5Kg1GH+l5lBw5JRCldIU=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -492,7 +492,7 @@ }, "@@pybind11_bazel+//:python_configure.bzl%extension": { "general": { - "bzlTransitiveDigest": "VhEtmxw1yzb9rBZVsKTdti7p+nDM/Fv1p9TmKdO45+Q=", + "bzlTransitiveDigest": "D2/qWHU6yQFwRG7Bb+caqrYMha5avsASao2vERrxK24=", "usagesDigest": "fycyB39YnXIJkfWCIXLUKJMZzANcuLy9ZE73hRucjFk=", "recordedFileInputs": { "@@pybind11_bazel+//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" @@ -526,7 +526,7 @@ }, "@@rules_fuzzing+//fuzzing/private:extensions.bzl%non_module_dependencies": { "general": { - "bzlTransitiveDigest": "CYUiFDCnL2VGx3uotIu/VuGabgObnZra2zzRUJCX0sU=", + "bzlTransitiveDigest": "4LouzhF/yT117s7peGnNs9ROomiJXC6Zl5R0oI21jho=", "usagesDigest": "wy6ISK6UOcBEjj/mvJ/S3WeXoO67X+1llb9yPyFtPgc=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -609,7 +609,7 @@ }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "03Qju4tW0vE+0RBuZGuV2A4Hx6AiSkdNahYvworx2aM=", + "bzlTransitiveDigest": "nvW/NrBXlAmiQw99EMGKkLaD2KbNp2mQDlxdfpr+0Ls=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -673,7 +673,7 @@ }, "@@rules_python+//python/extensions:config.bzl%config": { "general": { - "bzlTransitiveDigest": "EcMcbtKZvYmd5Mi1Fpg4EeBBztLHEE5tjO5tLDBYDuU=", + "bzlTransitiveDigest": "TRGIl0CDmorwyNiblOYyhWuyKzi/kWFHT2uIofq7o9Y=", "usagesDigest": "lDbpRfhoWmZCHSaNxwZv/8fF2y0wu2th0G0f/uqX7VM=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -949,7 +949,7 @@ }, "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { "general": { - "bzlTransitiveDigest": "OwuVsAcKBjxuWv8V5VDzHh0ceFQgRzkZo6GnN62QRYM=", + "bzlTransitiveDigest": "WNUB8qSmmMiopwWLL8yFJfPo7jD6XWsX7vPeKfhaw5A=", "usagesDigest": "1ieIYuafZ1pmP+ncVvISMfB3Em0hv9LCmCRwIH7gL8E=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/pyproject.toml b/pyproject.toml index 4b104cd..b684a7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ [tool.ruff] line-length = 120 -target-version = "py311" # oldest interpreter the module supports (see MODULE.bazel) +target-version = "py312" # the only interpreter the module supports (see MODULE.bazel) extend-exclude = ["__pycache__", ".*", "bazel-*", "integration_tests"] [tool.ruff.lint] @@ -41,7 +41,7 @@ known-first-party = ["score_coverage"] max-complexity = 15 [tool.ty.environment] -python-version = "3.11" +python-version = "3.12" [tool.pylint.format] max-line-length = 120 diff --git a/score_coverage/BUILD b/score_coverage/BUILD index a4d35a3..4af59ae 100644 --- a/score_coverage/BUILD +++ b/score_coverage/BUILD @@ -116,19 +116,11 @@ py_library( ) # In order to update the requirements, change the `requirements.in` file and run: -# `bazel run //score_coverage:requirements_3_XX.update --@@rules_python+//python/config_settings:python_version=3.XX`. -[ - compile_pip_requirements( - name = "requirements_3_{}".format(version), - src = "requirements.in", - python_version = "3.{}".format(version), - requirements_txt = "requirements_3_{}.txt".format(version), - tags = [ - "manual", - ], - ) - for version in [ - "11", - "12", - ] -] +# `bazel run //score_coverage:requirements_3_12.update`. +compile_pip_requirements( + name = "requirements_3_12", + src = "requirements.in", + python_version = "3.12", + requirements_txt = "requirements_3_12.txt", + tags = ["manual"], +) diff --git a/score_coverage/requirements_3_11.txt b/score_coverage/requirements_3_11.txt deleted file mode 100644 index 5017dc3..0000000 --- a/score_coverage/requirements_3_11.txt +++ /dev/null @@ -1,81 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# bazel run //coverage:requirements_3_11.update -# -pyyaml==6.0.3 \ - --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ - --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ - --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ - --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ - --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ - --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ - --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ - --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ - --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ - --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ - --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ - --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ - --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ - --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ - --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ - --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ - --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ - --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ - --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ - --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ - --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ - --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ - --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ - --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ - --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ - --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ - --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ - --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ - --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ - --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ - --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ - --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ - --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ - --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ - --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ - --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ - --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ - --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ - --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ - --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ - --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ - --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ - --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ - --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ - --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ - --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ - --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ - --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ - --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ - --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ - --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ - --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ - --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ - --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ - --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ - --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ - --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ - --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ - --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ - --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ - --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ - --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ - --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ - --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ - --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ - --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ - --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ - --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ - --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ - --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ - --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ - --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ - --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 - # via -r coverage/requirements.in diff --git a/tools/BUILD b/tools/BUILD index 95a9119..d7ce692 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -27,7 +27,6 @@ copyright_checker( "BUILD", "MODULE.bazel", "README.md", - "REUSE.toml", "defs.bzl", "docs", "integration_tests", From 7e36b681f5c60cfdac9890f71b56bec8c96350d8 Mon Sep 17 00:00:00 2001 From: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:10:51 +0300 Subject: [PATCH 17/17] Refresh MODULE.bazel.lock after the switch to Bazel 8.6.0 The bzlTransitiveDigest of the rules_distroless apt extension changes with the Bazel version; CI's lockfile check flagged the stale value. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com> --- MODULE.bazel.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index cc17feb..939ccf6 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -2103,7 +2103,7 @@ }, "@@rules_distroless+//apt:extensions.bzl%apt": { "general": { - "bzlTransitiveDigest": "9dVC3uLi04oXYQ63WRNkwE2eWtRHXoyVy1wiyRFJqcg=", + "bzlTransitiveDigest": "eH+PnBEEuvk2JyknjwUNuJrwPPryyUkKwi8qCYCVrvY=", "usagesDigest": "niPoYX/Hk8+YvcLiQxLt75319UN95ORAe5Ii2c+VVlk=", "recordedFileInputs": { "@@score_tooling+//third_party/docs_runtime/manifest.yaml": "f8bd762e0dcaf3150504eca8dfd60819fc3be428d3a8b58d1d9cc52d68e16a45",