Skip to content

Commit

Permalink
ci: sync build_wasm.ts with undici
Browse files Browse the repository at this point in the history
  • Loading branch information
tombl committed Jan 7, 2025
1 parent 421af68 commit be4de8f
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 102 deletions.
7 changes: 0 additions & 7 deletions .dockerignore

This file was deleted.

3 changes: 0 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,6 @@ jobs:
- name: Install dependencies
run: npm ci --ignore-scripts

- name: Prepare build
run: npm run prebuild-wasm

- name: Build
run: npm run build-wasm

Expand Down
13 changes: 0 additions & 13 deletions Dockerfile

This file was deleted.

172 changes: 95 additions & 77 deletions bin/build_wasm.ts
Original file line number Diff line number Diff line change
@@ -1,101 +1,119 @@
import { execSync } from 'child_process';
import { copyFileSync, mkdirSync } from 'fs';
import { join, resolve } from 'path';
import { execSync } from 'node:child_process'
import { writeFileSync, readFileSync, mkdirSync, copyFileSync } from 'node:fs'
import { join, resolve } from 'node:path'

let platform = process.env.WASM_PLATFORM ?? '';
const IS_CI = 'CI' in process.env;
const WASM_OUT = resolve(__dirname, '../build/wasm');
const WASM_SRC = resolve(__dirname, '../');
const WASM_BUILDER_CONTAINER = 'ghcr.io/nodejs/wasm-builder@\
sha256:975f391d907e42a75b8c72eb77c782181e941608687d4d8694c3e9df415a0970' // v0.0.9

if (!platform && process.argv[2]) {
platform = execSync('docker info -f "{{.OSType}}/{{.Architecture}}"').toString().trim();
}
const WASM_OUT = resolve(__dirname, '../build/wasm')
const WASM_SRC = resolve(__dirname, '../')

if (process.argv[2] === '--prebuild') {
const cmd = `docker build --platform=${platform.toString().trim()} -t llhttp_wasm_builder . --load`;
// These are defined by build environment
const WASM_CC = process.env.WASM_CC || 'clang'
let WASM_CFLAGS = process.env.WASM_CFLAGS || '--sysroot=/usr/share/wasi-sysroot -target wasm32-unknown-wasi'
let WASM_LDFLAGS = process.env.WASM_LDFLAGS || ''
const WASM_LDLIBS = process.env.WASM_LDLIBS || ''
const WASM_OPT = process.env.WASM_OPT || './wasm-opt'

console.log(`> ${cmd}\n\n`);
execSync(cmd, { stdio: 'inherit' });
// These are relevant for undici and should not be overridden
WASM_CFLAGS += ' -Ofast -fno-exceptions -fvisibility=hidden -mexec-model=reactor'
WASM_LDFLAGS += ' -Wl,-error-limit=0 -Wl,-O3 -Wl,--lto-O3 -Wl,--strip-all'
WASM_LDFLAGS += ' -Wl,--allow-undefined -Wl,--export-dynamic -Wl,--export-table'
WASM_LDFLAGS += ' -Wl,--export=malloc -Wl,--export=free -Wl,--no-entry'

process.exit(0);
}
const WASM_OPT_FLAGS = '-O4 --converge --strip-debug --strip-dwarf --strip-producers'

const writeWasmChunk = (path: string, dest: string) => {
const base64 = readFileSync(join(WASM_OUT, path)).toString('base64')
writeFileSync(join(WASM_OUT, dest), `'use strict'
const { Buffer } = require('node:buffer')
if (process.argv[2] === '--setup') {
try {
mkdirSync(join(WASM_SRC, 'build'));
process.exit(0);
} catch (error: unknown) {
if (isErrorWithCode(error) && error.code !== 'EEXIST') {
throw error;
}
process.exit(0);
const wasmBase64 = '${base64}'
let wasmBuffer
Object.defineProperty(module, 'exports', {
get: () => {
return wasmBuffer
? wasmBuffer
: (wasmBuffer = Buffer.from(wasmBase64, 'base64'))
}
})
`)
}

let platform = process.env.WASM_PLATFORM

if (process.argv[2] === '--docker') {
let cmd = `docker run --rm --platform=${platform.toString().trim()}`;
if (!IS_CI) {
cmd += ' -it';
}
// Try to avoid root permission problems on compiled assets
// when running on linux.
// It will work flawessly if uid === gid === 1000
// there will be some warnings otherwise.
platform = execSync('docker info -f "{{.OSType}}/{{.Architecture}}"').toString().trim()
let cmd = `docker run --rm --platform=${platform.toString().trim()} `
if (process.platform === 'linux') {
cmd += ` --user ${process.getuid!()}:${process.getegid!()}`;
cmd += ` --user ${process.getuid!()}:${process.getegid!()}`
}
cmd += ` --mount type=bind,source=${WASM_SRC}/build,target=/home/node/llhttp/build llhttp_wasm_builder npm run wasm`;

console.log(`> ${cmd}\n\n`);
execSync(cmd, { cwd: WASM_SRC, stdio: 'inherit' });
process.exit(0);
cmd += ` --mount type=bind,source=${WASM_SRC},target=/home/node/build \
-t ${WASM_BUILDER_CONTAINER} npm run wasm`
console.log(`> ${cmd}\n\n`)
execSync(cmd, { stdio: 'inherit' })
process.exit(0)
}

