# We need CMake 3.10 because the Xcode generator doesn't know about systems include paths before 3.9, and the minimum required version has been increased to 3.10. # See https://gitlab.kitware.com/cmake/cmake/issues/16795 cmake_minimum_required(VERSION 3.10) if (NOT NODE_MODULE_MINIMUM_ABI) set(NODE_MODULE_MINIMUM_ABI 46) # Don't build node modules for versions earlier than Node 4 endif() if (NOT NODE_MODULE_CACHE_DIR) set(NODE_MODULE_CACHE_DIR "${CMAKE_BINARY_DIR}") endif() function(_node_module_download _TYPE _URL _FILE) file(REMOVE_RECURSE "${_FILE}") string(RANDOM LENGTH 32 _TMP) set(_TMP "${CMAKE_BINARY_DIR}/${_TMP}") message(STATUS "[Node.js] Downloading ${_TYPE}...") file(DOWNLOAD "${_URL}" "${_TMP}" STATUS _STATUS TLS_VERIFY ON) list(GET _STATUS 0 _STATUS_CODE) if(NOT _STATUS_CODE EQUAL 0) file(REMOVE "${_TMP}") list(GET _STATUS 1 _STATUS_MESSAGE) message(FATAL_ERROR "[Node.js] Failed to download ${_TYPE}: ${_STATUS_MESSAGE}") else() get_filename_component(_DIR "${_FILE}" DIRECTORY) file(MAKE_DIRECTORY "${_DIR}") file(RENAME "${_TMP}" "${_FILE}") endif() endfunction() function(_node_module_unpack_tar_gz _TYPE _URL _PATH _DEST) string(RANDOM LENGTH 32 _TMP) set(_TMP "${CMAKE_BINARY_DIR}/${_TMP}") _node_module_download("${_TYPE}" "${_URL}" "${_TMP}.tar.gz") file(REMOVE_RECURSE "${_DEST}" "${_TMP}") file(MAKE_DIRECTORY "${_TMP}") execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz "${_TMP}.tar.gz" WORKING_DIRECTORY "${_TMP}" RESULT_VARIABLE _STATUS_CODE OUTPUT_VARIABLE _STATUS_MESSAGE ERROR_VARIABLE _STATUS_MESSAGE) if(NOT _STATUS_CODE EQUAL 0) message(FATAL_ERROR "[Node.js] Failed to unpack ${_TYPE}: ${_STATUS_MESSAGE}") endif() get_filename_component(_DIR "${_DEST}" DIRECTORY) file(MAKE_DIRECTORY "${_DIR}") file(RENAME "${_TMP}/${_PATH}" "${_DEST}") file(REMOVE_RECURSE "${_TMP}" "${_TMP}.tar.gz") endfunction() function(add_node_module NAME) cmake_parse_arguments("" "" "MINIMUM_NODE_ABI;NAN_VERSION;INSTALL_PATH;CACHE_DIR" "EXCLUDE_NODE_ABIS" ${ARGN}) if(NOT _MINIMUM_NODE_ABI) set(_MINIMUM_NODE_ABI "${NODE_MODULE_MINIMUM_ABI}") endif() if (NOT _CACHE_DIR) set(_CACHE_DIR "${NODE_MODULE_CACHE_DIR}") endif() if (NOT _INSTALL_PATH) set(_INSTALL_PATH "platform/node/lib/{node_abi}/${NAME}.node") endif() get_filename_component(_CACHE_DIR "${_CACHE_DIR}" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") if(_UNPARSED_ARGUMENTS) message(WARNING "[Node.js] Unused arguments: '${_UNPARSED_ARGUMENTS}'") endif() # Create master target add_library(${NAME} INTERFACE) # Obtain a list of current Node versions and retrieves the latest version per ABI if(NOT EXISTS "${_CACHE_DIR}/node/index.tab") _node_module_download( "Node.js version list" "https://nodejs.org/dist/index.tab" "${_CACHE_DIR}/node/index.tab" ) endif() file(STRINGS "${_CACHE_DIR}/node/index.tab" _INDEX_FILE) list(REMOVE_AT _INDEX_FILE 0) set(_ABIS) foreach(_LINE IN LISTS _INDEX_FILE) string(REGEX MATCHALL "[^\t]*\t" _COLUMNS "${_LINE}") list(GET _COLUMNS 8 _ABI) string(STRIP "${_ABI}" _ABI) if((_ABI GREATER _MINIMUM_NODE_ABI OR _ABI EQUAL _MINIMUM_NODE_ABI) AND NOT _ABI IN_LIST _EXCLUDE_NODE_ABIS AND NOT DEFINED _NODE_ABI_${_ABI}_VERSION) list(APPEND _ABIS ${_ABI}) list(GET _COLUMNS 0 _VERSION) string(STRIP "${_VERSION}" _NODE_ABI_${_ABI}_VERSION) endif() endforeach() # Install Nan if(_NAN_VERSION AND NOT EXISTS "${_CACHE_DIR}/nan/${_NAN_VERSION}/nan.h") _node_module_unpack_tar_gz( "Nan ${_NAN_VERSION}" "https://registry.npmjs.org/nan/-/nan-${_NAN_VERSION}.tgz" "package" "${_CACHE_DIR}/nan/${_NAN_VERSION}" ) endif() # Generate a target for every ABI set(_TARGETS) foreach(_ABI IN LISTS _ABIS) set(_NODE_VERSION ${_NODE_ABI_${_ABI}_VERSION}) # Download the headers if we don't have them yet if(NOT EXISTS "${_CACHE_DIR}/node/${_NODE_VERSION}/node.h") _node_module_unpack_tar_gz( "headers for Node ${_NODE_VERSION}" "https://nodejs.org/download/release/${_NODE_VERSION}/node-${_NODE_VERSION}-headers.tar.gz" "node-${_NODE_VERSION}/include/node" "${_CACHE_DIR}/node/${_NODE_VERSION}" ) endif() if(WIN32) # Determine Windows architecture if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64" OR CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") set(_ARCH arm64) elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_ARCH x64) else() set(_ARCH x86) endif() # Download the win-${_ARCH} libraries if we are compiling on Windows and don't have them yet if(NOT EXISTS "${_CACHE_DIR}/lib/node/${_NODE_VERSION}/win-${_ARCH}/node.lib") _node_module_download( "win-${_ARCH} library for Node ${_NODE_VERSION}" "https://nodejs.org/download/release/${_NODE_VERSION}/win-${_ARCH}/node.lib" "${_CACHE_DIR}/lib/node/${_NODE_VERSION}/win-${_ARCH}/node.lib" ) endif() endif() # Generate the library set(_TARGET "${NAME}.abi-${_ABI}") add_library(${_TARGET} SHARED "${_CACHE_DIR}/empty.cpp") list(APPEND _TARGETS "${_TARGET}") # C identifiers can only contain certain characters (e.g. no dashes) string(REGEX REPLACE "[^a-z0-9]+" "_" NAME_IDENTIFIER "${NAME}") set_target_properties(${_TARGET} PROPERTIES OUTPUT_NAME "${_TARGET}" SOURCES "" # Removes the fake empty.cpp again PREFIX "" SUFFIX ".node" MACOSX_RPATH ON C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden POSITION_INDEPENDENT_CODE TRUE ) target_compile_definitions(${_TARGET} PRIVATE "MODULE_NAME=${NAME_IDENTIFIER}" "BUILDING_NODE_EXTENSION" "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64" ) target_include_directories(${_TARGET} SYSTEM PRIVATE "${_CACHE_DIR}/node/${_NODE_VERSION}" ) if(_NAN_VERSION) target_include_directories(${_TARGET} SYSTEM PRIVATE "${_CACHE_DIR}/nan/${_NAN_VERSION}" ) endif() target_link_libraries(${_TARGET} PRIVATE ${NAME} mbgl-compiler-options) if(WIN32) target_include_directories(${_TARGET} SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/platform/windows/include") target_compile_definitions(${_TARGET} PRIVATE NOMINMAX) target_link_libraries(${_TARGET} PRIVATE "${_CACHE_DIR}/lib/node/${_NODE_VERSION}/win-${_ARCH}/node.lib") endif() if(APPLE) # Ensures that linked symbols are loaded when the module is loaded instead of causing # unresolved symbol errors later during runtime. set_target_properties(${_TARGET} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup -bind_at_load" ) target_compile_definitions(${_TARGET} PRIVATE "_DARWIN_USE_64_BIT_INODE=1" ) elseif(WIN32) # Do nothing here else() # Ensures that linked symbols are loaded when the module is loaded instead of causing # unresolved symbol errors later during runtime. set_target_properties(${_TARGET} PROPERTIES LINK_FLAGS "-z now" ) endif() # Copy the file to the installation directory. string(REPLACE "{node_abi}" "node-v${_ABI}" _OUTPUT_PATH "${_INSTALL_PATH}") get_filename_component(_OUTPUT_PATH "${_OUTPUT_PATH}" ABSOLUTE "${CMAKE_CURRENT_SOURCE_PATH}") add_custom_command( TARGET ${_TARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "$" "${_OUTPUT_PATH}" ) if(WIN32) unset(_ARCH) endif() endforeach() # Add a target that builds all Node ABIs. add_custom_target("${NAME}.all") add_dependencies("${NAME}.all" ${_TARGETS}) # Add a variable that allows users to iterate over all of the generated/dependendent targets. set("${NAME}::abis" "${_ABIS}" PARENT_SCOPE) set("${NAME}::targets" "${_TARGETS}" PARENT_SCOPE) endfunction()