1

I successfully created a native node addon with napi and cmakejs. But when adding a simple library the unix make file, generated by cmake-js explodes with

[ 50%] Linking CXX static library liblib_name.a [ 50%] Built target lib_name CMakeFiles/spielwiese.dir/flags.make:10: * missing separator. Stop. make[1]: * [CMakeFiles/Makefile2:72: CMakeFiles/spielwiese.dir/all] Error 2 make: *** [Makefile:130: all] Error 2 ERR! OMG Process terminated: 2

Minimized example project: https://github.com/Superlokkus/spielwiese/tree/napi

The root CMakeLists should be close or intented to be close to cmakejs example boilerplate version, just with an additional function PARSE_CMAKEJS_PROPERTIES to also build it via a cmake CLI command, for nice developing with IDEs like CLion. However the problem persisits when removing the PARSE_CMAKEJS_PROPERTIES function.

I added the library with a add_subdirectory, if you remove https://github.com/Superlokkus/spielwiese/blob/napi/CMakeLists.txt#L47 aka add_subdirectory(src/lib_name) and change https://github.com/Superlokkus/spielwiese/blob/napi/CMakeLists.txt#L63 aka target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB} lib_name) to target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB}) and also remove https://github.com/Superlokkus/spielwiese/blob/napi/src/spielwiese.cpp#L3 aka #include <lib_name/lib_name.hpp>,

the project builds again incl. the mocha test. However add the example library and you get the error again.

Root CMakeLists:

cmake_minimum_required(VERSION 3.2)

# The following function is just for nice CLion IDE support with cmake-js
function(PARSE_CMAKEJS_PROPERTIES)
    function(GET_VARIABLE INPUT_STRING VARIABLE_TO_SELECT OUTPUT_VARIABLE)
        set(SEARCH_STRING "${VARIABLE_TO_SELECT}=\"")
        string(LENGTH "${SEARCH_STRING}" SEARCH_STRING_LENGTH)
        string(LENGTH "${INPUT_STRING}" INPUT_STRING_LENGTH)

        string(FIND "${INPUT_STRING}" "${VARIABLE_TO_SELECT}=\"" SEARCH_STRING_INDEX)

        math(EXPR SEARCH_STRING_INDEX "${SEARCH_STRING_INDEX}+${SEARCH_STRING_LENGTH}")

        string(SUBSTRING "${INPUT_STRING}" ${SEARCH_STRING_INDEX} ${INPUT_STRING_LENGTH} AFTER_SEARCH_STRING)
        string(FIND "${AFTER_SEARCH_STRING}" "\"" QUOTE_INDEX)
        string(SUBSTRING "${AFTER_SEARCH_STRING}" "0" "${QUOTE_INDEX}" RESULT_STRING)
        set("${OUTPUT_VARIABLE}" "${RESULT_STRING}" PARENT_SCOPE)
    endfunction(GET_VARIABLE)

    string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
    if (CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
        exec_program(./node_modules/.bin/cmake-js ${CMAKE_CURRENT_SOURCE_DIR}
                ARGS print-configure --debug
                OUTPUT_VARIABLE CMAKE_JS_OUTPUT
                )
    else ()
        exec_program(./node_modules/.bin/cmake-js ${CMAKE_CURRENT_SOURCE_DIR}
                ARGS print-configure
                OUTPUT_VARIABLE CMAKE_JS_OUTPUT
                )
    endif ()

    get_variable("${CMAKE_JS_OUTPUT}" "CMAKE_JS_INC" CMAKE_JS_INC)
    set(CMAKE_JS_INC "${CMAKE_JS_INC}" PARENT_SCOPE)

    get_variable("${CMAKE_JS_OUTPUT}" "CMAKE_LIBRARY_OUTPUT_DIRECTORY" CMAKE_LIBRARY_OUTPUT_DIRECTORY)
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" PARENT_SCOPE)

endfunction(PARSE_CMAKEJS_PROPERTIES)

# Name of the project (will be the name of the plugin)
project(spielwiese VERSION 1.0)
if (NOT CMAKE_JS_INC)
    parse_cmakejs_properties()
endif ()

add_subdirectory(src/lib_name)

# Essential include files to build a node addon,
# you should add this line in every CMake.js based project.
include_directories(${CMAKE_JS_INC})

# Declare the location of the source files
file(GLOB SOURCE_FILES "src/*.cpp")

add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})

