-
Notifications
You must be signed in to change notification settings - Fork 124
255 lines (242 loc) · 10.8 KB
/
coverage.yml
File metadata and controls
255 lines (242 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
name: Coverage
# Runs clang source-based coverage on every PR (advisory only —
# never gates) and on a nightly schedule. The output is a per-line
# union of all platforms in the matrix, exported as the
# `coverage-merged` artifact (containing both .json and .md), and
# consumed by `coverage-comment.yml` (a separate workflow with the
# write token) to post the report on PRs / update the tracking
# issue.
#
# This workflow mirrors the regular ctest invocation across the CI
# matrix, so the coverage it reports is exactly what every-PR CI
# exercises.
on:
pull_request:
branches: [ main ]
schedule:
# Nightly at 04:00 UTC; cheapest free-runner slot.
- cron: '0 4 * * *'
workflow_dispatch:
# Default token; the build does not push, comment, or modify any
# resource. The follow-on `coverage-comment.yml` is the only writer.
permissions: read-all
concurrency:
group: coverage-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
# ============================================================================
# Linux + macOS host builds via reusable-cmake-build.yml.
# ============================================================================
build:
name: coverage / ${{ matrix.os }}${{ matrix.label-suffix }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-14
label-suffix: ''
label: macos-14
# macOS runners have AppleClang preinstalled but `llvm-cov`
# / `llvm-profdata` are NOT on PATH (they live behind
# `xcrun`). The coverage target's find_program(LLVM_COV ...)
# only looks for unversioned/-19/-15 names, so we install
# llvm@19 via brew and pass the explicit binary paths.
#
# SDKROOT is exported in the dependencies step so brew
# clang treats the Apple SDK as a system header path,
# which suppresses -Wundef on Apple's _STDC_VERSION_
# check inside <sys/cdefs.h>.
dependencies: |
brew install --quiet ninja llvm@19
echo "SDKROOT=$(xcrun --show-sdk-path)" >> "$GITHUB_ENV"
extra-cmake-flags: >-
-DCMAKE_CXX_COMPILER=/opt/homebrew/opt/llvm@19/bin/clang++
-DCMAKE_C_COMPILER=/opt/homebrew/opt/llvm@19/bin/clang
-DLLVM_COV=/opt/homebrew/opt/llvm@19/bin/llvm-cov
-DLLVM_PROFDATA=/opt/homebrew/opt/llvm@19/bin/llvm-profdata
self-host: false
# ------------------------------------------------------------------
# Linux representative leg. Mirrors main.yml's self-host job
# (SNMALLOC_MEMCPY_BOUNDS=ON + SNMALLOC_CHECK_LOADS=ON), so
# the bounds-checked memcpy and load-check paths are
# exercised; these are not reached by any other coverage
# leg. The self-host step iterates over every shim variant
# built (libsnmallocshim.so, libsnmallocshim-checks.so,
# libsnmallocshim-checks-memcpy-only.so) and the export
# step combines profraws from all of them.
# ------------------------------------------------------------------
- os: ubuntu-24.04
label-suffix: ' / self-host shim-checks'
label: linux-self-host-shim-checks
dependencies: 'sudo apt install -y ninja-build clang-19 llvm-19'
extra-cmake-flags: '-DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_C_COMPILER=clang-19 -DSNMALLOC_MEMCPY_BOUNDS=ON -DSNMALLOC_CHECK_LOADS=ON'
self-host: true
uses: ./.github/workflows/reusable-cmake-build.yml
with:
os: ${{ matrix.os }}
# build-type is overridden to Debug by the reusable workflow
# whenever coverage is true, but the input is required.
build-type: Debug
cmake-config: '-G Ninja'
extra-cmake-flags: ${{ matrix.extra-cmake-flags }}
dependencies: ${{ matrix.dependencies }}
self-host: ${{ matrix.self-host }}
coverage: true
coverage-artifact-name: ${{ matrix.label }}
# ============================================================================
# FreeBSD / NetBSD via reusable-vm-build.yml.
#
# llvm-profdata / llvm-cov must be installed in the VM by the
# `dependencies:` script. The reusable workflow forces
# copyback: true when coverage is true so coverage.json
# makes it back to the host runner.
# ============================================================================
build-vm:
name: coverage / ${{ matrix.label }}
strategy:
fail-fast: false
matrix:
include:
- label: freebsd-14
vm-type: freebsd
vm-version: '14.1'
# FreeBSD's llvm19 port installs versioned binaries
# (clang19, clang++19, llvm-cov19, llvm-profdata19)
# directly under /usr/local/bin/ — not under a
# /usr/local/llvm19/bin/ subtree. Pass absolute paths so
# find_program is preset and doesn't depend on PATH.
dependencies: 'pkg install -y cmake ninja llvm19'
cmake-flags: >-
-DCMAKE_CXX_COMPILER=/usr/local/bin/clang++19
-DCMAKE_C_COMPILER=/usr/local/bin/clang19
-DLLVM_COV=/usr/local/bin/llvm-cov19
-DLLVM_PROFDATA=/usr/local/bin/llvm-profdata19
# NetBSD intentionally omitted. pkgsrc's compiler-rt-19
# package ships a libclang_rt.profile-x86_64.a in which
# __llvm_profile_raw_version is declared hidden but not
# defined, so any -fprofile-instr-generate shared library
# (e.g. libsnmallocshim.so) fails to link with:
# R_X86_64_PC32 against undefined hidden symbol
# `__llvm_profile_raw_version` can not be used when
# making a shared object
# Revisit when pkgsrc ships a fixed compiler-rt, or wire
# up an in-VM compiler-rt build from llvm-project source.
uses: ./.github/workflows/reusable-vm-build.yml
with:
vm-type: ${{ matrix.vm-type }}
vm-version: ${{ matrix.vm-version }}
build-type: Debug
dependencies: ${{ matrix.dependencies }}
cmake-flags: ${{ matrix.cmake-flags }}
coverage: true
coverage-artifact-name: ${{ matrix.label }}
# ============================================================================
# Windows clang-cl coverage. Exercises the Windows PAL surface,
# which no other leg in the matrix touches.
#
# GitHub windows-2022 runners ship LLVM (clang-cl, llvm-profdata,
# llvm-cov) under C:\Program Files\LLVM\bin, with that directory
# already on PATH and ninja preinstalled. We rely on PATH lookup
# rather than passing -DLLVM_COV / -DLLVM_PROFDATA absolute paths,
# because the install dir contains a space ("Program Files") which
# YAML folded scalars + the reusable workflow's shell-expansion of
# ${{ inputs.extra-cmake-flags }} cannot preserve. Quoting at any
# single layer is undone by the next, leaving CMake to receive
# -DLLVM_PROFDATA=C:/Program and a phantom positional argument.
# ============================================================================
windows:
name: coverage / windows-2022 clang-cl
uses: ./.github/workflows/reusable-cmake-build.yml
with:
os: windows-2022
build-type: Debug
cmake-config: '-G Ninja'
extra-cmake-flags: '-DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER=clang-cl'
dependencies: ''
coverage: true
coverage-artifact-name: windows-2022
# ============================================================================
# Merge per-line union across every leg that produced
# coverage.json.
# ============================================================================
merge:
name: merge coverage
needs: [ build, build-vm, windows ]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Download coverage artifacts
uses: actions/download-artifact@v4
with:
path: coverage-artifacts
pattern: coverage-*
merge-multiple: false
- name: Inventory artifacts
run: |
set -euo pipefail
echo "Downloaded artifacts:"
find coverage-artifacts -mindepth 1 -maxdepth 1 -type d -printf ' %f\n'
echo
echo "JSON files:"
find coverage-artifacts -name '*.json' -printf ' %p (%s bytes)\n'
- name: Build merge inputs
id: inputs
run: |
set -euo pipefail
# Each artifact directory is named coverage-<label>; the
# merger takes "label=path" pairs. Self-host runs that
# produced selfhost.json get a separate label suffixed
# "-selfhost" so the merger treats them as distinct
# platforms in the per-platform breakdown.
# The directory list is sorted for deterministic merge
# input order (otherwise the per-platform table order in
# the rendered markdown depends on filesystem readdir).
inputs=()
while IFS= read -r d; do
label="${d##*/coverage-}"
label="${label%/}"
if [ -f "$d/coverage.json" ]; then
inputs+=("$label=$d/coverage.json")
fi
if [ -f "$d/selfhost.json" ]; then
inputs+=("$label-selfhost=$d/selfhost.json")
fi
done < <(find coverage-artifacts -mindepth 1 -maxdepth 1 -type d -name 'coverage-*' | sort)
if [ ${#inputs[@]} -eq 0 ]; then
echo "::error::no coverage JSON artifacts found"
exit 1
fi
printf 'merge inputs:\n'
printf ' %s\n' "${inputs[@]}"
# Hand off via env (newline-delimited).
{
echo 'INPUTS<<EOF'
printf '%s\n' "${inputs[@]}"
echo 'EOF'
} >> "$GITHUB_ENV"
- name: Merge per-line union
run: |
set -euo pipefail
mapfile -t args < <(printf '%s\n' "$INPUTS")
python3 .github/scripts/merge_coverage.py \
--output-json coverage-merged.json \
--output-md coverage-merged.md \
"${args[@]}"
echo
echo "=== merged coverage (markdown preview) ==="
head -40 coverage-merged.md
echo
echo "=== merged coverage (JSON totals) ==="
python3 -c "import json; m=json.load(open('coverage-merged.json')); print('files:', len(m['files'])); print('totals:', m['totals']); print('platforms:', list(m['platforms'].keys()))"
- name: Upload merged coverage
uses: actions/upload-artifact@v4
with:
# Artifact name is consumed by coverage-comment.yml. If
# this name changes, update coverage-comment.yml's
# download-artifact step in lockstep.
name: coverage-merged
path: |
coverage-merged.json
coverage-merged.md
if-no-files-found: error
retention-days: 30