Files
RedBear-OS/local/recipes/dev/libclc/source/llvm/utils/prepare-code-coverage-artifact.py
T
vasilito cb424d7448 build: static patch-sanity linter (shift-left the malformed-patch class)
verify-patch-sanity.py validates every active recipe .patch has internally-
consistent hunk line counts — catching the 'malformed patch at line N' failure
at commit/CI/preflight time instead of hours into a cook. This cycle hit that
class three times (qtwaylandscanner, sddm, xwayland), each only discovered when
cookbook tried to apply the patch.

Running it across the repo found 29 latent malformed patches (validated against
GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22').
They were harmless only because they sit in vendored recipes (baked, not re-
applied) — but would fail on any version-bump re-derivation. --fix recounts the
hunk headers (body untouched) and repaired all 29.

Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test
(test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats
(empty placeholders, bare-@@ git hunks).
2026-08-01 05:13:02 +03:00

199 lines
5.5 KiB
Python
Executable File

#!/usr/bin/env python
from __future__ import print_function
"""Prepare a code coverage artifact.
- Collate raw profiles into one indexed profile.
- Generate html reports for the given binaries.
Caution: The positional arguments to this script must be specified before any
optional arguments, such as --restrict.
"""
import argparse
import glob
import os
import subprocess
import sys
def merge_raw_profiles(host_llvm_profdata, profile_data_dir, preserve_profiles):
print(":: Merging raw profiles...", end="")
sys.stdout.flush()
raw_profiles = glob.glob(os.path.join(profile_data_dir, "*.profraw"))
manifest_path = os.path.join(profile_data_dir, "profiles.manifest")
profdata_path = os.path.join(profile_data_dir, "Coverage.profdata")
with open(manifest_path, "w") as manifest:
manifest.write("\n".join(raw_profiles))
subprocess.check_call(
[
host_llvm_profdata,
"merge",
"-sparse",
"-f",
manifest_path,
"-o",
profdata_path,
]
)
if not preserve_profiles:
for raw_profile in raw_profiles:
os.remove(raw_profile)
os.remove(manifest_path)
print("Done!")
return profdata_path
def prepare_html_report(
host_llvm_cov, profile, report_dir, binaries, restricted_dirs, compilation_dir
):
print(":: Preparing html report for {0}...".format(binaries), end="")
sys.stdout.flush()
objects = []
for i, binary in enumerate(binaries):
if i == 0:
objects.append(binary)
else:
objects.extend(("-object", binary))
invocation = (
[host_llvm_cov, "show"]
+ objects
+ [
"-format",
"html",
"-instr-profile",
profile,
"-o",
report_dir,
"-show-line-counts-or-regions",
"-show-directory-coverage",
"-Xdemangler",
"c++filt",
"-Xdemangler",
"-n",
]
+ restricted_dirs
)
if compilation_dir:
invocation += ["-compilation-dir=" + compilation_dir]
subprocess.check_call(invocation)
with open(os.path.join(report_dir, "summary.txt"), "wb") as Summary:
subprocess.check_call(
[host_llvm_cov, "report"]
+ objects
+ ["-instr-profile", profile]
+ restricted_dirs,
stdout=Summary,
)
print("Done!")
def prepare_html_reports(
host_llvm_cov,
profdata_path,
report_dir,
binaries,
unified_report,
restricted_dirs,
compilation_dir,
):
if unified_report:
prepare_html_report(
host_llvm_cov,
profdata_path,
report_dir,
binaries,
restricted_dirs,
compilation_dir,
)
else:
for binary in binaries:
binary_report_dir = os.path.join(report_dir, os.path.basename(binary))
prepare_html_report(
host_llvm_cov,
profdata_path,
binary_report_dir,
[binary],
restricted_dirs,
compilation_dir,
)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("host_llvm_profdata", help="Path to llvm-profdata")
parser.add_argument("host_llvm_cov", help="Path to llvm-cov")
parser.add_argument(
"profile_data_dir", help="Path to the directory containing the raw profiles"
)
parser.add_argument(
"report_dir", help="Path to the output directory for html reports"
)
parser.add_argument(
"binaries",
metavar="B",
type=str,
nargs="*",
help="Path to an instrumented binary",
)
parser.add_argument(
"--only-merge",
action="store_true",
help="Only merge raw profiles together, skip report " "generation",
)
parser.add_argument(
"--preserve-profiles", help="Do not delete raw profiles", action="store_true"
)
parser.add_argument(
"--use-existing-profdata", help="Specify an existing indexed profile to use"
)
parser.add_argument(
"--unified-report",
action="store_true",
help="Emit a unified report for all binaries",
)
parser.add_argument(
"--restrict",
metavar="R",
type=str,
nargs="*",
default=[],
help="Restrict the reporting to the given source paths"
" (must be specified after all other positional arguments)",
)
parser.add_argument(
"-C",
"--compilation-dir",
type=str,
default="",
help="The compilation directory of the binary",
)
args = parser.parse_args()
if args.use_existing_profdata and args.only_merge:
print("--use-existing-profdata and --only-merge are incompatible")
exit(1)
if args.use_existing_profdata:
profdata_path = args.use_existing_profdata
else:
profdata_path = merge_raw_profiles(
args.host_llvm_profdata, args.profile_data_dir, args.preserve_profiles
)
if not len(args.binaries):
print("No binaries specified, no work to do!")
exit(1)
if not args.only_merge:
prepare_html_reports(
args.host_llvm_cov,
profdata_path,
args.report_dir,
args.binaries,
args.unified_report,
args.restrict,
args.compilation_dir,
)