From 7ff6294daa0b3bea24dfcdd23f2980d0078a77a3 Mon Sep 17 00:00:00 2001 From: effulgentsia <477547+effulgentsia@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:43:27 -0700 Subject: [PATCH 1/7] Support minimal wasmtime builds via per-feature build tags (#2) --- config.go | 103 ---------------------------------- config_feat_cache.go | 41 ++++++++++++++ config_feat_cranelift.go | 72 ++++++++++++++++++++++++ config_feat_parallel.go | 13 +++++ config_feat_threads.go | 13 +++++ doc.go | 36 ++++++++++++ linker.go | 14 ----- linker_feat_wasi.go | 21 +++++++ module.go | 81 -------------------------- module_feat_cranelift.go | 72 ++++++++++++++++++++++++ module_feats_cranelift_wat.go | 25 +++++++++ store.go | 13 ----- store_feat_wasi.go | 20 +++++++ wasi.go | 2 + wat2wasm.go | 2 + 15 files changed, 317 insertions(+), 211 deletions(-) create mode 100644 config_feat_cache.go create mode 100644 config_feat_cranelift.go create mode 100644 config_feat_parallel.go create mode 100644 config_feat_threads.go create mode 100644 linker_feat_wasi.go create mode 100644 module_feat_cranelift.go create mode 100644 module_feats_cranelift_wat.go create mode 100644 store_feat_wasi.go diff --git a/config.go b/config.go index 8e93558..2246412 100644 --- a/config.go +++ b/config.go @@ -71,12 +71,6 @@ func (cfg *Config) SetMaxWasmStack(size int) { runtime.KeepAlive(cfg) } -// SetWasmThreads configures whether the wasm threads proposal is enabled -func (cfg *Config) SetWasmThreads(enabled bool) { - C.wasmtime_config_wasm_threads_set(cfg.ptr(), C.bool(enabled)) - runtime.KeepAlive(cfg) -} - // SetWasmReferenceTypes configures whether the wasm reference types proposal is enabled func (cfg *Config) SetWasmReferenceTypes(enabled bool) { C.wasmtime_config_wasm_reference_types_set(cfg.ptr(), C.bool(enabled)) @@ -162,24 +156,6 @@ func (cfg *Config) SetConsumeFuel(enabled bool) { runtime.KeepAlive(cfg) } -// SetParallelCompilation configures whether compilation should use multiple threads -func (cfg *Config) SetParallelCompilation(enabled bool) { - C.wasmtime_config_parallel_compilation_set(cfg.ptr(), C.bool(enabled)) - runtime.KeepAlive(cfg) -} - -// SetCraneliftNanCanonicalization configures whether whether Cranelift should perform a -// NaN-canonicalization pass. -// -// When Cranelift is used as a code generation backend this will configure it to replace NaNs with a single -// canonical value. This is useful for users requiring entirely deterministic WebAssembly computation. -// -// This is not required by the WebAssembly spec, so it is not enabled by default. -func (cfg *Config) SetCraneliftNanCanonicalization(enabled bool) { - C.wasmtime_config_cranelift_nan_canonicalization_set(cfg.ptr(), C.bool(enabled)) - runtime.KeepAlive(cfg) -} - // SetNativeUnwindInfo whether to generate native unwind information (e.g. .eh_frame on Linux). func (cfg *Config) SetNativeUnwindInfo(enabled bool) { C.wasmtime_config_native_unwind_info_set(cfg.ptr(), C.bool(enabled)) @@ -205,61 +181,12 @@ func (cfg *Config) SetMemoryInitCOWSet(enabled bool) { runtime.KeepAlive(cfg) } -// SetStrategy configures what compilation strategy is used to compile wasm code -func (cfg *Config) SetStrategy(strat Strategy) { - C.wasmtime_config_strategy_set(cfg.ptr(), C.wasmtime_strategy_t(strat)) - runtime.KeepAlive(cfg) -} - -// SetCraneliftDebugVerifier configures whether the cranelift debug verifier will be active when -// cranelift is used to compile wasm code. -func (cfg *Config) SetCraneliftDebugVerifier(enabled bool) { - C.wasmtime_config_cranelift_debug_verifier_set(cfg.ptr(), C.bool(enabled)) - runtime.KeepAlive(cfg) -} - -// SetCraneliftOptLevel configures the cranelift optimization level for generated code -func (cfg *Config) SetCraneliftOptLevel(level OptLevel) { - C.wasmtime_config_cranelift_opt_level_set(cfg.ptr(), C.wasmtime_opt_level_t(level)) - runtime.KeepAlive(cfg) -} - // SetProfiler configures what profiler strategy to use for generated code func (cfg *Config) SetProfiler(profiler ProfilingStrategy) { C.wasmtime_config_profiler_set(cfg.ptr(), C.wasmtime_profiling_strategy_t(profiler)) runtime.KeepAlive(cfg) } -// CacheConfigLoadDefault enables compiled code caching for this `Config` using the default settings -// configuration can be found. -// -// For more information about caching see -// https://bytecodealliance.github.io/wasmtime/cli-cache.html -func (cfg *Config) CacheConfigLoadDefault() error { - err := C.wasmtime_config_cache_config_load(cfg.ptr(), nil) - runtime.KeepAlive(cfg) - if err != nil { - return mkError(err) - } - return nil -} - -// CacheConfigLoad enables compiled code caching for this `Config` using the settings specified -// in the configuration file `path`. -// -// For more information about caching and configuration options see -// https://bytecodealliance.github.io/wasmtime/cli-cache.html -func (cfg *Config) CacheConfigLoad(path string) error { - cstr := C.CString(path) - err := C.wasmtime_config_cache_config_load(cfg.ptr(), cstr) - C.free(unsafe.Pointer(cstr)) - runtime.KeepAlive(cfg) - if err != nil { - return mkError(err) - } - return nil -} - // SetEpochInterruption enables epoch-based instrumentation of generated code to // interrupt WebAssembly execution when the current engine epoch exceeds a // defined threshold. @@ -289,36 +216,6 @@ func (cfg *Config) SetTarget(target string) error { return nil } -// EnableCraneliftFlag enables a target-specific flag in Cranelift. -// -// This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can -// be explored with `wasmtime settings` on the CLI. -// -// For more information see the Rust documentation at -// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_enable -func (cfg *Config) EnableCraneliftFlag(flag string) { - cstr := C.CString(flag) - C.wasmtime_config_cranelift_flag_enable(cfg.ptr(), cstr) - C.free(unsafe.Pointer(cstr)) - runtime.KeepAlive(cfg) -} - -// SetCraneliftFlag sets a target-specific flag in Cranelift to the specified value. -// -// This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can -// be explored with `wasmtime settings` on the CLI. -// -// For more information see the Rust documentation at -// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_set -func (cfg *Config) SetCraneliftFlag(name string, value string) { - cstrName := C.CString(name) - cstrValue := C.CString(value) - C.wasmtime_config_cranelift_flag_set(cfg.ptr(), cstrName, cstrValue) - C.free(unsafe.Pointer(cstrName)) - C.free(unsafe.Pointer(cstrValue)) - runtime.KeepAlive(cfg) -} - // See comments in `ffi.go` for what's going on here func (cfg *Config) ptr() *C.wasm_config_t { ret := cfg._ptr diff --git a/config_feat_cache.go b/config_feat_cache.go new file mode 100644 index 0000000..a93bac2 --- /dev/null +++ b/config_feat_cache.go @@ -0,0 +1,41 @@ +//go:build !no_feat_cache + +package wasmtime + +// #include +// #include +import "C" +import ( + "runtime" + "unsafe" +) + +// CacheConfigLoadDefault enables compiled code caching for this `Config` using the default settings +// configuration can be found. +// +// For more information about caching see +// https://bytecodealliance.github.io/wasmtime/cli-cache.html +func (cfg *Config) CacheConfigLoadDefault() error { + err := C.wasmtime_config_cache_config_load(cfg.ptr(), nil) + runtime.KeepAlive(cfg) + if err != nil { + return mkError(err) + } + return nil +} + +// CacheConfigLoad enables compiled code caching for this `Config` using the settings specified +// in the configuration file `path`. +// +// For more information about caching and configuration options see +// https://bytecodealliance.github.io/wasmtime/cli-cache.html +func (cfg *Config) CacheConfigLoad(path string) error { + cstr := C.CString(path) + err := C.wasmtime_config_cache_config_load(cfg.ptr(), cstr) + C.free(unsafe.Pointer(cstr)) + runtime.KeepAlive(cfg) + if err != nil { + return mkError(err) + } + return nil +} diff --git a/config_feat_cranelift.go b/config_feat_cranelift.go new file mode 100644 index 0000000..e24b860 --- /dev/null +++ b/config_feat_cranelift.go @@ -0,0 +1,72 @@ +//go:build !no_feat_cranelift + +package wasmtime + +// #include +// #include +import "C" +import ( + "runtime" + "unsafe" +) + +// SetStrategy configures what compilation strategy is used to compile wasm code +func (cfg *Config) SetStrategy(strat Strategy) { + C.wasmtime_config_strategy_set(cfg.ptr(), C.wasmtime_strategy_t(strat)) + runtime.KeepAlive(cfg) +} + +// SetCraneliftDebugVerifier configures whether the cranelift debug verifier will be active when +// cranelift is used to compile wasm code. +func (cfg *Config) SetCraneliftDebugVerifier(enabled bool) { + C.wasmtime_config_cranelift_debug_verifier_set(cfg.ptr(), C.bool(enabled)) + runtime.KeepAlive(cfg) +} + +// SetCraneliftOptLevel configures the cranelift optimization level for generated code +func (cfg *Config) SetCraneliftOptLevel(level OptLevel) { + C.wasmtime_config_cranelift_opt_level_set(cfg.ptr(), C.wasmtime_opt_level_t(level)) + runtime.KeepAlive(cfg) +} + +// SetCraneliftNanCanonicalization configures whether whether Cranelift should perform a +// NaN-canonicalization pass. +// +// When Cranelift is used as a code generation backend this will configure it to replace NaNs with a single +// canonical value. This is useful for users requiring entirely deterministic WebAssembly computation. +// +// This is not required by the WebAssembly spec, so it is not enabled by default. +func (cfg *Config) SetCraneliftNanCanonicalization(enabled bool) { + C.wasmtime_config_cranelift_nan_canonicalization_set(cfg.ptr(), C.bool(enabled)) + runtime.KeepAlive(cfg) +} + +// EnableCraneliftFlag enables a target-specific flag in Cranelift. +// +// This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can +// be explored with `wasmtime settings` on the CLI. +// +// For more information see the Rust documentation at +// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_enable +func (cfg *Config) EnableCraneliftFlag(flag string) { + cstr := C.CString(flag) + C.wasmtime_config_cranelift_flag_enable(cfg.ptr(), cstr) + C.free(unsafe.Pointer(cstr)) + runtime.KeepAlive(cfg) +} + +// SetCraneliftFlag sets a target-specific flag in Cranelift to the specified value. +// +// This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can +// be explored with `wasmtime settings` on the CLI. +// +// For more information see the Rust documentation at +// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_set +func (cfg *Config) SetCraneliftFlag(name string, value string) { + cstrName := C.CString(name) + cstrValue := C.CString(value) + C.wasmtime_config_cranelift_flag_set(cfg.ptr(), cstrName, cstrValue) + C.free(unsafe.Pointer(cstrName)) + C.free(unsafe.Pointer(cstrValue)) + runtime.KeepAlive(cfg) +} diff --git a/config_feat_parallel.go b/config_feat_parallel.go new file mode 100644 index 0000000..163d9c7 --- /dev/null +++ b/config_feat_parallel.go @@ -0,0 +1,13 @@ +//go:build !no_feat_parallel_compilation + +package wasmtime + +// #include +import "C" +import "runtime" + +// SetParallelCompilation configures whether compilation should use multiple threads +func (cfg *Config) SetParallelCompilation(enabled bool) { + C.wasmtime_config_parallel_compilation_set(cfg.ptr(), C.bool(enabled)) + runtime.KeepAlive(cfg) +} diff --git a/config_feat_threads.go b/config_feat_threads.go new file mode 100644 index 0000000..eaddf58 --- /dev/null +++ b/config_feat_threads.go @@ -0,0 +1,13 @@ +//go:build !no_feat_threads + +package wasmtime + +// #include +import "C" +import "runtime" + +// SetWasmThreads configures whether the wasm threads proposal is enabled +func (cfg *Config) SetWasmThreads(enabled bool) { + C.wasmtime_config_wasm_threads_set(cfg.ptr(), C.bool(enabled)) + runtime.KeepAlive(cfg) +} diff --git a/doc.go b/doc.go index a37191b..49a13a5 100644 --- a/doc.go +++ b/doc.go @@ -15,5 +15,41 @@ https://github.com/bytecodealliance/wasmtime-go/issues/new. It's also worth pointing out that the authors of this package up to this point primarily work in Rust, so if you've got suggestions of how to make this package more idiomatic for Go we'd love to hear your thoughts! + +# Feature Build Tags + +By default this package expects a full Wasmtime C library with all features. +A plain "go build" links against the full library and all functions are +available. + +To link against a Wasmtime C library built without some or all features, use +no_feat_ build tags to exclude the corresponding Go functions. The functions +are simply not compiled, so any accidental use becomes a compile-time error. + +For example, to disable the cranelift feature: + + go build -tags no_feat_cranelift + +To build with no optional features (minimal/headless): + + go build -tags "no_feat_cranelift,no_feat_wat,no_feat_wasi,no_feat_cache,no_feat_parallel_compilation,no_feat_threads" + +The tags correspond to Wasmtime's Cargo features +(see https://github.com/bytecodealliance/wasmtime/blob/main/crates/c-api/Cargo.toml). +When a feature is excluded, the following functions are not available: + + - no_feat_cranelift: [NewModule], [ModuleValidate], [Module.Serialize], + [Config.SetStrategy], [Config.SetCraneliftOptLevel], + [Config.SetCraneliftDebugVerifier], + [Config.SetCraneliftNanCanonicalization], [Config.EnableCraneliftFlag], + [Config.SetCraneliftFlag]. Use [NewModuleDeserialize] or + [NewModuleDeserializeFile] with a pre-compiled module instead. + - no_feat_wat: [Wat2Wasm]. Note: [NewModuleFromFile] requires both + cranelift and wat. + - no_feat_wasi: [WasiConfig] and all its methods, [Linker.DefineWasi], + [Store.SetWasi]. + - no_feat_cache: [Config.CacheConfigLoadDefault], [Config.CacheConfigLoad]. + - no_feat_parallel_compilation: [Config.SetParallelCompilation]. + - no_feat_threads: [Config.SetWasmThreads]. */ package wasmtime diff --git a/linker.go b/linker.go index a7eb8ef..0e2f4b1 100644 --- a/linker.go +++ b/linker.go @@ -197,20 +197,6 @@ func (l *Linker) DefineModule(store Storelike, name string, module *Module) erro return mkError(err) } -// DefineWasi links a WASI module into this linker, ensuring that all exported functions -// are available for linking. -// -// Returns an error if shadowing is disabled and names are already defined. -func (l *Linker) DefineWasi() error { - err := C.wasmtime_linker_define_wasi(l.ptr()) - runtime.KeepAlive(l) - if err == nil { - return nil - } - - return mkError(err) -} - // Instantiate instantiates a module with all imports defined in this linker. // // Returns an error if the instance's imports couldn't be satisfied, had the diff --git a/linker_feat_wasi.go b/linker_feat_wasi.go new file mode 100644 index 0000000..c84a5c3 --- /dev/null +++ b/linker_feat_wasi.go @@ -0,0 +1,21 @@ +//go:build !no_feat_wasi + +package wasmtime + +// #include +import "C" +import "runtime" + +// DefineWasi links a WASI module into this linker, ensuring that all exported functions +// are available for linking. +// +// Returns an error if shadowing is disabled and names are already defined. +func (l *Linker) DefineWasi() error { + err := C.wasmtime_linker_define_wasi(l.ptr()) + runtime.KeepAlive(l) + if err == nil { + return nil + } + + return mkError(err) +} diff --git a/module.go b/module.go index a92f05a..c641ca8 100644 --- a/module.go +++ b/module.go @@ -5,7 +5,6 @@ package wasmtime import "C" import ( - "os" "runtime" "unsafe" ) @@ -17,67 +16,6 @@ type Module struct { _ptr *C.wasmtime_module_t } -// NewModule compiles a new `Module` from the `wasm` provided with the given configuration -// in `engine`. -func NewModule(engine *Engine, wasm []byte) (*Module, error) { - // We can't create the `wasm_byte_vec_t` here and pass it in because - // that runs into the error of "passed a pointer to a pointer" because - // the vec itself is passed by pointer and it contains a pointer to - // `wasm`. To work around this we insert some C shims above and call - // them. - var wasmPtr *C.uint8_t - if len(wasm) > 0 { - wasmPtr = (*C.uint8_t)(unsafe.Pointer(&wasm[0])) - } - var ptr *C.wasmtime_module_t - err := C.wasmtime_module_new(engine.ptr(), wasmPtr, C.size_t(len(wasm)), &ptr) - runtime.KeepAlive(engine) - runtime.KeepAlive(wasm) - - if err != nil { - return nil, mkError(err) - } - - return mkModule(ptr), nil -} - -// NewModuleFromFile reads the contents of the `file` provided and interprets them as either the -// text format or the binary format for WebAssembly. -// -// Afterwards delegates to the `NewModule` constructor with the contents read. -func NewModuleFromFile(engine *Engine, file string) (*Module, error) { - wasm, err := os.ReadFile(file) - if err != nil { - return nil, err - } - // If this wasm isn't actually wasm, treat it as the text format and - // parse it as such. - if len(wasm) > 0 && wasm[0] != 0 { - wasm, err = Wat2Wasm(string(wasm)) - if err != nil { - return nil, err - } - } - return NewModule(engine, wasm) -} - -// ModuleValidate validates whether `wasm` would be a valid wasm module according to the -// configuration in `store` -func ModuleValidate(engine *Engine, wasm []byte) error { - var wasmPtr *C.uint8_t - if len(wasm) > 0 { - wasmPtr = (*C.uint8_t)(unsafe.Pointer(&wasm[0])) - } - err := C.wasmtime_module_validate(engine.ptr(), wasmPtr, C.size_t(len(wasm))) - runtime.KeepAlive(engine) - runtime.KeepAlive(wasm) - if err == nil { - return nil - } - - return mkError(err) -} - func mkModule(ptr *C.wasmtime_module_t) *Module { module := &Module{_ptr: ptr} runtime.SetFinalizer(module, func(module *Module) { @@ -217,22 +155,3 @@ func NewModuleDeserializeFile(engine *Engine, path string) (*Module, error) { return mkModule(ptr), nil } -// Serialize will convert this in-memory compiled module into a list of bytes. -// -// The purpose of this method is to extract an artifact which can be stored -// elsewhere from this `Module`. The returned bytes can, for example, be stored -// on disk or in an object store. The `NewModuleDeserialize` function can be -// used to deserialize the returned bytes at a later date to get the module -// back. -func (m *Module) Serialize() ([]byte, error) { - retVec := C.wasm_byte_vec_t{} - err := C.wasmtime_module_serialize(m.ptr(), &retVec) - runtime.KeepAlive(m) - - if err != nil { - return nil, mkError(err) - } - ret := C.GoBytes(unsafe.Pointer(retVec.data), C.int(retVec.size)) - C.wasm_byte_vec_delete(&retVec) - return ret, nil -} diff --git a/module_feat_cranelift.go b/module_feat_cranelift.go new file mode 100644 index 0000000..cfe7435 --- /dev/null +++ b/module_feat_cranelift.go @@ -0,0 +1,72 @@ +//go:build !no_feat_cranelift + +package wasmtime + +// #include "shims.h" +import "C" + +import ( + "runtime" + "unsafe" +) + +// NewModule compiles a new `Module` from the `wasm` provided with the given configuration +// in `engine`. +func NewModule(engine *Engine, wasm []byte) (*Module, error) { + // We can't create the `wasm_byte_vec_t` here and pass it in because + // that runs into the error of "passed a pointer to a pointer" because + // the vec itself is passed by pointer and it contains a pointer to + // `wasm`. To work around this we insert some C shims above and call + // them. + var wasmPtr *C.uint8_t + if len(wasm) > 0 { + wasmPtr = (*C.uint8_t)(unsafe.Pointer(&wasm[0])) + } + var ptr *C.wasmtime_module_t + err := C.wasmtime_module_new(engine.ptr(), wasmPtr, C.size_t(len(wasm)), &ptr) + runtime.KeepAlive(engine) + runtime.KeepAlive(wasm) + + if err != nil { + return nil, mkError(err) + } + + return mkModule(ptr), nil +} + +// ModuleValidate validates whether `wasm` would be a valid wasm module according to the +// configuration in `store` +func ModuleValidate(engine *Engine, wasm []byte) error { + var wasmPtr *C.uint8_t + if len(wasm) > 0 { + wasmPtr = (*C.uint8_t)(unsafe.Pointer(&wasm[0])) + } + err := C.wasmtime_module_validate(engine.ptr(), wasmPtr, C.size_t(len(wasm))) + runtime.KeepAlive(engine) + runtime.KeepAlive(wasm) + if err == nil { + return nil + } + + return mkError(err) +} + +// Serialize will convert this in-memory compiled module into a list of bytes. +// +// The purpose of this method is to extract an artifact which can be stored +// elsewhere from this `Module`. The returned bytes can, for example, be stored +// on disk or in an object store. The `NewModuleDeserialize` function can be +// used to deserialize the returned bytes at a later date to get the module +// back. +func (m *Module) Serialize() ([]byte, error) { + retVec := C.wasm_byte_vec_t{} + err := C.wasmtime_module_serialize(m.ptr(), &retVec) + runtime.KeepAlive(m) + + if err != nil { + return nil, mkError(err) + } + ret := C.GoBytes(unsafe.Pointer(retVec.data), C.int(retVec.size)) + C.wasm_byte_vec_delete(&retVec) + return ret, nil +} diff --git a/module_feats_cranelift_wat.go b/module_feats_cranelift_wat.go new file mode 100644 index 0000000..b31323d --- /dev/null +++ b/module_feats_cranelift_wat.go @@ -0,0 +1,25 @@ +//go:build !no_feat_cranelift && !no_feat_wat + +package wasmtime + +import "os" + +// NewModuleFromFile reads the contents of the `file` provided and interprets them as either the +// text format or the binary format for WebAssembly. +// +// Afterwards delegates to the `NewModule` constructor with the contents read. +func NewModuleFromFile(engine *Engine, file string) (*Module, error) { + wasm, err := os.ReadFile(file) + if err != nil { + return nil, err + } + // If this wasm isn't actually wasm, treat it as the text format and + // parse it as such. + if len(wasm) > 0 && wasm[0] != 0 { + wasm, err = Wat2Wasm(string(wasm)) + if err != nil { + return nil, err + } + } + return NewModule(engine, wasm) +} diff --git a/store.go b/store.go index 3340fa5..fef28c4 100644 --- a/store.go +++ b/store.go @@ -136,19 +136,6 @@ func (store *Store) GC() { runtime.KeepAlive(store) } -// SetWasi will configure the WASI state to use for instances within this -// `Store`. -// -// The `wasi` argument cannot be reused for another `Store`, it's consumed by -// this function. -func (store *Store) SetWasi(wasi *WasiConfig) { - runtime.SetFinalizer(wasi, nil) - ptr := wasi.ptr() - wasi._ptr = nil - C.wasmtime_context_set_wasi(store.Context(), ptr) - runtime.KeepAlive(store) -} - // Implementation of the `Storelike` interface func (store *Store) Context() *C.wasmtime_context_t { ret := C.wasmtime_store_context(store.ptr()) diff --git a/store_feat_wasi.go b/store_feat_wasi.go new file mode 100644 index 0000000..2875154 --- /dev/null +++ b/store_feat_wasi.go @@ -0,0 +1,20 @@ +//go:build !no_feat_wasi + +package wasmtime + +// #include +import "C" +import "runtime" + +// SetWasi will configure the WASI state to use for instances within this +// `Store`. +// +// The `wasi` argument cannot be reused for another `Store`, it's consumed by +// this function. +func (store *Store) SetWasi(wasi *WasiConfig) { + runtime.SetFinalizer(wasi, nil) + ptr := wasi.ptr() + wasi._ptr = nil + C.wasmtime_context_set_wasi(store.Context(), ptr) + runtime.KeepAlive(store) +} diff --git a/wasi.go b/wasi.go index da4bc61..7ca2024 100644 --- a/wasi.go +++ b/wasi.go @@ -1,3 +1,5 @@ +//go:build !no_feat_wasi + package wasmtime // #include diff --git a/wat2wasm.go b/wat2wasm.go index 735db5c..4345dc6 100644 --- a/wat2wasm.go +++ b/wat2wasm.go @@ -1,3 +1,5 @@ +//go:build !no_feat_wat + package wasmtime // #include From 6114e7994de3db8277781851b8085d5a66ca50d2 Mon Sep 17 00:00:00 2001 From: effulgentsia <477547+effulgentsia@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:57:34 -0700 Subject: [PATCH 2/7] Shorten documentation of build tags --- doc.go | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/doc.go b/doc.go index 49a13a5..527d7bc 100644 --- a/doc.go +++ b/doc.go @@ -23,33 +23,19 @@ A plain "go build" links against the full library and all functions are available. To link against a Wasmtime C library built without some or all features, use -no_feat_ build tags to exclude the corresponding Go functions. The functions -are simply not compiled, so any accidental use becomes a compile-time error. +"no_feat_*" build tags to exclude the corresponding Go functions, preventing +compilation errors due to the missing C functions. -For example, to disable the cranelift feature: +For example, to exclude the Go bindings for the cranelift feature: go build -tags no_feat_cranelift -To build with no optional features (minimal/headless): +To exclude the Go bindings of all optional features (e.g., if linking against +Wasmtime's minimal build): go build -tags "no_feat_cranelift,no_feat_wat,no_feat_wasi,no_feat_cache,no_feat_parallel_compilation,no_feat_threads" The tags correspond to Wasmtime's Cargo features (see https://github.com/bytecodealliance/wasmtime/blob/main/crates/c-api/Cargo.toml). -When a feature is excluded, the following functions are not available: - - - no_feat_cranelift: [NewModule], [ModuleValidate], [Module.Serialize], - [Config.SetStrategy], [Config.SetCraneliftOptLevel], - [Config.SetCraneliftDebugVerifier], - [Config.SetCraneliftNanCanonicalization], [Config.EnableCraneliftFlag], - [Config.SetCraneliftFlag]. Use [NewModuleDeserialize] or - [NewModuleDeserializeFile] with a pre-compiled module instead. - - no_feat_wat: [Wat2Wasm]. Note: [NewModuleFromFile] requires both - cranelift and wat. - - no_feat_wasi: [WasiConfig] and all its methods, [Linker.DefineWasi], - [Store.SetWasi]. - - no_feat_cache: [Config.CacheConfigLoadDefault], [Config.CacheConfigLoad]. - - no_feat_parallel_compilation: [Config.SetParallelCompilation]. - - no_feat_threads: [Config.SetWasmThreads]. */ package wasmtime From 8bdd208adff420c69845022ce89766841dc1d287 Mon Sep 17 00:00:00 2001 From: effulgentsia <477547+effulgentsia@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:00:02 -0700 Subject: [PATCH 3/7] Remove trailing blank line in module.go --- module.go | 1 - 1 file changed, 1 deletion(-) diff --git a/module.go b/module.go index c641ca8..187a69c 100644 --- a/module.go +++ b/module.go @@ -154,4 +154,3 @@ func NewModuleDeserializeFile(engine *Engine, path string) (*Module, error) { return mkModule(ptr), nil } - From a2896591e99fc40fe1ee4dd0720ea36248e209fe Mon Sep 17 00:00:00 2001 From: effulgentsia <477547+effulgentsia@users.noreply.github.com> Date: Fri, 20 Mar 2026 00:45:34 -0700 Subject: [PATCH 4/7] Replace per-feature build tags with a single `min` build tag --- config_feat_cache.go | 2 +- config_feat_cranelift.go | 2 +- ....go => config_feat_parallel_compilation.go | 2 +- config_feat_threads.go | 2 +- doc.go | 25 +++++-------------- linker_feat_wasi.go | 2 +- module_feat_cranelift.go | 2 +- module_feats_cranelift_wat.go | 2 +- store_feat_wasi.go | 2 +- wasi.go | 2 +- wat2wasm.go | 2 +- 11 files changed, 16 insertions(+), 29 deletions(-) rename config_feat_parallel.go => config_feat_parallel_compilation.go (88%) diff --git a/config_feat_cache.go b/config_feat_cache.go index a93bac2..eba127a 100644 --- a/config_feat_cache.go +++ b/config_feat_cache.go @@ -1,4 +1,4 @@ -//go:build !no_feat_cache +//go:build !min package wasmtime diff --git a/config_feat_cranelift.go b/config_feat_cranelift.go index e24b860..8dcd5d9 100644 --- a/config_feat_cranelift.go +++ b/config_feat_cranelift.go @@ -1,4 +1,4 @@ -//go:build !no_feat_cranelift +//go:build !min package wasmtime diff --git a/config_feat_parallel.go b/config_feat_parallel_compilation.go similarity index 88% rename from config_feat_parallel.go rename to config_feat_parallel_compilation.go index 163d9c7..c76492e 100644 --- a/config_feat_parallel.go +++ b/config_feat_parallel_compilation.go @@ -1,4 +1,4 @@ -//go:build !no_feat_parallel_compilation +//go:build !min package wasmtime diff --git a/config_feat_threads.go b/config_feat_threads.go index eaddf58..9816bfb 100644 --- a/config_feat_threads.go +++ b/config_feat_threads.go @@ -1,4 +1,4 @@ -//go:build !no_feat_threads +//go:build !min package wasmtime diff --git a/doc.go b/doc.go index 527d7bc..df063f8 100644 --- a/doc.go +++ b/doc.go @@ -16,26 +16,13 @@ It's also worth pointing out that the authors of this package up to this point primarily work in Rust, so if you've got suggestions of how to make this package more idiomatic for Go we'd love to hear your thoughts! -# Feature Build Tags +# Full vs minimal Go API -By default this package expects a full Wasmtime C library with all features. -A plain "go build" links against the full library and all functions are -available. +A plain `go build` includes all Go bindings, useful if using the full Wasmtime +binary. -To link against a Wasmtime C library built without some or all features, use -"no_feat_*" build tags to exclude the corresponding Go functions, preventing -compilation errors due to the missing C functions. - -For example, to exclude the Go bindings for the cranelift feature: - - go build -tags no_feat_cranelift - -To exclude the Go bindings of all optional features (e.g., if linking against -Wasmtime's minimal build): - - go build -tags "no_feat_cranelift,no_feat_wat,no_feat_wasi,no_feat_cache,no_feat_parallel_compilation,no_feat_threads" - -The tags correspond to Wasmtime's Cargo features -(see https://github.com/bytecodealliance/wasmtime/blob/main/crates/c-api/Cargo.toml). +`go build -tags min` omits Go bindings for optional Wasmtime features. This is +necessary if using the minimal Wasmtime binary, since otherwise compilation +fails due to the missing C functions. */ package wasmtime diff --git a/linker_feat_wasi.go b/linker_feat_wasi.go index c84a5c3..8ad9b3e 100644 --- a/linker_feat_wasi.go +++ b/linker_feat_wasi.go @@ -1,4 +1,4 @@ -//go:build !no_feat_wasi +//go:build !min package wasmtime diff --git a/module_feat_cranelift.go b/module_feat_cranelift.go index cfe7435..0e87e77 100644 --- a/module_feat_cranelift.go +++ b/module_feat_cranelift.go @@ -1,4 +1,4 @@ -//go:build !no_feat_cranelift +//go:build !min package wasmtime diff --git a/module_feats_cranelift_wat.go b/module_feats_cranelift_wat.go index b31323d..4db0e9b 100644 --- a/module_feats_cranelift_wat.go +++ b/module_feats_cranelift_wat.go @@ -1,4 +1,4 @@ -//go:build !no_feat_cranelift && !no_feat_wat +//go:build !min package wasmtime diff --git a/store_feat_wasi.go b/store_feat_wasi.go index 2875154..4fdc366 100644 --- a/store_feat_wasi.go +++ b/store_feat_wasi.go @@ -1,4 +1,4 @@ -//go:build !no_feat_wasi +//go:build !min package wasmtime diff --git a/wasi.go b/wasi.go index 7ca2024..d27eda4 100644 --- a/wasi.go +++ b/wasi.go @@ -1,4 +1,4 @@ -//go:build !no_feat_wasi +//go:build !min package wasmtime diff --git a/wat2wasm.go b/wat2wasm.go index 4345dc6..ea618bc 100644 --- a/wat2wasm.go +++ b/wat2wasm.go @@ -1,4 +1,4 @@ -//go:build !no_feat_wat +//go:build !min package wasmtime From ec8e3e06b2d55d5171557863d948bb0a4e3afc5e Mon Sep 17 00:00:00 2001 From: effulgentsia <477547+effulgentsia@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:36:40 -0700 Subject: [PATCH 5/7] Add test coverage for using the min binary --- .github/workflows/main.yml | 4 +++ .gitignore | 3 +- aotproduce/produce_test.go | 40 +++++++++++++++++++++++++++ ci/download-wasmtime.py | 3 +- ci/test-wasmtime-min.sh | 16 +++++++++++ config.go | 8 +++--- config_feat_component_model.go | 14 ++++++++++ config_test.go | 1 + ffi.go | 15 ++++++---- minload/minload_test.go | 50 ++++++++++++++++++++++++++++++++++ 10 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 aotproduce/produce_test.go create mode 100755 ci/test-wasmtime-min.sh create mode 100644 config_feat_component_model.go create mode 100644 minload/minload_test.go diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bac13bd..793bfd0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -72,6 +72,10 @@ jobs: shell: bash run: ./ci/test-vendoring.sh if: runner.os != 'Windows' + - name: Test wasmtime min AOT on *nix + shell: bash + run: ./ci/test-wasmtime-min.sh + if: runner.os != 'Windows' coverage: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index d6f1347..725fd8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build /bazel-* -test-vendoring-project \ No newline at end of file +test-vendoring-project +test-wasmtime-min \ No newline at end of file diff --git a/aotproduce/produce_test.go b/aotproduce/produce_test.go new file mode 100644 index 0000000..b17f3f0 --- /dev/null +++ b/aotproduce/produce_test.go @@ -0,0 +1,40 @@ +package aotproduce_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/bytecodealliance/wasmtime-go/v42" +) + +// TestWasmtimeMinAOT_Produce compiles inline WAT and writes a serialized module +// (same artifact shape as wasmtime compile) for TestWasmtimeMinAOT_Load / CI. +func TestWasmtimeMinAOT_Produce(t *testing.T) { + dir := os.Getenv("WASMTIME_TEST_AOT_DIR") + if dir == "" { + dir = "test-wasmtime-min" + } + require.NoError(t, os.MkdirAll(dir, 0o755)) + + wasm, err := wasmtime.Wat2Wasm(`(module (func (export "test")))`) + require.NoError(t, err) + + cfg := wasmtime.NewConfig() + cfg.SetGCSupport(false) + cfg.SetWasmGC(false) + cfg.SetWasmThreads(false) + cfg.SetWasmComponentModel(false) + engine := wasmtime.NewEngineWithConfig(cfg) + module, err := wasmtime.NewModule(engine, wasm) + require.NoError(t, err) + defer module.Close() + + artifact, err := module.Serialize() + require.NoError(t, err) + + path := filepath.Join(dir, "module.cwasm") + require.NoError(t, os.WriteFile(path, artifact, 0o644)) +} diff --git a/ci/download-wasmtime.py b/ci/download-wasmtime.py index 459df58..a203b34 100644 --- a/ci/download-wasmtime.py +++ b/ci/download-wasmtime.py @@ -10,7 +10,7 @@ import glob -version = 'v42.0.0' +version = 'v43.0.0' urls = [ ['wasmtime-{}-x86_64-mingw-c-api.zip', 'windows-x86_64'], ['wasmtime-{}-x86_64-linux-c-api.tar.xz', 'linux-x86_64'], @@ -51,6 +51,7 @@ shutil.copytree(src + '/include', 'build/include', dirs_exist_ok=True) shutil.copytree(src + '/lib', 'build/' + dirname, dirs_exist_ok=True) + shutil.copytree(src + '/min/lib', 'build/' + dirname + '-min', dirs_exist_ok=True) shutil.rmtree(src) for dylib in glob.glob("build/**/*.dll"): diff --git a/ci/test-wasmtime-min.sh b/ci/test-wasmtime-min.sh new file mode 100755 index 0000000..5768057 --- /dev/null +++ b/ci/test-wasmtime-min.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Two-step integration test: full Wasmtime produces a precompiled .cwasm artifact; +# minimal Wasmtime (Go build tag `min`) loads and runs it. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +rm -rf test-wasmtime-min +mkdir -p test-wasmtime-min +export WASMTIME_TEST_AOT_DIR="$ROOT/test-wasmtime-min" + +go test -run '^TestWasmtimeMinAOT_Produce$' ./aotproduce/ +go test -tags min -run '^TestWasmtimeMinAOT_Load$' ./minload/ + +echo "test-wasmtime-min: success" diff --git a/config.go b/config.go index 2246412..b7b5b35 100644 --- a/config.go +++ b/config.go @@ -137,10 +137,10 @@ func (cfg *Config) SetWasmGC(enabled bool) { runtime.KeepAlive(cfg) } -// SetWasmComponentModel configures whether the wasm component model proposal is -// enabled. -func (cfg *Config) SetWasmComponentModel(enabled bool) { - C.wasmtime_config_wasm_component_model_set(cfg.ptr(), C.bool(enabled)) +// SetGCSupport configures whether GC support is enabled in Wasmtime at all. +// When false, engines can be used without a GC collector (e.g. minimal Wasmtime builds). +func (cfg *Config) SetGCSupport(enabled bool) { + C.wasmtime_config_gc_support_set(cfg.ptr(), C.bool(enabled)) runtime.KeepAlive(cfg) } diff --git a/config_feat_component_model.go b/config_feat_component_model.go new file mode 100644 index 0000000..371e12c --- /dev/null +++ b/config_feat_component_model.go @@ -0,0 +1,14 @@ +//go:build !min + +package wasmtime + +// #include +import "C" +import "runtime" + +// SetWasmComponentModel configures whether the wasm component model proposal is +// enabled. +func (cfg *Config) SetWasmComponentModel(enabled bool) { + C.wasmtime_config_wasm_component_model_set(cfg.ptr(), C.bool(enabled)) + runtime.KeepAlive(cfg) +} diff --git a/config_test.go b/config_test.go index cb29f74..e878075 100644 --- a/config_test.go +++ b/config_test.go @@ -21,6 +21,7 @@ func TestConfig(t *testing.T) { NewConfig().SetWasmTailCall(true) NewConfig().SetWasmFunctionReferences(true) NewConfig().SetWasmGC(true) + NewConfig().SetGCSupport(true) NewConfig().SetWasmComponentModel(true) NewConfig().SetWasmWideArithmetic(true) NewConfig().SetConsumeFuel(true) diff --git a/ffi.go b/ffi.go index 7d47d7b..fd7ea63 100644 --- a/ffi.go +++ b/ffi.go @@ -4,11 +4,16 @@ package wasmtime // #cgo !windows LDFLAGS:-lwasmtime -lm -ldl -pthread // #cgo windows CFLAGS:-DWASM_API_EXTERN= -DWASI_API_EXTERN= // #cgo windows LDFLAGS:-lwasmtime -luserenv -lole32 -lntdll -lws2_32 -lkernel32 -lbcrypt -// #cgo linux,amd64 LDFLAGS:-L${SRCDIR}/build/linux-x86_64 -// #cgo linux,arm64 LDFLAGS:-L${SRCDIR}/build/linux-aarch64 -// #cgo darwin,amd64 LDFLAGS:-L${SRCDIR}/build/macos-x86_64 -// #cgo darwin,arm64 LDFLAGS:-L${SRCDIR}/build/macos-aarch64 -// #cgo windows,amd64 LDFLAGS:-L${SRCDIR}/build/windows-x86_64 +// #cgo linux,amd64,!min LDFLAGS:-L${SRCDIR}/build/linux-x86_64 +// #cgo linux,amd64,min LDFLAGS:-L${SRCDIR}/build/linux-x86_64-min +// #cgo linux,arm64,!min LDFLAGS:-L${SRCDIR}/build/linux-aarch64 +// #cgo linux,arm64,min LDFLAGS:-L${SRCDIR}/build/linux-aarch64-min +// #cgo darwin,amd64,!min LDFLAGS:-L${SRCDIR}/build/macos-x86_64 +// #cgo darwin,amd64,min LDFLAGS:-L${SRCDIR}/build/macos-x86_64-min +// #cgo darwin,arm64,!min LDFLAGS:-L${SRCDIR}/build/macos-aarch64 +// #cgo darwin,arm64,min LDFLAGS:-L${SRCDIR}/build/macos-aarch64-min +// #cgo windows,amd64,!min LDFLAGS:-L${SRCDIR}/build/windows-x86_64 +// #cgo windows,amd64,min LDFLAGS:-L${SRCDIR}/build/windows-x86_64-min // #include import "C" import ( diff --git a/minload/minload_test.go b/minload/minload_test.go new file mode 100644 index 0000000..f903e55 --- /dev/null +++ b/minload/minload_test.go @@ -0,0 +1,50 @@ +//go:build min + +// External tests for package wasmtime. The import path matches go.mod’s module +// path; it refers to this repository’s root package, not a remote version. +// Running `go test -tags min ./minload/` compiles github.com/.../wasmtime-go/v42 +// (the parent module) with the `min` build tag, so `//go:build !min` sources are +// omitted and the link matches the minimal C library. + +package minload_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/bytecodealliance/wasmtime-go/v42" +) + +// TestWasmtimeMinAOT_Load deserializes module.cwasm produced by +// TestWasmtimeMinAOT_Produce and invokes export "test". +func TestWasmtimeMinAOT_Load(t *testing.T) { + dir := os.Getenv("WASMTIME_TEST_AOT_DIR") + if dir == "" { + dir = "test-wasmtime-min" + } + path := filepath.Join(dir, "module.cwasm") + if _, err := os.Stat(path); err != nil { + t.Skipf("missing %s (run TestWasmtimeMinAOT_Produce or ci/test-wasmtime-min.sh first): %v", path, err) + } + + cfg := wasmtime.NewConfig() + cfg.SetGCSupport(false) + cfg.SetWasmGC(false) + engine := wasmtime.NewEngineWithConfig(cfg) + module, err := wasmtime.NewModuleDeserializeFile(engine, path) + require.NoError(t, err) + defer module.Close() + + store := wasmtime.NewStore(engine) + instance, err := wasmtime.NewInstance(store, module, []wasmtime.AsExtern{}) + require.NoError(t, err) + + fn := instance.GetFunc(store, "test") + require.NotNil(t, fn) + + _, err = fn.Call(store) + require.NoError(t, err) +} From e4444a256bb19b03741143dcada502ae0a2cbd08 Mon Sep 17 00:00:00 2001 From: effulgentsia <477547+effulgentsia@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:53:31 -0700 Subject: [PATCH 6/7] Consolidate minimal runtime tests under a single directory --- .github/workflows/main.yml | 2 +- .gitignore | 2 +- ci/test-minimal-runtime/compile_and_run.sh | 16 ++++++++++++++++ .../test-minimal-runtime/compile_test.go | 6 ++++-- .../test-minimal-runtime/run_test.go | 8 ++++---- ci/test-wasmtime-min.sh | 16 ---------------- 6 files changed, 26 insertions(+), 24 deletions(-) create mode 100755 ci/test-minimal-runtime/compile_and_run.sh rename aotproduce/produce_test.go => ci/test-minimal-runtime/compile_test.go (90%) rename minload/minload_test.go => ci/test-minimal-runtime/run_test.go (84%) delete mode 100755 ci/test-wasmtime-min.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 793bfd0..02f092c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -74,7 +74,7 @@ jobs: if: runner.os != 'Windows' - name: Test wasmtime min AOT on *nix shell: bash - run: ./ci/test-wasmtime-min.sh + run: ./ci/test-minimal-runtime/compile_and_run.sh if: runner.os != 'Windows' coverage: diff --git a/.gitignore b/.gitignore index 725fd8a..68a99a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ build /bazel-* test-vendoring-project -test-wasmtime-min \ No newline at end of file +ci/test-minimal-runtime/module.cwasm \ No newline at end of file diff --git a/ci/test-minimal-runtime/compile_and_run.sh b/ci/test-minimal-runtime/compile_and_run.sh new file mode 100755 index 0000000..0ff5084 --- /dev/null +++ b/ci/test-minimal-runtime/compile_and_run.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Two-step integration test: full Wasmtime produces a precompiled .cwasm artifact; +# minimal Wasmtime (Go build tag `min`) loads and runs it. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$ROOT" + +rm -f "$SCRIPT_DIR/module.cwasm" +export WASMTIME_TEST_AOT_DIR="$SCRIPT_DIR" + +go test -run '^TestWasmtimeMinAOT_Produce$' ./ci/test-minimal-runtime/ +go test -tags min -run '^TestWasmtimeMinAOT_Load$' ./ci/test-minimal-runtime/ + +echo "test-minimal-runtime: success" diff --git a/aotproduce/produce_test.go b/ci/test-minimal-runtime/compile_test.go similarity index 90% rename from aotproduce/produce_test.go rename to ci/test-minimal-runtime/compile_test.go index b17f3f0..b720a96 100644 --- a/aotproduce/produce_test.go +++ b/ci/test-minimal-runtime/compile_test.go @@ -1,4 +1,6 @@ -package aotproduce_test +//go:build !min + +package testminimalruntime_test import ( "os" @@ -15,7 +17,7 @@ import ( func TestWasmtimeMinAOT_Produce(t *testing.T) { dir := os.Getenv("WASMTIME_TEST_AOT_DIR") if dir == "" { - dir = "test-wasmtime-min" + dir = filepath.Join("ci", "test-minimal-runtime") } require.NoError(t, os.MkdirAll(dir, 0o755)) diff --git a/minload/minload_test.go b/ci/test-minimal-runtime/run_test.go similarity index 84% rename from minload/minload_test.go rename to ci/test-minimal-runtime/run_test.go index f903e55..b804027 100644 --- a/minload/minload_test.go +++ b/ci/test-minimal-runtime/run_test.go @@ -2,11 +2,11 @@ // External tests for package wasmtime. The import path matches go.mod’s module // path; it refers to this repository’s root package, not a remote version. -// Running `go test -tags min ./minload/` compiles github.com/.../wasmtime-go/v42 +// Running `go test -tags min ./ci/test-minimal-runtime/` compiles github.com/.../wasmtime-go/v42 // (the parent module) with the `min` build tag, so `//go:build !min` sources are // omitted and the link matches the minimal C library. -package minload_test +package testminimalruntime_test import ( "os" @@ -23,11 +23,11 @@ import ( func TestWasmtimeMinAOT_Load(t *testing.T) { dir := os.Getenv("WASMTIME_TEST_AOT_DIR") if dir == "" { - dir = "test-wasmtime-min" + dir = filepath.Join("ci", "test-minimal-runtime") } path := filepath.Join(dir, "module.cwasm") if _, err := os.Stat(path); err != nil { - t.Skipf("missing %s (run TestWasmtimeMinAOT_Produce or ci/test-wasmtime-min.sh first): %v", path, err) + t.Skipf("missing %s (run TestWasmtimeMinAOT_Produce or ci/test-minimal-runtime/compile_and_run.sh first): %v", path, err) } cfg := wasmtime.NewConfig() diff --git a/ci/test-wasmtime-min.sh b/ci/test-wasmtime-min.sh deleted file mode 100755 index 5768057..0000000 --- a/ci/test-wasmtime-min.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# Two-step integration test: full Wasmtime produces a precompiled .cwasm artifact; -# minimal Wasmtime (Go build tag `min`) loads and runs it. -set -euo pipefail - -ROOT="$(cd "$(dirname "$0")/.." && pwd)" -cd "$ROOT" - -rm -rf test-wasmtime-min -mkdir -p test-wasmtime-min -export WASMTIME_TEST_AOT_DIR="$ROOT/test-wasmtime-min" - -go test -run '^TestWasmtimeMinAOT_Produce$' ./aotproduce/ -go test -tags min -run '^TestWasmtimeMinAOT_Load$' ./minload/ - -echo "test-wasmtime-min: success" From 1e07aab64d7e2c3b918ce1d6129569f00da85997 Mon Sep 17 00:00:00 2001 From: effulgentsia <477547+effulgentsia@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:11:07 -0700 Subject: [PATCH 7/7] Simplify testing the minimal runtime --- .github/workflows/main.yml | 4 +- ci/test-minimal-runtime/compile_and_run.sh | 16 ------- ci/test-minimal-runtime/compile_test.go | 42 ------------------ ci/test-minimal-runtime/create_cwasm.go | 35 +++++++++++++++ ci/test-minimal-runtime/min_test.go | 31 ++++++++++++++ ci/test-minimal-runtime/run_test.go | 50 ---------------------- ci/test-minimal-runtime/test.sh | 7 +++ 7 files changed, 75 insertions(+), 110 deletions(-) delete mode 100755 ci/test-minimal-runtime/compile_and_run.sh delete mode 100644 ci/test-minimal-runtime/compile_test.go create mode 100644 ci/test-minimal-runtime/create_cwasm.go create mode 100644 ci/test-minimal-runtime/min_test.go delete mode 100644 ci/test-minimal-runtime/run_test.go create mode 100755 ci/test-minimal-runtime/test.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 02f092c..f9beaeb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -72,9 +72,9 @@ jobs: shell: bash run: ./ci/test-vendoring.sh if: runner.os != 'Windows' - - name: Test wasmtime min AOT on *nix + - name: Test the minimal runtime shell: bash - run: ./ci/test-minimal-runtime/compile_and_run.sh + run: ./ci/test-minimal-runtime/test.sh if: runner.os != 'Windows' coverage: diff --git a/ci/test-minimal-runtime/compile_and_run.sh b/ci/test-minimal-runtime/compile_and_run.sh deleted file mode 100755 index 0ff5084..0000000 --- a/ci/test-minimal-runtime/compile_and_run.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# Two-step integration test: full Wasmtime produces a precompiled .cwasm artifact; -# minimal Wasmtime (Go build tag `min`) loads and runs it. -set -euo pipefail - -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -cd "$ROOT" - -rm -f "$SCRIPT_DIR/module.cwasm" -export WASMTIME_TEST_AOT_DIR="$SCRIPT_DIR" - -go test -run '^TestWasmtimeMinAOT_Produce$' ./ci/test-minimal-runtime/ -go test -tags min -run '^TestWasmtimeMinAOT_Load$' ./ci/test-minimal-runtime/ - -echo "test-minimal-runtime: success" diff --git a/ci/test-minimal-runtime/compile_test.go b/ci/test-minimal-runtime/compile_test.go deleted file mode 100644 index b720a96..0000000 --- a/ci/test-minimal-runtime/compile_test.go +++ /dev/null @@ -1,42 +0,0 @@ -//go:build !min - -package testminimalruntime_test - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/bytecodealliance/wasmtime-go/v42" -) - -// TestWasmtimeMinAOT_Produce compiles inline WAT and writes a serialized module -// (same artifact shape as wasmtime compile) for TestWasmtimeMinAOT_Load / CI. -func TestWasmtimeMinAOT_Produce(t *testing.T) { - dir := os.Getenv("WASMTIME_TEST_AOT_DIR") - if dir == "" { - dir = filepath.Join("ci", "test-minimal-runtime") - } - require.NoError(t, os.MkdirAll(dir, 0o755)) - - wasm, err := wasmtime.Wat2Wasm(`(module (func (export "test")))`) - require.NoError(t, err) - - cfg := wasmtime.NewConfig() - cfg.SetGCSupport(false) - cfg.SetWasmGC(false) - cfg.SetWasmThreads(false) - cfg.SetWasmComponentModel(false) - engine := wasmtime.NewEngineWithConfig(cfg) - module, err := wasmtime.NewModule(engine, wasm) - require.NoError(t, err) - defer module.Close() - - artifact, err := module.Serialize() - require.NoError(t, err) - - path := filepath.Join(dir, "module.cwasm") - require.NoError(t, os.WriteFile(path, artifact, 0o644)) -} diff --git a/ci/test-minimal-runtime/create_cwasm.go b/ci/test-minimal-runtime/create_cwasm.go new file mode 100644 index 0000000..1372665 --- /dev/null +++ b/ci/test-minimal-runtime/create_cwasm.go @@ -0,0 +1,35 @@ +//go:build ignore + +package main + +import ( + "log" + "os" + + "github.com/bytecodealliance/wasmtime-go/v42" +) + +func main() { + wasm, err := wasmtime.Wat2Wasm(`(module (func (export "test") (result i32) (i32.const 1)))`) + check(err) + + cfg := wasmtime.NewConfig() + cfg.SetGCSupport(false) + cfg.SetWasmThreads(false) + cfg.SetWasmComponentModel(false) + engine := wasmtime.NewEngineWithConfig(cfg) + module, err := wasmtime.NewModule(engine, wasm) + check(err) + defer module.Close() + + artifact, err := module.Serialize() + check(err) + + check(os.WriteFile("module.cwasm", artifact, 0o644)) +} + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} diff --git a/ci/test-minimal-runtime/min_test.go b/ci/test-minimal-runtime/min_test.go new file mode 100644 index 0000000..eb8db96 --- /dev/null +++ b/ci/test-minimal-runtime/min_test.go @@ -0,0 +1,31 @@ +//go:build min + +package testminimalruntime_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/bytecodealliance/wasmtime-go/v42" +) + +func TestMinimalRuntime(t *testing.T) { + cfg := wasmtime.NewConfig() + cfg.SetGCSupport(false) + engine := wasmtime.NewEngineWithConfig(cfg) + module, err := wasmtime.NewModuleDeserializeFile(engine, "module.cwasm") + require.NoError(t, err) + defer module.Close() + + store := wasmtime.NewStore(engine) + instance, err := wasmtime.NewInstance(store, module, []wasmtime.AsExtern{}) + require.NoError(t, err) + + fn := instance.GetFunc(store, "test") + require.NotNil(t, fn) + + result, err := fn.Call(store) + require.NoError(t, err) + require.Equal(t, int32(1), result) +} diff --git a/ci/test-minimal-runtime/run_test.go b/ci/test-minimal-runtime/run_test.go deleted file mode 100644 index b804027..0000000 --- a/ci/test-minimal-runtime/run_test.go +++ /dev/null @@ -1,50 +0,0 @@ -//go:build min - -// External tests for package wasmtime. The import path matches go.mod’s module -// path; it refers to this repository’s root package, not a remote version. -// Running `go test -tags min ./ci/test-minimal-runtime/` compiles github.com/.../wasmtime-go/v42 -// (the parent module) with the `min` build tag, so `//go:build !min` sources are -// omitted and the link matches the minimal C library. - -package testminimalruntime_test - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/bytecodealliance/wasmtime-go/v42" -) - -// TestWasmtimeMinAOT_Load deserializes module.cwasm produced by -// TestWasmtimeMinAOT_Produce and invokes export "test". -func TestWasmtimeMinAOT_Load(t *testing.T) { - dir := os.Getenv("WASMTIME_TEST_AOT_DIR") - if dir == "" { - dir = filepath.Join("ci", "test-minimal-runtime") - } - path := filepath.Join(dir, "module.cwasm") - if _, err := os.Stat(path); err != nil { - t.Skipf("missing %s (run TestWasmtimeMinAOT_Produce or ci/test-minimal-runtime/compile_and_run.sh first): %v", path, err) - } - - cfg := wasmtime.NewConfig() - cfg.SetGCSupport(false) - cfg.SetWasmGC(false) - engine := wasmtime.NewEngineWithConfig(cfg) - module, err := wasmtime.NewModuleDeserializeFile(engine, path) - require.NoError(t, err) - defer module.Close() - - store := wasmtime.NewStore(engine) - instance, err := wasmtime.NewInstance(store, module, []wasmtime.AsExtern{}) - require.NoError(t, err) - - fn := instance.GetFunc(store, "test") - require.NotNil(t, fn) - - _, err = fn.Call(store) - require.NoError(t, err) -} diff --git a/ci/test-minimal-runtime/test.sh b/ci/test-minimal-runtime/test.sh new file mode 100755 index 0000000..69df19a --- /dev/null +++ b/ci/test-minimal-runtime/test.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" + +rm -f module.cwasm +go run create_cwasm.go +go test -count=1 -tags min .