Skip to content

c-api: install headers from build.rs without requiring cmake - #14144

Open
Isekai923 wants to merge 1 commit into
bytecodealliance:mainfrom
Isekai923:c-api-header-install-without-cmake
Open

c-api: install headers from build.rs without requiring cmake#14144
Isekai923 wants to merge 1 commit into
bytecodealliance:mainfrom
Isekai923:c-api-header-install-without-cmake

Conversation

@Isekai923

Copy link
Copy Markdown

Currently wasmtime-c-api-impl's build script shells out to cmake -P cmake/install-headers.cmake to produce the C API headers in OUT_DIR. cmake is only used as a scripting engine there — it substitutes the #cmakedefine lines in conf.h.in and copies the .h/.hh files — but this makes cmake a build requirement for every crate that transitively depends on wasmtime-c-api-impl, most notably anything using tree-sitter's wasm feature, even though nothing is actually compiled with cmake.

This regularly surprises downstream users because the failure only appears when the build-script fingerprint is invalidated ("it built yesterday"), and the error (failed to spawn 'cmake') doesn't look related to their code. See e.g. zed-industries/zed#18084; we hit the same thing in our project when a dev machine lost its cmake install.

What this PR does

Reimplements the header install directly in build.rs using std only (~60 lines):

  • wasmtime/conf.h is generated from conf.h.in by turning each #cmakedefine WASMTIME_FEATURE_X line into #define ... / /* #undef ... */ based on the corresponding CARGO_FEATURE_* env var, with CRLF newlines matching cmake's NEWLINE_STYLE CRLF. The feature list is read from the template itself, so build.rs no longer needs its own copy of the WASMTIME_FEATURE_LIST.
  • Headers are copied recursively, preserving directory structure — the equivalent of file(INSTALL ... FILES_MATCHING REGEX "\.hh?$").

The cmake scripts themselves are untouched and still used by the standalone CMake build of the C API; build.rs simply no longer invokes cmake.

Verification

  • The OUT_DIR/include tree is byte-for-byte identical (diff -r) to the cmake-generated one, tested in both directions: all features off, and with gc-drc/cranelift/wasi/wat enabled (covering #define and /* #undef */ paths, CRLF endings, and the recursive copy).
  • cargo check -p wasmtime-c-api-impl succeeds with cmake removed from PATH.

The build script previously shelled out to `cmake -P
cmake/install-headers.cmake` to produce the C API headers in OUT_DIR.
cmake was only being used as a scripting engine there: it substitutes
the #cmakedefine lines in conf.h.in and copies the .h/.hh files. This
made cmake a build requirement for every crate that transitively
depends on wasmtime-c-api-impl (e.g. anything using tree-sitter's
`wasm` feature), even though nothing is compiled with it.

Reimplement the header install directly in build.rs with std only:

- conf.h is generated from conf.h.in by turning each
  `#cmakedefine WASMTIME_FEATURE_X` line into `#define ...` or
  `/* #undef ... */` based on the corresponding CARGO_FEATURE_* env
  var, with CRLF newlines to match cmake's NEWLINE_STYLE CRLF. The
  feature list is read from the template itself, so build.rs no longer
  needs its own copy of WASMTIME_FEATURE_LIST.
- headers are copied recursively, matching
  file(INSTALL ... FILES_MATCHING REGEX "\.hh?$").

The cmake scripts are untouched and still used by the standalone CMake
build; build.rs simply no longer invokes cmake.

Verified that the OUT_DIR include tree is byte-for-byte identical to
the cmake-generated one (all-features-off and a cranelift/gc-drc/wasi/
wat set), and that `cargo check -p wasmtime-c-api-impl` succeeds with
cmake removed from PATH.
@Isekai923
Isekai923 requested a review from a team as a code owner August 16, 2026 23:44
@Isekai923
Isekai923 requested review from pchickey and removed request for a team August 16, 2026 23:44
@github-actions github-actions Bot added the wasmtime:c-api Issues pertaining to the C API. label Aug 17, 2026
@pchickey
pchickey requested review from alexcrichton and removed request for pchickey August 17, 2026 17:24
@pchickey

Copy link
Copy Markdown
Contributor

I'm fine with this change in principle but I don't know why cmake was originally used here, so I'm passing this to Alex.

@alexcrichton

Copy link
Copy Markdown
Member

Thanks for the PR, and while I've no doubt an LLM can reproduce CMake's configure_file logic the original purpose here was to avoid duplicating creation of header files across build systems. I understand the motivation to reduce dependencies, but from a maintenance perspective I'd prefer to not have two ways of doing the same thing with no comments indicating that they need to be kept in sync.

One strategy we've taken in the past for situations like this is to add extra CI checks to ensure that two systems intended to do the same thing actually do the same thing. For example there'd be some sort of CI check that the CMake-created set of header files is the same as the Rust-build-script-created set of header files. That would reduce the likelihood of divergence between the two, and while it wouldn't solve the duplication problem the duplication here is in theory not going to get all that much more complicated than it currently is.

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

Labels

wasmtime:c-api Issues pertaining to the C API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants