diff --git a/Dockerfile b/Dockerfile index d7e9092..beed103 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM node:22-alpine@sha256:ed9736a13b88ba55cbc08c75c9edac8ae7f72840482e40324670b ARG UID=1000 ARG GID=1000 -RUN apk add -U clang lld wasi-sdk && mkdir /home/node/llhttp +RUN apk add -U clang lld wasi-sdk bash && mkdir /home/node/llhttp WORKDIR /home/node/llhttp diff --git a/bin/build_wasm.sh b/bin/build_wasm.sh new file mode 100755 index 0000000..a7d84af --- /dev/null +++ b/bin/build_wasm.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +set -e + +WASM_SRC="$(dirname "$0")/../" +WASM_OUT="$(dirname "$0")/../build/wasm" + +cd "$WASM_SRC" + +if [[ -z "$WASM_PLATFORM" && -n "$1" ]]; then + WASM_PLATFORM=$(docker info -f "{{.OSType}}/{{.Architecture}}") +fi + +case "$1" in + --prebuild) + exec docker build --platform="$WASM_PLATFORM" -t llhttp_wasm_builder . --load + ;; + --setup) + mkdir -p build + exit 0 + ;; + --docker) + cmd=(docker run --rm --platform="$WASM_PLATFORM") + if [[ -z "$CI" ]]; then + cmd+=(-it) + fi + # 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. + if [[ "$(uname)" == Linux ]]; then + cmd+=(--user "$(id -u):$(id -g)") + fi + cmd+=(--mount "type=bind,source=./build,target=/home/node/llhttp/build" llhttp_wasm_builder npm run wasm) + + echo "> ${cmd[*]}" + exec "${cmd[@]}" + ;; +esac + +mkdir -p "$WASM_OUT" + +npm run build + +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 \ + build/c/*.c \ + src/native/*.c \ + -Ibuild \ + -o "$WASM_OUT/llhttp.wasm" + +cp lib/llhttp/{constants,utils}.* "$WASM_OUT/" diff --git a/bin/build_wasm.ts b/bin/build_wasm.ts deleted file mode 100644 index 2d97a89..0000000 --- a/bin/build_wasm.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { execSync } from 'child_process'; -import { copyFileSync, mkdirSync } from 'fs'; -import { join, resolve } from '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, '../'); - -if (!platform && process.argv[2]) { - platform = execSync('docker info -f "{{.OSType}}/{{.Architecture}}"').toString().trim(); -} - -if (process.argv[2] === '--prebuild') { - const cmd = `docker build --platform=${platform.toString().trim()} -t llhttp_wasm_builder . --load`; - - console.log(`> ${cmd}\n\n`); - execSync(cmd, { stdio: 'inherit' }); - - process.exit(0); -} - -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); - } -} - -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. - if (process.platform === 'linux') { - 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); -} - -try { - mkdirSync(WASM_OUT); -} catch (error: unknown) { - if (isErrorWithCode(error) && error.code !== 'EEXIST') { - throw error; - } -} - -// 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' }, -); - -// 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; -} diff --git a/package.json b/package.json index e7909c3..3d5fe83 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build-ts": "tsc", "prebuild-wasm": "npm run wasm -- --prebuild && npm run wasm -- --setup", "build-wasm": "npm run wasm -- --docker", - "wasm": "ts-node bin/build_wasm.ts", + "wasm": "bash bin/build_wasm.sh", "clean": "rm -rf lib && rm -rf test/tmp", "prepare": "npm run clean && npm run build-ts", "test": "node -r ts-node/register/type-check ./test/md-test.ts",