Skip to content
Merged
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
21 changes: 16 additions & 5 deletions docs/architecture/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ justification and gating layer on top. It has two phases.
[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)
[score_coverage_scope\naspect] --> (allowlist.txt\npath_map.txt\nobjects.txt\nsource files)
(coverage.dat zip\nprofdata + meta.json) --> [reporter.py\n--coverage_report_generator]
(allowlist.txt\nobjects.txt) --> [reporter.py\n--coverage_report_generator]
(allowlist.txt\npath_map.txt\nobjects.txt\nsource files) --> [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" {
Expand Down Expand Up @@ -79,9 +79,20 @@ workspace, does four things:

**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.
dependency graph from the listed production targets and collects every source
file a workspace target declares (including headers it vendors from an
external repository). Everything else, test sources, googletest, external
dependencies, headers a wrapper rule only forwards, is excluded. For headers
exposed through ``strip_include_prefix`` / ``include_prefix`` the aspect also
writes a path map from the generated ``_virtual_includes/`` path the compiler
records to the declared header, and it exports the source files themselves.

**Sources.** The reporter does not read sources through the workspace
directory: generated headers and external repositories are not there at
report time. It links every in-scope file from its runfiles into a staging
directory laid out like the coverage mapping expects, points llvm-cov at that
directory, and afterwards files the HTML pages under canonical paths so the
archive is machine-independent and every index link resolves.

**Baseline.** A file that no test executes produces no profile data. The scope
aspect therefore also collects the compiled archives and executables, and the
Expand Down
52 changes: 41 additions & 11 deletions docs/manual/known_problems.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,47 @@ stay listed with their upstream references.
- 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.
* - **Vendored headers appear under their virtual-includes path.** A header
compiled through ``strip_include_prefix`` is reported as
``<pkg>/_virtual_includes/<target>/<path>``, not under the label it was
declared with, because that is the identity the compiler records.
- Report rows named ``_virtual_includes``.
- Expected; justifications for such lines must use the reported path.
* - **An archive is rejected by llvm-cov** because one member has no
coverage mapping: the ``lib.rmeta`` of a Rust rlib, or the object of an
empty translation unit.
- ``no coverage data found`` on an archive; untested files of that library
missing from the report.
- Handled since the pipeline passes only members with a mapping to
llvm-cov; if seen, the installed version predates the fix.
* - **A header compiled under two different paths is reported once.** When
a translation unit includes a header through its ``_virtual_includes/``
path and another through the declared path, the compiler produces two
coverage entries for one file; the reporter keeps the declared-path
variant and drops the other.
- ``WARNING: <file> is compiled under several paths`` in the reporter log.
- Expected; hits recorded only through the dropped variant are not
counted. Include the header consistently.
* - **An in-scope header is absent from the report.** A header no
translation unit includes, or one that contains only templates that are
never instantiated, produces no code and therefore no coverage mapping;
``llvm-cov`` cannot show it, not even at 0 %.
- The file is named in the job summary under "In-scope files without
coverage data", in ``unmapped_files.txt`` of the archive and in a
reporter ``WARNING``. Headers whose same-named source file has data
(declaration-only) and placeholder sources that were compiled but hold
no code of their own are listed in the same file under their own
category and are not findings.
- Decide per file: write a test that instantiates it (it is shipped API),
or remove it from the target's ``hdrs`` (it is not needed).
* - **A header reached through several targets is compiled under several
names.** Virtual-include trees of targets outside the scope are
resolved to the declared header by their path tail; if two in-scope
files share that tail the header stays unresolved.
- ``WARNING: ... matches several in-scope files`` in the reporter log; the
header appears as ``no-data``.
- Rename one of the files, or declare the header only once.
* - **A source could not be staged for llvm-cov.** The reporter reads the
sources from the scope's exported files; a file that is neither there
nor in the workspace directory gets no HTML page (its numbers stay in
the index and the LCOV).
- ``WARNING: N in-scope sources were not found`` in the reporter log, an
index row without a link.
- Report it; every declared source is expected to be exported.
* - **Instrumentation filter appears ignored.**
- ``--instrumentation_filter`` has no visible effect.
- Expected: ``--experimental_use_llvm_covmap`` instruments everything;
Expand Down
5 changes: 3 additions & 2 deletions docs/manual/user_manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,9 @@ Command reference
* - ``--yaml <path>``
- Justification YAML, relative to the workspace root.
* - ``--archive-dir <dir>``
- Assemble HTML report, ``coverage_report.dat`` (LCOV), justification
report and JUnit XMLs into ``<dir>`` for artifact upload.
- Assemble HTML report, ``coverage_report.dat`` (LCOV),
``unmapped_files.txt`` (in-scope files without any coverage data),
justification report and JUnit XMLs into ``<dir>`` for artifact upload.
* - ``--archive <name>``
- Same content as a local ``<name>.zip`` (do not upload it: upload-artifact
zips again).
Expand Down
131 changes: 130 additions & 1 deletion docs/release/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,136 @@ Release notes
:security: NO
:realizes: wp__module_sw_release_note

0.1.0 (unreleased)
0.2.0 (unreleased)
------------------

In plain words
~~~~~~~~~~~~~~

This release fixes what the first baselibs reports showed, and adds one new
piece of information to every report.

**Third-party code no longer leaks into a module's report.** A module that
wraps a third-party library (baselibs wraps OpenSSL this way) got that
library's 72 header files counted as its own code, at 0 %. They are gone.
Only files a module lists itself in its build targets are part of its
report.

**Every link in the HTML report opens.** Some rows of the report pointed at
pages that had never been generated: files that Bazel generates during the
build and files from other repositories were not readable at the moment the
report was produced. The report now brings every source file along, so every
row opens. File names in the report are the paths of the source files, no
longer build-internal paths such as ``bazel-out/.../_virtual_includes/...``.
If a justification was written against such a build-internal path, it needs
the source path now.

**Untested files that the old report could not show are listed.** This is
the new piece. A coverage tool can only measure files that were compiled into
a test or a library. A file that nothing compiles has no lines to count, and
llvm-cov cannot show it, not even at 0 %. Until now such files were simply
absent from the report and nobody noticed. The report now lists them,
see :ref:`unmapped_files` below.

**Headers tested through a test-only twin target are measured.** A common
pattern declares a library's headers a second time in a test-only target with
test flags (baselibs' ``futurecpp_internal``). Tests then compile the headers
through that second target, and the report did not recognise the result as
belonging to the library: 172 futurecpp headers appeared untested. They are
now attributed to the library's declared header files.

**More untested files show their 0 %.** A library archive with one object that
contains no code (typical for header-only libraries, see below) was rejected
by llvm-cov as a whole, so the other files of that library lost their 0 %
entries. This is fixed; in baselibs 15 files reappeared.

.. _unmapped_files:

The file ``unmapped_files.txt``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The archive of every coverage run now contains ``unmapped_files.txt``, and the
job summary shows the same information as a table row and three collapsible
sections. It lists every file that belongs to the module's coverage scope but
for which no coverage data exists anywhere: no test and no library contains
compiled code from it. Such a file counts in no percentage. Each line reads
``<category>`` TAB ``<file>``; there are three categories.

.. list-table::
:header-rows: 1
:widths: 22 48 30

* - Category
- What it means
- What to do
* - ``no-data``
- **The findings.** Nothing in the module compiles this file: no source
includes the header, or it holds only templates that no test ever
uses with a concrete type. For a public API this means no test
exercises it. It also catches headers that contain nothing executable
(forward declarations, type traits, test mocks); the tool cannot tell
those apart from unused API.
- Look at each file: write a test that uses it, remove it if nobody
needs it, or note that it holds nothing testable.
* - ``declaration-only``
- A header that only announces functions. The code behind it lives in a
source file with the same name (``timerfd.h`` next to ``timerfd.cpp``),
and that source file is measured.
- Nothing. Listed for completeness.
* - ``compiled-without-code``
- A source file that Bazel compiled but that contains no code of its
own, only ``#include`` lines. Header-only libraries carry such a
placeholder file so that Bazel produces a library archive.
- Nothing. Listed for completeness.

Two things this list is not: it is not part of the coverage percentage, and
it does not say *why* a ``no-data`` file was never compiled. That needs a
look at the file.

Details for integrators
~~~~~~~~~~~~~~~~~~~~~~~

- Fixed (eclipse-score/coverage_tool#5): a workspace rule that forwards the
``CcInfo`` of a third-party library (e.g. a transition wrapper around
OpenSSL) no longer puts that library's headers into the scope; only headers
a workspace target declares itself count.
- Fixed (eclipse-score/coverage_tool#5): every index link of the HTML report
points at a generated page. The reporter stages the in-scope sources from
the scope's exported files instead of reading them through the workspace
directory, where generated headers and external repositories are not
present at report time.
- Changed: headers compiled through ``strip_include_prefix`` /
``include_prefix`` are reported under their declared path (e.g.
``score/flatbuffers/include/flatbuffers/base.h`` or
``external/flatbuffers+/include/flatbuffers/base.h``), no longer under the
generated ``_virtual_includes/`` path. Justifications written against a
``_virtual_includes/`` path must be updated.
- Changed: HTML pages live at ``coverage/<canonical path>.html``; the archive
contains no directory of the producing machine any more.
- New: in-scope files that carry no coverage data at all (a header nothing
includes, template-only code that is never instantiated) are no longer
silently absent. The reporter writes ``text_report/unmapped_files.txt`` and
warns; ``generate_coverage_html`` prints them, archives the list as
``unmapped_files.txt`` and adds a row and a section to the job summary
(``tool_req__coverage_report_unmapped``). Declaration-only headers and
placeholder sources compiled without code are categorised separately and
are not findings.
- Fixed: a library archive with one member lacking a coverage mapping (the
placeholder ``.cpp`` of a header-only library) was rejected by ``llvm-cov``
as a whole, so the library's other untested files silently lost their 0 %
baseline. Archive members are now inspected and only those with a mapping
are passed on, the way Rust rlibs were already handled
(``tool_req__coverage_report_rlib_expansion`` v2).
- Fixed: a ``_virtual_includes/`` path of a target outside the scope (test-only
twin of an in-scope library) is resolved to the allowlisted header with the
same path tail instead of being excluded; ambiguous tails are warned about.
- Fixed: the exclusion filter matches each out-of-scope compiled file exactly;
an excluded ``foo/bar.h`` no longer suppresses an in-scope ``src/foo/bar.h``.
- ``score_coverage_scope`` gained the ``path_map`` and ``source_files`` output
groups; ``score_coverage_reporter`` passes them to the reporter
(``--path_map``). Consumers only instantiate the macros; no change needed.

0.1.0 (2026-09-11)
------------------

First release as a standalone module, extracted from ``@score_tooling//coverage``
Expand Down
Loading
Loading