diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c650d04c..51dab839 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -32,6 +32,9 @@ jobs: with: go-version-file: go.mod + - name: Set up wams-tools.wasm + run: make internal/wasmtools/wasm-tools.wasm + - name: Run Go tests run: go test ${{ env.go-modules }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4186b5c2..e6ce91c8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -34,8 +34,13 @@ jobs: with: go-version-file: go.mod + - name: Set up wams-tools.wasm + run: make internal/wasmtools/wasm-tools.wasm + - name: Vet Go code - run: go vet ${{ env.go-modules }} ./tests/... + run: | + make build + go vet ${{ env.go-modules }} ./tests/... # Test with Go test-go: @@ -61,6 +66,9 @@ jobs: with: version: ${{ env.wasm-tools-version }} + - name: Set up wams-tools.wasm + run: make internal/wasmtools/wasm-tools.wasm + - name: Run Go tests run: go test -v ${{ env.go-modules }} @@ -147,6 +155,9 @@ jobs: with: version: ${{ env.wasm-tools-version }} + - name: Set up wams-tools.wasm + run: make internal/wasmtools/wasm-tools.wasm + - name: Set up Wasmtime uses: bytecodealliance/actions/wasmtime/setup@v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index f3b2d256..0b2d0b4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- `internal/wasmtools` now loads compiled `wasm-tools` Wasm module from the `wasm-tools` crate and executes it using wazero. This allows `wit-bindgen-go` to run on any platform without needing to install `wasm-tools` natively. + ## [v0.5.0] — 2024-12-14 ### Changed diff --git a/Makefile b/Makefile index 2bbe3468..34d76b6e 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,8 @@ wit_files = $(sort $(shell find testdata -name '*.wit' ! -name '*.golden.*')) json: $(wit_files) .PHONY: $(wit_files) -$(wit_files): - wasm-tools component wit -j --all-features $@ > $@.json +$(wit_files): internal/wasmtools/wasm-tools.wasm + wasmtime --dir . internal/wasmtools/wasm-tools.wasm component wit -j --all-features $@ > $@.json # golden recompiles the .golden.wit test files. .PHONY: golden @@ -21,12 +21,26 @@ generated: clean json .PHONY: clean clean: rm -rf ./generated/* + rm -f internal/wasmtools/wasm-tools.wasm.gz # tests/generated writes generated Go code to the tests directory .PHONY: tests/generated tests/generated: json go generate ./tests +# build builds the cmd/wit-bindgen-go binary +.PHONY: build +build: internal/wasmtools/wasm-tools.wasm + go build -o wit-bindgen-go ./cmd/wit-bindgen-go + +internal/wasmtools/wasm-tools.wasm: internal/wasmtools/wasm-tools.wasm.gz + gzip -dc $< > $@ + +internal/wasmtools/wasm-tools.wasm.gz: + cd internal/wasmtools && \ + cargo build --target wasm32-wasip1 --release -p wasm-tools + gzip -c internal/wasmtools/target/wasm32-wasip1/release/wasm-tools.wasm > $@ + # test runs Go and TinyGo tests GOTESTARGS := GOTESTMODULES := ./... ./cm/... diff --git a/go.mod b/go.mod index f6c8812a..e1c9915c 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/klauspost/compress v1.17.11 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/tetratelabs/wazero v1.8.2 // indirect github.com/ulikunitz/xz v0.5.12 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect diff --git a/go.sum b/go.sum index 70199c4b..e88216ac 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= +github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg= diff --git a/internal/wasmtools/.gitignore b/internal/wasmtools/.gitignore new file mode 100644 index 00000000..fb6223a0 --- /dev/null +++ b/internal/wasmtools/.gitignore @@ -0,0 +1,2 @@ +target +wasm-tools.wasm diff --git a/internal/wasmtools/Cargo.lock b/internal/wasmtools/Cargo.lock new file mode 100644 index 00000000..f8349ee9 --- /dev/null +++ b/internal/wasmtools/Cargo.lock @@ -0,0 +1,755 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + +[[package]] +name = "gimli" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.166" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spdx" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" +dependencies = [ + "smallvec", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasm-encoder" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de35b6c3ef1f53ac7a31b5e69bc00f1542ea337e7e7162dc34c68b537ff82690" +dependencies = [ + "leb128", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fca0d366d1be722f23e9e918e3b926945b21343424bb7a56e845beed82377bf" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-tools" +version = "1.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f493321aefc926ad54a0c60859eafd3a426418b41132f6bbe0148f816cdd0735" +dependencies = [ + "anyhow", + "clap", + "env_logger", + "log", + "serde_json", + "tempfile", + "termcolor", + "wasm-encoder", + "wasmparser", + "wasmprinter", + "wast", + "wat", + "wit-component", + "wit-encoder", + "wit-parser", +] + +[[package]] +name = "wasm-tools-go" +version = "0.0.0" +dependencies = [ + "wasm-tools", +] + +[[package]] +name = "wasmparser" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8659e755615170cfe20da468865c989da78c5da16d8652e69a75acda02406a92" +dependencies = [ + "bitflags", + "hashbrown", + "indexmap", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4234d0682f373b1878cefd5b3d99843312478ee4f603b2cea34cc2135f0a65" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser", +] + +[[package]] +name = "wast" +version = "221.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8eb1933d493dd07484a255c3f52236123333f5befaa3be36182a50d393ec54" +dependencies = [ + "bumpalo", + "gimli", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c813fd4e5b2b97242830b56e7b7dc5479bc17aaa8730109be35e61909af83993" +dependencies = [ + "wast", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-component" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6af482af5d64ecae38fe2cbd973c2fda25c4ce6d1cc2abc71c2f3ea81838ed" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wast", + "wat", + "wit-parser", +] + +[[package]] +name = "wit-encoder" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c7064427baef19f7645f4e18f30437b3d1946a1e1cd17d0f9f37d4984cf12" +dependencies = [ + "id-arena", + "pretty_assertions", + "semver", + "serde", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f5b0a971faff0855a827ef06b25528263a718907bed4056992382316f1e68e" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", + "wat", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/internal/wasmtools/Cargo.toml b/internal/wasmtools/Cargo.toml new file mode 100644 index 00000000..e142ef29 --- /dev/null +++ b/internal/wasmtools/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "wasm-tools-go" + +[dependencies] +wasm-tools = { version = "1.221.0", default-features = false, features = [ + "component", +] } + +[profile.release] +lto = true # compile with link-time optimization +codegen-units = 1 # compile with a single codegen unit +opt-level = "z" # optimize for size +panic = "abort" # panic=abort will remove the panic code +strip = true # strip the debug information diff --git a/internal/wasmtools/src/lib.rs b/internal/wasmtools/src/lib.rs new file mode 100644 index 00000000..36db4933 --- /dev/null +++ b/internal/wasmtools/src/lib.rs @@ -0,0 +1 @@ +// intentionally left empty diff --git a/internal/wasmtools/types.go b/internal/wasmtools/types.go new file mode 100644 index 00000000..91dd6271 --- /dev/null +++ b/internal/wasmtools/types.go @@ -0,0 +1,12 @@ +package wasmtools + +import ( + "context" + "io" + "io/fs" +) + +// Runner is an interface for running Wasm modules. +type Runner interface { + Run(ctx context.Context, args []string, stdin io.Reader, fsMap map[fs.FS]string, name *string) (stdout io.Reader, stderr io.Reader, err error) +} diff --git a/internal/wasmtools/wasm-tools.wasm.gz b/internal/wasmtools/wasm-tools.wasm.gz new file mode 100644 index 00000000..dde0933b Binary files /dev/null and b/internal/wasmtools/wasm-tools.wasm.gz differ diff --git a/internal/wasmtools/wasmtools.go b/internal/wasmtools/wasmtools.go new file mode 100644 index 00000000..329ee44c --- /dev/null +++ b/internal/wasmtools/wasmtools.go @@ -0,0 +1,87 @@ +//go:build !tinygo +// +build !tinygo + +package wasmtools + +import ( + "bytes" + "context" + "crypto/rand" + _ "embed" + "fmt" + "io" + "io/fs" + "time" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +//go:embed wasm-tools.wasm +var wasmTools []byte + +// Instance is a compiled wazero instance. +type Instance struct { + runtime wazero.Runtime + module wazero.CompiledModule +} + +// New creates a new wazero instance. +func New(ctx context.Context) (*Instance, error) { + c := wazero.NewRuntimeConfig().WithCloseOnContextDone(true) + r := wazero.NewRuntimeWithConfig(ctx, c) + if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { + return nil, fmt.Errorf("error instantiating WASI: %w", err) + } + + module, err := r.CompileModule(ctx, wasmTools) + if err != nil { + return nil, fmt.Errorf("error compiling wasm module: %w", err) + } + return &Instance{runtime: r, module: module}, nil +} + +// Close closes the wazero runtime resource. +func (w *Instance) Close(ctx context.Context) error { + return w.runtime.Close(ctx) +} + +// Run runs the wasm module with the context, arguments, stdin, filesystem map, and name. +// It returns the stdout, stderr, and error. +// The execution times out after 10 seconds. +func (w *Instance) Run(ctx context.Context, args []string, stdin io.Reader, fsMap map[fs.FS]string, name *string) (stdout io.Reader, stderr io.Reader, err error) { + stdoutBuffer := &bytes.Buffer{} + stderrBuffer := &bytes.Buffer{} + + config := wazero.NewModuleConfig(). + WithStdout(stdoutBuffer). + WithStderr(stderrBuffer). + WithRandSource(rand.Reader). + WithSysNanosleep(). + WithSysNanotime(). + WithSysWalltime(). + WithArgs(append([]string{"wasm-tools.wasm"}, args...)...) + if name != nil { + config = config.WithName(*name) + } + if stdin != nil { + config = config.WithStdin(stdin) + } + + fsConfig := wazero.NewFSConfig() + for f, guestPath := range fsMap { + fsConfig = fsConfig.WithFSMount(f, guestPath) + } + config = config.WithFSConfig(fsConfig) + + // timeout to 10 seconds + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + _, err = w.runtime.InstantiateModule(ctx, w.module, config) + if err != nil { + return nil, nil, fmt.Errorf("error instantiating wasm module: %w", err) + } + + return stdoutBuffer, stderrBuffer, nil +} diff --git a/internal/wasmtools/wasmtools_tinygo.go b/internal/wasmtools/wasmtools_tinygo.go new file mode 100644 index 00000000..d7c19d82 --- /dev/null +++ b/internal/wasmtools/wasmtools_tinygo.go @@ -0,0 +1,25 @@ +//go:build tinygo +// +build tinygo + +package wasmtools + +import ( + "context" + "errors" + "io" + "io/fs" +) + +type Instance struct{} + +func New(ctx context.Context) (*Instance, error) { + return &Instance{}, errors.New("wasm-tools functionality is disabled under TinyGo") +} + +func (w *Instance) Close(ctx context.Context) error { + return nil +} + +func (w *Instance) Run(ctx context.Context, args []string, stdin io.Reader, fsMap map[fs.FS]string, name *string) (stdout io.Reader, stderr io.Reader, err error) { + return nil, nil, errors.New("wasm-tools functionality is disabled under TinyGo") +} diff --git a/wit/load.go b/wit/load.go index 7d90b132..d2a7843d 100644 --- a/wit/load.go +++ b/wit/load.go @@ -2,11 +2,15 @@ package wit import ( "bytes" + "context" "errors" "fmt" "io" + "io/fs" "os" - "os/exec" + "path/filepath" + + "go.bytecodealliance.org/internal/wasmtools" ) // LoadJSON loads a [WIT] JSON file from path. @@ -48,27 +52,32 @@ func loadWIT(path string, reader io.Reader) (*Resolve, error) { return nil, errors.New("cannot set both path and reader; provide only one") } - wasmTools, err := exec.LookPath("wasm-tools") - if err != nil { - return nil, err - } - - var stdout, stderr bytes.Buffer + ctx := context.Background() + args := []string{"component", "wit", "-j", "--all-features"} + fsMap := make(map[fs.FS]string) + var stdin io.Reader - cmdArgs := []string{"component", "wit", "-j", "--all-features"} if path != "" { - cmdArgs = append(cmdArgs, path) + args = append(args, path) + dir := filepath.Dir(path) + fsMap[os.DirFS(dir)] = dir + } else { + stdin = reader } - - cmd := exec.Command(wasmTools, cmdArgs...) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - cmd.Stdin = reader - - if err := cmd.Run(); err != nil { - fmt.Fprint(os.Stderr, stderr.String()) + wasmTools, err := wasmtools.New(ctx) + if err != nil { return nil, err } - - return DecodeJSON(&stdout) + stdout, stderr, err := wasmTools.Run(ctx, args, stdin, fsMap, nil) + if err != nil { + return nil, fmt.Errorf("error executing wasm-tools: %w", err) + } + if stderr != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(stderr) + if buf.Len() > 0 { + return nil, fmt.Errorf("%s", buf.String()) + } + } + return DecodeJSON(stdout) } diff --git a/wit/testdata_test.go b/wit/testdata_test.go index 5946e8c4..e37cc9b4 100644 --- a/wit/testdata_test.go +++ b/wit/testdata_test.go @@ -2,8 +2,10 @@ package wit import ( "bytes" + "context" "flag" "fmt" + "io/fs" "os" "os/exec" "strings" @@ -11,6 +13,7 @@ import ( "testing" "go.bytecodealliance.org/internal/relpath" + "go.bytecodealliance.org/internal/wasmtools" "github.com/sergi/go-diff/diffmatchpatch" ) @@ -78,26 +81,33 @@ func TestGoldenWITRoundTrip(t *testing.T) { // t.Skip is not available in TinyGo, requires runtime.Goexit() return } - if !canWasmTools() { - t.Log("skipping test: wasm-tools not installed or cannot fork/exec (TinyGo)") + ctx := context.Background() + wasmTools, err := wasmtools.New(ctx) + if err != nil { + t.Logf("wasm-tools not available: %v", err) return } - err := loadTestdata(func(path string, res *Resolve) error { + + err = loadTestdata(func(path string, res *Resolve) error { data := res.WIT(nil, "") t.Run(path, func(t *testing.T) { - // Run the generated WIT through wasm-tools to generate JSON. - cmd := exec.Command("wasm-tools", "component", "wit", "-j", "--all-features") - cmd.Stdin = strings.NewReader(data) - stdout := &bytes.Buffer{} - stderr := &bytes.Buffer{} - cmd.Stdout = stdout - cmd.Stderr = stderr - err := cmd.Run() + args := []string{"component", "wit", "-j", "--all-features"} + stdin := strings.NewReader(data) + + fsMap := make(map[fs.FS]string) + stdout, stderr, err := wasmTools.Run(ctx, args, stdin, fsMap, &path) if err != nil { - t.Error(err, stderr.String()) + t.Errorf("wasm-tools: %v", err) return } - + if stderr != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(stderr) + if buf.Len() > 0 { + t.Error(buf.String()) + return + } + } // Parse the JSON into a Resolve. res2, err := DecodeJSON(stdout) if err != nil {