Add a native build mode so macOS gets tested at all - #10
Merged
Conversation
Every build these definitions have been through was Linux in Docker, because test/build can only test via Docker. macOS is the platform this repo exists for and it has never been built with the current flags — so `test/build native`, which builds on the host into a throwaway prefix and runs the same post-flight checks the containers run. That matters most for the arithmetic assertion. The 2**64 == 0 bug was GCC exploiting signed-overflow UB at -O3; whether Apple clang does the same was untested, and untestable. Today's Macs all compute 2**64 correctly because none of them is at -O3 via CFLAGS — the risk arrives with the next rebuild, not before it, which is exactly when you want to have already checked. Opt-in by name. PLATFORMS is untouched, so `test/build all` stays the clean-room Docker matrix; a native build compiles against whatever the host happens to have and proves nothing about a fresh machine. The verify script now takes its prefix from $RUBY_PREFIX rather than hardcoding /opt/ruby, so both paths run the same assertions — the point of the mode is that macOS gets these and not a weaker set. test_ruby splits into docker_build and native_build with the result reporting shared; the docker invocation itself is unchanged. Two details worth their comments. ruby-build is resolved once up front and a missing one is a hard error, never a skip — a native run that can't build is a failed run. It usually isn't on PATH, since mise invokes its own copy directly, so mise's cache is consulted too, via `mise cache` rather than a hardcoded ~/.cache/mise: on macOS that cache lives under ~/Library/Caches, and macOS is the whole point here. Each build also gets its own TMPDIR, because ruby-build derives both its log path and its build directory from it and these run concurrently — otherwise a failure tail is some neighbour's log. Prefixes are removed as soon as they're verified rather than kept. Six Rubies is several GB and the machines that need this are laptops. Verified. macOS (M1 Max, Apple clang 21 under Xcode-beta): 6/6, arithmetic assertions included, so clang does not reproduce the GCC signed-overflow miscompile. Not a vacuous pass either — a 1.8.7 built there reports CFLAGS "-O3 -fno-strict-overflow …", CC clang, and 2**64 correct, so the flags really did reach the compile line. Arch native 6/6, and the Docker matrix still 12/12. One wrinkle worth knowing: 1.8.7's freshen_automake_config shells out to `brew install automake` on macOS, so that build inherits whatever state brew is in. It failed once mid-upgrade with a brew rename error, unrelated to anything here, and passed on a retry.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every build these definitions have been through was Linux in Docker, because
test/buildcan only test via Docker. macOS is the platform this repo exists for and it had never been
built with the current flags.
So
test/build native <version>: builds on the host into a throwaway prefix and runs thesame post-flight checks the containers run.
That matters most for the arithmetic assertion. The
2**64 == 0bug was GCC exploitingsigned-overflow UB at
-O3; whether Apple clang does the same was untested, and untestable.Today's Macs all compute
2**64correctly because none of them is at-O3via CFLAGS —the risk arrives with the next rebuild, not before it, which is exactly when you want to
have already checked.
Opt-in by name.
PLATFORMSis untouched, sotest/build allstays the clean-roomDocker matrix. A native build compiles against whatever the host happens to have and proves
nothing about a fresh machine.
Shape of the change
test/buildis the only file touched.$RUBY_PREFIXinstead of hardcoding/opt/ruby,so both paths run identical assertions — the point of the mode is that macOS gets these
and not a weaker set.
test_rubysplits intodocker_buildandnative_buildwith the result reporting shared.The docker invocation itself is unchanged.
build_imagereturns early fornative; theRESULTS/trap,JOBSthrottle,single-
printfresult lines and exit-code-is-failure-count convention are reused as-is.Still Bash 3.2 clean.
Two details worth their comments.
ruby-buildis resolved once up front and a missing oneis a hard error, never a skip — a native run that can't build is a failed run. It usually
isn't on PATH, since mise invokes its own copy directly, so mise's cache is consulted too
via
mise cacherather than a hardcoded~/.cache/mise: on macOS that cache lives under~/Library/Caches, and macOS is the whole point here. Each build also gets its ownTMPDIR, because ruby-build derives both its log path and its build directory from it andthese run concurrently — otherwise a failure tail is some neighbour's log.
Prefixes are removed as soon as they're verified rather than kept. Six Rubies is several GB
and the machines that need this are laptops.
Verification
JOBS=2 test/build native alltest/build native alltest/build all(Docker, must be undisturbed)Apple clang does not reproduce the GCC signed-overflow miscompile. And not a vacuous
pass: a 1.8.7 built on that Mac reports
so the flags really did reach the compile line.
One wrinkle worth knowing: 1.8.7's
freshen_automake_configshells out tobrew install automakeon macOS, so that build inherits whatever state brew is in. Itfailed once mid-upgrade with a brew rename error, unrelated to anything here, and passed on
a retry.
bin/cigreen with signoff; shellcheck clean at--severity=warning.