Friday, December 9, 2011

Building native modules for nodeJs 0.6+ with visual studio

Since version 0.6 node js supports windows native builds which allows the windows dev to use visual studio to build both nodejs and the modules. In order to make a simple node module in visual studio you will have to link the obj file with the node.lib file that is generated when you compile the nodejs on visual studio. Here is a detailed walkthrough on how to successfully build a native node module in visual studio 2010.

Building node (to get the node.lib file)

First and foremost you have to download the latest code from node’s git (or a stable one if you prefer) and run the vcbuild.bat file that is in the source. This bat will create a visual studio solution and build node using the msbuild tool chain. Watch out that by default this will build a Debug version of node and will fail (at least at the moment) to sign node.exe because it tries to locate node.exe in the Release folder. If you want to do it right (which is not required) you can either edit the vcbuild.bat file or run “vcbuild Release”. Either way, you will end up with a Release or a Debug folder which contains both node.exe and node.lib files. You will need that node.lib to link against it later on.

Writing the Helloworld.cpp

A lot of blog posts have been written on how to right a simple native Hello World node module so I will mention only the basics here.

  • Node uses the v8 engine to expose the native methods to javascript. This is why we have to include the v8.h file.
  • Modules usually inherit from the ObjectWrap class and this is why we need the fairly obvious node.h include file.
  • Each module has a constructor and a destructor where you can do your staff there but you must also specify an Init method to initialize the v8 bindings (expose the supported methods etc). 
  • Moreover, you will have to create New method which will be called every time you create a new object in javascript. 
  • Finally you must not forget to add the extern “C” declaration where you specify the init subroutine (which simply points to your modules init method) and call to NODE_MODULE where you specify the module name and the init method (which is written right above). 

So here is the HelloWorld.cpp we will be compiling in the following sections:


Setting up the visual studio project

Start by creating an empty Visual C++ project. 

Add the HelloWorld.cpp file you have downloaded from above (don’t expect me to believe that you wrote it) and add it in the Source Files virtual folder. You may optionally add the “node.h” file located in the node’s source directory under the folder src, in the header files to improve your development experience. The project structure should look like the following: 

Open the project properties (right click properties on the project) and go to the Configuration Properties→General tab. There you may specify the output directory etc but you must make sure that you select a Dynamic Library (.dll) in the Configuration Type. You may also specify that the target extension is a .node file to avoid renaming the dll output to .node (nodejs requires modules to have a .node extension). My property page look like the following: 

Move on to Configuration Properties→VC++ Directories tab. Here you will have to add the include directories for node.h, v8.h and uv.h files and the location of the node.lib in the library directories. 

All files are located in the node source tree you have compiled earlier (from now on I will refer to that location as %NODE_ROOT%). In the “Include Directories” you must add the following: 
  • %NODE_ROOT%\src\ 
  • %NODE_ROOT%\deps\v8\include\ 
  • %NODE_ROOT%\deps\uv\include\ 

In the “Library Directories” you must add either %NODE_ROOT%\Release\ or %NODE_ROOT%\Debug\ depending on the exact location of your node.lib file and whether you need the debug symbols that are exposed by node or not. 

There is also an alternative to this step. You may specify the Include Directories in the Configuration Properties→C/C++→General tab under the “Additional Include Directories” field, illustrated bellow: 

As for the Library Directories, you may specify the exact path of the node.lib in the Linker (as show on a later step). 

Based on the reading I did on how to build a native module on nodejs, most of the tutorials mentioned that you should add the “BUILDING_NODE_EXTENSION 1” def into your modules. My personal experience says that this is not requires in node v0.6 but if you want, you may specify this def in the Configuration Properties→C/C++→Preprocessor tab as shown in the next picture: 

The final step is to go to the Configuration Properties→Linker→Input tab where you need to specify the node.lib as an “Additional Dependency”. If you skipped the VC++ Directories setup mentioned above, you should specify the full path to the node.lib file, otherwise a simple node.lib is sufficient: 

That’s all. You may now build the project and you should see the following output:

If you haven’t set the target extension to .node you will have to manually rename it from .dll to .node and you will be to load the module by posting the require(‘pathtomodule’) command

Watch out that in my HelloWorld.cpp I expose the Hello function as hello (note the small h) on purpose to demonstrate that it doesn't matter what’s the name of the method in you cpp file, but the name specified in the v8 engine. You may download a test.js file to test your newly created module here: 

