Skip to content

Commit ca0ad88

Browse files
committed
Add wasm side-module build for wp_mysql_parser and CI to gate it
Spike confirms the Rust extension can be compiled to a PHP-wasm side module via @php-wasm/compile-extension and loaded into Playground. Build recipe and findings live in wasm-spike/RESULT.md; the new GitHub Actions workflow runs the build against wordpress-playground trunk on every PR that touches the extension and verifies the produced .so loads and parses a query. The workflow currently fails on a Playground-side blocker (zend_ce_traversable not exported from PHP-wasm's MAIN_MODULE=2 export set). RESULT.md documents the blocker and the proposed fix in WordPress/wordpress-playground.
1 parent cba475a commit ca0ad88

9 files changed

Lines changed: 710 additions & 0 deletions

File tree

.github/workflows/wasm-spike.yml

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
name: WASM extension build
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'packages/php-ext-wp-mysql-parser/**'
9+
- '.github/workflows/wasm-spike.yml'
10+
pull_request:
11+
paths:
12+
- 'packages/php-ext-wp-mysql-parser/**'
13+
- '.github/workflows/wasm-spike.yml'
14+
workflow_dispatch:
15+
inputs:
16+
playground-ref:
17+
description: 'wordpress-playground branch/tag/SHA to build against'
18+
required: false
19+
default: 'trunk'
20+
21+
jobs:
22+
build-and-load:
23+
name: Build wp_mysql_parser.so and load it in Playground (PHP ${{ matrix.php }})
24+
runs-on: ubuntu-latest
25+
timeout-minutes: 45
26+
strategy:
27+
fail-fast: false
28+
matrix:
29+
php: ['8.4']
30+
async-mode: ['jspi']
31+
32+
steps:
33+
- name: Check out sqlite-database-integration
34+
uses: actions/checkout@v4
35+
with:
36+
path: sqlite-database-integration
37+
38+
- name: Check out wordpress-playground
39+
uses: actions/checkout@v4
40+
with:
41+
repository: WordPress/wordpress-playground
42+
ref: ${{ github.event.inputs.playground-ref || 'trunk' }}
43+
path: wordpress-playground
44+
45+
- name: Set up Node
46+
uses: actions/setup-node@v4
47+
with:
48+
node-version: '22'
49+
cache: 'npm'
50+
cache-dependency-path: wordpress-playground/package-lock.json
51+
52+
- name: Install Playground deps
53+
working-directory: wordpress-playground
54+
run: npm ci
55+
56+
- name: Build @php-wasm/compile-extension base image
57+
working-directory: wordpress-playground
58+
run: |
59+
npx @php-wasm/compile-extension --help >/dev/null 2>&1 || true
60+
docker images | grep playground-php-wasm || true
61+
62+
- name: Build wp_mysql_parser side module
63+
working-directory: sqlite-database-integration/packages/php-ext-wp-mysql-parser/wasm-spike
64+
env:
65+
PHP_VERSION: ${{ matrix.php }}
66+
ASYNC_MODE: ${{ matrix.async-mode }}
67+
run: bash build-in-docker-rust.sh
68+
69+
- name: Verify build artifacts exist
70+
working-directory: sqlite-database-integration/packages/php-ext-wp-mysql-parser/wasm-spike
71+
run: |
72+
test -f dist/wp_mysql_parser-php${{ matrix.php }}-${{ matrix.async-mode }}.so
73+
test -f dist/manifest.json
74+
test -f dist/libwp_mysql_parser.a
75+
ls -lh dist/
76+
77+
- name: Verify manifest sha256 matches the .so byte-for-byte
78+
working-directory: sqlite-database-integration/packages/php-ext-wp-mysql-parser/wasm-spike
79+
run: |
80+
MANIFEST_SHA=$(node -e "console.log(require('./dist/manifest.json').extensions[0].artifacts[0].sha256)")
81+
ACTUAL_SHA=$(sha256sum dist/wp_mysql_parser-php${{ matrix.php }}-${{ matrix.async-mode }}.so | cut -d' ' -f1)
82+
echo "manifest: $MANIFEST_SHA"
83+
echo "actual: $ACTUAL_SHA"
84+
test "$MANIFEST_SHA" = "$ACTUAL_SHA"
85+
86+
- name: Load the extension in Playground and parse a query
87+
working-directory: sqlite-database-integration/packages/php-ext-wp-mysql-parser/wasm-spike
88+
env:
89+
PLAYGROUND_REPO: ${{ github.workspace }}/wordpress-playground
90+
run: node run-spike.mjs
91+
92+
- name: Upload artifacts on success
93+
if: success()
94+
uses: actions/upload-artifact@v4
95+
with:
96+
name: wp_mysql_parser-php${{ matrix.php }}-${{ matrix.async-mode }}
97+
path: sqlite-database-integration/packages/php-ext-wp-mysql-parser/wasm-spike/dist/
98+
if-no-files-found: error
99+
retention-days: 14
100+
101+
- name: Upload diagnostic logs on failure
102+
if: failure()
103+
uses: actions/upload-artifact@v4
104+
with:
105+
name: wasm-spike-failure-php${{ matrix.php }}-${{ matrix.async-mode }}
106+
path: |
107+
sqlite-database-integration/packages/php-ext-wp-mysql-parser/wasm-spike/dist/
108+
sqlite-database-integration/packages/php-ext-wp-mysql-parser/wasm-spike/*.log
109+
if-no-files-found: ignore
110+
retention-days: 14
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist/
2+
*.log
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Layer that adds rustup + the wasm32-unknown-emscripten target on top of
2+
# Playground's compile-extension image.
3+
#
4+
# Base image is the one produced by `@php-wasm/compile-extension`'s
5+
# Dockerfile.ext, tagged as
6+
# `playground-php-wasm:compile-extension-php<MAJOR-MINOR>-<ASYNC>`. It
7+
# already contains emsdk and a wasm-targeted PHP install with headers under
8+
# /usr/local/include/php and a matching `php-config` on PATH — exactly what
9+
# ext-php-rs's bindgen step needs to succeed.
10+
#
11+
# Build with:
12+
# docker build \
13+
# --build-arg BASE_IMAGE=playground-php-wasm:compile-extension-php8-4-jspi \
14+
# -t playground-php-wasm-ext-rust:8.4-jspi \
15+
# -f Dockerfile.rust .
16+
17+
ARG BASE_IMAGE=playground-php-wasm:compile-extension-php8-4-jspi
18+
19+
# Stage providing a host PHP 8.4 CLI binary. ext-php-rs's build.rs shells
20+
# out to `php` to detect the PHP version (PHP_VERSION_ID drives the cfg
21+
# flags php80..php85). The wasm-targeted PHP install in /usr/local has no
22+
# CLI binary (--disable-cli), Ubuntu noble ships only PHP 8.3, and
23+
# packages.sury.org blocks our datacenter IP, so we steal a CLI from the
24+
# official php:8.4-cli image.
25+
FROM php:8.4-cli AS host-php-cli
26+
27+
FROM ${BASE_IMAGE}
28+
29+
COPY --from=host-php-cli /usr/local/bin/php /opt/host-php/bin/php
30+
COPY --from=host-php-cli /usr/local/lib/php /opt/host-php/lib/php
31+
COPY --from=host-php-cli /usr/local/etc/php /opt/host-php/etc/php
32+
COPY --from=host-php-cli /usr/local/include/php /opt/host-php/include/php
33+
34+
ENV RUSTUP_HOME=/root/rustup \
35+
CARGO_HOME=/root/cargo \
36+
PATH=/root/cargo/bin:/root/rustup/bin:/usr/local/bin:/usr/bin:/bin
37+
38+
RUN apt-get update && apt-get install -y --no-install-recommends \
39+
curl ca-certificates build-essential \
40+
libclang-dev clang llvm-dev pkg-config libxml2 libonig5 libsqlite3-0 \
41+
libargon2-1 libssl3 zlib1g libreadline8 \
42+
&& rm -rf /var/lib/apt/lists/* \
43+
&& ln -sf /opt/host-php/bin/php /usr/local/bin/php-host
44+
45+
# ext-php-rs's build.rs needs a host `php` executable to query include paths
46+
# from `php-config`. The wasm-targeted PHP install in /usr/local does not ship
47+
# a CLI binary (--disable-cli). We install Debian's host php-cli purely so the
48+
# build script's PHP shells out can run; bindgen still uses
49+
# /usr/local/bin/php-config (from PATH order) for headers — which IS the wasm
50+
# PHP install. Set PHP_CONFIG explicitly to make this contract obvious.
51+
ENV PHP_CONFIG=/usr/local/bin/php-config \
52+
PHP=/opt/host-php/bin/php
53+
54+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
55+
| sh -s -- -y --default-toolchain stable --profile minimal \
56+
&& /root/cargo/bin/rustup target add wasm32-unknown-emscripten \
57+
&& /root/cargo/bin/rustup toolchain install nightly --profile minimal \
58+
--component rust-src \
59+
&& /root/cargo/bin/rustup target add wasm32-unknown-emscripten --toolchain nightly
60+
61+
# Make libclang discoverable for bindgen regardless of LLVM minor version.
62+
RUN ln -sf "$(dpkg -L libclang-dev | grep -E 'libclang.so$' | head -1)" /usr/lib/libclang.so || true
63+
ENV LIBCLANG_PATH=/usr/lib/llvm-18/lib

0 commit comments

Comments
 (0)