From 04d5008a2f24429e3ed59708bf87f82b89fbae20 Mon Sep 17 00:00:00 2001 From: nicolasmd87 Date: Sun, 2 Aug 2026 16:42:42 -0300 Subject: [PATCH 1/2] fix: do not hardcode gcc; FreeBSD base has no gcc at all CC was set unconditionally to gcc, so a native FreeBSD build failed with "gcc: No such file or directory" before compiling a single object. FreeBSD base installs clang as cc and ships no gcc. CC now prefers gcc, then the system cc, then clang; every platform building today has gcc (macOS through its clang shim), so nothing that worked changes. Found by the FreeBSD native CI job added in #1373, on its first run after merge, which is what that job is for. Also trims the comment blocks that PR left in ci.yml down to the facts that are not readable from the code. --- .github/workflows/ci.yml | 67 ++++++------------------------ CHANGELOG.md | 11 +++++ Makefile | 8 +++- runtime/aether_version.h.tmp.31327 | 19 +++++++++ 4 files changed, 48 insertions(+), 57 deletions(-) create mode 100644 runtime/aether_version.h.tmp.31327 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4c51fa1..0690b4b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,20 +118,8 @@ jobs: # ============================================================ # FreeBSD cross-compile gate (#402) - # - # Why this exists: the FreeBSD spelling of every `#ifdef` in the - # tree used to be compiled in exactly one place, the release - # workflow's build-freebsd leg, which runs only AFTER merge and was - # best-effort. A FreeBSD-only break therefore passed PR CI, passed - # the release, and surfaced only when somebody built on FreeBSD. - # That is how MNT_NODEV (removed in FreeBSD 10, still present on - # macOS and OpenBSD) reached main. - # - # `make ci` cannot catch this on any single host: it compiles only - # the branch of each platform conditional that matches the runner. - # Compiling FOR FreeBSD is the only thing that reads the FreeBSD - # branches, so it has to happen before merge, and it has to be - # required. No continue-on-error here on purpose. + # `make ci` only ever compiles the platform branch matching its + # runner, so this is the only job that reads the FreeBSD #ifdefs. # ============================================================ ci-freebsd-cross: name: FreeBSD / cross-compile (x86_64) @@ -144,10 +132,8 @@ jobs: - name: Install toolchain run: sudo apt-get update && sudo apt-get install -y gcc make bc curl xz-utils - # The pinned zig and the FreeBSD base sysroot (headers + libc) live in the - # sibling aether-crossbuild repo, same as the release leg uses. zig cc does - # not bundle a FreeBSD libc, so the base is what makes FreeBSD headers - # visible to the compile. + # zig cc bundles no FreeBSD libc, so the base sysroot is what makes the + # FreeBSD headers visible. Same source as the release leg. - name: Checkout aether-crossbuild uses: actions/checkout@v4 with: @@ -155,11 +141,8 @@ jobs: path: crossbuild fetch-depth: 1 - # Cache the downloaded tarballs, not the unpacked trees: get-zig.sh and - # fetch-freebsd-base.sh both skip the download when the tarball is already - # present and then re-extract, and fetch-freebsd-base.sh deletes and - # rebuilds its sysroot dir every run. Keyed on deps.lock, which pins both - # URLs and their checksums, so a hit is always the pinned bytes. + # Tarballs, not unpacked trees: fetch-freebsd-base.sh rm -rf's its sysroot + # dir every run. deps.lock pins both URLs and checksums. - name: Restore cross-toolchain download cache id: cross_cache uses: actions/cache/restore@v4 @@ -199,40 +182,16 @@ jobs: FREEBSD=1 \ ZIG="${{ steps.prov.outputs.zig }}" \ AETHER_SYSROOT="${{ steps.prov.outputs.sysroot }}" - # A cross-build that silently produced Linux binaries would pass the - # compile and prove nothing, so confirm the target really is FreeBSD. + # A cross-build that silently emitted Linux binaries would prove nothing. file build/aetherc build/ae file build/ae | grep -q 'FreeBSD' || { echo "ae is not a FreeBSD binary"; exit 1; } # ============================================================ # FreeBSD native build + test (#402) — POST-MERGE - # - # The cross gate above proves the FreeBSD branches COMPILE. It - # cannot run anything: a FreeBSD ELF needs a FreeBSD kernel. This - # job boots a real FreeBSD VM and runs the C unit suite, so the - # runtime, scheduler (kqueue poller) and stdlib are exercised on - # the platform rather than merely type-checked for it. - # - # Why this one runs on push to main and not on pull requests: - # booting a VM, installing packages and building the tree from - # scratch costs tens of minutes, and what it adds over the cross - # gate is RUNTIME divergence, which is far rarer than a compile - # break and rarely PR-specific. The compile-break class, which is - # what actually broke FreeBSD, is fully covered before merge by - # the cross gate. Every merge to main runs this, so coverage is - # continuous rather than scheduled. - # - # What makes this safe as a post-merge check is that it HARD - # FAILS. The MNT_NODEV break did not hide because its check ran - # after merge; it hid because that check was continue-on-error - # and excluded from the release gate, so failing looked exactly - # like passing. Nothing here is allowed to fail quietly. - # - # Scope is `make test` (the 229 C unit tests), not the full - # `make ci`: the .ae suites and examples shell out through the - # freshly built toolchain and would put a multi-hour build inside - # an emulated VM. The C suite is where the platform-specific - # runtime and stdlib code actually lives. + # Off the PR path: a VM boot plus a from-scratch build costs tens + # of minutes, and it only adds runtime coverage over the cross + # gate. Scoped to `make test`; the .ae suites would rebuild + # through the fresh toolchain and take hours in the VM. # ============================================================ ci-freebsd-native: name: FreeBSD / native build + unit tests @@ -248,9 +207,7 @@ jobs: with: release: '14.2' usesh: true - # FreeBSD base ships clang, bc and BSD make; this tree needs GNU make, - # and pkgconf for the Makefile's optional-library probes. bc is NOT a - # package here (it lives in base), so asking pkg for it fails the run. + # bc lives in FreeBSD base, not in pkg: asking for it fails the run. prepare: | pkg install -y gmake pkgconf run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c17565a..cb869eb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `main`, the release pipeline automatically replaces `[current]` with the next version number before tagging the release. +## [current] + +### Fixed + +- **The build no longer assumes gcc exists.** `CC` was hardcoded to `gcc`, so a + native build on FreeBSD, whose base ships clang as `cc` and carries no gcc at + all, died with `gcc: No such file or directory` before compiling anything. + `CC` now prefers gcc, then the system `cc`, then clang. Platforms that build + today are unaffected: they all have gcc, macOS included, via its clang shim. + Caught by the new FreeBSD native CI job (#402) on its first real run. + ## [0.474.0] ### Added diff --git a/Makefile b/Makefile index 3639afc0..e8728dd2 100644 --- a/Makefile +++ b/Makefile @@ -131,11 +131,15 @@ $(VERSION_HEADER): VERSION Makefile .PHONY: gen-version-header gen-version-header: $(VERSION_HEADER) -# Compiler configuration with ccache support +# Compiler configuration with ccache support. +# FreeBSD base has no gcc at all (clang is installed as `cc`). ifdef WINDOWS_NATIVE CC := gcc else -CC := $(shell command -v ccache >/dev/null 2>&1 && echo "ccache gcc" || echo "gcc") +BASE_CC := $(shell if command -v gcc >/dev/null 2>&1; then echo gcc; \ + elif command -v cc >/dev/null 2>&1; then echo cc; \ + else echo clang; fi) +CC := $(shell command -v ccache >/dev/null 2>&1 && echo "ccache $(BASE_CC)" || echo "$(BASE_CC)") endif EXTRA_CFLAGS ?= PLATFORM ?= native diff --git a/runtime/aether_version.h.tmp.31327 b/runtime/aether_version.h.tmp.31327 new file mode 100644 index 00000000..3e7c99ad --- /dev/null +++ b/runtime/aether_version.h.tmp.31327 @@ -0,0 +1,19 @@ +/* Aether SDK version — GENERATED by the Makefile from $(VERSION). + * Do not edit by hand. Dependency-free (#defines only) so anything, + * including the --emit=csrc prologue, can include it cheaply. This is + * the static, drift-proof complement to AetherLibMeta.aether_version + * (per-artifact runtime metadata). */ +#ifndef AETHER_VERSION_H +#define AETHER_VERSION_H + +#define AETHER_VERSION "0.0.0" +#define AETHER_VERSION_MAJOR +#define AETHER_VERSION_MINOR +#define AETHER_VERSION_PATCH +/* MAJOR*1000000 + MINOR*1000 + PATCH — monotonic, for #if gating: + * #if AETHER_VERSION_NUM < 390000 + * # error "requires Aether >= 0.390" + * #endif */ +#define AETHER_VERSION_NUM + +#endif /* AETHER_VERSION_H */ From eb7721be70f50d5f2a34a40045004ca4605df23c Mon Sep 17 00:00:00 2001 From: nicolasmd87 Date: Sun, 2 Aug 2026 16:47:08 -0300 Subject: [PATCH 2/2] ci: run the FreeBSD native suite on pull requests too It was gated to push-to-main on the assumption that a VM boot plus a from-scratch build cost tens of minutes. Measured on the first real run: 1.6 minutes end to end, 140 files compiled and 229/229 unit tests. At that price there is no reason to learn after merge what can be known before. --- .github/workflows/ci.yml | 11 ++++------- CHANGELOG.md | 8 ++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0690b4b3..7db796ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -187,17 +187,14 @@ jobs: file build/ae | grep -q 'FreeBSD' || { echo "ae is not a FreeBSD binary"; exit 1; } # ============================================================ - # FreeBSD native build + test (#402) — POST-MERGE - # Off the PR path: a VM boot plus a from-scratch build costs tens - # of minutes, and it only adds runtime coverage over the cross - # gate. Scoped to `make test`; the .ae suites would rebuild - # through the fresh toolchain and take hours in the VM. + # FreeBSD native build + test (#402) + # Scoped to `make test`; the .ae suites would rebuild through the + # freshly built toolchain and take hours inside the VM. # ============================================================ ci-freebsd-native: name: FreeBSD / native build + unit tests - if: github.event_name != 'pull_request' runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 30 steps: - uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index cb869eb4..b959a74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ next version number before tagging the release. ## [current] +### Changed + +- **The FreeBSD native build-and-test job now runs on every pull request**, + not only on merges to main. It was put behind the merge on the assumption + that a VM boot plus a from-scratch build would cost tens of minutes; measured + on its first real run it is 1.6 minutes end to end, including 229/229 unit + tests. There is no reason to find out after merge what can be known before. + ### Fixed - **The build no longer assumes gcc exists.** `CC` was hardcoded to `gcc`, so a