From c18d916ab970e24e6cabd2cacd9a175fd657f53b Mon Sep 17 00:00:00 2001 From: pmp-p Date: Mon, 28 Jul 2025 13:36:37 +0200 Subject: [PATCH 1/7] docker builder, wasm->libpglite(wasi)->pglite all in one --- docker_rc.sh | 51 +++++++++++++++++++++++ wasm-builder/Dockerfile | 24 +++++++++++ wasm-builder/build-with-docker.sh | 69 +++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 docker_rc.sh create mode 100644 wasm-builder/Dockerfile create mode 100755 wasm-builder/build-with-docker.sh diff --git a/docker_rc.sh b/docker_rc.sh new file mode 100644 index 0000000000000..0832d6be2ef5c --- /dev/null +++ b/docker_rc.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +WORKSPACE=$(pwd) + +echo " + +SDK prepare + +" + +pushd / + # this is minimum required to **use** sdk on docker/debian 12, not build it + apt-get update && apt-get --yes install git wget curl lz4 xz-utils bison flex pkg-config autoconf make + + if [ -f $WORKSPACE/sdk.tar.lz4 ] + then + tar xf $WORKSPACE/sdk.tar.lz4 --use-compress-program=lz4 + fi + + if [ -d $SDKROOT/wasisdk/upstream ] + then + echo "wasi sdk common support is already installed" + else + tar xf ${WORKSPACE}/prebuilt/wasi-sdk-25.tar.xz + fi + + if [ -d $SDKROOT/wasisdk/upstream/lib ] + then + echo "wasi sdk $(arch) support is already installed" + else + pushd $SDKROOT/wasisdk + if arch|grep -q aarch64 + then + wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-arm64-linux.tar.gz -O/tmp/sdk.tar.gz + else + wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz -O/tmp/sdk.tar.gz + fi + tar xfz /tmp/sdk.tar.gz && rm /tmp/sdk.tar.gz + mv wasi-sdk-25.0-*/{bin,lib} upstream/ + popd + fi + +popd + +echo " + +Setting up SDK shell + +" + +${SDKROOT}/wasm32-bi-emscripten-shell.sh diff --git a/wasm-builder/Dockerfile b/wasm-builder/Dockerfile new file mode 100644 index 0000000000000..3564af11157d8 --- /dev/null +++ b/wasm-builder/Dockerfile @@ -0,0 +1,24 @@ +FROM debian:12 AS build_sdk + +ARG PG_VERSION=17.5 +ARG SDK_VERSION=3.1.74.11bi-w-n +ARG DEBUG=false +ARG OBJDUMP=true +ARG PG_BRANCH=REL_17_5_WASM-pglite + +ENV \ + PG_VERSION=$PG_VERSION \ + SDK_VERSION=$SDK_VERSION \ + SDKROOT=/tmp/sdk \ + SYS_PYTHON=/usr/bin/python3 \ + DEBUG=$DEBUG \ + BUILDS=3.12 \ + EMFLAVOUR=3.1.74 + +RUN apt-get update && \ + apt-get install -y git wget lz4 bzip2 pv bash python3 build-essential libreadline-dev zlib1g-dev bison flex xz-utils perl + +RUN apt-get install -y clang autoconf wget curl lz4 lsb-release zlib1g-dev libssl-dev + +# RUN bash -c "wget -qO- https://github.com/electric-sql/portable-sdk/releases/download/3.1.74.11bi-w-n/python3.13-wasm-sdk-debian12-$(arch).tar.lz4 | lz4 -d | tar -xvf -" +RUN bash -c "wget -qO- https://github.com/electric-sql/portable-sdk/releases/download/3.1.74.11.11/python3.13-wasm-sdk-debian12-$(arch).tar.lz4 | lz4 -d | tar -xvf -" \ No newline at end of file diff --git a/wasm-builder/build-with-docker.sh b/wasm-builder/build-with-docker.sh new file mode 100755 index 0000000000000..6396781c40027 --- /dev/null +++ b/wasm-builder/build-with-docker.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# we are using a custom emsdk to build pglite wasm +# this is available as a docker image under electricsql/pglite-builder +IMG_NAME=${IMG_NAME:-"electricsql/pglite-builder"} + +[ -f postgres-pglite/configure ] || ln -s . postgres-pglite + +export WORKSPACE=${GITHUB_WORKSPACE:-$(pwd)} + +# normally would default to /workspace but that may cause trouble with debug paths in some IDE +export DOCKER_WORKSPACE=${DOCKER_WORKSPACE:-$WORKSPACE} + +cd $(realpath ${WORKSPACE}/postgres-pglite) + +[ -f ${BUILD_CONFIG:-postgres-pglite}/.buildconfig ] && cp ${BUILD_CONFIG:-postgres-pglite}/.buildconfig .buildconfig +[ -f ./pglite/.buildconfig ] && cp ./pglite/.buildconfig .buildconfig + + +source .buildconfig + +cat .buildconfig + + +if echo $IMG_NAME|grep -q debian +then + IMG_NAME="debian" + IMG_TAG="12" + wget -q -Osdk.tar.lz4 \ + https://github.com/electric-sql/portable-sdk/releases/download/${SDK_VERSION}/python3.13-wasm-sdk-${IMG_NAME}${IMG_TAG}-$(arch).tar.lz4 +else + IMG_TAG="${PG_VERSION}_${SDK_VERSION}" +fi + + +mkdir -p dist/pglite dist/extensions-emsdk + +if echo -n $@|grep -q it$ +then + PROMPT="&& bash ) || bash" +else + PROMPT=")" +fi + +if $WASI +then + OUT_DIR=wasi +else + OUT_DIR=emscripten +fi + +if $DEBUG +then + MAP_OUT_DIRS="-v ${WORKSPACE}/postgres-pglite/out/${OUT_DIR}/build:/tmp/sdk/build:rw -v ${WORKSPACE}/postgres-pglite/out/${OUT_DIR}/pglite:/tmp/pglite:rw" +else + MAP_OUT_DIRS="" +fi + +docker run $@ \ + --rm \ + --env-file .buildconfig \ + -e DEBUG=${DEBUG:-false} \ + -e WASI=${WASI:-false} \ + --workdir=${DOCKER_WORKSPACE} \ + -v ${WORKSPACE}/postgres-pglite:${DOCKER_WORKSPACE}:rw \ + -v ${WORKSPACE}/postgres-pglite/dist:/tmp/sdk/dist:rw \ + $MAP_OUT_DIRS \ + $IMG_NAME:$IMG_TAG \ + bash --noprofile --rcfile ./docker_rc.sh -ci "( ./wasm-build.sh ${WHAT:-\"contrib extra\"} $PROMPT" From 4875f3f069f4bd5ef48e6d54d8bb01520c97a36e Mon Sep 17 00:00:00 2001 From: Tudor Zaharia <37596026+tdrz@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:34:37 +0200 Subject: [PATCH 2/7] Update Dockerfile --- wasm-builder/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wasm-builder/Dockerfile b/wasm-builder/Dockerfile index 3564af11157d8..a806e5e098b16 100644 --- a/wasm-builder/Dockerfile +++ b/wasm-builder/Dockerfile @@ -20,5 +20,4 @@ RUN apt-get update && \ RUN apt-get install -y clang autoconf wget curl lz4 lsb-release zlib1g-dev libssl-dev -# RUN bash -c "wget -qO- https://github.com/electric-sql/portable-sdk/releases/download/3.1.74.11bi-w-n/python3.13-wasm-sdk-debian12-$(arch).tar.lz4 | lz4 -d | tar -xvf -" -RUN bash -c "wget -qO- https://github.com/electric-sql/portable-sdk/releases/download/3.1.74.11.11/python3.13-wasm-sdk-debian12-$(arch).tar.lz4 | lz4 -d | tar -xvf -" \ No newline at end of file +RUN bash -c "wget -qO- https://github.com/electric-sql/portable-sdk/releases/download/3.1.74.12.0/python3.13-wasm-sdk-debian12-$(arch).tar.lz4 | lz4 -d | tar -xvf -" From 80621caed5773300a7caf1f88bc2573b1861840b Mon Sep 17 00:00:00 2001 From: pmp-p Date: Thu, 31 Jul 2025 13:18:07 +0200 Subject: [PATCH 3/7] sdk hotfix --- docker_rc.sh | 11 + hotfix/library_dylink.js | 1288 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 1299 insertions(+) create mode 100644 hotfix/library_dylink.js diff --git a/docker_rc.sh b/docker_rc.sh index 0832d6be2ef5c..bd9c42f40d1d5 100644 --- a/docker_rc.sh +++ b/docker_rc.sh @@ -46,6 +46,17 @@ echo " Setting up SDK shell + +Applying Hotfixes +------------------------------------------------------------------------------------------ " +echo "linker fix" +cp -vf hotfix/library_dylink.js ${SDKROOT}/emsdk/upstream/emscripten/src/ + + + +echo "------------------------------------------------------------------------------------------" + + ${SDKROOT}/wasm32-bi-emscripten-shell.sh diff --git a/hotfix/library_dylink.js b/hotfix/library_dylink.js new file mode 100644 index 0000000000000..8a44339b30670 --- /dev/null +++ b/hotfix/library_dylink.js @@ -0,0 +1,1288 @@ +/** + * @license + * Copyright 2020 The Emscripten Authors + * SPDX-License-Identifier: MIT + * + * Dynamic library loading + */ + +#if !RELOCATABLE +#error "library_dylink.js requires RELOCATABLE" +#endif + +var LibraryDylink = { +#if FILESYSTEM + $registerWasmPlugin__deps: ['$preloadPlugins'], + $registerWasmPlugin: () => { + // Use string keys here to avoid minification since the plugin consumer + // also uses string keys. + var wasmPlugin = { + 'promiseChainEnd': Promise.resolve(), + 'canHandle': (name) => { + return !Module['noWasmDecoding'] && name.endsWith('.so') + }, + 'handle': (byteArray, name, onload, onerror) => { + // loadWebAssemblyModule can not load modules out-of-order, so rather + // than just running the promises in parallel, this makes a chain of + // promises to run in series. + wasmPlugin['promiseChainEnd'] = wasmPlugin['promiseChainEnd'].then( + () => loadWebAssemblyModule(byteArray, {loadAsync: true, nodelete: true}, name, {})).then( + (exports) => { +#if DYLINK_DEBUG + dbg(`registering preloadedWasm: ${name}`); +#endif + preloadedWasm[name] = exports; + onload(byteArray); + }, + (error) => { + err(`failed to instantiate wasm: ${name}: ${error}`); + onerror(); + }); + } + }; + preloadPlugins.push(wasmPlugin); + }, + + $preloadedWasm__deps: ['$registerWasmPlugin'], + $preloadedWasm__postset: ` + registerWasmPlugin(); + `, + $preloadedWasm: {}, +#endif // FILESYSTEM + + $isSymbolDefined: (symName) => { + // Ignore 'stub' symbols that are auto-generated as part of the original + // `wasmImports` used to instantiate the main module. + var existing = wasmImports[symName]; + if (!existing || existing.stub) { + return false; + } +#if ASYNCIFY + // Even if a symbol exists in wasmImports, and is not itself a stub, it + // could be an ASYNCIFY wrapper function that wraps a stub function. + if (symName in asyncifyStubs && !asyncifyStubs[symName]) { + return false; + } +#endif + return true; + }, + + // Dynamic version of shared.py:make_invoke. This is needed for invokes + // that originate from side modules since these are not known at JS + // generation time. +#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' + $createInvokeFunction__internal: true, + $createInvokeFunction__deps: ['$dynCall', 'setThrew', '$stackSave', '$stackRestore'], + $createInvokeFunction: (sig) => (ptr, ...args) => { + var sp = stackSave(); + try { + return dynCall(sig, ptr, args); + } catch(e) { + stackRestore(sp); + // Create a try-catch guard that rethrows the Emscripten EH exception. +#if EXCEPTION_STACK_TRACES + // Exceptions thrown from C++ and longjmps will be an instance of + // EmscriptenEH. + if (!(e instanceof EmscriptenEH)) throw e; +#else + // Exceptions thrown from C++ will be a pointer (number) and longjmp + // will throw the number Infinity. Use the compact and fast "e !== e+0" + // test to check if e was not a Number. + if (e !== e+0) throw e; +#endif + _setThrew(1, 0); +#if WASM_BIGINT + // In theory this if statement could be done on + // creating the function, but I just added this to + // save wasting code space as it only happens on exception. + if (sig[0] == "j") return 0n; +#endif + } + }, +#endif + + // Resolve a global symbol by name. This is used during module loading to + // resolve imports, and by `dlsym` when used with `RTLD_DEFAULT`. + // Returns both the resolved symbol (i.e. a function or a global) along with + // the canonical name of the symbol (in some cases is modify the symbol as + // part of the loop process, so that actual symbol looked up has a different + // name). + $resolveGlobalSymbol__deps: ['$isSymbolDefined', +#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' + '$createInvokeFunction', +#endif + ], + $resolveGlobalSymbol__internal: true, + $resolveGlobalSymbol: (symName, direct = false) => { + var sym; +#if !WASM_BIGINT + // First look for the orig$ symbol which is the symbol without i64 + // legalization performed. + if (direct && ('orig$' + symName in wasmImports)) { + symName = 'orig$' + symName; + } +#endif + if (isSymbolDefined(symName)) { + sym = wasmImports[symName]; + } +#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' + // Asm.js-style exception handling: invoke wrapper generation + else if (symName.startsWith('invoke_')) { + // Create (and cache) new invoke_ functions on demand. + sym = wasmImports[symName] = createInvokeFunction(symName.split('_')[1]); + } +#endif +#if !DISABLE_EXCEPTION_CATCHING + else if (symName.startsWith('__cxa_find_matching_catch_')) { + // When the main module is linked we create whichever variants of + // `__cxa_find_matching_catch_` (see jsifier.js) that we know are needed, + // but a side module loaded at runtime might need different/additional + // variants so we create those dynamically. + sym = wasmImports[symName] = (...args) => { +#if MEMORY64 + args = args.map(Number); +#endif + var rtn = findMatchingCatch(args); + return {{{ to64('rtn') }}}; + } + } +#endif + return {sym, name: symName}; + }, + + $GOT: {}, + $currentModuleWeakSymbols: '=new Set({{{ JSON.stringify(Array.from(WEAK_IMPORTS)) }}})', + + // Create globals to each imported symbol. These are all initialized to zero + // and get assigned later in `updateGOT` + $GOTHandler__internal: true, + $GOTHandler__deps: ['$GOT', '$currentModuleWeakSymbols'], + $GOTHandler: { + get(obj, symName) { + var rtn = GOT[symName]; + if (!rtn) { + rtn = GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); +#if DYLINK_DEBUG == 2 + dbg("new GOT entry: " + symName); +#endif + } + if (!currentModuleWeakSymbols.has(symName)) { + // Any non-weak reference to a symbol marks it as `required`, which + // enabled `reportUndefinedSymbols` to report undefeind symbol errors + // correctly. + rtn.required = true; + } + return rtn; + } + }, + + $isInternalSym__internal: true, + $isInternalSym: (symName) => { + // TODO: find a way to mark these in the binary or avoid exporting them. + return [ + '__cpp_exception', + '__c_longjmp', + '__wasm_apply_data_relocs', + '__dso_handle', + '__tls_size', + '__tls_align', + '__set_stack_limits', + '_emscripten_tls_init', + '__wasm_init_tls', + '__wasm_call_ctors', + '__start_em_asm', + '__stop_em_asm', + '__start_em_js', + '__stop_em_js', + ].includes(symName) || symName.startsWith('__em_js__') +#if SPLIT_MODULE + // Exports synthesized by wasm-split should be prefixed with '%' + || symName[0] == '%' +#endif + ; + }, + + $updateGOT__internal: true, + $updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction', '$getFunctionAddress'], + $updateGOT: (exports, replace) => { +#if DYLINK_DEBUG + dbg("updateGOT: adding " + Object.keys(exports).length + " symbols"); +#endif + for (var symName in exports) { + if (isInternalSym(symName)) { + continue; + } + + var value = exports[symName]; +#if !WASM_BIGINT + if (symName.startsWith('orig$')) { + symName = symName.split('$')[1]; + replace = true; + } +#endif + + GOT[symName] ||= new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); + if (replace || GOT[symName].value == 0) { +#if DYLINK_DEBUG == 2 + dbg(`updateGOT: before: ${symName} : ${GOT[symName].value}`); +#endif + if (typeof value == 'function') { + GOT[symName].value = {{{ to64('addFunction(value)') }}}; +#if DYLINK_DEBUG == 2 + dbg(`updateGOT: FUNC: ${symName} : ${GOT[symName].value}`); +#endif + } else if (typeof value == {{{ POINTER_JS_TYPE }}}) { + GOT[symName].value = value; + } else { + err(`unhandled export type for '${symName}': ${typeof value}`); + } +#if DYLINK_DEBUG == 2 + dbg(`updateGOT: after: ${symName} : ${GOT[symName].value} (${value})`); +#endif + } +#if DYLINK_DEBUG + else if (GOT[symName].value != value) { + dbg(`updateGOT: EXISTING SYMBOL: ${symName} : ${GOT[symName].value} (${value})`); + } +#endif + } +#if DYLINK_DEBUG + dbg("done updateGOT"); +#endif + }, + + // Applies relocations to exported things. + $relocateExports__internal: true, + $relocateExports__deps: ['$updateGOT'], + $relocateExports__docs: '/** @param {boolean=} replace */', + $relocateExports: (exports, memoryBase, replace) => { + var relocated = {}; + + for (var e in exports) { + var value = exports[e]; +#if SPLIT_MODULE + // Do not modify exports synthesized by wasm-split + if (e.startsWith('%')) { + relocated[e] = value + continue; + } +#endif + if (typeof value == 'object') { + // a breaking change in the wasm spec, globals are now objects + // https://github.com/WebAssembly/mutable-global/issues/1 + value = value.value; + } + if (typeof value == {{{ POINTER_JS_TYPE }}}) { + value += {{{ to64('memoryBase') }}}; + } + relocated[e] = value; + } + updateGOT(relocated, replace); + return relocated; + }, + + $reportUndefinedSymbols__internal: true, + $reportUndefinedSymbols__deps: ['$GOT', '$resolveGlobalSymbol'], + $reportUndefinedSymbols: () => { +#if DYLINK_DEBUG + dbg('reportUndefinedSymbols'); +#endif + for (var [symName, entry] of Object.entries(GOT)) { + if (entry.value == 0) { + var value = resolveGlobalSymbol(symName, true).sym; + if (!value && !entry.required) { + // Ignore undefined symbols that are imported as weak. +#if DYLINK_DEBUG + dbg(`ignoring undefined weak symbol: ${symName}`); +#endif + continue; + } +#if ASSERTIONS + assert(value, `undefined symbol '${symName}'. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment`); +#endif +#if DYLINK_DEBUG == 2 + dbg(`assigning dynamic symbol from main module: ${symName} -> ${prettyPrint(value)}`); +#endif + if (typeof value == 'function') { + /** @suppress {checkTypes} */ + entry.value = {{{ to64('addFunction(value, value.sig)') }}}; +#if DYLINK_DEBUG == 2 + dbg(`assigning table entry for : ${symName} -> ${entry.value}`); +#endif + } else if (typeof value == 'number') { + entry.value = {{{ to64('value') }}}; +#if MEMORY64 + } else if (typeof value == 'bigint') { + entry.value = value; +#endif + } else { + throw new Error(`bad export type for '${symName}': ${typeof value}`); + } + } + } +#if DYLINK_DEBUG + dbg('done reportUndefinedSymbols'); +#endif + }, + + // dynamic linker/loader (a-la ld.so on ELF systems) + $LDSO__deps: ['$newDSO'], + $LDSO: { + // name -> dso [refcount, name, module, global]; Used by dlopen + loadedLibsByName: {}, + // handle -> dso; Used by dlsym + loadedLibsByHandle: {}, + init() { +#if ASSERTIONS + // This function needs to run after the initial wasmImports object + // as been created. + assert(wasmImports); +#endif + newDSO('__main__', {{{ cDefs.RTLD_DEFAULT }}}, wasmImports); + }, + }, + + $dlSetError__internal: true, + $dlSetError__deps: ['__dl_seterr', '$stringToUTF8OnStack', '$stackSave', '$stackRestore'], + $dlSetError: (msg) => { +#if DYLINK_DEBUG + dbg(`dlSetError: ${msg}`); +#endif + var sp = stackSave(); + var cmsg = stringToUTF8OnStack(msg); + ___dl_seterr(cmsg, 0); + stackRestore(sp); + }, + + // We support some amount of allocation during startup in the case of + // dynamic linking, which needs to allocate memory for dynamic libraries that + // are loaded. That has to happen before the main program can start to run, + // because the main program needs those linked in before it runs (so we can't + // use normally malloc from the main program to do these allocations). + // + // Allocate memory even if malloc isn't ready yet. The allocated memory here + // must be zero initialized since its used for all static data, including bss. + $getMemory__noleakcheck: true, + $getMemory__deps: ['$GOT', '__heap_base', '$alignMemory', 'calloc'], + $getMemory: (size) => { + // After the runtime is initialized, we must only use sbrk() normally. +#if DYLINK_DEBUG + dbg("getMemory: " + size + " runtimeInitialized=" + runtimeInitialized); +#endif + if (runtimeInitialized) { + // Currently we don't support freeing of static data when modules are + // unloaded via dlclose. This function is tagged as `noleakcheck` to + // avoid having this reported as leak. + return _calloc(size, 1); + } + var ret = ___heap_base; + // Keep __heap_base stack aligned. + var end = ret + alignMemory(size, {{{ STACK_ALIGN }}}); +#if ASSERTIONS + assert(end <= HEAP8.length, 'failure to getMemory - memory growth etc. is not supported there, call malloc/sbrk directly or increase INITIAL_MEMORY'); +#endif + ___heap_base = end; + GOT['__heap_base'].value = {{{ to64('end') }}}; + return ret; + }, + + // returns the side module metadata as an object + // { memorySize, memoryAlign, tableSize, tableAlign, neededDynlibs} + $getDylinkMetadata__deps: ['$UTF8ArrayToString'], + $getDylinkMetadata__internal: true, + $getDylinkMetadata: (binary) => { + var offset = 0; + var end = 0; + + function getU8() { + return binary[offset++]; + } + + function getLEB() { + var ret = 0; + var mul = 1; + while (1) { + var byte = binary[offset++]; + ret += ((byte & 0x7f) * mul); + mul *= 0x80; + if (!(byte & 0x80)) break; + } + return ret; + } + + function getString() { + var len = getLEB(); + offset += len; + return UTF8ArrayToString(binary, offset - len, len); + } + + /** @param {string=} message */ + function failIf(condition, message) { + if (condition) throw new Error(message); + } + + var name = 'dylink.0'; + if (binary instanceof WebAssembly.Module) { + var dylinkSection = WebAssembly.Module.customSections(binary, name); + if (dylinkSection.length === 0) { + name = 'dylink' + dylinkSection = WebAssembly.Module.customSections(binary, name); + } + failIf(dylinkSection.length === 0, 'need dylink section'); + binary = new Uint8Array(dylinkSection[0]); + end = binary.length + } else { + var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer); +#if SUPPORT_BIG_ENDIAN + var magicNumberFound = int32View[0] == 0x6d736100 || int32View[0] == 0x0061736d; +#else + var magicNumberFound = int32View[0] == 0x6d736100; +#endif + failIf(!magicNumberFound, 'need to see wasm magic number'); // \0asm + // we should see the dylink custom section right after the magic number and wasm version + failIf(binary[8] !== 0, 'need the dylink section to be first') + offset = 9; + var section_size = getLEB(); //section size + end = offset + section_size; + name = getString(); + } + + var customSection = { neededDynlibs: [], tlsExports: new Set(), weakImports: new Set() }; + if (name == 'dylink') { + customSection.memorySize = getLEB(); + customSection.memoryAlign = getLEB(); + customSection.tableSize = getLEB(); + customSection.tableAlign = getLEB(); + // shared libraries this module needs. We need to load them first, so that + // current module could resolve its imports. (see tools/shared.py + // WebAssembly.make_shared_library() for "dylink" section extension format) + var neededDynlibsCount = getLEB(); + for (var i = 0; i < neededDynlibsCount; ++i) { + var libname = getString(); + customSection.neededDynlibs.push(libname); + } + } else { + failIf(name !== 'dylink.0'); + var WASM_DYLINK_MEM_INFO = 0x1; + var WASM_DYLINK_NEEDED = 0x2; + var WASM_DYLINK_EXPORT_INFO = 0x3; + var WASM_DYLINK_IMPORT_INFO = 0x4; + var WASM_SYMBOL_TLS = 0x100; + var WASM_SYMBOL_BINDING_MASK = 0x3; + var WASM_SYMBOL_BINDING_WEAK = 0x1; + while (offset < end) { + var subsectionType = getU8(); + var subsectionSize = getLEB(); + if (subsectionType === WASM_DYLINK_MEM_INFO) { + customSection.memorySize = getLEB(); + customSection.memoryAlign = getLEB(); + customSection.tableSize = getLEB(); + customSection.tableAlign = getLEB(); + } else if (subsectionType === WASM_DYLINK_NEEDED) { + var neededDynlibsCount = getLEB(); + for (var i = 0; i < neededDynlibsCount; ++i) { + libname = getString(); + customSection.neededDynlibs.push(libname); + } + } else if (subsectionType === WASM_DYLINK_EXPORT_INFO) { + var count = getLEB(); + while (count--) { + var symname = getString(); + var flags = getLEB(); + if (flags & WASM_SYMBOL_TLS) { + customSection.tlsExports.add(symname); + } + } + } else if (subsectionType === WASM_DYLINK_IMPORT_INFO) { + var count = getLEB(); + while (count--) { + var modname = getString(); + var symname = getString(); + var flags = getLEB(); + if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) { + customSection.weakImports.add(symname); + } + } + } else { +#if ASSERTIONS + err(`unknown dylink.0 subsection: ${subsectionType}`) +#endif + // unknown subsection + offset += subsectionSize; + } + } + } + +#if ASSERTIONS + var tableAlign = Math.pow(2, customSection.tableAlign); + assert(tableAlign === 1, `invalid tableAlign ${tableAlign}`); + assert(offset == end); +#endif + +#if DYLINK_DEBUG + dbg(`dylink needed:${customSection.neededDynlibs}`); +#endif + + return customSection; + }, + +#if DYNCALLS || !WASM_BIGINT + $registerDynCallSymbols: (exports) => { + for (var [sym, exp] of Object.entries(exports)) { + if (sym.startsWith('dynCall_') && !Module.hasOwnProperty(sym)) { + Module[sym] = exp; + } + } + }, +#endif + + // Module.symbols <- libModule.symbols (flags.global handler) + $mergeLibSymbols__deps: ['$isSymbolDefined'], + $mergeLibSymbols: (exports, libName) => { +#if DYNCALLS || !WASM_BIGINT + registerDynCallSymbols(exports); +#endif + // add symbols into global namespace TODO: weak linking etc. + for (var [sym, exp] of Object.entries(exports)) { +#if ASSERTIONS == 2 + if (isSymbolDefined(sym)) { + var curr = wasmImports[sym], next = exp; + // don't warn on functions - might be odr, linkonce_odr, etc. + if (!(typeof curr == 'function' && typeof next == 'function')) { + err(`warning: symbol '${sym}' from '${libName}' already exists (duplicate symbol? or weak linking, which isn't supported yet?)`); // + [curr, ' vs ', next]); + } + } +#endif + + // When RTLD_GLOBAL is enabled, the symbols defined by this shared object + // will be made available for symbol resolution of subsequently loaded + // shared objects. + // + // We should copy the symbols (which include methods and variables) from + // SIDE_MODULE to MAIN_MODULE. + const setImport = (target) => { +#if ASYNCIFY + if (target in asyncifyStubs) { + asyncifyStubs[target] = exp; + } +#endif + if (!isSymbolDefined(target)) { + wasmImports[target] = exp; + } + } + setImport(sym); + +#if !hasExportedSymbol('main') + // Special case for handling of main symbol: If a side module exports + // `main` that also acts a definition for `__main_argc_argv` and vice + // versa. + const main_alias = '__main_argc_argv'; + if (sym == 'main') { + setImport(main_alias) + } + if (sym == main_alias) { + setImport('main') + } +#endif + } + }, + +#if DYLINK_DEBUG + $dumpTable__deps: ['$wasmTable'], + $dumpTable: () => { + var len = wasmTable.length; + for (var i = {{{ toIndexType(0) }}} ; i < len; i++) { + dbg(`table: ${i} : ${wasmTable.get(i)}`); + } + }, +#endif + + // Loads a side module from binary data or compiled Module. Returns the module's exports or a + // promise that resolves to its exports if the loadAsync flag is set. + $loadWebAssemblyModule__docs: ` + /** + * @param {string=} libName + * @param {Object=} localScope + * @param {number=} handle + */`, + $loadWebAssemblyModule__deps: [ + '$loadDynamicLibrary', '$getMemory', + '$relocateExports', '$resolveGlobalSymbol', '$GOTHandler', + '$getDylinkMetadata', '$alignMemory', + '$currentModuleWeakSymbols', + '$updateTableMap', + '$wasmTable', + ], + $loadWebAssemblyModule: (binary, flags, libName, localScope, handle) => { +#if DYLINK_DEBUG + dbg(`loadWebAssemblyModule: ${libName}`); +#endif + var metadata = getDylinkMetadata(binary); + currentModuleWeakSymbols = metadata.weakImports; +#if ASSERTIONS + var originalTable = wasmTable; +#endif + + // loadModule loads the wasm module after all its dependencies have been loaded. + // can be called both sync/async. + function loadModule() { + // The first thread to load a given module needs to allocate the static + // table and memory regions. Later threads re-use the same table region + // and can ignore the memory region (since memory is shared between + // threads already). + // If `handle` is specified than it is assumed that the calling thread has + // exclusive access to it for the duration of this function. See the + // locking in `dynlink.c`. + var firstLoad = !handle || !{{{ makeGetValue('handle', C_STRUCTS.dso.mem_allocated, 'i8') }}}; + if (firstLoad) { + // alignments are powers of 2 + var memAlign = Math.pow(2, metadata.memoryAlign); + // prepare memory + var memoryBase = metadata.memorySize ? alignMemory(getMemory(metadata.memorySize + memAlign), memAlign) : 0; // TODO: add to cleanups + var tableBase = metadata.tableSize ? {{{ from64Expr('wasmTable.length') }}} : 0; + if (handle) { + {{{ makeSetValue('handle', C_STRUCTS.dso.mem_allocated, '1', 'i8') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.mem_addr, 'memoryBase', '*') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.mem_size, 'metadata.memorySize', 'i32') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.table_addr, 'tableBase', '*') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.table_size, 'metadata.tableSize', 'i32') }}}; + } + } else { + memoryBase = {{{ makeGetValue('handle', C_STRUCTS.dso.mem_addr, '*') }}}; + tableBase = {{{ makeGetValue('handle', C_STRUCTS.dso.table_addr, '*') }}}; + } + + var tableGrowthNeeded = tableBase + metadata.tableSize - {{{ from64Expr('wasmTable.length') }}}; + if (tableGrowthNeeded > 0) { +#if DYLINK_DEBUG + dbg("loadModule: growing table: " + tableGrowthNeeded); +#endif + wasmTable.grow({{{ toIndexType('tableGrowthNeeded') }}}); + } +#if DYLINK_DEBUG + dbg("loadModule: memory[" + memoryBase + ":" + (memoryBase + metadata.memorySize) + "]" + + " table[" + tableBase + ":" + (tableBase + metadata.tableSize) + "]"); +#endif + + // This is the export map that we ultimately return. We declare it here + // so it can be used within resolveSymbol. We resolve symbols against + // this local symbol map in the case there they are not present on the + // global Module object. We need this fallback because Modules sometime + // need to import their own symbols + var moduleExports; + + function resolveSymbol(sym) { + var resolved = resolveGlobalSymbol(sym).sym; + if (!resolved && localScope) { + resolved = localScope[sym]; + } + if (!resolved) { + resolved = moduleExports[sym]; + } +#if ASSERTIONS + assert(resolved, `undefined symbol '${sym}'. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment`); +#endif + return resolved; + } + + // TODO kill ↓↓↓ (except "symbols local to this module", it will likely be + // not needed if we require that if A wants symbols from B it has to link + // to B explicitly: similarly to -Wl,--no-undefined) + // + // wasm dynamic libraries are pure wasm, so they cannot assist in + // their own loading. When side module A wants to import something + // provided by a side module B that is loaded later, we need to + // add a layer of indirection, but worse, we can't even tell what + // to add the indirection for, without inspecting what A's imports + // are. To do that here, we use a JS proxy (another option would + // be to inspect the binary directly). + var proxyHandler = { + get(stubs, prop) { + // symbols that should be local to this module + switch (prop) { + case '__memory_base': + return {{{ to64('memoryBase') }}}; + case '__table_base': + return {{{ to64('tableBase') }}}; +#if MEMORY64 +#if MEMORY64 == 2 + case '__memory_base32': + return memoryBase; +#endif + case '__table_base32': + return tableBase; +#endif + } + if (prop in wasmImports && !wasmImports[prop].stub) { + // No stub needed, symbol already exists in symbol table + return wasmImports[prop]; + } + // Return a stub function that will resolve the symbol + // when first called. + if (!(prop in stubs)) { + var resolved; + stubs[prop] = (...args) => { + resolved ||= resolveSymbol(prop); + if (!resolved) { +/* + if (prop==='getTempRet0') + return __emscripten_tempret_get(...args); + if (prop==='setTempRet0') + return __emscripten_tempret_set(...args); +*/ + throw new Error(); + } + return resolved(...args); + }; + } + return stubs[prop]; + } + }; + var proxy = new Proxy({}, proxyHandler); + var info = { + 'GOT.mem': new Proxy({}, GOTHandler), + 'GOT.func': new Proxy({}, GOTHandler), + 'env': proxy, + '{{{ WASI_MODULE_NAME }}}': proxy, + }; + + function postInstantiation(module, instance) { +#if ASSERTIONS + // the table should be unchanged + assert(wasmTable === originalTable); +#endif +#if PTHREADS + if (!ENVIRONMENT_IS_PTHREAD && libName) { +#if DYLINK_DEBUG + dbg(`registering sharedModules: ${libName}`) +#endif + // cache all loaded modules in `sharedModules`, which gets passed + // to new workers when they are created. + sharedModules[libName] = module; + } +#endif + // add new entries to functionsInTableMap + updateTableMap(tableBase, metadata.tableSize); + moduleExports = relocateExports(instance.exports, memoryBase); +#if ASYNCIFY + moduleExports = Asyncify.instrumentWasmExports(moduleExports); +#endif + if (!flags.allowUndefined) { + reportUndefinedSymbols(); + } +#if STACK_OVERFLOW_CHECK >= 2 + // If the runtime has already been initialized we set the stack limits + // now. Otherwise this is delayed until `setDylinkStackLimits` is + // called after initialization. + if (moduleExports['__set_stack_limits'] && runtimeInitialized) { + moduleExports['__set_stack_limits']({{{ to64('_emscripten_stack_get_base()') }}}, {{{ to64('_emscripten_stack_get_end()') }}}); + } +#endif + +#if MAIN_MODULE + function addEmAsm(addr, body) { + var args = []; + var arity = 0; + for (; arity < 16; arity++) { + if (body.indexOf('$' + arity) != -1) { + args.push('$' + arity); + } else { + break; + } + } + args = args.join(','); + var func = `(${args}) => { ${body} };`; +#if DYLINK_DEBUG + dbg(`adding new EM_ASM constant at: ${ptrToString(start)}`); +#endif + {{{ makeEval('ASM_CONSTS[start] = eval(func)') }}}; + } + + // Add any EM_ASM function that exist in the side module + if ('__start_em_asm' in moduleExports) { + var start = moduleExports['__start_em_asm']; + var stop = moduleExports['__stop_em_asm']; + {{{ from64('start') }}} + {{{ from64('stop') }}} + while (start < stop) { + var jsString = UTF8ToString(start); + addEmAsm(start, jsString); + start = HEAPU8.indexOf(0, start) + 1; + } + } + + function addEmJs(name, cSig, body) { + // The signature here is a C signature (e.g. "(int foo, char* bar)"). + // See `create_em_js` in emcc.py` for the build-time version of this + // code. + var jsArgs = []; + cSig = cSig.slice(1, -1) + if (cSig != 'void') { + cSig = cSig.split(','); + for (var i in cSig) { + var jsArg = cSig[i].split(' ').pop(); + jsArgs.push(jsArg.replaceAll('*', '')); + } + } + var func = `(${jsArgs}) => ${body};`; +#if DYLINK_DEBUG + dbg(`adding new EM_JS function: ${jsArgs} = ${func}`); +#endif + {{{ makeEval('moduleExports[name] = eval(func)') }}}; + } + + for (var name in moduleExports) { + if (name.startsWith('__em_js__')) { + var start = moduleExports[name] + var jsString = UTF8ToString({{{ from64Expr('start') }}}); + // EM_JS strings are stored in the data section in the form + // SIG<::>BODY. + var parts = jsString.split('<::>'); + addEmJs(name.replace('__em_js__', ''), parts[0], parts[1]); + delete moduleExports[name]; + } + } +#endif + + // initialize the module +#if PTHREADS + // Only one thread should call __wasm_call_ctors, but all threads need + // to call _emscripten_tls_init + registerTLSInit(moduleExports['_emscripten_tls_init'], instance.exports, metadata) + if (firstLoad) { +#endif + var applyRelocs = moduleExports['__wasm_apply_data_relocs']; + if (applyRelocs) { + if (runtimeInitialized) { +#if DYLINK_DEBUG + dbg('applyRelocs'); +#endif + applyRelocs(); + } else { + __RELOC_FUNCS__.push(applyRelocs); + } + } + var init = moduleExports['__wasm_call_ctors']; + if (init) { + if (runtimeInitialized) { + init(); + } else { + // we aren't ready to run compiled code yet + __ATINIT__.push(init); + } + } +#if PTHREADS + } +#endif + return moduleExports; + } + + if (flags.loadAsync) { + if (binary instanceof WebAssembly.Module) { + var instance = new WebAssembly.Instance(binary, info); + return Promise.resolve(postInstantiation(binary, instance)); + } + return WebAssembly.instantiate(binary, info).then( + (result) => postInstantiation(result.module, result.instance) + ); + } + + var module = binary instanceof WebAssembly.Module ? binary : new WebAssembly.Module(binary); + var instance = new WebAssembly.Instance(module, info); + return postInstantiation(module, instance); + } + + // now load needed libraries and the module itself. + if (flags.loadAsync) { + return metadata.neededDynlibs + .reduce((chain, dynNeeded) => chain.then(() => + loadDynamicLibrary(dynNeeded, flags, localScope) + ), Promise.resolve()) + .then(loadModule); + } + + metadata.neededDynlibs.forEach((needed) => loadDynamicLibrary(needed, flags, localScope)); + return loadModule(); + }, + +#if STACK_OVERFLOW_CHECK >= 2 + // Sometimes we load libraries before runtime initialization. In this case + // we delay calling __set_stack_limits (which must be called for each + // module). + $setDylinkStackLimits: (stackTop, stackMax) => { + for (var name in LDSO.loadedLibsByName) { +#if DYLINK_DEBUG + dbg(`setDylinkStackLimits for '${name}'`); +#endif + var lib = LDSO.loadedLibsByName[name]; + lib.exports['__set_stack_limits']?.({{{ to64("stackTop") }}}, {{{ to64("stackMax") }}}); + } + }, +#endif + + $newDSO: (name, handle, syms) => { + var dso = { + refcount: Infinity, + name, + exports: syms, + global: true, + }; + LDSO.loadedLibsByName[name] = dso; + if (handle != undefined) { + LDSO.loadedLibsByHandle[handle] = dso; + } + return dso; + }, + + // loadDynamicLibrary loads dynamic library @ lib URL / path and returns + // handle for loaded DSO. + // + // Several flags affect the loading: + // + // - if flags.global=true, symbols from the loaded library are merged into global + // process namespace. Flags.global is thus similar to RTLD_GLOBAL in ELF. + // + // - if flags.nodelete=true, the library will be never unloaded. Flags.nodelete + // is thus similar to RTLD_NODELETE in ELF. + // + // - if flags.loadAsync=true, the loading is performed asynchronously and + // loadDynamicLibrary returns corresponding promise. + // + // If a library was already loaded, it is not loaded a second time. However + // flags.global and flags.nodelete are handled every time a load request is made. + // Once a library becomes "global" or "nodelete", it cannot be removed or unloaded. + $loadDynamicLibrary__deps: ['$LDSO', '$loadWebAssemblyModule', + '$isInternalSym', '$mergeLibSymbols', '$newDSO', + '$asyncLoad', +#if FILESYSTEM + '$preloadedWasm', +#endif +#if DYNCALLS || !WASM_BIGINT + '$registerDynCallSymbols', +#endif + ], + $loadDynamicLibrary__docs: ` + /** + * @param {number=} handle + * @param {Object=} localScope + */`, + $loadDynamicLibrary: function(libName, flags = {global: true, nodelete: true}, localScope, handle) { +#if DYLINK_DEBUG + dbg(`loadDynamicLibrary: ${libName} handle: ${handle}`); + dbg(`existing: ${Object.keys(LDSO.loadedLibsByName)}`); +#endif + // when loadDynamicLibrary did not have flags, libraries were loaded + // globally & permanently + + var dso = LDSO.loadedLibsByName[libName]; + if (dso) { + // the library is being loaded or has been loaded already. +#if ASSERTIONS + assert(dso.exports !== 'loading', `Attempt to load '${libName}' twice before the first load completed`); +#endif + if (!flags.global) { + if (localScope) { + Object.assign(localScope, dso.exports); + } +#if DYNCALLS || !WASM_BIGINT + registerDynCallSymbols(dso.exports); +#endif + } else if (!dso.global) { + // The library was previously loaded only locally but not + // we have a request with global=true. + dso.global = true; + mergeLibSymbols(dso.exports, libName) + } + // same for "nodelete" + if (flags.nodelete && dso.refcount !== Infinity) { + dso.refcount = Infinity; + } + dso.refcount++ + if (handle) { + LDSO.loadedLibsByHandle[handle] = dso; + } + return flags.loadAsync ? Promise.resolve(true) : true; + } + + // allocate new DSO + dso = newDSO(libName, handle, 'loading'); + dso.refcount = flags.nodelete ? Infinity : 1; + dso.global = flags.global; + + // libName -> libData + function loadLibData() { +#if PTHREADS + var sharedMod = sharedModules[libName]; +#if DYLINK_DEBUG + dbg(`checking sharedModules: ${libName}: ${sharedMod ? 'found' : 'not found'}`); +#endif + if (sharedMod) { + return flags.loadAsync ? Promise.resolve(sharedMod) : sharedMod; + } +#endif + + // for wasm, we can use fetch for async, but for fs mode we can only imitate it + if (handle) { + var data = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data, '*') }}}; + var dataSize = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data_size, '*') }}}; + if (data && dataSize) { + var libData = HEAP8.slice(data, data + dataSize); + return flags.loadAsync ? Promise.resolve(libData) : libData; + } + } + + var libFile = locateFile(libName); + if (flags.loadAsync) { + return asyncLoad(libFile); + } + + // load the binary synchronously + if (!readBinary) { + throw new Error(`${libFile}: file not found, and synchronous loading of external files is not available`); + } + return readBinary(libFile); + } + + // libName -> exports + function getExports() { +#if FILESYSTEM + // lookup preloaded cache first + var preloaded = preloadedWasm[libName]; +#if DYLINK_DEBUG + dbg(`checking preloadedWasm: ${libName}: ${preloaded ? 'found' : 'not found'}`); +#endif + if (preloaded) { + return flags.loadAsync ? Promise.resolve(preloaded) : preloaded; + } +#endif + + // module not preloaded - load lib data and create new module from it + if (flags.loadAsync) { + return loadLibData().then((libData) => loadWebAssemblyModule(libData, flags, libName, localScope, handle)); + } + + return loadWebAssemblyModule(loadLibData(), flags, libName, localScope, handle); + } + + // module for lib is loaded - update the dso & global namespace + function moduleLoaded(exports) { + if (dso.global) { + mergeLibSymbols(exports, libName); + } else if (localScope) { + Object.assign(localScope, exports); +#if DYNCALLS || !WASM_BIGINT + registerDynCallSymbols(exports); +#endif + } + dso.exports = exports; + } + + if (flags.loadAsync) { +#if DYLINK_DEBUG + dbg("loadDynamicLibrary: done (async)"); +#endif + return getExports().then((exports) => { + moduleLoaded(exports); + return true; + }); + } + + moduleLoaded(getExports()); +#if DYLINK_DEBUG + dbg("loadDynamicLibrary: done"); +#endif + return true; + }, + + $loadDylibs__internal: true, + $loadDylibs__deps: ['$loadDynamicLibrary', '$reportUndefinedSymbols'], + $loadDylibs: () => { + if (!dynamicLibraries.length) { +#if DYLINK_DEBUG + dbg('loadDylibs: no libraries to preload'); +#endif + reportUndefinedSymbols(); + return; + } + +#if DYLINK_DEBUG + dbg(`loadDylibs: ${dynamicLibraries}`); +#endif + + // Load binaries asynchronously + addRunDependency('loadDylibs'); + dynamicLibraries + .reduce((chain, lib) => chain.then(() => + loadDynamicLibrary(lib, {loadAsync: true, global: true, nodelete: true, allowUndefined: true}) + ), Promise.resolve()) + .then(() => { + // we got them all, wonderful + reportUndefinedSymbols(); + removeRunDependency('loadDylibs'); + #if DYLINK_DEBUG + dbg('loadDylibs done!'); + #endif + }); + }, + + // void* dlopen(const char* filename, int flags); + $dlopenInternal__deps: ['$ENV', '$dlSetError', '$PATH'], + $dlopenInternal: (handle, jsflags) => { + // void *dlopen(const char *file, int mode); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html + var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}}); + var flags = {{{ makeGetValue('handle', C_STRUCTS.dso.flags, 'i32') }}}; +#if DYLINK_DEBUG + dbg(`dlopenInternal: ${filename}`); +#endif + filename = PATH.normalize(filename); + var searchpaths = []; + + var global = Boolean(flags & {{{ cDefs.RTLD_GLOBAL }}}); + var localScope = global ? null : {}; + + // We don't care about RTLD_NOW and RTLD_LAZY. + var combinedFlags = { + global, + nodelete: Boolean(flags & {{{ cDefs.RTLD_NODELETE }}}), + loadAsync: jsflags.loadAsync, + } + + if (jsflags.loadAsync) { + return loadDynamicLibrary(filename, combinedFlags, localScope, handle); + } + + try { + return loadDynamicLibrary(filename, combinedFlags, localScope, handle) + } catch (e) { +#if ASSERTIONS + err(`Error in loading dynamic library ${filename}: ${e}`); +#endif + dlSetError(`Could not load dynamic lib: ${filename}\n${e}`); + return 0; + } + }, + + _dlopen_js__deps: ['$dlopenInternal'], +#if ASYNCIFY + _dlopen_js__async: true, +#endif + _dlopen_js: (handle) => { +#if ASYNCIFY + return Asyncify.handleSleep((wakeUp) => { + dlopenInternal(handle, { loadAsync: true }) + .then(wakeUp) + .catch(() => wakeUp(0)); + }); +#else + return dlopenInternal(handle, { loadAsync: false }); +#endif + }, + + // Async version of dlopen. + _emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError'], + _emscripten_dlopen_js: (handle, onsuccess, onerror, user_data) => { + /** @param {Object=} e */ + function errorCallback(e) { + var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}}); + dlSetError(`'Could not load dynamic lib: ${filename}\n${e}`); + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => {{{ makeDynCall('vpp', 'onerror') }}}(handle, user_data)); + } + function successCallback() { + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => {{{ makeDynCall('vpp', 'onsuccess') }}}(handle, user_data)); + } + + {{{ runtimeKeepalivePush() }}} + var promise = dlopenInternal(handle, { loadAsync: true }); + if (promise) { + promise.then(successCallback, errorCallback); + } else { + errorCallback(); + } + }, + + _dlsym_catchup_js: (handle, symbolIndex) => { +#if DYLINK_DEBUG + dbg("_dlsym_catchup: handle=" + ptrToString(handle) + " symbolIndex=" + symbolIndex); +#endif + var lib = LDSO.loadedLibsByHandle[handle]; + var symDict = lib.exports; + var symName = Object.keys(symDict)[symbolIndex]; + var sym = symDict[symName]; + var result = addFunction(sym, sym.sig); +#if DYLINK_DEBUG + dbg(`_dlsym_catchup: result=${result}`); +#endif + return result; + }, + + // void* dlsym(void* handle, const char* symbol); + _dlsym_js__deps: ['$dlSetError', '$getFunctionAddress', '$addFunction'], + _dlsym_js: (handle, symbol, symbolIndex) => { + // void *dlsym(void *restrict handle, const char *restrict name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html + symbol = UTF8ToString(symbol); +#if DYLINK_DEBUG + dbg(`dlsym_js: ${symbol}`); +#endif + var result; + var newSymIndex; + + var lib = LDSO.loadedLibsByHandle[handle]; +#if ASSERTIONS + assert(lib, `Tried to dlsym() from an unopened handle: ${handle}`); +#endif + if (!lib.exports.hasOwnProperty(symbol) || lib.exports[symbol].stub) { + dlSetError(`Tried to lookup unknown symbol "${symbol}" in dynamic lib: ${lib.name}`) + return 0; + } + newSymIndex = Object.keys(lib.exports).indexOf(symbol); +#if !WASM_BIGINT + var origSym = 'orig$' + symbol; + result = lib.exports[origSym]; + if (result) { + newSymIndex = Object.keys(lib.exports).indexOf(origSym); + } + else +#endif + result = lib.exports[symbol]; + + if (typeof result == 'function') { +#if DYLINK_DEBUG + dbg(`dlsym_js: ${symbol} getting table slot for: ${result}`); +#endif + +#if ASYNCIFY + // Asyncify wraps exports, and we need to look through those wrappers. + if ('orig' in result) { + result = result.orig; + } +#endif + var addr = getFunctionAddress(result); + if (addr) { +#if DYLINK_DEBUG + dbg(`symbol already exists in table: ${symbol}`); +#endif + result = addr; + } else { + // Insert the function into the wasm table. If its a direct wasm + // function the second argument will not be needed. If its a JS + // function we rely on the `sig` attribute being set based on the + // `__sig` specified in library JS file. + result = addFunction(result, result.sig); +#if DYLINK_DEBUG + dbg(`adding symbol to table: ${symbol}`); +#endif + {{{ makeSetValue('symbolIndex', 0, 'newSymIndex', '*') }}}; + } + } +#if DYLINK_DEBUG + dbg(`dlsym_js: ${symbol} -> ${result}`); +#endif + return result; + }, +}; + +addToLibrary(LibraryDylink); From d7577b660b22091d2abc52e293bfb33667054b1f Mon Sep 17 00:00:00 2001 From: pmp-p Date: Thu, 31 Jul 2025 13:50:41 +0200 Subject: [PATCH 4/7] compare hotfixed --- .buildconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildconfig b/.buildconfig index a518dff8eedcc..ac9d2913c9535 100644 --- a/.buildconfig +++ b/.buildconfig @@ -1,6 +1,6 @@ PG_VERSION=17.5 PG_BRANCH=REL_17_5_WASM -SDK_VERSION=3.1.74.12.0 +SDK_VERSION=3.1.74.11.11 SDKROOT=/tmp/sdk GETZIC=false ZIC=/usr/sbin/zic From 7dbc9f69729a5a50effc0851a3c5d7ac72411c60 Mon Sep 17 00:00:00 2001 From: Marcus Pousette Date: Mon, 8 Sep 2025 13:54:09 +0200 Subject: [PATCH 5/7] Improve MemoryContext management to prevent memory exhaustion between queries (#47) * Improve MemoryContext management to prevent memory exhaustion between queries * Use MemoryContextReset; drop legacy MemoryContextResetAndDeleteChildren macro * Cleanup: remove extraneous comment; keep MemoryContextReset only --- pglite-wasm/interactive_one.c | 2 +- pglite-wasm/pg_main.c | 2 -- pglite-wasm/pgl_mains.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pglite-wasm/interactive_one.c b/pglite-wasm/interactive_one.c index fc3590961d5d6..124e99c5059f4 100644 --- a/pglite-wasm/interactive_one.c +++ b/pglite-wasm/interactive_one.c @@ -416,7 +416,7 @@ if (cma_rsize<0) } MemoryContextSwitchTo(MessageContext); - MemoryContextResetAndDeleteChildren(MessageContext); + MemoryContextReset(MessageContext); initStringInfo(&input_message); diff --git a/pglite-wasm/pg_main.c b/pglite-wasm/pg_main.c index 65e5d7ac01d2c..07c21cf1b5b24 100644 --- a/pglite-wasm/pg_main.c +++ b/pglite-wasm/pg_main.c @@ -28,8 +28,6 @@ // globals -#define MemoryContextResetAndDeleteChildren(...) -// #define SpinLockInit(...) diff --git a/pglite-wasm/pgl_mains.c b/pglite-wasm/pgl_mains.c index 98f425a6e4116..7dc200745a4d7 100644 --- a/pglite-wasm/pgl_mains.c +++ b/pglite-wasm/pgl_mains.c @@ -45,7 +45,7 @@ interactive_file() { * query input buffer in the cleared MessageContext. */ MemoryContextSwitchTo(MessageContext); - MemoryContextResetAndDeleteChildren(MessageContext); + MemoryContextReset(MessageContext); initStringInfo(&input_message); inBuf = &input_message; From c0269765ed89b8df428a91192d16ffbfdb8176e0 Mon Sep 17 00:00:00 2001 From: Tudor Zaharia <37596026+tdrz@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:46:51 +0200 Subject: [PATCH 6/7] Switch to vanilla emsdk for building pglite (#48) * first pass * skip postgresql tests as usual * preloaded files; pglite.data file autotools handling * new pglite and install-pglite Makefile targets; workflow succesful; * add pglite.{data,js,html} to gitignore * remove the need for emsdk-shared script * add password and PGPASSFILE static files * use system zic from /usr/sbin/zic for timezone * use static files from repo when preloading password and PGPASSFILE * add empty file * builder dockerfile; external deps source code * use -sMAIN_MODULE=2 which reduces size code * building with -sMAIN_MODULE=2 to reduce size * use file containing needed EXPORTED_FUNCTIONS - tested on data generated from the *.o files of plpgsql + the already predefined ones; -sDYLINK_DEBUG=2 flag is very useful in debugging emscripten's dynamic linking; generating the EXPORTED_FUNCTIONS needs to be automated * automate creation of imports needed by plpgsql; automate creation of EXPORTED_FUNCTIONS emscripten parameter; * infrastructure for contrib extensions * more excluded imports * link pglite.wasm against more libs so the exports are visible for emscripten * more EXPORTED_FUNCTIONS excludes * more EXPORTED_FUNCTIONS includes * extended docker image to contain the exports of the prebuilt libraries * change the exports format of shared libs * extend backend makefile to better handle EXPORTED_FUNCTIONS depending on shared libs' exports * git: ignore .map files * added pg_ivm as submodule; added Makefile to build such submodules * added pgvector as submodule and updated the Makefile to build it * use env var to point to llvm-nm for exports and imports defs * exclude libossp-uuid exports and dont link libossp-uuid to the exe * git ignore excluded.imports file * new simple bash script to build everything; allow debug builds * cleanup * added bashs script to clean everything * unset DEBUG var if it is false - there is a bug somewhere and until we fix that, this works; bug fix in passing PGLITE_CFLAGS * further simplify build-with-docker2.sh script * build pglite script remove SOURCE_MAP_PREFIXES * comments * rename script * add 2 more exported functions; sort the always included imports * separate dockerfiles for different archs - this is needed because emsdk does not publish different archs under the same tag; hopefully this will be fixed in the future * contrib extensions packaging; integration into the build of the said extensions * better error message when building/disting contribs * change custom build root for pglite contrib * add LDFLAGS_SL="-sSIDE_MODULE=1" when building contrib extensions * renamed pgvector submodule to vector * dist extra extensions insted of install (packaging pgvector and pg_ivm into tar.gz) * use pushed docker image * build only using native build platform, since wasm is the same on all platforms * remove unused dockerfiles * cleanup src/backend/Makefile * remove wasm-builder folder * added comment to other extensions Makefile * comments * remove binaries used for pglite-builder * remove config.site since the param that it contains is already passed in the command line * update clean-pglite.sh script --------- Co-authored-by: tudor --- .gitignore | 9 + .gitmodules | 6 + build-pglite.sh | 46 +++++ build-with-docker.sh | 13 ++ clean-pglite.sh | 6 + contrib/Makefile | 1 + contrib/dist.mk | 27 +++ other/PGPASSFILE | 3 + other/empty | 0 other/password | 1 + pglite-wasm/builder/Dockerfile | 89 +++++++++ pglite-wasm/excluded.pglite.imports | 32 ++++ pglite-wasm/included.pglite.imports | 38 ++++ pglite-wasm/pg_main.c | 7 +- pglite/Makefile | 35 ++++ pglite/pg_ivm | 1 + pglite/vector | 1 + src/Makefile.shlib | 5 +- src/backend/Makefile | 79 +++++++- src/backend/libpq/pqcomm.c | 4 + src/bin/initdb/Makefile | 9 + src/bin/pg_amcheck/Makefile | 9 + src/bin/pg_archivecleanup/Makefile | 9 + src/bin/pg_basebackup/Makefile | 15 ++ src/bin/pg_checksums/Makefile | 9 + src/bin/pg_combinebackup/Makefile | 9 + src/bin/pg_config/Makefile | 27 +++ src/bin/pg_controldata/Makefile | 9 + src/bin/pg_ctl/Makefile | 9 + src/bin/pg_dump/Makefile | 11 ++ src/bin/pg_resetwal/Makefile | 9 + src/bin/pg_rewind/Makefile | 9 + src/bin/pg_test_fsync/Makefile | 9 + src/bin/pg_test_timing/Makefile | 9 + src/bin/pg_upgrade/Makefile | 9 + src/bin/pg_verifybackup/Makefile | 9 + src/bin/pg_waldump/Makefile | 9 + src/bin/pg_walsummary/Makefile | 9 + src/bin/pgbench/Makefile | 9 + src/bin/psql/Makefile | 9 + src/bin/scripts/Makefile | 16 ++ src/include/port/emscripten.h | 7 +- src/include/port/pg_debug.h | 8 + src/include/port/sdk_port.h | 262 +++++++++++++++++++++++++++ src/include/port/wasm_common.h | 2 +- src/interfaces/ecpg/preproc/Makefile | 9 + src/makefiles/Makefile.emscripten | 6 + src/makefiles/pgxs.mk | 22 +++ src/pl/plpgsql/src/Makefile | 12 ++ src/test/Makefile | 8 + src/test/isolation/Makefile | 8 + src/timezone/Makefile | 7 + wasm-build/{ => include}/sdk_port.h | 0 wasm-builder/Dockerfile | 23 --- wasm-builder/build-with-docker.sh | 69 ------- 55 files changed, 968 insertions(+), 100 deletions(-) create mode 100644 .gitmodules create mode 100755 build-pglite.sh create mode 100755 build-with-docker.sh create mode 100755 clean-pglite.sh create mode 100644 contrib/dist.mk create mode 100644 other/PGPASSFILE create mode 100644 other/empty create mode 100644 other/password create mode 100644 pglite-wasm/builder/Dockerfile create mode 100644 pglite-wasm/excluded.pglite.imports create mode 100644 pglite-wasm/included.pglite.imports create mode 100644 pglite/Makefile create mode 160000 pglite/pg_ivm create mode 160000 pglite/vector create mode 100644 src/include/port/pg_debug.h create mode 100644 src/include/port/sdk_port.h rename wasm-build/{ => include}/sdk_port.h (100%) delete mode 100644 wasm-builder/Dockerfile delete mode 100755 wasm-builder/build-with-docker.sh diff --git a/.gitignore b/.gitignore index e76ef7cc785ea..2e67d7c8e6671 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,12 @@ lib*.pc /tmp_install/ /portlock/ /dist + +# emscripten build excludes +*.cjs +*.wasm +pglite.data +pglite.js +pglite.html +*.map +pglite-wasm/excluded.imports diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..9317a32956edc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "pglite/pg_ivm"] + path = pglite/pg_ivm + url = https://github.com/sraoss/pg_ivm.git +[submodule "pglite/pgvector"] + path = pglite/vector + url = https://github.com/pgvector/pgvector.git diff --git a/build-pglite.sh b/build-pglite.sh new file mode 100755 index 0000000000000..7fa05c3900d95 --- /dev/null +++ b/build-pglite.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +### NOTES ### +# $INSTALL_PREFIX is expected to point to the installation folder of various libraries built to wasm (see pglite-builder) +############# + +# final output folder +INSTALL_FOLDER=${INSTALL_FOLDER:-"/install/pglite"} + +# build with optimizations by default aka release +PGLITE_CFLAGS="-O2" +if [ "$DEBUG" = true ] +then + echo "pglite: building debug version." + PGLITE_CFLAGS="-g -gsource-map --no-wasm-opt" +else + echo "pglite: building release version." + # we shouldn't need to do this, but there's a bug somewhere that prevents a successful build if this is set + unset DEBUG +fi + +echo "pglite: PGLITE_CFLAGS=$PGLITE_CFLAGS" + +# Step 1: configure the project +LDFLAGS="-sWASM_BIGINT -sUSE_PTHREADS=0" CFLAGS="${PGLITE_CFLAGS} -sWASM_BIGINT -fpic -sENVIRONMENT=node,web,worker -sSUPPORT_LONGJMP=emscripten -DPYDK=1 -DCMA_MB=12 -Wno-declaration-after-statement -Wno-macro-redefined -Wno-unused-function -Wno-missing-prototypes -Wno-incompatible-pointer-types" emconfigure ./configure ac_cv_exeext=.cjs --disable-spinlocks --disable-largefile --without-llvm --without-pam --disable-largefile --with-openssl=no --without-readline --without-icu --with-includes=$INSTALL_PREFIX/include:$INSTALL_PREFIX/include/libxml2 --with-libraries=$INSTALL_PREFIX/lib --with-uuid=ossp --with-zlib --with-libxml --with-libxslt --with-template=emscripten --prefix=$INSTALL_FOLDER || { echo 'error: emconfigure failed' ; exit 11; } + +# Step 2: make and install all except pglite +emmake make PORTNAME=emscripten -j || { echo 'error: emmake make PORTNAME=emscripten -j' ; exit 21; } +emmake make PORTNAME=emscripten install || { echo 'error: emmake make PORTNAME=emscripten install' ; exit 22; } + +# Step 3.1: make all contrib extensions - do not install +emmake make PORTNAME=emscripten LDFLAGS_SL="-sSIDE_MODULE=1" -C contrib/ -j || { echo 'error: emmake make PORTNAME=emscripten -C contrib/ -j' ; exit 31; } +# Step 3.2: make dist contrib extensions - this will create an archive for each extension +emmake make PORTNAME=emscripten -C contrib/ dist || { echo 'error: emmake make PORTNAME=emscripten -C contrib/ dist' ; exit 32; } +# the above will also create a file with the imports that each extension needs - we pass these as input in the next step for emscripten to keep alive + +# Step 4: make and dist other extensions +SAVE_PATH=$PATH +PATH=$PATH:$INSTALL_FOLDER/bin +emmake make OPTFLAGS="" PORTNAME=emscripten -j -C pglite || { echo 'error: emmake make OPTFLAGS="" PORTNAME=emscripten -j -C pglite' ; exit 41; } +emmake make OPTFLAGS="" PORTNAME=emscripten LDFLAGS_SL="-sSIDE_MODULE=1" -C pglite/ dist || { echo 'error: make OPTFLAGS="" PORTNAME=emscripten LDFLAGS_SL="-sSIDE_MODULE=1" -C pglite/ dist ' ; exit 42; } +PATH=$SAVE_PATH + +# Step 5: make and install pglite +# Building pglite itself needs to be the last step because of the PRELOAD_FILES parameter (a list of files and folders) need to be available. +PGLITE_CFLAGS=$PGLITE_CFLAGS emmake make PORTNAME=emscripten -j -C src/backend/ install-pglite || { echo 'emmake make OPTFLAGS="" PORTNAME=emscripten -j -C pglite' ; exit 51; } diff --git a/build-with-docker.sh b/build-with-docker.sh new file mode 100755 index 0000000000000..d3d31a17fdc75 --- /dev/null +++ b/build-with-docker.sh @@ -0,0 +1,13 @@ +# although we could use any path inside docker, using the same path as on the host +# allows the DWARF info (when building in DEBUG) to contain the correct file paths +DOCKER_WORKSPACE=$(pwd) + +docker run $@ \ + --rm \ + -e DEBUG=${DEBUG:-false} \ + --workdir=${DOCKER_WORKSPACE} \ + -v .:${DOCKER_WORKSPACE}:rw \ + -v ./dist:/install/pglite:rw \ + electricsql/pglite-builder:3.1.74_1 \ + ./build-pglite.sh + diff --git a/clean-pglite.sh b/clean-pglite.sh new file mode 100755 index 0000000000000..3d6c86458baa6 --- /dev/null +++ b/clean-pglite.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +emmake make -C src/backend uninstall; emmake make -C src/backend clean; +emmake make -C pglite/ clean; emmake make -C pglite/ uninstall; +emmake make -C contrib/ clean; emmake make -C contrib/ uninstall; emmake make -C pglite clean; emmake make -C pglite uninstall; +emmake make clean; emmake make uninstal \ No newline at end of file diff --git a/contrib/Makefile b/contrib/Makefile index abd780f277405..603636cd4d248 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -90,6 +90,7 @@ endif # Missing: # start-scripts \ (does not have a makefile) +include ./dist.mk $(recurse) $(recurse_always) diff --git a/contrib/dist.mk b/contrib/dist.mk new file mode 100644 index 0000000000000..df3933f01fb82 --- /dev/null +++ b/contrib/dist.mk @@ -0,0 +1,27 @@ +# contrib/dist.mk +# +# Package each contrib extension into its own .tar.gz archive + +prefix ?= /install/pglite +CONTRIB_BUILD_ROOT := /tmp/extensions/build +ARCHIVE_DIR := /install/pglite/extensions + +CONTRIBS := $(SUBDIRS) + +# Default target: build tarballs for all contribs +dist: $(addsuffix .tar.gz,$(CONTRIBS)) + +# Pattern rule: build $(EXT).tar.gz for each contrib +%.tar.gz: + @echo "=== Staging $* ===" + rm -rf $(CONTRIB_BUILD_ROOT)/$* + bash -c 'mkdir -p $(CONTRIB_BUILD_ROOT)/$*/$(prefix)/{bin,lib,share/extension,share/doc,share/postgresql/extension,share/postgresql/tsearch_data,include}' + $(MAKE) -C $* install DESTDIR=$(CONTRIB_BUILD_ROOT)/$* + @echo "=== Packaging $* ===" + mkdir -p $(ARCHIVE_DIR) + cd $(CONTRIB_BUILD_ROOT)/$*/$(prefix) && \ + files=$$(find . -type f -o -type l | sed 's|^\./||') && \ + tar -czf $(ARCHIVE_DIR)/$*.tar.gz $$files +# tar -C $(CONTRIB_BUILD_ROOT)/$*/$(prefix) -czf $(ARCHIVE_DIR)/$*.tar.gz . + +.PHONY: dist diff --git a/other/PGPASSFILE b/other/PGPASSFILE new file mode 100644 index 0000000000000..b8a0b5b6a58ac --- /dev/null +++ b/other/PGPASSFILE @@ -0,0 +1,3 @@ +localhost:5432:postgres:password:md532e12f215ba27cb750c9e093ce4b5127 +localhost:5432:postgres:postgres:md53175bce1d3201d16594cebf9d7eb3f9d +localhost:5432:postgres:login:md5d5745f9425eceb269f9fe01d0bef06ff diff --git a/other/empty b/other/empty new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/other/password b/other/password new file mode 100644 index 0000000000000..f3097ab13082b --- /dev/null +++ b/other/password @@ -0,0 +1 @@ +password diff --git a/pglite-wasm/builder/Dockerfile b/pglite-wasm/builder/Dockerfile new file mode 100644 index 0000000000000..d8f5d931f75b1 --- /dev/null +++ b/pglite-wasm/builder/Dockerfile @@ -0,0 +1,89 @@ +ARG EMSDK_VER=3.1.74 +ARG BUILDPLATFORM +# we only need to build on one platform, since we're interested in the WASM output +# building on the native (BUILDPLATFORM) is much faster, so use that +# remove "-arm64" suffix if building on x86_64 +FROM --platform=$BUILDPLATFORM emscripten/emsdk:${EMSDK_VER}-arm64 AS builder + +ENV LLVM_NM=/emsdk/upstream/bin/llvm-nm + +RUN apt update && apt upgrade -y && apt install -y \ + xz-utils autoconf libtool automake pkgconf bison flex + +SHELL ["/bin/bash", "-c"] + +RUN mkdir -p /install/libs /install/exports + +WORKDIR /install/libs + +# zlib CAN be installed by using the emsdk like this +# RUN embuilder --pic --verbose build zlib + +WORKDIR /src + +# ENV EMCC_COMMON_FLAGS="-fPIC -sWASM_BIGINT -sMIN_SAFARI_VERSION=150000 -D__PYDK__=1 -O2 -m32 -D_FILE_OFFSET_BITS=64 -sSUPPORT_LONGJMP=emscripten -mno-bulk-memory -mnontrapping-fptoint -mno-reference-types -mno-sign-ext -mno-extended-const -mno-atomics -mno-tail-call -mno-multivalue -mno-relaxed-simd -mno-simd128 -mno-multimemory -mno-exception-handling -Wno-unused-command-line-argument -Wno-unreachable-code-fallthrough -Wno-unused-function -Wno-invalid-noreturn -Wno-declaration-after-statement -Wno-invalid-noreturn" +ENV EMCC_COMMON_FLAGS="-O2 -fPIC" + +WORKDIR /src +RUN curl -L https://www.zlib.net/zlib-1.3.1.tar.gz | tar -xz +WORKDIR /src/zlib-1.3.1 +RUN CFLAGS="${EMCC_COMMON_FLAGS}" CXXFLAGS="${EMCC_COMMON_FLAGS}" emconfigure ./configure --static --prefix=/install/libs +RUN emmake make -j && emmake make install +RUN ${LLVM_NM} /install/libs/lib/libz.a | awk '$2 ~ /^[TDB]$/ {print $3}' | sed '/^$/d' | sort -u > /install/exports/libz.exports + +WORKDIR /src +RUN curl -L https://gitlab.gnome.org/GNOME/libxml2/-/archive/v2.14.5/libxml2-v2.14.5.tar.gz | tar -xz +WORKDIR /src/libxml2-v2.14.5 +RUN ./autogen.sh --with-python=no +RUN CFLAGS="${EMCC_COMMON_FLAGS}" CXXFLAGS="${EMCC_COMMON_FLAGS}" emconfigure ./configure --enable-shared=no --enable-static=yes --with-python=no --prefix=/install/libs +RUN emmake make -j && emmake make install +# extract exported symbols - useful for passing them to emscripten as EXPORTED_FUNCTIONS +RUN ${LLVM_NM} /install/libs/lib/libxml2.a | awk '$2 ~ /^[TDB]$/ {print $3}' | sed '/^$/d' | sort -u > /install/exports/libxml2.exports + +WORKDIR /src +RUN curl -L https://gitlab.gnome.org/GNOME/libxslt/-/archive/v1.1.43/libxslt-v1.1.43.tar.gz | tar -xz +WORKDIR /src/libxslt-v1.1.43 +RUN ./autogen.sh --with-python=no +RUN CFLAGS="${EMCC_COMMON_FLAGS}" CXXFLAGS="${EMCC_COMMON_FLAGS}" emconfigure ./configure --enable-shared=no --enable-static=yes --with-python=no --prefix=/install/libs --with-libxml-src=/src/libxml2-v2.14.5/ --with-pic=yes +RUN emmake make -j && emmake make install +# extract exported symbols - useful for passing them to emscripten as EXPORTED_FUNCTIONS +RUN ${LLVM_NM} /install/libs/lib/libxslt.a | awk '$2 ~ /^[TDB]$/ {print $3}' | sed '/^$/d' | sort -u > /install/exports/libxslt.exports + +WORKDIR /src +RUN curl -L https://github.com/openssl/openssl/releases/download/openssl-3.0.17/openssl-3.0.17.tar.gz | tar xz +WORKDIR /src/openssl-3.0.17 +RUN emconfigure ./Configure no-tests linux-generic64 --prefix=/install/libs +RUN sed -i 's|^CROSS_COMPILE.*$|CROSS_COMPILE=|g' Makefile # see https://github.com/emscripten-core/emscripten/issues/19597#issue-1754476454 +RUN emmake make -j && emmake make install +# extract exported symbols - useful for passing them to emscripten as EXPORTED_FUNCTIONS +RUN ${LLVM_NM} /install/libs/lib/libssl.a | awk '$2 ~ /^[TDB]$/ {print $3}' | sed '/^$/d' | sort -u > /install/exports/libssl.exports + +WORKDIR /src +RUN curl -L ftp://ftp.ossp.org/pkg/lib/uuid/uuid-1.6.2.tar.gz | tar xz +WORKDIR /src/uuid-1.6.2 +RUN CFLAGS="${EMCC_COMMON_FLAGS}" CXXFLAGS="${EMCC_COMMON_FLAGS}" emconfigure ./configure --build=aarch64-unknown-linux-gnu --enable-shared=no --enable-static=yes --with-perl=no --with-perl-compat=no --prefix=/install/libs --with-php=no --with-pic=yes +RUN emmake make -j && emmake make install || true # install tries to strip the wasm, but it doesnt recognize the format, so ignore atm +WORKDIR /install/libs/lib +RUN ln -s libuuid.a libossp-uuid.a # contrib extensions use -lossp-uuid +RUN ${LLVM_NM} /install/libs/lib/libossp-uuid.a | awk '$2 ~ /^[TDB]$/ {print $3}' | sed '/^$/d' | sort -u > /install/exports/libossp-uuid.exports + +ARG TARGETARCH +FROM emscripten/emsdk:${EMSDK_VER} AS base-amd64 +FROM emscripten/emsdk:${EMSDK_VER}-arm64 AS base-arm64 +FROM base-${TARGETARCH} AS runner + +RUN apt update && apt upgrade -y && apt install -y \ + xz-utils autoconf libtool automake pkgconf bison flex + +# this is where the libraries will be installed and subsequently where the LIBS and INCLUDES can be found +ARG INSTALL_PREFIX=/install/libs +ENV INSTALL_PREFIX=${INSTALL_PREFIX} + +ARG LIB_EXPORTS_DIR=/install/exports +ENV LIB_EXPORTS_DIR=${LIB_EXPORTS_DIR} + +COPY --from=builder /install/libs ${INSTALL_PREFIX} +COPY --from=builder /install/exports ${LIB_EXPORTS_DIR} + +# needed in building pglite.wasm +ENV LLVM_NM=/emsdk/upstream/bin/llvm-nm \ No newline at end of file diff --git a/pglite-wasm/excluded.pglite.imports b/pglite-wasm/excluded.pglite.imports new file mode 100644 index 0000000000000..e0d801f2348b5 --- /dev/null +++ b/pglite-wasm/excluded.pglite.imports @@ -0,0 +1,32 @@ +__indirect_function_table +invoke_di +invoke_didi +invoke_dii +invoke_i +invoke_ii +invoke_iii +invoke_iiii +invoke_iiiii +invoke_iiiiii +invoke_iiiiiii +invoke_iiiiiiii +invoke_iij +invoke_ij +invoke_ijj +invoke_j +invoke_ji +invoke_jiiiii +invoke_v +invoke_vi +invoke_vii +invoke_viii +invoke_viiidi +invoke_viiii +invoke_viiiii +invoke_viiiiii +invoke_viiiiii +invoke_viiiiiiii +invoke_viiiiiiiii +invoke_viiji +invoke_vij +invoke_vijiiidjiiii diff --git a/pglite-wasm/included.pglite.imports b/pglite-wasm/included.pglite.imports new file mode 100644 index 0000000000000..63cbc5e039b90 --- /dev/null +++ b/pglite-wasm/included.pglite.imports @@ -0,0 +1,38 @@ +close +fcntl +free +get_buffer_addr +get_buffer_size +get_channel +getpid +gettimeofday +gmtime +interactive_one +interactive_read +interactive_write +ioctl +isalnum +isxdigit +lowerstr +main +malloc +memcmp +memcpy +memset +nanosleep +open +pgl_backend +pgl_initdb +pgl_shutdown +rand +read +readstoplist +realloc +searchstoplist +socket +srand +strcmp +strftime +strlen +strtoul +use_wire \ No newline at end of file diff --git a/pglite-wasm/pg_main.c b/pglite-wasm/pg_main.c index 07c21cf1b5b24..a3b77ece6cae6 100644 --- a/pglite-wasm/pg_main.c +++ b/pglite-wasm/pg_main.c @@ -26,6 +26,10 @@ #include /* chdir */ #include /* mkdir */ +#if defined(__EMSCRIPTEN__) +#include +#endif + // globals @@ -482,7 +486,8 @@ __attribute__ ((export_name("pgl_backend"))) #else remove(IDB_PIPE_BOOT); #endif - stdin = fdopen(saved_stdin, "r"); + // tdrz: I've commented this out!!! + // stdin = fdopen(saved_stdin, "r"); PDEBUG("# 479: initdb faking shutdown to complete WAL/OID states"); pg_proc_exit(66); diff --git a/pglite/Makefile b/pglite/Makefile new file mode 100644 index 0000000000000..4f4ef50406dd8 --- /dev/null +++ b/pglite/Makefile @@ -0,0 +1,35 @@ +# Packages other extensions into individual tar.gz archives + +top_builddir = .. +include $(top_builddir)/src/Makefile.global + +SUBDIRS = \ + pg_ivm \ + vector + +prefix ?= /install/pglite +EXTENSIONS_BUILD_ROOT := /tmp/extensions/build +ARCHIVE_DIR := /install/pglite/extensions + +EXTENSIONS := $(SUBDIRS) + +# Default target: build tarballs for all contribs +dist: $(addsuffix .tar.gz,$(EXTENSIONS)) + +# Pattern rule: build $(EXT).tar.gz for each extra extension +%.tar.gz: + @echo "=== Staging $* ===" + rm -rf $(EXTENSIONS_BUILD_ROOT)/$* + bash -c 'mkdir -p $(EXTENSIONS_BUILD_ROOT)/$*/$(prefix)/{bin,lib,share/extension,share/doc,share/postgresql/extension,share/postgresql/tsearch_data,include}' + $(MAKE) -C $* install DESTDIR=$(EXTENSIONS_BUILD_ROOT)/$* + @echo "=== Packaging $* ===" + mkdir -p $(ARCHIVE_DIR) + cd $(EXTENSIONS_BUILD_ROOT)/$*/$(prefix) && \ + files=$$(find . -type f -o -type l | sed 's|^\./||') && \ + tar -czf $(ARCHIVE_DIR)/$*.tar.gz $$files +# tar -C $(EXTENSIONS_BUILD_ROOT)/$*/$(prefix) -czf $(ARCHIVE_DIR)/$*.tar.gz . + +.PHONY: dist + + +$(recurse) \ No newline at end of file diff --git a/pglite/pg_ivm b/pglite/pg_ivm new file mode 160000 index 0000000000000..f4b40e93a6047 --- /dev/null +++ b/pglite/pg_ivm @@ -0,0 +1 @@ +Subproject commit f4b40e93a60478a1ea9d69f0fd305452e5c00690 diff --git a/pglite/vector b/pglite/vector new file mode 160000 index 0000000000000..2627c5ff775ae --- /dev/null +++ b/pglite/vector @@ -0,0 +1 @@ +Subproject commit 2627c5ff775ae6d7aef0c430121ccf857842d2f2 diff --git a/src/Makefile.shlib b/src/Makefile.shlib index aed880df4b876..befb131d977c4 100644 --- a/src/Makefile.shlib +++ b/src/Makefile.shlib @@ -225,13 +225,14 @@ ifeq ($(SHLIB_EXPORTS),) endif ifeq ($(PORTNAME), emscripten) - LINK.shared = emsdk-shared + LINK.shared = $(COMPILER) -shared -sSIDE_MODULE=1 -Wno-unused-function ifdef soname # emscripten uses unversioned shared libraries shlib = $(shlib_bare) soname = $(shlib_bare) endif - BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ + BUILD.exports = ( $(AWK) '/^[^\#]/ {printf "%s\n",$$1}' $< ) | sort -u >$@ +# BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ exports_file = $(SHLIB_EXPORTS:%.txt=%.list) # ifneq (,$(exports_file)) # LINK.shared += -Wl,--version-script=$(exports_file) diff --git a/src/backend/Makefile b/src/backend/Makefile index dbc1d4a148f3a..a79301f587fc3 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -77,14 +77,55 @@ ifeq ($(PORTNAME), emscripten) AR ?= llvm-ar LIBPGCORE ?= $(top_builddir)/libpgcore.a LIBPG = $(top_builddir)/libpostgres.a +LIBPGMAIN = $(top_builddir)/libpgmain.a PGCORE = $(top_builddir)/src/common/libpgcommon_srv.a $(top_builddir)/src/port/libpgport_srv.a $(LIBPG) PGMAIN = main/main.o tcop/postgres.o +PGLITE_MAIN = pglite-wasm/pg_main.c +EXPORTED_RUNTIME_METHODS="MEMFS,IDBFS,FS,setValue,getValue,UTF8ToString,stringToNewUTF8,stringToUTF8OnStack" +PGROOT=/install/pglite +PGPRELOAD=--preload-file ${PGROOT}/share/postgresql@/tmp/pglite/share/postgresql --preload-file ${PGROOT}/lib/postgresql@/tmp/pglite/lib/postgresql --preload-file $(top_builddir)/other/password@/tmp/pglite/password --preload-file $(top_builddir)/other/PGPASSFILE@/home/web_user/.pgpass --preload-file $(top_builddir)/other/empty@/tmp/pglite/bin/postgres --preload-file $(top_builddir)/other/empty@/tmp/pglite/bin/initdb postgres: $(OBJS) - $(AR) rcs $(top_builddir)/libpgmain.a $(PGMAIN) + $(AR) rcs $(LIBPGMAIN) $(PGMAIN) $(AR) rcs $(LIBPG) $(filter-out $(PGMAIN),$(call expand_subsys,$(ONLYOBJS))) $(CC) -r -o $(top_builddir)/libpgcore.o -Wl,--whole-archive $(PGCORE) $(AR) rcs $(LIBPGCORE) $(top_builddir)/libpgcore.o - COPTS="$(LOPTS)" $(CC) $(MAIN_MODULE) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPGCORE) $(top_builddir)/libpgmain.a $(LIBS) + COPTS="$(LOPTS)" $(CC) $(MAIN_MODULE) $(CFLAGS) $(LDFLAGS) -o $@$(X) $(LIBPGCORE) $(LIBPGMAIN) $(LIBS) + +# Extensions use functions from the core PG. These need to be exported by the emscripten compiler. +# The following target gathers all extension imports + the default ones (included.pglite.imports), +# excludes the one in excluded.pglite.imports and adds a leading _ to each. +pglite-exported-functions: + $(MKDIR_P) '$(emscripten_imports_dir)' + cat $(top_builddir)/pglite-wasm/excluded.*.imports $(top_builddir)/src/interfaces/libpq/exports.list $(LIB_EXPORTS_DIR)/libossp-uuid.exports | sort -u > '$(top_builddir)/pglite-wasm/excluded.imports' + cat $(DESTDIR)$(emscripten_extension_imports_dir)/*.imports '$(top_builddir)/pglite-wasm/included.pglite.imports' | \ + sort -u | \ + grep -Fvx -f '$(top_builddir)/pglite-wasm/excluded.imports' | \ + sed 's/^/_/' \ + > '$(emscripten_imports_dir)/exported_functions.txt' + +# -sDYLINK_DEBUG=2 use this for debugging missing exported symbols (ex when an extension calls a pgcore function that hasn't been exported) +# PGLITE_CFLAGS is something like "-O2" (aka release version) or "-g -gsource-map --no-wasm-opt" (aka debug version) +pglite: pglite-exported-functions + $(CC) $(CFLAGS) $(LDFLAGS) -DPG_PREFIX=/tmp/pglite -DCMA_MB=12 -I$(top_builddir)/src/include -I$(top_builddir)/src/ -I$(top_builddir)/src/interfaces/libpq -o pglite.o -c $(top_builddir)/$(PGLITE_MAIN) -Wno-incompatible-pointer-types-discards-qualifiers + $(CC) \ + $(PGLITE_CFLAGS) \ + -fPIC -sWASM_BIGINT -sMIN_SAFARI_VERSION=150000 -D__PYDK__=1 -m32 -D_FILE_OFFSET_BITS=64 -sSUPPORT_LONGJMP=emscripten -mno-bulk-memory -mnontrapping-fptoint -mno-reference-types -mno-sign-ext -mno-extended-const -mno-atomics -mno-tail-call -mno-multivalue -mno-relaxed-simd -mno-simd128 -mno-multimemory -mno-exception-handling -Wno-unused-command-line-argument -Wno-unreachable-code-fallthrough -Wno-unused-function -Wno-invalid-noreturn -Wno-declaration-after-statement -Wno-invalid-noreturn \ + -DPYDK=1 -DPG_PREFIX=/tmp/pglite -DCMA_MB=12 -o pglite.html --shell-file $(top_builddir)/pglite-wasm/repl.html \ + $(PGPRELOAD) \ + -sGLOBAL_BASE=12MB -ferror-limit=1 \ + -sFORCE_FILESYSTEM=1 -sNO_EXIT_RUNTIME=1 -sENVIRONMENT=node,web,worker \ + -sMAIN_MODULE=2 \ + -sEXPORTED_FUNCTIONS=@$(emscripten_imports_dir)/exported_functions.txt \ + -sMODULARIZE=1 -sEXPORT_ES6=1 -sEXPORT_NAME=Module \ + -sALLOW_TABLE_GROWTH -sALLOW_MEMORY_GROWTH -sERROR_ON_UNDEFINED_SYMBOLS=0 \ + -sEXPORTED_RUNTIME_METHODS=$(EXPORTED_RUNTIME_METHODS) \ + -I./src/include -I./src/ -I./src/interfaces/libpq \ + -L/install/libs/lib -L$(top_builddir) -L$(top_builddir)/src/interfaces/libpq/ \ + pglite.o \ + -Ldict_snowball \ + -lxslt \ + -lpgcore \ + -lnodefs.js -lidbfs.js -lxml2 -lz endif ifeq ($(PORTNAME), wasi) @@ -214,6 +255,11 @@ ifeq ($(PORTNAME), win32) ifeq ($(MAKE_DLL), true) $(INSTALL_DATA) libpostgres.a '$(DESTDIR)$(libdir)/libpostgres.a' endif +endif +ifeq ($(PORTNAME), emscripten) + $(INSTALL_DATA) $(LIBPG) '$(DESTDIR)$(libdir)/libpostgres.a' + $(INSTALL_DATA) $(LIBPGCORE) '$(DESTDIR)$(libdir)/libpgcore.a' + $(INSTALL_DATA) $(LIBPGMAIN) '$(DESTDIR)$(libdir)/libpgmain.a' endif $(MAKE) -C catalog install-data $(MAKE) -C tsearch install-data @@ -235,6 +281,9 @@ ifeq ($(MAKE_EXPORTS), true) $(INSTALL_DATA) $(POSTGRES_IMP) '$(DESTDIR)$(pkglibdir)/$(POSTGRES_IMP)' $(INSTALL_PROGRAM) $(MKLDEXPORT) '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)/mkldexport.sh' endif +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) postgres.wasm '$(DESTDIR)$(bindir)/postgres.wasm' +endif .PHONY: install-bin @@ -255,6 +304,16 @@ ifeq ($(MAKE_EXPORTS), true) $(MKDIR_P) '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)' endif +ifeq ($(PORTNAME), emscripten) +install-pglite: pglite + $(INSTALL_PROGRAM) pglite.html '$(DESTDIR)$(bindir)/pglite.html' + $(INSTALL_PROGRAM) pglite.wasm '$(DESTDIR)$(bindir)/pglite.wasm' + $(INSTALL_PROGRAM) pglite.js '$(DESTDIR)$(bindir)/pglite.js' + $(INSTALL_PROGRAM) pglite.data '$(DESTDIR)$(bindir)/pglite.data' +endif + +.PHONY: install-pglite + ########################################################################## @@ -273,6 +332,16 @@ ifeq ($(PORTNAME), win32) ifeq ($(MAKE_DLL), true) rm -f '$(DESTDIR)$(libdir)/libpostgres.a' endif +endif +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(libdir)/libpostgres.a' + rm -f '$(DESTDIR)$(libdir)/libpgcore.a' + rm -f '$(DESTDIR)$(libdir)/libpgmain.a' + rm -f '$(DESTDIR)$(bindir)/postgres.wasm' + rm -f '$(DESTDIR)$(bindir)/pglite.wasm' + rm -f '$(DESTDIR)$(bindir)/pglite.html' + rm -f '$(DESTDIR)$(bindir)/pglite.js' + rm -f '$(DESTDIR)$(bindir)/pglite.data' endif $(MAKE) -C catalog uninstall-data $(MAKE) -C tsearch uninstall-data @@ -295,6 +364,12 @@ endif ifeq ($(PORTNAME), win32) rm -f postgres.dll libpostgres.a $(WIN32RES) endif +ifeq ($(PORTNAME), emscripten) + rm -f postgres.wasm libpostgres.a libpgcore.a libpgmain.a pglite.o pglite.html pglite.js pglite.wasm pglite.data +endif +ifeq ($(PORTNAME), emscripten) + rm -f '$(emscripten_imports_dir)/exported_functions.txt' +endif distclean: clean # generated by configure diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index ff5277a682852..47fc44bf618e5 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -80,6 +80,10 @@ #include "utils/guc_hooks.h" #include "utils/memutils.h" +#if defined(__EMSCRIPTEN__) +#include +#endif + /* * Cope with the various platform-specific ways to spell TCP keepalive socket * options. This doesn't cover Windows, which as usual does its own thing. diff --git a/src/bin/initdb/Makefile b/src/bin/initdb/Makefile index 031cc77c9d61b..a960ce6580188 100644 --- a/src/bin/initdb/Makefile +++ b/src/bin/initdb/Makefile @@ -47,16 +47,25 @@ localtime.c: % : $(top_srcdir)/src/timezone/% install: all installdirs $(INSTALL_PROGRAM) initdb$(X) '$(DESTDIR)$(bindir)/initdb$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) initdb.wasm '$(DESTDIR)$(bindir)/initdb.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/initdb$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/initdb.wasm' +endif clean distclean: rm -f initdb$(X) $(OBJS) localtime.c rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f initdb.wasm +endif # ensure that changes in datadir propagate into object file initdb.o: initdb.c $(top_builddir)/src/Makefile.global diff --git a/src/bin/pg_amcheck/Makefile b/src/bin/pg_amcheck/Makefile index f9488c447a887..765032692b22a 100644 --- a/src/bin/pg_amcheck/Makefile +++ b/src/bin/pg_amcheck/Makefile @@ -33,16 +33,25 @@ pg_amcheck: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils install: all installdirs $(INSTALL_PROGRAM) pg_amcheck$(X) '$(DESTDIR)$(bindir)/pg_amcheck$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_amcheck.wasm '$(DESTDIR)$(bindir)/pg_amcheck.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_amcheck$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_amcheck.wasm' +endif clean distclean: rm -f pg_amcheck$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_amcheck.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_archivecleanup/Makefile b/src/bin/pg_archivecleanup/Makefile index 93fd703f22591..aaeac7ffd3524 100644 --- a/src/bin/pg_archivecleanup/Makefile +++ b/src/bin/pg_archivecleanup/Makefile @@ -18,16 +18,25 @@ pg_archivecleanup: $(OBJS) | submake-libpgport install: all installdirs $(INSTALL_PROGRAM) pg_archivecleanup$(X) '$(DESTDIR)$(bindir)/pg_archivecleanup$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_archivecleanup.wasm '$(DESTDIR)$(bindir)/pg_archivecleanup.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_archivecleanup$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_archivecleanup.wasm' +endif clean distclean: rm -f pg_archivecleanup$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_archivecleanup.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_basebackup/Makefile b/src/bin/pg_basebackup/Makefile index 26c53e473f560..9b5df3ca2ecd3 100644 --- a/src/bin/pg_basebackup/Makefile +++ b/src/bin/pg_basebackup/Makefile @@ -63,6 +63,12 @@ install: all installdirs $(INSTALL_PROGRAM) pg_createsubscriber$(X) '$(DESTDIR)$(bindir)/pg_createsubscriber$(X)' $(INSTALL_PROGRAM) pg_receivewal$(X) '$(DESTDIR)$(bindir)/pg_receivewal$(X)' $(INSTALL_PROGRAM) pg_recvlogical$(X) '$(DESTDIR)$(bindir)/pg_recvlogical$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_basebackup.wasm '$(DESTDIR)$(bindir)/pg_basebackup.wasm' + $(INSTALL_PROGRAM) pg_createsubscriber.wasm '$(DESTDIR)$(bindir)/pg_createsubscriber.wasm' + $(INSTALL_PROGRAM) pg_receivewal.wasm '$(DESTDIR)$(bindir)/pg_receivewal.wasm' + $(INSTALL_PROGRAM) pg_recvlogical.wasm '$(DESTDIR)$(bindir)/pg_recvlogical.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' @@ -72,12 +78,21 @@ uninstall: rm -f '$(DESTDIR)$(bindir)/pg_createsubscriber$(X)' rm -f '$(DESTDIR)$(bindir)/pg_receivewal$(X)' rm -f '$(DESTDIR)$(bindir)/pg_recvlogical$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_basebackup.wasm' + rm -f '$(DESTDIR)$(bindir)/pg_createsubscriber.wasm' + rm -f '$(DESTDIR)$(bindir)/pg_receivewal.wasm' + rm -f '$(DESTDIR)$(bindir)/pg_recvlogical.wasm' +endif clean distclean: rm -f pg_basebackup$(X) pg_createsubscriber$(X) pg_receivewal$(X) pg_recvlogical$(X) \ $(BBOBJS) pg_createsubscriber.o pg_receivewal.o pg_recvlogical.o \ $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_basebackup.wasm pg_createsubscriber.wasm pg_receivewal.wasm pg_recvlogical.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_checksums/Makefile b/src/bin/pg_checksums/Makefile index 31de5fb467344..f524f483fe2a7 100644 --- a/src/bin/pg_checksums/Makefile +++ b/src/bin/pg_checksums/Makefile @@ -29,16 +29,25 @@ pg_checksums: $(OBJS) | submake-libpgport install: all installdirs $(INSTALL_PROGRAM) pg_checksums$(X) '$(DESTDIR)$(bindir)/pg_checksums$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_checksums.wasm '$(DESTDIR)$(bindir)/pg_checksums.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_checksums$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_checksums.wasm' +endif clean distclean: rm -f pg_checksums$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_checksums.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_combinebackup/Makefile b/src/bin/pg_combinebackup/Makefile index c3729755ba4ba..167ec4be960f0 100644 --- a/src/bin/pg_combinebackup/Makefile +++ b/src/bin/pg_combinebackup/Makefile @@ -35,16 +35,25 @@ pg_combinebackup: $(OBJS) | submake-libpgport submake-libpgfeutils install: all installdirs $(INSTALL_PROGRAM) pg_combinebackup$(X) '$(DESTDIR)$(bindir)/pg_combinebackup$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_combinebackup.wasm '$(DESTDIR)$(bindir)/pg_combinebackup.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_combinebackup$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_combinebackup.wasm' +endif clean distclean maintainer-clean: rm -f pg_combinebackup$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_combinebackup.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_config/Makefile b/src/bin/pg_config/Makefile index c54f68c9daddd..3d3c44fa2b4b8 100644 --- a/src/bin/pg_config/Makefile +++ b/src/bin/pg_config/Makefile @@ -19,23 +19,50 @@ OBJS = \ $(WIN32RES) \ pg_config.o +all: +ifeq ($(PORTNAME), emscripten) +all: pg_config pg_config_sh +else all: pg_config +endif pg_config: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) +# the emscripten build generates multiple "runnable" files (.cjs/.mjs + .wasm) +# but other postgres tools expect exacly 'pg_config' as an executable +# the following rule creates a node script that calls the pg_config.cjs/.mjs +# pg_config.wasm is implicitly expected to be in the same folder +pg_config_sh: pg_config + echo "#!/usr/bin/env node" > pg_config + echo 'require("./pg_config$(X)")' >> pg_config + chmod +x pg_config + install: all installdirs $(INSTALL_SCRIPT) pg_config$(X) '$(DESTDIR)$(bindir)/pg_config$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_config.wasm '$(DESTDIR)$(bindir)/pg_config.wasm' + $(INSTALL_PROGRAM) pg_config '$(DESTDIR)$(bindir)/pg_config' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_config$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_config.wasm' + rm -f '$(DESTDIR)$(bindir)/pg_config' +endif + clean distclean: rm -f pg_config$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_config.wasm + rm -f pg_config +endif check: $(prove_check) diff --git a/src/bin/pg_controldata/Makefile b/src/bin/pg_controldata/Makefile index c3f64e189690a..0be4e65656b07 100644 --- a/src/bin/pg_controldata/Makefile +++ b/src/bin/pg_controldata/Makefile @@ -26,16 +26,25 @@ pg_controldata: $(OBJS) | submake-libpgport install: all installdirs $(INSTALL_PROGRAM) pg_controldata$(X) '$(DESTDIR)$(bindir)/pg_controldata$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_controldata.wasm '$(DESTDIR)$(bindir)/pg_controldata.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_controldata$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_controldata.wasm' +endif clean distclean: rm -f pg_controldata$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_controldata.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_ctl/Makefile b/src/bin/pg_ctl/Makefile index a3fbad7a3a5a5..514cd8cf032f9 100644 --- a/src/bin/pg_ctl/Makefile +++ b/src/bin/pg_ctl/Makefile @@ -35,16 +35,25 @@ pg_ctl: $(OBJS) | submake-libpgport $(SUBMAKE_LIBPQ) install: all installdirs $(INSTALL_PROGRAM) pg_ctl$(X) '$(DESTDIR)$(bindir)/pg_ctl$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_ctl.wasm '$(DESTDIR)$(bindir)/pg_ctl.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_ctl$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_ctl.wasm' +endif clean distclean: rm -f pg_ctl$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_ctl.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_dump/Makefile b/src/bin/pg_dump/Makefile index 930c741c95d85..cdd9cfcc5de96 100644 --- a/src/bin/pg_dump/Makefile +++ b/src/bin/pg_dump/Makefile @@ -57,6 +57,11 @@ install: all installdirs $(INSTALL_PROGRAM) pg_dump$(X) '$(DESTDIR)$(bindir)'/pg_dump$(X) $(INSTALL_PROGRAM) pg_restore$(X) '$(DESTDIR)$(bindir)'/pg_restore$(X) $(INSTALL_PROGRAM) pg_dumpall$(X) '$(DESTDIR)$(bindir)'/pg_dumpall$(X) +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_dump.wasm '$(DESTDIR)$(bindir)/pg_dump.wasm' + $(INSTALL_PROGRAM) pg_restore.wasm '$(DESTDIR)$(bindir)/pg_restore.wasm' + $(INSTALL_PROGRAM) pg_dumpall.wasm '$(DESTDIR)$(bindir)/pg_dumpall.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' @@ -69,7 +74,13 @@ installcheck: uninstall: rm -f $(addprefix '$(DESTDIR)$(bindir)'/, pg_dump$(X) pg_restore$(X) pg_dumpall$(X)) +ifeq ($(PORTNAME), emscripten) + rm -f $(addprefix '$(DESTDIR)$(bindir)'/, pg_dump.wasm pg_restore.wasm pg_dumpall.wasm) +endif clean distclean: rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_dump_sort.o pg_restore.o pg_dumpall.o rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_dump.wasm pg_restore.wasm pg_dumpall.wasm +endif \ No newline at end of file diff --git a/src/bin/pg_resetwal/Makefile b/src/bin/pg_resetwal/Makefile index 4228a5a772a9f..ff8e5d642a1f6 100644 --- a/src/bin/pg_resetwal/Makefile +++ b/src/bin/pg_resetwal/Makefile @@ -28,16 +28,25 @@ pg_resetwal: $(OBJS) | submake-libpgport install: all installdirs $(INSTALL_PROGRAM) pg_resetwal$(X) '$(DESTDIR)$(bindir)/pg_resetwal$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_resetwal.wasm '$(DESTDIR)$(bindir)/pg_resetwal.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_resetwal$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_resetwal.wasm' +endif clean distclean: rm -f pg_resetwal$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_resetwal.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_rewind/Makefile b/src/bin/pg_rewind/Makefile index 12b138b2f2ce3..e0a5f5dd2c38f 100644 --- a/src/bin/pg_rewind/Makefile +++ b/src/bin/pg_rewind/Makefile @@ -42,16 +42,25 @@ xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/% install: all installdirs $(INSTALL_PROGRAM) pg_rewind$(X) '$(DESTDIR)$(bindir)/pg_rewind$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_rewind.wasm '$(DESTDIR)$(bindir)/pg_rewind.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_rewind$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_rewind.wasm' +endif clean distclean: rm -f pg_rewind$(X) $(OBJS) xlogreader.c rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_rewind.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_test_fsync/Makefile b/src/bin/pg_test_fsync/Makefile index 4c5e518125033..e3bb3527a8858 100644 --- a/src/bin/pg_test_fsync/Makefile +++ b/src/bin/pg_test_fsync/Makefile @@ -18,6 +18,9 @@ pg_test_fsync: $(OBJS) | submake-libpgport install: all installdirs $(INSTALL_PROGRAM) pg_test_fsync$(X) '$(DESTDIR)$(bindir)/pg_test_fsync$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_test_fsync.wasm '$(DESTDIR)$(bindir)/pg_test_fsync.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' @@ -30,7 +33,13 @@ installcheck: uninstall: rm -f '$(DESTDIR)$(bindir)/pg_test_fsync$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_test_fsync.wasm' +endif clean distclean: rm -f pg_test_fsync$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_test_fsync.wasm +endif \ No newline at end of file diff --git a/src/bin/pg_test_timing/Makefile b/src/bin/pg_test_timing/Makefile index 7f677edadb30f..eb4f95c2bf088 100644 --- a/src/bin/pg_test_timing/Makefile +++ b/src/bin/pg_test_timing/Makefile @@ -18,6 +18,9 @@ pg_test_timing: $(OBJS) | submake-libpgport install: all installdirs $(INSTALL_PROGRAM) pg_test_timing$(X) '$(DESTDIR)$(bindir)/pg_test_timing$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_test_timing.wasm '$(DESTDIR)$(bindir)/pg_test_timing.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' @@ -30,7 +33,13 @@ installcheck: uninstall: rm -f '$(DESTDIR)$(bindir)/pg_test_timing$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_test_timing.wasm' +endif clean distclean: rm -f pg_test_timing$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_test_timing.wasm +endif \ No newline at end of file diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile index bde91e2beb82c..faa309ac31691 100644 --- a/src/bin/pg_upgrade/Makefile +++ b/src/bin/pg_upgrade/Makefile @@ -42,17 +42,26 @@ pg_upgrade: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils install: all installdirs $(INSTALL_PROGRAM) pg_upgrade$(X) '$(DESTDIR)$(bindir)/pg_upgrade$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_upgrade.wasm '$(DESTDIR)$(bindir)/pg_upgrade.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_upgrade$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_upgrade.wasm' +endif clean distclean: rm -f pg_upgrade$(X) $(OBJS) rm -rf delete_old_cluster.sh log/ tmp_check/ \ reindex_hash.sql +ifeq ($(PORTNAME), emscripten) + rm -f pg_upgrade.wasm +endif export with_icu diff --git a/src/bin/pg_verifybackup/Makefile b/src/bin/pg_verifybackup/Makefile index 7c045f142e8d7..e49278f32d28c 100644 --- a/src/bin/pg_verifybackup/Makefile +++ b/src/bin/pg_verifybackup/Makefile @@ -30,16 +30,25 @@ pg_verifybackup: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils install: all installdirs $(INSTALL_PROGRAM) pg_verifybackup$(X) '$(DESTDIR)$(bindir)/pg_verifybackup$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_verifybackup.wasm '$(DESTDIR)$(bindir)/pg_verifybackup.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_verifybackup$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_verifybackup.wasm' +endif clean distclean: rm -f pg_verifybackup$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_verifybackup.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_waldump/Makefile b/src/bin/pg_waldump/Makefile index 4c1ee649501f4..1f08027439af9 100644 --- a/src/bin/pg_waldump/Makefile +++ b/src/bin/pg_waldump/Makefile @@ -38,16 +38,25 @@ $(RMGRDESCSOURCES): % : $(top_srcdir)/src/backend/access/rmgrdesc/% install: all installdirs $(INSTALL_PROGRAM) pg_waldump$(X) '$(DESTDIR)$(bindir)/pg_waldump$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_waldump.wasm '$(DESTDIR)$(bindir)/pg_waldump.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_waldump$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_waldump.wasm' +endif clean distclean: rm -f pg_waldump$(X) $(OBJS) $(RMGRDESCSOURCES) xlogreader.c xlogstats.c rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_waldump.wasm +endif check: $(prove_check) diff --git a/src/bin/pg_walsummary/Makefile b/src/bin/pg_walsummary/Makefile index 1886c39e98b19..c686d9450641a 100644 --- a/src/bin/pg_walsummary/Makefile +++ b/src/bin/pg_walsummary/Makefile @@ -31,16 +31,25 @@ pg_walsummary: $(OBJS) | submake-libpgport submake-libpgfeutils install: all installdirs $(INSTALL_PROGRAM) pg_walsummary$(X) '$(DESTDIR)$(bindir)/pg_walsummary$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pg_walsummary.wasm '$(DESTDIR)$(bindir)/pg_walsummary.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pg_walsummary$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pg_walsummary.wasm' +endif clean distclean maintainer-clean: rm -f pg_walsummary$(X) $(OBJS) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f pg_walsummary.wasm +endif check: $(prove_check) diff --git a/src/bin/pgbench/Makefile b/src/bin/pgbench/Makefile index 987bf64df9de0..604f9493b35af 100644 --- a/src/bin/pgbench/Makefile +++ b/src/bin/pgbench/Makefile @@ -40,17 +40,26 @@ exprparse.o exprscan.o: exprparse.h install: all installdirs $(INSTALL_PROGRAM) pgbench$(X) '$(DESTDIR)$(bindir)/pgbench$(X)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) pgbench.wasm '$(DESTDIR)$(bindir)/pgbench.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/pgbench$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/pgbench.wasm' +endif clean distclean: rm -f pgbench$(X) $(OBJS) rm -rf tmp_check rm -f exprparse.h exprparse.c exprscan.c +ifeq ($(PORTNAME), emscripten) + rm -f pgbench.wasm +endif check: $(prove_check) diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile index 374c4c3ab8f83..9c17566be2f2f 100644 --- a/src/bin/psql/Makefile +++ b/src/bin/psql/Makefile @@ -65,17 +65,26 @@ psqlscanslash.c: FLEX_FIX_WARNING=yes install: all installdirs $(INSTALL_PROGRAM) psql$(X) '$(DESTDIR)$(bindir)/psql$(X)' $(INSTALL_DATA) $(srcdir)/psqlrc.sample '$(DESTDIR)$(datadir)/psqlrc.sample' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) psql.wasm '$(DESTDIR)$(bindir)/psql.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' '$(DESTDIR)$(datadir)' uninstall: rm -f '$(DESTDIR)$(bindir)/psql$(X)' '$(DESTDIR)$(datadir)/psqlrc.sample' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/psql.wasm' +endif clean distclean: rm -f psql$(X) $(OBJS) lex.backup rm -rf tmp_check rm -f sql_help.h sql_help.c psqlscanslash.c +ifeq ($(PORTNAME), emscripten) + rm -f psql.wasm +endif check: $(prove_check) diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile index 9633c99136880..f155e24f0c395 100644 --- a/src/bin/scripts/Makefile +++ b/src/bin/scripts/Makefile @@ -41,17 +41,33 @@ install: all installdirs $(INSTALL_PROGRAM) vacuumdb$(X) '$(DESTDIR)$(bindir)'/vacuumdb$(X) $(INSTALL_PROGRAM) reindexdb$(X) '$(DESTDIR)$(bindir)'/reindexdb$(X) $(INSTALL_PROGRAM) pg_isready$(X) '$(DESTDIR)$(bindir)'/pg_isready$(X) +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) createdb.wasm '$(DESTDIR)$(bindir)'/createdb.wasm + $(INSTALL_PROGRAM) dropdb.wasm '$(DESTDIR)$(bindir)'/dropdb.wasm + $(INSTALL_PROGRAM) createuser.wasm '$(DESTDIR)$(bindir)'/createuser.wasm + $(INSTALL_PROGRAM) dropuser.wasm '$(DESTDIR)$(bindir)'/dropuser.wasm + $(INSTALL_PROGRAM) clusterdb.wasm '$(DESTDIR)$(bindir)'/clusterdb.wasm + $(INSTALL_PROGRAM) vacuumdb.wasm '$(DESTDIR)$(bindir)'/vacuumdb.wasm + $(INSTALL_PROGRAM) reindexdb.wasm '$(DESTDIR)$(bindir)'/reindexdb.wasm + $(INSTALL_PROGRAM) pg_isready.wasm '$(DESTDIR)$(bindir)'/pg_isready.wasm +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f $(addprefix '$(DESTDIR)$(bindir)'/, $(addsuffix $(X), $(PROGRAMS))) +ifeq ($(PORTNAME), emscripten) + rm -f $(addprefix '$(DESTDIR)$(bindir)'/, $(addsuffix .wasm, $(PROGRAMS))) +endif clean distclean: rm -f $(addsuffix $(X), $(PROGRAMS)) $(addsuffix .o, $(PROGRAMS)) rm -f common.o $(WIN32RES) rm -rf tmp_check +ifeq ($(PORTNAME), emscripten) + rm -f $(addsuffix .wasm, $(PROGRAMS)) +endif export with_icu diff --git a/src/include/port/emscripten.h b/src/include/port/emscripten.h index db4903aa48f78..fb647c0d45199 100644 --- a/src/include/port/emscripten.h +++ b/src/include/port/emscripten.h @@ -4,10 +4,13 @@ #define I_WASM #if !defined(__cplusplus) -#include +// #include +// #include "/tmp/pglite/include/sdk_port.h" +// #include #endif -#include "/tmp/pglite/include/wasm_common.h" +// #include "/tmp/pglite/include/wasm_common.h" +#include "port/wasm_common.h" #define BOOT_END_MARK "build indices" diff --git a/src/include/port/pg_debug.h b/src/include/port/pg_debug.h new file mode 100644 index 0000000000000..73da07ddb9747 --- /dev/null +++ b/src/include/port/pg_debug.h @@ -0,0 +1,8 @@ +#ifndef I_PGDEBUG +#define I_PGDEBUG +#define WASM_USERNAME "postgres" +#define PDEBUG(string) +#define JSDEBUG(string) +#define ADEBUG(string) +#define PGDEBUG 0 +#endif diff --git a/src/include/port/sdk_port.h b/src/include/port/sdk_port.h new file mode 100644 index 0000000000000..7e6b178dce493 --- /dev/null +++ b/src/include/port/sdk_port.h @@ -0,0 +1,262 @@ +#if defined(__EMSCRIPTEN__) +#include + +#elif defined(__wasi__) + + +#ifndef I_WASI +#define I_WASI + +#undef HAVE_PTHREAD + +#if defined(HAVE_SETSID) +#undef HAVE_SETSID +#endif + +#if defined(HAVE_GETRLIMIT) +#undef HAVE_GETRLIMIT +#endif + + +#define PLATFORM_DEFAULT_SYNC_METHOD SYNC_METHOD_FDATASYNC + +#define EMSCRIPTEN_KEEPALIVE __attribute__((used)) +#define __declspec( dllimport ) __attribute__((used)) + +#define em_callback_func void +#define emscripten_set_main_loop(...) +#define emscripten_force_exit(...) +#define EM_JS(...) + +#include "wasm_common.h" + + +static pid_t +fork(void) { + puts("# 31: fork -1"); + return -1; +} + +// ======== signal ======================== +#define SA_RESTART 4 +#define SIG_SETMASK 2 + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 + +/* A signal handler. */ +typedef void (*handler_t) (int signal); +typedef unsigned char sigset_t; +typedef void (*__sighandler_t) (int); + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; +#ifdef SA_RESTORER + __sigrestore_t sa_restorer; +#endif + sigset_t sa_mask; +}; +extern int sigemptyset(sigset_t *set); +extern int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); +extern int sigdelset (sigset_t *set, int sig); +extern int sigfillset (sigset_t *set); +extern int sigprocmask (int operation, const sigset_t *set, sigset_t *old_set); +extern int sigaddset (sigset_t *set, int sig); + +// STUBS +extern int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); +extern int sigismember(const sigset_t *set, int signum); +extern int sigpending(sigset_t *set); +extern int sigwait(const sigset_t *restrict set, int *restrict sig); + +// ==================================================== +// unistd +extern unsigned int alarm(unsigned int seconds); + +// ==================================================== + + +// WIP : + +#include +static uid_t +getuid(void) { + return 1000; +} + +static int +dup(int fd) { + puts("# 128: dup"); + return fd; +} +static int +dup2(int old, int new) { + puts("# 140: dup2"); + return -1; +} +static int +pipe(int fd[2]) { + puts("# 145: pipe"); + abort(); + return -1; +} + +#include +#define RLIMIT_NOFILE 7 +#define RLIMIT_STACK 3 +#define RLIM_INFINITY ((unsigned long int)(~0UL)) + +struct rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; +static int +getrlimit(int resource, struct rlimit *rlim) { + return -1; +} + + +static const char *gai_strerror_msg = "# 165: gai_strerror_msg"; +static const char * +gai_strerror(int errcode) { + return gai_strerror_msg; +} + + + + +// WIP: semaphores here +// ================================================================== +#include + +static int +semctl(int semid, int semnum, int cmd, ...) { + return 0; // -1; +} + +static int +semget(key_t key, int nsems, int semflg) { +#if 0 // PGDEBUG + printf("# 213: semget(key_t key = %d, int nsems=%d, int semflg=%d)\n", key, nsems, semflg); +#endif + return 1; +} + +static int +semop(int semid, struct sembuf *sops, size_t nsops) { + return 0; // -1; +} + + + +#include +#if defined(PYDK) +extern int shm_open(const char *name, int oflag, mode_t mode); +extern int shm_unlink(const char *name); +#else +static int +shm_open(const char *name, int oflag, mode_t mode) { + char tmpnam[128]; + int fd; + snprintf(tmpnam, 128, "/tmp%s", name); + fd=fileno(fopen(tmpnam, "w+")); + fprintf(stderr, "# 212: shm_open(%s) => %d\n", tmpnam, fd); + return fd; +} + +static int +shm_unlink(const char *name) { + char tmpnam[128]; + snprintf(tmpnam, 128, "/tmp%s", name); + fprintf(stderr, "# 220: shm_unlink(%s) STUB\n", tmpnam); + return remove(tmpnam); // -1 +} + +#endif + + + +#define system(command) system_wasi(command) +extern int system_wasi(const char *command); + + + +// time.h + +static void +tzset(void) { + puts("# 241: tzset(void) STUB"); +} + +#if defined(PG_INITDB) || defined(FE_UTILS_PRINT) || defined(PG_DUMP_PARALLEL) +static void +__SIG_IGN(int param) { +} +#endif + + +extern void sock_flush(); + + +// TODO: socket here +// ================================================================== + +/* +#include + +extern ssize_t sdk_recv(int sockfd, void *buf, size_t len, int flags); +extern ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, void *address, socklen_t *address_len); + +extern ssize_t sdk_send(int sockfd, const void *buf, size_t len, int flags); +extern ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, void *dest_addr, socklen_t addrlen); + +#define recv(sockfd, buf, len, flags) sdk_recv(sockfd, buf, len, flags) + + + + +static int +listen(int sockfd, int backlog) { + return 0; +} + +static struct group *_Nullable +getgrnam(const char *_Nullable name) { + return NULL; +} + + +static int +getaddrinfo(const char *restrict node, + const char *restrict service, + void *restrict hints, + void **restrict res) { + puts("# 60: getaddrinfo"); + return -1; +} +static void +freeaddrinfo(void *res) { + puts("# 65: freeaddrinfo"); +} + +extern ssize_t recvfrom_bc(int socket, void *buffer, size_t length, int flags, void *address, socklen_t *address_len); + +*/ + + +//#define pthread_mutex_lock(mut) sdk_pthread_mutex_lock(mut) +//extern int sdk_pthread_mutex_lock(void *mutex); + +/* + int pthread_mutex_lock(pthread_mutex_t *mutex); + int pthread_mutex_trylock(pthread_mutex_t *mutex); + int pthread_mutex_unlock(pthread_mutex_t *mutex); +*/ + + +#endif // I_WASI + +#else + #error "unknown port mode should be __EMSCRIPTEN__ or __wasi__" +#endif // __EMSCRIPTEN__ diff --git a/src/include/port/wasm_common.h b/src/include/port/wasm_common.h index 6f53f0fc5bb92..d8ade8cc7523e 100644 --- a/src/include/port/wasm_common.h +++ b/src/include/port/wasm_common.h @@ -45,7 +45,7 @@ # define PG_PLUGIN_INCLUDE "/pgdata/pg_plugin.h" #endif -#include "pg_debug.h" +#include "port/pg_debug.h" // #define COPY_INTERNAL #define COPY_OFF diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 934b7cef1b0f5..ea5cbb1594caa 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -83,14 +83,23 @@ keywords.o: $(top_srcdir)/src/include/parser/kwlist.h install: all installdirs $(INSTALL_PROGRAM) ecpg$(X) '$(DESTDIR)$(bindir)' +ifeq ($(PORTNAME), emscripten) + $(INSTALL_PROGRAM) ecpg.wasm '$(DESTDIR)$(bindir)/ecpg.wasm' +endif installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' uninstall: rm -f '$(DESTDIR)$(bindir)/ecpg$(X)' +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(bindir)/ecpg.wasm' +endif clean distclean: rm -f *.o ecpg$(X) rm -f typename.c rm -f preproc.y preproc.c preproc.h pgc.c c_kwlist_d.h ecpg_kwlist_d.h +ifeq ($(PORTNAME), emscripten) + rm -f ecpg.wasm +endif \ No newline at end of file diff --git a/src/makefiles/Makefile.emscripten b/src/makefiles/Makefile.emscripten index 3d37d043ad8c4..72e4c10a9fba4 100644 --- a/src/makefiles/Makefile.emscripten +++ b/src/makefiles/Makefile.emscripten @@ -14,3 +14,9 @@ AROPT = crs # Rule for building a shared library from a single .o file %.so: %.o $(CC) $(CFLAGS) $< $(LDFLAGS) $(LDFLAGS_SL) -shared -o $@ + +emscripten_include_dir := $(pkgincludedir)/emscripten +emscripten_base_dir := $(emscripten_include_dir)/base +emscripten_imports_dir := $(emscripten_base_dir)/imports +emscripten_extension_dir := $(emscripten_include_dir)/extension +emscripten_extension_imports_dir := $(emscripten_extension_dir)/imports \ No newline at end of file diff --git a/src/makefiles/pgxs.mk b/src/makefiles/pgxs.mk index 0de3737e789b4..bf4916166cba1 100644 --- a/src/makefiles/pgxs.mk +++ b/src/makefiles/pgxs.mk @@ -249,6 +249,12 @@ ifdef MODULES ifeq ($(with_llvm), yes) $(foreach mod, $(MODULES), $(call install_llvm_module,$(mod),$(mod).bc)) endif # with_llvm +ifeq ($(PORTNAME), emscripten) + $(LLVM_NM) -u *.o | awk '{print $$2}' | sed '/^$$/d' | sort -u > undef.txt + $(LLVM_NM) *.o | awk '$$2 ~ /^[TDB]$$/ {print $$3}' | sed '/^$$/d' | sort -u > defs.txt + comm -23 undef.txt defs.txt > '$(emscripten_extension_imports_dir)/$(MODULES).imports' + rm -f undef.txt defs.txt +endif # PORTNAME=emscripten endif # MODULES ifdef DOCS ifdef docdir @@ -271,6 +277,12 @@ ifdef MODULE_big ifeq ($(with_llvm), yes) $(call install_llvm_module,$(MODULE_big),$(OBJS)) endif # with_llvm +ifeq ($(PORTNAME), emscripten) + $(LLVM_NM) -u $(OBJS) | awk '{print $$2}' | sed '/^$$/d' | sort -u > undef.txt + $(LLVM_NM) $(OBJS) | awk '$$2 ~ /^[TDB]$$/ {print $$3}' | sed '/^$$/d' | sort -u > defs.txt + comm -23 undef.txt defs.txt > '$(emscripten_extension_imports_dir)/$(MODULE_big).imports' + rm -f undef.txt defs.txt +endif # PORTNAME=emscripten install: install-lib endif # MODULE_big @@ -302,6 +314,9 @@ ifdef MODULE_big installdirs: installdirs-lib endif # MODULE_big +ifeq ($(PORTNAME), emscripten) + $(MKDIR_P) '$(DESTDIR)$(emscripten_extension_imports_dir)' +endif uninstall: ifneq (,$(EXTENSION)) @@ -318,6 +333,9 @@ ifdef MODULES ifeq ($(with_llvm), yes) $(foreach mod, $(MODULES), $(call uninstall_llvm_module,$(mod))) endif # with_llvm +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(emscripten_extension_imports_dir)/$(MODULES).imports' +endif endif # MODULES ifdef DOCS rm -f $(addprefix '$(DESTDIR)$(docdir)/$(docmoduledir)'/, $(DOCS)) @@ -340,6 +358,10 @@ ifeq ($(with_llvm), yes) $(call uninstall_llvm_module,$(MODULE_big)) endif # with_llvm +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(emscripten_extension_imports_dir)/$(MODULE_big).imports' +endif + uninstall: uninstall-lib endif # MODULE_big diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile index 63cb96fae3efc..7bdf2cd3f1e51 100644 --- a/src/pl/plpgsql/src/Makefile +++ b/src/pl/plpgsql/src/Makefile @@ -53,11 +53,20 @@ install: all install-lib install-data install-headers installdirs: installdirs-lib $(MKDIR_P) '$(DESTDIR)$(datadir)/extension' $(MKDIR_P) '$(DESTDIR)$(includedir_server)' +ifeq ($(PORTNAME), emscripten) + $(MKDIR_P) '$(DESTDIR)$(emscripten_extension_imports_dir)' +endif uninstall: uninstall-lib uninstall-data uninstall-headers install-data: installdirs $(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) '$(DESTDIR)$(datadir)/extension/' +ifeq ($(PORTNAME), emscripten) + $(LLVM_NM) -u $(OBJS) | awk '{print $$2}' | sed '/^$$/d' | sort -u > undef.txt + $(LLVM_NM) $(OBJS) | awk '$$2 ~ /^[TDB]$$/ {print $$3}' | sed '/^$$/d' | sort -u > defs.txt + comm -23 undef.txt defs.txt > '$(DESTDIR)$(emscripten_extension_imports_dir)/$(NAME).imports' + rm -f undef.txt defs.txt +endif # The plpgsql.h header file is needed by instrumentation plugins install-headers: installdirs @@ -65,6 +74,9 @@ install-headers: installdirs uninstall-data: rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA))) +ifeq ($(PORTNAME), emscripten) + rm -f '$(DESTDIR)$(emscripten_extension_imports_dir)/$(NAME).imports' +endif uninstall-headers: rm -f '$(DESTDIR)$(includedir_server)/plpgsql.h' diff --git a/src/test/Makefile b/src/test/Makefile index dbd3192874d33..03bb5ccd34dfe 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -7,6 +7,12 @@ # src/test/Makefile # #------------------------------------------------------------------------- +ifeq ($(PORTNAME), emscripten) +# edited for pglite +all: $(echo src/test and src/test/isolation skipped) +clean check installcheck all-src-recurse: all +install: all +else subdir = src/test top_builddir = ../.. @@ -49,3 +55,5 @@ $(call recurse,installcheck, $(installable_dirs)) $(call recurse,install, $(installable_dirs)) $(recurse_always) + +endif \ No newline at end of file diff --git a/src/test/isolation/Makefile b/src/test/isolation/Makefile index ade2256ed3aa7..e0094bcb662ff 100644 --- a/src/test/isolation/Makefile +++ b/src/test/isolation/Makefile @@ -1,6 +1,12 @@ # # Makefile for isolation tests # +ifeq ($(PORTNAME), emscripten) +# edited for pglite +all: $(echo src/test and src/test/isolation skipped) +clean check installcheck all-src-recurse: all +install: all +else PGFILEDESC = "pg_isolation_regress/isolationtester - multi-client test driver" PGAPPICON = win32 @@ -72,3 +78,5 @@ installcheck-prepared-txns: all temp-install check-prepared-txns: all temp-install $(pg_isolation_regress_check) --schedule=$(srcdir)/isolation_schedule prepared-transactions prepared-transactions-cic + +endif \ No newline at end of file diff --git a/src/timezone/Makefile b/src/timezone/Makefile index c85e831247a5a..d871b67582378 100644 --- a/src/timezone/Makefile +++ b/src/timezone/Makefile @@ -48,8 +48,12 @@ endif # but GNU make versions <= 3.78.1 or perhaps later have a bug # that causes a segfault; GNU make 3.81 or later fixes this. ifeq (,$(ZIC)) +ifeq ($(PORTNAME), emscripten) +ZIC= /usr/sbin/zic +else ZIC= ./zic endif +endif zic: $(ZICOBJS) | submake-libpgport $(CC) $(CFLAGS) $(ZICOBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) @@ -77,3 +81,6 @@ endif clean distclean: rm -f zic$(X) $(ZICOBJS) abbrevs.txt +ifeq ($(PORTNAME), emscripten) + rm -f zic.wasm +endif \ No newline at end of file diff --git a/wasm-build/sdk_port.h b/wasm-build/include/sdk_port.h similarity index 100% rename from wasm-build/sdk_port.h rename to wasm-build/include/sdk_port.h diff --git a/wasm-builder/Dockerfile b/wasm-builder/Dockerfile deleted file mode 100644 index a806e5e098b16..0000000000000 --- a/wasm-builder/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM debian:12 AS build_sdk - -ARG PG_VERSION=17.5 -ARG SDK_VERSION=3.1.74.11bi-w-n -ARG DEBUG=false -ARG OBJDUMP=true -ARG PG_BRANCH=REL_17_5_WASM-pglite - -ENV \ - PG_VERSION=$PG_VERSION \ - SDK_VERSION=$SDK_VERSION \ - SDKROOT=/tmp/sdk \ - SYS_PYTHON=/usr/bin/python3 \ - DEBUG=$DEBUG \ - BUILDS=3.12 \ - EMFLAVOUR=3.1.74 - -RUN apt-get update && \ - apt-get install -y git wget lz4 bzip2 pv bash python3 build-essential libreadline-dev zlib1g-dev bison flex xz-utils perl - -RUN apt-get install -y clang autoconf wget curl lz4 lsb-release zlib1g-dev libssl-dev - -RUN bash -c "wget -qO- https://github.com/electric-sql/portable-sdk/releases/download/3.1.74.12.0/python3.13-wasm-sdk-debian12-$(arch).tar.lz4 | lz4 -d | tar -xvf -" diff --git a/wasm-builder/build-with-docker.sh b/wasm-builder/build-with-docker.sh deleted file mode 100755 index 6396781c40027..0000000000000 --- a/wasm-builder/build-with-docker.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# we are using a custom emsdk to build pglite wasm -# this is available as a docker image under electricsql/pglite-builder -IMG_NAME=${IMG_NAME:-"electricsql/pglite-builder"} - -[ -f postgres-pglite/configure ] || ln -s . postgres-pglite - -export WORKSPACE=${GITHUB_WORKSPACE:-$(pwd)} - -# normally would default to /workspace but that may cause trouble with debug paths in some IDE -export DOCKER_WORKSPACE=${DOCKER_WORKSPACE:-$WORKSPACE} - -cd $(realpath ${WORKSPACE}/postgres-pglite) - -[ -f ${BUILD_CONFIG:-postgres-pglite}/.buildconfig ] && cp ${BUILD_CONFIG:-postgres-pglite}/.buildconfig .buildconfig -[ -f ./pglite/.buildconfig ] && cp ./pglite/.buildconfig .buildconfig - - -source .buildconfig - -cat .buildconfig - - -if echo $IMG_NAME|grep -q debian -then - IMG_NAME="debian" - IMG_TAG="12" - wget -q -Osdk.tar.lz4 \ - https://github.com/electric-sql/portable-sdk/releases/download/${SDK_VERSION}/python3.13-wasm-sdk-${IMG_NAME}${IMG_TAG}-$(arch).tar.lz4 -else - IMG_TAG="${PG_VERSION}_${SDK_VERSION}" -fi - - -mkdir -p dist/pglite dist/extensions-emsdk - -if echo -n $@|grep -q it$ -then - PROMPT="&& bash ) || bash" -else - PROMPT=")" -fi - -if $WASI -then - OUT_DIR=wasi -else - OUT_DIR=emscripten -fi - -if $DEBUG -then - MAP_OUT_DIRS="-v ${WORKSPACE}/postgres-pglite/out/${OUT_DIR}/build:/tmp/sdk/build:rw -v ${WORKSPACE}/postgres-pglite/out/${OUT_DIR}/pglite:/tmp/pglite:rw" -else - MAP_OUT_DIRS="" -fi - -docker run $@ \ - --rm \ - --env-file .buildconfig \ - -e DEBUG=${DEBUG:-false} \ - -e WASI=${WASI:-false} \ - --workdir=${DOCKER_WORKSPACE} \ - -v ${WORKSPACE}/postgres-pglite:${DOCKER_WORKSPACE}:rw \ - -v ${WORKSPACE}/postgres-pglite/dist:/tmp/sdk/dist:rw \ - $MAP_OUT_DIRS \ - $IMG_NAME:$IMG_TAG \ - bash --noprofile --rcfile ./docker_rc.sh -ci "( ./wasm-build.sh ${WHAT:-\"contrib extra\"} $PROMPT" From 6c55ae16d798b6ab96856b0876b48f7f1c44f071 Mon Sep 17 00:00:00 2001 From: Tudor Zaharia <37596026+tdrz@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:59:13 +0200 Subject: [PATCH 7/7] Skip system call during dbinit * although syscalls are supported by emscripten, this is not what we (always) want. in this particular case, initdb is querying the server configuration by executing postgres with various parameters. when running on node, the calls end up trying to execute on the OS, which leads to a series of errors and undesirable behavior. instead, these syscalls should remain inside the wasm sandbox somehow. until we decide on a proper way to do this, the initdb querying of postgres server configs will just fail (which is the case anyhow atm). --- src/bin/initdb/initdb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 9aa9b48a79325..eb4699868b44e 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1245,7 +1245,14 @@ test_specific_config_settings(int test_conns, int test_buffs) DEVNULL, DEVNULL); fflush(NULL); + #if defined(__EMSCRIPTEN__) + // emscripten supports (some?) syscalls, but that's not actually what we want, because we want to remain inside the wasm sandbox + // so just return a dummy value until we decide how to handle syscalls. + // TODO: https://github.com/electric-sql/pglite/issues/798 + status = 123; + #else status = system(cmd.data); + #endif // #if defined(__EMSCRIPTEN__) termPQExpBuffer(&cmd);