-
-
Notifications
You must be signed in to change notification settings - Fork 33.6k
build: Fix various shared library build issues. #41850
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Review requested:
|
|
@nodejs/platform-windows |
|
Welcome @lux01 and thanks for the pull request! Can you rebase this against the current master branch and resolve the conflict? |
|
@nodejs/build |
We do have this nightly job - https://ci.nodejs.org/view/Node.js%20Daily/job/node-test-commit-linux-as-shared-lib/ (and the AIX equivalent which is broken). Not as good as being part of the regular PR builds, but better than nothing. |
@codebytere does Electron have a similar problem? |
|
Build of the nightly AIX shared library runs to see if it resolves the existing failures - https://ci.nodejs.org/job/node-test-commit-aix-shared-lib/nodes=aix71-ppc64/1424/ |
common.gypi
Outdated
| 'conditions': [ | ||
| [ 'node_shared=="true"', { | ||
| 'ldflags': [ | ||
| '-Wl,-bnoipath', # Do not add the full path to libnode.a in executables, trust that the LIBPATH is set correctly by the user at runtime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs to have the comment be in lines above the flags so that we can keep it within 80 char limit we have set in the project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I pushed some work-in-progress AIX changes to this branch instead of a new one! I'll remove the AIX changes from the PR.
I'm guessing that this breaks the ability to pre-compile addons with node-addon-api and have them run with different node binaries? It would mean that any module that ships pre-compiled binaries would either fail for shared library based exes, or have to fall back to compiling? In option 2) are there additional complications if the name of the exe is going to be different than node ? |
|
The run against the AIX shared library test job still failed: https://ci.nodejs.org/job/node-test-commit-aix-shared-lib/nodes=aix71-ppc64/1424/console with @lux01 That's actually failing earlier, as the job used to try to build addons and was filing with |
|
Job on OSX testing with --shared option - https://ci.nodejs.org/view/All/job/node-test-commit-osx-as-shared-mdawson/nodes=osx1015/2/console One test seems to fail, but I don't believe it is related to this PR as it failed on main as well: 18:17:27 not ok 3236 pummel/test-heapdump-http2
18:17:27 ---
18:17:27 duration_ms: 1.751
18:17:27 severity: crashed
18:17:27 exitcode: -11
18:17:27 stack: |-
18:17:27 (node:11723) internal/test/binding: These APIs are for internal testing only. Do not use them.
18:17:27 (Use `node --trace-warnings ...` to show where the warning was created)
18:17:27 ...otherwise the test suite seems to run clean with the --shared config option. |
| copy /Y libnode.dll %TARGET_NAME%\ > nul | ||
| if errorlevel 1 echo Cannot copy libnode.dll && goto package_error | ||
|
|
||
| mkdir %TARGET_NAME%\Release > nul |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lux01 I'm wondering why the node.lib goes into a new sub directory called Release ? The install.py seems to install libnode.lib and libnode.dll so not quite sure why there is a node.lib being copied here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed when using the --nodedir option to build native addons. When this is specified, node-gyp explicitly looks for node.lib in the Release or Debug subfolder of the node installation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k thanks.
vcbuild.bat
Outdated
| if errorlevel 1 echo Cannot copy node.lib && goto package_error | ||
|
|
||
| copy /Y ..\common.gypi %TARGET_NAME%\ > nul | ||
| if errorlevel 1 echo Cannot copy common.gypi && goto package_error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also wondering what this is needed for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, node-gyp tries to find common.gypi in the root of the nodedir if its not at include/node/common.gypi. I think this may be a remnant left over from before I figured out the HEADERS_ONLY install below. I'll try removing it
@lux01, I'm wondering why this is not a problem in the regular builds but is for the shared library build? |
| 'defines': [ | ||
| 'NODE_SHARED_MODE', | ||
| ], | ||
| 'conditions': [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary for all platforms? I'm wondering since the only issues you mentioned for linux/osx don't seem related to this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you may be right that these are only necessary on Windows. Their purpose is to ensure that libuv and v8 classes are correctly annotated with __declspec(dllimport) rather than __declspec(dllexport). I will refactor this condition block to only apply to Windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't hurt (and is more correct) to enable these on non-Windows.
The Lines 1398 to 1402 in 0367b5c
In both the main build and the shared library build cases this resolves to
You're right, I hadn't considered that as a use case. The product I work on only uses a small number of native addons, none of which are shipped pre-compiled. Addons that only ship pre-compiled binaries would not work at all, others would have to be rebuilt. With regards to the executable name issue, https://github.com/nodejs/node-gyp/blob/0da2e0140dbf4296d5ba7ea498fac05e74bb4bbb/addon.gypi#L81 Secondly it injects a delay load hook into the native addon which is run when Windows tries to resolve the delayed As long as the host process exports the necessary symbols, its name does not matter. For option (2) to work we have to rely on this delay load behaviour and ensure that the host process, whatever it is (be it a renamed From what I can tell, Electron doesn't have this issue because it doesn't use the shared library build of node, at least on Windows. I'm inferring this because running To get option (2) to work we would need to run the following steps after building
This is very achievable but is also very messy and means your host executable is tightly coupled with libnode.dll in a non-obvious way. The product I work on currently does this but we would prefer not to given the choice. We have a similar issue on AIX where native addons currently assume all their symbols can be found in the host executable. I think there we only have option (2) available to us. I'm still investigating... |
|
I build/ran on Windows and see some errors. I assume it is likely something I've not set up properly because I don't build on windows very often. @lux01 do you get a clean test pass and did you have to set up anything beyond what is outlined in https://github.com/nodejs/node/blob/master/BUILDING.md#option-1-manual-install to get everything passing? EDIT2 setting up the linux tools in the path AND running as admin, I'm down to 1 failures running with python tool\test.py C:\Users\midawson\node>python tools\test.py
Skipping pseudo-tty tests, as pseudo terminals are not available on Windows.
=== release test-fs-stat ===
Path: parallel/test-fs-stat
node:assert:991
throw newErr;
^
AssertionError [ERR_ASSERTION]: ifError got unwanted exception: EISDIR: illegal operation on a directory, fstat
at C:\Users\midawson\node\test\common\index.js:374:12
at C:\Users\midawson\node\test\common\index.js:411:15
at FSReqCallback.oncomplete (node:fs:197:21) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [Error: EISDIR: illegal operation on a directory, fstat] {
errno: -4068,
code: 'EISDIR',
syscall: 'fstat'
},
expected: null,
operator: 'ifError'
}
Node.js v18.0.0-pre
Command: C:\Users\midawson\node\out\Release\node.exe C:\Users\midawson\node\test\parallel\test-fs-stat.js
[05:55|% 100|+ 3369|- 1]: DoneAnd I found - #40006 so clearly the issue is not related to the PR. I'll take my results as a pass running the Node.js on windows using the shared library build with the wrapper node.exe and libnode.dll |
|
@lux01 - Thanks for the detailed reply in #41850 (comment) I think you are right that electron likely builds an exe that has statically linked in what it needed from Node.js versus using an external shared library. I believe that is also the case for boxnode so the shared library may be a different use case than those two. In terms of native addons, my take is that is an area where we could improve, but I don't think it should be a blocker for this PR as I don't think it breaks existing functionality, just improves what does work when built on a shared library (Correct me if I'm wrong on that). From my testing so far I can confirm that on Windows, Linux and OSX it results in a shared library and a node.exe wrapper that builds and passes the test suites (well, close enough that I think the single failure I see on each platform is not related to this PR). That leaves AIX which I think you were going to cover in a follow on PR? |
|
The main remaining question I have is related to NODE_EXTERN and if there would be any concern about the symbols being exported signaling some sort of commitment for support. I think that on non-windows platforms they are exported already, and from the explanation in #41850 (comment) they are exported in the windows static builds already so my thinking is that is should not change anything with respect to public exported API. I looked through our documentation and did not find anything with guidance on when to/when not to add NODE_EXTERN. I asked @jasnell and he indicated he thought it was ok as well but that maybe we should see if @bnoordhuis would chime in as well. @bnoordhuis do you have any concerns with us adding NODE_EXTERN to the methods as done in this PR? |
tools/msvs/find_python.cmd
Outdated
|
|
||
| :check-python | ||
| %~1 -V | ||
| %* -V |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lux01 can you comment on why this change is needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed when the path to python.exe has a space in it. Python 3.10 when installed for all users defaults to C:\Program Files\Python310\python.exe which fails without this change:
PS C:\source\node> .\vcbuild.bat debug vs2019 x64 dll
Looking for Python
Python found in C:\Program Files\Python310\\python.exe
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
Not an executable Python program
Could not find Python.
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: #41850
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Richard Lau <[email protected]>
|
Thank you @mhdawson! Glad to see this finally close :) |
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: #41850
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Richard Lau <[email protected]>
Refs: #41850 I think it would be good to have some info/context for maintainers on the shared library option. Add that based on disussion in #41850 Signed-off-by: Michael Dawson <[email protected]> PR-URL: #42517 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
Refs: #41850 I think it would be good to have some info/context for maintainers on the shared library option. Add that based on disussion in #41850 Signed-off-by: Michael Dawson <[email protected]> PR-URL: #42517 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
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: #41850
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Richard Lau <[email protected]>
Refs: #41850 I think it would be good to have some info/context for maintainers on the shared library option. Add that based on disussion in #41850 Signed-off-by: Michael Dawson <[email protected]> PR-URL: #42517 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
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: #41850
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Richard Lau <[email protected]>
Refs: #41850 I think it would be good to have some info/context for maintainers on the shared library option. Add that based on disussion in #41850 Signed-off-by: Michael Dawson <[email protected]> PR-URL: #42517 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
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: #41850
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Richard Lau <[email protected]>
Refs: #41850 I think it would be good to have some info/context for maintainers on the shared library option. Add that based on disussion in #41850 Signed-off-by: Michael Dawson <[email protected]> PR-URL: #42517 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
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: #41850
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Richard Lau <[email protected]>
Refs: #41850 I think it would be good to have some info/context for maintainers on the shared library option. Add that based on disussion in #41850 Signed-off-by: Michael Dawson <[email protected]> PR-URL: #42517 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
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: nodejs/node#41850
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Richard Lau <[email protected]>
Refs: nodejs/node#41850 I think it would be good to have some info/context for maintainers on the shared library option. Add that based on disussion in nodejs/node#41850 Signed-off-by: Michael Dawson <[email protected]> PR-URL: nodejs/node#42517 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
Node.js unofficially supports a shared library variant where the main
nodeexecutable is a thin wrapper aroundnode.dll/libnode.so. The key benefit of this is to support embedding Node.js in other applications, for example the product I work on embeds a Node.js runtime in 3 separate applications (alongside the JVM and CLR in the same processes) in addition to having some standalone Node.js applications.Since Node.js 12 there have been a number of issues preventing the shared library build from working correctly with the most significant issues being on Windows:
mksnapshotare not exported fromlibnode.dllusing aNODE_EXTERNattributeWinmmsystem library is missingnode.execlaiming to export a number of functions that are actually inlibnode.dllnode.exeattempts to export symbols,node.libgets generated causing native extensions to try to link againstnode.exenotlibnode.dll.node.dllwas renamed tolibnode.dll, native extensions don't know to look forlibnode.librather thannode.lib.libnode.dylibrelative tonodein the same folder. This works fine from theout/Releasefolder but not from an installed prefix, wherenodewill be inbin/andlibnode.dylibwill be inlib/.$ORIGINso LD_LIBRARY_PATH needs setting correctly forbin/nodeto findlib/libnode.so.For the
libnode.libvsnode.libissue there are two possible options:node.libfromnode.exedoes not get generated, and instead copylibnode.libtonode.lib. This means addons compiled when referencing the correctnode.libfile will correctly depend onlibnode.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.libnode.dll, dump the exports usingdumpbin, and process this to generate anode.deffile to be linked intonode.exewith the/DEF:node.defflag. The export entries innode.defwould all readnode.exewill redirect all exported symbols back tolibnode.dll. This has the benefit that addons compiled with stock Node.js will load correctly intonode.exefrom 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,
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 easily by specifying the correctnodediroption.For example
I have tested my changes on Linux/x86_64, Linux/s390x, Linux/ppc64le (all on RHEL 7.9 devtoolset-10), Windows/x86_64 (VS2019 on Windows Server 2019 Datacenter Edition), and macOS/x86_64 (macOS 12.1 with Apple Clang 13.0.0). I have tried to test on AIX but the GCC 8 installation on the machine I have access to is currently broken...
Fixes #34539
Fixes #41559
This PR is essentially a set of patches that I have been maintaining for the Node.js 14.x branch internally in my day job. Ideally I would love to see these changes get back ported but I can understand if this is not desireable.
Short of making shared library builds of Node.js the default, as is common for other embeddable runtimes such as the JVM or the CLR, or by building both the standard build and the shared library build together in the CI, I don't see a way to prevent changes that break the shared library from being merged in the future.