Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 30 additions & 23 deletions packages/emnapi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ set(EMNAPI_BASIC_MT_TARGET_NAME "emnapi-basic-mt")
set(EMNAPI_TARGET_NAME "emnapi")
set(EMNAPI_MT_TARGET_NAME "emnapi-mt")

set(EMNAPI_BASIC_NAPI_RS_MT_TARGET_NAME "emnapi-basic-napi-rs-mt")
set(EMNAPI_BASIC_NAPI_RS_TARGET_NAME "emnapi-basic-napi-rs")
set(EMNAPI_NAPI_RS_MT_TARGET_NAME "emnapi-napi-rs-mt")

set(DLMALLOC_TARGET_NAME "dlmalloc")
Expand Down Expand Up @@ -214,38 +214,48 @@ if(EMNAPI_BUILD_BASIC_LIBS)
target_link_options(${EMNAPI_BASIC_TARGET_NAME} INTERFACE "--js-library=${EMNAPI_JS_LIB}")
endif()

if(IS_WASM32 OR IS_WASM32_WASIP1 OR IS_WASM32_WASIP1_THREADS)
if(IS_WASM32 OR IS_WASM32_WASIP1_THREADS)
set(EMNAPI_BUILD_BASIC_MT ON)
else()
set(EMNAPI_BUILD_BASIC_MT OFF)
endif()
endif()

if(IS_WASM32_WASIP1_THREADS)
if(IS_WASM32_WASIP1 OR IS_WASM32_WASIP1_THREADS)
set(EMNAPI_BUILD_FOR_NAPI_RS ON)
else()
set(EMNAPI_BUILD_FOR_NAPI_RS OFF)
endif()

