Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions experiments/001_hello_world/lib
2 changes: 1 addition & 1 deletion experiments/002_chromium_sandbox/lib
2 changes: 1 addition & 1 deletion experiments/003_wasm_compile/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ python-raw/wit_world/
python-raw/componentize_py_types.py
python-raw/componentize_py_async_support/
python-raw/poll_loop.py
rust/target/
rust-hello/target/
as-hello/node_modules/
as-hello/build/
__pycache__/
Expand Down
46 changes: 33 additions & 13 deletions experiments/003_wasm_compile/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
.PHONY: deps build test bench bench-quick clean help
.PHONY: deps build build-js build-py build-rust build-as test bench bench-quick clean help

CONTAINER_CMD ?= $(shell command -v podman 2>/dev/null || echo docker)
VENV := .venv
COMPONENTIZE := $(VENV)/bin/componentize-py
GIT_ROOT := $(shell git rev-parse --show-toplevel)
VENV := $(GIT_ROOT)/.venv
ACTIVATE := . $(VENV)/bin/activate &&
BUILD_DIR := build

$(VENV)/bin/activate: requirements.txt
python3 -m venv $(VENV)
$(VENV)/bin/pip install --quiet -r requirements.txt
uv venv --clear $(VENV)
uv pip install --quiet -r requirements.txt
@touch $(VENV)/bin/activate

deps: $(VENV)/bin/activate ## Install toolchain dependencies (spin, componentize-py, hey)
Expand All @@ -16,34 +18,52 @@ deps: $(VENV)/bin/activate ## Install toolchain dependencies (spin, componentiz
brew install hey
@echo "✓ deps ready (componentize-py in $(VENV); JS deps fetched at build time by spin build)"

build: $(VENV)/bin/activate ## Compile all sources to .wasm
build: build-js build-py build-rust build-as ## Compile all sources to .wasm → build/
@echo "✓ All .wasm artifacts in $(BUILD_DIR)/"

build-js: ## Build JS → .wasm (Spin)
@echo "→ Building JS (Spin)..."
cd js-spin && spin build
@mkdir -p $(BUILD_DIR)
cp js-spin/dist/hello-js.wasm $(BUILD_DIR)/hello-js-spin.wasm

build-py: $(VENV)/bin/activate ## Build Python → .wasm (componentize-py + Spin)
@mkdir -p $(BUILD_DIR)
@echo "→ Building Python/raw (componentize-py)..."
cd python-raw && ../$(COMPONENTIZE) -d wit -w proxy componentize app -o hello-py-raw.wasm
cd python-raw && VIRTUAL_ENV= $(VENV)/bin/componentize-py -d wit -w example:hello/proxy --all-features componentize app -o hello-py-raw.wasm
cp python-raw/hello-py-raw.wasm $(BUILD_DIR)/hello-py-raw.wasm
@echo "→ Building Python (Spin)..."
cd python-spin && spin build
cd python-spin && $(ACTIVATE) spin build
cp python-spin/app.wasm $(BUILD_DIR)/hello-py-spin.wasm

build-rust: ## Build Rust → .wasm (cargo wasm32-wasip2)
@echo "→ Building Rust baseline..."
cd rust && cargo build --target wasm32-wasip2 --release --quiet
cd rust-hello && cargo build --target wasm32-wasip2 --release --quiet
@mkdir -p $(BUILD_DIR)
cp rust-hello/target/wasm32-wasip2/release/leg3.wasm $(BUILD_DIR)/hello-rust.wasm

build-as: ## Build AssemblyScript → .wasm (asc + wasm-tools)
@echo "→ Building AssemblyScript (asc + wasm-tools)..."
cd as-hello && npm ci --silent 2>/dev/null && ./build.sh
@echo "✓ All .wasm artifacts built"
cd as-hello && npm ci --silent && ./build.sh
@mkdir -p $(BUILD_DIR)
cp as-hello/build/hello-as.wasm $(BUILD_DIR)/hello-as.wasm

test: ## Run BATS unit tests
bats tests/

bench: ## Run full 6-leg benchmark
bench: ## Run full 7-leg benchmark
./benchmark.sh

bench-quick: ## Quick benchmark (10 requests)
HEY_N=10 ./benchmark.sh

clean: ## Remove build artifacts
rm -rf $(BUILD_DIR)
rm -rf $(VENV)
rm -rf js-spin/node_modules js-spin/build js-spin/dist
rm -f python-spin/app.wasm
rm -f python-raw/hello-py-raw.wasm
rm -rf rust/target
rm -rf rust-hello/target
rm -rf as-hello/node_modules as-hello/build
$(CONTAINER_CMD) rmi -f hello-js-spin hello-py-spin 2>/dev/null || true

