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
64 changes: 32 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
runtime:
name: build the runtime and run what stands above it (${{ matrix.toolchain }})
runs-on: ubuntu-24.04
# ⚠️ Raised for the self-build step below, which is temporary. See the note
# Raised for the self-build step below, which is temporary. See the note
# there: when mcpp#486 ships, that step goes and so does this.
timeout-minutes: 90
strategy:
Expand Down Expand Up @@ -59,7 +59,7 @@ jobs:
- name: Install mcpp
run: bash tools/install-mcpp.sh

# THE ENGINE EVERY STEP BELOW WILL USE, COMPARED AGAINST THE ONE BUILT.
# THE ENGINE EVERY STEP BELOW WILL USE, COMPARED AGAINST THE ONE BUILT.
#
# The step above appends a directory to GITHUB_PATH, which governs the
# steps that follow it, so that step cannot observe its own effect. Whether
Expand Down Expand Up @@ -97,7 +97,7 @@ jobs:
- name: The runtime builds
run: mcpp build

# ⚠️ THE ONE OBSERVATION A BUILD CANNOT MAKE.
# 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
Expand All @@ -106,7 +106,7 @@ jobs:
# _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.
# THE UNWINDER, AND EVERY POSITION `__config_site` DECLARES.
# THE UNWINDER, AND EVERY POSITION `__config_site` DECLARES.
#
# The throw and the destructor beside it are what say the runtime is
# really there. The rest of the probe asks whether the CLAIMS this package
Expand Down Expand Up @@ -222,7 +222,7 @@ jobs:
echo " ok the declaration holds: no terminal support is claimed and none is present"
fi

