From 5b24b1474187807b46de2e4c4fb587bcdee4d1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 21 Feb 2017 18:32:53 +0100 Subject: [PATCH] [build] use node-cmake 2.x - Removes need to run `npm install` before invoking cmake --- Makefile | 5 - cmake/NodeJS.cmake | 600 +++++++++++++++++++++++++++++++++++++++++++++ cmake/mbgl.cmake | 17 +- cmake/node.cmake | 8 + package.json | 1 - 5 files changed, 612 insertions(+), 19 deletions(-) create mode 100644 cmake/NodeJS.cmake diff --git a/Makefile b/Makefile index c0c495335ff..0da2dc159eb 100644 --- a/Makefile +++ b/Makefile @@ -47,12 +47,7 @@ endif .PHONY: default default: test -.NOTPARALLEL: node_modules -node_modules: package.json - npm install --ignore-scripts # Install dependencies but don't run our own install script. - BUILD_DEPS += Makefile -BUILD_DEPS += node_modules BUILD_DEPS += CMakeLists.txt #### macOS targets ############################################################## diff --git a/cmake/NodeJS.cmake b/cmake/NodeJS.cmake new file mode 100644 index 00000000000..8e0ec569823 --- /dev/null +++ b/cmake/NodeJS.cmake @@ -0,0 +1,600 @@ +# NOTE: We're using a patched version of the original https://github.com/cjntaylor/node-cmake + +# Our version is in https://github.com/mapbox/node-cmake/blob/mapbox-gl-native/NodeJS.cmake and +# contains these patches: +# - https://github.com/cjntaylor/node-cmake/pull/20 +# - https://github.com/cjntaylor/node-cmake/pull/22 +# - https://github.com/cjntaylor/node-cmake/pull/23 + +# Defaults for standard Node.js builds +set(NODEJS_DEFAULT_URL https://nodejs.org/download/release) +set(NODEJS_DEFAULT_VERSION installed) +set(NODEJS_VERSION_FALLBACK latest) +set(NODEJS_DEFAULT_NAME node) +set(NODEJS_DEFAULT_CHECKSUM SHASUMS256.txt) +set(NODEJS_DEFAULT_CHECKTYPE SHA256) + +include(CMakeParseArguments) + +# Find a path by walking upward from a base directory until the path is +# found. Sets the variable ${PATH} to False if the path can't +# be determined +function(find_path_parent NAME BASE PATH) + set(ROOT ${BASE}) + set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE) + set(DRIVE "^[A-Za-z]?:?/$") + while(NOT ROOT MATCHES ${DRIVE} AND NOT EXISTS ${ROOT}/${NAME}) + get_filename_component(ROOT ${ROOT} DIRECTORY) + set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE) + endwhile() + if(ROOT MATCHES ${DRIVE}) + set(${PATH} False PARENT_SCOPE) + endif() +endfunction() + +# Shortcut for finding standard node module locations +macro(find_nodejs_module NAME BASE PATH) + find_path_parent(node_modules/${NAME} ${BASE} ${PATH}) +endmacro() + +# Download with a bit of nice output (without spewing progress) +function(download_file DESCRIPTION URL FILE) + message(STATUS "Downloading: ${URL}") + file(DOWNLOAD + ${URL} + ${FILE}.tmp + ${ARGN} + STATUS RESULT + ) + list(GET RESULT 0 STATUS) + if(STATUS) + list(GET result 1 MESSAGE) + message(FATAL_ERROR "Unable to download ${DESCRIPTION} from ${URL}: ${MESSAGE}") + else() + file(RENAME ${FILE}.tmp ${FILE}) + endif() +endfunction() + +# Embedded win_delay_load_hook file so that this file can be copied +# into projects directly (recommended practice) +function(nodejs_generate_delayload_hook OUTPUT) + file(WRITE ${OUTPUT} "") + file(APPEND ${OUTPUT} "/*\n") + file(APPEND ${OUTPUT} " * When this file is linked to a DLL, it sets up a delay-load hook that\n") + file(APPEND ${OUTPUT} " * intervenes when the DLL is trying to load 'node.exe' or 'iojs.exe'\n") + file(APPEND ${OUTPUT} " * dynamically. Instead of trying to locate the .exe file it'll just return\n") + file(APPEND ${OUTPUT} " * a handle to the process image.\n") + file(APPEND ${OUTPUT} " *\n") + file(APPEND ${OUTPUT} " * This allows compiled addons to work when node.exe or iojs.exe is renamed.\n") + file(APPEND ${OUTPUT} " */\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} "#ifdef _MSC_VER\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} "#ifndef DELAYIMP_INSECURE_WRITABLE_HOOKS\n") + file(APPEND ${OUTPUT} "#define DELAYIMP_INSECURE_WRITABLE_HOOKS\n") + file(APPEND ${OUTPUT} "#endif\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} "#ifndef WIN32_LEAN_AND_MEAN\n") + file(APPEND ${OUTPUT} "#define WIN32_LEAN_AND_MEAN\n") + file(APPEND ${OUTPUT} "#endif\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} "#include \n") + file(APPEND ${OUTPUT} "#include \n") + file(APPEND ${OUTPUT} "#include \n") + file(APPEND ${OUTPUT} "#include \n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} "static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo* info) {\n") + file(APPEND ${OUTPUT} " if (event != dliNotePreLoadLibrary) return NULL;\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " if (_stricmp(info->szDll, \"iojs.exe\") != 0 &&\n") + file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.exe\") != 0 &&\n") + file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.dll\") != 0)\n") + file(APPEND ${OUTPUT} " return NULL;\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " // Get a handle to the current process executable.\n") + file(APPEND ${OUTPUT} " HMODULE processModule = GetModuleHandle(NULL);\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " // Get the path to the executable.\n") + file(APPEND ${OUTPUT} " TCHAR processPath[_MAX_PATH];\n") + file(APPEND ${OUTPUT} " GetModuleFileName(processModule, processPath, _MAX_PATH);\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " // Get the name of the current executable.\n") + file(APPEND ${OUTPUT} " LPSTR processName = PathFindFileName(processPath);\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " // If the current process is node or iojs, then just return the proccess \n") + file(APPEND ${OUTPUT} " // module.\n") + file(APPEND ${OUTPUT} " if (_stricmp(processName, \"node.exe\") == 0 ||\n") + file(APPEND ${OUTPUT} " _stricmp(processName, \"iojs.exe\") == 0) {\n") + file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n") + file(APPEND ${OUTPUT} " }\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " // If it is another process, attempt to load 'node.dll' from the same \n") + file(APPEND ${OUTPUT} " // directory.\n") + file(APPEND ${OUTPUT} " PathRemoveFileSpec(processPath);\n") + file(APPEND ${OUTPUT} " PathAppend(processPath, \"node.dll\");\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " HMODULE nodeDllModule = GetModuleHandle(processPath);\n") + file(APPEND ${OUTPUT} " if(nodeDllModule != NULL) {\n") + file(APPEND ${OUTPUT} " // This application has a node.dll in the same directory as the executable,\n") + file(APPEND ${OUTPUT} " // use that.\n") + file(APPEND ${OUTPUT} " return (FARPROC) nodeDllModule;\n") + file(APPEND ${OUTPUT} " }\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} " // Fallback to the current executable, which must statically link to \n") + file(APPEND ${OUTPUT} " // node.lib\n") + file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n") + file(APPEND ${OUTPUT} "}\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} "PfnDliHook __pfnDliNotifyHook2 = load_exe_hook;\n") + file(APPEND ${OUTPUT} "\n") + file(APPEND ${OUTPUT} "#endif\n") +endfunction() + +# Sets up a project to build Node.js native modules +# - Downloads required dependencies and unpacks them to the build directory. +# Internet access is required the first invocation but not after ( +# provided the download is successful) +# - Sets up several variables for building against the downloaded +# dependencies +# - Guarded to prevent multiple executions, so a single project hierarchy +# will only call this once +function(nodejs_init) + # Prevents this function from executing more than once + if(NODEJS_INIT) + return() + endif() + + # Regex patterns used by the init function for component extraction + set(HEADERS_MATCH "^([A-Fa-f0-9]+)[ \t]+([^-]+)-(headers|v?[0-9.]+)-(headers|v?[0-9.]+)([.]tar[.]gz)$") + set(LIB32_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-x86)?(/)?([^/]*)(.lib)$") + set(LIB64_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-)?(x64/)(.*)(.lib)$") + + # Parse function arguments + cmake_parse_arguments(nodejs_init + "" "URL;NAME;VERSION;CHECKSUM;CHECKTYPE" "" ${ARGN} + ) + + # Allow the download URL to be overridden by command line argument + # NODEJS_URL + if(NODEJS_URL) + set(URL ${NODEJS_URL}) + else() + # Use the argument if specified, falling back to the default + set(URL ${NODEJS_DEFAULT_URL}) + if(nodejs_init_URL) + set(URL ${nodejs_init_URL}) + endif() + endif() + + # Allow name to be overridden by command line argument NODEJS_NAME + if(NODEJS_NAME) + set(NAME ${NODEJS_NAME}) + else() + # Use the argument if specified, falling back to the default + set(NAME ${NODEJS_DEFAULT_NAME}) + if(nodejs_init_NAME) + set(NAME ${nodejs_init_NAME}) + endif() + endif() + + # Allow the checksum file to be overridden by command line argument + # NODEJS_CHECKSUM + if(NODEJS_CHECKSUM) + set(CHECKSUM ${NODEJS_CHECKSUM}) + else() + # Use the argument if specified, falling back to the default + set(CHECKSUM ${NODEJS_DEFAULT_CHECKSUM}) + if(nodejs_init_CHECKSUM) + set(CHECKSUM ${nodejs_init_CHECKSUM}) + endif() + endif() + + # Allow the checksum type to be overriden by the command line argument + # NODEJS_CHECKTYPE + if(NODEJS_CHECKTYPE) + set(CHECKTYPE ${NODEJS_CHECKTYPE}) + else() + # Use the argument if specified, falling back to the default + set(CHECKTYPE ${NODEJS_DEFAULT_CHECKTYPE}) + if(nodejs_init_CHECKTYPE) + set(CHECKTYPE ${nodejs_init_CHECKTYPE}) + endif() + endif() + + # Allow the version to be overridden by the command line argument + # NODEJS_VERSION + if(NODEJS_VERSION) + set(VERSION ${NODEJS_VERSION}) + else() + # Use the argument if specified, falling back to the default + set(VERSION ${NODEJS_DEFAULT_VERSION}) + if(nodejs_init_VERSION) + set(VERSION ${nodejs_init_VERSION}) + endif() + endif() + + # "installed" is a special version that tries to use the currently + # installed version (determined by running node) + set(NODEJS_INSTALLED False CACHE BOOL "Node.js install status" FORCE) + if(VERSION STREQUAL "installed") + if(NOT NAME STREQUAL ${NODEJS_DEFAULT_NAME}) + message(FATAL_ERROR + "'Installed' version identifier can only be used with" + "the core Node.js library" + ) + endif() + # Fall back to the "latest" version if node isn't installed + set(VERSION ${NODEJS_VERSION_FALLBACK}) + find_program(NODEJS_BINARY NAMES nodejs node) + if(NODEJS_BINARY) + execute_process( + COMMAND ${NODEJS_BINARY} --version + RESULT_VARIABLE INSTALLED_VERSION_RESULT + OUTPUT_VARIABLE INSTALLED_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(INSTALLED_VERSION_RESULT STREQUAL "0") + set(NODEJS_INSTALLED True CACHE BOOL + "Node.js install status" FORCE + ) + set(VERSION ${INSTALLED_VERSION}) + endif() + endif() + endif() + + # Create a temporary download directory + set(TEMP ${CMAKE_CURRENT_BINARY_DIR}/temp) + file(MAKE_DIRECTORY ${TEMP}) + + # Unless the target is special version "latest", the parameters + # necessary to construct the root path are known + if(NOT VERSION STREQUAL "latest") + set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION}) + # Extract checksums from the existing checksum file + set(CHECKSUM_TARGET ${ROOT}/CHECKSUM) + endif() + + # If we're trying to determine the version or we haven't saved the + # checksum file for this version, download it from the specified server + if(VERSION STREQUAL "latest" OR + (DEFINED ROOT AND NOT EXISTS ${ROOT}/CHECKSUM)) + if(DEFINED ROOT) + # Clear away the old checksum in case the new one is different + # and/or it fails to download + file(REMOVE ${ROOT}/CHECKSUM) + endif() + file(REMOVE ${TEMP}/CHECKSUM) + download_file( + "checksum file" + ${URL}/${VERSION}/${CHECKSUM} + ${TEMP}/CHECKSUM + INACTIVITY_TIMEOUT 10 + ) + # Extract checksums from the temporary file + set(CHECKSUM_TARGET ${TEMP}/CHECKSUM) + endif() + + # Extract the version, name, header archive and archive checksum + # from the file. This first extract is what defines / specifies the + # actual version number and name. + file(STRINGS + ${CHECKSUM_TARGET} HEADERS_CHECKSUM + REGEX ${HEADERS_MATCH} + LIMIT_COUNT 1 + ) + if(NOT HEADERS_CHECKSUM) + file(REMOVE ${TEMP}/CHECKSUM) + if(DEFINED ROOT) + file(REMOVE ${ROOT}/CHECKSUM) + endif() + message(FATAL_ERROR "Unable to extract header archive checksum") + endif() + string(REGEX MATCH ${HEADERS_MATCH} HEADERS_CHECKSUM ${HEADERS_CHECKSUM}) + set(HEADERS_CHECKSUM ${CMAKE_MATCH_1}) + set(NAME ${CMAKE_MATCH_2}) + if(CMAKE_MATCH_3 STREQUAL "headers") + set(VERSION ${CMAKE_MATCH_4}) + else() + set(VERSION ${CMAKE_MATCH_3}) + endif() + set(HEADERS_ARCHIVE + ${CMAKE_MATCH_2}-${CMAKE_MATCH_3}-${CMAKE_MATCH_4}${CMAKE_MATCH_5} + ) + # Make sure that the root directory exists, and that the checksum + # file has been moved over from temp + if(DEFINED ROOT) + set(OLD_ROOT ${ROOT}) + endif() + set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION}) + if(DEFINED OLD_ROOT AND NOT ROOT STREQUAL "${OLD_ROOT}") + file(REMOVE ${TEMP}/CHECKSUM) + file(REMOVE ${ROOT}/CHECKSUM) + message(FATAL_ERROR "Version/Name mismatch") + endif() + file(MAKE_DIRECTORY ${ROOT}) + if(EXISTS ${TEMP}/CHECKSUM) + file(REMOVE ${ROOT}/CHECKSUM) + file(RENAME ${TEMP}/CHECKSUM ${ROOT}/CHECKSUM) + endif() + + # Now that its fully resolved, report the name and version of Node.js being + # used + message(STATUS "NodeJS: Using ${NAME}, version ${VERSION}") + + # Download the headers for the version being used + # Theoretically, these could be found by searching the installed + # system, but in practice, this can be error prone. They're provided + # on the download servers, so just use the ones there. + if(NOT EXISTS ${ROOT}/include) + file(REMOVE ${TEMP}/${HEADERS_ARCHIVE}) + download_file( + "Node.js headers" + ${URL}/${VERSION}/${HEADERS_ARCHIVE} + ${TEMP}/${HEADERS_ARCHIVE} + INACTIVITY_TIMEOUT 10 + EXPECTED_HASH ${CHECKTYPE}=${HEADERS_CHECKSUM} + ) + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xfz ${TEMP}/${HEADERS_ARCHIVE} + WORKING_DIRECTORY ${TEMP} + ) + + # This adapts the header extraction to support a number of different + # header archive contents in addition to the one used by the + # default Node.js library + unset(NODEJS_HEADERS_PATH CACHE) + find_path(NODEJS_HEADERS_PATH + NAMES src include + PATHS + ${TEMP}/${NAME}-${VERSION}-headers + ${TEMP}/${NAME}-${VERSION} + ${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION}-headers + ${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION} + ${TEMP}/${NODEJS_DEFAULT_NAME} + ${TEMP} + NO_DEFAULT_PATH + ) + if(NOT NODEJS_HEADERS_PATH) + message(FATAL_ERROR "Unable to find extracted headers folder") + endif() + + # Move the headers into a standard location with a standard layout + file(REMOVE ${TEMP}/${HEADERS_ARCHIVE}) + file(REMOVE_RECURSE ${ROOT}/include) + if(EXISTS ${NODEJS_HEADERS_PATH}/include/node) + file(RENAME ${NODEJS_HEADERS_PATH}/include/node ${ROOT}/include) + elseif(EXISTS ${NODEJS_HEADERS_PATH}/src) + file(MAKE_DIRECTORY ${ROOT}/include) + if(NOT EXISTS ${NODEJS_HEADERS_PATH}/src) + file(REMOVE_RECURSE ${ROOT}/include) + message(FATAL_ERROR "Unable to find core headers") + endif() + file(COPY ${NODEJS_HEADERS_PATH}/src/ + DESTINATION ${ROOT}/include + ) + if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/uv/include) + file(REMOVE_RECURSE ${ROOT}/include) + message(FATAL_ERROR "Unable to find libuv headers") + endif() + file(COPY ${NODEJS_HEADERS_PATH}/deps/uv/include/ + DESTINATION ${ROOT}/include + ) + if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/v8/include) + file(REMOVE_RECURSE ${ROOT}/include) + message(FATAL_ERROR "Unable to find v8 headers") + endif() + file(COPY ${NODEJS_HEADERS_PATH}/deps/v8/include/ + DESTINATION ${ROOT}/include + ) + if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/zlib) + file(REMOVE_RECURSE ${ROOT}/include) + message(FATAL_ERROR "Unable to find zlib headers") + endif() + file(COPY ${NODEJS_HEADERS_PATH}/deps/zlib/ + DESTINATION ${ROOT}/include + ) + endif() + file(REMOVE_RECURSE ${NODEJS_HEADERS_PATH}) + unset(NODEJS_HEADERS_PATH CACHE) + endif() + + # Only download the libraries on windows, since its the only place + # its necessary. Note, this requires rerunning CMake if moving + # a module from one platform to another (should happen automatically + # with most generators) + if(WIN32) + # Download the win32 library for linking + file(STRINGS + ${ROOT}/CHECKSUM LIB32_CHECKSUM + LIMIT_COUNT 1 + REGEX ${LIB32_MATCH} + ) + if(NOT LIB32_CHECKSUM) + message(FATAL_ERROR "Unable to extract x86 library checksum") + endif() + string(REGEX MATCH ${LIB32_MATCH} LIB32_CHECKSUM ${LIB32_CHECKSUM}) + set(LIB32_CHECKSUM ${CMAKE_MATCH_1}) + set(LIB32_PATH win-x86) + set(LIB32_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5}) + set(LIB32_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB32_NAME}) + if(NOT EXISTS ${ROOT}/${LIB32_PATH}) + file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH}) + download_file( + "Node.js windows library (32-bit)" + ${URL}/${VERSION}/${LIB32_TARGET} + ${TEMP}/${LIB32_PATH}/${LIB32_NAME} + INACTIVITY_TIMEOUT 10 + EXPECTED_HASH ${CHECKTYPE}=${LIB32_CHECKSUM} + ) + file(REMOVE_RECURSE ${ROOT}/${LIB32_PATH}) + file(MAKE_DIRECTORY ${ROOT}/${LIB32_PATH}) + file(RENAME + ${TEMP}/${LIB32_PATH}/${LIB32_NAME} + ${ROOT}/${LIB32_PATH}/${LIB32_NAME} + ) + file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH}) + endif() + + # Download the win64 library for linking + file(STRINGS + ${ROOT}/CHECKSUM LIB64_CHECKSUM + LIMIT_COUNT 1 + REGEX ${LIB64_MATCH} + ) + if(NOT LIB64_CHECKSUM) + message(FATAL_ERROR "Unable to extract x64 library checksum") + endif() + string(REGEX MATCH ${LIB64_MATCH} LIB64_CHECKSUM ${LIB64_CHECKSUM}) + set(LIB64_CHECKSUM ${CMAKE_MATCH_1}) + set(LIB64_PATH win-x64) + set(LIB64_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5}) + set(LIB64_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB64_NAME}) + if(NOT EXISTS ${ROOT}/${LIB64_PATH}) + file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH}) + download_file( + "Node.js windows library (64-bit)" + ${URL}/${VERSION}/${LIB64_TARGET} + ${TEMP}/${LIB64_PATH}/${LIB64_NAME} + INACTIVITY_TIMEOUT 10 + EXPECTED_HASH ${CHECKTYPE}=${LIB64_CHECKSUM} + ) + file(REMOVE_RECURSE ${ROOT}/${LIB64_PATH}) + file(MAKE_DIRECTORY ${ROOT}/${LIB64_PATH}) + file(RENAME + ${TEMP}/${LIB64_PATH}/${LIB64_NAME} + ${ROOT}/${LIB64_PATH}/${LIB64_NAME} + ) + file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH}) + endif() + endif() + + # The downloaded headers should always be set for inclusion + list(APPEND INCLUDE_DIRS ${ROOT}/include) + + # Look for the NAN module, and add it to the includes + find_nodejs_module( + nan + ${CMAKE_CURRENT_SOURCE_DIR} + NODEJS_NAN_DIR + ) + if(NODEJS_NAN_DIR) + list(APPEND INCLUDE_DIRS ${NODEJS_NAN_DIR}) + endif() + + # Under windows, we need a bunch of libraries (due to the way + # dynamic linking works) + if(WIN32) + # Generate and use a delay load hook to allow the node binary + # name to be changed while still loading native modules + set(DELAY_LOAD_HOOK ${CMAKE_CURRENT_BINARY_DIR}/win_delay_load_hook.c) + nodejs_generate_delayload_hook(${DELAY_LOAD_HOOK}) + set(SOURCES ${DELAY_LOAD_HOOK}) + + # Necessary flags to get delayload working correctly + list(APPEND LINK_FLAGS + "-IGNORE:4199" + "-DELAYLOAD:iojs.exe" + "-DELAYLOAD:node.exe" + "-DELAYLOAD:node.dll" + ) + + # Core system libraries used by node + list(APPEND LIBRARIES + kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib + odbc32.lib Shlwapi.lib DelayImp.lib + ) + + # Also link to the node stub itself (downloaded above) + if(CMAKE_CL_64) + list(APPEND LIBRARIES ${ROOT}/${LIB64_PATH}/${LIB64_NAME}) + else() + list(APPEND LIBRARIES ${ROOT}/${LIB32_PATH}/${LIB32_NAME}) + endif() + else() + # Non-windows platforms should use these flags + list(APPEND DEFINITIONS _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64) + endif() + + # Special handling for OSX / clang to allow undefined symbols + # Define is required by node on OSX + if(APPLE) + list(APPEND LINK_FLAGS "-undefined dynamic_lookup") + list(APPEND DEFINITIONS _DARWIN_USE_64_BIT_INODE=1) + endif() + + # Export all settings for use as arguments in the rest of the build + set(NODEJS_VERSION ${VERSION} PARENT_SCOPE) + set(NODEJS_SOURCES ${SOURCES} PARENT_SCOPE) + set(NODEJS_INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) + set(NODEJS_LIBRARIES ${LIBRARIES} PARENT_SCOPE) + set(NODEJS_LINK_FLAGS ${LINK_FLAGS} PARENT_SCOPE) + set(NODEJS_DEFINITIONS ${DEFINITIONS} PARENT_SCOPE) + + # Prevents this function from executing more than once + set(NODEJS_INIT TRUE PARENT_SCOPE) +endfunction() + +# Helper function for defining a node module +# After nodejs_init, all of the settings and dependencies necessary to do +# this yourself are defined, but this helps make sure everything is configured +# correctly. Feel free to use it as a model to do this by hand (or to +# tweak this configuration if you need something custom). +function(add_nodejs_module NAME) + # Make sure node is initialized (variables set) before defining the module + if(NOT NODEJS_INIT) + message(FATAL_ERROR + "Node.js has not been initialized. " + "Call nodejs_init before adding any modules" + ) + endif() + # In order to match node-gyp, we need to build into type specific folders + # ncmake takes care of this, but be sure to set CMAKE_BUILD_TYPE yourself + # if invoking CMake directly + if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) + message(FATAL_ERROR + "Configuration type must be specified. " + "Set CMAKE_BUILD_TYPE or use a different generator" + ) + endif() + + # A node module is a shared library + add_library(${NAME} SHARED ${NODEJS_SOURCES} ${ARGN}) + # Add compiler defines for the module + # Two helpful ones: + # MODULE_NAME must match the name of the build library, define that here + target_compile_definitions(${NAME} + PRIVATE MODULE_NAME=${NAME} + PUBLIC ${NODEJS_DEFINITIONS} + ) + # This properly defines includes for the module + target_include_directories(${NAME} PUBLIC ${NODEJS_INCLUDE_DIRS}) + + # Add link flags to the module + target_link_libraries(${NAME} ${NODEJS_LIBRARIES}) + + # Set required properties for the module to build properly + # Correct naming, symbol visiblity and C++ standard + set_target_properties(${NAME} PROPERTIES + OUTPUT_NAME ${NAME} + PREFIX "" + SUFFIX ".node" + MACOSX_RPATH ON + C_VISIBILITY_PRESET hidden + CXX_VISIBILITY_PRESET hidden + POSITION_INDEPENDENT_CODE TRUE + CMAKE_CXX_STANDARD_REQUIRED TRUE + CXX_STANDARD 11 + LINK_FLAGS "${NODEJS_LINK_FLAGS}" + ) + + # Make sure we're buiilding in a build specific output directory + # Only necessary on single-target generators (Make, Ninja) + # Multi-target generators do this automatically + # This (luckily) mirrors node-gyp conventions + if(NOT CMAKE_CONFIGURATION_TYPES) + set_property(TARGET ${NAME} PROPERTY + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BUILD_TYPE} + ) + endif() +endfunction() diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake index 41d820a0775..8c9aa0fe8f5 100644 --- a/cmake/mbgl.cmake +++ b/cmake/mbgl.cmake @@ -6,21 +6,12 @@ if (NOT MBGL_PLATFORM) endif() endif() -if(NOT EXISTS ${CMAKE_SOURCE_DIR}/node_modules/node-cmake/FindNodeJS.cmake) - message(FATAL_ERROR "Can't find node-cmake") +find_program(NodeJS_EXECUTABLE NAMES nodejs node) +if (NOT NodeJS_EXECUTABLE) + message(FATAL_ERROR "Could not find Node.js") endif() -# Load Node.js -set(NodeJS_CXX_STANDARD 14 CACHE INTERNAL "Use C++14" FORCE) -set(NodeJS_DOWNLOAD ON CACHE INTERNAL "Download node.js sources" FORCE) -set(NodeJS_USE_CLANG_STDLIB OFF CACHE BOOL "Don't use libc++ by default" FORCE) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/node_modules/node-cmake) -find_package(NodeJS) - -find_program(npm_EXECUTABLE - NAMES npm - PATHS ${NodeJS_ROOT_DIR}) - +find_program(npm_EXECUTABLE NAMES npm) if (NOT npm_EXECUTABLE) message(FATAL_ERROR "Could not find npm") endif() diff --git a/cmake/node.cmake b/cmake/node.cmake index ea28e86106b..2dd4a66c38a 100644 --- a/cmake/node.cmake +++ b/cmake/node.cmake @@ -1,7 +1,15 @@ +# Load Node.js +include(cmake/NodeJS.cmake) +nodejs_init() + add_nodejs_module(mbgl-node platform/node/src/node_mapbox_gl_native.cpp ) +# NodeJS.cmake forces C++11. +# https://github.com/cjntaylor/node-cmake/issues/18 +set_target_properties("mbgl-node" PROPERTIES CXX_STANDARD 14) + target_sources(mbgl-node PRIVATE platform/node/src/node_logging.hpp PRIVATE platform/node/src/node_logging.cpp diff --git a/package.json b/package.json index 19acd188c12..d389b065142 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "ejs": "^2.4.1", "express": "^4.11.1", "lodash": "^4.16.4", - "node-cmake": "^1.2.1", "pixelmatch": "^4.0.2", "pngjs": "^3.0.0", "request": "^2.72.0",