diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eac1abf7..8c430de0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,3 +44,42 @@ jobs: run: MODE=muen opam exec -- dune build --root example - name: Compile examples with xen run: MODE=xen opam exec -- dune build --root example + + # TEMP: end-to-end macOS check that pins the not-yet-released stack + # (Solo5/solo5#641 + #642, mirage/ocaml-gmp#29, ocaml/Zarith#173). solo5 is a + # tar pin to exercise the SOLO5_VERSION fallback. Drop once those land in opam. + macos-e2e: + name: macOS aarch64 e2e (zarith) + runs-on: macos-latest + env: + OPAMYES: 1 + steps: + - uses: actions/checkout@v6 + # No brew install or PATH setup: opam pulls the LLVM toolchain and target + # runtime through solo5-cross-aarch64's depexts, and configure resolves the + # keg-only tools to absolute paths. + - uses: ocaml/setup-ocaml@v3 + with: + ocaml-compiler: 5.4.1 + opam-pin: false + - name: Pin the stack and build the cross-compiler + run: | + opam pin add -n solo5 https://github.com/samoht/solo5/archive/refs/heads/macos.tar.gz + opam pin add -n solo5-cross-aarch64 https://github.com/samoht/solo5/archive/refs/heads/macos.tar.gz + opam pin add -n gmp git+https://github.com/samoht/ocaml-gmp.git#ship-gmp-pc + opam pin add -n zarith git+https://github.com/samoht/zarith.git#honor-ocaml-env + opam pin add -n ocaml-solo5-cross-aarch64 . + opam install ocaml-solo5-cross-aarch64 + - name: Cross-build zarith against ocaml-gmp's pkg-config (Zarith#173) + run: | + set -euo pipefail + opam remove -y zarith gmp conf-gmp || true + opam install gmp # ocaml-gmp fork: relocatable gmp.pc under lib/pkgconfig + OCAMLC="ocamlfind -toolchain solo5 ocamlc" \ + OCAMLOPT="ocamlfind -toolchain solo5 ocamlopt" \ + OCAMLDEP="ocamlfind -toolchain solo5 ocamldep" \ + OCAMLMKLIB="ocamlfind -toolchain solo5 ocamlmklib" \ + PKG_CONFIG_PATH="$(opam var lib)/pkgconfig:${PKG_CONFIG_PATH:-}" \ + opam install zarith --verbose 2>&1 | tee zarith-build.log + # fail unless zarith was actually built through the solo5 cross toolchain + grep -qE 'aarch64-solo5-ocaml|toolchain solo5' zarith-build.log diff --git a/Makefile b/Makefile index add901bc..a0762c34 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,8 @@ nolibc/libnolibc.a: phony-nolibc phony-nolibc: $(MAKE) -C nolibc libnolibc.a \ "CC=$(MAKECONF_TOOLCHAIN)-cc" \ + "AR=$(MAKECONF_TOOLPREFIX)ar" \ + "RANLIB=$(MAKECONF_TOOLPREFIX)ranlib" \ "FREESTANDING_CFLAGS=$(NOLIBC_CFLAGS)" # OPENLIBM @@ -38,6 +40,8 @@ openlibm/libopenlibm.a: phony-openlibm phony-openlibm: $(MAKE) -C openlibm libopenlibm.a \ "CC=$(MAKECONF_TOOLCHAIN)-cc" \ + "AR=$(MAKECONF_TOOLPREFIX)ar" \ + "RANLIB=$(MAKECONF_TOOLPREFIX)ranlib" \ "CPPFLAGS=$(LIB_CFLAGS)" # TOOLCHAIN diff --git a/configure.sh b/configure.sh index f9dfd870..fda49471 100755 --- a/configure.sh +++ b/configure.sh @@ -77,6 +77,20 @@ TARGET_TRIPLET="$("$CONFIG_TARGET-cc" -dumpmachine)" MAKECONF_TOOLPREFIX="${MAKECONF_TOOLPREFIX:-$TARGET_TRIPLET-}" +# On macOS the llvm- tools (ar, ranlib, nm) live in a keg-only/MacPorts bindir +# off PATH; pin the prefix there so the build and wrappers resolve them. +if ! command -v "${MAKECONF_TOOLPREFIX}ar" >/dev/null 2>&1; then + for bindir in \ + "$(command -v brew >/dev/null 2>&1 && brew --prefix llvm 2>/dev/null)/bin" \ + /opt/homebrew/opt/llvm/bin /usr/local/opt/llvm/bin \ + /opt/local/libexec/llvm-*/bin; do + if [ -x "${bindir}/${MAKECONF_TOOLPREFIX}ar" ]; then + MAKECONF_TOOLPREFIX="${bindir}/${MAKECONF_TOOLPREFIX}" + break + fi + done +fi + case "${TARGET_TRIPLET}" in amd64-*|x86_64-*) TARGET_ARCH="x86_64" diff --git a/gen_toolchain_tool.sh b/gen_toolchain_tool.sh index e711ad30..0af37fa1 100755 --- a/gen_toolchain_tool.sh +++ b/gen_toolchain_tool.sh @@ -100,18 +100,24 @@ gen_tool() { TARGET_TOOL="$TARGET_STRIP" ;; esac + # Resolve to an absolute path so the generated wrapper does not depend on the + # tool's directory being on PATH when the wrapper runs. This matters for the + # OTHERTOOLPREFIX tools (e.g. llvm-ar on macOS), which live outside the Solo5 + # toolchain and the opam switch, so a plain `dune build -x` would otherwise + # fail to find them. if test "$TARGET_TOOL" ; then TOOL="$TARGET_TOOL" - elif command -v -- "$SOLO5_TOOLCHAIN-$TOOL" > /dev/null; then - TOOL="$SOLO5_TOOLCHAIN-$TOOL" + elif resolved="$(command -v -- "$SOLO5_TOOLCHAIN-$TOOL")"; then + TOOL="$resolved" else case "$TOOL" in as) - TOOL="$SOLO5_TOOLCHAIN-cc -c" + resolved="$(command -v -- "$SOLO5_TOOLCHAIN-cc")" + TOOL="${resolved:-$SOLO5_TOOLCHAIN-cc} -c" ;; *) - if command -v -- "$OTHERTOOLPREFIX$TOOL" > /dev/null; then - TOOL="$OTHERTOOLPREFIX$TOOL" + if resolved="$(command -v -- "$OTHERTOOLPREFIX$TOOL")"; then + TOOL="$resolved" fi ;; esac diff --git a/nolibc/Makefile b/nolibc/Makefile index 9ecd3aa1..79f1a1e7 100644 --- a/nolibc/Makefile +++ b/nolibc/Makefile @@ -10,7 +10,7 @@ clean: $(RM) libnolibc.a *.o test-include/*.[co] test-include/sys/*.[co] CC=cc -CFLAGS=-O2 -std=c99 -Wall -Wno-parentheses -Werror +CFLAGS=-O2 -std=c99 -Wall -Wno-parentheses -Wno-asm-operand-widths -Werror CFLAGS+=$(FREESTANDING_CFLAGS) OBJS=assert.o \ diff --git a/ocaml-solo5-cross-aarch64.opam b/ocaml-solo5-cross-aarch64.opam index c5789f0e..b66bdde6 100644 --- a/ocaml-solo5-cross-aarch64.opam +++ b/ocaml-solo5-cross-aarch64.opam @@ -13,6 +13,12 @@ build: [ "--target=aarch64-solo5-none-static" "--ocaml-configure-option=--disable-flat-float-array" {ocaml-option-no-flat-float-array:installed} "--ocaml-configure-option=--enable-flambda" {ocaml-option-flambda:installed} + # macOS uses the LLVM "other" tools (llvm-ar/ranlib/nm/objcopy); the + # freestanding nolibc lacks the C99 float ops clang would otherwise inline, + # and the cross compiler is built without function sections. + "--othertoolprefix=llvm-" {os = "macos"} + "--ocaml-configure-option=--enable-imprecise-c99-float-ops" {os = "macos"} + "--ocaml-configure-option=--disable-function-sections" {os = "macos"} ] [make "-j%{jobs}%"] [make "%{name}%.install"] @@ -31,8 +37,10 @@ depends: [ "opatch" {build} # to patch the compiler sources "ocamlfind" {build} "ocaml-src" {build} - "ocaml" {= "5.4.1" | "5.5.0"} - "solo5" {>= "0.9.1"} + "ocaml" {= "5.4.1" | = "5.5.0"} + # On macOS the native solo5 (tenders) has no build; the cross OCaml needs + # only the cross toolchain + headers from solo5-cross-aarch64. + "solo5" {>= "0.9.1" & os != "macos"} "solo5-cross-aarch64" {>= "0.9.1" } ] conflicts: [ @@ -45,7 +53,8 @@ conflicts: [ available: [ ((os = "linux" & (arch = "x86_64" | arch = "arm64")) | (os = "freebsd" & arch = "x86_64") - | (os = "openbsd" & arch = "x86_64")) + | (os = "openbsd" & arch = "x86_64") + | (os = "macos" & arch = "arm64")) ] x-maintenance-intent: [ "(latest)" ] synopsis: "OCaml cross-compiler to the freestanding 64-bit ARM Solo5 backend"