# This line will give our library file a .node extension without any "lib" prefix
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")

# Essential library files to link to a node addon,
# you should add this line in every CMake.js based project.
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB} lib_name)

# Include N-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE NODE_ADDON_API_DIR
        )
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})

Libnames CMakeLists

cmake_minimum_required(VERSION 3.0)
project(lib_name VERSION 1.0)

string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER_CASE)
configure_file(
        ${PROJECT_NAME}_version.hpp.in
        ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/${PROJECT_NAME}_version.hpp
)

set(${PROJECT_NAME}_implementation_files
        src/lib.cpp
        )


add_library(${PROJECT_NAME} ${${PROJECT_NAME}_implementation_files})
set_property(TARGET ${PROJECT_NAME} PROPERTY LINKER_LANGUAGE CXX)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)

target_include_directories(${PROJECT_NAME}
        PUBLIC
        $<INSTALL_INTERFACE:include>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
        )

install(TARGETS ${PROJECT_NAME}
        ARCHIVE
        DESTINATION lib)
install(TARGETS ${PROJECT_NAME}
        PUBLIC_HEADER
        DESTINATION include)

Update I did some experimentation and it seems

target_include_directories(${PROJECT_NAME}
        PUBLIC
        $<INSTALL_INTERFACE:include>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
        )

is doing the trouble, so it looks like cmakejs has some problem with that command?!

Superlokkus
  • 4,731
  • 1
  • 25
  • 57
  • Using CMake 3.13 it fails to generate a Makefile because of errors in the install target syntax. Once this is fixed it works fine. You should look in the Makefile that is causing the error and see it looks correct. – fdk1342 May 24 '19 at 12:07
  • @Fred I was able to boil it down to the snippet found in my update ( target_include_directories ) which looks pretty correct to me, since it works on my plain cmake projects and is right out of the cmake done right blog post. But maybe I am missing something? – Superlokkus May 24 '19 at 12:08
  • 1
    That command effects the contents of the flags.make file. – fdk1342 May 24 '19 at 12:17
  • Which might be an indication that cmake-js is having trouble getting its own flags right? – Superlokkus May 24 '19 at 12:18
  • (Since it works perfectly well when using exactly in the same way on the master branch with vanilla cmake) – Superlokkus May 24 '19 at 12:24
  • Yeah for some reason the generated CXX_INCLUDES gets a new line, which is accodring to my bad unix make file skills wrong: CXX_INCLUDES = -I/home/markus/.cmake-js/node-x64/v11.15.0/include/node -I/home/markus/CLionProjects/spielwiese/node_modules/node-addon-api -I/home/markus/CLionProjects/spielwiese/src/lib_name/include – Superlokkus May 24 '19 at 12:27
  • A new line without a `\\` would be incorrect and cause that error message. – fdk1342 May 24 '19 at 12:30
  • Thanks @Fred I wasn't aware of the 3rd make file, because unix make files output did not grouped it to the make file stack. Thanks to you I found the broken make file output and by that the thing the cmake-js project did wrong. – Superlokkus May 24 '19 at 13:16

1 Answers1

0

Turns out the snippet taken from the official cmake-js documentation

execute_process(COMMAND node -p "require('node-addon-api').include"
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    OUTPUT_VARIABLE NODE_ADDON_API_DIR
    )

is invoking node to get the path to the node headers. But the output of node might contain a newline, which will ruin the day of all includes after that. So the correct part of the root CMakeLists.txt would be

# Include N-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE NODE_ADDON_API_DIR
        )
string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})

So a addition of string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) to get rid of the newline fixed the problem.

Thanks @fred for the help to find the problem

Already created a pull request to fix the cmake-js documentation: https://github.com/cmake-js/cmake-js/issues/175

Superlokkus
  • 4,731
  • 1
  • 25
  • 57