Skip to content

chore!: upgrade to Zig 0.16.0#9

Merged
nazarhussain merged 22 commits into
ChainSafe:mainfrom
lodekeeper-z:chore/zig-master
Apr 28, 2026
Merged

chore!: upgrade to Zig 0.16.0#9
nazarhussain merged 22 commits into
ChainSafe:mainfrom
lodekeeper-z:chore/zig-master

Conversation

@lodekeeper-z
Copy link
Copy Markdown
Contributor

Upgrade to Zig 0.16.0-dev (master) — part of the lodestar-z Zig master upgrade effort.

Key changes:

  • std.iostd.Io migration
  • std.Io.Dir methods now require explicit io parameter
  • Build system changes for 0.16 compatibility

🤖 Generated with AI assistance

- Remove usingnamespace (removed in 0.15): c.zig now exports cImport as .c field
- All imports updated: @import("c.zig") → @import("c.zig").c
- callconv(.C) → callconv(.c) (lowercase)
- napi module requires link_libc = true for @cImport
- Example disabled (TODO: std.time.Timer/sleep moved to std.Io)

🤖 Generated with AI assistance
@wemeetagain wemeetagain marked this pull request as ready for review March 19, 2026 19:59
@wemeetagain wemeetagain marked this pull request as draft March 19, 2026 19:59
@wemeetagain
Copy link
Copy Markdown
Member

@lodekeeper-z please refresh this PR based on the latest main branch

…r callbacks)

Updates examples to match new wrap() signature from ChainSafe#10 (added ref parameter).

🤖 Generated with AI assistance
@lodekeeper-z
Copy link
Copy Markdown
Contributor Author

Refreshed against latest main — merged in #10 (Ref refactor), #8 (raw variant functions), and #5 (safer callbacks). Updated the type_tag and hello_world examples to match the new wrap() signature (added ref parameter).

Note: the type_tag example has a linker issue (undefined NAPI symbols) that's pre-existing from upstream — it was introduced when #7 and #10 were merged without updating the example's wrap() call site. Our branch now fixes that compilation error, but the example still can't link standalone (needs to run as a Node addon).

@nazarhussain
Copy link
Copy Markdown
Contributor

@lodekeeper-z please refresh this PR based on the latest main branch

@lodekeeper-z
Copy link
Copy Markdown
Contributor Author

Refreshed — merged latest main (napi→zapi rename, CI workflows, type_tag example). hello_world example is still TODO due to std.time.Timer/std.Io migration in 0.16.

1 similar comment
@lodekeeper-z
Copy link
Copy Markdown
Contributor Author

Refreshed — merged latest main (napi→zapi rename, CI workflows, type_tag example). hello_world example is still TODO due to std.time.Timer/std.Io migration in 0.16.

- Replace std.time.Timer with local Timer using std.c.clock_gettime
- Replace std.time.sleep with std.c.nanosleep
- Restore hello_world module, library, and test in build.zig
- Fix b.modules.put to use b.allocator (Zig 0.16 API change)
- Update minimum_zig_version to 0.16.0
- Update CI setup-env to use Zig 0.16.0
- Remove duplicate .zig-cache/ in .gitignore
Replace std.c.clock_gettime/nanosleep with std.Io.Timestamp and
std.Io.sleep, using Io.Threaded.init_single_threaded for the Io
instance in the NAPI callback context.
Remove module-level var and getIo() helper. Use the stdlib-provided
global_single_threaded instance directly.
Per Zig author recommendation, prefer explicit one-time initialization
over global_single_threaded for dynamically-loaded NAPI libraries.
The custom Timer struct was an unnecessary wrapper around Io.Timestamp.
Use Io.Timestamp directly in the NAPI callbacks.
@GrapeBaBa GrapeBaBa marked this pull request as ready for review April 17, 2026 05:00
Copilot AI review requested due to automatic review settings April 17, 2026 05:00
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Upgrades this Zig N-API wrapper to be compatible with Zig 0.16, including updates to C interop patterns, C ABI callconv annotations, and build/CI configuration. Also updates the hello_world example to use std.Io APIs in place of std.time.