# napi-rs links these archives against plain `extern "C"` declarations, so
# every `napi_*` reference must resolve through the `env` wasm import module
# (the NAPI_EXTERN compile definition below), except
# `napi_add_env_cleanup_hook` / `napi_remove_env_cleanup_hook` which napi-rs
# imports from the `napi` module: src/emnapi_internal.h re-declares the two
# hooks under `napi` (EMNAPI_NAPI_RS_IMPORTS) so that every translation unit
# referencing them — async_cleanup_hook.c and, in the full composition,
# threadsafe_function.c — binds them to the same import module.
#
# wasm32-wasip1-threads ships the FULL composition: the same C async work and
# thread-safe function implementation the emscripten emnapi-mt archive uses.
# Plain wasm32-wasip1 ships the basic composition: async work and thread-safe
# functions remain wasm imports resolved by the JavaScript implementations
# from @emnapi/core plugins.
if(EMNAPI_BUILD_FOR_NAPI_RS)
add_library(${EMNAPI_NAPI_RS_MT_TARGET_NAME} STATIC ${EMNAPI_SRC} ${UV_SRC})
target_include_directories(${EMNAPI_NAPI_RS_MT_TARGET_NAME} PUBLIC ${EMNAPI_INCLUDE})
target_compile_definitions(${EMNAPI_NAPI_RS_MT_TARGET_NAME}
if(IS_WASM32_WASIP1_THREADS)
set(EMNAPI_NAPI_RS_TARGET ${EMNAPI_NAPI_RS_MT_TARGET_NAME})
set(EMNAPI_NAPI_RS_SRC ${EMNAPI_SRC} ${UV_SRC})
else()
set(EMNAPI_NAPI_RS_TARGET ${EMNAPI_BASIC_NAPI_RS_TARGET_NAME})
set(EMNAPI_NAPI_RS_SRC ${ENAPI_BASIC_SRC} ${UV_SRC})
endif()

add_library(${EMNAPI_NAPI_RS_TARGET} STATIC ${EMNAPI_NAPI_RS_SRC})
target_include_directories(${EMNAPI_NAPI_RS_TARGET} PUBLIC ${EMNAPI_INCLUDE})
target_compile_definitions(${EMNAPI_NAPI_RS_TARGET}
PUBLIC ${EMNAPI_DEFINES} "NAPI_EXTERN=__attribute__((__import_module__(\"env\")))"
PRIVATE "EMNAPI_NAPI_RS_IMPORTS"
)

if(EMNAPI_BUILD_BASIC_LIBS)
add_library(${EMNAPI_BASIC_NAPI_RS_MT_TARGET_NAME} STATIC
${ENAPI_BASIC_SRC}
${UV_SRC}
"${CMAKE_CURRENT_SOURCE_DIR}/src/thread/async_worker_create.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/thread/async_worker_init.S"
)
target_include_directories(${EMNAPI_BASIC_NAPI_RS_MT_TARGET_NAME} PUBLIC ${EMNAPI_INCLUDE})
target_compile_definitions(${EMNAPI_BASIC_NAPI_RS_MT_TARGET_NAME}
PUBLIC ${EMNAPI_DEFINES} "NAPI_EXTERN=__attribute__((__import_module__(\"env\")))"
)
endif()
endif()

if(EMNAPI_BUILD_BASIC_LIBS)
Expand Down Expand Up @@ -334,10 +344,7 @@ if(LIB_ARCH)
endif()

if(EMNAPI_BUILD_FOR_NAPI_RS)
install(TARGETS ${EMNAPI_NAPI_RS_MT_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
if(EMNAPI_BUILD_BASIC_LIBS)
install(TARGETS ${EMNAPI_BASIC_NAPI_RS_MT_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
endif()
install(TARGETS ${EMNAPI_NAPI_RS_TARGET} DESTINATION "lib/${LIB_ARCH}")
endif()
if(IS_WASM32)
install(TARGETS ${DLMALLOC_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
Expand Down
27 changes: 27 additions & 0 deletions packages/emnapi/src/emnapi_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,33 @@

#include "emnapi.h"

#ifdef EMNAPI_NAPI_RS_IMPORTS
#include "node_api.h"
#if NAPI_VERSION >= 8
// The napi-rs targets resolve every `napi_*` reference through the `env` wasm
// import module (see the NAPI_EXTERN compile definition in CMakeLists.txt),
// but napi-rs itself imports the env cleanup hooks from the `napi` module
// (`#[link(wasm_import_module = "napi")]` in napi-rs crates/napi/src/lib.rs).
// The last declaration wins in clang, so re-declaring the two hooks here
// rebinds only these two symbols to the `napi` import module. The
// re-declaration lives in this shared header because every translation unit
// referencing the hooks — async_cleanup_hook.c and, in the full composition,
// threadsafe_function.c — must agree on the import module, or wasm-ld
// rejects the final link with an import module mismatch.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wignored-attributes"
EXTERN_C_START
__attribute__((__import_module__("napi")))
napi_status NAPI_CDECL napi_add_env_cleanup_hook(
node_api_basic_env env, napi_cleanup_hook fun, void* arg);
__attribute__((__import_module__("napi")))
napi_status NAPI_CDECL napi_remove_env_cleanup_hook(
node_api_basic_env env, napi_cleanup_hook fun, void* arg);
EXTERN_C_END
#pragma clang diagnostic pop
#endif
#endif

#if defined(__EMSCRIPTEN__) || defined(__wasi__)
#include <assert.h>
#include <stdlib.h>
Expand Down
97 changes: 68 additions & 29 deletions script/release.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,36 +91,34 @@ async function main () {
// sysroot
// ], cwd)

// const wasip1ToolchainFile = path.join(__dirname, 'wasip1.cmake')
// fs.writeFileSync(
// wasip1ToolchainFile,
// fs.readFileSync(wasiToolchainFile, 'utf8').replace(/wasm32-wasi/g, 'wasm32-wasip1'),
// 'utf8'
// )

// await spawn('cmake', [
// ...generatorOptions,
// `-DCMAKE_TOOLCHAIN_FILE=${wasip1ToolchainFile.replace(/\\/g, '/')}`,
// `-DWASI_SDK_PREFIX=${WASI_SDK_PATH}`,
// '-DCMAKE_BUILD_TYPE=Release',
// '-DCMAKE_VERBOSE_MAKEFILE=1',
// '-DNAPI_EXPERIMENTAL=1',
// '-DNODE_API_EXPERIMENTAL_NO_WARNING=1',
// '-H.',
// '-Bbuild/wasm32-wasip1'
// ], cwd)
// wasi-sdk >= 25 ships a toolchain file targeting wasm32-wasip1 directly
if (fs.existsSync(path.join(wasiSdkPath, 'share/cmake/wasi-sdk-p1.cmake'))) {
await spawn('cmake', [
...generatorOptions,
`-DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_PATH}/share/cmake/wasi-sdk-p1.cmake`,
`-DWASI_SDK_PREFIX=${WASI_SDK_PATH}`,
'-DCMAKE_BUILD_TYPE=Release',
'-DCMAKE_VERBOSE_MAKEFILE=1',
'-DNAPI_EXPERIMENTAL=1',
'-DNODE_API_EXPERIMENTAL_NO_WARNING=1',
'-H.',
'-Bbuild/wasm32-wasip1'
], cwd)

// await spawn('cmake', [
// '--build',
// 'build/wasm32-wasip1'
// ], cwd)
await spawn('cmake', [
'--build',
'build/wasm32-wasip1'
], cwd)

// await spawn('cmake', [
// '--install',
// 'build/wasm32-wasip1',
// '--prefix',
// sysroot
// ], cwd)
await spawn('cmake', [
'--install',
'build/wasm32-wasip1',
'--prefix',
sysroot
], cwd)
} else {
throw new Error('share/cmake/wasi-sdk-p1.cmake not found, please use latest wasi-sdk')
}

let WASI_THREADS_CMAKE_TOOLCHAIN_FILE = ''
if (fs.existsSync(path.join(wasiSdkPath, 'share/cmake/wasi-sdk-pthread.cmake'))) {
Expand Down Expand Up @@ -240,19 +238,60 @@ async function main () {

fs.copySync(path.join(sysroot, 'lib/wasm32-emscripten'), path.join(__dirname, '../packages/emnapi/lib/wasm32-emscripten'))
fs.copySync(path.join(sysroot, 'lib/wasm64-emscripten'), path.join(__dirname, '../packages/emnapi/lib/wasm64-emscripten'))

// the freshly built sysroot must contain exactly the expected archive sets;
// checked before copying so that stale files in packages/emnapi/lib can
// never mask an archive missing from this build
for (const [arch, expected] of Object.entries(EXPECTED_WASI_ARCHIVES)) {
assertArchiveSet(path.join(sysroot, 'lib', arch), expected)
}

// fs.copySync(path.join(sysroot, 'lib/wasm32-wasi'), path.join(__dirname, '../packages/emnapi/lib/wasm32-wasi'))
// fs.copySync(path.join(sysroot, 'lib/wasm32-wasip1'), path.join(__dirname, '../packages/emnapi/lib/wasm32-wasip1'))
fs.copySync(path.join(sysroot, 'lib/wasm32-wasip1'), path.join(__dirname, '../packages/emnapi/lib/wasm32-wasip1'))
// fs.copySync(path.join(sysroot, 'lib/wasm32'), path.join(__dirname, '../packages/emnapi/lib/wasm32'))
if (WASI_THREADS_CMAKE_TOOLCHAIN_FILE) {
fs.copySync(path.join(sysroot, 'lib/wasm32-wasip1-threads'), path.join(__dirname, '../packages/emnapi/lib/wasm32-wasip1-threads'))
}

// the shipped lib dirs must match exactly too (catches leftovers from
// previous local runs, since fs.copySync merges into an existing dir)
for (const [arch, expected] of Object.entries(EXPECTED_WASI_ARCHIVES)) {
assertArchiveSet(path.join(__dirname, '../packages/emnapi/lib', arch), expected)
}

crossZip.zipSync(sysroot, path.join(__dirname, 'emnapi.zip'))
// fs.rmSync(sysroot, { force: true, recursive: true })

console.log(`Output: ${sysroot}`)
}

// exact archive sets shipped in packages/emnapi/lib for each wasi target;
// keep in sync with the install() rules in packages/emnapi/CMakeLists.txt
const EXPECTED_WASI_ARCHIVES = {
'wasm32-wasip1': [
'libemnapi.a',
'libemnapi-basic-napi-rs.a'
],
'wasm32-wasip1-threads': [
'libemnapi.a',
'libemnapi-mt.a',
'libemnapi-napi-rs-mt.a'
]
}

function assertArchiveSet (libDir, expected) {
const actual = fs.existsSync(libDir) ? fs.readdirSync(libDir) : []
const missing = expected.filter(name => !actual.includes(name))
const unexpected = actual.filter(name => !expected.includes(name))
if (missing.length > 0 || unexpected.length > 0) {
throw new Error(
`Unexpected archive set in ${libDir}\n` +
` missing: ${missing.length > 0 ? missing.join(', ') : '(none)'}\n` +
` unexpected: ${unexpected.length > 0 ? unexpected.join(', ') : '(none)'}`
)
}
}

main().catch(err => {
if (err instanceof ChildProcessError) {
process.exit(err.code)
Expand Down
Loading