-
Notifications
You must be signed in to change notification settings - Fork 52
233 lines (214 loc) · 11 KB
/
Copy path_cross-build.yml
File metadata and controls
233 lines (214 loc) · 11 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
name: _cross-build
# Reusable: build kasld for every supported musl architecture (the matrix below
# fans out one job per arch). This is the SINGLE source of the released arch set
# — build.yml (compile-gate, package=false) and release.yml (package=true)
# both call it, so the two can't drift.
#
# Toolchains come from cross-tools/musl-cross GitHub Releases (musl.cc blocks
# GitHub Actions), broad enough to cover every arch kasld targets — including
# loongarch64, which musl.cc never provided. armeb is omitted: cross-tools/
# musl-cross ships no big-endian-ARM toolchain.
on:
workflow_call:
inputs:
toolchain_version:
description: 'cross-tools/musl-cross release tag to build against'
required: true
type: string
package:
description: 'release mode: native-test x86_64, tarball + upload artifacts'
required: false
default: false
type: boolean
run_replay:
description: 'after building, replay this arch''s fixtures under qemu-user'
required: false
default: false
type: boolean
run_test_cross:
description: 'after building, run the engine tests for this arch under qemu-user'
required: false
default: false
type: boolean
run_validate_fixtures:
description: 'after building, assert truth is in the resolved window for this arch''s fixtures'
required: false
default: false
type: boolean
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# arch: friendly name (job label + tarball / artifact name).
# musl_triple: cross-tools/musl-cross triple — the tar.xz stem, the
# extracted dir name, and the <triple>-gcc prefix.
# The float/baseline variants (i586, armhf, armv7, mipssf, ...) compile
# the same kasld as their base arch, but each is a distinct shipped
# toolchain, so the gate builds them too.
include:
# --- x86 ---
- { arch: x86_64, musl_triple: x86_64-unknown-linux-musl, native_test: true }
- { arch: i686, musl_triple: i686-unknown-linux-musl }
- { arch: i586, musl_triple: i586-unknown-linux-musl }
# --- ARM (soft/hard float, v6/v7) ---
- { arch: aarch64, musl_triple: aarch64-unknown-linux-musl }
- { arch: arm, musl_triple: arm-unknown-linux-musleabi }
- { arch: armhf, musl_triple: arm-unknown-linux-musleabihf }
- { arch: armv7, musl_triple: armv7-unknown-linux-musleabi }
- { arch: armv7l, musl_triple: armv7-unknown-linux-musleabihf }
# --- MIPS (hard/soft float, BE/LE) ---
- { arch: mips, musl_triple: mips-unknown-linux-musl }
- { arch: mipssf, musl_triple: mips-unknown-linux-muslsf }
- { arch: mipsel, musl_triple: mipsel-unknown-linux-musl }
- { arch: mipselsf, musl_triple: mipsel-unknown-linux-muslsf }
- { arch: mips64, musl_triple: mips64-unknown-linux-musl }
- { arch: mips64el, musl_triple: mips64el-unknown-linux-musl }
# --- POWER (32/64, BE/LE) ---
- { arch: powerpc, musl_triple: powerpc-unknown-linux-musl }
- { arch: powerpcle, musl_triple: powerpcle-unknown-linux-musl }
- { arch: powerpc64, musl_triple: powerpc64-unknown-linux-musl }
- { arch: powerpc64le, musl_triple: powerpc64le-unknown-linux-musl }
# --- RISC-V (32/64) ---
- { arch: riscv32, musl_triple: riscv32-unknown-linux-musl }
- { arch: riscv64, musl_triple: riscv64-unknown-linux-musl }
# --- s390x ---
- { arch: s390x, musl_triple: s390x-ibm-linux-musl }
# --- LoongArch ---
- { arch: loongarch64, musl_triple: loongarch64-unknown-linux-musl }
steps:
- uses: actions/checkout@v6
- name: Cache toolchain
uses: actions/cache@v4
with:
path: ~/musl-cross/${{ matrix.musl_triple }}
key: musl-${{ matrix.musl_triple }}-${{ inputs.toolchain_version }}
- name: Fetch toolchain + add to PATH
run: |
set -euo pipefail
t="${{ matrix.musl_triple }}"
root="$HOME/musl-cross"; mkdir -p "$root"
if [ ! -d "$root/$t" ]; then
base="https://github.com/cross-tools/musl-cross/releases/download/${{ inputs.toolchain_version }}"
dl() { curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "$1" -o "$2"; }
dl "${base}/${t}.tar.xz" "/tmp/${t}.tar.xz"
dl "${base}/${t}.tar.xz.sha256" "/tmp/${t}.sha256"
# The .sha256 holds only the bare digest; pair it with the filename.
echo "$(cut -d' ' -f1 "/tmp/${t}.sha256") /tmp/${t}.tar.xz" | sha256sum -c -
tar -xJf "/tmp/${t}.tar.xz" -C "$root"
fi
echo "$root/$t/bin" >> "$GITHUB_PATH"
# Static zlib for this target. No musl toolchain ships one, so without it
# HAVE_ZLIB is empty and proc_config decompresses /proc/config.gz by
# running zcat from the target's PATH — which a minimal userland need not
# have, in a binary shipped precisely to be self-contained. Pinned by
# version and checksum in the Makefile. Cached like the toolchain: the
# source is a fixed tarball and the build is deterministic.
- name: Cache zlib prefix
uses: actions/cache@v4
with:
path: build/deps
key: zlib-${{ matrix.musl_triple }}-${{ hashFiles('Makefile') }}
- name: Build zlib for this target
run: make cross-deps TRIPLE=${{ matrix.musl_triple }}
- name: Build
run: |
set -euo pipefail
xf="$(make --no-print-directory cross-extra-flags TRIPLE=${{ matrix.musl_triple }})"
make build CC=${{ matrix.musl_triple }}-gcc EXTRA_CFLAGS="$xf" EXTRA_LDFLAGS="$xf"
# A release binary that still shells out to zcat is the failure this whole
# step exists to prevent, and it is invisible in a build log.
- name: Verify zlib is linked
run: |
set -euo pipefail
b="build/$(${{ matrix.musl_triple }}-gcc -dumpmachine)/components/proc_config"
${{ matrix.musl_triple }}-nm "$b" | grep -qi inflate
- name: Identify build directory
id: builddir
run: echo "dir=build/$(${{ matrix.musl_triple }}-gcc -dumpmachine)" >> "$GITHUB_OUTPUT"
- name: Verify static linkage
run: |
file "${{ steps.builddir.outputs.dir }}/kasld"
file "${{ steps.builddir.outputs.dir }}/kasld" | grep -q "statically linked"
# qemu-user provides the qemu-<arch> binaries (resolved from PATH by
# tests/replay, tests/test-cross and extra/validate-bundle). jq is for
# extra/validate-bundle (fixture soundness). Shared by the modes below.
- name: Install qemu-user
if: inputs.run_replay || inputs.run_test_cross || inputs.run_validate_fixtures
run: sudo apt-get update && sudo apt-get install -y qemu-user jq
# Per-push (via build.yml): execute the engine tests for this arch under
# qemu-user, so its arch-gated rule bodies and assertions run their real
# path — the cross-compile above only proves they compile. Self-contained
# (no fixtures); compiles the test binaries with this arch's toolchain.
#
# No compiler cache here. Each job is a fresh runner and nothing restores
# one, so every compile would miss while still paying to hash and store
# its result — a cold cache costs more than it saves. Pinning the setting
# also keeps the step's cost independent of whether the runner image
# happens to ship ccache. The harness still shares its rule objects
# between the two engine binaries, which is where the saving comes from.
- name: Cross-arch engine tests (qemu-user)
if: inputs.run_test_cross
env:
USE_CCACHE: 0
run: tests/test-cross "${{ matrix.musl_triple }}"
# Replay mode (manual, via replay.yml): run the real binary just built here
# over the captured fixtures. Each matrix job runs the whole corpus but only
# this arch's fixtures have a matching binary — the rest skip — so the matrix
# covers every fixture exactly once.
- name: End-to-end replay (qemu-user)
if: inputs.run_replay
run: tests/replay
# Offline soundness gate (via build.yml, per push): assert the resolved
# window contains the real base for this arch's truth-bearing fixtures.
# Runs the whole corpus but only this arch's fixtures resolve a binary —
# the rest skip — so the matrix covers every fixture once. Complements the
# crash-smoke replay: "does it run?" vs "is the window sound?".
- name: Fixture soundness (qemu-user)
if: inputs.run_validate_fixtures
run: tests/validate-fixtures
# Truth-free complement (see build.yml): no container-fakeable input may
# move the guaranteed window. Whole corpus, both anonymized states; only
# this arch's fixtures resolve a binary — the rest skip — so the matrix
# covers every fixture once. This is where the coupled arches
# (arm32/mips/ppc/loongarch/riscv32) get their ceiling-rule coverage.
- name: Fixture perturbation (qemu-user)
if: inputs.run_validate_fixtures
run: tests/validate-fixtures --perturb
# Release mode only: x86_64 runs natively on the runner. (PR coverage
# tests x86_64 via build.yml's host job, so this would be redundant there.)
- name: Run tests (native, x86_64)
if: inputs.package && matrix.native_test
run: make test CC=${{ matrix.musl_triple }}-gcc
- name: Package release tarball
if: inputs.package
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
DIR="kasld-${TAG}-${{ matrix.arch }}"
mkdir -p "${DIR}"
# Ship the build/<arch> output (kasld + components/), minus the obj/
# build-intermediates subdir. Arch-gated components leave a
# non-executable stamp in components/ (so make skips their failing
# compile on rebuild); drop those too, matching `make install`, which
# copies only executable component binaries.
cp -a "${{ steps.builddir.outputs.dir }}/." "${DIR}/"
rm -rf "${DIR}/obj"
find "${DIR}/components" -type f ! -perm -u+x -delete
cp README.md LICENSE THIRD-PARTY-NOTICES.md "${DIR}/"
# Companion symbol-offset tool (arch-independent) + the docs, so the
# tarball matches `make install` and the README's doc links resolve.
mkdir -p "${DIR}/extra"
cp extra/ksymoff "${DIR}/extra/"
cp -a docs "${DIR}/docs"
tar czf "${DIR}.tar.gz" "${DIR}"
sha256sum "${DIR}.tar.gz" > "${DIR}.tar.gz.sha256"
- name: Upload artifact
if: inputs.package
uses: actions/upload-artifact@v5
with:
name: kasld-${{ matrix.arch }}
path: |
kasld-*-${{ matrix.arch }}.tar.gz
kasld-*-${{ matrix.arch }}.tar.gz.sha256