I hope that this article will give you a heads up on the native windows nodejs module building process! You may also check a simple bat script I have prepared to autocompile the node module.

9 comments:

Warspawn said...

thank you so much for this! this is what I wanted to do but didn't know how.

Brad Williams said...

Thanks for this post! I downloaded 0.6.6 node source, and have python 2.7.2, but vcbuild.bat gives error almost immediately:
C:\nodejs>vcbuild.bat
Traceback (most recent call last):
File "tools\gyp_node", line 59, in
run_gyp(gyp_args)
File "tools\gyp_node", line 18, in run_gyp
rc = gyp.main(args)
File ".\tools\gyp\pylib\gyp\__init__.py", line 471, in main
options.circular_check)
File ".\tools\gyp\pylib\gyp\__init__.py", line 111, in Load
depth, generator_input_info, check, circular_check)
File ".\tools\gyp\pylib\gyp\input.py", line 2270, in Load
depth, check)
File ".\tools\gyp\pylib\gyp\input.py", line 387, in LoadTargetBuildFile
build_file_path)
File ".\tools\gyp\pylib\gyp\input.py", line 1010, in ProcessVariablesAndConditionsInDict
build_file)
File ".\tools\gyp\pylib\gyp\input.py", line 1025, in ProcessVariablesAndConditionsInList
ProcessVariablesAndConditionsInDict(item, is_late, variables, build_file)
File ".\tools\gyp\pylib\gyp\input.py", line 984, in ProcessVariablesAndConditionsInDict
ProcessConditionsInDict(the_dict, is_late, variables, build_file)
File ".\tools\gyp\pylib\gyp\input.py", line 842, in ProcessConditionsInDict
if eval(ast_code, {'__builtins__': None}, variables):
File "", line 1, in
NameError: name 'node_use_isolates' is not defined while evaluating condition 'node_use_isolates=="true"' in node.gyp while trying to load node.gyp
Failed to create vc project files.

Any ideas?

Brad Williams said...

It turns out 0.6.6 has this problem. I went back to 0.6.2 and can build node.js. Thanks again for your post!

esmitt said...

Hi, I follow all your steps and I compile the .cpp in order to obtain the .node file. Then, I got the following error:

Loading module from :
> C:Project\Debug\Again. node

node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Out of memory.
at Object..node (module.js:463:11)
at Module.load (module.js:351:31)
at Function._load (module.js:310:12)
at Module.require (module.js:357:17)
at require (module.js:368:17)
at Object. (C:\Project\Debug\some.js:3:18)
at Module._compile (module.js:432:26)
at Object..js (module.js:450:10)
at Module.load (module.js:351:31)
at Function._load (module.js:310:12)

Any idea about it? Out of memory error? I was using the same .js and .cpp that you post here.
Thanks in advanced

Andreas Botsikas said...

@esmitt: This is a generic error that node couldn't load your module. A bug I have noticed in the current version of node in windows is that it doesn't load any module with a - in their names (btw, i hope it's an error the space between the name and the extension in your comment). If this is not your case, check your dependencies with http://www.dependencywalker.com/ and make sure that node has access to all your dependencies. If even this is not your case check my post on debuging node loading modules (http://botsikas.blogspot.com/2011/12/debugging-native-node-modules-loading.html) or edit node.cc from line 1606 and on to display the actual system error (err.sys_errno_) and look it up (see my post) to have a better understanding of your problem. A quick and ugly hack to do that is the following code:

printf("System error code: %d",err.sys_errno_);

You should also do something similar to the generic "Out of memory." exception thrown in the same code file so that you can track down the exact problem.

If this doesn't help you either, then please post the code cause it might be an error there...

Andreas Botsikas said...

@esmitt also make sure you check http://nodejs.org/docs/latest/api/addons.html.

Note that all Node addons must export an initialization function:

NODE_MODULE(module_name, Initialize)

The module_name needs to match the filename of the final binary (minus the .node suffix).

Eric Muyser said...

Note: the nodejs vcbuild.bat doesn't work in directories which have spaces, due to a dependency. So use a simple directory like c:\nodejs.

The last comment, "module_name needs to match the filename of the final binary" was pretty important. I had forgotten that. Tutorial works great, thanks!

Anonymous said...

I have tried with node 0.6.17 on windows. To make HelloWorld working I have modified the code like:
extern "C" void NODE_EXTERN init (Handle Object target)
{
HelloWorld::Init(target);
}

Unknown said...

Thank you for the guide, it is very helpful!