Expand Down
2 changes: 1 addition & 1 deletion experiments/003_wasm_compile/benchmark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ run_leg3() {
require_port_free 5035 "Leg 3"
command -v cargo &>/dev/null || fail "cargo not found"

pushd "$SCRIPT_DIR/rust" >/dev/null
pushd "$SCRIPT_DIR/rust-hello" >/dev/null
APP_3=$(human_size src/lib.rs)
BUILD_3=$(timed_build "cargo wasm32-wasip2" \
cargo build --target wasm32-wasip2 --release --quiet 2>/dev/null)
Expand Down
2 changes: 1 addition & 1 deletion experiments/003_wasm_compile/lib
28 changes: 14 additions & 14 deletions experiments/003_wasm_compile/python-spin/app.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import json
import time

from spin_sdk import http
from spin_sdk.http import Request, Response
from spin_sdk.http import IncomingHandler, Request, Response


def handle_request(request: Request) -> Response:
body = json.dumps(
{
"message": "Hello World",
"timestamp": time.time(),
}
)
return Response(
200,
{"content-type": "application/json"},
bytes(body, "utf-8"),
)
class IncomingHandler(IncomingHandler):
def handle_request(self, request: Request) -> Response:
body = json.dumps(
{
"message": "Hello World",
"timestamp": time.time(),
}
)
return Response(
200,
{"content-type": "application/json"},
bytes(body, "utf-8"),
)
2 changes: 1 addition & 1 deletion experiments/003_wasm_compile/python-spin/spin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ source = "app.wasm"
allowed_outbound_hosts = []

[component.hello-py.build]
command = "../.venv/bin/componentize-py -d wit -w proxy componentize app -o app.wasm"
command = "componentize-py -w spin-http componentize app -o app.wasm"
watch = ["app.py"]
3 changes: 2 additions & 1 deletion experiments/003_wasm_compile/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
componentize-py
componentize-py>=0.17,<0.18
spin-sdk>=3.4,<4
1 change: 0 additions & 1 deletion experiments/003_wasm_compile/rust

This file was deleted.

43 changes: 43 additions & 0 deletions experiments/003_wasm_compile/rust-hello/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions experiments/003_wasm_compile/rust-hello/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "leg3"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasi = "0.14"
49 changes: 49 additions & 0 deletions experiments/003_wasm_compile/rust-hello/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail

PORT=5003
DIR="$(cd "$(dirname "$0")" && pwd)"

# Ensure Rust/cargo is on PATH — respects CARGO_HOME (XDG-compliant)
export PATH="${CARGO_HOME:-${XDG_DATA_HOME:-$HOME/.local/share}/cargo}/bin:$PATH"

command -v cargo &>/dev/null || { echo "✗ cargo not found — install rustup" >&2; exit 1; }
command -v wasmtime &>/dev/null || { echo "✗ wasmtime not found — brew install wasmtime" >&2; exit 1; }

# Ensure wasm32-wasip2 target is available
if ! rustup target list --installed 2>/dev/null | grep -q "wasm32-wasip2"; then
echo "→ Adding wasm32-wasip2 Rust target..."
rustup target add wasm32-wasip2
fi

cd "$DIR"

echo "→ Building Rust WASM component (wasm32-wasip2)..."
cargo build --target wasm32-wasip2 --release 2>&1

WASM=$(find target/wasm32-wasip2/release -maxdepth 1 -name "*.wasm" | head -1)
if [ -z "$WASM" ]; then
echo "✗ No .wasm file found in target/wasm32-wasip2/release/" >&2
exit 1
fi
echo "→ Binary: $WASM ($(du -sh "$WASM" | cut -f1))"

echo "→ Starting wasmtime serve on port $PORT..."
wasmtime serve -S cli --addr "127.0.0.1:$PORT" "$WASM" &
WASMTIME_PID=$!

# Wait for readiness
echo -n "→ Waiting for HTTP..."
for i in $(seq 1 30); do
if curl -sf "http://127.0.0.1:$PORT/" &>/dev/null; then
echo " ready"
curl -s "http://127.0.0.1:$PORT/" | python3 -m json.tool
wait "$WASMTIME_PID"
exit 0
fi
sleep 0.2
done

echo " timeout" >&2
kill "$WASMTIME_PID" 2>/dev/null || true
exit 1
33 changes: 33 additions & 0 deletions experiments/003_wasm_compile/rust-hello/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use wasi::clocks::wall_clock;
use wasi::exports::wasi::http::incoming_handler::Guest;
use wasi::http::types::{Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam};

struct Component;

impl Guest for Component {
fn handle(_request: IncomingRequest, response_out: ResponseOutparam) {
let now = wall_clock::now();
let ts = now.seconds as f64 + (now.nanoseconds as f64 / 1_000_000_000.0);
let body_str = format!(r#"{{"message":"Hello World","timestamp":{ts:.6}}}"#);

let headers = Fields::new();
headers
.append(&"content-type".to_string(), &b"application/json".to_vec())
.unwrap();

let response = OutgoingResponse::new(headers);
response.set_status_code(200).unwrap();

let out_body = response.body().unwrap();
{
let stream = out_body.write().unwrap();
stream
.blocking_write_and_flush(body_str.as_bytes())
.unwrap();
}
OutgoingBody::finish(out_body, None).unwrap();
ResponseOutparam::set(response_out, Ok(response));
}
}

wasi::http::proxy::export!(Component with_types_in wasi);
Loading