try {
mkdirSync(WASM_OUT);
} catch (error: unknown) {
if (isErrorWithCode(error) && error.code !== 'EEXIST') {
throw error;
const hasApk = (function () {
try { execSync('command -v apk'); return true } catch { return false }
})()
const hasOptimizer = (function () {
try { execSync(`${WASM_OPT} --version`); return true } catch { return false }
})()
if (hasApk) {
// Gather information about the tools used for the build
const buildInfo = execSync('apk info -v').toString()
if (!buildInfo.includes('wasi-sdk')) {
console.log('Failed to generate build environment information')
process.exit(-1)
}
console.log(buildInfo)
}

mkdirSync(WASM_OUT, { recursive: true })

// Build ts
execSync('npm run build', { cwd: WASM_SRC, stdio: 'inherit' });

// Build wasm binary
execSync(
`clang \
--sysroot=/usr/share/wasi-sysroot \
-target wasm32-unknown-wasi \
-Ofast \
-fno-exceptions \
-fvisibility=hidden \
-mexec-model=reactor \
-Wl,-error-limit=0 \
-Wl,-O3 \
-Wl,--lto-O3 \
-Wl,--strip-all \
-Wl,--allow-undefined \
-Wl,--export-dynamic \
-Wl,--export-table \
-Wl,--export=malloc \
-Wl,--export=free \
-Wl,--no-entry \
${join(WASM_SRC, 'build', 'c')}/*.c \
${join(WASM_SRC, 'src', 'native')}/*.c \
-I${join(WASM_SRC, 'build')} \
-o ${join(WASM_OUT, 'llhttp.wasm')}`,
{ stdio: 'inherit' },
);
execSync(`${WASM_CC} ${WASM_CFLAGS} ${WASM_LDFLAGS} \
${join(WASM_SRC, 'build/c')}/*.c \
${join(WASM_SRC, 'src/native')}/*.c \
-I${join(WASM_SRC, 'build')} \
-o ${join(WASM_OUT, 'llhttp.wasm')} \
${WASM_LDLIBS}`, { stdio: 'inherit' })

// Copy constants for `.js` and `.ts` users.
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'constants.js'), join(WASM_OUT, 'constants.js'));
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'constants.js.map'), join(WASM_OUT, 'constants.js.map'));
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'constants.d.ts'), join(WASM_OUT, 'constants.d.ts'));
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'utils.js'), join(WASM_OUT, 'utils.js'));
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'utils.js.map'), join(WASM_OUT, 'utils.js.map'));
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'utils.d.ts'), join(WASM_OUT, 'utils.d.ts'));

function isErrorWithCode(error: unknown): error is Error & { code: string } {
return typeof error === 'object' && error !== null && 'code' in error;
if (hasOptimizer) {
execSync(`${WASM_OPT} ${WASM_OPT_FLAGS} \
-o ${join(WASM_OUT, 'llhttp.wasm')} \
${join(WASM_OUT, 'llhttp.wasm')}`, { stdio: 'inherit' })
}
writeWasmChunk('llhttp.wasm', 'llhttp-wasm.js')

// Build wasm simd binary
execSync(`${WASM_CC} ${WASM_CFLAGS} -msimd128 ${WASM_LDFLAGS} \
${join(WASM_SRC, 'build/c')}/*.c \
${join(WASM_SRC, 'src/native')}/*.c \
-I${join(WASM_SRC, 'build')} \
-o ${join(WASM_OUT, 'llhttp_simd.wasm')} \
${WASM_LDLIBS}`, { stdio: 'inherit' })

if (hasOptimizer) {
execSync(`${WASM_OPT} ${WASM_OPT_FLAGS} --enable-simd \
-o ${join(WASM_OUT, 'llhttp_simd.wasm')} \
${join(WASM_OUT, 'llhttp_simd.wasm')}`, { stdio: 'inherit' })
}
writeWasmChunk('llhttp_simd.wasm', 'llhttp_simd-wasm.js')

// Copy constants for `.js` and `.ts` users.
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'constants.js'), join(WASM_OUT, 'constants.js'))
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'constants.js.map'), join(WASM_OUT, 'constants.js.map'))
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'constants.d.ts'), join(WASM_OUT, 'constants.d.ts'))
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'utils.js'), join(WASM_OUT, 'utils.js'))
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'utils.js.map'), join(WASM_OUT, 'utils.js.map'))
copyFileSync(join(WASM_SRC, 'lib', 'llhttp', 'utils.d.ts'), join(WASM_OUT, 'utils.d.ts'))
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
"bench": "ts-node bench/",
"build": "ts-node bin/generate.ts",
"build-ts": "tsc",
"prebuild-wasm": "npm run wasm -- --prebuild && npm run wasm -- --setup",
"build-wasm": "npm run wasm -- --docker",
"build-wasm": "npm run build-ts && npm run wasm -- --docker",
"wasm": "ts-node bin/build_wasm.ts",
"clean": "rm -rf lib && rm -rf test/tmp",
"prepare": "npm run clean && npm run build-ts",
Expand Down

0 comments on commit be4de8f

Please sign in to comment.