Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .bazelignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

python_basics/integration_tests
starpls/integration_tests
# Separate local Bazel module: consumer-style integration test for the
# LLVM coverage pipeline (run from its own directory)
coverage/integration_tests
# Should be run from its own directory (separate Module)
bazel/rules/rules_score/test
# Separate local Bazel modules (use @seooc// and @some_other_library// instead)
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ jobs:
run: |
cd cr_checker/tests
bazel test //...
- name: coverage
run: |
coverage/integration_tests/run_integration_test.sh
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.0
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ user.bazelrc
__pycache__
.ruff_cache/
coverage-html/

# Outputs of coverage/integration_tests/run_integration_test.sh
coverage/integration_tests/coverage_artifacts.zip
coverage/integration_tests/lcov.dat
coverage/integration_tests/coverage_linux/
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ repos:
- id: check-executables-have-shebangs
- id: check-added-large-files
args: [--maxkb=100, --enforce-all] # increase or add git lfs if too strict
exclude: org.eclipse.dash.licenses-1.1.0.jar|blanket_index.html
exclude: org.eclipse.dash.licenses-1.1.0.jar
- repo: local
hooks:
- id: copyright
Expand Down
24 changes: 22 additions & 2 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ module(
###############################################################################
bazel_dep(name = "aspect_rules_py", version = "1.4.0") # py_binary/py_library loads in cli_helper.bzl:13, cr_checker.bzl:16, defs.bzl:16, etc.

bazel_dep(name = "aspect_rules_lint", version = "2.5.0", dev_dependency = True)

# NOT a dev_dependency: third_party/format/macros.bzl (use_format_targets,
# public consumer API) load()s @aspect_rules_lint//format:defs.bzl, which
# must resolve from score_tooling's own repo mapping in CONSUMER builds too.
bazel_dep(name = "aspect_rules_lint", version = "2.5.0")
bazel_dep(name = "rules_multitool", version = "1.9.0")
bazel_dep(name = "rules_java", version = "8.15.1") # required for dash

Expand Down Expand Up @@ -215,6 +217,24 @@ pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
# Lobster Bazel
use_repo(pip, "pip_tooling")

# Coverage pipeline Python deps (pyyaml for justify.py), kept in its own hub
# so the reusable coverage component stays independent of the other tools.
[
pip.parse(
envsubst = ["PIP_INDEX_URL"],
extra_pip_args = ["--index-url=${PIP_INDEX_URL:-https://pypi.org/simple/}"],
hub_name = "pip_coverage",
python_version = "3.{}".format(version),
requirements_lock = "//coverage:requirements_3_{}.txt".format(version),
)
for version in [
"11",
"12",
]
]

use_repo(pip, "pip_coverage")

[
pip.parse(
envsubst = ["PIP_INDEX_URL"],
Expand Down
55 changes: 33 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,48 +38,58 @@ See the individual README files for detailed usage instructions and configuratio
| **python_basics** | Python development utilities and testing | [README](python_basics/README.md) |
| **starpls** | Starlark language server support | [README](starpls/README.md) |
| **tools** | Formatters & Linters | [README](tools/README.md) |
| **coverage** | Rust + Python coverage reports | [README](coverage/README.md) |
| **coverage** | Unified LLVM source-based coverage (C++ + Rust) | [README](coverage/README.md) |

## Coverage

Generate a combined Rust + Python HTML coverage report for `plantuml`, `validation`,
and `manual_analysis`:
The `coverage/` module provides the reusable LLVM source-based coverage pipeline
used across S-CORE repositories: one report covering C++ and Rust (line + branch),
exact 0% entries for untested in-scope files, a justification system
(`COV_JUSTIFIED` markers + YAML), and effective-coverage gating. See the
[adoption guide](coverage/README.md) and the
[mechanism deep-dive](coverage/COVERAGE_GUIDE.md).

> **Breaking change:** the former Ferrocene `symbol-report`/`blanket` workflow
> (`rust_coverage_report`, `//coverage:ferrocene_report`) was removed. The LLVM
> pipeline replaces it with unified C++ + Rust reports; see the
> [adoption guide](coverage/README.md) for migration.

Generate a combined Rust + Python HTML coverage report for this repository's own
tools (`plantuml`, `validation`, `manual_analysis`):

```bash
bazel run //coverage:combined_report
```

See [coverage/README.md](coverage/README.md) for full details, options, and the
Ferrocene Rust coverage workflow.

## Usage Examples

Load tools in your `BUILD` files:

```starlark
load("@score_tooling//:defs.bzl", "score_py_pytest")
load("@score_tooling//:defs.bzl", "cli_tool")
load("@score_tooling//coverage:coverage.bzl", "rust_coverage_report")
load("@score_tooling//coverage:defs.bzl", "score_coverage_reporter", "score_coverage_scope")
```

Create a repo-local coverage target:
Declare the coverage scope and reporter for your repository (see the
[coverage adoption guide](coverage/README.md) for the full setup, including the
required `.bazelrc` configuration and toolchains):

```starlark
rust_coverage_report(
name = "rust_coverage",
bazel_configs = [
"ferrocene-x86_64-linux",
"ferrocene-coverage",
],
query = 'kind("rust_test", //...)',
min_line_coverage = "80",
score_coverage_scope(
name = "coverage_scope",
testonly = True,
deps = ["//src/mylib"],
)
```

Then run:

```bash
bazel run //:rust_coverage -- --min-line-coverage 80
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",
)
```

## Upgrading from separate MODULES
Expand All @@ -100,7 +110,8 @@ The available import targets are:
- dash_license_checker
- cli_helper
- setup_starpls
- rust_coverage_report
- score_coverage_scope
- score_coverage_reporter

Formatting, linting, and cr_checker are no longer re-exported from `defs.bzl`; use
`@score_tooling//third_party/format:macros.bzl`, `@score_tooling//third_party/lint:macros.bzl`,
Expand Down
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = ["cr_checker/resources/config.json",
"**/.bazelversion",
"coverage/tests/fixtures/symbol_report.json",
"coverage/requirements.in",
"python_basics/.bazelversion",
"python_basics/integration_tests/venv-with-extra-requirements/requirements.in",
"python_basics/requirements.in",
Expand Down
116 changes: 105 additions & 11 deletions coverage/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,120 @@
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

load("@pip_coverage//:requirements.bzl", "requirement")
load("@rules_cc//cc/toolchains:args.bzl", "cc_args")
load("@rules_cc//cc/toolchains:feature.bzl", "cc_feature")
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"])

exports_files([
"ferrocene_report_wrapper.sh.tpl",
"scripts/normalize_symbol_report.py",
"scripts/parse_line_coverage.py",
])
# =============================================================================
# LLVM source-based coverage pipeline (C++ + Rust).
# Consumer-facing API lives in defs.bzl; see README.md for the adoption guide
# and COVERAGE_GUIDE.md for how the pipeline works.
# =============================================================================

# Per-test coverage output generator. Referenced by consumers directly:
# coverage:llvm_cov --coverage_output_generator=@score_tooling//coverage:merger
# It 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"],
)

sh_binary(
name = "ferrocene_report",
srcs = ["ferrocene_report.sh"],
data = [
"scripts/normalize_symbol_report.py",
"scripts/parse_line_coverage.py",
name = "generate_coverage_html",
srcs = ["generate_coverage_html.sh"],
)

# Import-only libraries so unit tests can `from coverage.merger import ...`.
py_library(
name = "merger_lib",
srcs = ["merger.py"],
imports = [".."],
)

py_library(
name = "reporter_lib",
srcs = ["reporter.py"],
imports = [".."],
deps = ["@rules_python//python/runfiles"],
)

# 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.
# Consumers reference this feature from their toolchains_llvm extension config:
# llvm.toolchain(extra_known_features =
# ["@score_tooling//coverage:enable_llvm_coverage_for_death_tests"], ...)
cc_args(
name = "runtime_relocation_args",
actions = [
"@rules_cc//cc/toolchains/actions:compile_actions",
"@rules_cc//cc/toolchains/actions:link_actions",
],
visibility = ["//visibility:public"],
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",
)

# In order to update the requirements, change the `requirements.in` file and run:
# `bazel run //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",
]
]

# =============================================================================
# Legacy helpers (kept):
# - llvm_profile_wrapper: still referenced by downstream ferrocene-coverage
# configs (test --run_under=...) during migration to the LLVM pipeline.
# - combined_report: Rust + Python lcov/genhtml report for THIS repository's
# own tools, wired into deploy_docs.yml.
# =============================================================================

sh_binary(
name = "llvm_profile_wrapper",
srcs = ["llvm_profile_wrapper.sh"],
Expand Down
Loading
Loading