Skip to content

Provide net, datagram, timeout and exec; decline space in terms #79

Provide net, datagram, timeout and exec; decline space in terms

Provide net, datagram, timeout and exec; decline space in terms #79

Workflow file for this run

name: CI
# What this workflow asserts.
#
# conformance the suite in the specification package runs against this
# implementation and every observation holds, under all three
# of this environment's toolchains
# surface the exported names are exactly the fifty-one the
# specification lists
# independence the objects reference no C runtime symbol
#
# This environment has two application binary interfaces and three toolchains
# that reach them, and an implementation that built under one of the three would
# not be an implementation for this environment. So all three are built: the two
# that produce the environment's own ABI, and the one that produces the other.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
inputs:
mcpp_ref:
description: "Branch of mcpp-community/mcpp to build and test against (empty = the released pin)"
required: false
default: ""
env:
MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF }}
MCPP_VERSION: 2026.8.27.1
XLINGS_VERSION: v2026.8.17.2
XLINGS_NON_INTERACTIVE: '1'
jobs:
# ---------------------------------------------------------------------------
# On the system itself.
conformance:
name: conformance (${{ matrix.name }})
runs-on: windows-2022
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- { name: 'llvm, msvc ABI', toolchain: 'llvm@20.1.7', target: '' }
- { name: 'msvc', toolchain: 'msvc@system', target: '' }
- { name: 'gcc, gnu CRT', toolchain: 'gcc@16.1.0', target: 'x86_64-windows-gnu' }
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
# The specification is checked out at the branch under test where it has
# one, so that this run asserts what it is for: that the specification as
# written there and this implementation as written here agree today.
- name: The specification
run: |
git clone --quiet https://github.com/mcpplibs/openkal.git .spec
branch='${{ github.head_ref || github.ref_name }}'
if git -C .spec rev-parse --verify --quiet "origin/$branch" > /dev/null; then
git -C .spec checkout --quiet "origin/$branch"
echo "the specification is at $branch"
else
echo "the specification has no $branch; its default branch is used"
fi
- name: Install xlings
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: Install mcpp
run: |
xlings update
# ⚠️ THE PIN MAY NAME THE RELEASE THIS RUN IS VALIDATING, which does
# not exist yet — that is the whole point of MCPP_SOURCE_REF. Bootstrap
# from whatever the index has; the step below replaces it with the
# build under review, and the pin is what an ordinary run tests.
if [ -n "${MCPP_SOURCE_REF:-}" ]; then
xlings install mcpp -y -g
else
xlings install "mcpp@$MCPP_VERSION" -y -g
fi
# ⚠️⚠️ TRANSITION: GIVE THE BOOTSTRAP THE glibc ITS BINDING NAMES.
#
# `xim:glibc`'s `latest` moved from `2.44` to `2.44.2`. A payload
# directory is named after the version a request RESOLVED to, while a
# RuntimeBinding carries the version that was DECLARED — and the xlings
# a released mcpp vendors into its own sandbox still declares `2.44`.
# So a clean machine installs `2.44.2`, the toolchain fixup asks for
# `2.44`, and the build stops before anything is compiled:
#
# error: selected RuntimeBinding glibc@2.44 requires payload
# '…/xpkgs/xim-x-glibc/2.44', but it is not installed
#
# ⚠️ On every NEW machine and on none that already existed, which is why
# it is invisible from a developer's own. Measured on `main` as readily
# as on any branch — the index records the same failure verbatim in
# `pkgs/g/glibc.lua` and states the rule it broke: "The index is DATA
# and the client is a PROGRAM: the consumer ships first."
#
# ⭐ REMOVE THIS once a released mcpp resolves it. `mcpp 2026.8.27.1`
# accepts an installed payload whose version REFINES the requested one
# (`payload_dir_for_version`), so a bootstrap from it needs nothing
# here. Until then the missing payload is simply installed.
if [ -x "$HOME/.mcpp/registry/bin/xlings" ]; then
XLINGS_HOME="$HOME/.mcpp/registry" XLINGS_NON_INTERACTIVE=1 \
"$HOME/.mcpp/registry/bin/xlings" install glibc@2.44 -y -g \
>/dev/null 2>&1 || true
echo "glibc payloads present: $(ls "$HOME/.mcpp/registry/data/xpkgs/xim-x-glibc" 2>/dev/null | tr '\n' ' ')"
fi
mcpp --version
mcpp self config --mirror GLOBAL
# ⭐⭐ CROSS-VALIDATION: BUILD THE mcpp UNDER REVIEW AND USE THAT ONE.
#
# Empty in the ordinary run, so this job keeps testing the RELEASED
# mcpp the pin above names. Set it — `workflow_dispatch` input, or the
# repository variable — and the same job runs against that source.
#
# ⚠️ THIS EXISTS BECAUSE THE ORDER USED TO BE WRONG. Several mcpp
# releases went out green and only then turned this ecosystem red: the
# engine's own CI cannot see a defect that appears only in a real
# dependency graph, and this repository could not see the engine until
# it had been published. Validating before the release closes that gap.
#
# The released mcpp installed just above is the bootstrap that compiles
# it; mcpp builds itself and there is no other compiler for it here.
if [ -n "${MCPP_SOURCE_REF:-}" ]; then
src="$RUNNER_TEMP/mcpp-src"
[ -d "$src" ] || git clone --quiet --depth 1 \
--branch "$MCPP_SOURCE_REF" \
https://github.com/mcpp-community/mcpp.git "$src"
# ⚠️ THE CLONE'S OWN WORKSPACE PIN MUST NOT DECIDE WHICH mcpp
# BUILDS IT. `.xlings.json` at mcpp's root pins the mcpp that
# compiles mcpp, and that pin does not move when mcpp is released —
# so a build inside the checkout obeys it and tries to install a
# version the index may no longer carry:
#
# [error] xlings: version '2026.8.17.1' not found for 'mcpp'
# available: 2026.8.25.1
#
# What is wanted here is the source compiled by the mcpp installed
# above, which is exactly what removing the file leaves.
rm -f "$src/.xlings.json"
( cd "$src" && mcpp build --release )
# ⚠️ BOTH SPELLINGS, AND NO `-perm`. The matrix reaches Windows and
# macOS runners too: on Windows the artefact is `mcpp.exe`, and
# `-perm -u+x` is not a question that filesystem answers the way this
# expects. Measured: `Finished release [optimized] in 173.44s`
# followed by "mcpp did not build" — the build had succeeded and the
# search was looking for the wrong name.
#
# `$src` is a FRESH clone each run, so `target/` holds exactly what
# this step just built; `-printf` would be the safer form on a cached
# tree and is a GNU extension this must not use.
built=$(find "$src/target" -type f \
\( -name mcpp -o -name mcpp.exe \) | head -1)
[ -n "$built" ] || { echo "::error::mcpp did not build from $MCPP_SOURCE_REF"; exit 1; }
echo "$(cd "$(dirname "$built")" && pwd)" >> "$GITHUB_PATH"
# ⚠️ Reported, because a PATH entry that does not win looks exactly
# like one that does until something built with the wrong engine.
echo "under review: $("$built" --version) (from $MCPP_SOURCE_REF)"
fi
# EVERY NAME THE HEADER DECLARES IS EXPORTED BY ONE OF THE .def FILES.
#
# `port/*.def` is an explicit list, and an import library generated from it
# contains exactly those names. So a declaration added to `src/win32.h`
# without a matching line in a `.def` compiles, and fails at the link of a
# CONSUMER --- not of this package, which does not link.
#
# ⚠️ MEASURED. Three declarations were added for openkal 0.8 and the names
# were not, and this package's own CI stayed green: the failure appeared in
# openkal-llvm-runtime's cross-build, one repository away, as
#
# ld.lld: error: undefined symbol: __declspec(dllimport) CreatePipe
#
# which reads as a defect in the consumer. The check belongs here, where
# the two lists are.
- name: Every declared name is exported by a .def
run: |
python3 - <<'PY'
import glob, os, re, sys
# ⚠️ THE ENCODING IS NAMED. Python opens a file with the platform's
# default, which on this runner is cp1252, and these sources are UTF-8:
#
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x90
#
# A check that reads source files must say what they are encoded in, or
# it reports on the runner's locale.
declared = set(re.findall(r'OKW_IMPORT\s+\w+\s+OKW_API\s+(\w+)\s*\(',
open("src/win32.h", encoding="utf-8").read()))
exported = set()
for f in glob.glob("port/*.def"):
body = open(f, encoding="utf-8").read().split("EXPORTS", 1)
if len(body) < 2: continue
exported |= {l.strip() for l in body[1].split("\n")
if l.strip() and not l.lstrip().startswith(';')}
# A denominator on both sides: with either list empty the difference is
# vacuously empty too.
if not declared or not exported:
print(f"::error::declared={len(declared)} exported={len(exported)}; nothing was compared")
sys.exit(1)
missing = sorted(declared - exported)
print(f" {len(declared)} declared, {len(exported)} exported across "
f"{len(glob.glob('port/*.def'))} .def files")
if missing:
print("::error::declared in src/win32.h and exported by no .def:")
for m in missing: print(f" {m}")
sys.exit(1)
print(" ok every declared name is exported")
PY
- name: Select the toolchain
run: |
spec='${{ matrix.toolchain }}'
case "$spec" in
msvc*) mcpp toolchain default msvc ;;
*) mcpp toolchain install "${spec%@*}" "${spec#*@}"
mcpp toolchain default "$spec" ;;
esac
mcpp toolchain list
- name: Every interface, every kind of examination
run: |
extra=''
[ -n '${{ matrix.target }}' ] && extra='--target ${{ matrix.target }}'
# ⚠️ THE SET IS ENUMERATED AND `optional` IS NOT NAMED, AND THE
# DIFFERENCE IS ONE INTERFACE.
#
# `optional` includes `space`, which this system does not provide:
# there is no primitive here that copies an address space and starts a
# context in the copy. A set demanding it would not report an
# observation that did not hold — it would fail to LINK, naming
# `kal_space_start`, which is clause 6.1's report arriving where a
# report was not wanted.
#
# So the six this implementation does provide are named, and the
# seventh is absent from the list for the same reason it is absent
# from the objects.
bash .spec/tools/run-conformance.sh openkal-windows . \
full,exec,random,terminal,net,datagram,timeout $extra
# ---------------------------------------------------------------------------
# From a system that is not this one.
#
# The same package, produced by a cross build and run under an implementation
# of this environment's interfaces rather than on the environment. It is here
# for two reasons: it is where the exported names can be examined with tools
# that read this environment's object format and are not on it, and a package
# that only builds on the system it targets is a package a contributor on any
# other system cannot work on.
cross:
name: cross build, run under wine, and examine the objects
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: The specification
run: |
git clone --quiet https://github.com/mcpplibs/openkal.git .spec
branch='${{ github.head_ref || github.ref_name }}'
if git -C .spec rev-parse --verify --quiet "origin/$branch" > /dev/null; then
git -C .spec checkout --quiet "origin/$branch"
fi
- name: Install wine
run: |
sudo dpkg --add-architecture i386
sudo apt-get update -qq
# Which package carries it and what the program is called differ
# between distributions and between releases of one, so both are
# tried and the name that exists is the one used. A script that names
# one of them fails with "command not found" after a successful
# install, which reads as a missing package rather than a renamed
# program.
sudo apt-get install -y --no-install-recommends wine wine64 > /dev/null 2>&1 \
|| sudo apt-get install -y --no-install-recommends wine > /dev/null
runner="$(command -v wine || command -v wine64)"
test -n "$runner" || { echo "no wine was installed" >&2; exit 1; }
"$runner" --version
echo "OPENKAL_CONFORMANCE_RUNNER=$runner" >> "$GITHUB_ENV"
- name: Install xlings
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: |
xlings update
# ⚠️ THE PIN MAY NAME THE RELEASE THIS RUN IS VALIDATING, which does
# not exist yet — that is the whole point of MCPP_SOURCE_REF. Bootstrap
# from whatever the index has; the step below replaces it with the
# build under review, and the pin is what an ordinary run tests.
if [ -n "${MCPP_SOURCE_REF:-}" ]; then
xlings install mcpp -y -g
else
xlings install "mcpp@$MCPP_VERSION" -y -g
fi
mcpp self config --mirror GLOBAL
# ⭐⭐ CROSS-VALIDATION: BUILD THE mcpp UNDER REVIEW AND USE THAT ONE.
#
# Empty in the ordinary run, so this job keeps testing the RELEASED
# mcpp the pin above names. Set it — `workflow_dispatch` input, or the
# repository variable — and the same job runs against that source.
#
# ⚠️ THIS EXISTS BECAUSE THE ORDER USED TO BE WRONG. Several mcpp
# releases went out green and only then turned this ecosystem red: the
# engine's own CI cannot see a defect that appears only in a real
# dependency graph, and this repository could not see the engine until
# it had been published. Validating before the release closes that gap.
#
# The released mcpp installed just above is the bootstrap that compiles
# it; mcpp builds itself and there is no other compiler for it here.
if [ -n "${MCPP_SOURCE_REF:-}" ]; then
src="$RUNNER_TEMP/mcpp-src"
[ -d "$src" ] || git clone --quiet --depth 1 \
--branch "$MCPP_SOURCE_REF" \
https://github.com/mcpp-community/mcpp.git "$src"
# ⚠️ THE CLONE'S OWN WORKSPACE PIN MUST NOT DECIDE WHICH mcpp
# BUILDS IT. `.xlings.json` at mcpp's root pins the mcpp that
# compiles mcpp, and that pin does not move when mcpp is released —
# so a build inside the checkout obeys it and tries to install a
# version the index may no longer carry:
#
# [error] xlings: version '2026.8.17.1' not found for 'mcpp'
# available: 2026.8.25.1
#
# What is wanted here is the source compiled by the mcpp installed
# above, which is exactly what removing the file leaves.
rm -f "$src/.xlings.json"
( cd "$src" && mcpp build --release )
# ⚠️ BOTH SPELLINGS, AND NO `-perm`. The matrix reaches Windows and
# macOS runners too: on Windows the artefact is `mcpp.exe`, and
# `-perm -u+x` is not a question that filesystem answers the way this
# expects. Measured: `Finished release [optimized] in 173.44s`
# followed by "mcpp did not build" — the build had succeeded and the
# search was looking for the wrong name.
#
# `$src` is a FRESH clone each run, so `target/` holds exactly what
# this step just built; `-printf` would be the safer form on a cached
# tree and is a GNU extension this must not use.
built=$(find "$src/target" -type f \
\( -name mcpp -o -name mcpp.exe \) | head -1)
[ -n "$built" ] || { echo "::error::mcpp did not build from $MCPP_SOURCE_REF"; exit 1; }
echo "$(cd "$(dirname "$built")" && pwd)" >> "$GITHUB_PATH"
# ⚠️ Reported, because a PATH entry that does not win looks exactly
# like one that does until something built with the wrong engine.
echo "under review: $("$built" --version) (from $MCPP_SOURCE_REF)"
fi
- name: Every interface, every kind of examination
env:
WINEDEBUG: '-all'
run: |
bash .spec/tools/run-conformance.sh openkal-windows . \
full,exec,random,terminal,net,datagram,timeout \
--target x86_64-windows-gnu
- name: The exported surface is complete and contains nothing else
run: |
# Clause 9.3. The list and the checker come from the specification
# rather than from a copy kept here, so that the comparison has one
# source. --complete because this implementation claims every
# interface: a name it fails to export is a failure, not an interface
# it declines to provide.
rm -rf target && mcpp build --target x86_64-windows-gnu
objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' | tr '\n' ' ')"
test -n "$objs" || { echo "no objects were found" >&2; exit 1; }
bash .spec/tools/check-surface.sh --complete .spec/SURFACE.txt $objs
# The property this implementation exists to have. A program above openkal
# may supply a C runtime of its own; an implementation that reached for
# this environment's would have its calls resolve to the program's, and
# the program's would resolve back here.
- name: The objects reference no C runtime symbol
run: |
rm -rf target && mcpp build --features standalone --target x86_64-windows-gnu
objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o')"
test -n "$objs" || { echo "no objects were found; the check would pass vacuously" >&2; exit 1; }
# The permitted set, and why each entry is in it.
#
# __imp_* this environment's own interfaces, reached through
# the import table. They are the environment, not a C runtime.
# Nt*, Rtl* the object manager, likewise.
# WaitOnAddress, WakeByAddress* the suspension primitive, which
# this environment publishes without an import stub.
# memcpy, memmove, memset, memcmp a compiler emits calls to these
# from ordinary loops. They compute rather than call, so none of
# them can re-enter this implementation.
# __libc_start_main, main the hand-over, undefined here by
# construction.
# kal_*, _ZN3okw* the interface and this package.
# __chkstk_ms, __udivti3, ... the compiler's own support
# routines, which are in the compiler's library and not in a C
# runtime.
permitted='^(__imp_[A-Za-z0-9_]+|Nt[A-Za-z]+|Rtl[A-Za-z]+|WaitOnAddress|WakeByAddress(All|Single)|memcpy|memmove|memset|memcmp|__libc_start_main|main|kal_[a-z_]+|_ZN3okw.*|_*chkstk[a-z_]*|__udiv[a-z0-9]*|__umod[a-z0-9]*|__divti3|__modti3|_GLOBAL_OFFSET_TABLE_)$'
bad=0
for s in $(nm --undefined-only $objs | awk '{print $2}' | sort -u); do
[ -n "$s" ] || continue
printf '%s\n' "$s" | grep -qE "$permitted" || {
echo "the implementation references a symbol it must not: $s" >&2
bad=1
}
done
test "$bad" -eq 0
echo "the implementation references no C runtime symbol"
# A checker is only useful if it fails when it should.
- name: The independence check detects a dependence
run: |
printf 'extern "C" int puts(const char*);\nextern "C" void okw_probe(void) { puts("x"); }\n' > src/probe.cpp
rm -rf target && mcpp build --features standalone --target x86_64-windows-gnu
objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o')"
if ! nm --undefined-only $objs | awk '{print $2}' | grep -qx puts; then
echo "the probe did not produce the reference it was written to produce" >&2
rm -f src/probe.cpp; exit 1
fi
rm -f src/probe.cpp
echo "a dependence upon a C runtime is visible to the check"