From 20aefdc780da192997957f41866303496480c164 Mon Sep 17 00:00:00 2001 From: Andrew Calcutt Date: Sat, 18 Feb 2023 14:38:06 -0500 Subject: [PATCH] Move module.cmake out of mapbox/cmake-node-module (#821) --- package-lock.json | 14 --- package.json | 1 - platform/node/CMakeLists.txt | 8 +- platform/node/cmake/module.cmake | 199 +++++++++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 21 deletions(-) create mode 100644 platform/node/cmake/module.cmake diff --git a/package-lock.json b/package-lock.json index 1c34952f6..12e4995a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "dependencies": { "@acalcutt/node-pre-gyp": "^1.0.11", "@acalcutt/node-pre-gyp-github": "1.4.8", - "@mapbox/cmake-node-module": "^1.2.0", "minimatch": "^6.1.8", "npm-run-all": "^4.0.2" }, @@ -148,14 +147,6 @@ "node": ">=6.0.0" } }, - "node_modules/@mapbox/cmake-node-module": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@mapbox/cmake-node-module/-/cmake-node-module-1.2.0.tgz", - "integrity": "sha512-kmpkNQH7hR4d5bD/T+8KITE3xzYF2PgVB3VrSGEC3JxNtH2KdfqXbBA9xu+L2ODkEDQkg9GnD/SSnkeKkEBsYg==", - "bin": { - "cmake-node-module": "install.sh" - } - }, "node_modules/@mapbox/flow-remove-types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@mapbox/flow-remove-types/-/flow-remove-types-2.0.0.tgz", @@ -4439,11 +4430,6 @@ "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==", "dev": true }, - "@mapbox/cmake-node-module": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@mapbox/cmake-node-module/-/cmake-node-module-1.2.0.tgz", - "integrity": "sha512-kmpkNQH7hR4d5bD/T+8KITE3xzYF2PgVB3VrSGEC3JxNtH2KdfqXbBA9xu+L2ODkEDQkg9GnD/SSnkeKkEBsYg==" - }, "@mapbox/flow-remove-types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@mapbox/flow-remove-types/-/flow-remove-types-2.0.0.tgz", diff --git a/package.json b/package.json index 20d976f21..2f707c5bd 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "dependencies": { "@acalcutt/node-pre-gyp": "^1.0.11", "@acalcutt/node-pre-gyp-github": "1.4.8", - "@mapbox/cmake-node-module": "^1.2.0", "minimatch": "^6.1.8", "npm-run-all": "^4.0.2" }, diff --git a/platform/node/CMakeLists.txt b/platform/node/CMakeLists.txt index e378cbdaa..f2e5e8ee1 100644 --- a/platform/node/CMakeLists.txt +++ b/platform/node/CMakeLists.txt @@ -1,10 +1,6 @@ -if(NOT EXISTS ${PROJECT_SOURCE_DIR}/node_modules/@mapbox/cmake-node-module/module.cmake) - message(WARNING "Not building node bindings, dependencies not found. Run 'npm update'.") - return() -endif() - +set(NODE_MODULE_MINIMUM_ABI 83) # Minimum supported Node.js version: 14 set(NODE_MODULE_CACHE_DIR ${CMAKE_SOURCE_DIR}/build/headers) -include(${PROJECT_SOURCE_DIR}/node_modules/@mapbox/cmake-node-module/module.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cmake/module.cmake) add_node_module( mbgl-node diff --git a/platform/node/cmake/module.cmake b/platform/node/cmake/module.cmake new file mode 100644 index 000000000..90e8ce455 --- /dev/null +++ b/platform/node/cmake/module.cmake @@ -0,0 +1,199 @@ +# We need CMake 3.9 because the Xcode generator doesn't know about systems include paths before 3.9. +# See https://gitlab.kitware.com/cmake/cmake/issues/16795 +cmake_minimum_required(VERSION 3.9) + +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 "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() + + + # 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) + # Nan requires C++11. Use a compile option to allow interfaces to override this with a later version. + target_compile_options(${_TARGET} PRIVATE -std=c++11) + target_include_directories(${_TARGET} SYSTEM PRIVATE + "${_CACHE_DIR}/nan/${_NAN_VERSION}" + ) + endif() + + target_link_libraries(${_TARGET} PRIVATE ${NAME}) + + 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" + ) + 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}" + ) + 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()