From a74fd6cb3613f15ca9d8df0bab05b532f5a3c13e Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Sat, 27 Jan 2024 05:32:24 +0800 Subject: [PATCH] Support building for externally shared js builtins Initial support for loading unbundled module in `AddExternalizedBuiltin`. - Reduces downstream distribution package size (by not shipping wasm twice and not base64-encoding it) - Provides a cleaner stacktrace - Easier to patch To enable this, pass `EXTERNAL_PATH=/path/to/global/node_modules/undici` to `build/wasm.js`. You shall also pass this path to `--shared-builtin-undici/undici-path` in Node.js's `configure.py`. Reference: https://github.com/nodejs/node/commit/ca5be26b318 https://github.com/nodejs/node/pull/44376 Signed-off-by: Zephyr Lykos --- CONTRIBUTING.md | 10 ++++++++++ build/wasm.js | 41 ++++++++++++++++++++++++++++++----------- lib/client.js | 4 ++-- package.json | 1 + 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a7f3ffd14f..7a910263bd1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,7 @@ * [Test](#test) * [Coverage](#coverage) * [Update `WPTs`](#update-wpts) + * [Building for externally shared node builtins](#external-builds) * [Developer's Certificate of Origin 1.1](#developers-certificate-of-origin) * [Moderation Policy](#moderation-policy) @@ -165,6 +166,15 @@ npm run test npm run coverage ``` + +### Building for externally shared node builtins + +If you are packaging `undici` for a distro, this might help if you would like to use +an unbundled version instead of bundling one in `libnode.so`. + +To enable this, pass `EXTERNAL_PATH=/path/to/global/node_modules/undici` to `build/wasm.js`. +You shall also pass this path to `--shared-builtin-undici/undici-path` in Node.js's `configure.py`. + ## Developer's Certificate of Origin 1.1 diff --git a/build/wasm.js b/build/wasm.js index 2b63f3c7ab1..1104cfec0b0 100644 --- a/build/wasm.js +++ b/build/wasm.js @@ -2,7 +2,7 @@ const { execSync } = require('child_process') const { writeFileSync, readFileSync } = require('fs') -const { join, resolve } = require('path') +const { join, resolve, basename } = require('path') const ROOT = resolve(__dirname, '../') const WASM_SRC = resolve(__dirname, '../deps/llhttp') @@ -15,6 +15,8 @@ let WASM_CFLAGS = process.env.WASM_CFLAGS || '--sysroot=/usr/share/wasi-sysroot let WASM_LDFLAGS = process.env.WASM_LDFLAGS || '' const WASM_LDLIBS = process.env.WASM_LDLIBS || '' +const EXTERNAL_PATH = process.env.EXTERNAL_PATH + // 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' @@ -60,6 +62,23 @@ if (hasApk) { writeFileSync(join(WASM_OUT, 'wasm_build_env.txt'), buildInfo) } +const writeWasmChunk = EXTERNAL_PATH + ? (path, dest) => { + const base64 = readFileSync(join(WASM_OUT, path)).toString('base64') + writeFileSync(join(WASM_OUT, dest), ` +const { Buffer } = require('node:buffer') + +module.exports = Buffer.from('${base64}', 'base64') +`) + } + : (path, dest) => { + writeFileSync(join(WASM_OUT, dest), ` +const { fs } = require('node:fs') + +module.exports = fs.readFileSync(require.resolve('./${basename(path)}')) +`) + } + // Build wasm binary execSync(`${WASM_CC} ${WASM_CFLAGS} ${WASM_LDFLAGS} \ ${join(WASM_SRC, 'src')}/*.c \ @@ -67,11 +86,7 @@ execSync(`${WASM_CC} ${WASM_CFLAGS} ${WASM_LDFLAGS} \ -o ${join(WASM_OUT, 'llhttp.wasm')} \ ${WASM_LDLIBS}`, { stdio: 'inherit' }) -const base64Wasm = readFileSync(join(WASM_OUT, 'llhttp.wasm')).toString('base64') -writeFileSync( - join(WASM_OUT, 'llhttp-wasm.js'), - `module.exports = '${base64Wasm}'\n` -) +writeWasmChunk('llhttp.wasm', 'llhttp-wasm.js') // Build wasm simd binary execSync(`${WASM_CC} ${WASM_CFLAGS} -msimd128 ${WASM_LDFLAGS} \ @@ -80,8 +95,12 @@ execSync(`${WASM_CC} ${WASM_CFLAGS} -msimd128 ${WASM_LDFLAGS} \ -o ${join(WASM_OUT, 'llhttp_simd.wasm')} \ ${WASM_LDLIBS}`, { stdio: 'inherit' }) -const base64WasmSimd = readFileSync(join(WASM_OUT, 'llhttp_simd.wasm')).toString('base64') -writeFileSync( - join(WASM_OUT, 'llhttp_simd-wasm.js'), - `module.exports = '${base64WasmSimd}'\n` -) +writeWasmChunk('llhttp_simd.wasm', 'llhttp_simd-wasm.js') + +if (EXTERNAL_PATH) { + writeFileSync(join(ROOT, 'loader.js'), ` +'use strict' + +module.exports = require('node:module').createRequire('${EXTERNAL_PATH}/loader.js')('./index-fetch.js') +`) +} diff --git a/lib/client.js b/lib/client.js index b9077d4dd6a..a46a1504b54 100644 --- a/lib/client.js +++ b/lib/client.js @@ -479,7 +479,7 @@ async function lazyllhttp () { let mod try { - mod = await WebAssembly.compile(Buffer.from(require('./llhttp/llhttp_simd-wasm.js'), 'base64')) + mod = await WebAssembly.compile(require('./llhttp/llhttp_simd-wasm.js')) } catch (e) { /* istanbul ignore next */ @@ -487,7 +487,7 @@ async function lazyllhttp () { // being enabled, but the occurring of this other error // * https://github.com/emscripten-core/emscripten/issues/11495 // got me to remove that check to avoid breaking Node 12. - mod = await WebAssembly.compile(Buffer.from(llhttpWasmData || require('./llhttp/llhttp-wasm.js'), 'base64')) + mod = await WebAssembly.compile(llhttpWasmData || require('./llhttp/llhttp-wasm.js')) } return await WebAssembly.instantiate(mod, { diff --git a/package.json b/package.json index de76be67b8f..a50765c7d88 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "*.d.ts", "index.js", "index-fetch.js", + "loader.js", "lib", "types", "docs"