Skip to content

0.1.0 —— C++ 运行时上到 openkal,连同编译器自己的运行时和裸机的展开约定 #6

0.1.0 —— C++ 运行时上到 openkal,连同编译器自己的运行时和裸机的展开约定

0.1.0 —— C++ 运行时上到 openkal,连同编译器自己的运行时和裸机的展开约定 #6

Workflow file for this run

name: CI
# What this workflow asserts.
#
# A C++ standard library is not portable in the sense a program is: it is
# configured for one C library and compiled against that library's headers. The
# claim here is therefore not "it builds" but "a program above it does the
# things a C++ program does", and the one that settles it is an exception thrown
# across frames and caught --- because that is the path a build proves nothing
# about. Everything else this package could check, a runtime that was linked but
# never worked would also satisfy.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
env:
MCPP_VERSION: 2026.8.19.4
XLINGS_VERSION: v2026.8.17.2
XLINGS_NON_INTERACTIVE: '1'
jobs:
runtime:
name: build the runtime and run what stands above it (${{ matrix.toolchain }})
runs-on: ubuntu-24.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
toolchain: ['llvm@22.1.8']
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Install xlings and mcpp
run: |
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \
| bash -s "$XLINGS_VERSION"
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"
- name: Install mcpp
run: |
for attempt in 1 2 3 4 5 6; do
xlings update > /dev/null 2>&1 || true
if xlings install "mcpp@$MCPP_VERSION" -y -g; then break; fi
if [ "$attempt" = 6 ]; then
echo "::error::mcpp@$MCPP_VERSION never appeared in the index"; exit 1
fi
sleep 60
done
mcpp --version
mcpp self config --mirror GLOBAL
- name: Select the toolchain
run: |
spec='${{ matrix.toolchain }}'
mcpp toolchain install "${spec%@*}" "${spec#*@}"
mcpp toolchain default "$spec"
- name: The runtime builds
run: mcpp build
# ⚠️ THE ONE OBSERVATION A BUILD CANNOT MAKE.
#
# An unwinder that cannot find the program's frame descriptions compiles,
# links, and runs every path that does not throw. Measured while this
# package was written: the whole example above printed its first line and
# then `libc++abi: terminating due to uncaught exception', with
# _Unwind_Backtrace walking zero frames --- and nothing named the cause.
# So the throw is the check, and the destructor beside it is what says the
# unwind was correct rather than merely non-fatal.
- name: A C++ program above it throws across frames and catches
run: |
cd examples/cxx && mcpp run 2>&1 | tee out.log
grep -q -- '-- failures: 0 --' out.log
- name: import std above it
run: |
cd examples/import-std && mcpp run 2>&1 | tee out.log
grep -q 'import std above openkal: 2 4 7' out.log
# ⭐⭐ THE SAME PROGRAM ON A MACHINE WITH NO OPERATING SYSTEM.
#
# Everything above this step runs on a host, and a host has a C library, a
# C++ runtime and an unwinder already installed. A program that reaches
# one of them by mistake still works — so those steps can pass without
# having exercised this package's own copies at all.
#
# There is nothing here to reach. The C library is openkal-musl, the
# standard library and the unwinder are this package's, and beneath them
# is firmware whose whole interface is `ecall`. ⇒ This is the step that
# cannot go green by accident, which is why the design document makes it
# the acceptance criterion rather than one more row.
#
# ⚠️ The assertion is on the OUTPUT and not on the exit status: firmware
# that never reaches the payload exits zero, and so does a payload whose
# console writes go nowhere.
- name: Install the emulator
if: matrix.toolchain == 'llvm@22.1.8'
run: |
xlings install xim:qemu-riscv -y
XLINGS_HOME="$HOME/.mcpp/registry" xlings install xim:qemu-riscv -y
# ⚠️ THE BUILD TOOL IS BUILT FROM A BRANCH, AND THAT IS TEMPORARY.
#
# This step needs four decisions that no released mcpp makes yet, each of
# which belongs to the tool rather than to this package:
#
# `import std` is gated on whether a package PROVIDES a standard
# library for the target rather than on whether the target is
# freestanding; a package may carry its own std module source;
# `-fno-exceptions` / `-fno-rtti` / `-ffreestanding` come off when the
# graph supplies a C++ runtime built for the target; and unwind tables
# go on, because the compiler turns them off for this kind of target
# and a partial set of tables stops the walk rather than degrading it.
#
# They are on mcpp-community/mcpp#486. Until that is released this job
# builds the tool the same way every other dependency here is taken —
# from the branch — so the criterion below is actually enforced instead
# of being a comment saying it was verified once on a laptop.
#
# ⇒ When #486 ships, delete this step and raise MCPP_VERSION.
- name: The build tool, from the branch that has what this needs
if: matrix.toolchain == 'llvm@22.1.8'
run: |
set -euo pipefail
git clone --depth 1 -b feat/import-std-capability \
https://github.com/mcpp-community/mcpp "$RUNNER_TEMP/mcpp-src"
cd "$RUNNER_TEMP/mcpp-src"
mcpp build --release
BUILT=$(find target -type f -name mcpp -perm -u+x | head -1)
[ -n "$BUILT" ] || { echo "::error::mcpp did not build"; exit 1; }
echo "$(cd "$(dirname "$BUILT")" && pwd)" >> "$GITHUB_PATH"
"$BUILT" --version
- name: The same program on bare metal, with exceptions
if: matrix.toolchain == 'llvm@22.1.8'
run: |
set -euo pipefail
cd examples/baremetal
Q=$(ls -d "$HOME"/.mcpp/registry/data/xpkgs/xim-x-qemu-riscv/*/bin/qemu-system-riscv64 | head -1)
sed -i "s|\"qemu-system-riscv64\"|\"$Q\"|" mcpp.toml
mcpp run 2>&1 | tee out.log
grep -q 'sorted: 2 4 7' out.log # containers + algorithms + the allocator
grep -q 'caught: 42' out.log # the unwinder found the handler
grep -q 'unwound: true' out.log # ⭐ and ran a destructor on the way
grep -q 'baremetal import std: ok' out.log