GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Build for wasm32-wasip1 (WASI preview 1) by pankgeorg · Pull Request #7464 · libgit2/libgit2

Conversation

@pankgeorg

@pankgeorg pankgeorg commented Nov 6, 2026

Copy link
Copy Markdown

This lets libgit2 build for WebAssembly with wasi-sdk, as a static library for wasm32-wasip1. There are ten small commits, each guarded by __wasi__ (or by CMake's WASI system name), so other platforms are unchanged.

WASI preview 1 offers a POSIX-like filesystem through wasi-libc. It has no sockets, processes, users, mmap or threads. With these changes the local parts of libgit2 work under any WASI runtime (repositories, objects, refs, index, checkout, diff, the local transport). Network access works through a custom transport or stream (git_transport_register / git_stream_register) that the embedder provides.

Commits

  1. integer: type-generic overflow builtins on WebAssembly. On wasm32, size_t is unsigned long while SIZE_MAX == UINT_MAX. The SIZE_MAX tests therefore pick __builtin_uadd_overflow, and clang rejects every caller (-Wincompatible-pointer-types). __builtin_add_overflow/__builtin_mul_overflow are used there instead.
  2. streams: no default socket stream on WASI. The BSD socket code is compiled out. The default stream returns an error asking for a registered stream or transport, and a custom GIT_STREAM_STANDARD registration is still used.
  3. sysdir: no passwd database on WASI. No <pwd.h>/getpwuid_r/getuid. Home and XDG directories come from $HOME / $XDG_CONFIG_HOME only.
  4. process: no unix process API on WASI. There is no fork/exec. Only the exec-based SSH transport uses it, and SSH is not available on WASI.
  5. llhttp: no JavaScript callback imports on WASI. llhttp's api.c declares wasm_on_* imports for llhttp's own JS-hosted build, which leaves 8 undefined imports in any other wasm build that links it. (This one touches deps/llhttp. I'm happy to send it to llhttp as well, or to drop it if you'd rather carry it differently.)
  6. rand: no process or user ids in the fallback seed on WASI. The seed comes from getentropy, which WASI has and CMake already detects. The time-based fallback leaves out getpid/getppid/getpgid/getsid/getuid/getgid, which WASI doesn't have.
  7. fs_path: no file ownership on WASI. There are no users or geteuid, and stat reports no owner. WASI gets its own git_fs_path_owner_is that treats every existing file as the current user's, as on a single-user system, so the safe.directory checks pass.
  8. cmake: build for WASI. NO_MMAP for CMAKE_SYSTEM_NAME WASI, so packs are read into memory as on other mmap-less platforms.
  9. realpath: resolve paths below nested preopens on WASI. wasi-libc's realpath() calls readlink() on every prefix of the path. A prefix that is not inside a preopened directory fails, for example /a for the preopen /a/b. So with a preopen of more than one component, git_repository_init/open fail with "failed to resolve path". On WASI, such a path is resolved lexically and checked for existence instead. (The root cause is in wasi-libc; this keeps libgit2 usable with today's runtimes.)
  10. posix: readlink with room to spare on WASI. Node's WASI (uvwasi) fails readlink() with ENOBUFS when the target fills the buffer exactly, where POSIX truncates. libgit2 passes buffers of the target's length, so under Node every symlink in a work tree fails diffs and status ("failed to read symlink data ... No buffer space available"). On WASI, the target is read into a buffer one byte longer.

Building

With wasi-sdk (tested with wasi-sdk 34, clang 23):

cmake -S . -B build-wasi -G Ninja \
  -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK/share/cmake/wasi-sdk-p1.cmake \
  -DCMAKE_BUILD_TYPE=MinSizeRel -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTS=OFF -DBUILD_CLI=OFF \
  -DUSE_THREADS=OFF -DUSE_HTTPS=OFF -DUSE_SSH=OFF -DUSE_AUTH_NTLM=OFF -DUSE_AUTH_NEGOTIATE=OFF
cmake --build build-wasi

This produces libgit2.a with no warnings. Bundled zlib, PCRE, llhttp, xdiff and SHA1DC are used.

Testing

I don't add CI here, and I'd welcome guidance if you want a WASI job (the clar suite would need a WASI runner such as wasmtime or Node, plus a list of tests that need sockets or processes).

What I checked:

  • I linked the library, with a small wrapper, into a wasm32-wasip1 reactor module and ran it under Node's WASI, against git 2.43:
    • init, add and commit (git fsck --strict clean; the tree equals git write-tree);
    • opening repositories made by git (packed objects and refs, annotated tags);
    • revparse, tree entry by path, blob and raw tree content (byte-identical to git cat-file), ls-tree, refs;
    • diff of HEAD against the workdir with index;
    • git_checkout_tree from a bare repository into a target_directory (identical to git archive);
    • hard reset.
  • I registered a custom smart subtransport for https (stateless RPC, I/O through a host-provided fetch) and used it to clone, shallow clone (depth = 1), unshallow fetch and ls-remote against github.com. The refs were identical to native libgit2 and git fsck was clean.
  • with preopens of one and of several path components (/work, /a/b, a host path used as the guest path);
  • on this branch (main) and on v1.9.7 (the same commits, branch wasi-v1.9.7).

