From cde12bd7c1630ed7c9975a091c2e834d12f08e27 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Thu, 4 Jun 2026 19:15:01 -0700 Subject: [PATCH 1/2] Build napi as a shared library on Android Link napi as a shared library (libnapi.so) on Android instead of statically into each consumer, so native addons can be dlopen'd as standalone .node modules and resolve their napi_* imports via a real DT_NEEDED. The host and every addon then share a single napi instance. Other platforms keep static napi. --- Core/Node-API/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Core/Node-API/CMakeLists.txt b/Core/Node-API/CMakeLists.txt index 84c808fa..539223bc 100644 --- a/Core/Node-API/CMakeLists.txt +++ b/Core/Node-API/CMakeLists.txt @@ -152,7 +152,15 @@ if(NAPI_BUILD_ABI) message(STATUS "Selected ${NAPI_JAVASCRIPT_ENGINE}") endif() -add_library(napi ${SOURCES}) +# On Android, build napi as a shared library (libnapi.so) so that native addons can be dlopen'd as +# standalone .node modules and resolve their napi_* imports via a real DT_NEEDED -- bionic will not +# surface a statically-linked host's napi to a dlopen'd module. The host and every addon then share a +# single napi instance (one libnapi.so). Elsewhere napi remains a static library. +if(ANDROID) + add_library(napi SHARED ${SOURCES}) +else() + add_library(napi ${SOURCES}) +endif() target_include_directories(napi ${INCLUDE_DIRECTORIES}) target_link_libraries(napi ${LINK_LIBRARIES}) From 2e2ad1588babcc3689de1d285e7ae072bdc83787 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Thu, 4 Jun 2026 19:33:45 -0700 Subject: [PATCH 2/2] Gate the shared napi behind a JSR_NAPI_SHARED option (review feedback) - Don't hard-force SHARED: add a JSR_NAPI_SHARED CMake option (default ON on Android, OFF elsewhere) so integrators can keep a static napi via -DJSR_NAPI_SHARED=OFF without patching the project. - Fix the comment: the non-shared branch keeps add_library(napi ${SOURCES}), which follows the project's default library type (BUILD_SHARED_LIBS), not necessarily static. --- Core/Node-API/CMakeLists.txt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Core/Node-API/CMakeLists.txt b/Core/Node-API/CMakeLists.txt index 539223bc..e03d4af7 100644 --- a/Core/Node-API/CMakeLists.txt +++ b/Core/Node-API/CMakeLists.txt @@ -152,11 +152,20 @@ if(NAPI_BUILD_ABI) message(STATUS "Selected ${NAPI_JAVASCRIPT_ENGINE}") endif() -# On Android, build napi as a shared library (libnapi.so) so that native addons can be dlopen'd as -# standalone .node modules and resolve their napi_* imports via a real DT_NEEDED -- bionic will not -# surface a statically-linked host's napi to a dlopen'd module. The host and every addon then share a -# single napi instance (one libnapi.so). Elsewhere napi remains a static library. +# On Android, native addons are dlopen'd as standalone .node modules and resolve their napi_* imports +# from a shared napi at load time -- bionic will not surface a statically-linked host's napi to a +# dlopen'd module, so the host and every addon must share a single libnapi.so. Default napi to a +# shared library on Android so that model works out of the box; an integrator who wants a static napi +# (e.g. for size/packaging) can override with -DJSR_NAPI_SHARED=OFF. The option defaults OFF on other +# platforms, where napi keeps following the project's default library type (i.e. honors +# BUILD_SHARED_LIBS). +set(JSR_NAPI_SHARED_DEFAULT OFF) if(ANDROID) + set(JSR_NAPI_SHARED_DEFAULT ON) +endif() +option(JSR_NAPI_SHARED "Build napi as a shared library (libnapi.so)" ${JSR_NAPI_SHARED_DEFAULT}) + +if(JSR_NAPI_SHARED) add_library(napi SHARED ${SOURCES}) else() add_library(napi ${SOURCES})