Changes:

  • Switch C imports to @import("c.zig").c and update c.zig to expose pub const c = @cImport(...).
  • Replace callconv(.C) with callconv(.c) for C ABI callbacks.
  • Bump Zig toolchain requirements (build.zig.zon + GitHub Action) and adjust build script for Zig 0.16 API changes; update examples to std.Io.

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/value_types.zig Update C import access via .c.
src/threadsafe_function.zig Update C import access and C callconv to .c.
src/status.zig Update C import access via .c.
src/root.zig Re-export c as the @cImport result (.c).
src/module.zig Update C import access and C callconv to .c for exported registration function.
src/finalize_callback.zig Update C import access and finalize callback callconv to .c.
src/cleanup_hook.zig Update C import access and hook callback callconv to .c.
src/callback_info.zig Update C import access via .c.
src/callback.zig Update C import access and callback callconv to .c.
src/c.zig Change from usingnamespace @cImport to pub const c = @cImport(...).
src/async_work.zig Update C import access and async callbacks callconv to .c.
src/async_cleanup_hook.zig Update C import access and cleanup hook callconv to .c.
src/args.zig Update C import access via .c.
src/Value.zig Update C import access via .c.
src/Ref.zig Update C import access via .c.
src/NodeVersion.zig Update C import access via .c.
src/HandleScope.zig Update C import access via .c.
src/EscapableHandleScope.zig Update C import access via .c.
src/Env.zig Update C import access via .c.
src/Deferred.zig Update C import access via .c.
src/AsyncContext.zig Update C import access via .c.
examples/hello_world/mod.zig Migrate time usage to std.Io (Io.Timestamp, io().sleep).
build.zig.zon Bump .minimum_zig_version to 0.16.0.
build.zig Update build definitions for Zig 0.16 (modules map API + link_libc for zapi module).
.gitignore Ignore zig-pkg/ directory.
.github/actions/setup-env/action.yml Pin CI Zig version to 0.16.0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread build.zig.zon
Comment thread build.zig
Comment thread .github/actions/setup-env/action.yml
Comment thread examples/hello_world/mod.zig Outdated
Comment thread build.zig Outdated
@GrapeBaBa GrapeBaBa changed the title chore: upgrade to Zig 0.16.0-dev (master) chore: upgrade to Zig 0.16.0 Apr 17, 2026
Example modules reference NAPI symbols that are only available at
runtime via Node.js. Running them as standalone zig tests causes
linker errors. Keep them as individual test steps
(test:example_hello_world, test:example_type_tag) but exclude from
the global `zig build test`.
- Remove stale "generated by zbuild" comment from build.zig.zon
- Update zbuild.zon minimum_zig_version to 0.16.0
- Add link_libc to zapi module in zbuild.zon for consistency with build.zig
Each thread (main, libuv worker, spawned) gets its own init_single_threaded
instance, which is correct since init_single_threaded has no synchronization.
build.zig and build.zig.zon are generated by zbuild — keep the header
so manual edits don't get silently overwritten on regeneration.
zapi only uses @cImport for NAPI C headers (stddef.h, stdint.h) which
Zig provides natively. link_libc is only needed by the example dynamic
libraries that load into Node.js at runtime.
@GrapeBaBa GrapeBaBa force-pushed the chore/zig-master branch 9 times, most recently from 4ccc9d8 to fefb88e Compare April 21, 2026 05:06
GrapeBaBa added a commit to GrapeBaBa/state-transition-z that referenced this pull request Apr 21, 2026
Four of the five Zig-0.16-upgrade PRs on our dependency forks have
merged into ChainSafe upstream main:

  * ChainSafe/blst.zig#4  → 6ad139a
  * ChainSafe/hashtree-z#10 → 56e406f
  * ChainSafe/snappy.zig#5 → 7139424
  * ChainSafe/zig-yaml#1  → b74f4e8

Switch from `lodekeeper-z/<repo>?ref=chore/zig-master` to the merged
commit on `ChainSafe/<repo>` main for each. Content is identical so
the existing `hash` fields stay valid.