Known WASI limits that need no code here: there are no permission bits, so core.filemode is detected as false at init and the executable bit is not restored on checkout. There is no chmod.

The patches, tests and this description were drafted with an AI agent (Claude Code, Claude Opus 5.5) and reviewed before opening.

🤖 Generated with Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
pankgeorg added 10 commits November 5, 2026 21:39
@pankgeorg
integer: use the type-generic overflow builtins on WebAssembly
3b268ec
On wasm32 `size_t` is `unsigned long`, but `SIZE_MAX == UINT_MAX`, so the
`SIZE_MAX` tests select `__builtin_uadd_overflow`/`__builtin_umul_overflow`,
whose `unsigned int *` result type does not match a `size_t *`; clang rejects
every caller with -Wincompatible-pointer-types. Use the type-generic
`__builtin_add_overflow`/`__builtin_mul_overflow` there, which are correct for
any width.
@pankgeorg
streams: no default socket stream on WASI
8d27bdf
WASI preview 1 has no sockets (no `<netdb.h>`, `<sys/socket.h>` or
`<netinet/*>`). Build the socket stream there without the BSD socket code:
the default stream fails with an error that says to register a stream
(`git_stream_register`) or a transport (`git_transport_register`). A custom
`GIT_STREAM_STANDARD` registration is still honoured.
@pankgeorg
sysdir: no passwd database on WASI
9e13e1b
WASI has no users: no `<pwd.h>`, `getpwuid_r`, `getuid` or `geteuid`. Treat it
as a single-user system whose home and XDG directories come from `$HOME` and
`$XDG_CONFIG_HOME` only; the passwd lookup reports not found.
@pankgeorg
process: do not build the unix process API on WASI
cc3b2fd
WASI has no processes (no fork, exec, pipes or `<sys/wait.h>`). Only the
`exec` SSH transport uses `git_process`, and SSH is not available on WASI;
compile the unix implementation out there.
@pankgeorg
llhttp: no JavaScript callback imports on WASI
3a15d7d
llhttp's api.c declares `wasm_on_*` functions for its own build, which runs in
a JavaScript host that implements them. Any other WebAssembly build that links
llhttp then has eight undefined imports. Exclude them under `__wasi__`, where
llhttp is used as an ordinary C library.
@pankgeorg
rand: no process or user ids in the fallback seed on WASI
a31e059
WASI has no `getpid`, `getppid`, `getpgid`, `getsid`, `getuid` or `getgid`.
The seed comes from `getentropy` there (which WASI provides and CMake
detects); the time-based fallback leaves the ids out.
@pankgeorg
fs_path: no file ownership on WASI
06642e8
WASI has no users: no `geteuid`, and `stat` reports no owner. Give WASI its
own `git_fs_path_owner_is` that treats every existing file as the current
user's, so the ownership checks of `safe.directory` pass, as they do for a
single-user system.
@pankgeorg
cmake: build for WASI
8836029
Define NO_MMAP for `CMAKE_SYSTEM_NAME` WASI: WASI preview 1 has no `mmap`
(wasi-libc offers only an emulation behind `_WASI_EMULATED_MMAN`), so
packfiles and other mapped files are read into allocated memory, as on other
platforms without mmap.

With the preceding changes, libgit2 builds for wasm32-wasip1 with wasi-sdk's
toolchain file and

    cmake -DCMAKE_TOOLCHAIN_FILE=<wasi-sdk>/share/cmake/wasi-sdk-p1.cmake \
          -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTS=OFF -DBUILD_CLI=OFF \
          -DUSE_THREADS=OFF -DUSE_HTTPS=OFF -DUSE_SSH=OFF

Network access needs a custom transport or stream, since WASI preview 1 has
no sockets.
@pankgeorg
realpath: resolve paths below nested preopens on WASI
bc5c7dd
wasi-libc's realpath() calls readlink() on every prefix of the path, and a
prefix that is not inside a preopened directory fails: "/a" for the preopen
"/a/b". So with a preopen of more than one path component, nothing below it
resolves, and `git_repository_init`, `git_repository_open` and others fail
with "failed to resolve path".

When realpath() fails that way on WASI, resolve the path lexically (`.` and
`..`, relative to the working directory) and check that it exists. Symbolic
links are not resolved in that case.
@pankgeorg
posix: readlink with room to spare on WASI
ad73890
Node's WASI (uvwasi) fails readlink() with ENOBUFS when the link's target
fills the buffer exactly, where POSIX truncates it. libgit2 reads a symbolic
link into a buffer of its target's length (`st_size`), so on Node every
symlink in a work tree fails: diffs and status report "failed to read symlink
data ... No buffer space available".

On WASI, read into a buffer one byte longer and copy the target.
pankgeorg added a commit to pankgeorg/LibGit2_wasm_jll.jl that referenced this pull request Nov 6, 2026
@pankgeorg
README: the Yggdrasil recipe is JuliaPackaging/Yggdrasil#15481; libgi…
5b801aa
…t2's patches are libgit2/libgit2#7464

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@pankgeorg
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.