From 6ed0750ab7dd852509d6e50a5795d9f3587ddd43 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Mon, 26 Oct 2026 22:31:42 +0900 Subject: [PATCH] 0.16.0 --- the runtime inside an ok image (-openkal), above openkal-musl 0.22.0's image form Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com> --- .github/workflows/ci.yml | 104 ++++++++++++++++++++++++++++++++ README.md | 12 ++++ examples/image-cxx/mcpp.toml | 17 ++++++ examples/image-cxx/src/main.cpp | 36 +++++++++++ mcpp.toml | 16 +++-- tools/branch-graph.sh | 8 ++- 6 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 examples/image-cxx/mcpp.toml create mode 100644 examples/image-cxx/src/main.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 464ea10c..8d85b146 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -621,3 +621,107 @@ jobs: grep -q 'caught: 42' out.log grep -q 'unwound: true' out.log grep -q 'import std over openkal: ok' out.log + + # ── AN OK IMAGE (`-openkal`, openkal SPEC clause 12) ───────────────── + # + # libc++, libc++abi, libunwind and the builtins inside an image, above + # openkal-musl's image form: built once on Linux for both architectures by the + # mcpp that has the target, and the same bytes run through okrun on four + # systems. + images: + name: ok images (built once) + runs-on: ubuntu-24.04 + timeout-minutes: 120 + env: + MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF || 'release/2026.10.26.2' }} + XLINGS_VERSION: v2026.10.26.1 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - name: Install xlings + run: | + curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 \ + 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: The mcpp that has the target + run: | + xlings update > /dev/null 2>&1 || true + xlings install mcpp -y -g + mcpp self config --mirror GLOBAL + src="$RUNNER_TEMP/mcpp-src" + git clone --quiet --depth 1 --branch "$MCPP_SOURCE_REF" https://github.com/mcpp-community/mcpp.git "$src" + rm -f "$src/.xlings.json" + ( cd "$src" && mcpp build --release ) + built=$(find "$src/target" -type f -name mcpp | head -1) + [ -n "$built" ] || { echo "::error::mcpp did not build"; exit 1; } + echo "$(cd "$(dirname "$built")" && pwd)" >> "$GITHUB_PATH" + - name: The stack under test, from the same-named branch + run: bash tools/branch-graph.sh '${{ github.head_ref || github.ref_name }}' + - name: The images + run: | + set -euo pipefail + for arch in x86_64 aarch64; do + mkdir -p "out/$arch" + ( cd examples/image-cxx && mcpp build --target "$arch-openkal" ) + cp examples/image-cxx/target/"$arch"-openkal/*/bin/image-cxx.oke "out/$arch/" + done + (cd out && sha256sum */* | tee SHA256SUMS) + - uses: actions/upload-artifact@v4 + with: { name: cxx-images, path: out } + + images-run: + name: ok images on ${{ matrix.host }} + needs: images + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + env: + XLINGS_VERSION: v2026.10.26.1 + strategy: + fail-fast: false + matrix: + include: + - { host: linux-x86_64, os: ubuntu-24.04, arch: x86_64 } + - { host: linux-aarch64, os: ubuntu-24.04-arm, arch: aarch64 } + - { host: macos-arm64, os: macos-15, arch: aarch64 } + - { host: windows-x86_64, os: windows-2022, arch: x86_64 } + defaults: + run: + shell: bash + steps: + - uses: actions/download-artifact@v4 + with: { name: cxx-images, path: out } + - name: Install xlings (Unix) + if: runner.os != 'Windows' + run: | + curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 \ + 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 xlings (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + irm https://d2learn.org/xlings-install.ps1.txt | iex + "$env:USERPROFILE\.xlings\subos\current\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: okrun, built here + run: | + xlings install mcpp -y -g + mcpp self config --mirror GLOBAL + mcpp toolchain install llvm 23.1.3 + mcpp toolchain default llvm@23.1.3 + git clone --quiet --depth 1 --branch openkal-0.17.0 https://github.com/mcpplibs/openkal-image.git "$RUNNER_TEMP/oi" + ( cd "$RUNNER_TEMP/oi/tools" && mcpp build --release ) + okrun=$(find "$RUNNER_TEMP/oi/target" -path '*bin*' \( -name okrun -o -name okrun.exe \) -type f | head -1) + [ -n "$okrun" ] || { echo "::error::okrun did not build"; exit 1; } + echo "OKRUN=$okrun" >> "$GITHUB_ENV" + - name: The same bytes + run: | + set -euo pipefail + a='${{ matrix.arch }}' + if command -v sha256sum > /dev/null; then (cd out && sha256sum -c SHA256SUMS) + else (cd out && shasum -a 256 -c SHA256SUMS); fi + "$OKRUN" "out/$a/image-cxx.oke" | tr -d '\r' | tee run.log + grep -q -- '-- failures: 0 --' run.log || { echo "::error::image-cxx"; exit 1; } diff --git a/README.md b/README.md index 9bdd452c..a9ede860 100644 --- a/README.md +++ b/README.md @@ -222,3 +222,15 @@ fit it is not made. The larger half of that document is the list of places that did **not** need changing — twelve of the nineteen `#include ` sites resolve themselves once the predicate is answered correctly, because libc++'s POSIX branch was already reaching openkal through musl. + +## Inside an ok image (0.16.0) + +For the target `-openkal` (openkal SPEC clause 12) the runtime is built +into an **ok image** that `okrun` runs on Linux, macOS and Windows of its +architecture. It takes the Linux selection --- the same C library above +openkal, the same object format and calling convention --- and depends on +`openkal-musl` 0.22.0 without its default `image-builtins` feature, since this +package supplies the compiler runtime itself and a build has one. +`examples/image-cxx` (an exception, a thread with its own thread-local +variable, the containers, formatting) is built once on Linux for both +architectures and run through okrun on four systems in CI. diff --git a/examples/image-cxx/mcpp.toml b/examples/image-cxx/mcpp.toml new file mode 100644 index 00000000..00ef2857 --- /dev/null +++ b/examples/image-cxx/mcpp.toml @@ -0,0 +1,17 @@ +# The C++ runtime inside an ok image (target `-openkal', openkal SPEC +# clause 12): one image per architecture, run through okrun on Linux, macOS and +# Windows. `import std', an exception, a thread, a thread-local variable and the +# containers, above openkal-musl's image form. +[package] +name = "image-cxx" +version = "0.1.0" + +[dependencies] +openkal-llvm-runtime = { path = "../.." } + +[build-dependencies] +openkal-tools = { git = "https://github.com/mcpplibs/openkal-image.git", branch = "openkal-0.17.0", tools = ["okld", "okrun"] } + +[targets.image-cxx] +kind = "bin" +main = "src/main.cpp" diff --git a/examples/image-cxx/src/main.cpp b/examples/image-cxx/src/main.cpp new file mode 100644 index 00000000..fa9186f5 --- /dev/null +++ b/examples/image-cxx/src/main.cpp @@ -0,0 +1,36 @@ +// The C++ runtime in an ok image: each line is one observation, and the last +// counts the ones that did not hold. +import std; + +namespace { +int failures = 0; +void check(bool held, std::string_view what) { + std::println("{} {}", held ? " ok " : " FAIL", what); + if (!held) ++failures; +} +thread_local int tl = 7; +} + +int main(int argc, char** argv) { + check(argc >= 1 && std::string_view(argv[0]).ends_with(".oke"), "the image is told its own name"); + + bool caught = false; + try { throw std::runtime_error("thrown"); } + catch (const std::exception& e) { caught = std::string_view(e.what()) == "thrown"; } + check(caught, "an exception is caught by its base class"); + + int seen = 0; + std::thread t([&] { seen = tl; tl = 99; }); + t.join(); + check(seen == 7 && tl == 7, "a thread has its own thread-local variable"); + + std::map m{{"a", 1}, {"b", 2}}; + std::vector v(100); + std::iota(v.begin(), v.end(), 1); + check(m.at("b") == 2 && std::accumulate(v.begin(), v.end(), 0) == 5050, "the containers and algorithms"); + + check(std::format("{:>6.2f}", 3.14159) == " 3.14", "formatting a floating-point value"); + + std::println("-- failures: {} --", failures); + return failures ? 1 : 0; +} diff --git a/mcpp.toml b/mcpp.toml index b3cb0646..5eb13853 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-llvm-runtime" -version = "0.15.6" +version = "0.16.0" description = "LLVM's C++ runtime libraries — libc++, libc++abi and libunwind — configured for openkal-musl rather than for a host C library." license = "Apache-2.0" authors = ["mcpplibs"] @@ -216,7 +216,11 @@ sources = [ cflags = ["-DDISABLE_AARCH64_FMV=1"] [dependencies] -openkal-musl = "0.21.1" +# WITHOUT ITS DEFAULT `image-builtins'. This package supplies the compiler +# runtime itself (`mcpp:compiler-runtime=compiler-rt' above), and on an ok image +# (`-openkal') that feature would bring a second supplier of the same layer +# --- which a build refuses rather than picks between. +openkal-musl = { version = "0.22.0", default-features = false, features = ["posix"] } [build] @@ -472,7 +476,11 @@ sources = [ # Building these alongside `-lgcc` is not a duplicate-definition hazard. An # archive contributes only what is still undefined, so if ours are linked first # the archive is never consulted for them. -[target.'cfg(os = "linux")'.build] +# THE OK IMAGE (`-openkal', openkal SPEC clause 12) TAKES THE LINUX +# SELECTION: the same C library above openkal, the same ELF object format and +# the same calling convention per architecture; what differs is the loader that +# binds the image's `kal_*' imports, which this package never names. +[target.'cfg(any(os = "linux", os = "openkal"))'.build] sources = [ "llvm/compiler-rt/lib/builtins/*.c", "!llvm/compiler-rt/lib/builtins/atomic*.c", @@ -509,7 +517,7 @@ sources = [ # today, so the exclusion looked like an OS property. It is not. Sorting on the # axis the fact actually lives on is what makes a second architecture work # without a third copy of the list. -[target.'cfg(all(os = "linux", not(arch = "x86_64")))'.build] +[target.'cfg(all(any(os = "linux", os = "openkal"), not(arch = "x86_64")))'.build] sources = [ "!llvm/compiler-rt/lib/builtins/*xf*.c", "!llvm/compiler-rt/lib/builtins/*xc3.c", diff --git a/tools/branch-graph.sh b/tools/branch-graph.sh index 963c555f..888abb9b 100755 --- a/tools/branch-graph.sh +++ b/tools/branch-graph.sh @@ -94,8 +94,14 @@ for backend in $(grep -oE '^openkal-[a-z]+ = \{ version' .musl/mcpp.toml | cut - manifests+=(".$backend/mcpp.toml") done +# The image form's loader library (`openkal-ld', a member of openkal-image). +fetch openkal-image .openkal-image +edit .musl/mcpp.toml 's|^openkal-ld = .*$|openkal-ld = { path = "../.openkal-image/ld" }|' + edit .musl/mcpp.toml 's|^openkal = .*$|openkal = { path = "../.spec" }|' -edit mcpp.toml 's|^openkal-musl = .*$|openkal-musl = { path = "./.musl" }|' +# The tail is kept: this package asks for the C library without its default +# features (see the manifest). +edit mcpp.toml 's|^openkal-musl = \{ version = "[^"]*"(.*)$|openkal-musl = { path = "./.musl"\1|' # The substitution is asserted rather than assumed. One that matched nothing # would leave the manifest naming a version, the resolver would fetch a