# ⭐⭐ THE SAME PROGRAM ON A MACHINE WITH NO OPERATING SYSTEM.
# 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
Expand All @@ -235,7 +235,7 @@ jobs:
# 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
# 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
Expand All @@ -250,14 +250,14 @@ jobs:
set -euo pipefail
cd examples/same-source
Q=$(ls -d "$HOME"/.mcpp/registry/data/xpkgs/xim-x-qemu-riscv/*/bin/qemu-system-riscv64 | head -1)
# ⚠️ Anchored on the BARE NAME, so a checkout that already carries a
# Anchored on the BARE NAME, so a checkout that already carries a
# path is left alone rather than getting a path inside a path. The
# manifest is required to carry the bare name; the note beside it says
# why, and this is the step that relies on it.
grep -q '"qemu-system-riscv64"' mcpp.toml \
|| { echo "::error::the manifest no longer carries the bare emulator name"; exit 1; }
sed -i "s|\"qemu-system-riscv64\"|\"$Q\"|" mcpp.toml
# ⚠️ `--target riscv64-none-elf`, AND THE FLAG IS THE WHOLE STEP.
# `--target riscv64-none-elf`, AND THE FLAG IS THE WHOLE STEP.
#
# This manifest carries no `[build] target`, so a bare `mcpp run`
# builds for the HOST and passes — the four lines below appear either
Expand All @@ -271,10 +271,10 @@ jobs:
grep -q 'Boot HART' out.log # firmware ran; this is OpenSBI
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 'unwound: true' out.log # and ran a destructor on the way
grep -q 'import std over openkal: ok' out.log

# ⭐⭐ AND THE SAME SOURCE ON THIS MACHINE, WHICH IS WHAT MAKES THE STEP
# AND THE SAME SOURCE ON THIS MACHINE, WHICH IS WHAT MAKES THE STEP
# ABOVE A DEMONSTRATION RATHER THAN AN ILLUSTRATION.
#
# Nothing is edited between the two commands — no `#if`, no second
Expand All @@ -289,7 +289,7 @@ jobs:
grep -q 'import std over openkal: ok' host.log
# The four lines are the same four lines.
#
# ⚠️ `tr -d '\r'` ON BOTH SIDES, AND IT IS NOT COSMETIC. The bare-metal
# `tr -d '\r'` ON BOTH SIDES, AND IT IS NOT COSMETIC. The bare-metal
# run reaches the console through an emulated 16550 UART, and a serial
# console terminates lines with CRLF; the native run does not. Without
# this the diff reports four differing lines whose visible text is
Expand All @@ -299,14 +299,14 @@ jobs:
diff <(grep -E '^(sorted|caught|unwound|import std over openkal):' out.log | tr -d '\r') \
<(grep -E '^(sorted|caught|unwound|import std over openkal):' host.log | tr -d '\r')

# ⭐⭐ AND THE SAME SOURCE FOR TWO MACHINES THIS ONE IS NOT.
# AND THE SAME SOURCE FOR TWO MACHINES THIS ONE IS NOT.
#
# The two steps above prove the source does not know which machine it is
# for. These prove the BUILD does not need to be on it: one Linux host
# produces a PE and a Mach-O, and the jobs below run them on the real
# thing with nothing installed.
#
# ⚠️ THE ARTEFACT IS THE ARGUMENT, WHICH IS WHY THOSE JOBS INSTALL NOTHING.
# THE ARTEFACT IS THE ARGUMENT, WHICH IS WHY THOSE JOBS INSTALL NOTHING.
# Not mcpp, not a compiler, not a C runtime — the program carries its C
# library, its C++ runtime and its unwinder, and what remains is the
# operating system it was built for. A run that needed a redistributable
Expand All @@ -325,13 +325,13 @@ jobs:
echo "$t → $(file -b "$a")"
cp "$a" "$RUNNER_TEMP/cross/"
done
# ⚠️ The format is asserted here rather than left to the run jobs. A
# The format is asserted here rather than left to the run jobs. A
# run that fails tells you the program did not work; this tells you
# what was produced, and the two failures need different fixes.
file "$RUNNER_TEMP/cross/openkal-same-source.exe" | grep -q 'PE32+ executable'
file "$RUNNER_TEMP/cross/openkal-same-source" | grep -q 'Mach-O 64-bit arm64'

# AND A TRANSLATION UNIT THAT INCLUDES A STANDARD HEADER, FOR THE SAME TWO.
# AND A TRANSLATION UNIT THAT INCLUDES A STANDARD HEADER, FOR THE SAME TWO.
#
# `same-source` imports std, and the std module is compiled with this
# package's own flags, so it cannot see what a consumer's translation unit
Expand Down Expand Up @@ -359,15 +359,15 @@ jobs:
if-no-files-found: error

# ---------------------------------------------------------------------------
# ⭐⭐ THE ACCEPTANCE CRITERION FOR PORTABILITY OF THE ARTEFACT.
# THE ACCEPTANCE CRITERION FOR PORTABILITY OF THE ARTEFACT.
#
# A cross build that produces a well-formed file proves the compiler was told
# the right target. It does not prove the program runs, and every difference
# this ecosystem has had to find on these two platforms — the loader-bootstrapped
# thread-local, the unwinder's search for its own tables, the personality
# routine — links successfully and fails at run time.
#
# ⚠️ These jobs deliberately have NO toolchain steps. If one is ever added
# These jobs deliberately have NO toolchain steps. If one is ever added
# because "the program needs it", that is the finding, not the fix.
run-on-windows:
name: the artefact built on Linux runs on Windows
Expand All @@ -386,7 +386,7 @@ jobs:
./art/openkal-same-source.exe 2>&1 | tee out.log
grep -q 'sorted: 2 4 7' out.log
grep -q 'caught: 42' out.log
# The line a link cannot fake: a destructor ran during the unwind,
# The line a link cannot fake: a destructor ran during the unwind,
# so libunwind found `.eh_frame` by reading the image rather than by
# asking the operating system to enumerate modules.
grep -q 'unwound: true' out.log
Expand All @@ -403,33 +403,33 @@ jobs:
- name: It runs, and it unwinds
run: |
set -euo pipefail
# ⚠️ The executable bit does not survive an artefact upload.
# The executable bit does not survive an artefact upload.
chmod +x art/openkal-same-source
# ⚠️ AND THE SIGNATURE DOES. arm64 macOS refuses an unsigned image, so
# AND THE SIGNATURE DOES. arm64 macOS refuses an unsigned image, so
# this is asserted before the run: a failure here is "the linker did
# not ad-hoc sign it", which is a different repair from "the program
# crashed".
codesign -dv art/openkal-same-source 2>&1 | grep -q 'adhoc\|Signature'
# ⚠️ A CRASH IS DIAGNOSED HERE RATHER THAN GUESSED AT LATER. This is
# A CRASH IS DIAGNOSED HERE RATHER THAN GUESSED AT LATER. This is
# the first time an artefact of this stack has run on this system, and
# the interesting failures — the entry point's assumptions about what
# the kernel hands it, the thread pointer, the two borrowed names —
# all look identical from outside: `Segmentation fault: 11`.
# One CI cycle that prints a backtrace is worth several that do not.
if ! ./art/openkal-same-source > out.log 2>&1; then
echo "--- how far the loader got ---"
# ⚠️ To a file, then read. Piping into `tail` interleaves dyld's
# To a file, then read. Piping into `tail` interleaves dyld's
# output with the shell's own report of the signal, and the last
# lines — the ones that say which initializer was running — are the
# ones that get lost.
DYLD_PRINT_INITIALIZERS=1 ./art/openkal-same-source > dyld.log 2>&1 || true
tail -40 dyld.log
echo "--- it did not run; what the debugger saw ---"
# `lr` IS THE DATUM. A jump to address 0 leaves no frame to
# `lr` IS THE DATUM. A jump to address 0 leaves no frame to
# unwind, so `bt` says only "frame #0: 0x0" — which is the symptom
# restated. The link register still holds the return address of
# whoever made that call, and `image lookup` turns it into a name.
# ⚠️ `-k` AND NOT `-o`. In `--batch` lldb abandons the remaining
# `-k` AND NOT `-o`. In `--batch` lldb abandons the remaining
# `-o` commands after the first one that errors, and reading
# register state at a PC of 0 errors — so the three commands that
# would have said something never ran. `-k` is the list lldb
Expand All @@ -454,15 +454,15 @@ jobs:
grep -q 'import std over openkal: ok' out.log

# ---------------------------------------------------------------------------
# ⭐⭐ THE HOST DIMENSION — THE HALF OF THE CLAIM THE JOBS ABOVE DO NOT TOUCH.
# THE HOST DIMENSION — THE HALF OF THE CLAIM THE JOBS ABOVE DO NOT TOUCH.
#
# Everything above builds on Linux. That establishes "one host reaches every
# target" and leaves open the thing an N×N matrix would otherwise have to
# enumerate: whether the HOST matters. The scheme's answer is that it does not
# — the target side is a set of packages and the compiler is a retargetable
# clang, so N hosts × N targets collapses to N implementations plus one tool.
#
# ⚠️ THAT IS A CLAIM, AND CLAIMS OF THIS SHAPE HAVE BEEN WRONG HERE BEFORE:
# THAT IS A CLAIM, AND CLAIMS OF THIS SHAPE HAVE BEEN WRONG HERE BEFORE:
# the Linux host needed four separate repairs before it reached PE, and every
# one of them was invisible until a build was actually run. Two more hosts
# cost two jobs; asserting the collapse without running them costs a paragraph
Expand Down Expand Up @@ -510,7 +510,7 @@ jobs:
- name: Install mcpp
run: bash tools/install-mcpp.sh

# THE ENGINE EVERY STEP BELOW WILL USE, COMPARED AGAINST THE ONE BUILT.
# THE ENGINE EVERY STEP BELOW WILL USE, COMPARED AGAINST THE ONE BUILT.
#
# The step above appends a directory to GITHUB_PATH, which governs the
# steps that follow it, so that step cannot observe its own effect. Whether
Expand All @@ -534,15 +534,15 @@ jobs:
fi
echo " every step below runs $on_path, built from $MCPP_SOURCE_REF"

# THE SAME ENGINE AND THE SAME STACK AS THE LINUX JOB, FROM A DIFFERENT
# THE SAME ENGINE AND THE SAME STACK AS THE LINUX JOB, FROM A DIFFERENT
# HOST. This job had neither: it installed the released engine and
# resolved this ecosystem from the index, so a change spanning these
# repositories was validated on one host of three and reported as
# validated everywhere.
- name: Select the toolchain
run: |
set -euo pipefail
# ⚠️ INSTALL, THEN SELECT. `toolchain default` names a toolchain and
# INSTALL, THEN SELECT. `toolchain default` names a toolchain and
# does not fetch one, so selecting an absent payload fails with
# `llvm@22.1.8 is not installed` — measured on both rows of this job.
mcpp toolchain install llvm 22.1.8
Expand All @@ -563,9 +563,9 @@ jobs:
echo "$t → $(file -b "$a" 2>/dev/null || echo built)"
done

# And the one that is this machine, run rather than inspected — the same
# And the one that is this machine, run rather than inspected — the same
# criterion the Linux job holds itself to, applied from a different host.
# AND A HOSTED C++ PROGRAM, WHICH IS WHERE THREADS AND FILES ARE.
# AND A HOSTED C++ PROGRAM, WHICH IS WHERE THREADS AND FILES ARE.
#
# `same-source` also runs on bare metal, so it cannot start a thread;
# `examples/cxx` can, and on Windows a joined std::thread was an access
Expand All @@ -583,7 +583,7 @@ jobs:
grep -q 'ok: a detached thread runs and ends' out.log
grep -q "ok: a string grows past the allocator's own mappings" out.log

# ⚠️ AND BUILT WITH --release, WHICH IS WHAT A USER SHIPS. Until
# AND BUILT WITH --release, WHICH IS WHAT A USER SHIPS. Until
# openkal-macos 0.9.1 a release program faulted before `main' on macOS (a
# returned register declared an input), and until openkal-windows 0.7.1 its
# arguments were shortened on Windows (a loop became `wcslen'). The dev
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ target/
compile_commands.json
mcpp.lock
.cache/
# ⚠️ THE ONE THIS REPOSITORY DID NOT HAVE, AND THE ONE THAT SLIPPED IN.
# THE ONE THIS REPOSITORY DID NOT HAVE, AND THE ONE THAT SLIPPED IN.
# `.mcpp/` is where the build tool keeps a workspace's own state; every other
# repository in this ecosystem ignores it and this one did not, so a
# `.mcpp/.xlings.json` from an experiment was committed with the bare-metal
# work and then travelled in the pull request.
.mcpp/
# ⚠️ One `.mcpp/` rule matches the directory at any depth, and it does not
# One `.mcpp/` rule matches the directory at any depth, and it does not
# remove a file that is ALREADY TRACKED. This repository had two: one at the
# root and one under `examples/import-std/`. The first cleanup found only the
# root one, because the scan it used anchored the path at the beginning.
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ C library.

```toml
[dependencies]
openkal-llvm-runtime = "0.7.0"
openkal-llvm-runtime = "0.10.0"
```

A C++ standard library is not portable in the way a program is. It is
Expand Down Expand Up @@ -62,7 +62,7 @@ __locale:439: error: unknown rune table for this platform

With it at `1`, none.

⚠️ **A claim that drifts from what the port provides fails neither the build nor
**A claim that drifts from what the port provides fails neither the build nor
the link.** It produces a program that takes a path the environment cannot
support, and reports nothing. `_LIBCPP_HAS_RANDOM_DEVICE` was `0` until openkal
gained `openkal.random`; `_LIBCPP_HAS_TERMINAL` was `1` while every `isatty`
Expand Down Expand Up @@ -94,7 +94,7 @@ only under it — LLVM's own runtimes build defines it for the same reason).
objects, `ELF 64-bit LSB relocatable, UCB RISC-V`, with `__cxa_throw` and
`__cxa_begin_catch` defined.

⚠️ `import std;` on such a target is still refused, and not by this package:
`import std;` on such a target is still refused, and not by this package:

```
error: `import std;` is not available on 'riscv64-none-elf'
Expand Down Expand Up @@ -129,7 +129,7 @@ whether a hosted standard library is *present*.
`examples/import-std` asserts the other half: `import std;` — the module, not the
headers — with `std::ranges::sort` and `std::println`.

## ⚠️ The observation a build cannot make
## The observation a build cannot make

The last two assertions are the package. Everything else here a runtime that was
linked but never worked would also satisfy.
Expand Down Expand Up @@ -170,7 +170,7 @@ reaches one of them by mistake still works. There is nothing to reach on the
fourth: 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`.

⚠️ **And the artefacts are run on the real machines, not inspected.** Continuous
**And the artefacts are run on the real machines, not inspected.** Continuous
integration builds all three hosted targets on Linux and executes each on the
system it was built for; `mcpp`'s own `openkal-cross` workflow builds them from
three hosts and runs all nine combinations. Every difference this package had to
Expand All @@ -189,7 +189,7 @@ definitions — **links successfully and fails at run time**.
The port is Apache-2.0. The vendored sources under `llvm/` are Apache-2.0 with
LLVM exceptions; `llvm/LICENSE.TXT` is theirs.

⚠️ They are **almost** unchanged, and the exceptions are enumerated rather than
They are **almost** unchanged, and the exceptions are enumerated rather than
described. `llvm/PATCHES.md` lists every one — five regions in four files, each
between `// ─── openkal ─── BEGIN` and `// ─── openkal ─── END`, countable with

Expand Down
Loading
Loading