Skip to content

MNT: Bump to 0.16.0 (osx-arm64 + linux-aarch64 green)#101

Open
tdejager wants to merge 3 commits intoconda-forge:mainfrom
tdejager:mnt/upgrade-0.16.0
Open

MNT: Bump to 0.16.0 (osx-arm64 + linux-aarch64 green)#101
tdejager wants to merge 3 commits intoconda-forge:mainfrom
tdejager:mnt/upgrade-0.16.0

Conversation

@tdejager
Copy link
Copy Markdown
Contributor

Summary

Bumps zig to 0.16.0. Rebases the MacOS and Linux patch stacks against 0.16 internals and gets both osx-arm64 and linux-aarch64 building and testing cleanly end-to-end. Drops 3 patches that are obsolete in 0.16. Windows (non_unix/*) and ppc64le stacks are deliberately left un-rebased in this PR and those targets will fail CI until a follow-up.

Toolchain / environment

  • Source URL moved to codeberg (upstream repo migrated; GitHub tag for 0.16.0 does not exist).
  • LLVM 20 → 21 everywhere (0.16 hard-requires 21.x at CMake configure time).
  • Clang/clangxx 20 → 21 on osx.
  • linux-aarch64 c_stdlib_version 2.17 → 2.28 — zig 0.16's stage2 C transpile emits getrandom / copy_file_range / statx, none of which are in glibc 2.17. Other linux variants will likely need the same bump when they're rebased.
  • Added aarch64-conda-linux-gnu to build_triplet. Native linux-aarch64 builds were previously unreachable (only the linux-64-cross CI path was wired up).
  • On osx-arm64, a second source entry ships an upstream pre-built zig-0.15.2 tarball as an offline bootstrap. The published conda-forge zig_impl_osx-arm64 0.15.2 pins libcxx 20.* at runtime, which is mutually exclusive with the libcxx 21 compiler stack 0.16 requires. build.sh symlinks the extracted binary into PATH as ${CONDA_ZIG_BUILD}.

Build path

  • CMAKE_FALLBACK: 0 → 1. The 0.15.2 bootstrap can't parse 0.16's build.zig (new fields use_new_linker, graph.io), so we invoke zig's own stage1 → zig2 → stage3 CMake bootstrap instead. Bootstrap zig is still on PATH so the CMake zigcpp step finds it, but the zig-build-with-zig attempt fails-over to the CMake path.
  • cmake_install_dir → \${PREFIX}. Zig's upstream cmake/install.cmake bakes CMAKE_INSTALL_PREFIX into the generated install script via install(CODE ...), so cmake --install . --prefix X is silently ignored. We configure with the real prefix up front.
  • Fixed a set -e hazard: is_debug && echo \"SUCCESS...\" as the final statement of cmake_fallback_build's success branch returned non-zero in non-debug mode, tripping set -e in the caller between cmake-install and the mv zig triplet-zig rename. Trailing || true now forces a 0 exit on the success path. Same treatment for symmetric sites in build.sh and apply_cmake_patches. Previously this was masked by leaving DEBUG_ZIG_BUILD=1 on.

Patches — common

Cleanly re-split so the two Lld patches aren't implicitly coupled:

  • Lld.zig-macho-lld-support — rebased. Only MachO LLD plumbing: MachO struct, machoLink function with static libcxx linking, ZigLLDLinkMachO binding, target.zig hasLldSupport. No libcxx_shared references.
  • Lld.zig-prefer-shared-libcxx — rebased. Owns every shared-libcxx change: the new src/link/libcxx_shared.zig file plus hunks in src/link/Lld.zig (COFF/ELF/WASM/machoLink) and src/link/MachO.zig. Must apply after macho-lld-support since it patches inside machoLink; the recipe order already reflects this.
  • main.zig-fuse-ld-lld-cc-path — rebased. Added ld64.lld to the early lldMain dispatch and to the cmdMain LinkMachO dispatch; .other cc-path handler intercepts -fuse-ld=lld and pass-through of unrecognized linker args as -Wl,....
  • Config.zig-macho-no-auto-lld, llvm.zig-lld-ofmt-macho — line-shift rebases, logic unchanged.
  • zig_llvm.cpp-mingw-def-dll-suffix.patchdropped permanently. 0.16 rewrote MinGW .def parsing in pure Zig (src/libs/mingw/def.zig) and already appends .dll to LIBRARY names without extensions.

The big API migration across the rebased Lld patches:

  • std.fs.Dir.copyFile(src, s, dst, d, .{})Io.Dir.copyFile(..., io, .{})
  • std.fs.cwd().access(path, .{})Io.Dir.cwd().access(io, path, .{})
  • libcxx_shared.zig takes io from comp.io.

Patches — linux

  • build.zig-01-maxrss, build.zig-02-doctest-forward-target — rebased (line shifts + one missed else-if hunk restored).
  • llvm.zig-triple-no-glibc-version — rebased.
  • link.zig-01-ldscript-relpath-and-l-flags — rebased, including migration to 0.16's Io.Dir API for directory iteration (openDir(io, ...), close(io), iter.next(io)).
  • link.zig-02-default-allow-so-scripts — rebased; regenerated against the 01-applied baseline so the 3-patch stack applies correctly.
  • link.zig-03-ldscript-sysroot-remap — rebased; preserves 0.16's arena.dupe + mutex.lockUncancelable(io) pattern on the no-sysroot path, falls back to it when base.comp.sysroot is not set.
  • posix.zig-glibc-2.17-fstat.patchdropped. std.posix.fstat was removed entirely in 0.16; no callers remain anywhere in the stdlib or compiler.
  • Object.zig-elf-zstd-decompression.patchdropped. Upstream 0.16 already has the .ZSTD branch (link(ELF): add ZSTD decompression support for compressed sections ziglang/zig#25107 merged).
  • Elf.zig-debug-tag-bare-TODO-panics.patch — dropped. Diagnostic-only tagging from 0.15 debugging, not needed.

Recipe tests

  • Re-enabled the osx -fuse-ld=lld -Wl,-exported_symbols_list test — now passes with rebased Lld patches.
  • Broadened package_contents glob for lib/zig/c/ (0.16 added ctype, fcntl, malloc, math, stdlib/*.zig, sys/*.zig, etc.).
  • Disabled the doc/langref.html check across all platforms for now. 0.16's stage2 hardcodes -Dno-langref (the CMake-driven bootstrap path we rely on) because @cImport is unavailable there; a proper post-install zig build langref step using stage3 is follow-up work.

Local validation

Both green end to end:

  • osx-arm64 (native on M1): all 4 packages produced, 63/63 tests pass (0 failed, 8 skipped intentionally), including the rebased -fuse-ld=lld macOS/Mach-O LLD test.
  • linux-aarch64 (OrbStack ubuntu:questing arm64 VM, same tree over 9P): all 4 packages produced, 62 activation tests pass, the -fuse-ld=lld --dynamic-list Linux/ELF test passes, and a chunk of zig's own behavior suite runs (skips are arch/emulation-related).

Known follow-ups not in this PR

  • Windows (non_unix/*) patches still on 0.15.2 — will fail.
  • ppc64le (11 arch-enablement patches against ELF internals) — will fail.
  • linux-64 / linux-riscv64 / linux-s390x / osx-64 / win-arm64 variants have not been attempted locally.
  • c_stdlib_version bump in this PR is only linux-aarch64; other linux variants probably need the same.
  • Wire up a post-install zig build langref step using stage3 so doc/langref.html can be re-enabled in package_contents.

Test plan

  • CI runs osx-arm64
  • CI runs linux-aarch64
  • CI runs linux-64, osx-64, win-64 (expected to fail until their patch stacks are rebased)
  • Someone with a linux-ppc64le / win-arm64 / linux-s390x environment picks up the remaining patch rebases

🤖 Generated with Claude Code

Source:
- Version 0.15.2 → 0.16.0, build 20 → 0. Upstream moved to codeberg
  (GitHub mirror retired its tags); URL and sha256 updated accordingly.
- New second source: upstream pre-built zig-aarch64-macos-0.15.2 tarball
  on osx-arm64, used as an offline bootstrap. The published conda-forge
  zig_impl 0.15.2 pins libcxx 20.* at runtime and can't coexist with the
  libcxx 21 compiler stack required by 0.16, so we sidestep the solver
  clash by shipping the upstream binary.

Toolchain:
- LLVM 20 → 21 (0.16 hard-requires 21.x at CMake configure).
- Clang/clangxx 20 → 21 on osx.
- linux-aarch64 c_stdlib_version 2.17 → 2.28 — zig 0.16 emits
  getrandom / copy_file_range / statx in the stage2 C transpile, none
  of which are in glibc 2.17. Other linux variants likely need the same
  bump before their patches are rebased.
- Added aarch64-conda-linux-gnu to build_triplet so native
  linux-aarch64 builds work (previously only reachable via the
  linux-64 cross-compiler path used by CI).

Build:
- CMAKE_FALLBACK 0 → 1. 0.15.2 bootstrap can't parse 0.16's build.zig
  (new fields use_new_linker, graph.io), so invoke zig's own
  stage1 → zig2 → stage3 CMake bootstrap instead.
- cmake_install_dir → ${PREFIX}. Zig's upstream cmake/install.cmake
  bakes CMAKE_INSTALL_PREFIX into the generated script via
  install(CODE ...), so `cmake --install . --prefix X` is silently
  ignored. Configure with the real prefix up front.

build.sh / _cmake.sh:
- On osx-arm64, symlink the upstream bootstrap binary into a PATH dir
  as CONDA_ZIG_BUILD before invoking the build.
- Fix a `set -e` hazard: `is_debug && echo "SUCCESS..."` as the final
  statement of `cmake_fallback_build`'s success branch returned non-zero
  in non-debug mode, which tripped set -e in the caller between
  cmake-install and the `mv zig triplet-zig` rename. Trailing `|| true`
  forces a 0 exit on the success path. Same treatment for the symmetric
  site in build.sh and apply_cmake_patches.

Patches (common):
- Lld.zig-macho-lld-support: rebased against 0.16. Cleanly scoped —
  only MachO LLD plumbing (MachO struct, machoLink function that links
  against static libcxx, ZigLLDLinkMachO binding, hasLldSupport
  update). No shared-libcxx references.
- Lld.zig-prefer-shared-libcxx: rebased. Owns every shared-libcxx
  change — the new src/link/libcxx_shared.zig file plus hunks in
  src/link/Lld.zig (COFF/ELF/WASM/machoLink) and src/link/MachO.zig.
  Must apply after macho-lld-support since it patches inside machoLink.
- main.zig-fuse-ld-lld-cc-path: rebased. Added `ld64.lld` to the early
  lldMain dispatch and the cmdMain LinkMachO dispatch; intercept
  -fuse-ld=lld in the .other cc-path handler and pass unrecognized
  linker args through as -Wl,... when LLD was requested.
- Config.zig-macho-no-auto-lld, llvm.zig-lld-ofmt-macho: line-shift
  rebases, logic unchanged.
- Migrated std.fs.* calls to 0.16's Io API: std.fs.Dir.copyFile(...) →
  Io.Dir.copyFile(..., io, ...); std.fs.cwd().access(path, ...) →
  Io.Dir.cwd().access(io, path, ...). libcxx_shared.zig takes io from
  comp.io.
- zig_llvm.cpp-mingw-def-dll-suffix.patch: dropped permanently. 0.16
  rewrote MinGW .def parsing in pure Zig (src/libs/mingw/def.zig) and
  already appends `.dll` to LIBRARY names without extensions.

Patches (linux):
- build.zig-01-maxrss, build.zig-02-doctest-forward-target: rebased.
- llvm.zig-triple-no-glibc-version: rebased.
- link.zig-01-ldscript-relpath-and-l-flags: rebased, including
  migration to 0.16's Io.Dir API for directory iteration.
- link.zig-02-default-allow-so-scripts: rebased.
- link.zig-03-ldscript-sysroot-remap: rebased against the 01-applied
  baseline; preserves 0.16's arena/mutex pattern on the no-sysroot path.
- posix.zig-glibc-2.17-fstat.patch: dropped. std.posix.fstat was
  removed entirely in 0.16; no callers remain.
- Object.zig-elf-zstd-decompression.patch: dropped. Upstream 0.16
  already has the .ZSTD branch (ziglang/zig#25107 merged).
- Elf.zig-debug-tag-bare-TODO-panics.patch: dropped. Diagnostic-only
  tagging from 0.15 debugging, not needed for the upgrade.

Recipe:
- Re-enabled osx -fuse-ld=lld with -exported_symbols_list test (now
  passes with rebased Lld.zig patches).
- Broadened package_contents glob for lib/zig/c/ (0.16 added ctype,
  fcntl, malloc, math, stdlib/*.zig, sys/*.zig, etc.).
- Skipped the doc/langref.html check across all platforms for now.
  0.16's stage2 hardcodes -Dno-langref (the CMake-driven bootstrap
  path we rely on) because @cImport is unavailable there; a proper
  post-install `zig build langref` step using stage3 is follow-up work.

Not done in this PR:
- Windows (non_unix/*) and ppc64le (linux/x86_64 arch-enablement)
  patch stacks are still unrebased — builds for those targets will
  fail until someone follows up. osx-64, linux-64 (x86_64) have not
  been attempted locally but should behave like their arm64 peers
  modulo sysroot version.
@conda-forge-admin
Copy link
Copy Markdown
Contributor

Hi! This is the friendly automated conda-forge-linting service.

I just wanted to let you know that I linted all conda-recipes in your PR (recipe/recipe.yaml) and found it was in an excellent condition.

@MementoRC
Copy link
Copy Markdown
Contributor

Cool, I did look into the 2.17 and think there is a path, got linux-64 passing locally, I'll stop working on it and review this PR, add what I found. I directed my 0.15.2 work toward a new branch, this way we don't mix-up main

@MementoRC
Copy link
Copy Markdown
Contributor

Hey @tdejager, great to see you working on 0.16.0! I've been doing parallel work on the same bump and hit a few issues that might save you time, especially for linux-64. I put my findings in a draft PR for reference: #102

Here are the key things I found:

1. CMAKE_FALLBACK=1 is required for the bootstrap

Zig 0.16.0's build.zig uses APIs (use_new_linker, b.graph.io) that are incompatible with the 0.15.2 bootstrap compiler. The zig build step fails, and the cmake fallback path (compiling zig2.c with the host C compiler) is needed. I set CMAKE_FALLBACK: "1" in the recipe env vars.

2. glibc 2.17 syscall stubs for zig2.c linking

The pre-generated zig2.c calls getrandom (glibc 2.25), copy_file_range (glibc 2.27), and statx (glibc 2.28) — all absent from conda-forge's glibc 2.17 sysroot. This causes undefined reference linker errors on linux-64.

Fix: recipe/building/_glibc217_syscall_stubs.sh — C stubs using syscall() with weak symbols, compiled to .o and linked into zig2 via both config.h injection (zig build path) and the CMakeLists.txt patch (cmake fallback path).

3. Drop Threaded.zig-glibc-2.17-stat-fallback.patch

This patch from 0.15.x uses linux.E.init(rc) which doesn't exist on 0.16.0's raw enum(u16) E type. More importantly, the patch isn't needed: when building with -Dtarget=x86_64-linux-gnu.2.17, zig 0.16.0 correctly sets statx_use_c=false and uses raw kernel syscalls (which work on modern CI kernels ≥ 4.11).

4. ppc64le + Windows patches regenerated

I also regenerated the ppc64le and non_unix patch stacks for 0.16.0 in #102 if those are useful for the follow-up.


Happy to coordinate — let me know if you'd prefer I push changes to your branch or if cherry-picking from #102 works better.

@tdejager
Copy link
Copy Markdown
Contributor Author

Sounds good, note that I did let Claude loose mostly. Although I try to be critical to what it does.

I used osx-arm64 for local builds and a vm for linux-arm as well.

I guess the cmake fallback is something we can turn off once we have a new version bootstrapped. Not totally sure why an old version cannot be used instead though.

@tdejager
Copy link
Copy Markdown
Contributor Author

Oh we posted at the same time, feel free to push anything you want on this. You answered 2 of my questions there :)

@tdejager
Copy link
Copy Markdown
Contributor Author

Oh btw I see Claude added the MNRT prefix. Not sure if that means memento 😅, sorry don't mean to appropriate that one :)

@MementoRC
Copy link
Copy Markdown
Contributor

@tdejager Right, I think is it intentional, from discussion with Andrew in the past he had recommended the CMake as the foolproof way of building ZIG. I think it stems from them providing bootstrap versions and have the flexibility to break version backward compatibility when needed.

The CMAKE_FALLBACK little thingy defaults to zig building zig anyway,

Do you know whether there was a change in rattler-build, all my PRs suddenly stop to build anything

@MementoRC
Copy link
Copy Markdown
Contributor

MementoRC commented Apr 22, 2026

@tdejager MNRT? I would rather let you continue with this PR, I posted my version just for you to have the reference code or commits - I am still struggling with the 0.15.2 libc++ dynamic library

Dropping off for a while, I'll check back later on

@tdejager
Copy link
Copy Markdown
Contributor Author

Ah always from the phone it corrects into something silly I meant the MNT prefix.

Regarding rattler-build there has been a big release yesterday I believe. What are you encountering?

And sure I'll reference the other PR.

…nux CI

CI on PR conda-forge#101 revealed two cross-cutting failures that weren't visible
locally:

1. The recipe's `script: { env: {...} }` block (with no `file:`) is
   treated as an empty inline script by rattler-build 0.63+, so
   `recipe/build.sh` never runs on CI. All macOS jobs and the native
   linux-aarch64 Azure job produced packages containing only test
   files and no compiled zig. Fix: explicit `file: build.sh` on the
   zig_impl script block.

2. The ppc64le patch stack was gated on
   `linux and (x86_64 or ppc64le)`, which means a conda-forge
   linux-64 runner cross-compiling to linux-aarch64 or linux-riscv64
   still applies the ppc64le patches — and a single un-rebased
   `0001-arch-support-Elf.zig.patch` hunk kills every linux variant
   before its own patches get a chance. Tighten the gate to
   `cross_target_platform_ == "linux-ppc64le"` so only actual
   ppc64le builds are affected. linux-ppc64le will still fail
   patch-apply until someone rebases that stack (see conda-forge#102 for
   regenerated versions).

3. non_unix patches are commented out for the same reason — the
   first one (build.zig-maxrss-search-prefix) fails to apply on
   0.16 before any Windows compilation starts. Unblocks Windows CI
   to reach the build step (which will then surface further work).

4. Adopt the glibc 2.17 syscall stubs from conda-forge#102 (replaces my earlier
   linux-aarch64 sysroot bump 2.17 → 2.28):

   - New helper recipe/building/_glibc217_syscall_stubs.sh compiles
     weak-symbol stubs for getrandom / copy_file_range / statx using
     raw syscall() numbers.
   - build.sh invokes the helper after configure_cmake_zigcpp and
     appends the .o to ZIG_LLVM_LIBRARIES so zig-build path picks it
     up.
   - recipe/patches/cmake/0001-linux-maxrss-CMakeLists.txt.patch and
     0002-linux-pthread-atfork-stub-zig2-CMakeLists.txt.patch both
     rebased against 0.16's CMakeLists.txt; 0002 extended to also
     link glibc217_syscall_stubs.o for the CMake fallback path.
   - linux-aarch64 variant reverted to c_stdlib_version: '2.17'.

Validated locally: linux-aarch64 v6 build with glibc 2.17 baseline +
stubs produces all 4 packages, 62/62 activation tests pass, behavior
test suite runs, -fuse-ld=lld with --dynamic-list (Linux/ELF) passes.

Still-untouched in this PR: Windows non_unix patches, ppc64le patches,
osx-64 cross failure on shim zig_impl_osx-64 dep resolution (needs
both impl outputs in same channel before shim solves).
@tdejager
Copy link
Copy Markdown
Contributor Author

tdejager commented Apr 22, 2026

Claude says:

Thanks @MementoRC — big timesaver. I've adopted the key pieces from #102 in 449744c:

  • glibc 2.17 syscall stubs: cherry-picked your _glibc217_syscall_stubs.sh helper and the updated cmake/0001 and cmake/0002 patches. build.sh now compiles the stubs after configure_cmake_zigcpp and appends the .o to ZIG_LLVM_LIBRARIES; cmake/0002 handles the CMake-fallback path. Reverted my linux-aarch64 c_stdlib_version: 2.17 → 2.28 bump — keeps the conda-forge 2.17 baseline intact. Validated locally in OrbStack: linux-aarch64 builds end-to-end with glibc 2.17 + stubs, 62/62 activation tests pass, -fuse-ld=lld --dynamic-list (Linux/ELF) passes.

Plus two CI-only fixes I caught via cf-job-logs:

  • build.sh never ran on every macOS job and the Azure linux-aarch64 job. rattler-build 0.63+ treats script: { env: {...} } (no file: or content:) as an empty inline script, whereas 0.62 fell back to recipe/build.sh. CI is on 0.63; this is why lib/zig/* was missing from every osx package. Fix: explicit file: build.sh.

  • ppc64le patches were killing every linux variant. The gate was linux and (x86_64 or ppc64le), so a conda-forge linux-64 runner cross-compiling to linux-aarch64 / linux-riscv64 still applied the un-rebased ppc64le stack and died on ppc64le/0001-arch-support-Elf.zig.patch. Tightened to cross_target_platform_ == \"linux-ppc64le\". Commented out the non_unix/* block for the same reason so Windows CI at least reaches the build step.

Still untouched here, cleanly pointable at #102:

  • non_unix/* rebased patches (your Windows rebases)
  • ppc64le/* rebased patches (the 5 you kept + the 2 upstream-fixed drops)
  • doctest-forward-target deferral

Will try and cherry-pick the rest.

CI run on commit 449744c surfaced `expected LLVM 21.x but found 20.1.8`
from cmake/Findllvm.cmake on every variant I hadn't already bumped.
Previously I had only updated osx-arm64 and linux-aarch64 variant
yamls; the other 10 files still carried the 0.15.2-era pin. Bumping
all of them (linux-64, linux-aarch64-cross, linux-ppc64le,
linux-riscv64, linux-ppc64le-native, osx-64, osx-64-cross, osx-arm64-cross,
win-64, win-arm64) plus matching c_compiler_version / cxx_compiler_version
on the osx ones.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants