Introduction to node-gyp

node-gyp is the recommended tool by Node.js to build native (i.e. C++) addons. “It bundles the gyp project used by the Chromium team and takes away the pain of dealing with the various differences in build platforms”¹. gyp is just another build tool (like cmake) which makes compiling and linking much easier. node-gyp by default generates a shared .so or .dll library, which can be required (e.g. using bindings) and used by Node.js modules.

Motivation

In my latest project, I was responsible for writing a JavaScript binding (a Node.js addon to be more specific) to an existing hardware abstraction module which was written in C++. This module in turn is dependent on other in-house libraries. Since all of our C++ projects are built using cmake (equipped with some fancy customized macros), I was already familiar how to tell the compiler and linker where to search for sources and required libraries, and how to create an executable or libraries which knows where to look for dependencies at the runtime. All I had to do now was to translate my cmake knowledge to node-gyp.

If someone doesn’t have a solid background in C++, one will be confronted (sooner or later) with one of the following errors during the build process:

  • No such file or directory
  • Unable to find libxxx.so.x
  • Undefined symbol
  • ...

Such errors are either compilation or linking² errors which we don’t have in case of interpreted languages³ (e.g JavaScript) and might be confusing to developers of such programming languages.

Crash Course: C++ Life Cycle

A typical C++ program is made up of source files containing a number of #includes, #defines, and other directives. The first step in the course of making an executable or a library from source files is called preprocessing which takes care of the directives. The resulting file (without any directives) is fed to the compiler and and an object file is returned: still neither an executable nor a library. At this stage linker comes into action and creates a library or an executable from the object file(s). From now on the generated executable can be run or the generated library can be used by other libraries and executables.

Configuring node-gyp

node-gyp (just like gyp) uses a .gyp file called binding.gyp which is used to generate a Makefile (in Unix) or vcxproj file (in Windows) which simply denote how the native addon is to be compiled and linked. This section focuses on the structure of binding.gyp in case of special needs. Before continuing it is recommended to take a look at skeleton of a gyp file.

As long as you don’t use third party libraries, that is everything except own code, node library, or system libraries, you can follow the official tutorial and you’re good to go. The problem begins when you want to use libraries which are not on the prerprocessor, or linker search paths. In former case you have to define the the path for all included headers and in the latter you have to define the path on which all used libraries reside.

  • Header files: include_dirs is an array that can be used to specify the location of all used headers.
  • Libraries : both at the time of linking, i.e. last phase of creating a library/executable, and at runtime, i.e. using that library/executable, required libraries have to be available. In both cases link_settings dictionary is to be used:
    • To provide a list of required libraries to the linker, libraries array is used. Here you can either have paths to specific libraries or you could use -l and -L flags to respectively define the library name and the directory to look for libraries into (example follows).
    • To introduce a “runtime search path”, rpath can be used inside ldflags array.

Example of binding.gyp

In the following example it is assumed that Boost libraries are required for the addon, but these are not on the standard system paths for headers and libraries and rather reside on a custom route called boost_root.

{
  "variables": {
    "boost_root%": "/path/to/boost"
  },
  "targets": [
    {
      "target_name": "fancy_addon",
      "sources": [ "addon.cpp", "fancy.cpp" ],
      "include_dirs": [
        "<@(boost_root)",
      ],
      "link_settings": {
        "libraries": [
          "-lboost_program_options",
          "-lboost_log",
        ],
        "ldflags": [
            "-L<@(boost_root)/stage/lib",
            "-Wl,-rpath,<@(boost_root)/stage/lib",
        ]
      },
      "cflags": [
        "-std=c++11"
      ],
      "cflags_cc!": [
        "-fno-rtti",
        "-fno-exceptions"
      ]
    }
  ]
}

Resources

¹ node-gyp Documentation

² C++ Compilation and Linking

³ Compiled vs. Interpreted

gcc -L / -l option flags

Wikipedia rpath