c++node.jslinkernode-modulesnode-gyp

How to use a static library with node-gyp on Windows?


I am trying to link a static library using node-gyp in order to use it as a native node addon. However, when I run node-gyp rebuild I get the following linking error:

error LNK2001: unresolved external symbol "double __cdecl mylibengine::criticalZvalue(double)" (?criticalZvalue@mylibengine@@YANN@Z)

Which is immediately followed by:

C:\Users\Mihai\Desktop\msp-addon\build\Release\mylib-addon.node : fatal error LNK1120: 1 unresolved externals [C:\Users\Mihai\Desktop\mylib-addon\build\mylib-addon.vcxproj

The binding.gyp looks like:

{
    'targets': [{
        'target_name': 'mylib-addon',
        'cflags!': [ '-fno-exceptions' ],
        'cflags_cc!': [ '-fno-exceptions' ],

        'include_dirs': [
            '<!@(node -p \"require(\'node-addon-api\').include\")',
            '<(module_root_dir)/dependencies/mylib/include/'
        ],

        'sources': [
            'src/MyLibNode.cpp'
        ],

        'dependencies': [
            '<!(node -p \"require(\'node-addon-api\').gyp\")'
        ],

        'libraries': [
            '<(module_root_dir)/dependencies/mylib/lib/x64/libmylib.a'
        ],

        'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ]
    }]
}

The MyLibNode.cpp looks like this:

#include <napi.h>
#include "engine.h"

Napi::Object InitAll(Napi::Env env, Napi::Object exports)
{
    // Temporary check to make sure `libmylib.a` works.
    mylibengine::criticalZvalue(.3);

    return exports;
}

NODE_API_MODULE(mspaddon, InitAll)

I am sure that the linker can find libmylib.a, because when I change the name of the library (e.g., to disabled.a) I get a different error saying that the input file cannot be opened. However, it is unable to resolve the external symbol.

I found a similar, unanswered, question here. Answers to other questions indicate adding the full library path to libraries, but I don't think that's the problem for my scenario.

Why can't node-gyp resolve the external symbol?


Additional information:

I have the following project structure:

├───build
└───dependencies
    └───mylib
        ├───include
        └───lib
            └───x64
#pragma once

namespace mylibengine
{
    double criticalZvalue(double quantile);
}
#include <iostream>
#include <engine.h>


int main()
{
    std::cout << "Main working." << "\n";
    std::cout << mylibengine::criticalZvalue(.3) << "\n";
}

I can link the library in the executable as:

g++ -obuild/main main.cpp -Idependencies/mylib/include/ -Ldependencies/mylib/lib/x64/ -lmylib

At this point, running main.exe will output just fine:

Main working.
-0.524002

Solution

  • In the question above, the static library libmylib.a is compiled using the g++ from a mingw64 installation. However, node-gyp uses the Microsoft C and C++ (MSVC) runtime libraries.

    It seems (i.e., see this issue on GitHub) that node-gyp does not support mingw64. In the issue linked, someone indicates

    mingw uses its own name mangling scheme that makes interoperability impossible.

    which is also the issue encountered in this question (i.e., error LNK2001: unresolved external symbol). The solution is to create the static library using the Microsoft toolchain and then the symbols will be correctly resolved by the linker.