diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e95294..c33dd0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -169,17 +169,25 @@ jobs: # unresolvable here — this version is not published yet, and a check that # silently fell back to the previous one would be testing the wrong tree. # - # ⚠️ IN THE `gate` JOB'S riscv64 ROW, AND THE PLACEMENT IS THE FIX FOR A - # MISTAKE. 0.4.0 put this in `host-encoders`, which installs no emulator — + # ⚠️ IN THE `gate` JOB, AND THE PLACEMENT IS THE FIX FOR A MISTAKE MADE + # TWICE. 0.4.0 put this in `host-encoders`, which installs no emulator — # so the check could only ever assert `mcpp build`, and a generated project # that ran on nothing passed it. The emulator lives here. # - # One row rather than all three: the template's CONTENT is + # ⚠️ AND 0.5.0 MADE THE SAME MISTAKE ONE SIZE SMALLER. This step ran only + # in the riscv64 row, which installs only the RISC-V emulator, so the + # claim it could support was "the template runs on riscv64" while the + # template's README had begun to claim all three. It did not: the + # template's own `build.mcpp.in` was left with x86_64's old early return, + # and nothing here could see that. The RUN half is per-row from 0.5.1 — + # each row asserts the machine whose emulator it installed. + # + # The BUILD half stays on one row: the template's CONTENT is # host-independent, and the rewrite below puts `$PWD` into a manifest, # which on a Windows runner is `/d/a/openarch/openarch` under Git Bash # while mcpp wants a native path — a mismatch this repository has been # bitten by before. - - name: The template generates a project that builds for all three machines and runs + - name: The template generates a project that builds for all three machines if: matrix.arch == 'riscv64' run: | set -euo pipefail @@ -202,25 +210,47 @@ jobs: done echo "the template builds for riscv64, aarch64 and x86_64" - # ⚠️ AND IT MUST RUN, NOT ONLY BUILD — THIS ASSERTION IS HERE BECAUSE - # ITS ABSENCE SHIPPED A BROKEN QUICK START. - # - # 0.4.0's check stopped at `mcpp build`, so a generated project that - # compiled for all three machines and ran on none of them passed. What - # was missing is not in the sources: `mcpp.toml` declares the emulator - # packages under `[xlings] deps`, which is not an install trigger, so - # on a machine that has not installed them `build.mcpp` finds nothing - # and configures no runner. Measured against a freshly unpacked mcpp. - # - # This job installs the emulator in an earlier step, so the run below - # exercises the path a user gets AFTER the README's one-time install. - # What it cannot cover is the before — see the note in the README, and - # the comment in build.mcpp on why a build program cannot warn. - ( cd "$D" && mcpp run --target riscv64-none-elf 2>&1 | tee run.log ) \ - || { cat "$D/run.log"; echo "the generated project does not run"; exit 1; } + # ⚠️ AND IT MUST RUN, NOT ONLY BUILD — THIS ASSERTION EXISTS BECAUSE ITS + # ABSENCE SHIPPED A BROKEN QUICK START, AND IT IS PER-ROW BECAUSE ITS + # BEING ON ONE ROW SHIPPED A SECOND ONE. + # + # 0.4.0's check stopped at `mcpp build`, so a generated project that + # compiled for all three machines and ran on none of them passed. What was + # missing is not in the sources: `mcpp.toml` declares the emulator packages + # under `[xlings] deps`, which is not an install trigger, so on a machine + # that has not installed them `build.mcpp` finds nothing and configures no + # runner. + # + # Each row installs one emulator, so each row asserts one machine. A row + # cannot assert a machine whose emulator it does not have — which is the + # whole reason the previous arrangement could not see that the template's + # `build.mcpp.in` still had x86_64's early return in it. + # + # This job installs the emulator in an earlier step, so the run below + # exercises the path a user gets AFTER the README's one-time install. What + # it cannot cover is the before — see the note in the README, and the + # comment in build.mcpp on why a build program cannot warn. + - name: The template's project runs on ${{ matrix.arch }}, not only builds + run: | + set -euo pipefail + T=templates/three-machines + V=$(grep -m1 '^version' mcpp.toml | cut -d'"' -f2) + D=$(mktemp -d); mkdir -p "$D/src" + for f in mcpp.toml README.md build.mcpp; do + sed -e "s/{{project\.name}}/k/g" -e "s/{{self\.version}}/$V/g" \ + "$T/$f.in" > "$D/$f" + done + cp "$T"/*.ld "$D/" + cp "$T"/src/* "$D/src/" + sed -i.bak "s|openarch = \"$V\"|openarch = { path = \"$PWD\" }|" "$D/mcpp.toml" + rm -f "$D/mcpp.toml.bak" + grep -q "path = " "$D/mcpp.toml" || { cat "$D/mcpp.toml"; echo "the dependency rewrite did not apply"; exit 1; } + ( cd "$D" && mcpp build --target ${{ matrix.triple }} > /dev/null 2>&1 ) || true + ( cd "$D" && mcpp run --target ${{ matrix.triple }} 2>&1 | tee run.log ) \ + || { cat "$D/run.log"; echo "the generated project does not run on ${{ matrix.arch }}"; exit 1; } grep -q "switch ok" "$D/run.log" \ || { cat "$D/run.log"; echo "the generated project ran without reaching the end"; exit 1; } - echo "the template's project runs, not only builds" + echo "the template's project runs on ${{ matrix.arch }}" # The same source produced that output. Asserted rather than trusted: a # probe that had quietly grown a per-architecture branch would still pass diff --git a/abi/mcpp.toml b/abi/mcpp.toml index 582e178..d181b04 100644 --- a/abi/mcpp.toml +++ b/abi/mcpp.toml @@ -24,7 +24,7 @@ [package] namespace = "mcpplibs" name = "openarch-abi" -version = "0.5.0" +version = "0.5.1" description = "openarch's C ABI: the contract between the interface and an instruction set's backend" license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/backends/aarch64/mcpp.toml b/backends/aarch64/mcpp.toml index b48972d..10c8912 100644 --- a/backends/aarch64/mcpp.toml +++ b/backends/aarch64/mcpp.toml @@ -14,7 +14,7 @@ [package] namespace = "mcpplibs" name = "openarch-aarch64" -version = "0.5.0" +version = "0.5.1" description = "openarch's aarch64 backend: the instructions behind the ABI" license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/backends/riscv64/mcpp.toml b/backends/riscv64/mcpp.toml index 9cfec3e..90c7702 100644 --- a/backends/riscv64/mcpp.toml +++ b/backends/riscv64/mcpp.toml @@ -14,7 +14,7 @@ [package] namespace = "mcpplibs" name = "openarch-riscv64" -version = "0.5.0" +version = "0.5.1" description = "openarch's riscv64 backend: the instructions behind the ABI" license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/backends/x86_64/mcpp.toml b/backends/x86_64/mcpp.toml index 309cc3e..ec4d773 100644 --- a/backends/x86_64/mcpp.toml +++ b/backends/x86_64/mcpp.toml @@ -22,7 +22,7 @@ [package] namespace = "mcpplibs" name = "openarch-x86-64" -version = "0.5.0" +version = "0.5.1" description = "openarch's x86_64 backend: the instructions behind the ABI" license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/mcpp.toml b/mcpp.toml index bc06864..73f9c77 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -33,7 +33,7 @@ [package] namespace = "mcpplibs" name = "openarch" -version = "0.5.0" +version = "0.5.1" description = "openarch: the architecture-mechanism layer — execution contexts, traps, per-CPU state and address spaces, as one interface over several instruction sets" license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/templates/three-machines/build.mcpp.in b/templates/three-machines/build.mcpp.in index 4ef7c0d..78a476d 100644 --- a/templates/three-machines/build.mcpp.in +++ b/templates/three-machines/build.mcpp.in @@ -30,25 +30,25 @@ int main() { // against whichever home owns it, which is not necessarily the home this // build is using. // - // The two packages are different — xPack builds QEMU per target family, so - // `qemu-riscv` carries only the two RISC-V emulators and `qemu-arm` only - // the two Arm ones. There is no single package that runs both machines. - // ⚠️ x86_64 IS ABSENT FROM THIS TABLE, AND THE ABSENCE IS THE ECOSYSTEM'S - // RATHER THAN THIS PROBE'S. xPack publishes QEMU per target family, and the - // index carries `qemu-riscv` and `qemu-arm` because those are the families - // it publishes. There is no `xim:qemu-x86`, so on that target this build - // program configures no runner and `mcpp build` is the whole of what CI can - // do — which is stated here rather than worked around, because a runner - // silently pointing at a host `qemu-system-x86_64` would make the build - // depend on what happens to be installed. - if (arch == "x86_64") { - mcpp::rerun_if_env_changed("MCPP_TARGET_ARCH"); - mcpp::rerun_if_changed("x86_64.ld"); - return 0; - } - - const char* pkg = (arch == "riscv64") ? "qemu-riscv" : "qemu-arm"; - const char* sys = (arch == "riscv64") ? "riscv64" : "aarch64"; + // ⭐ THREE PACKAGES, ONE PER MACHINE, AND THAT IS THE ECOSYSTEM'S SHAPE + // RATHER THAN THIS PROBE'S. QEMU is published per target family, so + // `qemu-riscv` carries the RISC-V emulators, `qemu-arm` the Arm ones and + // `qemu-x86` the x86_64 one. No single package runs all three machines, + // which is why this table exists at all. + // + // ⚠️ x86_64 WAS AN EXCEPTION HERE UNTIL 0.5.0, and the exception was the + // absence of a package rather than a property of the machine: with no + // `xim:qemu-x86` this program configured no runner, CI could assert only + // `mcpp build`, and the third machine was the one machine whose findings + // could not be reproduced by running it. `mcpplibs/qemu-x86` builds that + // emulator from source for five hosts and the exception is gone — the row + // below is now the same shape as the other two. + const char* pkg = (arch == "riscv64") ? "qemu-riscv" + : (arch == "aarch64") ? "qemu-arm" + : "qemu-x86"; + const char* sys = (arch == "riscv64") ? "riscv64" + : (arch == "aarch64") ? "aarch64" + : "x86_64"; // ⚠️ DECLARED IS NOT INSTALLED, AND THE DIFFERENCE IS INVISIBLE UNTIL // SOMEBODY BUILDS THIS ON A MACHINE THAT HAS NOT ALREADY GOT THE EMULATOR. // @@ -79,8 +79,14 @@ int main() { // So the README carries it, and this comment records why it has to. if (const char* dir = mcpp::xpkg_dir("xim", pkg); dir && *dir) { mcpp::runner(std::format("{}/bin/qemu-system-{}", dir, sys).c_str()); + // ⚠️ THE MACHINE TYPE IS NOT UNIVERSAL. `virt` is the para-virtual + // board the two RISC machines use; x86_64 has no such board and boots + // `q35`, a model of real PC chipset hardware. The image reaches it + // through multiboot rather than through `-kernel`'s raw-image path, + // which is why the linker script for that machine carries an a.out + // kludge the other two do not need. mcpp::runner("-machine"); - mcpp::runner("virt"); + mcpp::runner(arch == "x86_64" ? "q35" : "virt"); // ⚠️ aarch64's `virt` has no default CPU that implements the features // this image needs; riscv's does. Naming one on the machine that // requires it rather than on both, so that the argument list says which @@ -98,5 +104,6 @@ int main() { mcpp::rerun_if_env_changed("MCPP_TARGET_ARCH"); mcpp::rerun_if_changed("riscv64.ld"); mcpp::rerun_if_changed("aarch64.ld"); + mcpp::rerun_if_changed("x86_64.ld"); return 0; }