`zapi` (ChainSafe/zapi#9) is still open upstream — bump the
`lodekeeper-z/zapi` commit pointer to the current PR head
(fefb88ebf5c9) which includes the latest port/refactor work; TODO
note to switch to ChainSafe/zapi once ChainSafe#9 merges.
The merge with main brought in the js_dsl feature (ChainSafe#11) and a refactor
of `src/root.zig` (napi symbols moved to `src/napi.zig`, re-exported
via `pub usingnamespace`). All of that was written for 0.14.1 and hits
several 0.16 breaking changes:

* `b.modules.put(key, value)` → `b.modules.put(allocator, key, value)`.
  Two call sites in `build.zig` were missed by the previous update
  pass (the `napi` module and the `example_js_dsl` module).

* `src/napi.zig` exported `pub const c = @import("c.zig")`, but every
  internal file accesses napi types through the inner `.c` namespace.
  Restore the `.c` on the import.

* `pub usingnamespace` was removed in Zig 0.16. Replace it in
  `src/root.zig` with explicit `pub const foo = napi.foo;` re-exports.

* `callconv(.C)` → `callconv(.c)` (6 NAPI callback shims in
  `src/js/wrap_class.zig` and `src/js/wrap_function.zig`).

* `std.Thread.Mutex` → `std.Io.Mutex`. The DSL's per-class registry
  in `src/js/class_runtime.zig` needs a 0.16-compatible mutex, which
  requires an `Io`. The `Io` is threaded in through the DSL's entry
  point rather than letting zapi carry global state:

  - `js.exportModule` gains an optional `.io = fn () std.Io` field in
    `options`. The generated `moduleInit` calls it after the user's
    `options.init` hook and passes the result down into
    `registerDecls` → `class_runtime.registerClass`. If `.io` is
    omitted, a DSL-local `init_single_threaded` fallback keeps
    zero-config modules compiling.
  - `registerClass` caches the resolved `Io` in per-`T`
    `state(T).io`; `getConstructor`, `materializeClassInstance`, and
    `cleanupHook` all read from this cache because they execute
    inside napi C callbacks that can't receive user-provided `Io`.
  - Normal paths (`registerClass`, `getConstructor`) use cancelable
    `try mutex.lock(io)` so caller cancellation can propagate.
  - `cleanupHook` (a napi C callback with a fixed `(*Entry)` signature
    that cannot return errors) uses `lockUncancelable` deliberately —
    unwinding on cancel would leak the entry and hand napi a dangling
    pointer.

* `Env` gains a `fromRaw(raw_env)` constructor so the 19 napi C
  callback sites don't all hand-roll the `Env{ .env = ... }` literal.
  `Env` itself stays a pure `napi_env` wrapper — no `io` field.

After this commit `zig build` succeeds, and `zig build test` runs
the 42 regular tests green. `example_js_dsl` has a separate,
pre-existing link issue in `zig build test` (undefined `_napi_wrap`
and friends, resolved by Node at runtime when loading the `.node`
file) — orthogonal to the 0.16 migration.
@GrapeBaBa
Copy link
Copy Markdown
Contributor

@wemeetagain @nazarhussain @spiral-ladder need to check if current approach is good for injecting IO

Copy link
Copy Markdown
Contributor

@nazarhussain nazarhussain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the DSL point of view, let me rethink why we need io for a comptime layer. The only usage we have for class constructor list, might be avoided with some other pattern. I will get back to it.

Comment thread src/js/export_module.zig Outdated
Comment thread src/js/export_module.zig
Comment thread src/js/export_module.zig
@nazarhussain
Copy link
Copy Markdown
Contributor

nazarhussain commented Apr 22, 2026

@GrapeBaBa @wemeetagain With this PR merged we will not have io dependency in the DSL, so no change would be needed.
#20

wemeetagain pushed a commit to ChainSafe/lodestar-z that referenced this pull request Apr 24, 2026
## Motivation

Get the codebase cleanly compatible with **Zig 0.16.0** release,
following the new "explicit `io` everywhere, no hidden global state"
design — without changing runtime behavior.

## Description

- **`std.Io`** — File/Dir operations, `Mutex`/`Condition`/`Event`
(cancelable vs uncancelable split), entropy via
`io.random`/`io.randomSecure`, timestamps via `std.Io.Timestamp`
- **ArrayList unmanaged** — allocator threaded through
`deinit`/`append`/`resize`/`ensureCapacity`/`clone`; replace `items.len
= N` hacks with `resize` / `expandToCapacity` /
`appendNTimesAssumeCapacity` (preserves semantics, matches
`initCapacity`'s Precise allocation)
- **Struct builder** — `@Type(.@"struct" = ...)` → `@Struct(names,
types, attrs)` builtin in `ssz/type/container.zig`
- **Copy-elision workaround** — `std.Io` 0.16 result-location semantics
pre-write the union tag of the assignment target, so reads from
`self.state.init_group.*` during RHS evaluation would observe the new
variant. Snapshot pre-upgrade fields to locals in `state_transition.zig`
fork-upgrade chain and `era/Writer.zig`
- **ThreadPool** — `io` is a parameter on
`init`/`deinit`/`verifyMultipleAggregateSignatures`/`aggregateVerify`
(not a stored field); normal paths use cancelable `lock`; shutdown +
`submitAndWait.done.wait` stay uncancelable with comments explaining the
invariant
- **Shared `src/time.zig`** — `timestampNow` / `since (Duration)` /
`durationSeconds`, replacing four duplicated helper pairs across
`state_transition` and `bench`
- **IO signature convention** — `(allocator?, io, file, ...)` applied
consistently to `e2s.*` and `era.*`
- **Reader API polish** — `takeArray` (zero-copy) + `readSliceAll` where
appropriate; drop single-buffer `readVecAll` and `catch |err| return
err`
- **Other**: `RefCount` comptime-reflect on wrapped T's deinit arity
(1-arg vs 2-arg) so 0.16 unmanaged `ArrayList` and project types with
stored allocators both work; `sanitize_c = .trap` on blst artifact so
Debug fuzz builds link without `libubsan`; untrack 426 stale
`test/fuzz/zig-pkg/` entries from the git index
- **CI**: `ZIG_VERSION` bumped `0.14.1 → 0.16.0`; `zbuild` sync-check +
`build-examples` jobs temporarily commented out pending upstream Zig
0.16 support

## Dependency PRs

Each Zig 0.16 upgrade landed as a dedicated PR on the upstream repo.
`build.zig.zon` points at `lodekeeper-z` forks on the `chore/zig-master`
branch until these merge and cut releases:

| Dependency | Upstream PR |
|---|---|
| `blst` |
[ChainSafe/blst.zig#4](ChainSafe/blst.zig#4) |
| `hashtree` |
[ChainSafe/hashtree-z#10](ChainSafe/hashtree-z#10)
|
| `snappy` |
[ChainSafe/snappy.zig#5](ChainSafe/snappy.zig#5)
|
| `yaml` |
[ChainSafe/zig-yaml#1](ChainSafe/zig-yaml#1) |
| `zapi` | [ChainSafe/zapi#9](ChainSafe/zapi#9)
|
| `httpz` (disabled) |
[karlseguin/http.zig#191](karlseguin/http.zig#191)
|
Replace the auto-generated 175-line build.zig + the deleted zbuild.zon CLI
mode with the new zbuild library-mode pattern: a 6-line wrapper invoking
`zbuild.configureBuild` over a manifest-driven build.zig.zon.

build.zig:
  - shrunk from 175 lines to a wrapper that delegates to
    `zbuild.configureBuild(b, @import("build.zig.zon"), .{})`.
  - Post-processes the 3 example test artifacts to set
    `linker_allow_shlib_undefined = true` (zbuild exposes this option only
    for libraries, so we apply it post-hoc to tests that link against napi
    C symbols Node provides at dlopen time).

build.zig.zon:
  - Adds zbuild as a dependency (pinned to refactor/comptime-library-rewrite
    @ 17c389b — same pin as lodestar-z `main`).
  - Declares all modules / libraries / tests / options_modules in the
    manifest; zbuild materializes them at build time.
  - All build steps from the previous build.zig are preserved
    (`build-lib:*`, `test:*`, `build-test:*`, `test`).

Verified: `zig build` clean, `zig build test` passes 42/42, all 3
`.node` files install correctly, `zig fmt --check` clean.
Bump zbuild to 7ec852f, which adds `linker_allow_shlib_undefined`
support for `tests` entries (previously only allowed in `libraries`).

This lets the example tests declare the flag directly in the manifest
instead of post-processing the test artifacts in build.zig:

  build.zig: shrinks back to a 6-line wrapper (no post-hoc fixup loop).
  build.zig.zon: 3 example test entries each set
    `linker_allow_shlib_undefined = true`.
@nazarhussain
Copy link
Copy Markdown
Contributor

@lodekeeper-z Please resolve the conflicts.

@nazarhussain
Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

@nazarhussain nazarhussain changed the title chore: upgrade to Zig 0.16.0 chore!: upgrade to Zig 0.16.0 Apr 28, 2026
@nazarhussain
Copy link
Copy Markdown
Contributor

Discussed with @GrapeBaBa the change will make this library not compatible with project using old version of zig so we are considering this a breaking change for this library to bump it's major version.

Resolves conflicts from main's PR ChainSafe#20 (refactor: remove mutex/io
dependency from the dsl) against the Zig 0.16 migration on this branch:

- src/js/class_runtime.zig: take main's atomic spinlock; the
  std.Io.Mutex added during the 0.16 port is no longer needed.
- Drop the now-dead `io` plumbing from exportModule, registerDecls,
  and the js_dsl example — main's intent was to keep the DSL Io-free,
  so the option, the panic, and the helper are removed end-to-end.
- zbuild.zon: keep deleted (obsolete after the zbuild library-mode
  migration on this branch; release-please version info now lives in
  build.zig.zon).
@GrapeBaBa GrapeBaBa changed the title chore!: upgrade to Zig 0.16.0 chore: upgrade to Zig 0.16.0 Apr 28, 2026
@GrapeBaBa GrapeBaBa changed the title chore: upgrade to Zig 0.16.0 chore!: upgrade to Zig 0.16.0 Apr 28, 2026
@nazarhussain nazarhussain merged commit 8e80f1c into ChainSafe:main Apr 28, 2026
7 checks passed
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.

5 participants