Skip to content

Instantly share code, notes, and snippets.

@gdianaty
Last active October 2, 2024 23:14
Show Gist options
  • Save gdianaty/33c3190a217cd188ea43cd4b06618ba9 to your computer and use it in GitHub Desktop.
Save gdianaty/33c3190a217cd188ea43cd4b06618ba9 to your computer and use it in GitHub Desktop.
CMake.js Inclusion Function for ordinary CMake

CMake.js Inclusion Function for ordinary CMake

I recently had an issue where my IDE (Visual Studio) had no support for CMake.js and this was my solution. This solution sets up all the CMake.js variables. By defining CMAKE_JS_INC and CMAKE_JS_LIB for use in your project. Just call setup_cmakejs() to define the variables. It is also compatible with Windows development environments. Linux and macOS are untested, but assumedly work just fine.

Happy coding!

Here's an example just in case you need it:

cmake_minimum_required(VERSION 3.2.2)

include("cmakejs.cmake")
setup_cmakejs()

project (my_project_name)
include_directories(${CMAKE_JS_INC})
file(GLOB SOURCE_FILES "main.cpp")
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})

set_target_properties(${PROJECT_NAME} PROPERTIES 
		PREFIX 				""
		SUFFIX 				".node"
        	RUNTIME_OUTPUT_DIRECTORY 	${ARTIFACTS_DIRECTORY}
        	LINKER_LANGUAGE 		CXX
		)

target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})
# Authored by Graham Dianaty for Bitlogix Technologies. Based on code from Rene Hollander. ==========//
function(setup_cmakejs)
find_program(CMAKEJS "cmake-js")
find_program(NPM "npm")
# first, check if we have NPM:
if(NPM)
message(VERBOSE "NPM found.")
else()
message(FATAL_ERROR "NPM not found. This project requires Node.js")
endif()
if(CMAKEJS)
message(VERBOSE "CMake.js found.")
else()
message(ERROR "CMake.js not found, installing globally...")
exec_program(${NPM_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR} ARGS install -g cmake-js OUTPUT_VARIABLE NPM_OUTPUT)
message(STATUS "CMake.js should now be installed.")
message(VERBOSE ${NPM_OUTPUT})
endif()
if(WIN32)
set(NPM_COMMAND ${NPM}.cmd)
set(CMAKEJS_CMD ${CMAKEJS}.cmd)
else()
set(NPM_COMMAND ${NPM})
set(CMAKEJS_CMD ${CMAKEJS})
endif()
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("${CMAKEJS_CMD}" ${CMAKE_CURRENT_SOURCE_DIR}
ARGS print-configure --debug
OUTPUT_VARIABLE CMAKE_JS_OUTPUT
)
else()
exec_program("${CMAKEJS_CMD}" ${CMAKE_CURRENT_SOURCE_DIR}
ARGS print-configure
OUTPUT_VARIABLE CMAKE_JS_OUTPUT
)
endif ()
message(VERBOSE ${CMAKE_JS_OUTPUT})
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_JS_LIB" CMAKE_JS_LIB)
set(CMAKE_JS_LIB "${CMAKE_JS_LIB}" 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)
message(STATUS "[CMakeJS] Set up variables.")
endfunction(setup_cmakejs)
@gdianaty
Copy link
Author

How do you mean? Are you suggesting adding compatibility for checking the Node.js version or making sure that Node exists via NVM?

@gonejack
Copy link

find_program may fail on looking up npm since npm is just shell function by nvm.
image

@gdianaty
Copy link
Author

gdianaty commented Apr 27, 2020

Huh, odd. On windows, when installing LTS Node.js on Windows, NPM is actually npm.cmd on the PATH variable. If you take a look at lines 22 thru 28, you can see me appending that extension. Would something similar suffice for your problem?

@gonejack
Copy link

I'd try writing something like npm.sh to work it out.

@gdianaty
Copy link
Author

gdianaty commented Apr 28, 2020 via email

@vjpr
Copy link

vjpr commented Dec 23, 2020

What cmake-js needs is a --porcelain option like the git cli has so we don't have to do this string parsing stuff.


Actually, an easier way would be to use Node.js to extract the variables we need from print-configure...

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

@GitUT
Copy link

GitUT commented Jan 17, 2021

Thank you so much! I'm relatively new to node.js and trying to setup a C++ project with Node.js, Vue.js + Electron front end. I got the front end architecture running in a day or two but I've been struggling with C++ integration for a week! Especially troublesome was Clion integration with Cmake-js (4 days wasted on this).

Your code finally saved me! Thank you!

@gdianaty
Copy link
Author

gdianaty commented Jan 17, 2021 via email

@innocenat
Copy link

I tried this code and fine that my cmake-js on Windows don't quote the parameter, so the string search failed. Here's my GET_VARIABLE that make it works:

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(REPLACE "\\\\" "\\" INPUT_STRING "${INPUT_STRING}")

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

    if(SEARCH_STRING_INDEX EQUAL -1)
        string(FIND "${INPUT_STRING}" "${VARIABLE_TO_SELECT}=" SEARCH_STRING_INDEX)

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

        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)

    else()

        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)

    endif()
endfunction(GET_VARIABLE)

@gdianaty
Copy link
Author

Full disclosure: all of my knowledge about CMake-js has fallen out of my brain, and these scripts are now Martian to me. Not entirely sure how to help.

@ParticleG
Copy link

Hi, How about add a switch for pnpm, yarn and other package manager support?

@gdianaty
Copy link
Author

If you define the CMake variable for NPM_COMMAND with the desired package manager's executable elsewhere in your project (i.e. prior to function being called) you should be able to get this functionality. As far as I know, the syntax used above should work with yarn and will
almost definitely work with pnpm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment