diff options
author | William Marlow <william.marlow@ibm.com> | 2022-02-04 13:12:57 +0300 |
---|---|---|
committer | RafaelGSS <rafael.nunu@hotmail.com> | 2022-05-10 15:13:19 +0300 |
commit | 0eb32ed976296b5082c7c5e1e13f32e91cb23400 (patch) | |
tree | 90d2389881106e04696c40dce513feab6b7b15b3 /src | |
parent | 840e61e745e37448c9b23a0d44892b0bc50e1f24 (diff) |
build: fix various shared library build issues
Node.js unofficially supports a shared library variant where the
main node executable is a thin wrapper around node.dll/libnode.so.
The key benefit of this is to support embedding Node.js in other
applications.
Since Node.js 12 there have been a number of issues preventing the
shared library build from working correctly, primarily on Windows:
* A number of functions used executables such as `mksnapshot` are
not exported from `libnode.dll` using a `NODE_EXTERN` attribute
* A dependency on the `Winmm` system library is missing
* Incorrect defines on executable targets leads to `node.exe`
claiming to export a number of functions that are actually in
`libnode.dll`
* Because `node.exe` attempts to export symbols, `node.lib` gets
generated causing native extensions to try to link against
`node.exe` not `libnode.dll`.
* Similarly, because `node.dll` was renamed to `libnode.dll`,
native extensions don't know to look for `libnode.lib` rather
than `node.lib`.
* On macOS an RPATH is added to find `libnode.dylib` relative to
`node` in the same folder. This works fine from the
`out/Release` folder but not from an installed prefix, where
`node` will be in `bin/` and `libnode.dylib` will be in `lib/`.
* Similarly on Linux, no RPATH is added so LD_LIBRARY_PATH needs
setting correctly for `bin/node` to find `lib/libnode.so`.
For the `libnode.lib` vs `node.lib` issue there are two possible
options:
1. Ensure `node.lib` from `node.exe` does not get generated, and
instead copy `libnode.lib` to `node.lib`. This means addons
compiled when referencing the correct `node.lib` file will
correctly depend on `libnode.dll`. The down side is that
native addons compiled with stock Node.js will still try to
resolve symbols against node.exe rather than libnode.dll.
2. After building `libnode.dll`, dump the exports using `dumpbin`,
and process this to generate a `node.def` file to be linked into
`node.exe` with the `/DEF:node.def` flag. The export entries
in `node.def` will all read
```
my_symbol=libnode.my_symbol
```
so that `node.exe` will redirect all exported symbols back to
`libnode.dll`. This has the benefit that addons compiled with
stock Node.js will load correctly into `node.exe` from a shared
library build, but means that every embedding executable also
needs to perform this same trick.
I went with the first option as it is the cleaner of the two
solutions in my opinion. Projects wishing to generate a shared
library variant of Node.js can now, for example,
```
.\vcbuild dll package vs
```
to generate a full node installation including `libnode.dll`,
`Release\node.lib`, and all the necessary headers. Native addons
can then be built against the shared library build easily by
specifying the correct `nodedir` option.
For example
```
>npx node-gyp configure --nodedir
C:\Users\User\node\Release\node-v18.0.0-win-x64
...
>npx node-gyp build
...
>dumpbin /dependents build\Release\binding.node
Microsoft (R) COFF/PE Dumper Version 14.29.30136.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file build\Release\binding.node
File Type: DLL
Image has the following dependencies:
KERNEL32.dll
libnode.dll
VCRUNTIME140.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
...
```
PR-URL: https://github.com/nodejs/node/pull/41850
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Beth Griggs <bgriggs@redhat.com>
Reviewed-By: Richard Lau <rlau@redhat.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/debug_utils.h | 6 | ||||
-rw-r--r-- | src/env.h | 2 | ||||
-rw-r--r-- | src/node.h | 10 | ||||
-rw-r--r-- | src/node_internals.h | 15 | ||||
-rw-r--r-- | src/node_native_module.h | 2 | ||||
-rw-r--r-- | src/node_options.h | 2 | ||||
-rw-r--r-- | src/node_snapshot_builder.h | 2 | ||||
-rw-r--r-- | src/util.h | 6 |
8 files changed, 29 insertions, 16 deletions
diff --git a/src/debug_utils.h b/src/debug_utils.h index 377493359e9..bd1fa5207f9 100644 --- a/src/debug_utils.h +++ b/src/debug_utils.h @@ -35,7 +35,7 @@ template <typename... Args> inline std::string SPrintF(const char* format, Args&&... args); template <typename... Args> inline void FPrintF(FILE* file, const char* format, Args&&... args); -void FWrite(FILE* file, const std::string& str); +void NODE_EXTERN_PRIVATE FWrite(FILE* file, const std::string& str); // Listing the AsyncWrap provider types first enables us to cast directly // from a provider type to a debug category. @@ -57,7 +57,7 @@ enum class DebugCategory { CATEGORY_COUNT }; -class EnabledDebugList { +class NODE_EXTERN_PRIVATE EnabledDebugList { public: bool enabled(DebugCategory category) const { DCHECK_GE(static_cast<int>(category), 0); @@ -168,7 +168,7 @@ void CheckedUvLoopClose(uv_loop_t* loop); void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream); namespace per_process { -extern EnabledDebugList enabled_debug_list; +extern NODE_EXTERN_PRIVATE EnabledDebugList enabled_debug_list; template <typename... Args> inline void FORCE_INLINE Debug(DebugCategory cat, diff --git a/src/env.h b/src/env.h index 7e35833e45b..475ca4d8b7c 100644 --- a/src/env.h +++ b/src/env.h @@ -558,7 +558,7 @@ class Environment; struct AllocatedBuffer; typedef size_t SnapshotIndex; -class IsolateData : public MemoryRetainer { +class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { public: IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop, diff --git a/src/node.h b/src/node.h index dec8dd6805f..e866fcd9369 100644 --- a/src/node.h +++ b/src/node.h @@ -32,6 +32,16 @@ # define NODE_EXTERN __attribute__((visibility("default"))) #endif +// Declarations annotated with NODE_EXTERN_PRIVATE do not form part of +// the public API. They are implementation details that can and will +// change between releases, even in semver patch releases. Do not use +// any such symbol in external code. +#ifdef NODE_SHARED_MODE +#define NODE_EXTERN_PRIVATE NODE_EXTERN +#else +#define NODE_EXTERN_PRIVATE +#endif + #ifdef BUILDING_NODE_EXTENSION # undef BUILDING_V8_SHARED # undef BUILDING_UV_SHARED diff --git a/src/node_internals.h b/src/node_internals.h index bf760f4d995..c56fccb9845 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -320,13 +320,14 @@ enum InitializationSettingsFlags : uint64_t { }; // TODO(codebytere): eventually document and expose to embedders. -InitializationResult InitializeOncePerProcess(int argc, char** argv); -InitializationResult InitializeOncePerProcess( - int argc, - char** argv, - InitializationSettingsFlags flags, - ProcessFlags::Flags process_flags = ProcessFlags::kNoFlags); -void TearDownOncePerProcess(); +InitializationResult NODE_EXTERN_PRIVATE InitializeOncePerProcess(int argc, + char** argv); +InitializationResult NODE_EXTERN_PRIVATE InitializeOncePerProcess( + int argc, + char** argv, + InitializationSettingsFlags flags, + ProcessFlags::Flags process_flags = ProcessFlags::kNoFlags); +void NODE_EXTERN_PRIVATE TearDownOncePerProcess(); void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s); void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s); void SetIsolateCreateParamsForNode(v8::Isolate::CreateParams* params); diff --git a/src/node_native_module.h b/src/node_native_module.h index 3be3f2364dd..7acd154d419 100644 --- a/src/node_native_module.h +++ b/src/node_native_module.h @@ -29,7 +29,7 @@ using NativeModuleCacheMap = // This class should not depend on any Environment, or depend on access to // the its own singleton - that should be encapsulated in NativeModuleEnv // instead. -class NativeModuleLoader { +class NODE_EXTERN_PRIVATE NativeModuleLoader { public: NativeModuleLoader(const NativeModuleLoader&) = delete; NativeModuleLoader& operator=(const NativeModuleLoader&) = delete; diff --git a/src/node_options.h b/src/node_options.h index a3937900b41..8e9a044520e 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -491,7 +491,7 @@ void Parse( namespace per_process { extern Mutex cli_options_mutex; -extern std::shared_ptr<PerProcessOptions> cli_options; +extern NODE_EXTERN_PRIVATE std::shared_ptr<PerProcessOptions> cli_options; } // namespace per_process diff --git a/src/node_snapshot_builder.h b/src/node_snapshot_builder.h index 61450a51dfa..2714293fbc9 100644 --- a/src/node_snapshot_builder.h +++ b/src/node_snapshot_builder.h @@ -13,7 +13,7 @@ namespace node { class ExternalReferenceRegistry; struct SnapshotData; -class SnapshotBuilder { +class NODE_EXTERN_PRIVATE SnapshotBuilder { public: static std::string Generate(const std::vector<std::string> args, const std::vector<std::string> exec_args); diff --git a/src/util.h b/src/util.h index 8c21c53a134..14c8758c849 100644 --- a/src/util.h +++ b/src/util.h @@ -26,6 +26,8 @@ #include "v8.h" +#include "node.h" + #include <climits> #include <cstddef> #include <cstdio> @@ -110,8 +112,8 @@ struct AssertionInfo { const char* message; const char* function; }; -[[noreturn]] void Assert(const AssertionInfo& info); -[[noreturn]] void Abort(); +[[noreturn]] void NODE_EXTERN_PRIVATE Assert(const AssertionInfo& info); +[[noreturn]] void NODE_EXTERN_PRIVATE Abort(); void DumpBacktrace(FILE* fp); // Windows 8+ does not like abort() in Release mode |