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
104 changes: 104 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 (`<arch>-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; }
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <windows.h>` 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 `<arch>-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.
17 changes: 17 additions & 0 deletions examples/image-cxx/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# The C++ runtime inside an ok image (target `<arch>-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"
36 changes: 36 additions & 0 deletions examples/image-cxx/src/main.cpp
Original file line number Diff line number Diff line change
@@ -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<std::string, int> m{{"a", 1}, {"b", 2}};
std::vector<int> 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;
}
16 changes: 12 additions & 4 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down Expand Up @@ -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
# (`<arch>-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]

Expand Down Expand Up @@ -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 (`<arch>-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",
Expand Down Expand Up @@ -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",
Expand Down
8 changes: 7 additions & 1 deletion tools/branch-graph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading