Skip to content

Commit

Permalink
feat: build wasm in common wasm-builder (#105)
Browse files Browse the repository at this point in the history
Refs: nodejs/security-wg#1236

Update to build in docker using wasm-builder image
maintained in https://github.com/nodejs/wasm-builder.

As a side effect this also updates the versions of the
wasm tools to a newer version.

undici and amaro have already been moved over and I plan to
document in Node.js docs that this is the projects standard
way to build WASM blobs.

Signed-off-by: Michael Dawson <[email protected]>
  • Loading branch information
mhdawson authored Nov 21, 2024
1 parent 5d0e273 commit 8411abd
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 40 deletions.
18 changes: 3 additions & 15 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ on:
branches: main

env:
WASI_VERSION: 12
WASI_VERSION_FULL: "12.0"
WABT_VERSION: "1.0.24"
EMCC_VERSION: "1.40.1-fastcomp"

Expand All @@ -25,22 +23,12 @@ jobs:
export PWD=$(pwd);
echo "::set-output name=PROJ_ROOT::$PWD";
- name: Set up Docker
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1

- name: Install
run: npm install

- name: Install wasi-sdk
env:
PROJ_ROOT: ${{ steps.preparation.outputs.PROJ_ROOT }}
run: |
cd $PROJ_ROOT;
cd ../;
export WASI_OS="linux"
curl -sL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-${WASI_OS}.tar.gz -O
# check if package downloaded
ls -la
tar xvf wasi-sdk-${WASI_VERSION_FULL}-${WASI_OS}.tar.gz
# print clang version
./wasi-sdk-${WASI_VERSION_FULL}/bin/clang --version
- name: Install wabt
env:
PROJ_ROOT: ${{ steps.preparation.outputs.PROJ_ROOT }}
Expand Down
19 changes: 1 addition & 18 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,33 +1,16 @@
# These flags depend on the system and may be overridden
WASM_CC := ../wasi-sdk-12.0/bin/clang
WASM_CFLAGS := --sysroot=../wasi-sdk-12.0/share/wasi-sysroot
WASM_LDFLAGS := -nostartfiles

WASM2WAT := ../wabt/bin/wasm2wat
WASM_OPT := ../binaryen/bin/wasm-opt

# These are project-specific and are expected to be kept intact
WASM_EXTRA_CFLAGS := -I include-wasm/ -Wno-logical-op-parentheses -Wno-parentheses -Oz
WASM_EXTRA_LDFLAGS := -Wl,-z,stack-size=13312,--no-entry,--compress-relocations,--strip-all
WASM_EXTRA_LDFLAGS += -Wl,--export=__heap_base,--export=parseCJS,--export=sa
WASM_EXTRA_LDFLAGS += -Wl,--export=e,--export=re,--export=es,--export=ee
WASM_EXTRA_LDFLAGS += -Wl,--export=rre,--export=ree,--export=res,--export=ru,--export=us,--export=ue

.PHONY: optimize clean

lib/lexer.wat: lib/lexer.wasm
$(WASM2WAT) lib/lexer.wasm -o lib/lexer.wat

lib/lexer.wasm: include-wasm/cjs-module-lexer.h src/lexer.c | lib/
$(WASM_CC) $(WASM_CFLAGS) $(WASM_EXTRA_CFLAGS) \
src/lexer.c -o lib/lexer.wasm \
$(WASM_LDFLAGS) $(WASM_EXTRA_LDFLAGS)
node build/wasm.js --docker

lib/:
@mkdir -p $@

optimize: lib/lexer.wasm
$(WASM_OPT) -Oz lib/lexer.wasm -o lib/lexer.wasm

clean:
$(RM) lib/*
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,17 +442,19 @@ test/samples/*.js (3635 KiB)

### Wasm Build Steps

To build download the WASI SDK from https://github.com/WebAssembly/wasi-sdk/releases.
The build uses docker and make, they must be installed first.

The Makefile assumes the existence of "wasi-sdk-11.0" and "wabt" (optional) as sibling folders to this project.
To build the lexer wasm run `npm run build-wasm`.

The build through the Makefile is then run via `make lib/lexer.wasm`, which can also be triggered via `npm run build-wasm` to create `dist/lexer.js`.
Optimization passes are run with [Binaryen](https://github.com/WebAssembly/binaryen)
prior to publish to reduce the Web Assembly footprint.

On Windows it may be preferable to use the Linux subsystem.
After building the lexer wasm, build the final distribution components
(lexer.js and lexer.mjs) by running `npm run build`.

After the Web Assembly build, the CJS build can be triggered via `npm run build`.

Optimization passes are run with [Binaryen](https://github.com/WebAssembly/binaryen) prior to publish to reduce the Web Assembly footprint.
If you need to build lib/lexer.wat (optional) you must first install
[wabt](https://github.com/WebAssembly/wabt) as a sibling folder to this
project. The wat file is then build by running `make lib/lexer.wat`

### License

Expand Down
13 changes: 13 additions & 0 deletions build/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
lib/lexer.wasm: include-wasm/cjs-module-lexer.h src/lexer.c
@mkdir -p lib
clang --sysroot=/usr/share/wasi-sysroot -target wasm32-unknown-wasi src/lexer.c -I include-wasm -o lib/lexer.wasm -nostartfiles \
-Wl,-z,stack-size=13312,--no-entry,--compress-relocations,--strip-all,--export=__heap_base,\
--export=parseCJS,--export=sa,--export=e,--export=re,--export=es,--export=ee,--export=rre,--export=ree,--export=res,--export=ru,--export=us,--export=ue \
-Wno-logical-op-parentheses -Wno-parentheses \
-Oz

optimize: lib/lexer.wasm
${WASM_OPT} -Oz lib/lexer.wasm -o lib/lexer.wasm

clean:
rm lib/*
54 changes: 54 additions & 0 deletions build/wasm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict'

const WASM_BUILDER_CONTAINER = 'ghcr.io/nodejs/wasm-builder@sha256:975f391d907e42a75b8c72eb77c782181e941608687d4d8694c3e9df415a0970' // v0.0.9

const WASM_OPT = './wasm-opt'

const { execSync } = require('node:child_process')
const { writeFileSync, readFileSync, existsSync, mkdirSync } = require('node:fs')
const { join, resolve } = require('node:path')

const ROOT = resolve(__dirname, '../')

let platform = process.env.WASM_PLATFORM
if (!platform && process.argv[2]) {
platform = execSync('docker info -f "{{.OSType}}/{{.Architecture}}"').toString().trim()
}

if (process.argv[2] === '--docker') {
let cmd = `docker run --rm --platform=${platform.toString().trim()} `
if (process.platform === 'linux') {
cmd += ` --user ${process.getuid()}:${process.getegid()}`
}

if (!existsSync(`${ROOT}/dist`)){
mkdirSync(`${ROOT}/dist`);
}

cmd += ` --mount type=bind,source=${ROOT}/lib,target=/home/node/build/lib \
--mount type=bind,source=${ROOT}/src,target=/home/node/build/src \
--mount type=bind,source=${ROOT}/dist,target=/home/node/build/dist \
--mount type=bind,source=${ROOT}/node_modules,target=/home/node/build/node_modules \
--mount type=bind,source=${ROOT}/build/wasm.js,target=/home/node/build/wasm.js \
--mount type=bind,source=${ROOT}/build/Makefile,target=/home/node/build/Makefile \
--mount type=bind,source=${ROOT}/build.js,target=/home/node/build/build.js \
--mount type=bind,source=${ROOT}/package.json,target=/home/node/build/package.json \
--mount type=bind,source=${ROOT}/include-wasm,target=/home/node/build/include-wasm \
-t ${WASM_BUILDER_CONTAINER} node wasm.js`
console.log(`> ${cmd}\n\n`)
execSync(cmd, { stdio: 'inherit' })
process.exit(0)
}

const hasOptimizer = (function () {
try { execSync(`${WASM_OPT} --version`); return true } catch (error) { return false }
})()

// Build wasm binary
console.log('Building wasm');
execSync(`make lib/lexer.wasm`, { stdio: 'inherit' })
if (hasOptimizer) {
console.log('Optimizing wasm');
execSync(`make optimize`, { stdio: 'inherit' })
}
execSync(`node build.js`, { stdio: 'inherit' })
Binary file modified lib/lexer.wasm
Binary file not shown.

0 comments on commit 8411abd

Please sign in to comment.