diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 404ed06..e1deee2 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -6,10 +6,10 @@ jobs: syntax-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v6 - name: Check Markdown links - uses: gaurav-nelson/github-action-markdown-link-check@v1 + uses: gaurav-nelson/github-action-markdown-link-check@3c3b66f1f7d0900e37b71eca45b63ea9eedfce31 # v1.0.17 with: use-verbose-mode: 'yes' - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79664ad..12e6c14 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,143 +4,186 @@ on: push: branches: - "main" + pull_request: + workflow_dispatch: + +# This repository restricts GitHub Actions to a curated allowlist +# (Settings > Actions > General > Allow select actions). Every `uses:` here +# must reference an allowlisted action, pinned as the allowlist requires. +# +# Coverage: +# * c-header-probe -- Linux musl/glibc ABI detection at the C +# preprocessor level (Alpine + Debian, x86_64 +# / x86 / arm64), using only `docker run`. +# * ocaml-alpine-musl -- the full dune-configurator -> c_abi.ml path +# on a real musl host. +# * Cross-Platform-Action -- FreeBSD and OpenBSD guests. +# * test-with-setup-ocaml -- Windows/macOS/Linux glibc via ocaml/setup-ocaml +# (not the DkML MSVC/MSYS2 + manylinux reusable +# workflow, whose actions are not allowlisted). jobs: + # Compile the C probe header on musl (Alpine) and glibc (Debian) for each + # Linux architecture that has a musl ABI variant, and assert the DKML_ABI + # string and its companion define. The OCaml layer above the header is + # architecture-independent string templating, so the full toolchain only + # needs to run once on a musl host (see ocaml-alpine-musl below). + c-header-probe: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + image: alpine:3.21 + expected: linux_x86_64_musl + setup: apk add --no-cache gcc musl-dev + - platform: linux/386 + image: alpine:3.21 + expected: linux_x86_musl + setup: apk add --no-cache gcc musl-dev + - platform: linux/arm64 + image: alpine:3.21 + expected: linux_arm64_musl + setup: apk add --no-cache gcc musl-dev + - platform: linux/amd64 + image: debian:bookworm + expected: linux_x86_64 + setup: apt-get update -qq && apt-get install -y -qq gcc + - platform: linux/386 + image: debian:bookworm + expected: linux_x86 + setup: apt-get update -qq && apt-get install -y -qq gcc + - platform: linux/arm64 + image: debian:bookworm + expected: linux_arm64 + setup: apt-get update -qq && apt-get install -y -qq gcc + name: c-probe / ${{ matrix.image }} ${{ matrix.platform }} + steps: + - name: Checkout code + uses: actions/checkout@v6 + + # Register binfmt_misc handlers for cross-architecture emulation. This + # is what docker/setup-qemu-action does internally; run the image + # directly so no non-allowlisted GitHub Action is required. + - name: Enable qemu emulation + if: matrix.platform == 'linux/arm64' + run: docker run --privileged --rm tonistiigi/binfmt --install arm64 + + - name: Probe DKML_ABI + run: | + cat > probe.c <<'EOF' + #include + #include "src/config/dkml_compiler_probe.h" + int main(void) { printf("%s\n", DKML_ABI); return 0; } + EOF + cat > check.c < generated c_abi.ml path reports Linux_x86_64_musl. + ocaml-alpine-musl: + runs-on: ubuntu-latest + timeout-minutes: 60 + name: ocaml / alpine (musl x86_64) + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Build, test and probe on Alpine + run: | + docker run --rm -v "$PWD:/src" ocaml/opam:alpine-ocaml-4.14 sh -ec ' + sudo cp -r /src /home/opam/work + sudo chown -R opam /home/opam/work + cd /home/opam/work + opam update + opam install . --deps-only --with-test --yes + opam exec -- dune build --display=short + opam exec -- dune runtest --display=short + opam exec -- dune install + opam exec -- sh -c "cat samples/show_abi.ml | ocaml" | tee /tmp/show_abi.out + grep -q "Linux_x86_64_musl" /tmp/show_abi.out' + test-with-Cross-Platform-Action: - # macOS-12 is only GitHub CI runner that supports VirtualBox - # - the suggested 10.15 from https://stackoverflow.com/questions/66261101/using-vagrant-on-github-actions-ideally-incl-virtualbox - # is deprecated (https://github.com/actions/runner-images/issues/5583) - # - virtualbox added to macOS-12 in https://github.com/actions/runner-images/pull/5594 - runs-on: macos-12 + # BSD guests in QEMU virtual machines on the Linux runners. (Historically + # this ran VirtualBox on macos-12, the only runner that supported it; + # macos-12 was retired in December 2024 and cross-platform-actions runs + # on ubuntu runners now.) + runs-on: ubuntu-latest timeout-minutes: 90 strategy: fail-fast: false matrix: os: + - name: freebsd + version: '14.2' - name: openbsd - version: '6.8' - - name: openbsd - version: '7.1' - # 6.9 gives ... - # <><> Fetching repository information ><><><><><><><><><><><><><><><><><><><><><> - # Error: Could not update repository "default": OpamDownload.Download_fail(_, "Download command failed: \"/usr/bin/ftp -o /tmp/opam-87092-8f3a45/index.tar.gz.part -U opam/2.1.2 -- https://opam.ocaml.org/index.tar.gz\" exited with code 1 \"TLS handshake failure: certificate verification failed: certificate has expired\"") - # because a syspatch has not been applied to the Cross-Platform-Action OpenBSD 6.9. - # See https://marc.info/?l=openbsd-misc&m=163303405028215&w=2 - # It would be nice to run syspatch at startup, but it complains of `syspatch: cannot apply patches while reorder_kernel is running` - # and `run_as_root rcctl disable library_aslr` (https://www.facebook.com/groups/2210554563/posts/10157117341779564/) is ineffective + version: '7.6' + # OpenBSD guests have historically been the flakiest; do not + # block the Tests on them. continue-on-error: yes - - name: freebsd - version: '13.0' - # netbsd 9.2 does not start in VirtualBox as of 2022-06-05 - # - name: netbsd - # version: '9.2' name: Cross-Platform-Action / ${{ matrix.os.name }}-${{ matrix.os.version }} continue-on-error: ${{ matrix.os.continue-on-error == 'yes' }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v6 - - name: Cache Opam root - uses: actions/cache@v3 - with: - path: _opamroot - key: ${{ matrix.os.name }}-${{ matrix.os.version }} - - - name: Cache Opam binaries - uses: actions/cache@v3 - with: - path: _opambin - key: ${{ matrix.os.name }}-${{ matrix.os.version }} - - - name: Test in VirtualBox - uses: cross-platform-actions/action@v0.6.2 + - name: Test in virtual machine + uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 with: operating_system: ${{ matrix.os.name }} version: ${{ matrix.os.version }} - shell: bash + shell: sh run: | - uname -a - usys=$(uname -s) - workspace_root=$(pwd) - export OPAMROOT="$workspace_root/_opamroot" - - run_as_root() { - # doas on OpenBSD is not enabled (yet) - sudo "$@" - } - - set -x - - case "$usys" in - OpenBSD) - run_as_root pkg_add curl - run_as_root pkg_add gmake bzip2 - run_as_root pkg_add gpatch - # 6.8 - run_as_root pkg_add gtar-1.32p1 unzip-6.0p13 || true - # 6.9 and 7.1 - run_as_root pkg_add gtar-1.34 unzip-6.0p14 || true - run_as_root pkg_add git - ;; + set -eux + # opam is available from the BSD package repositories; use the + # pkg-installed OCaml as the switch compiler (ocaml-system) so no + # compiler is built from source inside the (emulated) VM. The BSD + # opam ships a stale package index, so `opam update` is needed + # before ocaml-system can resolve against the current repository. + # `opam install .` (without --with-test) builds the package in + # release mode, which skips the ounit2/mdx test stanzas -- those + # are exercised on the setup-ocaml matrix below. Here we just + # confirm the BSD ABI is detected and the library builds/installs. + # git is required by `opam install .` to read the pinned local + # directory's VCS state; it is not in the OpenBSD base system. + case "$(uname -s)" in FreeBSD) - run_as_root pkg upgrade --yes pkg - run_as_root pkg install --yes curl - run_as_root pkg install --yes gmake bzip2 - run_as_root pkg install --yes patch gtar unzip - run_as_root pkg install --yes git + sudo pkg install --yes ocaml ocaml-opam git + expected=FreeBSD_x86_64 + ;; + OpenBSD) + sudo pkg_add -I ocaml opam git + expected=OpenBSD_x86_64 ;; esac - # bin/opam cache miss? - opamver="2.1.2" - opamsha256="de1e3efffd5942e0101ef83dcdead548cc65a93e13986aecb4a264a059457ede" - opamcachemiss=OFF - if [ ! -x "$workspace_root/_opambin/bin/opam" ]; then - opamcachemiss=ON - else - opamactualver=$("$workspace_root/_opambin/bin/opam" --version || true) - if [ ! "$opamactualver" = "$opamver" ]; then - opamcachemiss=ON - fi - fi - if [ "$opamcachemiss" = ON ]; then - # curl - curl -fsSL https://github.com/ocaml/opam/releases/download/$opamver/opam-full-$opamver.tar.gz > opam-full-$opamver.tar.gz - case "$usys" in - OpenBSD) - echo "SHA256 (opam-full-$opamver.tar.gz) = de1e3efffd5942e0101ef83dcdead548cc65a93e13986aecb4a264a059457ede" | sha256 -c - ;; - FreeBSD) - sha256 -c 'de1e3efffd5942e0101ef83dcdead548cc65a93e13986aecb4a264a059457ede' opam-full-$opamver.tar.gz - ;; - esac - tar xfz opam-full-$opamver.tar.gz - rm -f opam-full-$opamver.tar.gz - - # gmake bzip2 - cd opam-full-$opamver - gmake cold CONFIGURE_ARGS="--prefix '$workspace_root/_opambin'" - gmake cold-install - cd "$workspace_root" - rm -rf opam-full-$opamver - fi - PATH="$workspace_root/_opambin/bin:$PATH" + opam init --bare --auto-setup --yes --disable-sandboxing + opam update + opam switch create dcp ocaml-system --yes + eval "$(opam env)" - # minimize cross-platform-actions/action post-step 'rsync' that - # transfers files out of virtual machine - install -d "$workspace_root/_build" - trap 'rm -rf $workspace_root/_build' EXIT + # ocamlfind provides topfind, which samples/show_abi.ml loads via + # `#use "topfind"` before it can `#require` the package. + opam install . ocamlfind --yes - # gpatch gtar unzip - opam init --auto-setup - - # git - opam install . --deps-only --with-test --yes - - opam exec -- dune build --root "$workspace_root" --display=short - opam exec -- dune runtest --root "$workspace_root" --display=short - opam exec -- dune install --root "$workspace_root" - opam exec -- sh -c 'cat samples/show_abi.ml | ocaml' + # `ocaml` reading a script on stdin exits 0 even when the toplevel + # errors (e.g. an unbound module), so assert on the output rather + # than trusting the exit code. + opam exec -- sh -c 'cat samples/show_abi.ml | ocaml' | tee /tmp/abi.out + grep -qF "Ok Dkml_c_probe.C_abi.V4.$expected" /tmp/abi.out test-with-setup-ocaml: strategy: @@ -151,51 +194,21 @@ jobs: - ubuntu-latest - macos-latest ocaml-compiler: - - "4.13.x" + - "4.14.x" runs-on: ${{ matrix.os }} name: test-ocaml / ${{ matrix.os }}-${{ matrix.ocaml-compiler }} - # Windows on ocaml/setup-ocaml has been flaky ... let's not stop the Tests simply because it is flaky. - # Confer: ocaml/setup-ocaml issue no. 529 - # Also: - # -> installed opam-depext.1.1.5 - - # #===ERRORwhile compiling depext-cygwinports.0.0.9===========================# - # #context 2.0.10 | win32/x86_64 | ocaml-variants.4.13.1+mingw64c | git+https://github.com/fdopen/opam-repository-mingw.git#opam2 - # #path D:/a/dkml-c-probe/dkml-c-probe/_opam/.opam-switch/build/depext-cygwinports.0.0.9 - # #command D:\cygwin\bin\make.exe -j2 all - # #exit-code 2 - # #env-file D:/.opam/log/depext-cygwinports-1604-d2d111.env - # #output-file D:/.opam/log/depext-cygwinports-1604-d2d111.out - # ### output ### - # # ocamlopt -g str.cmxa unix.cmxa config_file.mli config_file.ml run.mli run.ml cygwin.mli cygwin.ml -o cygwin-install.exe - # # x86_64-w64-mingw32-gcc -O2 -fno-strict-aliasing -fwrapv -mms-bitfields -s symlink.c -o pkg-config.exe - # # ** Fatal error: Cannot run cygpath -m "libmsvcrt" "libmsvcrt.lib" "libmsvcrt.dll.a" "libmsvcrt.a" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\libmsvcrt" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\libmsvcrt.lib" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\libmsvcrt.dll.a" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\libmsvcrt.a" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\flexdll\libmsvcrt" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\flexdll\libmsvcrt.lib" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\flexdll\libmsvcrt.dll.a" "D:/a/dkml-c-probe/dkml-c-probe/_opam/lib/ocaml\flexdll\libmsvcrt.a" "/usr/lib/gcc/x86_64-w64-mingw32/11/libmsvcrt" "/usr/lib/gcc/x86_64-w64-mingw32/11/libmsvcrt.lib" "/usr/lib/gcc/x86_64-w64-mingw32/11/libmsvcrt.dll.a" "/usr/lib/gcc/x86_64-w64-mingw32/11/libmsvcrt.a" "/usr/x86_64-w64-mingw32/lib/x86_64-w64-mingw32/11/libmsvcrt" "/usr/x86_64-w64-mingw32/lib/x86_64-w64-mingw32/11/libmsvcrt.lib" "/usr/x86_64-w64-mingw32/lib/x86_64-w64-mingw32/11/libmsvcrt.dll.a" "/usr/x86_64-w64-mingw32/lib/x86_64-w64-mingw32/11/libmsvcrt.a" "/usr/x86_64-w64-mingw32/lib/libmsvcrt" "/usr/x86_64-w64-mingw32/lib/libmsvcrt.lib" "/usr/x86_64-w64-mingw32/lib/libmsvcrt.dll.a" "/usr/x86_64-w64-mingw32/lib/libmsvcrt.a" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/x86_64-w64-mingw32/11/libmsvcrt" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/x86_64-w64-mingw32/11/libmsvcrt.lib" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/x86_64-w64-mingw32/11/libmsvcrt.dll.a" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/x86_64-w64-mingw32/11/libmsvcrt.a" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libmsvcrt" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libmsvcrt.lib" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libmsvcrt.dll.a" "/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libmsvcrt.a" - # # File "caml_startup", line 1: - # # Error: Error during linking (exit code 2) - # # make: *** [Makefile:24: cygwin-install.exe] Error 2 + # Windows on ocaml/setup-ocaml has been flaky ... let's not stop the + # Tests simply because it is flaky. continue-on-error: ${{ startsWith(matrix.os, 'windows-') }} steps: - name: Checkout code - uses: actions/checkout@v3 - - - name: Hack Git CRLF for ocaml/setup-ocaml issue no. 529 - if: ${{ startsWith(matrix.os, 'windows-') }} - run: | - & "C:\Program Files\Git\bin\git.exe" config --system core.autocrlf input + uses: actions/checkout@v6 - name: OCaml ${{ matrix.ocaml-compiler }} with Dune cache - uses: ocaml/setup-ocaml@v2 - if: ${{ !startsWith(matrix.os, 'windows-') }} + uses: ocaml/setup-ocaml@57d9aa353e8d39677b1b9fc39637d48f27fe4a91 # v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} dune-cache: true - - name: OCaml ${{ matrix.ocaml-compiler }} without Dune cache - uses: ocaml/setup-ocaml@v2 - if: ${{ startsWith(matrix.os, 'windows-') }} - with: - ocaml-compiler: ${{ matrix.ocaml-compiler }} - dune-cache: false - cache-prefix: v3 - name: Install Opam dependencies run: opam install . --deps-only --with-test --yes - name: Build OCaml @@ -205,196 +218,7 @@ jobs: - name: Install dkml-c-probe run: opam exec -- dune install - name: Display probe - run: opam exec -- sh -c 'cat samples/show_abi.ml | ocaml' - - setup-dkml: - uses: "diskuv/dkml-workflows/.github/workflows/setup-dkml.yml@v0" - permissions: - # By explicitly setting at least one permission, all other permissions - # are set to none. setup-dkml.yml does not need access to your code! - # Verify in 'Set up job > GITHUB_TOKEN permissions'. - actions: none - with: - ocaml-compiler: 4.12.1 - - test-with-setup-dkml: - needs: setup-dkml - strategy: - fail-fast: false - matrix: - include: - - os: windows-2019 - abi-pattern: win32-windows_x86 - dkml-host-abi: windows_x86 - opam-root: D:/.opam - default_shell: msys2 {0} - msys2_system: MINGW32 - msys2_packages: mingw-w64-i686-pkg-config - bits: "32" - - os: windows-2019 - abi-pattern: win32-windows_x86_64 - dkml-host-abi: windows_x86_64 - opam-root: D:/.opam - default_shell: msys2 {0} - msys2_system: CLANG64 - msys2_packages: mingw-w64-clang-x86_64-pkg-config - bits: "64" - - os: macos-latest - abi-pattern: macos-darwin_all - dkml-host-abi: darwin_x86_64 - crosscompile-toolchain: darwin_arm64 - crosscompile-dune-context: | - (context (default (targets native darwin_arm64))) - default_shell: sh - opam-root: /Users/runner/.opam - bits: "64" - - os: ubuntu-latest - abi-pattern: manylinux2014-linux_x86 - bits: "32" - default_shell: sh - dkml-host-abi: linux_x86 - opam-root: .ci/opamroot # local directory of $GITHUB_WORKSPACE so available to dockcross - - os: ubuntu-latest - abi-pattern: manylinux2014-linux_x86_64 - bits: "64" - default_shell: sh - dkml-host-abi: linux_x86_64 - opam-root: .ci/opamroot # local directory of $GITHUB_WORKSPACE so available to dockcross - runs-on: ${{ matrix.os }} - name: test-dkml / ${{ matrix.abi-pattern }} - defaults: - run: - shell: ${{ matrix.default_shell }} - env: - OPAMROOT: ${{ matrix.opam-root }} - COMPONENT: dkml-component-staging-opam${{ matrix.bits }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - - uses: actions/download-artifact@v3 - with: - path: .ci/dist - - - name: Install MSYS2 (Windows) - if: startsWith(matrix.dkml-host-abi, 'windows_') - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.msys2_system }} - update: true - install: >- - ${{ matrix.msys2_packages }} - wget - make - rsync - diffutils - patch - unzip - git - tar - - - name: Import build environments from setup-dkml - run: | - ${{ needs.setup-dkml.outputs.import_func }} - import ${{ matrix.abi-pattern }} - - - name: Cache Opam downloads by host - uses: actions/cache@v3 - with: - path: ${{ matrix.opam-root }}/download-cache - key: ${{ matrix.dkml-host-abi }} - - - name: Dune-ify non-Dune Opam dependencies - run: | - opamrun repository add dune-universe git+https://github.com/dune-universe/opam-overlays.git#master - opamrun pin astring --no-action --yes -k version 0.8.5+dune - opamrun pin cmdliner --no-action --yes -k version 1.1.1+dune - opamrun pin fmt --no-action --yes -k version 0.8.9+dune - opamrun pin logs --no-action --yes -k version 0.7.0+dune2 - opamrun pin seq --no-action --yes -k version base+dune - - - name: Cache Opam binaries - uses: actions/cache@v3 - with: - path: _opambin - key: ${{ matrix.dkml-host-abi }} - - - name: Configure Opam for cross-compilation - if: matrix.crosscompile-dune-context - run: | - projectrundir=$(opamrun exec -- pwd) - - # Create / cache opam-installer which is not always present from setup-dkml.yml - if [ ! -x _opambin/bin/opam-installer ] && [ ! -x _opambin/bin/opam-installer.exe ]; then - # Get the binary - opamrun install opam-installer --yes - opaminstaller="$(opamrun var opam-installer:bin)/opam-installer" - # Copy the binary - install -d _opambin/bin - if [ -x "$opaminstaller.exe" ]; then - install "$opaminstaller.exe" _opambin/bin/opam-installer.exe - else - install "$opaminstaller" _opambin/bin/opam-installer - fi - # Remove all packages which just got installed (is there any easy way to do that?); - # technically we only need to remove those that should cross-compiled later. - # But for consistency between cache hit and cache miss, better to remove everything - # installed during `opam install opam-installer`. - opamrun remove cmdliner cppo ocamlgraph opam-file-format re seq stdlib-shims --yes - opamrun list - fi - opaminstaller=$projectrundir/_opambin/bin/opam-installer - - # Install ocamlfind since without it opam-installer can give errors trying - # to find the `ocamlfind` executable - opamrun install ocamlfind --yes - opamrun remove cmdliner seq --yes - - # Inject [dune-workspace] into almost all Opam packages - dunecontext='${{ matrix.crosscompile-dune-context }}' - option_args=$(printf 'pre-build-commands=["%s" "%s" "%s" "%s"]' \ - "$projectrundir/samples/crosscompiling-workspace-generator.sh" \ - '%{name}%' \ - '%{_:build}%/dune-workspace' \ - "$dunecontext" \ - ) - opamrun option "$option_args" - - # Add [dune-workspace] to dkml-c-probe (this package) - "$projectrundir/samples/crosscompiling-workspace-generator.sh" \ - "dkml-c-probe" \ - "dune-workspace" \ - "$dunecontext" - - # Each Opam package must install its cross-compiled libraries into Opam switch - option_args=$(printf 'post-install-commands=["%s" "%s" "%s" "%s" "%s" "%s" "%s" "%s" "%s"]' \ - "$projectrundir/samples/crosscompiling-opam-installer.sh" \ - "$opaminstaller" \ - "%{name}%-${{ matrix.crosscompile-toolchain }}.install" \ - "%{name}%" \ - "%{lib}%" \ - "%{man}%" \ - "%{prefix}%" \ - "%{stublibs}%" \ - "%{toplevel}%" \ - ) - opamrun option "$option_args" - - # Diagnostics: Show the options - opamrun option - - - name: Install Opam dependencies - run: opamrun install . --deps-only --with-test --yes - - name: Build OCaml - run: opamrun exec -- dune build --display=short - - name: Test OCaml - run: opamrun exec -- dune runtest --display=short - - name: Install dkml-c-probe - run: opamrun install . --yes - - name: Display probe of default toolchain - run: opamrun exec -- sh -c 'cat samples/show_abi.ml | ocaml' - - name: Display source for cross-compiled toolchain - if: matrix.crosscompile-toolchain + # `ocaml` on stdin exits 0 even on a toplevel error, so assert that a + # successful `Ok ` result was actually printed. run: | - prefix=$(opamrun var prefix) - opamrun exec -- cat "$prefix/${{ matrix.crosscompile-toolchain }}-sysroot/lib/dkml-c-probe/c_abi.ml" + opam exec -- sh -c 'cat samples/show_abi.ml | ocaml | tee abi.out; grep -qF "Ok Dkml_c_probe.C_abi.V4." abi.out' diff --git a/CHANGES.md b/CHANGES.md index 1b9b9f1..b9ba690 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,36 @@ -## 3.3.0 +## 4.0.0 + +* `V4` gains full musl parity on Linux: new ABIs `Linux_x86_musl` and + `Linux_arm64_musl` join `Linux_x86_64_musl`. The `` / + `__GLIBC__` test now covers the `__i386__` and `__aarch64__` branches of + the C header (new macros `DKML_ABI_linux_x86_musl` and + `DKML_ABI_linux_arm64_musl`). +* `V1`-`V3` report the new musl ABIs under their glibc names (`Linux_x86`, + `Linux_arm64`), matching the existing `Linux_x86_64_musl` -> `Linux_x86_64` + behavior. In `V1`, an x86 musl host gets the pre-existing `Result.error` + because `V1` never had `Linux_x86`. +* BREAKING (relative to the unpublished 3.3.0 git tag): the `V4.t_abi` + constructor list stays alphabetical, so the insertions change the ranks of + later constructors. Rebuild anything that marshalled `V4.t_abi` values. + 3.3.0 was tagged but never published to opam; use 4.0.0 instead. +* `C_conf.load ()` now uses `V4` (was `V3`): on musl hosts the ABI-specific + environment variable suffix is now e.g. `DEFAULT_LINUX_X86_64_MUSL` instead + of `DEFAULT_LINUX_X86_64`. glibc, Windows, and macOS hosts are unchanged. +* Versioning policy restored: interface module `V` ships in major version + `n` (`V4` <-> 4.0.0, as `V3` <-> 3.0.0 and `V2` <-> 2.0.0). +* Known gap: the Linux arm32 ABIs (`linux_arm32v6`, `linux_arm32v7`) have no + musl variants yet. +* CI: GitHub Actions re-enabled and every workflow brought onto the + repository's curated Actions allowlist (third-party actions SHA-pinned). + The `Tests` workflow now also covers musl: a C-header probe on Alpine + (musl) and Debian (glibc) across x86_64, x86 and arm64, and the full + dune-configurator path on a real musl host, alongside the BSD guests + (upgraded `cross-platform-actions`) and the Windows/macOS/Linux matrix on + `ocaml/setup-ocaml` (replacing the former DkML MSVC/MSYS2 + manylinux + reusable-workflow matrix, whose actions are not allowlisted). `syntax.yml` + upgraded to pinned `markdown-link-check` and `action-shellcheck`. + +## 3.3.0 (git tag only; never published to opam — superseded by 4.0.0) * Add `V4` with the new ABI `Linux_x86_64_musl`: a Linux x86_64 host whose C compiler targets a non-glibc libc (musl). Detection pulls in `` diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8fcc5c4..dc0f69a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -25,7 +25,13 @@ If you would like to add a new ABI, you will need to: * Add the ABI introspection test to the C header at [src/config/dkml_compiler_probe.h](src/config/dkml_compiler_probe.h). * Create a new versioned module (ex. `module V123`) of the ABI enumeration in [src/config/discover.ml](src/config/discover.ml). See how `module V2` extends `module V1` in a backwards-compatible way. -* Update the `(version xxx)` in [dune-project](dune-project) +* Extend `adjust_pre_v_abi` in [src/config/discover.ml](src/config/discover.ml) + so the older modules degrade the new ABI: to the closest pre-existing ABI if + older consumers always saw that value on such hosts (like the musl ABIs), or + to `Result.error` if the ABI is genuinely new to them (like the BSDs). +* Update the `(version xxx)` in [dune-project](dune-project). A new + `module V` must ship as major version `n.0.0`: the interface module + version matches the opam major version. Before submitting your PR make sure you have: 1. Run `dune build` diff --git a/README.md b/README.md index e208580..2cca521 100644 --- a/README.md +++ b/README.md @@ -195,9 +195,11 @@ module V4 : | Linux_arm32v6 | Linux_arm32v7 | Linux_arm64 + | Linux_arm64_musl | Linux_x86 | Linux_x86_64 | Linux_x86_64_musl + | Linux_x86_musl | NetBSD_x86_64 | OpenBSD_x86_64 | Windows_arm32 @@ -350,9 +352,24 @@ The header file will be available as the following expressions: # else # define DKML_OS_NAME "Linux" # define DKML_OS_Linux + /* musl does not define an identifying macro. Pull in + (glibc and uclibc define __GLIBC__ there; musl defines nothing) + and treat a hosted Linux without __GLIBC__ as musl. */ +# if defined(__has_include) +# if __has_include() +# include +# endif +# else +# include +# endif # if __aarch64__ -# define DKML_ABI "linux_arm64" -# define DKML_ABI_linux_arm64 +# if defined(__GLIBC__) +# define DKML_ABI "linux_arm64" +# define DKML_ABI_linux_arm64 +# else +# define DKML_ABI "linux_arm64_musl" +# define DKML_ABI_linux_arm64_musl +# endif /* __GLIBC__ */ # elif __arm__ # if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) # define DKML_ABI "linux_arm32v6" @@ -362,16 +379,6 @@ The header file will be available as the following expressions: # define DKML_ABI_linux_arm32v7 # endif /* __ARM_ARCH_6__ || ..., __ARM_ARCH_7__ || ... */ # elif __x86_64__ - /* musl does not define an identifying macro. Pull in - (glibc and uclibc define __GLIBC__ there; musl defines nothing) - and treat a hosted Linux x86_64 without __GLIBC__ as musl. */ -# if defined(__has_include) -# if __has_include() -# include -# endif -# else -# include -# endif # if defined(__GLIBC__) # define DKML_ABI "linux_x86_64" # define DKML_ABI_linux_x86_64 @@ -380,8 +387,13 @@ The header file will be available as the following expressions: # define DKML_ABI_linux_x86_64_musl # endif /* __GLIBC__ */ # elif __i386__ -# define DKML_ABI "linux_x86" -# define DKML_ABI_linux_x86 +# if defined(__GLIBC__) +# define DKML_ABI "linux_x86" +# define DKML_ABI_linux_x86 +# else +# define DKML_ABI "linux_x86_musl" +# define DKML_ABI_linux_x86_musl +# endif /* __GLIBC__ */ # elif defined(__ppc64__) || defined(__PPC64__) # define DKML_ABI "linux_ppc64" # define DKML_ABI_linux_ppc64 diff --git a/dkml-c-probe.opam b/dkml-c-probe.opam index c672d21..ec040f9 100644 --- a/dkml-c-probe.opam +++ b/dkml-c-probe.opam @@ -1,6 +1,6 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" -version: "3.3.0" +version: "4.0.0" synopsis: "Cross-compiler friendly ABI and library discovery for OCaml's native C compilers" description: """ @@ -32,8 +32,8 @@ build: [ ["dune" "build" "-p" name "-j" jobs "@doc"] {with-doc} ] # dkml-c-probe has strong backwards-compatibility. -# Any 3.x minor version is maintained for the latest patch version. +# Any 3.x or 4.x minor version is maintained for the latest patch version. # Please upgrade if there is a patch version for a minor release; # the patch generally means there was an important bug fix. -# Also the latest version is maintained, in case it gets to 4.x.x. -x-maintenance-intent: ["latest" "3.(any).(latest)"] +# Also the latest version is maintained. +x-maintenance-intent: ["latest" "4.(any).(latest)" "3.(any).(latest)"] diff --git a/dkml-c-probe.opam.template b/dkml-c-probe.opam.template index 87386ab..0b56a44 100644 --- a/dkml-c-probe.opam.template +++ b/dkml-c-probe.opam.template @@ -8,8 +8,8 @@ build: [ ["dune" "build" "-p" name "-j" jobs "@doc"] {with-doc} ] # dkml-c-probe has strong backwards-compatibility. -# Any 3.x minor version is maintained for the latest patch version. +# Any 3.x or 4.x minor version is maintained for the latest patch version. # Please upgrade if there is a patch version for a minor release; # the patch generally means there was an important bug fix. -# Also the latest version is maintained, in case it gets to 4.x.x. -x-maintenance-intent: ["latest" "3.(any).(latest)"] +# Also the latest version is maintained. +x-maintenance-intent: ["latest" "4.(any).(latest)" "3.(any).(latest)"] diff --git a/dune-project b/dune-project index 3db96c8..0d83b8d 100644 --- a/dune-project +++ b/dune-project @@ -2,7 +2,7 @@ (name dkml-c-probe) -(version 3.3.0) +(version 4.0.0) (using mdx 0.1) diff --git a/src/c_conf.ml b/src/c_conf.ml index bae74bd..8ce3db5 100644 --- a/src/c_conf.ml +++ b/src/c_conf.ml @@ -89,7 +89,7 @@ let load_from_dune_context_name ?(getenv = default_env_getter) ctxname = else create_error () let load ?(getenv = default_env_getter) () = - let abi_name_res = C_abi.V3.get_abi_name () in + let abi_name_res = C_abi.V4.get_abi_name () in match abi_name_res with | Ok abi_name -> load_from_findlib_toolchain ~getenv (Some abi_name) | Error msg -> Error msg diff --git a/src/config/discover.ml b/src/config/discover.ml index c8e0b79..227210a 100644 --- a/src/config/discover.ml +++ b/src/config/discover.ml @@ -17,21 +17,6 @@ open Configurator.V1 open Flags -type t_abi = - | Android_arm64v8a - | Android_arm32v7a - | Android_x86 - | Android_x86_64 - | Darwin_arm64 - | Darwin_x86_64 - | Linux_arm64 - | Linux_arm32v6 - | Linux_arm32v7 - | Linux_x86_64 - | Linux_x86 - | Windows_x86_64 - | Windows_x86 - type osinfo = { ostypename : (string, string) result; osname: (string, string) result; @@ -95,6 +80,8 @@ let get_osinfo t = (Result.ok "Unknown_unknown") | [ (_, String ("linux_arm64")) ] -> (Result.ok "Linux_arm64") + | [ (_, String ("linux_arm64_musl")) ] -> + (Result.ok "Linux_arm64_musl") | [ (_, String ("linux_arm32v6")) ] -> (Result.ok "Linux_arm32v6") | [ (_, String ("linux_arm32v7")) ] -> @@ -104,6 +91,8 @@ let get_osinfo t = | [ (_, String ("linux_x86_64_musl")) ] -> (Result.ok "Linux_x86_64_musl") | [ (_, String ("linux_x86")) ] -> (Result.ok "Linux_x86") + | [ (_, String ("linux_x86_musl")) ] -> + (Result.ok "Linux_x86_musl") | [ (_, String "linux_ppc64") ] -> (Result.ok "Unknown_unknown") | [ (_, String "linux_s390x") ] -> @@ -144,15 +133,21 @@ let result_to_quoted_string = function | Result.Error e -> "Result.error (\"" ^ String.escaped e ^ "\")" (* Unlike the pre-V2/pre-V3 adjusters which downgrade newer ABIs to - Result.error, the musl ABI degrades to plain Linux_x86_64 in V1-V3. A musl - host is a Linux x86_64 host, and V1-V3 consumers compiled before musl - detection existed always saw Linux_x86_64 on musl (the probe could not tell - the difference), so reporting Linux_x86_64 preserves their behavior instead - of turning working consumers into runtime errors. *) + Result.error, the musl ABIs degrade to their glibc counterparts in V1-V3. + A musl host is still the same Linux machine architecture, and V1-V3 + consumers compiled before musl detection existed always saw the glibc name + there (the probe could not tell the difference), so keeping the glibc name + preserves their behavior instead of turning working consumers into runtime + errors. In V1 the degraded Linux_x86 then hits adjust_pre_v2_abi's + Result.error, since V1 never had Linux_x86. *) let adjust_pre_v4_abi ~abitypename ~abiname = match (abitypename, abiname) with + | Result.Ok "Linux_arm64_musl", _ | _, Result.Ok "linux_arm64_musl" -> + (Result.ok "Linux_arm64", Result.ok "linux_arm64") | Result.Ok "Linux_x86_64_musl", _ | _, Result.Ok "linux_x86_64_musl" -> (Result.ok "Linux_x86_64", Result.ok "linux_x86_64") + | Result.Ok "Linux_x86_musl", _ | _, Result.Ok "linux_x86_musl" -> + (Result.ok "Linux_x86", Result.ok "linux_x86") | Result.Ok tn, Result.Ok n -> (Result.ok tn, Result.ok n) | Result.Error e, _ | _, Result.Error e -> (Result.error e, Result.error e) @@ -365,8 +360,9 @@ let () = (* V4. - Adds Linux_x86_64_musl: a Linux x86_64 host whose C compiler targets - a non-glibc libc (musl). V1-V3 report such a host as Linux_x86_64 + Adds the musl ABIs Linux_arm64_musl, Linux_x86_64_musl and + Linux_x86_musl: Linux hosts whose C compiler targets a non-glibc + libc (musl). V1-V3 report such hosts under the glibc ABI names (see adjust_pre_v4_abi). *) let lines = lines @@ -398,9 +394,11 @@ let () = | Linux_arm32v6 | Linux_arm32v7 | Linux_arm64 + | Linux_arm64_musl | Linux_x86 | Linux_x86_64 | Linux_x86_64_musl + | Linux_x86_musl | NetBSD_x86_64 | OpenBSD_x86_64 | Windows_arm32 diff --git a/src/config/dkml_compiler_probe.h b/src/config/dkml_compiler_probe.h index f5c737b..6098b97 100644 --- a/src/config/dkml_compiler_probe.h +++ b/src/config/dkml_compiler_probe.h @@ -94,9 +94,24 @@ # else # define DKML_OS_NAME "Linux" # define DKML_OS_Linux + /* musl does not define an identifying macro. Pull in + (glibc and uclibc define __GLIBC__ there; musl defines nothing) + and treat a hosted Linux without __GLIBC__ as musl. */ +# if defined(__has_include) +# if __has_include() +# include +# endif +# else +# include +# endif # if __aarch64__ -# define DKML_ABI "linux_arm64" -# define DKML_ABI_linux_arm64 +# if defined(__GLIBC__) +# define DKML_ABI "linux_arm64" +# define DKML_ABI_linux_arm64 +# else +# define DKML_ABI "linux_arm64_musl" +# define DKML_ABI_linux_arm64_musl +# endif /* __GLIBC__ */ # elif __arm__ # if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) # define DKML_ABI "linux_arm32v6" @@ -106,16 +121,6 @@ # define DKML_ABI_linux_arm32v7 # endif /* __ARM_ARCH_6__ || ..., __ARM_ARCH_7__ || ... */ # elif __x86_64__ - /* musl does not define an identifying macro. Pull in - (glibc and uclibc define __GLIBC__ there; musl defines nothing) - and treat a hosted Linux x86_64 without __GLIBC__ as musl. */ -# if defined(__has_include) -# if __has_include() -# include -# endif -# else -# include -# endif # if defined(__GLIBC__) # define DKML_ABI "linux_x86_64" # define DKML_ABI_linux_x86_64 @@ -124,8 +129,13 @@ # define DKML_ABI_linux_x86_64_musl # endif /* __GLIBC__ */ # elif __i386__ -# define DKML_ABI "linux_x86" -# define DKML_ABI_linux_x86 +# if defined(__GLIBC__) +# define DKML_ABI "linux_x86" +# define DKML_ABI_linux_x86 +# else +# define DKML_ABI "linux_x86_musl" +# define DKML_ABI_linux_x86_musl +# endif /* __GLIBC__ */ # elif defined(__ppc64__) || defined(__PPC64__) # define DKML_ABI "linux_ppc64" # define DKML_ABI_linux_ppc64