With this, our end of the code is ready. What is left is slight modification of nodejs code, unfortunately you cannot skip this (at least I haven't found a way how to do this without modification to node).
Nodejs modification has several steps:
1) node.h modification to create proper registration functions
2) bootstrap_node.js modification to include our module in internal variables
3) node.vcxproj modification to fix building (at the moment of writing there are bugs in gyp script which cause errors while building as static library)
1) First let's start with simple modification of src\node.h Open the file and look for define of NODE_C_CTOR, you should find something like this:
#if defined(_MSC_VER)
#pragma section(".CRT$XCU", read)
#define NODE_C_CTOR(fn) \
NODE_CTOR_PREFIX void __cdecl fn(void); \
__declspec(dllexport, allocate(".CRT$XCU")) \
void (__cdecl*fn ## _)(void) = fn; \
NODE_CTOR_PREFIX void __cdecl fn(void)
This bit, specifically __declspec(dllexport) causes our registration functions to be omitted as they are stripped when built into static library. To fix this and to expose registration functions to linker we change that define to:
//#define NODE_C_CTOR(fn) \
// NODE_CTOR_PREFIX void __cdecl fn(void); \
// __declspec(dllexport, allocate(".CRT$XCU")) \
// void (__cdecl*fn ## _)(void) = fn; \
// NODE_CTOR_PREFIX void __cdecl fn(void)
#define NODE_C_CTOR(fn) void fn(void)
now all registration functions will have that signature and will be visible to linker.
2) With that modification out of the way, we need to modify javascript bootstrap code to include our addon in global namespace. Fortunately it's straightforward, open lib\internal\bootstrap_node.js, find function setupGlobalVariables() and before its end add a line that will register your addon:
global.addon = process.binding('myaddon');
From now on, you can access your addon in any node script by simply using addon.hello(); without the need to use require or anything else.
3) Now it's time to build node. First we need to generate solution and project files, in command line inside your node directory type:
vcbuild static release x86 nobuild
This will generate all the project and solution files. Of course for it to work, you need to have Visual Studio 2015/2017 installed alongside Python 2.7. Python also should be in your PATH environment variable.
With the files generated, we need to modify them slightly, just so we can build it (hopefully this will get fixed soon though and this step will be obsolete). Open node.vcxproj in your favourite text editor and find the following:
<Lib>
<OutputFile>$(OutDir)lib\$(ProjectName)$(TargetExt)</OutputFile>
</Lib>
This should be close to the beginning of the file. Change it to look like this:
<Lib>
<OutputFile>$(OutDir)lib\$(ProjectName)$(TargetExt)</OutputFile>
<TargetMachine>MachineX86</TargetMachine>
</Lib>
This made it buildable for Debug, now for Release find:
<Lib>
<AdditionalOptions>/LTCG %(AdditionalOptions)</AdditionalOptions>
<OutputFile>$(OutDir)lib\$(ProjectName)$(TargetExt)</OutputFile>
</Lib>
And similarily change to:
<Lib>
<AdditionalOptions>/LTCG %(AdditionalOptions)</AdditionalOptions>
<OutputFile>$(OutDir)lib\$(ProjectName)$(TargetExt)</OutputFile>
<TargetMachine>MachineX86</TargetMachine>
</Lib>
The last change we have to do, is to remove resources from the project. For some reason there's a conflict in compiling resources, to fix that, change
<ItemGroup>
<ResourceCompile Include="tools\msvs\genfiles\node_etw_provider.rc"/>
<ResourceCompile Include="tools\msvs\genfiles\node_perfctr_provider.rc"/>
<ResourceCompile Include="src\res\node.rc"/>
</ItemGroup>
to
<ItemGroup>
<ResourceCompile Include="tools\msvs\genfiles\node_etw_provider.rc"/>
<ResourceCompile Include="tools\msvs\genfiles\node_perfctr_provider.rc"/>
</ItemGroup>
After that save the project file and we're ready to compile:
vcbuild static release x86 noprojgen
Simply replace nobuild to noprojgen to skip project generation (it would overwrite our changes) and go straight to building. Node is quite big so it will take a while, but as a result you get node\Release\lib directory with compiled static libraries that are ready to be included in our project.
Final steps to building our project is to modify some settings in our addon. In your addon project, in Visual Studio, go to Properties -> C/C++ -> General and in Additional Include Directories input:
(node_dir)\deps\v8\include\;
(node_dir)\deps\uv\include\;
(node_dir)\src\;
(node_dir)\deps\cares\include\
(nan_dir)\;
Making sure you replace (node_dir) with a path where you cloned node and (nan_dir) with path where you cloned Nan.
In C/C++ -> Code Generation make sure to have Runtime Library set to Multi-threaded (/MT) or the debug equivalent if you run Debug version.
In Linker -> General set Additional Library Directories to (node_dir)\$(Configuration)\lib and in Linker -> Input set Additional Dependencies to
node.lib
cares.lib
gtest.lib
http_parser.lib
icudata.lib
icui18n.lib
icustubdata.lib
icutools.lib
icuucx.lib
libuv.lib
openssl.lib
v8_base_0.lib
v8_base_1.lib
v8_base_2.lib
v8_base_3.lib
v8_libbase.lib
v8_libplatform.lib
v8_libsampler.lib
v8_nosnapshot.lib
v8_snapshot.lib
zlib.lib
ws2_32.lib
shlwapi.lib
comctl32.lib
dbghelp.lib
Iphlpapi.lib
psapi.lib
wininet.lib
uxtheme.lib
winmm.lib
wintrust.lib
userenv.lib
Now you're ready to build your addon with nodejs. After the linking (which also can take a long time, it's LARGE!) you are given an exe file that you can run.
Sample run:
nodetest.exe -i
> addon
{ hello: [Function: hello] }
> addon.hello()
'hello world'
Hope this little guide will help someone in the future :)
Best regards,
Piotr