Makefile

  1
  2gofumpt       := mvdan.cc/gofumpt@v0.6.0
  3gosimports    := github.com/rinchsan/gosimports/cmd/gosimports@v0.3.8
  4golangci_lint := github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.5
  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
 23.PHONY: test.examples
 24test.examples:
 25	@go test $(go_test_options) ./examples/... ./imports/assemblyscript/example/... ./imports/emscripten/... ./imports/wasi_snapshot_preview1/example/...
 26
 27.PHONY: build.examples.as
 28build.examples.as:
 29	@cd ./imports/assemblyscript/example/testdata && npm install && npm run build
 30
 31%.wasm: %.zig
 32	@(cd $(@D); zig build -Doptimize=ReleaseSmall)
 33	@mv $(@D)/zig-out/*/$(@F) $(@D)
 34
 35.PHONY: build.examples.zig
 36build.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
 37	@cd internal/testing/dwarftestdata/testdata/zig; zig build; mv zig-out/*/main.wasm ./ # Need DWARF custom sections.
 38
 39tinygo_reactor_sources_reactor := examples/basic/testdata/add.go examples/allocation/tinygo/testdata/greet.go
 40.PHONY: build.examples.tinygo_reactor
 41build.examples.tinygo_reactor: $(tinygo_sources_reactor)
 42	@for f in $^; do \
 43	    tinygo build -o $$(echo $$f | sed -e 's/\.go/\.wasm/') -scheduler=none --no-debug --target=wasip1 -buildmode=c-shared $$f; \
 44	done
 45
 46tinygo_sources_clis := examples/cli/testdata/cli.go imports/wasi_snapshot_preview1/example/testdata/tinygo/cat.go imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go cmd/wazero/testdata/cat/cat.go
 47.PHONY: build.examples.tinygo_clis
 48build.examples.tinygo_clis: $(tinygo_sources_clis)
 49	@for f in $^; do \
 50	    tinygo build -o $$(echo $$f | sed -e 's/\.go/\.wasm/') -scheduler=none --no-debug --target=wasip1 $$f; \
 51	done
 52	@mv cmd/wazero/testdata/cat/cat.wasm cmd/wazero/testdata/cat/cat-tinygo.wasm
 53
 54.PHONY: build.examples.tinygo
 55build.examples.tinygo: build.examples.tinygo_reactor build.examples.tinygo_clis
 56
 57# We use zig to build C as it is easy to install and embeds a copy of zig-cc.
 58# Note: Don't use "-Oz" as that breaks our wasi sock example.
 59c_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
 60.PHONY: build.examples.zig-cc
 61build.examples.zig-cc: $(c_sources)
 62	@for f in $^; do \
 63	    zig cc --target=wasm32-wasi -o $$(echo $$f | sed -e 's/\.c/\.wasm/') $$f; \
 64	done
 65
 66# Here are the emcc args we use:
 67#
 68# * `-Oz` - most optimization for code size.
 69# * `--profiling` - adds the name section.
 70# * `-s STANDALONE_WASM` - ensures wasm is built for a non-js runtime.
 71# * `-s EXPORTED_FUNCTIONS=_malloc,_free` - export allocation functions so that
 72#   they can be used externally as "malloc" and "free".
 73# * `-s WARN_ON_UNDEFINED_SYMBOLS=0` - imports not defined in JavaScript error
 74#   otherwise. See https://github.com/emscripten-core/emscripten/issues/13641
 75# * `-s TOTAL_STACK=8KB -s TOTAL_MEMORY=64KB` - reduce memory default from 16MB
 76#   to one page (64KB). To do this, we have to reduce the stack size.
 77# * `-s ALLOW_MEMORY_GROWTH` - allows "memory.grow" instructions to succeed, but
 78#   requires a function import "emscripten_notify_memory_growth".
 79emscripten_sources := $(wildcard imports/emscripten/testdata/*.cc)
 80.PHONY: build.examples.emscripten
 81build.examples.emscripten: $(emscripten_sources)
 82	@for f in $^; do \
 83		em++ -Oz --profiling \
 84		-s STANDALONE_WASM \
 85		-s EXPORTED_FUNCTIONS=_malloc,_free \
 86		-s WARN_ON_UNDEFINED_SYMBOLS=0 \
 87		-s TOTAL_STACK=8KB -s TOTAL_MEMORY=64KB \
 88		-s ALLOW_MEMORY_GROWTH \
 89		--std=c++17 -o $$(echo $$f | sed -e 's/\.cc/\.wasm/') $$f; \
 90	done
 91
 92%/greet.wasm : cargo_target := wasm32-unknown-unknown
 93%/cat.wasm : cargo_target := wasm32-wasip1
 94%/wasi.wasm : cargo_target := wasm32-wasip1
 95
 96.PHONY: build.examples.rust
 97build.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
 98
 99# Normally, we build release because it is smaller. Testing dwarf requires the debug build.
100internal/testing/dwarftestdata/testdata/rust/main.wasm.xz:
101	cd $(@D) && cargo build --target wasm32-wasip1
102	mv $(@D)/target/wasm32-wasip1/debug/main.wasm $(@D)
103	cd $(@D) && xz -k -f ./main.wasm # Rust's DWARF section is huge, so compress it.
104
105# Builds rust using cargo normally
106%.wasm: %.rs
107	@(cd $(@D); cargo build --target $(cargo_target) --release)
108	@mv $(@D)/target/$(cargo_target)/release/$(@F) $(@D)
109
110spectest_base_dir := internal/integration_test/spectest
111spectest_v1_dir := $(spectest_base_dir)/v1
112spectest_v1_testdata_dir := $(spectest_v1_dir)/testdata
113spec_version_v1 := wg-1.0
114spectest_v2_dir := $(spectest_base_dir)/v2
115spectest_v2_testdata_dir := $(spectest_v2_dir)/testdata
116# Latest draft state as of March 12, 2024.
117spec_version_v2 := 1c5e5d178bd75c79b7a12881c529098beaee2a05
118spectest_threads_dir := $(spectest_base_dir)/threads
119spectest_threads_testdata_dir := $(spectest_threads_dir)/testdata
120# From https://github.com/WebAssembly/threads/tree/upstream-rebuild which has not been merged to main yet.
121# It will likely be renamed to main in the future - https://github.com/WebAssembly/threads/issues/216.
122spec_version_threads := 3635ca51a17e57e106988846c5b0e0cc48ac04fc
123
124.PHONY: build.spectest
125build.spectest:
126	@$(MAKE) build.spectest.v1
127	@$(MAKE) build.spectest.v2
128
129.PHONY: build.spectest.v1
130build.spectest.v1: # Note: wabt by default uses >1.0 features, so wast2json flags might drift as they include more. See WebAssembly/wabt#1878
131	@rm -rf $(spectest_v1_testdata_dir)
132	@mkdir -p $(spectest_v1_testdata_dir)
133	@cd $(spectest_v1_testdata_dir) \
134		&& 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
135	@cd $(spectest_v1_testdata_dir) && for f in `find . -name '*.wast'`; do \
136		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; \
137		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; \
138		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; \
139		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; \
140		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; \
141		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; \
142		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; \
143		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; \
144		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; \
145		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; \
146		wast2json \
147			--disable-saturating-float-to-int \
148			--disable-sign-extension \
149			--disable-simd \
150			--disable-multi-value \
151			--disable-bulk-memory \
152			--disable-reference-types \
153			--debug-names $$f; \
154	done
155
156.PHONY: build.spectest.v2
157build.spectest.v2: # Note: SIMD cases are placed in the "simd" subdirectory.
158	@mkdir -p $(spectest_v2_testdata_dir)
159	@cd $(spectest_v2_testdata_dir) \
160		&& 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
161	@cd $(spectest_v2_testdata_dir) \
162		&& 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
163	@cd $(spectest_v2_testdata_dir) && for f in `find . -name '*.wast'`; do \
164		wast2json --debug-names --no-check $$f || true; \
165	done # Ignore the error here as some tests (e.g. comments.wast right now) are not supported by wast2json yet.
166
167# Note: We currently cannot build the "threads" subdirectory that spawns threads due to missing support in wast2json.
168# https://github.com/WebAssembly/wabt/issues/2348#issuecomment-1878003959
169.PHONY: build.spectest.threads
170build.spectest.threads:
171	@mkdir -p $(spectest_threads_testdata_dir)
172	@cd $(spectest_threads_testdata_dir) \
173		&& curl -sSL 'https://api.github.com/repos/WebAssembly/threads/contents/test/core?ref=$(spec_version_threads)' | jq -r '.[]| .download_url' | grep -E "atomic.wast" | xargs -Iurl curl -sJL url -O
174	@cd $(spectest_threads_testdata_dir) && for f in `find . -name '*.wast'`; do \
175		wast2json --enable-threads --debug-names $$f; \
176	done
177
178.PHONY: test
179test:
180	@go test $(go_test_options) ./...
181	@cd internal/version/testdata && go test $(go_test_options) ./...
182	@cd internal/integration_test/fuzz/wazerolib && CGO_ENABLED=0 WASM_BINARY_PATH=testdata/test.wasm go test ./...
183
184.PHONY: coverage
185# replace spaces with commas
186coverpkg = $(shell echo $(main_packages) | tr ' ' ',')
187coverage: ## Generate test coverage
188	@go test -coverprofile=coverage.txt -covermode=atomic --coverpkg=$(coverpkg) $(main_packages)
189	@go tool cover -func coverage.txt
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 -E testableexamples
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 wasip1. 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_default_flags := --no-trace-compares --sanitizer=none -- -rss_limit_mb=8192
259
260fuzz_timeout_seconds ?= 10
261.PHONY: fuzz
262fuzz:
263	@cd internal/integration_test/fuzz && cargo test
264	@cd internal/integration_test/fuzz && cargo fuzz run logging_no_diff $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
265	@cd internal/integration_test/fuzz && cargo fuzz run no_diff $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
266	@cd internal/integration_test/fuzz && cargo fuzz run memory_no_diff $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
267	@cd internal/integration_test/fuzz && cargo fuzz run validation $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
268
269libsodium:
270	cd ./internal/integration_test/libsodium/testdata && \
271		curl -s "https://api.github.com/repos/jedisct1/webassembly-benchmarks/contents/2022-12/wasm?ref=7e86d68e99e60130899fbe3b3ab6e9dce9187a7c" \
272		| jq -r '.[] | .download_url' | xargs -n 1 curl -LO
273
274#### CLI release related ####
275
276VERSION ?= dev
277# Default to a dummy version 0.0.1.1, which is always lower than a real release.
278# Legal version values should look like 'x.x.x.x' where x is an integer from 0 to 65534.
279# https://learn.microsoft.com/en-us/windows/win32/msi/productversion?redirectedfrom=MSDN
280# https://stackoverflow.com/questions/9312221/msi-version-numbers
281MSI_VERSION ?= 0.0.1.1
282non_windows_platforms := darwin_amd64 darwin_arm64 linux_amd64 linux_arm64
283non_windows_archives  := $(non_windows_platforms:%=dist/wazero_$(VERSION)_%.tar.gz)
284windows_platforms     := windows_amd64 # TODO: add arm64 windows once we start testing on it.
285windows_archives      := $(windows_platforms:%=dist/wazero_$(VERSION)_%.zip) $(windows_platforms:%=dist/wazero_$(VERSION)_%.msi)
286checksum_txt          := dist/wazero_$(VERSION)_checksums.txt
287
288# define macros for multi-platform builds. these parse the filename being built
289go-arch = $(if $(findstring amd64,$1),amd64,arm64)
290go-os   = $(if $(findstring .exe,$1),windows,$(if $(findstring linux,$1),linux,darwin))
291# msi-arch is a macro so we can detect it based on the file naming convention
292msi-arch     = $(if $(findstring amd64,$1),x64,arm64)
293
294build/wazero_%/wazero:
295	$(call go-build,$@,$<)
296
297build/wazero_%/wazero.exe:
298	$(call go-build,$@,$<)
299
300dist/wazero_$(VERSION)_%.tar.gz: build/wazero_%/wazero
301	@echo tar.gz "tarring $@"
302	@mkdir -p $(@D)
303# On Windows, we pass the special flag `--mode='+rx' to ensure that we set the executable flag.
304# This is only supported by GNU Tar, so we set it conditionally.
305	@tar -C $(<D) -cpzf $@ $(if $(findstring Windows_NT,$(OS)),--mode='+rx',) $(<F)
306	@echo tar.gz "ok"
307
308define go-build
309	@echo "building $1"
310	@# $(go:go=) removes the trailing 'go', so we can insert cross-build variables
311	@$(go:go=) CGO_ENABLED=0 GOOS=$(call go-os,$1) GOARCH=$(call go-arch,$1) go build \
312		-ldflags "-s -w -X github.com/tetratelabs/wazero/internal/version.version=$(VERSION)" \
313		-o $1 $2 ./cmd/wazero
314	@echo build "ok"
315endef
316
317# this makes a marker file ending in .signed to avoid repeatedly calling codesign
318%.signed: %
319	$(call codesign,$<)
320	@touch $@
321
322# This requires osslsigncode package (apt or brew) or latest windows release from mtrojnar/osslsigncode
323#
324# Default is self-signed while production should be a Digicert signing key
325#
326# Ex.
327# ```bash
328# keytool -genkey -alias wazero -storetype PKCS12 -keyalg RSA -keysize 2048 -storepass wazero-bunch \
329# -keystore wazero.p12 -dname "O=wazero,CN=wazero.io" -validity 3650
330# ```
331WINDOWS_CODESIGN_P12      ?= packaging/msi/wazero.p12
332WINDOWS_CODESIGN_PASSWORD ?= wazero-bunch
333define codesign
334	@printf "$(ansi_format_dark)" codesign "signing $1"
335	@osslsigncode sign -h sha256 -pkcs12 ${WINDOWS_CODESIGN_P12} -pass "${WINDOWS_CODESIGN_PASSWORD}" \
336	-n "wazero is the zero dependency WebAssembly runtime for Go developers" -i https://wazero.io -t http://timestamp.digicert.com \
337	$(if $(findstring msi,$(1)),-add-msi-dse) -in $1 -out $1-signed
338	@mv $1-signed $1
339	@printf "$(ansi_format_bright)" codesign "ok"
340endef
341
342# This task is only supported on Windows, where we use candle.exe (compile wxs to wixobj) and light.exe (link to msi)
343dist/wazero_$(VERSION)_%.msi: build/wazero_%/wazero.exe.signed
344ifeq ($(OS),Windows_NT)
345	@echo msi "building $@"
346	@mkdir -p $(@D)
347	@candle -nologo -arch $(call msi-arch,$@) -dVersion=$(MSI_VERSION) -dBin=$(<:.signed=) -o build/wazero.wixobj packaging/msi/wazero.wxs
348	@light -nologo -o $@ build/wazero.wixobj -spdb
349	$(call codesign,$@)
350	@echo msi "ok"
351endif
352
353dist/wazero_$(VERSION)_%.zip: build/wazero_%/wazero.exe.signed
354	@echo zip "zipping $@"
355	@mkdir -p $(@D)
356	@zip -qj $@ $(<:.signed=)
357	@echo zip "ok"
358
359# Darwin doesn't have sha256sum. See https://github.com/actions/virtual-environments/issues/90
360sha256sum := $(if $(findstring darwin,$(shell go env GOOS)),shasum -a 256,sha256sum)
361$(checksum_txt):
362	@cd $(@D); touch $(@F); $(sha256sum) * >> $(@F)
363
364dist: $(non_windows_archives) $(if $(findstring Windows_NT,$(OS)),$(windows_archives),) $(checksum_txt)