diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f9beaeb..5a6bb74 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -77,6 +77,57 @@ jobs: run: ./ci/test-minimal-runtime/test.sh if: runner.os != 'Windows' + build-cross: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + goarch: [amd64, arm64, riscv64] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + - run: python ci/download-wasmtime.py + + - uses: actions/setup-go@v2 + with: + go-version: '1.24.x' + + - name: Install cross-compiler (arm64) + if: matrix.goarch == 'arm64' + run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu + + - name: Install zig (riscv64) + if: matrix.goarch == 'riscv64' + uses: mlugg/setup-zig@v2 + with: + version: 0.14.0 + + - name: Build test files for amd64 + if: matrix.goarch == 'amd64' + env: + GOARCH: amd64 + CGO_ENABLED: 1 + run: go build -o /dev/null ./... 2>&1 | head -20 + + - name: Build test files for arm64 + if: matrix.goarch == 'arm64' + env: + GOARCH: arm64 + CGO_ENABLED: 1 + CC: aarch64-linux-gnu-gcc + run: go build -o /dev/null ./... 2>&1 | head -20 + + - name: Build test files for riscv64 + if: matrix.goarch == 'riscv64' + env: + GOARCH: riscv64 + CGO_ENABLED: 1 + CC: zig cc -target riscv64-linux-gnu + run: go build -o /dev/null ./... 2>&1 | head -20 + coverage: runs-on: ubuntu-latest steps: diff --git a/BUILD.bazel b/BUILD.bazel index 3532d60..f2be686 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -8,6 +8,7 @@ cc_library( "@io_bazel_rules_go//go/platform:windows_amd64": ["build/windows-x86_64/libwasmtime.a"], "@io_bazel_rules_go//go/platform:darwin_arm64": ["build/macos-aarch64/libwasmtime.a"], "@io_bazel_rules_go//go/platform:linux_arm64": ["build/linux-aarch64/libwasmtime.a"], + "@io_bazel_rules_go//go/platform:linux_riscv64": ["build/linux-riscv64/libwasmtime.a"], }), hdrs = glob(["build/include/**/*.h"]), includes = ["build/include"], diff --git a/README.md b/README.md index 3e28801..eb01e5c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ ## Installation ```sh -go get -u github.com/bytecodealliance/wasmtime-go/v45@v45.0.1 +go get -u github.com/bytecodealliance/wasmtime-go/v45@v45.0.0 ``` Be sure to check out the [API documentation][api]! @@ -34,8 +34,9 @@ This Go library uses CGO to consume the C API of the [Wasmtime project][wasmtime] which is written in Rust. Precompiled binaries of Wasmtime are checked into this repository on tagged releases so you won't have to install Wasmtime locally, but it means that this project only works on Linux x86\_64, -macOS x86\_64 , and Windows x86\_64 currently. Building on other platforms will -need to arrange to build Wasmtime and use `CGO_*` env vars to compile correctly. +Linux aarch64, Linux riscv64, macOS x86\_64, macOS aarch64, and Windows x86\_64 currently. +Building on other platforms will need to arrange to build Wasmtime and use +`CGO_*` env vars to compile correctly. This project has been tested with Go 1.13 or later. @@ -130,11 +131,11 @@ $ git clone https://github.com/bytecodealliance/wasmtime-go Next up you'll want to have a [local Wasmtime build available](https://bytecodealliance.github.io/wasmtime/contributing-building.html). -You'll need to build at least the `wasmtime-c-api` crate, which, at the time of -this writing, would be: +You'll need to build the `wasmtime-c-api` artifact crate to produce the static +library (`libwasmtime.a`) and shared library (`libwasmtime.so`): ```sh -$ cargo build -p wasmtime-c-api +$ cargo build --release -p wasmtime-c-api --manifest-path crates/c-api/artifact/Cargo.toml ``` Once you've got that you can set up the environment of this library with: @@ -166,7 +167,7 @@ Make sure everything passes at the current version. Next run: ``` -$ git ls-files | xargs sed -i 's/v16/v17/g' +$ git ls-files | xargs sed -i 's/v45/v46/g' $ python3 ci/download-wasmtime.py $ go test ``` diff --git a/ci/download-wasmtime.py b/ci/download-wasmtime.py index a36020a..6fe720c 100644 --- a/ci/download-wasmtime.py +++ b/ci/download-wasmtime.py @@ -22,6 +22,7 @@ ['wasmtime-{}-x86_64-macos-c-api.tar.xz', 'macos-x86_64'], ['wasmtime-{}-aarch64-linux-c-api.tar.xz', 'linux-aarch64'], ['wasmtime-{}-aarch64-macos-c-api.tar.xz', 'macos-aarch64'], + ['wasmtime-{}-riscv64gc-linux-c-api.tar.xz', 'linux-riscv64'], ] try: @@ -49,7 +50,10 @@ z.extractall() else: t = tarfile.open(fileobj=io.BytesIO(contents)) - t.extractall() + if hasattr(tarfile, 'data_filter'): + t.extractall(filter='data') + else: + t.extractall() src = filename.replace('.zip', '').replace('.tar.xz', '') include_src = src + '/min/include' if args.min else src + '/include' diff --git a/ci/local.sh b/ci/local.sh index d211ebe..98e49e4 100755 --- a/ci/local.sh +++ b/ci/local.sh @@ -1,35 +1,53 @@ #!/bin/bash +set -euo pipefail wasmtime=$1 if [ "$wasmtime" = "" ]; then - echo "must pass path to wasmtime" + echo "usage: $0 " exit 1 fi -# Clean and re-create "build" directory hierarchy rm -rf build -for d in "include" "include/wasmtime" "linux-x86_64" "macos-x86_64" "windows-x86_64" "linux-aarch64" "macos-aarch64"; do - path="build/$d" - mkdir -p "$path" - name=$(basename $d) - echo "package ${name/-/_}" > "$path/empty.go" + +# Build the C API with CMake (invokes cargo under the hood) +cmake -S "$wasmtime/crates/c-api" -B "$wasmtime/build-cmake" -DCMAKE_BUILD_TYPE=Release +cmake --build "$wasmtime/build-cmake" +rm -rf "$wasmtime/build-cmake" + +# Create the expected directory structure with empty.go files +for d in "include" "include/wasmtime" "include/wasmtime/component" "include/wasmtime/component/types" "include/wasmtime/types" "linux-x86_64" "macos-x86_64" "windows-x86_64" "linux-aarch64" "macos-aarch64" "linux-riscv64"; do + mkdir -p "build/$d" + name=$(basename "$d" | tr - _) + echo "package $name" > "build/$d/empty.go" done -build="$wasmtime/target/release" -if [ ! -d "$build" ]; then - build="$wasmtime/target/debug" -fi -# Use absolute path for symbolic links -build=$(cd "$build" && pwd) +# Determine host platform +host_os=$(uname -s) +host_arch=$(uname -m) +case "$host_os-$host_arch" in + Linux-x86_64) platform=linux-x86_64; rust_target=x86_64-unknown-linux-gnu ;; + Linux-aarch64) platform=linux-aarch64; rust_target=aarch64-unknown-linux-gnu ;; + Linux-riscv64) platform=linux-riscv64; rust_target=riscv64gc-unknown-linux-gnu ;; + Darwin-x86_64) platform=macos-x86_64; rust_target=x86_64-apple-darwin ;; + Darwin-arm64) platform=macos-aarch64; rust_target=aarch64-apple-darwin ;; + *) echo "Unsupported platform: $host_os-$host_arch"; exit 1 ;; +esac -if [ ! -f "$build/libwasmtime.a" ]; then - echo 'Missing libwasmtime.a. Did you `cargo build -p wasmtime-c-api`?' +# The CMake/cargo build puts artifacts in the wasmtime target directory. +# Native builds go to target/release/, cross builds to target/$rust_target/release/. +wasmtime_target_dir="$wasmtime/target/$rust_target/release" +if [ ! -d "$wasmtime_target_dir" ]; then + wasmtime_target_dir="$wasmtime/target/release" fi -for d in "linux-x86_64" "macos-x86_64" "linux-aarch64" "macos-aarch64"; do - ln -s "$build/libwasmtime.a" "build/$d/libwasmtime.a" -done +# Find the built library (may be libwasmtime.a or wasmtime.lib) +built_lib=$(find "$wasmtime_target_dir" -maxdepth 1 -name "libwasmtime*.a" -o -name "wasmtime.lib" | head -1) +if [ -z "$built_lib" ]; then + echo "Failed to find built wasmtime library in $wasmtime_target_dir" + exit 1 +fi +ln -s "$built_lib" "build/$platform/libwasmtime.a" -cp "$wasmtime"/crates/c-api/include/*.h build/include -cp -r "$wasmtime"/crates/c-api/include/wasmtime build/include -cp "$wasmtime"/crates/c-api/wasm-c-api/include/*.h build/include +# Copy headers +cp "$wasmtime/crates/c-api/include/"*.h build/include/ +cp -r "$wasmtime/crates/c-api/include/wasmtime" build/include/ diff --git a/ci/test-minimal-runtime/test.sh b/ci/test-minimal-runtime/test.sh index aa24295..41be8e9 100755 --- a/ci/test-minimal-runtime/test.sh +++ b/ci/test-minimal-runtime/test.sh @@ -1,17 +1,29 @@ #!/usr/bin/env bash set -euo pipefail -cd "$(dirname "$0")" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +WASMTIME_GO="$(cd "$SCRIPT_DIR/../.." && pwd)" +DOWNLOAD_SCRIPT="$WASMTIME_GO/ci/download-wasmtime.py" + +# Step 1: Ensure the local wasmtime-go build directory has the v45 full libraries. +# The local build may contain libraries built from a different wasmtime version +# (e.g., a newer version built from local Rust source), which causes serialization format mismatches. +# We temporarily replace them with the official v45 release binaries. trap 'rm -rf vendor module.cwasm' EXIT +( + cd "$WASMTIME_GO" + python3 "$DOWNLOAD_SCRIPT" +) -# Step 1: Create a pre-compiled module using the full Wasmtime library. +# Step 2: Create a pre-compiled module using the full Wasmtime library. go run create_cwasm.go -# Step 2: Vendor the module, then adjust the vendored copy so it compiles +# Step 3: Vendor the module, then adjust the vendored copy so it compiles # against the minimal Wasmtime library: # a) Remove Go source files that call C functions absent from the min binary. # b) Download the min static libraries over the full ones so CGO links the right lib. -DOWNLOAD_SCRIPT="$(pwd)/../download-wasmtime.py" go mod vendor ( cd vendor/github.com/bytecodealliance/wasmtime-go/v45 @@ -19,5 +31,5 @@ go mod vendor python3 "$DOWNLOAD_SCRIPT" --min ) -# Step 3: Test that the minimal Wasmtime binary can deserialize and run a module. +# Step 4: Test that the minimal Wasmtime binary can deserialize and run a module. go test -count=1 . diff --git a/ffi.go b/ffi.go index 7d47d7b..f021b9c 100644 --- a/ffi.go +++ b/ffi.go @@ -1,13 +1,14 @@ package wasmtime // #cgo CFLAGS:-I${SRCDIR}/build/include -// #cgo !windows LDFLAGS:-lwasmtime -lm -ldl -pthread +// #cgo !windows LDFLAGS:-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 linux,amd64 LDFLAGS:-L${SRCDIR}/build/linux-x86_64 -lwasmtime -lm +// #cgo linux,arm64 LDFLAGS:-L${SRCDIR}/build/linux-aarch64 -lwasmtime -lm +// #cgo linux,riscv64 LDFLAGS:-L${SRCDIR}/build/linux-riscv64 -lwasmtime -lm +// #cgo darwin,amd64 LDFLAGS:-L${SRCDIR}/build/macos-x86_64 -lwasmtime +// #cgo darwin,arm64 LDFLAGS:-L${SRCDIR}/build/macos-aarch64 -lwasmtime // #cgo windows,amd64 LDFLAGS:-L${SRCDIR}/build/windows-x86_64 // #include import "C" diff --git a/includebuild.go b/includebuild.go index e20726e..c342cb9 100644 --- a/includebuild.go +++ b/includebuild.go @@ -18,6 +18,7 @@ import ( _ "github.com/bytecodealliance/wasmtime-go/v45/build/include/wasmtime/component/types" _ "github.com/bytecodealliance/wasmtime-go/v45/build/include/wasmtime/types" _ "github.com/bytecodealliance/wasmtime-go/v45/build/linux-aarch64" + _ "github.com/bytecodealliance/wasmtime-go/v45/build/linux-riscv64" _ "github.com/bytecodealliance/wasmtime-go/v45/build/linux-x86_64" _ "github.com/bytecodealliance/wasmtime-go/v45/build/macos-aarch64" _ "github.com/bytecodealliance/wasmtime-go/v45/build/macos-x86_64"