1
2gofumpt := mvdan.cc/gofumpt@v0.5.0
3gosimports := github.com/rinchsan/gosimports/cmd/gosimports@v0.3.8
4golangci_lint := github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2
5asmfmt := github.com/klauspost/asmfmt/cmd/asmfmt@v1.3.2
6# sync this with netlify.toml!
7hugo := github.com/gohugoio/hugo@v0.115.2
8
9# Make 3.81 doesn't support '**' globbing: Set explicitly instead of recursion.
10all_sources := $(wildcard *.go */*.go */*/*.go */*/*/*.go */*/*/*.go */*/*/*/*.go)
11all_testdata := $(wildcard testdata/* */testdata/* */*/testdata/* */*/testdata/*/* */*/*/testdata/*)
12all_testing := $(wildcard internal/testing/* internal/testing/*/* internal/testing/*/*/*)
13all_examples := $(wildcard examples/* examples/*/* examples/*/*/* */*/example/* */*/example/*/* */*/example/*/*/*)
14all_it := $(wildcard internal/integration_test/* internal/integration_test/*/* internal/integration_test/*/*/*)
15# main_sources exclude any test or example related code
16main_sources := $(wildcard $(filter-out %_test.go $(all_testdata) $(all_testing) $(all_examples) $(all_it), $(all_sources)))
17# main_packages collect the unique main source directories (sort will dedupe).
18# Paths need to all start with ./, so we do that manually vs foreach which strips it.
19main_packages := $(sort $(foreach f,$(dir $(main_sources)),$(if $(findstring ./,$(f)),./,./$(f))))
20
21go_test_options ?= -timeout 300s
22
23ensureCompilerFastest := -ldflags '-X github.com/tetratelabs/wazero/internal/integration_test/vs.ensureCompilerFastest=true'
24.PHONY: bench
25bench:
26 @go test -run=NONE -benchmem -bench=. ./internal/engine/compiler/...
27 @go build ./internal/integration_test/bench/...
28 @# Don't use -test.benchmem as it isn't accurate when comparing against CGO libs
29 @for d in vs/time vs/wasmedge vs/wasmtime ; do \
30 cd ./internal/integration_test/$$d ; \
31 go test -bench=. . -tags='wasmedge' $(ensureCompilerFastest) ; \
32 cd - ;\
33 done
34
35bench_testdata_dir := internal/integration_test/bench/testdata
36.PHONY: build.bench
37build.bench:
38 @tinygo build -o $(bench_testdata_dir)/case.wasm -scheduler=none --no-debug -target=wasi $(bench_testdata_dir)/case.go
39
40.PHONY: test.examples
41test.examples:
42 @go test $(go_test_options) ./examples/... ./imports/assemblyscript/example/... ./imports/emscripten/... ./experimental/gojs/example/... ./imports/wasi_snapshot_preview1/example/...
43
44.PHONY: build.examples.as
45build.examples.as:
46 @cd ./imports/assemblyscript/example/testdata && npm install && npm run build
47
48%.wasm: %.zig
49 @(cd $(@D); zig build -Doptimize=ReleaseSmall)
50 @mv $(@D)/zig-out/*/$(@F) $(@D)
51
52.PHONY: build.examples.zig
53build.examples.zig: examples/allocation/zig/testdata/greet.wasm imports/wasi_snapshot_preview1/example/testdata/zig/cat.wasm imports/wasi_snapshot_preview1/testdata/zig/wasi.wasm
54 @cd internal/testing/dwarftestdata/testdata/zig; zig build; mv zig-out/*/main.wasm ./ # Need DWARF custom sections.
55
56tinygo_sources := examples/basic/testdata/add.go examples/allocation/tinygo/testdata/greet.go examples/cli/testdata/cli.go imports/wasi_snapshot_preview1/example/testdata/tinygo/cat.go imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go
57.PHONY: build.examples.tinygo
58build.examples.tinygo: $(tinygo_sources)
59 @for f in $^; do \
60 tinygo build -o $$(echo $$f | sed -e 's/\.go/\.wasm/') -scheduler=none --no-debug --target=wasi $$f; \
61 done
62
63# We use zig to build C as it is easy to install and embeds a copy of zig-cc.
64# Note: Don't use "-Oz" as that breaks our wasi sock example.
65c_sources := imports/wasi_snapshot_preview1/example/testdata/zig-cc/cat.c imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.c internal/testing/dwarftestdata/testdata/zig-cc/main.c
66.PHONY: build.examples.zig-cc
67build.examples.zig-cc: $(c_sources)
68 @for f in $^; do \
69 zig cc --target=wasm32-wasi -o $$(echo $$f | sed -e 's/\.c/\.wasm/') $$f; \
70 done
71
72# Here are the emcc args we use:
73#
74# * `-Oz` - most optimization for code size.
75# * `--profiling` - adds the name section.
76# * `-s STANDALONE_WASM` - ensures wasm is built for a non-js runtime.
77# * `-s EXPORTED_FUNCTIONS=_malloc,_free` - export allocation functions so that
78# they can be used externally as "malloc" and "free".
79# * `-s WARN_ON_UNDEFINED_SYMBOLS=0` - imports not defined in JavaScript error
80# otherwise. See https://github.com/emscripten-core/emscripten/issues/13641
81# * `-s TOTAL_STACK=8KB -s TOTAL_MEMORY=64KB` - reduce memory default from 16MB
82# to one page (64KB). To do this, we have to reduce the stack size.
83# * `-s ALLOW_MEMORY_GROWTH` - allows "memory.grow" instructions to succeed, but
84# requires a function import "emscripten_notify_memory_growth".
85emscripten_sources := $(wildcard imports/emscripten/testdata/*.cc)
86.PHONY: build.examples.emscripten
87build.examples.emscripten: $(emscripten_sources)
88 @for f in $^; do \
89 em++ -Oz --profiling \
90 -s STANDALONE_WASM \
91 -s EXPORTED_FUNCTIONS=_malloc,_free \
92 -s WARN_ON_UNDEFINED_SYMBOLS=0 \
93 -s TOTAL_STACK=8KB -s TOTAL_MEMORY=64KB \
94 -s ALLOW_MEMORY_GROWTH \
95 --std=c++17 -o $$(echo $$f | sed -e 's/\.cc/\.wasm/') $$f; \
96 done
97
98%/greet.wasm : cargo_target := wasm32-unknown-unknown
99%/cat.wasm : cargo_target := wasm32-wasi
100%/wasi.wasm : cargo_target := wasm32-wasi
101
102.PHONY: build.examples.rust
103build.examples.rust: examples/allocation/rust/testdata/greet.wasm imports/wasi_snapshot_preview1/example/testdata/cargo-wasi/cat.wasm imports/wasi_snapshot_preview1/testdata/cargo-wasi/wasi.wasm internal/testing/dwarftestdata/testdata/rust/main.wasm.xz
104
105# Normally, we build release because it is smaller. Testing dwarf requires the debug build.
106internal/testing/dwarftestdata/testdata/rust/main.wasm.xz:
107 cd $(@D) && cargo wasi build
108 mv $(@D)/target/wasm32-wasi/debug/main.wasm $(@D)
109 cd $(@D) && xz -k -f ./main.wasm # Rust's DWARF section is huge, so compress it.
110
111# Builds rust using cargo normally, or cargo-wasi.
112%.wasm: %.rs
113 @(cd $(@D); cargo $(if $(findstring wasi,$(cargo_target)),wasi build,build --target $(cargo_target)) --release)
114 @mv $(@D)/target/$(cargo_target)/release/$(@F) $(@D)
115
116spectest_base_dir := internal/integration_test/spectest
117spectest_v1_dir := $(spectest_base_dir)/v1
118spectest_v1_testdata_dir := $(spectest_v1_dir)/testdata
119spec_version_v1 := wg-1.0
120spectest_v2_dir := $(spectest_base_dir)/v2
121spectest_v2_testdata_dir := $(spectest_v2_dir)/testdata
122# Latest draft state as of May 23, 2023.
123spec_version_v2 := 2e8912e88a3118a46b90e8ccb659e24b4e8f3c23
124
125.PHONY: build.spectest
126build.spectest:
127 @$(MAKE) build.spectest.v1
128 @$(MAKE) build.spectest.v2
129
130.PHONY: build.spectest.v1
131build.spectest.v1: # Note: wabt by default uses >1.0 features, so wast2json flags might drift as they include more. See WebAssembly/wabt#1878
132 @rm -rf $(spectest_v1_testdata_dir)
133 @mkdir -p $(spectest_v1_testdata_dir)
134 @cd $(spectest_v1_testdata_dir) \
135 && curl -sSL 'https://api.github.com/repos/WebAssembly/spec/contents/test/core?ref=$(spec_version_v1)' | jq -r '.[]| .download_url' | grep -E ".wast" | xargs -Iurl curl -sJL url -O
136 @cd $(spectest_v1_testdata_dir) && for f in `find . -name '*.wast'`; do \
137 perl -pi -e 's/\(assert_return_canonical_nan\s(\(invoke\s"f32.demote_f64"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \(f32.const nan:canonical\)\)/g' $$f; \
138 perl -pi -e 's/\(assert_return_arithmetic_nan\s(\(invoke\s"f32.demote_f64"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \(f32.const nan:arithmetic\)\)/g' $$f; \
139 perl -pi -e 's/\(assert_return_canonical_nan\s(\(invoke\s"f64\.promote_f32"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \(f64.const nan:canonical\)\)/g' $$f; \
140 perl -pi -e 's/\(assert_return_arithmetic_nan\s(\(invoke\s"f64\.promote_f32"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \(f64.const nan:arithmetic\)\)/g' $$f; \
141 perl -pi -e 's/\(assert_return_canonical_nan\s(\(invoke\s"[a-z._0-9]+"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \($$2.const nan:canonical\)\)/g' $$f; \
142 perl -pi -e 's/\(assert_return_arithmetic_nan\s(\(invoke\s"[a-z._0-9]+"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \($$2.const nan:arithmetic\)\)/g' $$f; \
143 perl -pi -e 's/\(assert_return_canonical_nan\s(\(invoke\s"[a-z._0-9]+"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\s\([a-z0-9.\s+-:]+\)\))\)/\(assert_return $$1 \($$2.const nan:canonical\)\)/g' $$f; \
144 perl -pi -e 's/\(assert_return_arithmetic_nan\s(\(invoke\s"[a-z._0-9]+"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\s\([a-z0-9.\s+-:]+\)\))\)/\(assert_return $$1 \($$2.const nan:arithmetic\)\)/g' $$f; \
145 perl -pi -e 's/\(assert_return_canonical_nan\s(\(invoke\s"[a-z._0-9]+"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \($$2.const nan:canonical\)\)/g' $$f; \
146 perl -pi -e 's/\(assert_return_arithmetic_nan\s(\(invoke\s"[a-z._0-9]+"\s\((f[0-9]{2})\.const\s[a-z0-9.+:-]+\)\))\)/\(assert_return $$1 \($$2.const nan:arithmetic\)\)/g' $$f; \
147 wast2json \
148 --disable-saturating-float-to-int \
149 --disable-sign-extension \
150 --disable-simd \
151 --disable-multi-value \
152 --disable-bulk-memory \
153 --disable-reference-types \
154 --debug-names $$f; \
155 done
156
157.PHONY: build.spectest.v2
158build.spectest.v2: # Note: SIMD cases are placed in the "simd" subdirectory.
159 @mkdir -p $(spectest_v2_testdata_dir)
160 @cd $(spectest_v2_testdata_dir) \
161 && curl -sSL 'https://api.github.com/repos/WebAssembly/spec/contents/test/core?ref=$(spec_version_v2)' | jq -r '.[]| .download_url' | grep -E ".wast" | xargs -Iurl curl -sJL url -O
162 @cd $(spectest_v2_testdata_dir) \
163 && curl -sSL 'https://api.github.com/repos/WebAssembly/spec/contents/test/core/simd?ref=$(spec_version_v2)' | jq -r '.[]| .download_url' | grep -E ".wast" | xargs -Iurl curl -sJL url -O
164 @cd $(spectest_v2_testdata_dir) && for f in `find . -name '*.wast'`; do \
165 wast2json --debug-names --no-check $$f; \
166 done
167
168.PHONY: test
169test:
170 @go test $(go_test_options) $$(go list ./... | grep -vE '$(spectest_v1_dir)|$(spectest_v2_dir)')
171 @cd internal/version/testdata && go test $(go_test_options) ./...
172
173.PHONY: coverage
174# replace spaces with commas
175coverpkg = $(shell echo $(main_packages) | tr ' ' ',')
176coverage: ## Generate test coverage
177 @go test -coverprofile=coverage.txt -covermode=atomic --coverpkg=$(coverpkg) $(main_packages)
178 @go tool cover -func coverage.txt
179
180.PHONY: spectest
181spectest:
182 @$(MAKE) spectest.v1
183 @$(MAKE) spectest.v2
184
185spectest.v1:
186 @go test $(go_test_options) $$(go list ./... | grep $(spectest_v1_dir))
187
188spectest.v2:
189 @go test $(go_test_options) $$(go list ./... | grep $(spectest_v2_dir))
190
191golangci_lint_path := $(shell go env GOPATH)/bin/golangci-lint
192
193$(golangci_lint_path):
194 @go install $(golangci_lint)
195
196golangci_lint_goarch ?= $(shell go env GOARCH)
197
198.PHONY: lint
199lint: $(golangci_lint_path)
200 @GOARCH=$(golangci_lint_goarch) CGO_ENABLED=0 $(golangci_lint_path) run --timeout 5m
201
202.PHONY: format
203format:
204 @go run $(gofumpt) -l -w .
205 @go run $(gosimports) -local github.com/tetratelabs/ -w $(shell find . -name '*.go' -type f)
206 @go run $(asmfmt) -w $(shell find . -name '*.s' -type f)
207
208.PHONY: check # Pre-flight check for pull requests
209check:
210# The following checks help ensure our platform-specific code used for system
211# calls safely falls back on a platform unsupported by the compiler engine.
212# This makes sure the intepreter can be used. Most often the package that can
213# drift here is "platform" or "sysfs":
214#
215# Ensure we build on plan9. See #1578
216 @GOARCH=amd64 GOOS=plan9 go build ./...
217# Ensure we build on gojs. See #1526.
218 @GOARCH=wasm GOOS=js go build ./...
219# Ensure we build on gojs. See #1526.
220 @GOARCH=wasm GOOS=wasip1 go build ./...
221# Ensure we build on aix. See #1723
222 @GOARCH=ppc64 GOOS=aix go build ./...
223# Ensure we build on windows:
224 @GOARCH=amd64 GOOS=windows go build ./...
225# Ensure we build on an arbitrary operating system:
226 @GOARCH=amd64 GOOS=dragonfly go build ./...
227# Ensure we build on solaris/illumos:
228 @GOARCH=amd64 GOOS=illumos go build ./...
229 @GOARCH=amd64 GOOS=solaris go build ./...
230# Ensure we build on linux arm for Dapr:
231# gh release view -R dapr/dapr --json assets --jq 'first(.assets[] | select(.name = "daprd_linux_arm.tar.gz") | {url, downloadCount})'
232 @GOARCH=arm GOOS=linux go build ./...
233# Ensure we build on linux 386 for Trivy:
234# gh release view -R aquasecurity/trivy --json assets --jq 'first(.assets[] | select(.name| test("Linux-32bit.*tar.gz")) | {url, downloadCount})'
235 @GOARCH=386 GOOS=linux go build ./...
236# Ensure we build on FreeBSD amd64 for Trivy:
237# gh release view -R aquasecurity/trivy --json assets --jq 'first(.assets[] | select(.name| test("FreeBSD-64bit.*tar.gz")) | {url, downloadCount})'
238 @GOARCH=amd64 GOOS=freebsd go build ./...
239 @$(MAKE) lint golangci_lint_goarch=arm64
240 @$(MAKE) lint golangci_lint_goarch=amd64
241 @$(MAKE) format
242 @go mod tidy
243 @if [ ! -z "`git status -s`" ]; then \
244 echo "The following differences will fail CI until committed:"; \
245 git diff --exit-code; \
246 fi
247
248.PHONY: site
249site: ## Serve website content
250 @git submodule update --init
251 @cd site && go run $(hugo) server --minify --disableFastRender --baseURL localhost:1313 --cleanDestinationDir -D
252
253.PHONY: clean
254clean: ## Ensure a clean build
255 @rm -rf dist build coverage.txt
256 @go clean -testcache
257
258fuzz_timeout_seconds ?= 10
259.PHONY: fuzz
260fuzz:
261 @cd internal/integration_test/fuzz && cargo test
262 @cd internal/integration_test/fuzz && cargo fuzz run no_diff --sanitizer=none -- -rss_limit_mb=8192 -max_total_time=$(fuzz_timeout_seconds)
263 @cd internal/integration_test/fuzz && cargo fuzz run memory_no_diff --sanitizer=none -- -rss_limit_mb=8192 -max_total_time=$(fuzz_timeout_seconds)
264 @cd internal/integration_test/fuzz && cargo fuzz run validation --sanitizer=none -- -rss_limit_mb=8192 -max_total_time=$(fuzz_timeout_seconds)
265
266#### CLI release related ####
267
268VERSION ?= dev
269# Default to a dummy version 0.0.1.1, which is always lower than a real release.
270# Legal version values should look like 'x.x.x.x' where x is an integer from 0 to 65534.
271# https://learn.microsoft.com/en-us/windows/win32/msi/productversion?redirectedfrom=MSDN
272# https://stackoverflow.com/questions/9312221/msi-version-numbers
273MSI_VERSION ?= 0.0.1.1
274non_windows_platforms := darwin_amd64 darwin_arm64 linux_amd64 linux_arm64
275non_windows_archives := $(non_windows_platforms:%=dist/wazero_$(VERSION)_%.tar.gz)
276windows_platforms := windows_amd64 # TODO: add arm64 windows once we start testing on it.
277windows_archives := $(windows_platforms:%=dist/wazero_$(VERSION)_%.zip) $(windows_platforms:%=dist/wazero_$(VERSION)_%.msi)
278checksum_txt := dist/wazero_$(VERSION)_checksums.txt
279
280# define macros for multi-platform builds. these parse the filename being built
281go-arch = $(if $(findstring amd64,$1),amd64,arm64)
282go-os = $(if $(findstring .exe,$1),windows,$(if $(findstring linux,$1),linux,darwin))
283# msi-arch is a macro so we can detect it based on the file naming convention
284msi-arch = $(if $(findstring amd64,$1),x64,arm64)
285
286build/wazero_%/wazero:
287 $(call go-build,$@,$<)
288
289build/wazero_%/wazero.exe:
290 $(call go-build,$@,$<)
291
292dist/wazero_$(VERSION)_%.tar.gz: build/wazero_%/wazero
293 @echo tar.gz "tarring $@"
294 @mkdir -p $(@D)
295# On Windows, we pass the special flag `--mode='+rx' to ensure that we set the executable flag.
296# This is only supported by GNU Tar, so we set it conditionally.
297 @tar -C $(<D) -cpzf $@ $(if $(findstring Windows_NT,$(OS)),--mode='+rx',) $(<F)
298 @echo tar.gz "ok"
299
300define go-build
301 @echo "building $1"
302 @# $(go:go=) removes the trailing 'go', so we can insert cross-build variables
303 @$(go:go=) CGO_ENABLED=0 GOOS=$(call go-os,$1) GOARCH=$(call go-arch,$1) go build \
304 -ldflags "-s -w -X github.com/tetratelabs/wazero/internal/version.version=$(VERSION)" \
305 -o $1 $2 ./cmd/wazero
306 @echo build "ok"
307endef
308
309# this makes a marker file ending in .signed to avoid repeatedly calling codesign
310%.signed: %
311 $(call codesign,$<)
312 @touch $@
313
314# This requires osslsigncode package (apt or brew) or latest windows release from mtrojnar/osslsigncode
315#
316# Default is self-signed while production should be a Digicert signing key
317#
318# Ex.
319# ```bash
320# keytool -genkey -alias wazero -storetype PKCS12 -keyalg RSA -keysize 2048 -storepass wazero-bunch \
321# -keystore wazero.p12 -dname "O=wazero,CN=wazero.io" -validity 3650
322# ```
323WINDOWS_CODESIGN_P12 ?= packaging/msi/wazero.p12
324WINDOWS_CODESIGN_PASSWORD ?= wazero-bunch
325define codesign
326 @printf "$(ansi_format_dark)" codesign "signing $1"
327 @osslsigncode sign -h sha256 -pkcs12 ${WINDOWS_CODESIGN_P12} -pass "${WINDOWS_CODESIGN_PASSWORD}" \
328 -n "wazero is the zero dependency WebAssembly runtime for Go developers" -i https://wazero.io -t http://timestamp.digicert.com \
329 $(if $(findstring msi,$(1)),-add-msi-dse) -in $1 -out $1-signed
330 @mv $1-signed $1
331 @printf "$(ansi_format_bright)" codesign "ok"
332endef
333
334# This task is only supported on Windows, where we use candle.exe (compile wxs to wixobj) and light.exe (link to msi)
335dist/wazero_$(VERSION)_%.msi: build/wazero_%/wazero.exe.signed
336ifeq ($(OS),Windows_NT)
337 @echo msi "building $@"
338 @mkdir -p $(@D)
339 @candle -nologo -arch $(call msi-arch,$@) -dVersion=$(MSI_VERSION) -dBin=$(<:.signed=) -o build/wazero.wixobj packaging/msi/wazero.wxs
340 @light -nologo -o $@ build/wazero.wixobj -spdb
341 $(call codesign,$@)
342 @echo msi "ok"
343endif
344
345dist/wazero_$(VERSION)_%.zip: build/wazero_%/wazero.exe.signed
346 @echo zip "zipping $@"
347 @mkdir -p $(@D)
348 @zip -qj $@ $(<:.signed=)
349 @echo zip "ok"
350
351# Darwin doesn't have sha256sum. See https://github.com/actions/virtual-environments/issues/90
352sha256sum := $(if $(findstring darwin,$(shell go env GOOS)),shasum -a 256,sha256sum)
353$(checksum_txt):
354 @cd $(@D); touch $(@F); $(sha256sum) * >> $(@F)
355
356dist: $(non_windows_archives) $(if $(findstring Windows_NT,$(OS)),$(windows_archives),) $(checksum_txt)
View as plain text