Repository navigation
feat(examples): add hello-odin, an Odin ABI v2 guest - #158
Merged
Merged
Conversation
The hello guest in Odin, an example only (#155): the c flavour's plumbing byte for byte (wit/, src/gen/, src/fnv1/, proto/, which the examples-share-the-scaffold-plumbing test now holds identical, and which the root Makefile's vendor-proto and gen-bindings regenerate there too), the generated nanopb structs declared again in Odin with their wasm32 layout held by #asserts, the codec and the c glue's wasi:http client called over Odin's foreign interface, and the bindings' exports_function_run defined in Odin. odin builds a freestanding wasm32 object, build.zig compiles the C half and links the core module through zig cc (not build-exe: Odin's runtime defines bzero with a return value, and wasm-ld's signature-mismatch warning fails build-exe), and guestfn build takes the project for a c one and wraps it with no adapter: a 71 KB component, compiled by the runtime in about 10 ms. Unit tests run natively with odin test over the C half compiled for the host; the xprin suite is the c example's.
A render (odin) job modelled on render (c): Odin from its GitHub release for linux amd64, pinned by version and sha256 (the binary is statically linked, its collections sit beside it), Zig as the c job installs it, the c job's codec and bindings drift checks over the example's copies, lint and the native tests, then make render-check with the prebuilt runtime, guestfn and xprin. The report job counts it.
Odin moves from the possible-untested list to the tested set on the other-languages page, with a paragraph on the example beside the C# one; the README names it as an example only; AGENTS.md gains the examples tree entry, the per-example test, lint and render commands, and the seventeen-copy count of the vendored proto; the language one-pager's 2026-10-07 paragraph records it (revision 0.6); the Renovate comments listing the examples name it.
3 tasks done
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.
Promotes the Odin spike (#155) to a real example:
examples/hello-odin, the hello guest in Odin as an ABI v2 component on thecflavour's plumbing, with arender (odin)e2e job and the docs. Per the 2026-10-07 decision, an example only: no scaffold,guestfn buildbuilds it through thecpath (itsbuild.zig).Part of #155.
What it is
wit/,src/gen/,src/fnv1/,proto/are thecflavour's byte for byte, andexamples_share_the_scaffold_plumbingnow holds them identical (hello-odinis a seventh entry in the list, underLANG_C, sharingproto/,wit/,src/fnv1/,src/gen/; its glue andbuild.zigare its own). The rootMakefile'svendor-proto-codecsandgen-bindingsregenerate the example's copies too.src/fnv1.odindeclares nanopb's generated structs again in Odin (request 152 B, response 144 B,Value16 B with a#raw_union, the streams 16 and 20 B) and holds every size and offset with#asserts against whatzig cccomputes for wasm32; the native layout is held by a unit test that sends a request through nanopb's decode, run and encode.src/wasmfn.odindefines the bindings'exports_function_run, the typedlog,http_get_textover thecglue'swasi:http@0.2client (src/wasmfn.c, kept as it is minus the export, codec and log) and an Odin allocator over wasi-libc's heap.src/main.odinis the function.build.zig:odin build src -target:freestanding_wasm32 -build-mode:obj -no-entry-point(the Odin runtime then imports nothing), the C half throughzig cc, the core module linked byzig cc(a wasip1 reactor with nowasi_snapshot_preview1imports, so no adapter),zig build test(odin testnatively over the C half compiled for the host, as objects: zig's archiver writes mach-o members Apple'sldrejects) andzig build lint(odin check -vet -strict-style).Makefile:build,test,lint,gen-proto,gen-bindings,render,render-check,clean.example/is thecexample's (both xprin cases).Measurements
core:fmtis left out on purpose: its reflection tables make the module 198 KB.odin buildof the guest 0.2 s (a 24 KB object). Coldguestfn build(odin, eightzig cccompiles, the link, the wrap; nanopb already fetched) 6.9 s, warm 0.2 s.FFI and allocator notes
foreign import x "name.o"yields plain link-time symbols (nowasm-import-module), and Odin never opens the file in an object build; a bare name orsystem:makes the procedures wasm imports of that module (the smoke module showedfunction.function_lognever binding to the C definition). A foreign variable (the message descriptors) must sit in a library-lessforeign {}block. Natively the same imports name the objectszig build testcompiles.@(require, linkage = "strong")on the export: nothing in Odin references it, and@(export)would add a second wasm export beside the world'srun.proc "c"assreton wasm32, matching clang, sopb_istream_from_bufferis called as declared.free(cabi_reallocisrealloc,cabi_post_runisfree), so the context's allocator wrapsmalloc/realloc/free/aligned_alloc: one heap, anything Odin allocates may be handed over. Aproc "c"sets its owncontext; the freestanding temp allocator is the nil allocator, aliased to the libc one. A panic traps without a message; no_startup_runtimeruns.The bzero workaround and its upstream fix
Odin's runtime (
base/runtime/procs.odin) definesmemcpy,memset,memmoveandbzerostrongly, withrequire, on every wasm target;bzeroreturnsrawptrwhere libc's returnsvoid. Zig's libc copies are weak and lose, butwasm-ldreports thebzeropair as a function signature mismatch, whichzig build-exeturns into a build failure ("unexpected LLD stderr").build.zigtherefore links through azig ccsystem command, which passes the warning through (exit 0; zig echoes it once on a cold build under a misleading "failed command:" label). The self-hosted zig linker is no alternative (it cannot readfunction_component_type.oand refuses Odin's object for bulk-memory). The upstream fix is avoidreturn on Odin'sbzero, after whichbuild.zigcan use the normaladdExecutablepath.Docs checklist
examples/hello-odin, ~71 KB, on thecflavour's bindings and codec); the flavour table lists scaffolds only and did not change.docs/guests/other-languages.md: "Tested on ABI v2" gains Odin, the "Possible, untested" Odin bullet is gone, and a paragraph beside the C# one describes the example (toolchain, size, build and compile times, theguestfn buildpath,core:fmt, thebzeroand silent-panic notes, native tests).docs/guests/write-a-module.md,docs/introduction.md,docs/use-cases.md: nothing needed (none lists or counts the hello examples; use-cases lists the use-case examples only).(cd examples/hello-odin && zig build test)in Testing,make lintin Linting, the Testing paragraph's toolchains and suite exemption (Odin beside C#), the plumbing sentence (hello-odin rides the c template's four directories), the proto copy count (seventeen: five templates, five goldens, seven examples), the WASI WIT and gen-bindings lists, the render command list. Key Reference Documents: nothing relevant.vendor-proto-codecsrunsgen-protoin hello-odin,gen-bindingsruns it there too (the c template is still mirrored from hello-c; the plumbing test holds hello-odin to it); the header comments' counts and lists updated.docs/one-pager-language-support.md: one sentence in the 2026-10-07 paragraph, revision 0.5 to 0.6.docs/one-pager-use-case-examples.md: nothing needed (it names no hello example)..github/renovate.json5: no rule change. The proto and WASI WIT trackers match the example's copies by file pattern; itsbuild.zig.zon(nanopb by URL and hash, the same pin as hello-c's) is read by no manager, so it moves by hand at release time like hello-c's and hello-zig's; the three comments listing the examples name it. The monthly examples batch (cargo, npm, gomod, nuget) has nothing of its to pick up.hello-zigas the model: every site checked (renovate comments, e2e.yml, AGENTS.md, Makefile, README, scaffold.rs, other-languages.md, the language one-pager).Verified locally
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test -p guestfn(the plumbing test with hello-odin in it) pass.make -C examples/hello-odin lint test build render-checkwith the release runtime andguestfn: lint clean, 5 native tests pass, the component builds, both xprin cases pass (hi example-xr,howdy example-xrthrough the host's egress).mdbook buildclean;actionlintreports nothing new (the local one predates theubuntu-26.04labels, and the SC2046 is the go job's).The
render (odin)job runs on the next/e2eafter the merge: the comment-triggered workflow runs frommain's file. Odin dev-2026-10 for linux amd64 is pinned by sha256c3c8b095621fd0c75f7f73e3a0829f1b4d45324225f20ba11ed8dc4da310a8ab; the release binary is statically linked and itsbase/andcore/collections sit beside it, so the job needs no LLVM.