From 1a6bce79d13a1eccfc3b4804ce85b0b63d4420e4 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 30 Oct 2022 00:04:49 +0100 Subject: [PATCH 01/50] fix: missing js_native_api_symbols --- lib/cMake.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/cMake.js b/lib/cMake.js index c73f4bce..39d73a22 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -269,6 +269,9 @@ CMake.prototype._generateNodeLibDef = async function (targetFile) { for (const sym of ver.node_api_symbols) { allSymbols.add(sym) } + for (const sym of ver.js_native_api_symbols) { + allSymbols.add(sym) + } } // Write a 'def' file for NODE.EXE From 3be712f122251c9947f68bf3877bcc0d27d41947 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 30 Oct 2022 00:24:22 +0100 Subject: [PATCH 02/50] chore: add node-api unit test --- tests/es6/buildSystem.js | 5 +++++ tests/es6/prototype-napi/CMakeLists.txt | 20 ++++++++++++++++++++ tests/es6/prototype-napi/package.json | 5 +++++ tests/es6/prototype-napi/src/addon.cpp | 14 ++++++++++++++ tests/es6/testCases.js | 12 ++++++++++++ 5 files changed, 56 insertions(+) create mode 100644 tests/es6/prototype-napi/CMakeLists.txt create mode 100644 tests/es6/prototype-napi/package.json create mode 100644 tests/es6/prototype-napi/src/addon.cpp diff --git a/tests/es6/buildSystem.js b/tests/es6/buildSystem.js index b6989a8d..87ea8979 100644 --- a/tests/es6/buildSystem.js +++ b/tests/es6/buildSystem.js @@ -39,6 +39,11 @@ describe("BuildSystem", function () { it("should rebuild prototype if cwd is the source directory", async function () { await testCases.buildPrototype2WithCWD(); }); + + it("should build prototpye with nodeapi", async function () { + await testCases.buildPrototypeNapi(); + }); + it("should run with old GNU compilers", async function () { await testCases.shouldConfigurePreC11Properly(); diff --git a/tests/es6/prototype-napi/CMakeLists.txt b/tests/es6/prototype-napi/CMakeLists.txt new file mode 100644 index 00000000..9a39c3fa --- /dev/null +++ b/tests/es6/prototype-napi/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) + +project (addon_napi) + +if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") +endif() + +include_directories(${CMAKE_JS_INC}) + +add_library(${PROJECT_NAME} SHARED src/addon.cpp) + +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") + +target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) + +if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) + # Generate node.lib + execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) +endif() diff --git a/tests/es6/prototype-napi/package.json b/tests/es6/prototype-napi/package.json new file mode 100644 index 00000000..2a750b1a --- /dev/null +++ b/tests/es6/prototype-napi/package.json @@ -0,0 +1,5 @@ +{ + "binary": { + "napi_versions": 7 + } +} \ No newline at end of file diff --git a/tests/es6/prototype-napi/src/addon.cpp b/tests/es6/prototype-napi/src/addon.cpp new file mode 100644 index 00000000..6e777a9b --- /dev/null +++ b/tests/es6/prototype-napi/src/addon.cpp @@ -0,0 +1,14 @@ +#include + +Napi::Value Mul(const Napi::CallbackInfo &info) +{ + double value = info[0].As().DoubleValue() * info[1].As().DoubleValue(); + return Napi::Number::New(info.Env(), value); +} + +Napi::Object Init(Napi::Env env, Napi::Object exports) +{ + exports.Set("mul", Napi::Function::New(env, Mul)); +} + +NODE_API_MODULE(addon_napi, Init) diff --git a/tests/es6/testCases.js b/tests/es6/testCases.js index 29d77fa5..c0622531 100644 --- a/tests/es6/testCases.js +++ b/tests/es6/testCases.js @@ -27,6 +27,18 @@ const testCases = { process.chdir(cwd); } }, + buildPrototypeNapi: async function(options) { + const cwd = process.cwd(); + process.chdir(path.resolve(path.join(__dirname, "./prototype-napi"))); + const buildSystem = new BuildSystem(options); + try { + await buildSystem.rebuild(); + assert.ok((await fs.stat(path.join(__dirname, "prototype-napi/build/Release/addon_napi.node"))).isFile()); + } + finally { + process.chdir(cwd); + } + }, shouldConfigurePreC11Properly: async function(options) { options = { directory: path.resolve(path.join(__dirname, "./prototype")), From 98e4184ebd2bc35fde66aec40218b50047435d8e Mon Sep 17 00:00:00 2001 From: Craig Mason Date: Wed, 14 Dec 2022 22:46:44 +0000 Subject: [PATCH 03/50] fix: win_delay_load_hook.cc will fall back to the host process (#289) --- lib/cpp/win_delay_load_hook.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/cpp/win_delay_load_hook.cc b/lib/cpp/win_delay_load_hook.cc index 72dca35d..8add2927 100644 --- a/lib/cpp/win_delay_load_hook.cc +++ b/lib/cpp/win_delay_load_hook.cc @@ -40,6 +40,9 @@ static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo* info) { if (_stricmp(info->szDll, "node.exe") != 0) return NULL; + + // Fall back to the current process + if(!node_dll) node_dll = GetModuleHandleA(NULL); return (FARPROC) node_dll; } From b06d80a19a39986e6a451c7ef72ad18ff62c4b9b Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Wed, 14 Dec 2022 23:35:21 +0000 Subject: [PATCH 04/50] feat: add print-cmakejs-* commands for ide integration #290 --- README.md | 37 +++++++------ bin/cmake-js | 26 ++++++++- lib/buildSystem.js | 12 +++++ lib/cMake.js | 128 +++++++++++++++++++++++++-------------------- 4 files changed, 128 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 4d7b9eba..6411eccc 100644 --- a/README.md +++ b/README.md @@ -28,20 +28,27 @@ cmake-js --help Usage: cmake-js [] [options] Commands: - install Install Node.js distribution files if needed - configure Configure CMake project - print-configure Print the configuration command - build Build the project (will configure first if required) - print-build Print the build command - clean Clean the project directory - print-clean Print the clean command - reconfigure Clean the project directory then configure the project - rebuild Clean the project directory then build the project - compile Build the project, and if build fails, try a full rebuild + cmake-js install Install Node.js distribution files if needed + cmake-js configure Configure CMake project + cmake-js print-configure Print the configuration command + cmake-js print-cmakejs-src Print the value of the CMAKE_JS_SRC variable + cmake-js print-cmakejs-include Print the value of the CMAKE_JS_INC variable + cmake-js print-cmakejs-lib Print the value of the CMAKE_JS_LIB variable + cmake-js build Build the project (will configure first if + required) + cmake-js print-build Print the build command + cmake-js clean Clean the project directory + cmake-js print-clean Print the clean command + cmake-js reconfigure Clean the project directory then configure the + project + cmake-js rebuild Clean the project directory then build the + project + cmake-js compile Build the project, and if build fails, try a + full rebuild Options: - --version Show version number [boolean] - -h, --help show this screen [boolean] + --version Show version number [boolean] + -h, --help Show help [boolean] -l, --log-level set log level (silly, verbose, info, http, warn, error), default is info [string] -d, --directory specify CMake project's directory (where CMakeLists.txt @@ -61,13 +68,13 @@ Options: -T, --target only build the specified target [string] -C, --prefer-clang use Clang compiler instead of default CMake compiler, if available (Posix) [boolean] - --cc use the specified C compiler [string] - --cxx use the specified C++ compiler [string] + --cc use the specified C compiler [string] + --cxx use the specified C++ compiler [string] -r, --runtime the runtime to use [string] -v, --runtime-version the runtime version to use [string] -a, --arch the architecture to build in [string] -p, --parallel the number of threads cmake can use [number] - --CD Custom argument passed to CMake in format: + --CD Custom argument passed to CMake in format: -D [string] -i, --silent Prevents CMake.js to print to the stdio [boolean] -O, --out Specify the output directory to compile to, default is diff --git a/bin/cmake-js b/bin/cmake-js index aecf12f3..d747d740 100755 --- a/bin/cmake-js +++ b/bin/cmake-js @@ -23,14 +23,15 @@ for (const [key, value] of Object.entries(npmConfigData)) { } } -console.log(process.argv); - const yargs = require("yargs") .usage("CMake.js " + version + "\n\nUsage: $0 [] [options]") .version(version) .command("install", "Install Node.js distribution files if needed") .command("configure", "Configure CMake project") .command("print-configure", "Print the configuration command") + .command("print-cmakejs-src", "Print the value of the CMAKE_JS_SRC variable") + .command("print-cmakejs-include", "Print the value of the CMAKE_JS_INC variable") + .command("print-cmakejs-lib", "Print the value of the CMAKE_JS_LIB variable") .command("build", "Build the project (will configure first if required)") .command("print-build", "Print the build command") .command("clean", "Clean the project directory") @@ -261,6 +262,24 @@ function printConfigure() { console.info(command); })); } +function printCmakeJsLib() { + exitOnError(buildSystem.getCmakeJsLibString() + .then(function (command) { + console.info(command); + })); +} +function printCmakeJsInclude() { + exitOnError(buildSystem.getCmakeJsIncludeString() + .then(function (command) { + console.info(command); + })); +} +function printCmakeJsSrc() { + exitOnError(buildSystem.getCmakeJsSrcString() + .then(function (command) { + console.info(command); + })); +} function build() { exitOnError(buildSystem.build()); } @@ -292,6 +311,9 @@ function compile() { let done = ifCommand("install", install); done = done || ifCommand("configure", configure); done = done || ifCommand("print-configure", printConfigure); +done = done || ifCommand("print-cmakejs-src", printCmakeJsSrc); +done = done || ifCommand("print-cmakejs-include", printCmakeJsInclude); +done = done || ifCommand("print-cmakejs-lib", printCmakeJsLib); done = done || ifCommand("build", build); done = done || ifCommand("print-build", printBuild); done = done || ifCommand("clean", clean); diff --git a/lib/buildSystem.js b/lib/buildSystem.js index e1690df7..d60d0fd1 100644 --- a/lib/buildSystem.js +++ b/lib/buildSystem.js @@ -95,6 +95,18 @@ BuildSystem.prototype.getConfigureCommand = function () { return this._invokeCMake("getConfigureCommand"); }; +BuildSystem.prototype.getCmakeJsLibString = function () { + return this._invokeCMake("getCmakeJsLibString"); +}; + +BuildSystem.prototype.getCmakeJsIncludeString = function () { + return this._invokeCMake("getCmakeJsIncludeString"); +}; + +BuildSystem.prototype.getCmakeJsSrcString = function () { + return this._invokeCMake("getCmakeJsSrcString"); +}; + BuildSystem.prototype.configure = function () { return this._invokeCMake("configure"); }; diff --git a/lib/cMake.js b/lib/cMake.js index 39d73a22..1d033c2c 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -117,7 +117,9 @@ CMake.prototype.verifyIfAvailable = function () { } }; -CMake.prototype.getConfigureCommand = async function (nodeLibDefPath) { +CMake.prototype.getConfigureCommand = async function () { + const nodeLibDefPath = environment.isWin && this.options.isNodeApi ? path.join(this.options.out, 'node-lib.def') : undefined + // Create command: let command = [this.path, this.projectRoot, "--no-warn-unused-cli"]; @@ -139,48 +141,13 @@ CMake.prototype.getConfigureCommand = async function (nodeLibDefPath) { // This tries to encourage MT builds which are larger but less likely to have this crash. D.push({"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>"}) - let incPaths = []; - if (!this.options.isNodeApi) { - // Include and lib: - if (this.dist.headerOnly) { - incPaths = [path.join(this.dist.internalPath, "/include/node")]; - } - else { - const nodeH = path.join(this.dist.internalPath, "/src"); - const v8H = path.join(this.dist.internalPath, "/deps/v8/include"); - const uvH = path.join(this.dist.internalPath, "/deps/uv/include"); - incPaths = [nodeH, v8H, uvH]; - } - - // NAN - const nanH = await locateNAN(this.projectRoot); - if (nanH) { - incPaths.push(nanH); - } - } else { - // Base headers - const apiHeaders = require('node-api-headers') - incPaths.push(apiHeaders.include_dir) - - // Node-api - const napiH = await locateNodeApi(this.projectRoot) - if (napiH) { - incPaths.push(napiH) - } - } - // Includes: - D.push({"CMAKE_JS_INC": incPaths.join(";")}); + const includesString = await this.getCmakeJsIncludeString(); + D.push({ "CMAKE_JS_INC": includesString }); // Sources: - const srcPaths = []; - if (environment.isWin) { - const delayHook = path.normalize(path.join(__dirname, 'cpp', 'win_delay_load_hook.cc')); - - srcPaths.push(delayHook.replace(/\\/gm, '/')); - } - - D.push({"CMAKE_JS_SRC": srcPaths.join(";")}); + const srcsString = this.getCmakeJsSrcString(); + D.push({ "CMAKE_JS_SRC": srcsString }); // Runtime: D.push({"NODE_RUNTIME": this.targetOptions.runtime}); @@ -203,21 +170,8 @@ CMake.prototype.getConfigureCommand = async function (nodeLibDefPath) { // Toolset: await this.toolset.initialize(false); - if (environment.isWin) { - // Win - const libs = [] - if (nodeLibDefPath) { - const nodeLibPath = path.join(this.workDir, 'node.lib') - D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) - D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) - libs.push(nodeLibPath) - } else { - libs.push(...this.dist.winLibs) - } - if (libs.length) { - D.push({"CMAKE_JS_LIB": libs.join(";")}); - } - } + const libsString = this.getCmakeJsLibString() + D.push({ "CMAKE_JS_LIB": libsString }); if (this.toolset.generator) { command.push("-G", this.toolset.generator); @@ -261,6 +215,66 @@ CMake.prototype.getConfigureCommand = async function (nodeLibDefPath) { return command; }; +CMake.prototype.getCmakeJsLibString = function () { + const libs = [] + if (environment.isWin) { + if (nodeLibDefPath) { + const nodeLibPath = path.join(this.workDir, 'node.lib') + D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) + D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) + libs.push(nodeLibPath) + } else { + libs.push(...this.dist.winLibs) + } + } + return libs.join(";"); +}; + +CMake.prototype.getCmakeJsIncludeString = async function () { + let incPaths = []; + if (!this.options.isNodeApi) { + // Include and lib: + if (this.dist.headerOnly) { + incPaths = [path.join(this.dist.internalPath, "/include/node")]; + } + else { + const nodeH = path.join(this.dist.internalPath, "/src"); + const v8H = path.join(this.dist.internalPath, "/deps/v8/include"); + const uvH = path.join(this.dist.internalPath, "/deps/uv/include"); + incPaths = [nodeH, v8H, uvH]; + } + + // NAN + const nanH = await locateNAN(this.projectRoot); + if (nanH) { + incPaths.push(nanH); + } + } else { + // Base headers + const apiHeaders = require('node-api-headers') + incPaths.push(apiHeaders.include_dir) + + // Node-api + const napiH = await locateNodeApi(this.projectRoot) + if (napiH) { + incPaths.push(napiH) + } + } + + return incPaths.join(";"); +}; + +CMake.prototype.getCmakeJsSrcString = function () { + const srcPaths = []; + if (environment.isWin) { + const delayHook = path.normalize(path.join(__dirname, 'cpp', 'win_delay_load_hook.cc')); + + srcPaths.push(delayHook.replace(/\\/gm, '/')); + } + + return srcPaths.join(";"); +}; + CMake.prototype._generateNodeLibDef = async function (targetFile) { try { // Compile a Set of all the symbols that could be exported @@ -288,11 +302,9 @@ CMake.prototype._generateNodeLibDef = async function (targetFile) { CMake.prototype.configure = async function () { this.verifyIfAvailable(); - const nodeLibDefPath = environment.isWin && this.options.isNodeApi ? path.join(this.options.out, 'node-lib.def') : undefined - this.log.info("CMD", "CONFIGURE"); const listPath = path.join(this.projectRoot, "CMakeLists.txt"); - const command = await this.getConfigureCommand(nodeLibDefPath); + const command = await this.getConfigureCommand(); try { await fs.lstat(listPath); From 42c5f7f8ad9be0b24f365cf72bdd0f5b37f0d325 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Wed, 14 Dec 2022 23:50:25 +0000 Subject: [PATCH 05/50] v7.1.0 --- changelog.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index a8c1a41c..d9767b2c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,10 @@ +v7.1.0 - 14/12/22 +========== + +- add commands for retrieving cmake-js include and lib directories +- fix win delay hook issues with electron +- fix missing js_native_api_symbols in windows node.lib + v7.0.0 - 08/10/22 ========== diff --git a/package.json b/package.json index eb6849f7..5117a2f5 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node-addon-api" ], "main": "lib", - "version": "7.0.0", + "version": "7.1.0", "author": "Gábor Mező aka unbornchikken", "maintainers": [ { From d2e1c42687b9ed7f9ac1e0d440430bfa203ea8c9 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 15 Dec 2022 00:20:17 +0000 Subject: [PATCH 06/50] fix: typo --- lib/cMake.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/cMake.js b/lib/cMake.js index 1d033c2c..79988ba0 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -118,7 +118,6 @@ CMake.prototype.verifyIfAvailable = function () { }; CMake.prototype.getConfigureCommand = async function () { - const nodeLibDefPath = environment.isWin && this.options.isNodeApi ? path.join(this.options.out, 'node-lib.def') : undefined // Create command: let command = [this.path, this.projectRoot, "--no-warn-unused-cli"]; @@ -216,8 +215,10 @@ CMake.prototype.getConfigureCommand = async function () { }; CMake.prototype.getCmakeJsLibString = function () { + const libs = [] - if (environment.isWin) { + if (environment.isWin) { + const nodeLibDefPath = this.getNodeLibDefPath() if (nodeLibDefPath) { const nodeLibPath = path.join(this.workDir, 'node.lib') D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) @@ -275,6 +276,10 @@ CMake.prototype.getCmakeJsSrcString = function () { return srcPaths.join(";"); }; +CMake.prototype.getNodeLibDefPath = function () { + return environment.isWin && this.options.isNodeApi ? path.join(this.options.out, 'node-lib.def') : undefined +} + CMake.prototype._generateNodeLibDef = async function (targetFile) { try { // Compile a Set of all the symbols that could be exported @@ -323,6 +328,8 @@ CMake.prototype.configure = async function () { const cwd = process.cwd(); process.chdir(this.workDir); try { + const nodeLibDefPath = this.getNodeLibDefPath() + if (environment.isWin && nodeLibDefPath) { await this._generateNodeLibDef(nodeLibDefPath) } From a50552c8fcb8d038a77ceedf38c8622fbe852a21 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 15 Dec 2022 00:40:12 +0000 Subject: [PATCH 07/50] fix: typo --- lib/cMake.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/cMake.js b/lib/cMake.js index 79988ba0..ab152ce9 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -172,6 +172,15 @@ CMake.prototype.getConfigureCommand = async function () { const libsString = this.getCmakeJsLibString() D.push({ "CMAKE_JS_LIB": libsString }); + if (environment.isWin) { + const nodeLibDefPath = this.getNodeLibDefPath() + if (nodeLibDefPath) { + const nodeLibPath = path.join(this.workDir, 'node.lib') + D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) + D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) + } + } + if (this.toolset.generator) { command.push("-G", this.toolset.generator); } @@ -220,10 +229,7 @@ CMake.prototype.getCmakeJsLibString = function () { if (environment.isWin) { const nodeLibDefPath = this.getNodeLibDefPath() if (nodeLibDefPath) { - const nodeLibPath = path.join(this.workDir, 'node.lib') - D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) - D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) - libs.push(nodeLibPath) + libs.push(path.join(this.workDir, 'node.lib')) } else { libs.push(...this.dist.winLibs) } From 9ccfd0dfb99ead7edeeb967ab5ed34722c58ad24 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 15 Dec 2022 00:56:02 +0000 Subject: [PATCH 08/50] chore: update github action --- .github/workflows/node.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 7e6d77b3..b59c82f9 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -48,7 +48,7 @@ jobs: libc: musl steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js 14.x if: ${{ !matrix.docker-arch }} From 3a06b71cd27332b29e042bad972843d827586f88 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 15 Dec 2022 00:57:27 +0000 Subject: [PATCH 09/50] v7.1.1 --- changelog.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index d9767b2c..a1b87a6f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,8 @@ +v7.1.1 - 15/12/22 +========== + +- fix build errors on windows + v7.1.0 - 14/12/22 ========== diff --git a/package.json b/package.json index 5117a2f5..40da7ac9 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node-addon-api" ], "main": "lib", - "version": "7.1.0", + "version": "7.1.1", "author": "Gábor Mező aka unbornchikken", "maintainers": [ { From e36dd99d9fb2fb3eb744b2bc4edbf6df1cb690dd Mon Sep 17 00:00:00 2001 From: Paul Taylor Date: Sun, 12 Feb 2023 04:01:19 -0800 Subject: [PATCH 10/50] fix: `-DCMAKE_JS_VERSION=undefined` (#298) Fixes https://github.com/cmake-js/cmake-js/issues/295 --- lib/cMake.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cMake.js b/lib/cMake.js index ab152ce9..5be18a2b 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -125,7 +125,7 @@ CMake.prototype.getConfigureCommand = async function () { const D = []; // CMake.js watermark - D.push({"CMAKE_JS_VERSION": environment.moduleVersion}); + D.push({"CMAKE_JS_VERSION": environment.cmakeJsVersion}); // Build configuration: D.push({"CMAKE_BUILD_TYPE": this.config}); From e40d2f31624ea62ca9e978aec738a69944d7ab61 Mon Sep 17 00:00:00 2001 From: Paul Taylor Date: Sun, 12 Feb 2023 04:03:24 -0800 Subject: [PATCH 11/50] feat: Forward extra arguments to CMake commands (#297) --- bin/cmake-js | 3 ++- lib/cMake.js | 18 +++++++++++------- tests/es6/buildSystem.js | 8 ++++++-- tests/es6/testCases.js | 20 +++++++++++++++++++- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/bin/cmake-js b/bin/cmake-js index d747d740..55df9fa4 100755 --- a/bin/cmake-js +++ b/bin/cmake-js @@ -224,7 +224,8 @@ const options = { silent: argv.i, out: argv.O, config: argv.B, - parallel: argv.p + parallel: argv.p, + extraCMakeArgs: argv._.slice(1), }; log.verbose("CON", "options:"); diff --git a/lib/cMake.js b/lib/cMake.js index 5be18a2b..9fea02b9 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -25,6 +25,7 @@ function CMake(options) { this.targetOptions = new TargetOptions(this.options); this.toolset = new Toolset(this.options); this.cMakeOptions = this.options.cMakeOptions || {}; + this.extraCMakeArgs = this.options.extraCMakeArgs || []; this.silent = !!options.silent; } @@ -135,11 +136,11 @@ CMake.prototype.getConfigureCommand = async function () { else { D.push({"CMAKE_LIBRARY_OUTPUT_DIRECTORY": this.buildDir}); } - + // In some configurations MD builds will crash upon attempting to free memory. - // This tries to encourage MT builds which are larger but less likely to have this crash. + // This tries to encourage MT builds which are larger but less likely to have this crash. D.push({"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>"}) - + // Includes: const includesString = await this.getCmakeJsIncludeString(); D.push({ "CMAKE_JS_INC": includesString }); @@ -220,11 +221,11 @@ CMake.prototype.getConfigureCommand = async function () { return "-D" + Object.keys(p)[0] + "=" + Object.values(p)[0]; })); - return command; + return command.concat(this.extraCMakeArgs); }; CMake.prototype.getCmakeJsLibString = function () { - + const libs = [] if (environment.isWin) { const nodeLibDefPath = this.getNodeLibDefPath() @@ -364,7 +365,7 @@ CMake.prototype.getBuildCommand = function () { if (this.options.parallel) { command.push("--parallel", this.options.parallel); } - return Promise.resolve(command); + return Promise.resolve(command.concat(this.extraCMakeArgs)); }; CMake.prototype.build = async function () { @@ -377,7 +378,7 @@ CMake.prototype.build = async function () { }; CMake.prototype.getCleanCommand = function () { - return [this.path, "-E", "remove_directory", this.workDir]; + return [this.path, "-E", "remove_directory", this.workDir].concat(this.extraCMakeArgs); }; CMake.prototype.clean = function () { @@ -388,16 +389,19 @@ CMake.prototype.clean = function () { }; CMake.prototype.reconfigure = async function () { + this.extraCMakeArgs = []; await this.clean(); await this.configure(); }; CMake.prototype.rebuild = async function () { + this.extraCMakeArgs = []; await this.clean(); await this.build(); }; CMake.prototype.compile = async function () { + this.extraCMakeArgs = []; try { await this.build(); } diff --git a/tests/es6/buildSystem.js b/tests/es6/buildSystem.js index 87ea8979..a8b28310 100644 --- a/tests/es6/buildSystem.js +++ b/tests/es6/buildSystem.js @@ -3,7 +3,7 @@ const assert = require("assert"); const lib = require("../../"); -const locateNAN = require("../../lib/locateNAN") +const locateNAN = require("../../lib/locateNAN"); const CMake = lib.CMake; const path = require("path"); const log = require("npmlog"); @@ -39,7 +39,7 @@ describe("BuildSystem", function () { it("should rebuild prototype if cwd is the source directory", async function () { await testCases.buildPrototype2WithCWD(); }); - + it("should build prototpye with nodeapi", async function () { await testCases.buildPrototypeNapi(); }); @@ -52,4 +52,8 @@ describe("BuildSystem", function () { it("should configure with custom option", async function () { await testCases.configureWithCustomOptions(); }); + + it("should forward extra arguments to CMake", async function () { + await testCases.shouldForwardExtraCMakeArgs(); + }); }); diff --git a/tests/es6/testCases.js b/tests/es6/testCases.js index c0622531..6f8af6b1 100644 --- a/tests/es6/testCases.js +++ b/tests/es6/testCases.js @@ -63,7 +63,25 @@ const testCases = { const command = await buildSystem.getConfigureCommand(); assert.notEqual(command.indexOf("-Dfoo=bar"), -1, "custom options added"); - } + }, + shouldForwardExtraCMakeArgs: async function(options) { + options = { + directory: path.resolve(path.join(__dirname, "./prototype")), + ...options + }; + + options.extraCMakeArgs = ["--debug-find-pkg=Boost", "--trace-source=FindBoost.cmake"]; + const configure = await (new BuildSystem(options)).getConfigureCommand(); + assert.deepEqual(configure.slice(-2), options.extraCMakeArgs, "extra CMake args appended"); + + options.extraCMakeArgs = ["--", "CMakeFiles/x.dir/y.cpp.o"]; + const build = await (new BuildSystem(options)).getBuildCommand(); + assert.deepEqual(build.slice(-2), options.extraCMakeArgs, "extra CMake args appended"); + + options.extraCMakeArgs = [".cache", "/tmp/jest_rs"]; + const clean = await (new BuildSystem(options)).getCleanCommand(); + assert.deepEqual(clean.slice(-2), options.extraCMakeArgs, "extra CMake args appended"); + } }; module.exports = testCases; From e2452ee226490bc666d286ab0860aeb35fbbb035 Mon Sep 17 00:00:00 2001 From: Paul Taylor Date: Sun, 12 Feb 2023 04:14:43 -0800 Subject: [PATCH 12/50] fix: Only add build type to `CMAKE_LIBRARY_OUTPUT_DIRECTORY` if needed (#299) Co-authored-by: Julian Waller --- lib/cMake.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/cMake.js b/lib/cMake.js index 9fea02b9..03ac5ccf 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -131,11 +131,14 @@ CMake.prototype.getConfigureCommand = async function () { // Build configuration: D.push({"CMAKE_BUILD_TYPE": this.config}); if (environment.isWin) { - D.push({"CMAKE_RUNTIME_OUTPUT_DIRECTORY": this.workDir}); - } - else { - D.push({"CMAKE_LIBRARY_OUTPUT_DIRECTORY": this.buildDir}); - } + D.push({"CMAKE_RUNTIME_OUTPUT_DIRECTORY": this.workDir}); + } + else if (this.workDir.endsWith(this.config)) { + D.push({"CMAKE_LIBRARY_OUTPUT_DIRECTORY": this.workDir}); + } + else { + D.push({"CMAKE_LIBRARY_OUTPUT_DIRECTORY": this.buildDir}); + } // In some configurations MD builds will crash upon attempting to free memory. // This tries to encourage MT builds which are larger but less likely to have this crash. From cea310355598d6612894b9bbbba30b3afbd3c564 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 12 Feb 2023 12:26:03 +0000 Subject: [PATCH 13/50] chore: update cmake version for tests --- tests/es6/prototype-napi/CMakeLists.txt | 4 +++- tests/es6/prototype/CMakeLists.txt | 4 +++- tests/es6/prototype2/CMakeLists.txt | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/es6/prototype-napi/CMakeLists.txt b/tests/es6/prototype-napi/CMakeLists.txt index 9a39c3fa..5e090b60 100644 --- a/tests/es6/prototype-napi/CMakeLists.txt +++ b/tests/es6/prototype-napi/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.15) +cmake_policy(SET CMP0091 NEW) +cmake_policy(SET CMP0042 NEW) project (addon_napi) diff --git a/tests/es6/prototype/CMakeLists.txt b/tests/es6/prototype/CMakeLists.txt index 4c29d9bc..6893b869 100644 --- a/tests/es6/prototype/CMakeLists.txt +++ b/tests/es6/prototype/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.15) +cmake_policy(SET CMP0091 NEW) +cmake_policy(SET CMP0042 NEW) project (addon) diff --git a/tests/es6/prototype2/CMakeLists.txt b/tests/es6/prototype2/CMakeLists.txt index 0189ddc4..0d4bde80 100644 --- a/tests/es6/prototype2/CMakeLists.txt +++ b/tests/es6/prototype2/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.15) +cmake_policy(SET CMP0091 NEW) +cmake_policy(SET CMP0042 NEW) project (addon2) From b7aeab6fba9998b54563283b818233d0e82c46cc Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 12 Feb 2023 13:17:24 +0000 Subject: [PATCH 14/50] chore: fix test failures --- tests/es6/prototype-napi/CMakeLists.txt | 2 +- tests/es6/prototype/CMakeLists.txt | 2 +- tests/es6/prototype2/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/es6/prototype-napi/CMakeLists.txt b/tests/es6/prototype-napi/CMakeLists.txt index 5e090b60..53320e33 100644 --- a/tests/es6/prototype-napi/CMakeLists.txt +++ b/tests/es6/prototype-napi/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_policy(SET CMP0042 NEW) project (addon_napi) -if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) +if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") endif() diff --git a/tests/es6/prototype/CMakeLists.txt b/tests/es6/prototype/CMakeLists.txt index 6893b869..0a0e9ab3 100644 --- a/tests/es6/prototype/CMakeLists.txt +++ b/tests/es6/prototype/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_policy(SET CMP0042 NEW) project (addon) -if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) +if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") endif() diff --git a/tests/es6/prototype2/CMakeLists.txt b/tests/es6/prototype2/CMakeLists.txt index 0d4bde80..aab7498a 100644 --- a/tests/es6/prototype2/CMakeLists.txt +++ b/tests/es6/prototype2/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_policy(SET CMP0042 NEW) project (addon2) -if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) +if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") endif() From 862108474134f8eaf15bac7a36ec5a3111e9275d Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 12 Feb 2023 13:38:27 +0000 Subject: [PATCH 15/50] chore: update debian version --- .github/workflows/node.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index b59c82f9..7faf9887 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -35,11 +35,11 @@ jobs: - os: ubuntu-latest arch: arm64 docker-arch: linux/arm64 - docker-image: node:14-buster + docker-image: node:14-bullseye - os: ubuntu-latest arch: arm docker-arch: linux/arm/v7 - docker-image: node:14-buster + docker-image: node:14-bullseye # linux-musl - os: ubuntu-latest arch: x64 From c63c9f76119a9f85602700909c181a061a094220 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 12 Feb 2023 13:35:28 +0000 Subject: [PATCH 16/50] chore: fix windows x86 tests --- package.json | 2 +- tests/es6/testRunner.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 40da7ac9..08ae4e39 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "devDependencies": { "mocha": "*", "nan": "^2.16.0", - "node-addon-api": "^5.0.0" + "node-addon-api": "^6.0.0" }, "scripts": { "test": "mocha tests", diff --git a/tests/es6/testRunner.js b/tests/es6/testRunner.js index c9616bc0..d54cec8f 100644 --- a/tests/es6/testRunner.js +++ b/tests/es6/testRunner.js @@ -24,7 +24,7 @@ function* generateRuntimeOptions() { // Current: yield { runtime: "node", - runtimeVersion: "18.3.0", + runtimeVersion: "18.13.0", arch: arch }; } From 46a0f84bcd6b736ebd7af9b60eeea04fe1826862 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 12 Feb 2023 14:20:38 +0000 Subject: [PATCH 17/50] chore: update dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 08ae4e39..eec8ed95 100644 --- a/package.json +++ b/package.json @@ -41,12 +41,12 @@ "node": ">= 14.15.0" }, "dependencies": { - "axios": "^0.27.2", + "axios": "^1.3.2", "debug": "^4", "fs-extra": "^10.1.0", "lodash.isplainobject": "^4.0.6", "memory-stream": "^1.0.0", - "node-api-headers": "^0.0.1", + "node-api-headers": "^0.0.2", "npmlog": "^6.0.2", "rc": "^1.2.7", "semver": "^7.3.8", From a1c10147773b55dd4ac1b6244e267bd25d97ccfd Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 12 Feb 2023 15:07:48 +0000 Subject: [PATCH 18/50] v7.2.0 --- changelog.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index a1b87a6f..96f68a7f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,10 @@ +v7.2.0 - 12/02/23 +========== + +- fix: `-DCMAKE_JS_VERSION=undefined` (#298) +- fix: Only add build type to `CMAKE_LIBRARY_OUTPUT_DIRECTORY` if needed (#299) +- feat: Forward extra arguments to CMake commands (#297) + v7.1.1 - 15/12/22 ========== diff --git a/package.json b/package.json index eec8ed95..9f3f4c48 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node-addon-api" ], "main": "lib", - "version": "7.1.1", + "version": "7.2.0", "author": "Gábor Mező aka unbornchikken", "maintainers": [ { From 6eb7ea3816ed948a09459b104996c7419dbfdca6 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 13 Feb 2023 22:41:06 +0000 Subject: [PATCH 19/50] fix: update find-visualstudio to support Windows11SDK feat: Update function getSDK() to support Windows 11 SDK (#2565) https://github.com/nodejs/node-gyp/commit/ea8520e3855374bd15b6d001fe112d58a8d7d737 --- lib/import/find-visualstudio.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/import/find-visualstudio.js b/lib/import/find-visualstudio.js index 4d1dfa5d..2522d03a 100644 --- a/lib/import/find-visualstudio.js +++ b/lib/import/find-visualstudio.js @@ -309,29 +309,30 @@ VisualStudioFinder.prototype = { getSDK: function getSDK (info) { const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK' const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.' + const win11SDKPrefix = 'Microsoft.VisualStudio.Component.Windows11SDK.' - var Win10SDKVer = 0 + var Win10or11SDKVer = 0 info.packages.forEach((pkg) => { - if (!pkg.startsWith(win10SDKPrefix)) { + if (!pkg.startsWith(win10SDKPrefix) && !pkg.startsWith(win11SDKPrefix)) { return } const parts = pkg.split('.') if (parts.length > 5 && parts[5] !== 'Desktop') { - this.log.silly('- ignoring non-Desktop Win10SDK:', pkg) + this.log.silly('- ignoring non-Desktop Win10/11SDK:', pkg) return } const foundSdkVer = parseInt(parts[4], 10) if (isNaN(foundSdkVer)) { // Microsoft.VisualStudio.Component.Windows10SDK.IpOverUsb - this.log.silly('- failed to parse Win10SDK number:', pkg) + this.log.silly('- failed to parse Win10/11SDK number:', pkg) return } - this.log.silly('- found Win10SDK:', foundSdkVer) - Win10SDKVer = Math.max(Win10SDKVer, foundSdkVer) + this.log.silly('- found Win10/11SDK:', foundSdkVer) + Win10or11SDKVer = Math.max(Win10or11SDKVer, foundSdkVer) }) - if (Win10SDKVer !== 0) { - return `10.0.${Win10SDKVer}.0` + if (Win10or11SDKVer !== 0) { + return `10.0.${Win10or11SDKVer}.0` } else if (info.packages.indexOf(win8SDK) !== -1) { this.log.silly('- found Win8SDK') return '8.1' From 14525bf4750824b2f87ef515a5d19b5b3249b267 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 13 Feb 2023 22:41:57 +0000 Subject: [PATCH 20/50] fix: update find-visualstudio to remove support for VS2015 feat: remove support for VS2015 in Node.js >=19 (#2746) https://github.com/nodejs/node-gyp/commit/131d1a463baf034a04154bcda753a8295f112a34 --- lib/import/find-visualstudio.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/import/find-visualstudio.js b/lib/import/find-visualstudio.js index 2522d03a..c040f0b3 100644 --- a/lib/import/find-visualstudio.js +++ b/lib/import/find-visualstudio.js @@ -341,7 +341,12 @@ VisualStudioFinder.prototype = { }, // Find an installation of Visual Studio 2015 to use - findVisualStudio2015: function findVisualStudio2015 (cb) { + findVisualStudio2015: function findVisualStudio2015(cb) { + if (this.nodeSemver.major >= 19) { + this.addLog( + 'not looking for VS2015 as it is only supported up to Node.js 18') + return cb(null) + } return this.findOldVS({ version: '14.0', versionMajor: 14, From 6a2a50ba3d2e82a0ea80a8bb77cd2d3a03fb838c Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Tue, 14 Feb 2023 20:41:35 +0000 Subject: [PATCH 21/50] v7.2.1 --- changelog.md | 5 +++++ package.json | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 96f68a7f..838ab8e2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,8 @@ +v7.2.1 - 14/02/23 +========== + +- fix: support Windows11SDK + v7.2.0 - 12/02/23 ========== diff --git a/package.json b/package.json index 9f3f4c48..246ee9ae 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "build", "buildtools", "cmake", - "nw.js", + "nw.js", "electron", "boost", "nan", @@ -21,7 +21,7 @@ "node-addon-api" ], "main": "lib", - "version": "7.2.0", + "version": "7.2.1", "author": "Gábor Mező aka unbornchikken", "maintainers": [ { From e112728f769ec3da1582b9ab919c9b0ac16af779 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sun, 29 Oct 2023 17:29:05 +0000 Subject: [PATCH 22/50] chore: clarify how to declare project as node-api to readme --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 6411eccc..c759983e 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,14 @@ endif() } ``` +- Add the following into your package.json, using the same NAPI_VERSION value you provided to cmake + +```json +"binary": { + "napi_versions": [7] + }, +``` + #### Commandline With cmake-js installed as a depdendency or devDependency of your module, you can access run commands directly with: @@ -302,6 +310,13 @@ you need to make your package depend on it with: cmake-js will then add it to the include search path automatically +You should add the following to your package.json, with the correct version number, so that cmake-js knows the module is node-api and that it can skip downloading the nodejs headers + +```json +"binary": { + "napi_versions": [7] + }, +``` #### Electron From eb05966c8bda0a7514c545ba1309eafc98bfef49 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 30 Oct 2023 19:41:41 +0000 Subject: [PATCH 23/50] fix: update `node-api-headers` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 246ee9ae..1b67eb08 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "fs-extra": "^10.1.0", "lodash.isplainobject": "^4.0.6", "memory-stream": "^1.0.0", - "node-api-headers": "^0.0.2", + "node-api-headers": "^1.1.0", "npmlog": "^6.0.2", "rc": "^1.2.7", "semver": "^7.3.8", From d9c190bf2a4f0d60e39222bb1ddb7a4f88c28a49 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 30 Oct 2023 19:50:39 +0000 Subject: [PATCH 24/50] fix(windows): replace custom `libnode.def` with version provided by `node-api-headers` --- lib/cMake.js | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/lib/cMake.js b/lib/cMake.js index 03ac5ccf..b9feba55 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -287,31 +287,7 @@ CMake.prototype.getCmakeJsSrcString = function () { }; CMake.prototype.getNodeLibDefPath = function () { - return environment.isWin && this.options.isNodeApi ? path.join(this.options.out, 'node-lib.def') : undefined -} - -CMake.prototype._generateNodeLibDef = async function (targetFile) { - try { - // Compile a Set of all the symbols that could be exported - const allSymbols = new Set() - for (const ver of Object.values(headers.symbols)) { - for (const sym of ver.node_api_symbols) { - allSymbols.add(sym) - } - for (const sym of ver.js_native_api_symbols) { - allSymbols.add(sym) - } - } - - // Write a 'def' file for NODE.EXE - const allSymbolsArr = Array.from(allSymbols) - await fs.writeFile(targetFile, 'NAME NODE.EXE\nEXPORTS\n' + allSymbolsArr.join('\n')) - - return targetFile - } catch(e) { - // It most likely wasn't found - throw new Error(`Failed to generate def for node.lib`) - } + return environment.isWin && this.options.isNodeApi ? headers.def_paths.node_api_def : undefined } CMake.prototype.configure = async function () { @@ -338,12 +314,6 @@ CMake.prototype.configure = async function () { const cwd = process.cwd(); process.chdir(this.workDir); try { - const nodeLibDefPath = this.getNodeLibDefPath() - - if (environment.isWin && nodeLibDefPath) { - await this._generateNodeLibDef(nodeLibDefPath) - } - await this._run(command); } finally { From 789bfa7a82fa9f45ee969e22580bc78d6b8c5e07 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 30 Oct 2023 20:17:17 +0000 Subject: [PATCH 25/50] chore: update ci platforms --- .github/workflows/node.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 7faf9887..c6348df5 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -24,12 +24,12 @@ jobs: arch: x64 # - os: macos-11 # arch: arm64 - - os: macos-10.15 + - os: macos-11 arch: x64 # linux - os: ubuntu-22.04 arch: x64 - - os: ubuntu-18.04 + - os: ubuntu-20.04 arch: x64 # linux-libc - os: ubuntu-latest From 62f98e00f19d8293451babb623d07fb52ffa0cf6 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 15 Jan 2024 20:07:41 +0000 Subject: [PATCH 26/50] fix: update find-visualstudio #317 --- lib/import/README | 6 +- lib/import/find-visualstudio.js | 237 +++++++++++++++++--------------- lib/import/util.js | 67 +++++---- lib/toolset.js | 5 +- 4 files changed, 167 insertions(+), 148 deletions(-) diff --git a/lib/import/README b/lib/import/README index 9c246cdd..55d97301 100644 --- a/lib/import/README +++ b/lib/import/README @@ -1,6 +1,6 @@ -This is a copy of some files from node-gyp, with some minor modifications.a +This is a copy of some files from node-gyp, with some minor modifications. -Currently based on v9.0.0 with support for vs2013 removed +Currently based on v10.0.1 node-gyp has a decent strategy for finding Visual Studio, and has a lot more developer time behind them to make a good and robust solution. -We may as well benefit from their solution than pointlessly reinvent it +We may as well benefit from their solution than reinvent it diff --git a/lib/import/find-visualstudio.js b/lib/import/find-visualstudio.js index c040f0b3..f48a5ac6 100644 --- a/lib/import/find-visualstudio.js +++ b/lib/import/find-visualstudio.js @@ -1,38 +1,33 @@ 'use strict' const log = require('npmlog') -const execFile = require('child_process').execFile -const fs = require('fs') -const path = require('path').win32 -const logWithPrefix = require('./util').logWithPrefix -const regSearchKeys = require('./util').regSearchKeys - -function findVisualStudio (configMsvsVersion, callback) { - const finder = new VisualStudioFinder(configMsvsVersion, - callback) - finder.findVisualStudio() -} +const { existsSync } = require('fs') +const { win32: path } = require('path') +const { regSearchKeys, execFile, logWithPrefix } = require('./util') +const semver = require("semver"); -function VisualStudioFinder (configMsvsVersion, callback) { - this.configMsvsVersion = configMsvsVersion - this.callback = callback - this.errorLog = [] - this.validVersions = [] -} +class VisualStudioFinder { + static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio() -VisualStudioFinder.prototype = { - log: logWithPrefix(log, 'find VS'), + log = logWithPrefix(log, 'find VS') - regSearchKeys: regSearchKeys, + regSearchKeys = regSearchKeys + + constructor (nodeSemver, configMsvsVersion) { + this.nodeSemver = nodeSemver + this.configMsvsVersion = configMsvsVersion + this.errorLog = [] + this.validVersions = [] + } // Logs a message at verbose level, but also saves it to be displayed later // at error level if an error occurs. This should help diagnose the problem. - addLog: function addLog (message) { + addLog (message) { this.log.verbose(message) this.errorLog.push(message) - }, + } - findVisualStudio: function findVisualStudio () { + async findVisualStudio () { this.configVersionYear = null this.configPath = null if (this.configMsvsVersion) { @@ -59,28 +54,30 @@ VisualStudioFinder.prototype = { this.addLog('VCINSTALLDIR not set, not running in VS Command Prompt') } - this.findVisualStudio2017OrNewer((info) => { + const checks = [ + () => this.findVisualStudio2017OrNewer(), + () => this.findVisualStudio2015(), + () => this.findVisualStudio2013() + ] + + for (const check of checks) { + const info = await check() if (info) { return this.succeed(info) } - this.findVisualStudio2015((info) => { - if (info) { - return this.succeed(info) - } - - this.fail() - }) - }) - }, + } + + return this.fail() + } - succeed: function succeed (info) { + succeed (info) { this.log.info(`using VS${info.versionYear} (${info.version}) found at:` + `\n"${info.path}"` + '\nrun with --verbose for detailed information') - process.nextTick(this.callback.bind(null, null, info)) - }, + return info + } - fail: function fail () { + fail () { if (this.configMsvsVersion && this.envVcInstallDir) { this.errorLog.push( 'msvs_version does not match this VS Command Prompt or the', @@ -114,17 +111,16 @@ VisualStudioFinder.prototype = { ].join('\n') this.log.error(`\n${errorLog}\n\n${infoLog}\n`) - process.nextTick(this.callback.bind(null, new Error( - 'Could not find any Visual Studio installation to use'))) - }, + throw new Error('Could not find any Visual Studio installation to use') + } // Invoke the PowerShell script to get information about Visual Studio 2017 // or newer installations - findVisualStudio2017OrNewer: function findVisualStudio2017OrNewer (cb) { - var ps = path.join(process.env.SystemRoot, 'System32', + async findVisualStudio2017OrNewer () { + const ps = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe') - var csFile = path.join(__dirname, 'Find-VisualStudio.cs') - var psArgs = [ + const csFile = path.join(__dirname, 'Find-VisualStudio.cs') + const psArgs = [ '-ExecutionPolicy', 'Unrestricted', '-NoProfile', @@ -133,22 +129,19 @@ VisualStudioFinder.prototype = { ] this.log.silly('Running', ps, psArgs) - var child = execFile(ps, psArgs, { encoding: 'utf8' }, - (err, stdout, stderr) => { - this.parseData(err, stdout, stderr, cb) - }) - child.stdin.end() - }, + const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' }) + return this.parseData(err, stdout, stderr) + } // Parse the output of the PowerShell script and look for an installation // of Visual Studio 2017 or newer to use - parseData: function parseData (err, stdout, stderr, cb) { + parseData (err, stdout, stderr) { this.log.silly('PS stderr = %j', stderr) const failPowershell = () => { this.addLog( 'could not use PowerShell to find Visual Studio 2017 or newer, try re-running with \'--loglevel silly\' for more details') - cb(null) + return null } if (err) { @@ -156,9 +149,9 @@ VisualStudioFinder.prototype = { return failPowershell() } - var vsInfo + let vsInfo try { - vsInfo = JSON.parse(stdout) + vsInfo = JSON.parse(stdout) } catch (e) { this.log.silly('PS stdout = %j', stdout) this.log.silly(e) @@ -173,7 +166,7 @@ VisualStudioFinder.prototype = { vsInfo = vsInfo.map((info) => { this.log.silly(`processing installation: "${info.path}"`) info.path = path.resolve(info.path) - var ret = this.getVersionInfo(info) + const ret = this.getVersionInfo(info) ret.path = info.path ret.msBuild = this.getMSBuild(info, ret.versionYear) ret.toolset = this.getToolset(info, ret.versionYear) @@ -194,7 +187,7 @@ VisualStudioFinder.prototype = { // Sort to place newer versions first vsInfo.sort((a, b) => b.versionYear - a.versionYear) - for (var i = 0; i < vsInfo.length; ++i) { + for (let i = 0; i < vsInfo.length; ++i) { const info = vsInfo[i] this.addLog(`checking VS${info.versionYear} (${info.version}) found ` + `at:\n"${info.path}"`) @@ -224,23 +217,23 @@ VisualStudioFinder.prototype = { continue } - return cb(info) + return info } this.addLog( 'could not find a version of Visual Studio 2017 or newer to use') - cb(null) - }, + return null + } // Helper - process version information - getVersionInfo: function getVersionInfo (info) { + getVersionInfo (info) { const match = /^(\d+)\.(\d+)\..*/.exec(info.version) if (!match) { this.log.silly('- failed to parse version:', info.version) return {} } this.log.silly('- version match = %j', match) - var ret = { + const ret = { version: info.version, versionMajor: parseInt(match[1], 10), versionMinor: parseInt(match[2], 10) @@ -259,12 +252,17 @@ VisualStudioFinder.prototype = { } this.log.silly('- unsupported version:', ret.versionMajor) return {} - }, + } + + msBuildPathExists (path) { + return existsSync(path) + } // Helper - process MSBuild information - getMSBuild: function getMSBuild (info, versionYear) { + getMSBuild (info, versionYear) { const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base' const msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') + const msbuildPathArm64 = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'arm64', 'MSBuild.exe') if (info.packages.indexOf(pkg) !== -1) { this.log.silly('- found VC.MSBuild.Base') if (versionYear === 2017) { @@ -274,15 +272,21 @@ VisualStudioFinder.prototype = { return msbuildPath } } - // visual studio 2022 don't has msbuild pkg - if (fs.existsSync(msbuildPath)) { + /** + * Visual Studio 2022 doesn't have the MSBuild package. + * Support for compiling _on_ ARM64 was added in MSVC 14.32.31326, + * so let's leverage it if the user has an ARM64 device. + */ + if (process.arch === 'arm64' && this.msBuildPathExists(msbuildPathArm64)) { + return msbuildPathArm64 + } else if (this.msBuildPathExists(msbuildPath)) { return msbuildPath } return null - }, + } // Helper - process toolset information - getToolset: function getToolset (info, versionYear) { + getToolset (info, versionYear) { const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' const express = 'Microsoft.VisualStudio.WDExpress' @@ -303,15 +307,15 @@ VisualStudioFinder.prototype = { } this.log.silly('- invalid versionYear:', versionYear) return null - }, + } // Helper - process Windows SDK information - getSDK: function getSDK (info) { + getSDK (info) { const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK' const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.' const win11SDKPrefix = 'Microsoft.VisualStudio.Component.Windows11SDK.' - var Win10or11SDKVer = 0 + let Win10or11SDKVer = 0 info.packages.forEach((pkg) => { if (!pkg.startsWith(win10SDKPrefix) && !pkg.startsWith(win11SDKPrefix)) { return @@ -338,14 +342,14 @@ VisualStudioFinder.prototype = { return '8.1' } return null - }, + } // Find an installation of Visual Studio 2015 to use - findVisualStudio2015: function findVisualStudio2015(cb) { - if (this.nodeSemver.major >= 19) { + async findVisualStudio2015 () { + if (semver.gte(this.nodeSemver, "19.0.0")) { this.addLog( 'not looking for VS2015 as it is only supported up to Node.js 18') - return cb(null) + return null } return this.findOldVS({ version: '14.0', @@ -353,55 +357,68 @@ VisualStudioFinder.prototype = { versionMinor: 0, versionYear: 2015, toolset: 'v140' - }, cb) - }, + }) + } + + // Find an installation of Visual Studio 2013 to use + async findVisualStudio2013 () { + if (semver.gte(this.nodeSemver, "9.0.0")) { + this.addLog( + 'not looking for VS2013 as it is only supported up to Node.js 8') + return null + } + return this.findOldVS({ + version: '12.0', + versionMajor: 12, + versionMinor: 0, + versionYear: 2013, + toolset: 'v120' + }) + } // Helper - common code for VS2013 and VS2015 - findOldVS: function findOldVS (info, cb) { + async findOldVS (info) { const regVC7 = ['HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7', 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7'] const regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions' this.addLog(`looking for Visual Studio ${info.versionYear}`) - this.regSearchKeys(regVC7, info.version, [], (err, res) => { - if (err) { - this.addLog('- not found') - return cb(null) - } - + try { + let res = await this.regSearchKeys(regVC7, info.version, []) const vsPath = path.resolve(res, '..') this.addLog(`- found in "${vsPath}"`) - const msBuildRegOpts = process.arch === 'ia32' ? [] : ['/reg:32'] - this.regSearchKeys([`${regMSBuild}\\${info.version}`], - 'MSBuildToolsPath', msBuildRegOpts, (err, res) => { - if (err) { - this.addLog( - '- could not find MSBuild in registry for this version') - return cb(null) - } - - const msBuild = path.join(res, 'MSBuild.exe') - this.addLog(`- MSBuild in "${msBuild}"`) - - if (!this.checkConfigVersion(info.versionYear, vsPath)) { - return cb(null) - } - - info.path = vsPath - info.msBuild = msBuild - info.sdk = null - cb(info) - }) - }) - }, + + try { + res = await this.regSearchKeys([`${regMSBuild}\\${info.version}`], 'MSBuildToolsPath', msBuildRegOpts) + } catch (err) { + this.addLog('- could not find MSBuild in registry for this version') + return null + } + + const msBuild = path.join(res, 'MSBuild.exe') + this.addLog(`- MSBuild in "${msBuild}"`) + + if (!this.checkConfigVersion(info.versionYear, vsPath)) { + return null + } + + info.path = vsPath + info.msBuild = msBuild + info.sdk = null + return info + } catch (err) { + this.addLog('- not found') + return null + } + } // After finding a usable version of Visual Studio: // - add it to validVersions to be displayed at the end if a specific // version was requested and not found; // - check if this is the version that was requested. // - check if this matches the Visual Studio Command Prompt - checkConfigVersion: function checkConfigVersion (versionYear, vsPath) { + checkConfigVersion (versionYear, vsPath) { this.validVersions.push(versionYear) this.validVersions.push(vsPath) @@ -424,8 +441,4 @@ VisualStudioFinder.prototype = { } } -module.exports = findVisualStudio -module.exports.test = { - VisualStudioFinder: VisualStudioFinder, - findVisualStudio: findVisualStudio -} \ No newline at end of file +module.exports = VisualStudioFinder \ No newline at end of file diff --git a/lib/import/util.js b/lib/import/util.js index 9e78a819..cf7cb9fe 100644 --- a/lib/import/util.js +++ b/lib/import/util.js @@ -1,9 +1,16 @@ 'use strict' const log = require('npmlog') -const execFile = require('child_process').execFile +const cp = require('child_process') const path = require('path') + +const execFile = async (...args) => new Promise((resolve) => { + const child = cp.execFile(...args, (...a) => resolve(a)) + child.stdin.end() +}) + + function logWithPrefix (log, prefix) { function setPrefix (logFunction) { return (...args) => logFunction.apply(null, [ prefix, ...args ]) // eslint-disable-line @@ -17,48 +24,48 @@ function logWithPrefix (log, prefix) { } } -function regGetValue (key, value, addOpts, cb) { +async function regGetValue (key, value, addOpts) { const outReValue = value.replace(/\W/g, '.') const outRe = new RegExp(`^\\s+${outReValue}\\s+REG_\\w+\\s+(\\S.*)$`, 'im') const reg = path.join(process.env.SystemRoot, 'System32', 'reg.exe') const regArgs = ['query', key, '/v', value].concat(addOpts) log.silly('reg', 'running', reg, regArgs) - const child = execFile(reg, regArgs, { encoding: 'utf8' }, - function (err, stdout, stderr) { - log.silly('reg', 'reg.exe stdout = %j', stdout) - if (err || stderr.trim() !== '') { - log.silly('reg', 'reg.exe err = %j', err && (err.stack || err)) - log.silly('reg', 'reg.exe stderr = %j', stderr) - return cb(err, stderr) - } + const [err, stdout, stderr] = await execFile(reg, regArgs, { encoding: 'utf8' }) - const result = outRe.exec(stdout) - if (!result) { - log.silly('reg', 'error parsing stdout') - return cb(new Error('Could not parse output of reg.exe')) - } - log.silly('reg', 'found: %j', result[1]) - cb(null, result[1]) - }) - child.stdin.end() + log.silly('reg', 'reg.exe stdout = %j', stdout) + if (err || stderr.trim() !== '') { + log.silly('reg', 'reg.exe err = %j', err && (err.stack || err)) + log.silly('reg', 'reg.exe stderr = %j', stderr) + if (err) { + throw err + } + throw new Error(stderr) + } + + const result = outRe.exec(stdout) + if (!result) { + log.silly('reg', 'error parsing stdout') + throw new Error('Could not parse output of reg.exe') + } + + log.silly('reg', 'found: %j', result[1]) + return result[1] } -function regSearchKeys (keys, value, addOpts, cb) { - var i = 0 - const search = () => { - log.silly('reg-search', 'looking for %j in %j', value, keys[i]) - regGetValue(keys[i], value, addOpts, (err, res) => { - ++i - if (err && i < keys.length) { return search() } - cb(err, res) - }) +async function regSearchKeys (keys, value, addOpts) { + for (const key of keys) { + try { + return await regGetValue(key, value, addOpts) + } catch { + continue + } } - search() } module.exports = { logWithPrefix: logWithPrefix, regGetValue: regGetValue, - regSearchKeys: regSearchKeys + regSearchKeys: regSearchKeys, + execFile: execFile } \ No newline at end of file diff --git a/lib/toolset.js b/lib/toolset.js index 9156feb5..c6cbad42 100644 --- a/lib/toolset.js +++ b/lib/toolset.js @@ -4,9 +4,8 @@ const environment = require("./environment"); const assert = require("assert"); const CMLog = require("./cmLog"); const util = require('util') -const findVisualStudioRaw = require('./import/find-visualstudio'); +const { findVisualStudio } = environment.isWin ? require('./import/find-visualstudio') : {} -const findVisualStudio = util.promisify(findVisualStudioRaw) function Toolset(options) { this.options = options || {}; @@ -195,7 +194,7 @@ Toolset.prototype._getTopSupportedVisualStudioGenerator = async function () { const CMake = require("./cMake"); assert(environment.isWin); - const selectedVs = await findVisualStudio(this.options.msvsVersion) + const selectedVs = await findVisualStudio(environment.runtimeVersion, this.options.msvsVersion) if (!selectedVs) return null; const list = await CMake.getGenerators(this.options, this.log); From 8e3905003d6e84af1218246cc8af8bc9cf815132 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 15 Jan 2024 20:08:04 +0000 Subject: [PATCH 27/50] chore: enable tests for pull_requests --- .github/workflows/node.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index c6348df5..5e19bc6f 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -2,6 +2,7 @@ name: Test on: push: + pull_request: jobs: test: From eee2b4d6b7e78d3d32afc985e34f6207346bc49a Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 15 Jan 2024 20:16:33 +0000 Subject: [PATCH 28/50] chore: add some more examples to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c759983e..f972e36e 100644 --- a/README.md +++ b/README.md @@ -385,6 +385,8 @@ build environment. ## Real examples * [@julusian/jpeg-turbo](https://github.com/julusian/node-jpeg-turbo) - A Node-API wrapping around libjpeg-turbo. cmake-js was a good fit here, as libjpeg-turbo provides cmake files that can be used, and would be hard to replicate correctly in node-gyp +* [node-datachannel](https://github.com/murat-dogan/node-datachannel) - Easy to use WebRTC data channels and media transport +* [aws-iot-device-sdk-v2](https://github.com/aws/aws-iot-device-sdk-js-v2) AWS IoT Device SDK for JavaScript v2 Open a PR to add your own project here. From f3169e835c0f6839b8d00feff0bd7adef2a548ed Mon Sep 17 00:00:00 2001 From: Andreas Franek Date: Mon, 15 Jan 2024 21:30:18 +0100 Subject: [PATCH 29/50] fix(windows): always remove `Path` if `PATH` is also defined (#319) Co-authored-by: Andreas Franek --- lib/processHelpers.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/processHelpers.js b/lib/processHelpers.js index eb9ae574..d039418a 100644 --- a/lib/processHelpers.js +++ b/lib/processHelpers.js @@ -8,8 +8,10 @@ const processHelpers = { return new Promise(function (resolve, reject) { const env = Object.assign({}, process.env); - if (env.Path && env.PATH && env.Path !== env.PATH) { - env.PATH = env.Path + ';' + env.PATH; + if (env.Path && env.PATH) { + if(env.Path !== env.PATH) { + env.PATH = env.Path + ';' + env.PATH; + } delete env.Path; } const child = spawn(command[0], command.slice(1), { From 30e9ae5d96a51e4dbd01fb01be3602b1639fdcec Mon Sep 17 00:00:00 2001 From: Matthew Bramer <515152+iOnline247@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:31:32 -0500 Subject: [PATCH 30/50] chore: fix typo in README.md (#316) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f972e36e..c92070ac 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## About CMake.js is a Node.js native addon build tool which works (almost) *exactly* like [node-gyp](https://github.com/TooTallNate/node-gyp), but instead of [gyp](http://en.wikipedia.org/wiki/GYP_%28software%29), it is based on [CMake](http://cmake.org) build system. It's compatible with the following runtimes: -- Node.js 14.15+ since CMake.js v7.0.0 (for older runtimes please use an earlier version of CMake.js). Newer versions can produce builds targetting older runtimes +- Node.js 14.15+ since CMake.js v7.0.0 (for older runtimes please use an earlier version of CMake.js). Newer versions can produce builds targeting older runtimes - [NW.js](https://github.com/nwjs/nw.js): all CMake.js based native modules are compatible with NW.js out-of-the-box, there is no [nw-gyp like magic](https://github.com/nwjs/nw.js/wiki/Using-Node-modules#3rd-party-modules-with-cc-addons) required - [Electron](https://github.com/electron/electron): out-of-the-box build support, [no post build steps required](https://github.com/electron/electron/blob/main/docs/tutorial/using-native-node-modules.md) From b3d4cc6eb198745e8e45a6ad03d99383f62564df Mon Sep 17 00:00:00 2001 From: Rasmus Eneman Date: Mon, 15 Jan 2024 21:48:18 +0100 Subject: [PATCH 31/50] fix: Cmake arguments got converted to numbers (#314) --- bin/cmake-js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/cmake-js b/bin/cmake-js index 55df9fa4..0b2afdac 100755 --- a/bin/cmake-js +++ b/bin/cmake-js @@ -197,9 +197,14 @@ log.verbose("CON", "Parsing arguments"); // Extract custom cMake options const customOptions = {}; -for (const [key, value] of Object.entries(argv)) { - if (value && key.startsWith("CD")) { - customOptions[key.substr(2)] = value; +for (const arg of process.argv) { + if (arg.startsWith("--CD")) { + const separator = arg.indexOf('='); + if (separator < 5) continue; + const key = arg.substring(4, separator); + const value = arg.substring(separator + 1); + if (!value) continue; + customOptions[key] = value; } } From eee8351ca320393ba030bb49b1e4ba4300bc0872 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 15 Jan 2024 21:03:54 +0000 Subject: [PATCH 32/50] chore: update dependencies --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 1b67eb08..2046c947 100644 --- a/package.json +++ b/package.json @@ -41,24 +41,24 @@ "node": ">= 14.15.0" }, "dependencies": { - "axios": "^1.3.2", + "axios": "^1.6.5", "debug": "^4", - "fs-extra": "^10.1.0", + "fs-extra": "^11.2.0", "lodash.isplainobject": "^4.0.6", "memory-stream": "^1.0.0", "node-api-headers": "^1.1.0", "npmlog": "^6.0.2", "rc": "^1.2.7", - "semver": "^7.3.8", - "tar": "^6.1.11", + "semver": "^7.5.4", + "tar": "^6.2.0", "url-join": "^4.0.1", "which": "^2.0.2", - "yargs": "^17.6.0" + "yargs": "^17.7.2" }, "devDependencies": { "mocha": "*", - "nan": "^2.16.0", - "node-addon-api": "^6.0.0" + "nan": "^2.18.0", + "node-addon-api": "^6.1.0" }, "scripts": { "test": "mocha tests", From b4b5b160ee6cd7b0b3756709c0590df490d2815d Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 15 Jan 2024 21:09:47 +0000 Subject: [PATCH 33/50] v7.3.0 --- changelog.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 838ab8e2..fedf7d8a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,13 @@ +v7.3.0 - 15/01/23 +========== + +- feat(windows): replace custom libnode.def generation with version from node-api-headers +- fix: support for vs2015 with nodejs 18 and older (#317) +- fix(windows): always remove Path if PATH is also defined (#319) +- fix: Cmake arguments got converted to numbers (#314) +- fix: update node-api-headers +- chore: update dependencies + v7.2.1 - 14/02/23 ========== diff --git a/package.json b/package.json index 2046c947..6f69f0de 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node-addon-api" ], "main": "lib", - "version": "7.2.1", + "version": "7.3.0", "author": "Gábor Mező aka unbornchikken", "maintainers": [ { From 66fc7b575f65254bbb50f4baa6437ea246ed1e2c Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Tue, 16 Jan 2024 22:17:09 +0000 Subject: [PATCH 34/50] chore: reformat with prettier and fix eslint --- .eslintrc.json | 30 +- .prettierrc.json | 9 + README.md | 77 +-- bin/cmake-js | 552 ++++++++-------- changelog.md | 497 +++++++-------- lib/appCMakeJSConfig.js | 112 ++-- lib/buildSystem.js | 203 +++--- lib/cMake.js | 678 ++++++++++---------- lib/cmLog.js | 121 ++-- lib/dist.js | 346 +++++------ lib/downloader.js | 158 ++--- lib/environment.js | 204 +++--- lib/import/find-visualstudio.js | 863 +++++++++++++------------- lib/import/util.js | 105 ++-- lib/index.js | 20 +- lib/locateNAN.js | 108 ++-- lib/locateNodeApi.js | 32 +- lib/npmConfig.js | 46 +- lib/processHelpers.js | 100 ++- lib/runtimePaths.js | 169 ++--- lib/targetOptions.js | 70 +-- lib/toolset.js | 427 +++++++------ package.json | 151 ++--- tests/es6/buildSystem.js | 121 ++-- tests/es6/dist.js | 41 +- tests/es6/index.js | 8 +- tests/es6/locateNAN.js | 50 +- tests/es6/prototype-napi/package.json | 8 +- tests/es6/testCases.js | 158 +++-- tests/es6/testRunner.js | 192 +++--- tests/fixtures/project/package.json | 1 + tests/index.js | 2 +- 32 files changed, 2800 insertions(+), 2859 deletions(-) create mode 100644 .prettierrc.json diff --git a/.eslintrc.json b/.eslintrc.json index af263b4e..bdfb5bb5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,11 +1,21 @@ { - "parserOptions": { - "ecmaVersion": 2019, - "sourceType": "module" - }, - "rules": { - "semi": 2, - "no-var": 2, - "prefer-const": 2 - } -} \ No newline at end of file + "extends": ["eslint:recommended", "prettier"], + "parserOptions": { + "ecmaVersion": 2023, + "sourceType": "commonjs" + }, + "rules": {}, + "env": { + "node": true, + "es6": true, + "es2023": true + }, + "overrides": [ + { + "files": "tests/**/*.js", + "env": { + "mocha": true + } + } + ] +} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..1687e196 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "printWidth": 120, + "semi": false, + "singleQuote": true, + "useTabs": true, + "endOfLine": "lf" +} diff --git a/README.md b/README.md index c92070ac..01c0d48e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ [![npm](https://img.shields.io/npm/v/cmake-js)](https://www.npmjs.com/package/cmake-js) ## About -CMake.js is a Node.js native addon build tool which works (almost) *exactly* like [node-gyp](https://github.com/TooTallNate/node-gyp), but instead of [gyp](http://en.wikipedia.org/wiki/GYP_%28software%29), it is based on [CMake](http://cmake.org) build system. It's compatible with the following runtimes: + +CMake.js is a Node.js native addon build tool which works (almost) _exactly_ like [node-gyp](https://github.com/TooTallNate/node-gyp), but instead of [gyp](http://en.wikipedia.org/wiki/GYP_%28software%29), it is based on [CMake](http://cmake.org) build system. It's compatible with the following runtimes: - Node.js 14.15+ since CMake.js v7.0.0 (for older runtimes please use an earlier version of CMake.js). Newer versions can produce builds targeting older runtimes - [NW.js](https://github.com/nwjs/nw.js): all CMake.js based native modules are compatible with NW.js out-of-the-box, there is no [nw-gyp like magic](https://github.com/nwjs/nw.js/wiki/Using-Node-modules#3rd-party-modules-with-cc-addons) required @@ -85,13 +86,13 @@ Options: - [CMake](http://www.cmake.org/download/) - A proper C/C++ compiler toolchain of the given platform - - **Windows**: - - [Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/). If you installed nodejs with the installer, you can install these when prompted. - - An alternate way is to install the [Chocolatey package manager](https://chocolatey.org/install), and run `choco install visualstudio2017-workload-vctools` in an Administrator Powershell - - If you have multiple versions installed, you can select a specific version with `npm config set msvs_version 2017` (Note: this will also affect `node-gyp`) - - **Unix/Posix**: - - Clang or GCC - - Ninja or Make (Ninja will be picked if both present) + - **Windows**: + - [Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/). If you installed nodejs with the installer, you can install these when prompted. + - An alternate way is to install the [Chocolatey package manager](https://chocolatey.org/install), and run `choco install visualstudio2017-workload-vctools` in an Administrator Powershell + - If you have multiple versions installed, you can select a specific version with `npm config set msvs_version 2017` (Note: this will also affect `node-gyp`) + - **Unix/Posix**: + - Clang or GCC + - Ninja or Make (Ninja will be picked if both present) ## Usage @@ -99,7 +100,7 @@ Options: It is advised to use Node-API for new projects instead of NAN. It provides ABI stability making usage simpler and reducing maintainance. -In a nutshell. *(For more complete documentation please see [the first tutorial](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-01-Creating-a-native-module-by-using-CMake.js-and-NAN).)* +In a nutshell. _(For more complete documentation please see [the first tutorial](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-01-Creating-a-native-module-by-using-CMake.js-and-NAN).)_ - Install cmake-js for your module `npm install --save cmake-js` - Put a CMakeLists.txt file into your module root with this minimal required content: @@ -228,6 +229,7 @@ You can add custom CMake options by beginning option name with `CD`. #### Example In command prompt: + ``` cmake-js compile --CDFOO="bar" ``` @@ -258,27 +260,26 @@ You can configure runtimes for compiling target for all depending CMake.js modul ```json { - "name": "ta-taram-taram", - "description": "pa-param-pam-pam", - "version": "1.0.0", - "main": "app.js", - "cmake-js": { - "runtime": "node", - "runtimeVersion": "0.12.0", - "arch": "ia32" - } + "name": "ta-taram-taram", + "description": "pa-param-pam-pam", + "version": "1.0.0", + "main": "app.js", + "cmake-js": { + "runtime": "node", + "runtimeVersion": "0.12.0", + "arch": "ia32" + } } ``` Available settings: - **runtime**: application's target runtime, possible values are: - - `node`: Node.js - - `nw`: nw.js - - `electron`: Electron + - `node`: Node.js + - `nw`: nw.js + - `electron`: Electron - **runtimeVersion**: version of the application's target runtime, for example: `0.12.1` -- **arch**: architecture of application's target runtime (eg: `x64`, `ia32`, `arm64`, `arm`). *Notice: on non-Windows systems the C++ toolset's architecture's gonna be used despite this setting. If you don't specify this on Windows, then architecture of the main node runtime is gonna be used, so you have to choose a matching nw.js runtime.* - +- **arch**: architecture of application's target runtime (eg: `x64`, `ia32`, `arm64`, `arm`). _Notice: on non-Windows systems the C++ toolset's architecture's gonna be used despite this setting. If you don't specify this on Windows, then architecture of the main node runtime is gonna be used, so you have to choose a matching nw.js runtime._ #### Node-API and `node-addon-api` @@ -287,13 +288,14 @@ Available settings: which was previously known as N-API, supplies a set of C APIs that allow to compilation and loading of native modules by different versions of Node.js that support Node-API which includes -all versions of Node.js v10.x and later. +all versions of Node.js v10.x and later. To compile a native module that uses only the [plain `C` Node-API calls](https://nodejs.org/api/n-api.html#n_api_node_api), follow the directions for plain `node` native modules. You must also add the following lines to your CMakeLists.txt, to allow for building on windows + ``` if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) # Generate node.lib @@ -339,18 +341,18 @@ To make compatible your NW.js application with any NAN CMake.js based modules, w ```json { - "cmake-js": { - "runtime": "nw", - "runtimeVersion": "nw.js-version-here", - "arch": "whatever-setting-is-appropriate-for-your-application's-windows-build" - } + "cmake-js": { + "runtime": "nw", + "runtimeVersion": "nw.js-version-here", + "arch": "whatever-setting-is-appropriate-for-your-application's-windows-build" + } } ``` That's it. There is nothing else to do either on the application's or on the module's side, CMake.js modules are compatible with NW.js out-of-the-box. For more complete documentation please see [the third tutorial](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-03-Using-CMake.js-based-native-modules-with-nw.js). - #### Heroku + [Heroku](https://heroku.com) uses the concept of a [buildpack](https://devcenter.heroku.com/articles/buildpacks) to define how an application should be prepared to run in a [dyno](https://devcenter.heroku.com/articles/dynos). The typical buildpack for note-based applications, @@ -361,20 +363,19 @@ but not [CMake](http://cmake.org). The least "painful" way of addressing this is to use heroku's multipack facility: - Set the applications' buildpack to -[https://github.com/heroku/heroku-buildpack-multi.git](https://github.com/heroku/heroku-buildpack-multi.git) + [https://github.com/heroku/heroku-buildpack-multi.git](https://github.com/heroku/heroku-buildpack-multi.git) - In the root directory of the application, -create a file called `.buildpacks` with these two lines: + create a file called `.buildpacks` with these two lines: - https://github.com/brave/heroku-cmake-buildpack.git - https://github.com/heroku/heroku-buildpack-nodejs.git + https://github.com/brave/heroku-cmake-buildpack.git + https://github.com/heroku/heroku-buildpack-nodejs.git - Deploy the application to have the changes take effect The `heroku-buildpack-multi` will run each buildpack in order allowing the node application to reference CMake in the Heroku build environment. - ## Tutorials - [TUTORIAL 01 Creating a native module by using CMake.js and NAN](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-01-Creating-a-native-module-by-using-CMake.js-and-NAN) @@ -384,9 +385,9 @@ build environment. ## Real examples -* [@julusian/jpeg-turbo](https://github.com/julusian/node-jpeg-turbo) - A Node-API wrapping around libjpeg-turbo. cmake-js was a good fit here, as libjpeg-turbo provides cmake files that can be used, and would be hard to replicate correctly in node-gyp -* [node-datachannel](https://github.com/murat-dogan/node-datachannel) - Easy to use WebRTC data channels and media transport -* [aws-iot-device-sdk-v2](https://github.com/aws/aws-iot-device-sdk-js-v2) AWS IoT Device SDK for JavaScript v2 +- [@julusian/jpeg-turbo](https://github.com/julusian/node-jpeg-turbo) - A Node-API wrapping around libjpeg-turbo. cmake-js was a good fit here, as libjpeg-turbo provides cmake files that can be used, and would be hard to replicate correctly in node-gyp +- [node-datachannel](https://github.com/murat-dogan/node-datachannel) - Easy to use WebRTC data channels and media transport +- [aws-iot-device-sdk-v2](https://github.com/aws/aws-iot-device-sdk-js-v2) AWS IoT Device SDK for JavaScript v2 Open a PR to add your own project here. diff --git a/bin/cmake-js b/bin/cmake-js index 0b2afdac..fdea4c5c 100755 --- a/bin/cmake-js +++ b/bin/cmake-js @@ -1,339 +1,343 @@ #!/usr/bin/env node -"use strict"; +'use strict' -const log = require("npmlog"); -const BuildSystem = require("../").BuildSystem; -const util = require("util"); -const version = require("../package").version; -const logLevels = ["silly", "verbose", "info", "http", "warn", "error"]; +const log = require('npmlog') +const BuildSystem = require('../').BuildSystem +const util = require('util') +const version = require('../package').version +const logLevels = ['silly', 'verbose', 'info', 'http', 'warn', 'error'] -const npmConfigData = require("rc")("npm"); +const npmConfigData = require('rc')('npm') for (const [key, value] of Object.entries(npmConfigData)) { - if (key.startsWith("cmake_js_")) { - const option = key.substr(9); - if (option.length === 1) { - process.argv.push("-" + option); - } - else { - process.argv.push("--" + option); - } - if (value) { - process.argv.push(value); - } - } + if (key.startsWith('cmake_js_')) { + const option = key.substr(9) + if (option.length === 1) { + process.argv.push('-' + option) + } else { + process.argv.push('--' + option) + } + if (value) { + process.argv.push(value) + } + } } -const yargs = require("yargs") - .usage("CMake.js " + version + "\n\nUsage: $0 [] [options]") - .version(version) - .command("install", "Install Node.js distribution files if needed") - .command("configure", "Configure CMake project") - .command("print-configure", "Print the configuration command") - .command("print-cmakejs-src", "Print the value of the CMAKE_JS_SRC variable") - .command("print-cmakejs-include", "Print the value of the CMAKE_JS_INC variable") - .command("print-cmakejs-lib", "Print the value of the CMAKE_JS_LIB variable") - .command("build", "Build the project (will configure first if required)") - .command("print-build", "Print the build command") - .command("clean", "Clean the project directory") - .command("print-clean", "Print the clean command") - .command("reconfigure", "Clean the project directory then configure the project") - .command("rebuild", "Clean the project directory then build the project") - .command("compile", "Build the project, and if build fails, try a full rebuild") - .options({ - h: { - alias: "help", - demand: false, - describe: "show this screen", - type: "boolean" - }, - l: { - alias: "log-level", - demand: false, - describe: "set log level (" + logLevels.join(", ") + "), default is info", - type: "string" - }, - d: { - alias: "directory", - demand: false, - describe: "specify CMake project's directory (where CMakeLists.txt located)", - type: "string" - }, - D: { - alias: "debug", - demand: false, - describe: "build debug configuration", - type: "boolean" - }, - B: { - alias: "config", - demand: false, - describe: "specify build configuration (Debug, RelWithDebInfo, Release), will ignore '--debug' if specified", - type: "string" - }, - c: { - alias: "cmake-path", - demand: false, - describe: "path of CMake executable", - type: "string" - }, - m: { - alias: "prefer-make", - demand: false, - describe: "use Unix Makefiles even if Ninja is available (Posix)", - type: "boolean" - }, - x: { - alias: "prefer-xcode", - demand: false, - describe: "use Xcode instead of Unix Makefiles", - type: "boolean" - }, - g: { - alias: "prefer-gnu", - demand: false, - describe: "use GNU compiler instead of default CMake compiler, if available (Posix)", - type: "boolean" - }, - G: { - alias: "generator", - demand: false, - describe: "use specified generator", - type: "string" - }, - t: { - alias: "toolset", - demand: false, - describe: "use specified toolset", - type: "string" - }, - A: { - alias: "platform", - demand: false, - describe: "use specified platform name", - type: "string" - }, - T: { - alias: "target", - demand: false, - describe: "only build the specified target", - type: "string" - }, - C: { - alias: "prefer-clang", - demand: false, - describe: "use Clang compiler instead of default CMake compiler, if available (Posix)", - type: "boolean" - }, - cc: { - demand: false, - describe: "use the specified C compiler", - type: "string" - }, - cxx: { - demand: false, - describe: "use the specified C++ compiler", - type: "string" - }, - r: { - alias: "runtime", - demand: false, - describe: "the runtime to use", - type: "string" - }, - v: { - alias: "runtime-version", - demand: false, - describe: "the runtime version to use", - type: "string" - }, - a: { - alias: "arch", - demand: false, - describe: "the architecture to build in", - type: "string" - }, - p: { - alias: "parallel", - demand: false, - describe: "the number of threads cmake can use", - type: "number" - }, - "CD": { - demand: false, - describe: "Custom argument passed to CMake in format: -D", - type: "string" - }, - i: { - alias: "silent", - describe: "Prevents CMake.js to print to the stdio", - type: "boolean" - }, - O: { - alias: "out", - describe: "Specify the output directory to compile to, default is projectRoot/build", - type: "string" - } - }); -const argv = yargs.argv; +const yargs = require('yargs') + .usage('CMake.js ' + version + '\n\nUsage: $0 [] [options]') + .version(version) + .command('install', 'Install Node.js distribution files if needed') + .command('configure', 'Configure CMake project') + .command('print-configure', 'Print the configuration command') + .command('print-cmakejs-src', 'Print the value of the CMAKE_JS_SRC variable') + .command('print-cmakejs-include', 'Print the value of the CMAKE_JS_INC variable') + .command('print-cmakejs-lib', 'Print the value of the CMAKE_JS_LIB variable') + .command('build', 'Build the project (will configure first if required)') + .command('print-build', 'Print the build command') + .command('clean', 'Clean the project directory') + .command('print-clean', 'Print the clean command') + .command('reconfigure', 'Clean the project directory then configure the project') + .command('rebuild', 'Clean the project directory then build the project') + .command('compile', 'Build the project, and if build fails, try a full rebuild') + .options({ + h: { + alias: 'help', + demand: false, + describe: 'show this screen', + type: 'boolean', + }, + l: { + alias: 'log-level', + demand: false, + describe: 'set log level (' + logLevels.join(', ') + '), default is info', + type: 'string', + }, + d: { + alias: 'directory', + demand: false, + describe: "specify CMake project's directory (where CMakeLists.txt located)", + type: 'string', + }, + D: { + alias: 'debug', + demand: false, + describe: 'build debug configuration', + type: 'boolean', + }, + B: { + alias: 'config', + demand: false, + describe: "specify build configuration (Debug, RelWithDebInfo, Release), will ignore '--debug' if specified", + type: 'string', + }, + c: { + alias: 'cmake-path', + demand: false, + describe: 'path of CMake executable', + type: 'string', + }, + m: { + alias: 'prefer-make', + demand: false, + describe: 'use Unix Makefiles even if Ninja is available (Posix)', + type: 'boolean', + }, + x: { + alias: 'prefer-xcode', + demand: false, + describe: 'use Xcode instead of Unix Makefiles', + type: 'boolean', + }, + g: { + alias: 'prefer-gnu', + demand: false, + describe: 'use GNU compiler instead of default CMake compiler, if available (Posix)', + type: 'boolean', + }, + G: { + alias: 'generator', + demand: false, + describe: 'use specified generator', + type: 'string', + }, + t: { + alias: 'toolset', + demand: false, + describe: 'use specified toolset', + type: 'string', + }, + A: { + alias: 'platform', + demand: false, + describe: 'use specified platform name', + type: 'string', + }, + T: { + alias: 'target', + demand: false, + describe: 'only build the specified target', + type: 'string', + }, + C: { + alias: 'prefer-clang', + demand: false, + describe: 'use Clang compiler instead of default CMake compiler, if available (Posix)', + type: 'boolean', + }, + cc: { + demand: false, + describe: 'use the specified C compiler', + type: 'string', + }, + cxx: { + demand: false, + describe: 'use the specified C++ compiler', + type: 'string', + }, + r: { + alias: 'runtime', + demand: false, + describe: 'the runtime to use', + type: 'string', + }, + v: { + alias: 'runtime-version', + demand: false, + describe: 'the runtime version to use', + type: 'string', + }, + a: { + alias: 'arch', + demand: false, + describe: 'the architecture to build in', + type: 'string', + }, + p: { + alias: 'parallel', + demand: false, + describe: 'the number of threads cmake can use', + type: 'number', + }, + CD: { + demand: false, + describe: 'Custom argument passed to CMake in format: -D', + type: 'string', + }, + i: { + alias: 'silent', + describe: 'Prevents CMake.js to print to the stdio', + type: 'boolean', + }, + O: { + alias: 'out', + describe: 'Specify the output directory to compile to, default is projectRoot/build', + type: 'string', + }, + }) +const argv = yargs.argv // If help, then print and exit: if (argv.h) { - console.info(yargs.help()); - process.exit(0); + console.info(yargs.help()) + process.exit(0) } // Setup log level: if (argv.l && logLevels.includes(argv.l)) { - log.level = argv.l; - log.resume(); + log.level = argv.l + log.resume() } -log.silly("CON", "argv:"); -log.silly("CON", util.inspect(argv)); +log.silly('CON', 'argv:') +log.silly('CON', util.inspect(argv)) -log.verbose("CON", "Parsing arguments"); +log.verbose('CON', 'Parsing arguments') // Extract custom cMake options -const customOptions = {}; +const customOptions = {} for (const arg of process.argv) { - if (arg.startsWith("--CD")) { - const separator = arg.indexOf('='); - if (separator < 5) continue; - const key = arg.substring(4, separator); - const value = arg.substring(separator + 1); - if (!value) continue; - customOptions[key] = value; - } + if (arg.startsWith('--CD')) { + const separator = arg.indexOf('=') + if (separator < 5) continue + const key = arg.substring(4, separator) + const value = arg.substring(separator + 1) + if (!value) continue + customOptions[key] = value + } } const options = { - directory: argv.directory || null, - debug: argv.debug, - cmakePath: argv.c || null, - generator: argv.G, - toolset: argv.t, - platform: argv.A, - target: argv.T, - preferMake: argv.m, - preferXcode: argv.x, - preferGnu: argv.g, - preferClang: argv.C, - cCompilerPath: argv.cc, - cppCompilerPath: argv.cxx, - runtime: argv.r, - runtimeVersion: argv.v, - arch: argv.a, - cMakeOptions: customOptions, - silent: argv.i, - out: argv.O, - config: argv.B, - parallel: argv.p, - extraCMakeArgs: argv._.slice(1), -}; + directory: argv.directory || null, + debug: argv.debug, + cmakePath: argv.c || null, + generator: argv.G, + toolset: argv.t, + platform: argv.A, + target: argv.T, + preferMake: argv.m, + preferXcode: argv.x, + preferGnu: argv.g, + preferClang: argv.C, + cCompilerPath: argv.cc, + cppCompilerPath: argv.cxx, + runtime: argv.r, + runtimeVersion: argv.v, + arch: argv.a, + cMakeOptions: customOptions, + silent: argv.i, + out: argv.O, + config: argv.B, + parallel: argv.p, + extraCMakeArgs: argv._.slice(1), +} -log.verbose("CON", "options:"); -log.verbose("CON", util.inspect(options)); +log.verbose('CON', 'options:') +log.verbose('CON', util.inspect(options)) -const command = argv._[0] || "build"; +const command = argv._[0] || 'build' -log.verbose("CON", "Running command: " + command); +log.verbose('CON', 'Running command: ' + command) -const buildSystem = new BuildSystem(options); +const buildSystem = new BuildSystem(options) function ifCommand(c, f) { - if (c === command) { - f(); - return true; - } - return false; + if (c === command) { + f() + return true + } + return false } function exitOnError(promise) { - promise.catch(function () { - process.exit(1); - }); + promise.catch(function () { + process.exit(1) + }) } function install() { - exitOnError(buildSystem.install()); + exitOnError(buildSystem.install()) } function configure() { - exitOnError(buildSystem.configure()); + exitOnError(buildSystem.configure()) } function printConfigure() { - exitOnError(buildSystem.getConfigureCommand() - .then(function (command) { - console.info(command); - })); + exitOnError( + buildSystem.getConfigureCommand().then(function (command) { + console.info(command) + }), + ) } function printCmakeJsLib() { - exitOnError(buildSystem.getCmakeJsLibString() - .then(function (command) { - console.info(command); - })); + exitOnError( + buildSystem.getCmakeJsLibString().then(function (command) { + console.info(command) + }), + ) } function printCmakeJsInclude() { - exitOnError(buildSystem.getCmakeJsIncludeString() - .then(function (command) { - console.info(command); - })); + exitOnError( + buildSystem.getCmakeJsIncludeString().then(function (command) { + console.info(command) + }), + ) } function printCmakeJsSrc() { - exitOnError(buildSystem.getCmakeJsSrcString() - .then(function (command) { - console.info(command); - })); + exitOnError( + buildSystem.getCmakeJsSrcString().then(function (command) { + console.info(command) + }), + ) } function build() { - exitOnError(buildSystem.build()); + exitOnError(buildSystem.build()) } function printBuild() { - exitOnError(buildSystem.getBuildCommand() - .then(function (command) { - console.info(command); - })); + exitOnError( + buildSystem.getBuildCommand().then(function (command) { + console.info(command) + }), + ) } function clean() { - exitOnError(buildSystem.clean()); + exitOnError(buildSystem.clean()) } function printClean() { - exitOnError(buildSystem.getCleanCommand() - .then(function (command) { - console.info(command); - })); + exitOnError( + buildSystem.getCleanCommand().then(function (command) { + console.info(command) + }), + ) } function reconfigure() { - exitOnError(buildSystem.reconfigure()); + exitOnError(buildSystem.reconfigure()) } function rebuild() { - exitOnError(buildSystem.rebuild()); + exitOnError(buildSystem.rebuild()) } function compile() { - exitOnError(buildSystem.compile()); + exitOnError(buildSystem.compile()) } -let done = ifCommand("install", install); -done = done || ifCommand("configure", configure); -done = done || ifCommand("print-configure", printConfigure); -done = done || ifCommand("print-cmakejs-src", printCmakeJsSrc); -done = done || ifCommand("print-cmakejs-include", printCmakeJsInclude); -done = done || ifCommand("print-cmakejs-lib", printCmakeJsLib); -done = done || ifCommand("build", build); -done = done || ifCommand("print-build", printBuild); -done = done || ifCommand("clean", clean); -done = done || ifCommand("print-clean", printClean); -done = done || ifCommand("reconfigure", reconfigure); -done = done || ifCommand("rebuild", rebuild); -done = done || ifCommand("compile", compile); +let done = ifCommand('install', install) +done = done || ifCommand('configure', configure) +done = done || ifCommand('print-configure', printConfigure) +done = done || ifCommand('print-cmakejs-src', printCmakeJsSrc) +done = done || ifCommand('print-cmakejs-include', printCmakeJsInclude) +done = done || ifCommand('print-cmakejs-lib', printCmakeJsLib) +done = done || ifCommand('build', build) +done = done || ifCommand('print-build', printBuild) +done = done || ifCommand('clean', clean) +done = done || ifCommand('print-clean', printClean) +done = done || ifCommand('reconfigure', reconfigure) +done = done || ifCommand('rebuild', rebuild) +done = done || ifCommand('compile', compile) if (!done) { - if (command) { - log.error("COM", "Unknown command: " + command); - process.exit(1); - } - else { - build(); - } + if (command) { + log.error('COM', 'Unknown command: ' + command) + process.exit(1) + } else { + build() + } } diff --git a/changelog.md b/changelog.md index fedf7d8a..04b55332 100644 --- a/changelog.md +++ b/changelog.md @@ -1,272 +1,225 @@ -v7.3.0 - 15/01/23 -========== - -- feat(windows): replace custom libnode.def generation with version from node-api-headers -- fix: support for vs2015 with nodejs 18 and older (#317) -- fix(windows): always remove Path if PATH is also defined (#319) -- fix: Cmake arguments got converted to numbers (#314) -- fix: update node-api-headers -- chore: update dependencies - -v7.2.1 - 14/02/23 -========== - -- fix: support Windows11SDK - -v7.2.0 - 12/02/23 -========== - -- fix: `-DCMAKE_JS_VERSION=undefined` (#298) -- fix: Only add build type to `CMAKE_LIBRARY_OUTPUT_DIRECTORY` if needed (#299) -- feat: Forward extra arguments to CMake commands (#297) - -v7.1.1 - 15/12/22 -========== - -- fix build errors on windows - -v7.1.0 - 14/12/22 -========== - -- add commands for retrieving cmake-js include and lib directories -- fix win delay hook issues with electron -- fix missing js_native_api_symbols in windows node.lib - -v7.0.0 - 08/10/22 -========== - -- update dependencies -- replace some dependencies with modern language features -- follow node-gyp behaviour for visual-studio version detection and selection -- automatically locate node-addon-api and add to include paths -- avoid downloads when building for node-api -- encourage use of MT builds with MSVC, rather than MD - -v6.3.1 - 05/06/22 -========== - -- add missing bluebird dependency -- fix platform detection for visual studio 2019 and newer -- fix platform detection for macos - -v6.3.0 - 26/11/21 -========== - -- add offline mode: https://github.com/cmake-js/cmake-js/pull/260 -- handle missing buildSystem.log: https://github.com/cmake-js/cmake-js/pull/259 -- Add config flag: https://github.com/cmake-js/cmake-js/pull/251 -- Remove escaped quotes from windows registry queries: https://github.com/cmake-js/cmake-js/pull/250 - -v6.2.1 - 20/07/21 -========== - -- EOL hotfix (Thx Windows!) - -v6.2.0 - 19/07/21 -========== - -- various fixes - -v6.1.0 - 27/02/20 -========== - -- Add support for "-A/--platform" option to make target platform selectable for Visual Studio 2019 generator: https://github.com/cmake-js/cmake-js/pull/201 - -v6.0.0 - 30/09/19 -================= - -- Dropped compatibility of old Node.js runtimes (<10.0.0) -- --cc and --cxx flags for overriding compiler detection: https://github.com/cmake-js/cmake-js/pull/191 - -v5.3.2 - 21/08/19 -================= - -- Visual Studio detection fixes - -v5.3.1 - 18/07/19 -================= - -- VS 2019 Support fix: https://github.com/cmake-js/cmake-js/pull/187 - -v5.3.0 - 09/07/19 -================= - -- VS 2019 Support: https://github.com/cmake-js/cmake-js/pull/178/, https://github.com/cmake-js/cmake-js/pull/184/ - -v5.2.1 - 10/04/19 -================= - -- Win delay load hook: https://github.com/cmake-js/cmake-js/pull/165/ - -v5.1.1 - 02/04/19 -================= - -- CMake 3.14 support fixed - https://github.com/cmake-js/cmake-js/pull/161 - -v5.1.0 - 14/02/19 -================= - -- CMake 3.14 support - https://github.com/cmake-js/cmake-js/pull/159 - -v5.0.1 - 24/01/19 -================= - -- Linux line ending hotfix (I hate Windows!) - -v5.0.0 - 24/01/19 -================= - -- [semver major] Add case sensitive NPM config integration https://github.com/cmake-js/cmake-js/pull/151 -- better npm config integration, all CMake.js commandline argument could be set by using npm config: https://github.com/cmake-js/cmake-js#npm-config-integration -- support for Electron v4+ https://github.com/cmake-js/cmake-js/pull/152 - -v4.0.1 - 03/10/18 -================= - -- log argument hotfix https://github.com/cmake-js/cmake-js/pull/145 - -v4.0.0 - 14/09/18 -================= - -BREAKING CHANGES: - -- -s/--std (along with -o/--prec11 option removed, you have to specify compiler standard in CMakeLists.txt files https://github.com/cmake-js/cmake-js/issues/72 -- Implicit -w compiler flag doesn't get added on OSX https://github.com/cmake-js/cmake-js/pull/133 - -v3.7.3 - 16/05/18 -================= - -- npm config hotfix https://github.com/cmake-js/cmake-js/pull/123 - -v3.7.2 - 16/05/18 -================= - -- do not use, breaks ES5 compatibility - -v3.7.1 - 07/05/18 -================= - -- Linux line ending hotfix (wat) - -v3.7.0 - 07/05/18 -================= - -- PR: replace unzip with unzipper https://github.com/cmake-js/cmake-js/pull/120 -- PR: replace npmconf with rc https://github.com/cmake-js/cmake-js/pull/119 -- PR: update to modern fs-extras https://github.com/cmake-js/cmake-js/pull/118 -- PR: Adds toolset command line flag https://github.com/cmake-js/cmake-js/pull/115 - -v3.6.2 - 17/02/18 -================= - -- use https distribution download urls -- custom cmake options made case sensitive - -v3.6.1 - 11/01/18 -================= - -- Detect 2017 Windows Build Tools - -v3.6.0 - 11/27/17 -================= - -- "T" option for building specified target: https://github.com/cmake-js/cmake-js/pull/98 - -v3.5.0 - 06/21/17 -================= - -- Added Visual Studio 2017 compatibility: https://github.com/cmake-js/cmake-js/pull/78 - -v3.4.1 - 02/4/17 -================= - -- FIX: test output instead of guessing by platform: https://github.com/cmake-js/cmake-js/pull/77 - -v3.4.0 - 01/12/17 -================= - -- "G" option to set custom generators: https://github.com/cmake-js/cmake-js/pull/64 - -v3.3.1 - 09/13/16 -================= - -- fix of default parameters: https://github.com/cmake-js/cmake-js/pull/57 - -v3.3.0 - 09/02/16 -================= - -- silent option (https://github.com/cmake-js/cmake-js/pull/54) -- out option (https://github.com/cmake-js/cmake-js/pull/53) - -v3.2.3 - 08/17/16 -================= - -- Line endings - -v3.2.2 - 12/08/16 -================= - -- Multi directory support for Windows/MSVC build - -v3.2.1 - 25/04/16 -================= - -- Linux line ending hotfix - -v3.2.0 - 25/04/16 -================= - -- Added NW.js 0.13+ compatibility -- Node v0.10.x support fixed (https://github.com/cmake-js/cmake-js/pull/45, https://github.com/cmake-js/cmake-js/issues/50) -- CMAKE_JS_VERSION defined (https://github.com/cmake-js/cmake-js/issues/48) - -v3.1.2 - 03/02/16 -================= - -- Fixed cmake-js binary ES5 compatibility. - -v3.1.1 - 03/02/16 -================= - -- Fixed line endings - -v3.1.0 - 03/02/16 -================= - -- Custom CMake parameter support (https://github.com/gerhardberger) - -v3.0.0 - 20/11/15 -================= - -- Visual C++ Build Tools support -- std option introduced -- better unit test coverage - -v2.1.0 - 29/10/15 -================= - -- explicit options for use GNU or Clang compiler instead of CMake's default (see --help for details) - -v2.0.2 - 22/10/15 -================= - -- Fix: print-* commands report "undefined" - -v2.0.0 - 17/10/15 -================= - -- Fix: distribution files only gets downloaded if needed (4.0.0+) -- option to generate Xcode project (-x, --prefer-xcode) - by https://github.com/javedulu -- compile command for fast module compilation during npm updates (instead of rebuild) -- codebase switched to ECMAScript 2015 - -v1.1.1 - 06/10/15 -================= - -- Hotfix for build NW.js correctly. - -v1.1.0 - 05/10/15 -================= - -- Node.js 4.0.0+ support -- Downloads the small, header only tarball for Node.js 4+ +# v7.3.0 - 15/01/23 + +- feat(windows): replace custom libnode.def generation with version from node-api-headers +- fix: support for vs2015 with nodejs 18 and older (#317) +- fix(windows): always remove Path if PATH is also defined (#319) +- fix: Cmake arguments got converted to numbers (#314) +- fix: update node-api-headers +- chore: update dependencies + +# v7.2.1 - 14/02/23 + +- fix: support Windows11SDK + +# v7.2.0 - 12/02/23 + +- fix: `-DCMAKE_JS_VERSION=undefined` (#298) +- fix: Only add build type to `CMAKE_LIBRARY_OUTPUT_DIRECTORY` if needed (#299) +- feat: Forward extra arguments to CMake commands (#297) + +# v7.1.1 - 15/12/22 + +- fix build errors on windows + +# v7.1.0 - 14/12/22 + +- add commands for retrieving cmake-js include and lib directories +- fix win delay hook issues with electron +- fix missing js_native_api_symbols in windows node.lib + +# v7.0.0 - 08/10/22 + +- update dependencies +- replace some dependencies with modern language features +- follow node-gyp behaviour for visual-studio version detection and selection +- automatically locate node-addon-api and add to include paths +- avoid downloads when building for node-api +- encourage use of MT builds with MSVC, rather than MD + +# v6.3.1 - 05/06/22 + +- add missing bluebird dependency +- fix platform detection for visual studio 2019 and newer +- fix platform detection for macos + +# v6.3.0 - 26/11/21 + +- add offline mode: https://github.com/cmake-js/cmake-js/pull/260 +- handle missing buildSystem.log: https://github.com/cmake-js/cmake-js/pull/259 +- Add config flag: https://github.com/cmake-js/cmake-js/pull/251 +- Remove escaped quotes from windows registry queries: https://github.com/cmake-js/cmake-js/pull/250 + +# v6.2.1 - 20/07/21 + +- EOL hotfix (Thx Windows!) + +# v6.2.0 - 19/07/21 + +- various fixes + +# v6.1.0 - 27/02/20 + +- Add support for "-A/--platform" option to make target platform selectable for Visual Studio 2019 generator: https://github.com/cmake-js/cmake-js/pull/201 + +# v6.0.0 - 30/09/19 + +- Dropped compatibility of old Node.js runtimes (<10.0.0) +- --cc and --cxx flags for overriding compiler detection: https://github.com/cmake-js/cmake-js/pull/191 + +# v5.3.2 - 21/08/19 + +- Visual Studio detection fixes + +# v5.3.1 - 18/07/19 + +- VS 2019 Support fix: https://github.com/cmake-js/cmake-js/pull/187 + +# v5.3.0 - 09/07/19 + +- VS 2019 Support: https://github.com/cmake-js/cmake-js/pull/178/, https://github.com/cmake-js/cmake-js/pull/184/ + +# v5.2.1 - 10/04/19 + +- Win delay load hook: https://github.com/cmake-js/cmake-js/pull/165/ + +# v5.1.1 - 02/04/19 + +- CMake 3.14 support fixed - https://github.com/cmake-js/cmake-js/pull/161 + +# v5.1.0 - 14/02/19 + +- CMake 3.14 support - https://github.com/cmake-js/cmake-js/pull/159 + +# v5.0.1 - 24/01/19 + +- Linux line ending hotfix (I hate Windows!) + +# v5.0.0 - 24/01/19 + +- [semver major] Add case sensitive NPM config integration https://github.com/cmake-js/cmake-js/pull/151 +- better npm config integration, all CMake.js commandline argument could be set by using npm config: https://github.com/cmake-js/cmake-js#npm-config-integration +- support for Electron v4+ https://github.com/cmake-js/cmake-js/pull/152 + +# v4.0.1 - 03/10/18 + +- log argument hotfix https://github.com/cmake-js/cmake-js/pull/145 + +# v4.0.0 - 14/09/18 + +BREAKING CHANGES: + +- -s/--std (along with -o/--prec11 option removed, you have to specify compiler standard in CMakeLists.txt files https://github.com/cmake-js/cmake-js/issues/72 +- Implicit -w compiler flag doesn't get added on OSX https://github.com/cmake-js/cmake-js/pull/133 + +# v3.7.3 - 16/05/18 + +- npm config hotfix https://github.com/cmake-js/cmake-js/pull/123 + +# v3.7.2 - 16/05/18 + +- do not use, breaks ES5 compatibility + +# v3.7.1 - 07/05/18 + +- Linux line ending hotfix (wat) + +# v3.7.0 - 07/05/18 + +- PR: replace unzip with unzipper https://github.com/cmake-js/cmake-js/pull/120 +- PR: replace npmconf with rc https://github.com/cmake-js/cmake-js/pull/119 +- PR: update to modern fs-extras https://github.com/cmake-js/cmake-js/pull/118 +- PR: Adds toolset command line flag https://github.com/cmake-js/cmake-js/pull/115 + +# v3.6.2 - 17/02/18 + +- use https distribution download urls +- custom cmake options made case sensitive + +# v3.6.1 - 11/01/18 + +- Detect 2017 Windows Build Tools + +# v3.6.0 - 11/27/17 + +- "T" option for building specified target: https://github.com/cmake-js/cmake-js/pull/98 + +# v3.5.0 - 06/21/17 + +- Added Visual Studio 2017 compatibility: https://github.com/cmake-js/cmake-js/pull/78 + +# v3.4.1 - 02/4/17 + +- FIX: test output instead of guessing by platform: https://github.com/cmake-js/cmake-js/pull/77 + +# v3.4.0 - 01/12/17 + +- "G" option to set custom generators: https://github.com/cmake-js/cmake-js/pull/64 + +# v3.3.1 - 09/13/16 + +- fix of default parameters: https://github.com/cmake-js/cmake-js/pull/57 + +# v3.3.0 - 09/02/16 + +- silent option (https://github.com/cmake-js/cmake-js/pull/54) +- out option (https://github.com/cmake-js/cmake-js/pull/53) + +# v3.2.3 - 08/17/16 + +- Line endings + +# v3.2.2 - 12/08/16 + +- Multi directory support for Windows/MSVC build + +# v3.2.1 - 25/04/16 + +- Linux line ending hotfix + +# v3.2.0 - 25/04/16 + +- Added NW.js 0.13+ compatibility +- Node v0.10.x support fixed (https://github.com/cmake-js/cmake-js/pull/45, https://github.com/cmake-js/cmake-js/issues/50) +- CMAKE_JS_VERSION defined (https://github.com/cmake-js/cmake-js/issues/48) + +# v3.1.2 - 03/02/16 + +- Fixed cmake-js binary ES5 compatibility. + +# v3.1.1 - 03/02/16 + +- Fixed line endings + +# v3.1.0 - 03/02/16 + +- Custom CMake parameter support (https://github.com/gerhardberger) + +# v3.0.0 - 20/11/15 + +- Visual C++ Build Tools support +- std option introduced +- better unit test coverage + +# v2.1.0 - 29/10/15 + +- explicit options for use GNU or Clang compiler instead of CMake's default (see --help for details) + +# v2.0.2 - 22/10/15 + +- Fix: print-\* commands report "undefined" + +# v2.0.0 - 17/10/15 + +- Fix: distribution files only gets downloaded if needed (4.0.0+) +- option to generate Xcode project (-x, --prefer-xcode) - by https://github.com/javedulu +- compile command for fast module compilation during npm updates (instead of rebuild) +- codebase switched to ECMAScript 2015 + +# v1.1.1 - 06/10/15 + +- Hotfix for build NW.js correctly. + +# v1.1.0 - 05/10/15 + +- Node.js 4.0.0+ support +- Downloads the small, header only tarball for Node.js 4+ diff --git a/lib/appCMakeJSConfig.js b/lib/appCMakeJSConfig.js index 23f602b7..ed8621dd 100644 --- a/lib/appCMakeJSConfig.js +++ b/lib/appCMakeJSConfig.js @@ -1,63 +1,59 @@ -"use strict"; -const path = require("path"); -const isPlainObject = require("lodash.isplainobject"); +'use strict' +const path = require('path') +const isPlainObject = require('lodash.isplainobject') function getConfig(lookPath, log) { - const pjsonPath = path.join(lookPath, "package.json"); - log.silly("CFG", "Looking for package.json in: '" + pjsonPath + "'."); - try { - const json = require(pjsonPath); - log.silly("CFG", "Loaded:\n" + JSON.stringify(json)); - if (isPlainObject(json) && isPlainObject(json["cmake-js"])) { - log.silly("CFG", "Config found."); - return json["cmake-js"]; - } - else { - log.silly("CFG", "Config not found."); - return null; - } - } - catch (e) { - log.silly("CFG", "'package.json' not found."); - return null; - } + const pjsonPath = path.join(lookPath, 'package.json') + log.silly('CFG', "Looking for package.json in: '" + pjsonPath + "'.") + try { + const json = require(pjsonPath) + log.silly('CFG', 'Loaded:\n' + JSON.stringify(json)) + if (isPlainObject(json) && isPlainObject(json['cmake-js'])) { + log.silly('CFG', 'Config found.') + return json['cmake-js'] + } else { + log.silly('CFG', 'Config not found.') + return null + } + } catch (e) { + log.silly('CFG', "'package.json' not found.") + return null + } } module.exports = function (projectPath, log) { - log.verbose("CFG", "Looking for application level CMake.js config in '" + projectPath + "."); - let currPath = projectPath; - let lastConfig = null; - let currConfig; - for (; ;) { - currConfig = getConfig(currPath, log); - if (currConfig) { - lastConfig = currConfig; - } - try { - log.silly("CFG", "Looking for parent path."); - const lastPath = currPath; - currPath = path.normalize(path.join(currPath, "..")); - if (lastPath === currPath) { - currPath = null; // root - } - if (currPath) { - log.silly("CFG", "Parent path: '" + currPath + "'."); - } - } - catch (e) { - log.silly("CFG", "Exception:\n" + e.stack); - break; - } - if (currPath === null) { - log.silly("CFG", "Parent path with package.json file doesn't exists. Done."); - break; - } - } - if (lastConfig) { - log.verbose("CFG", "Application level CMake.js config found:\n" + JSON.stringify(lastConfig)); - } - else { - log.verbose("CFG", "Application level CMake.js config doesn't exists."); - } - return lastConfig; -}; + log.verbose('CFG', "Looking for application level CMake.js config in '" + projectPath + '.') + let currPath = projectPath + let lastConfig = null + let currConfig + for (;;) { + currConfig = getConfig(currPath, log) + if (currConfig) { + lastConfig = currConfig + } + try { + log.silly('CFG', 'Looking for parent path.') + const lastPath = currPath + currPath = path.normalize(path.join(currPath, '..')) + if (lastPath === currPath) { + currPath = null // root + } + if (currPath) { + log.silly('CFG', "Parent path: '" + currPath + "'.") + } + } catch (e) { + log.silly('CFG', 'Exception:\n' + e.stack) + break + } + if (currPath === null) { + log.silly('CFG', "Parent path with package.json file doesn't exists. Done.") + break + } + } + if (lastConfig) { + log.verbose('CFG', 'Application level CMake.js config found:\n' + JSON.stringify(lastConfig)) + } else { + log.verbose('CFG', "Application level CMake.js config doesn't exists.") + } + return lastConfig +} diff --git a/lib/buildSystem.js b/lib/buildSystem.js index d60d0fd1..b94d8893 100644 --- a/lib/buildSystem.js +++ b/lib/buildSystem.js @@ -1,142 +1,139 @@ -"use strict"; -const CMake = require("./cMake"); -const Dist = require("./dist"); -const CMLog = require("./cmLog"); -const appCMakeJSConfig = require("./appCMakeJSConfig"); -const npmConfig = require("./npmConfig"); -const path = require("path"); -const isPlainObject = require("lodash.isplainobject"); -const Toolset = require("./toolset"); +'use strict' +const CMake = require('./cMake') +const Dist = require('./dist') +const CMLog = require('./cmLog') +const appCMakeJSConfig = require('./appCMakeJSConfig') +const npmConfig = require('./npmConfig') +const path = require('path') +const isPlainObject = require('lodash.isplainobject') +const Toolset = require('./toolset') function isNodeApi(log, projectRoot) { - try { - const projectPkgJson = require(path.join(projectRoot, 'package.json')) - // Make sure the property exists - return !!projectPkgJson?.binary?.napi_versions - } catch (e) { - log.silly("CFG", "'package.json' not found."); - return false - } + try { + const projectPkgJson = require(path.join(projectRoot, 'package.json')) + // Make sure the property exists + return !!projectPkgJson?.binary?.napi_versions + } catch (e) { + log.silly('CFG', "'package.json' not found.") + return false + } } function BuildSystem(options) { - this.options = options || {}; - this.options.directory = path.resolve(this.options.directory || process.cwd()); - this.options.out = path.resolve(this.options.out || path.join(this.options.directory, "build")); - this.log = new CMLog(this.options); - this.options.isNodeApi = isNodeApi(this.log, this.options.directory) - const appConfig = appCMakeJSConfig(this.options.directory, this.log); - const npmOptions = npmConfig(this.log); - - if (isPlainObject(npmOptions) && Object.keys(npmOptions).length) { - this.options.runtimeDirectory = npmOptions["nodedir"]; - this.options.msvsVersion = npmOptions['msvs_version'] - } - if (isPlainObject(appConfig)) { - if (Object.keys(appConfig).length) { - this.log.verbose("CFG", "Applying CMake.js config from root package.json:"); - this.log.verbose("CFG", JSON.stringify(appConfig)); - // Applying applications's config, if there is no explicit runtime related options specified - this.options.runtime = this.options.runtime || appConfig.runtime; - this.options.runtimeVersion = this.options.runtimeVersion || appConfig.runtimeVersion; - this.options.arch = this.options.arch || appConfig.arch; - } - } - this.log.verbose("CFG", "Build system options:"); - this.log.verbose("CFG", JSON.stringify(this.options)); - this.cmake = new CMake(this.options); - this.dist = new Dist(this.options); - this.toolset = new Toolset(this.options); + this.options = options || {} + this.options.directory = path.resolve(this.options.directory || process.cwd()) + this.options.out = path.resolve(this.options.out || path.join(this.options.directory, 'build')) + this.log = new CMLog(this.options) + this.options.isNodeApi = isNodeApi(this.log, this.options.directory) + const appConfig = appCMakeJSConfig(this.options.directory, this.log) + const npmOptions = npmConfig(this.log) + + if (isPlainObject(npmOptions) && Object.keys(npmOptions).length) { + this.options.runtimeDirectory = npmOptions['nodedir'] + this.options.msvsVersion = npmOptions['msvs_version'] + } + if (isPlainObject(appConfig)) { + if (Object.keys(appConfig).length) { + this.log.verbose('CFG', 'Applying CMake.js config from root package.json:') + this.log.verbose('CFG', JSON.stringify(appConfig)) + // Applying applications's config, if there is no explicit runtime related options specified + this.options.runtime = this.options.runtime || appConfig.runtime + this.options.runtimeVersion = this.options.runtimeVersion || appConfig.runtimeVersion + this.options.arch = this.options.arch || appConfig.arch + } + } + this.log.verbose('CFG', 'Build system options:') + this.log.verbose('CFG', JSON.stringify(this.options)) + this.cmake = new CMake(this.options) + this.dist = new Dist(this.options) + this.toolset = new Toolset(this.options) } BuildSystem.prototype._ensureInstalled = async function () { - try { - await this.toolset.initialize(true); - if (!this.options.isNodeApi) { - await this.dist.ensureDownloaded(); - } - } - catch (e) { - this._showError(e); - throw e; - } -}; + try { + await this.toolset.initialize(true) + if (!this.options.isNodeApi) { + await this.dist.ensureDownloaded() + } + } catch (e) { + this._showError(e) + throw e + } +} BuildSystem.prototype._showError = function (e) { - if (this.log === undefined) { - // handle internal errors (init failed) - console.error("OMG", e.stack); - return; - } - if (this.log.level === "verbose" || this.log.level === "silly") { - this.log.error("OMG", e.stack); - } - else { - this.log.error("OMG", e.message); - } -}; + if (this.log === undefined) { + // handle internal errors (init failed) + console.error('OMG', e.stack) + return + } + if (this.log.level === 'verbose' || this.log.level === 'silly') { + this.log.error('OMG', e.stack) + } else { + this.log.error('OMG', e.message) + } +} BuildSystem.prototype.install = function () { - return this._ensureInstalled(); -}; + return this._ensureInstalled() +} BuildSystem.prototype._invokeCMake = async function (method) { - try { - await this._ensureInstalled(); - return await this.cmake[method](); - } - catch (e) { - this._showError(e); - throw e; - } -}; + try { + await this._ensureInstalled() + return await this.cmake[method]() + } catch (e) { + this._showError(e) + throw e + } +} BuildSystem.prototype.getConfigureCommand = function () { - return this._invokeCMake("getConfigureCommand"); -}; + return this._invokeCMake('getConfigureCommand') +} BuildSystem.prototype.getCmakeJsLibString = function () { - return this._invokeCMake("getCmakeJsLibString"); -}; + return this._invokeCMake('getCmakeJsLibString') +} BuildSystem.prototype.getCmakeJsIncludeString = function () { - return this._invokeCMake("getCmakeJsIncludeString"); -}; + return this._invokeCMake('getCmakeJsIncludeString') +} BuildSystem.prototype.getCmakeJsSrcString = function () { - return this._invokeCMake("getCmakeJsSrcString"); -}; + return this._invokeCMake('getCmakeJsSrcString') +} BuildSystem.prototype.configure = function () { - return this._invokeCMake("configure"); -}; + return this._invokeCMake('configure') +} BuildSystem.prototype.getBuildCommand = function () { - return this._invokeCMake("getBuildCommand"); -}; + return this._invokeCMake('getBuildCommand') +} BuildSystem.prototype.build = function () { - return this._invokeCMake("build"); -}; + return this._invokeCMake('build') +} BuildSystem.prototype.getCleanCommand = function () { - return this._invokeCMake("getCleanCommand"); -}; + return this._invokeCMake('getCleanCommand') +} BuildSystem.prototype.clean = function () { - return this._invokeCMake("clean"); -}; + return this._invokeCMake('clean') +} BuildSystem.prototype.reconfigure = function () { - return this._invokeCMake("reconfigure"); -}; + return this._invokeCMake('reconfigure') +} BuildSystem.prototype.rebuild = function () { - return this._invokeCMake("rebuild"); -}; + return this._invokeCMake('rebuild') +} BuildSystem.prototype.compile = function () { - return this._invokeCMake("compile"); -}; + return this._invokeCMake('compile') +} -module.exports = BuildSystem; +module.exports = BuildSystem diff --git a/lib/cMake.js b/lib/cMake.js index b9feba55..6042787f 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -1,392 +1,384 @@ -"use strict"; -const which = require("which"); -const fs = require("fs-extra"); -const path = require("path"); -const environment = require("./environment"); -const Dist = require("./dist"); -const CMLog = require("./cmLog"); -const TargetOptions = require("./targetOptions"); -const processHelpers = require("./processHelpers"); -const locateNAN = require("./locateNAN"); -const locateNodeApi = require("./locateNodeApi"); -const npmConfigData = require("rc")("npm"); -const Toolset = require("./toolset"); -const headers = require('node-api-headers'); +'use strict' +const which = require('which') +const fs = require('fs-extra') +const path = require('path') +const environment = require('./environment') +const Dist = require('./dist') +const CMLog = require('./cmLog') +const TargetOptions = require('./targetOptions') +const processHelpers = require('./processHelpers') +const locateNAN = require('./locateNAN') +const locateNodeApi = require('./locateNodeApi') +const npmConfigData = require('rc')('npm') +const Toolset = require('./toolset') +const headers = require('node-api-headers') function CMake(options) { - this.options = options || {}; - this.log = new CMLog(this.options); - this.dist = new Dist(this.options); - this.projectRoot = path.resolve(this.options.directory || process.cwd()); - this.workDir = path.resolve(this.options.out || path.join(this.projectRoot, "build")); - this.config = this.options.config || (this.options.debug ? "Debug" : "Release"); - this.buildDir = path.join(this.workDir, this.config); - this._isAvailable = null; - this.targetOptions = new TargetOptions(this.options); - this.toolset = new Toolset(this.options); - this.cMakeOptions = this.options.cMakeOptions || {}; - this.extraCMakeArgs = this.options.extraCMakeArgs || []; - this.silent = !!options.silent; + this.options = options || {} + this.log = new CMLog(this.options) + this.dist = new Dist(this.options) + this.projectRoot = path.resolve(this.options.directory || process.cwd()) + this.workDir = path.resolve(this.options.out || path.join(this.projectRoot, 'build')) + this.config = this.options.config || (this.options.debug ? 'Debug' : 'Release') + this.buildDir = path.join(this.workDir, this.config) + this._isAvailable = null + this.targetOptions = new TargetOptions(this.options) + this.toolset = new Toolset(this.options) + this.cMakeOptions = this.options.cMakeOptions || {} + this.extraCMakeArgs = this.options.extraCMakeArgs || [] + this.silent = !!options.silent } Object.defineProperties(CMake.prototype, { - path: { - get: function () { - return this.options.cmakePath || "cmake"; - } - }, - isAvailable: { - get: function () { - if (this._isAvailable === null) { - this._isAvailable = CMake.isAvailable(this.options); - } - return this._isAvailable; - } - } -}); + path: { + get: function () { + return this.options.cmakePath || 'cmake' + }, + }, + isAvailable: { + get: function () { + if (this._isAvailable === null) { + this._isAvailable = CMake.isAvailable(this.options) + } + return this._isAvailable + }, + }, +}) CMake.isAvailable = function (options) { - options = options || {}; - try { - if (options.cmakePath) { - const stat = fs.lstatSync(options.cmakePath); - return !stat.isDirectory(); - } - else { - which.sync("cmake"); - return true; - } - } - catch (e) { - // Ignore - } - return false; -}; + options = options || {} + try { + if (options.cmakePath) { + const stat = fs.lstatSync(options.cmakePath) + return !stat.isDirectory() + } else { + which.sync('cmake') + return true + } + } catch (e) { + // Ignore + } + return false +} CMake.getGenerators = async function (options, log) { - const arch = " [arch]"; - options = options || {}; - const gens = []; - if (CMake.isAvailable(options)) { - // try parsing machine-readable capabilities (available since CMake 3.7) - try { - const stdout = await processHelpers.execFile([options.cmakePath || "cmake", "-E", "capabilities"]); - const capabilities = JSON.parse(stdout); - return capabilities.generators.map(x => x.name); - } - catch (error) { - if (log) { - log.verbose("TOOL", "Failed to query CMake capabilities (CMake is probably older than 3.7)"); - } - } - - // fall back to parsing help text - const stdout = await processHelpers.execFile([options.cmakePath || "cmake", "--help"]); - const hasCr = stdout.includes("\r\n"); - const output = hasCr ? stdout.split("\r\n") : stdout.split("\n"); - let on = false; - output.forEach(function (line, i) { - if (on) { - const parts = line.split("="); - if ((parts.length === 2 && parts[0].trim()) || - (parts.length === 1 && i !== output.length - 1 && output[i + 1].trim()[0] === "=")) { - let gen = parts[0].trim(); - if (gen.endsWith(arch)) { - gen = gen.substr(0, gen.length - arch.length); - } - gens.push(gen); - } - } - if (line.trim() === "Generators") { - on = true; - } - }); - } - else { - throw new Error("CMake is not installed. Install CMake."); - } - return gens; -}; + const arch = ' [arch]' + options = options || {} + const gens = [] + if (CMake.isAvailable(options)) { + // try parsing machine-readable capabilities (available since CMake 3.7) + try { + const stdout = await processHelpers.execFile([options.cmakePath || 'cmake', '-E', 'capabilities']) + const capabilities = JSON.parse(stdout) + return capabilities.generators.map((x) => x.name) + } catch (error) { + if (log) { + log.verbose('TOOL', 'Failed to query CMake capabilities (CMake is probably older than 3.7)') + } + } + + // fall back to parsing help text + const stdout = await processHelpers.execFile([options.cmakePath || 'cmake', '--help']) + const hasCr = stdout.includes('\r\n') + const output = hasCr ? stdout.split('\r\n') : stdout.split('\n') + let on = false + output.forEach(function (line, i) { + if (on) { + const parts = line.split('=') + if ( + (parts.length === 2 && parts[0].trim()) || + (parts.length === 1 && i !== output.length - 1 && output[i + 1].trim()[0] === '=') + ) { + let gen = parts[0].trim() + if (gen.endsWith(arch)) { + gen = gen.substr(0, gen.length - arch.length) + } + gens.push(gen) + } + } + if (line.trim() === 'Generators') { + on = true + } + }) + } else { + throw new Error('CMake is not installed. Install CMake.') + } + return gens +} CMake.prototype.getGenerators = function () { - return CMake.getGenerators(this.options, this.log); -}; + return CMake.getGenerators(this.options, this.log) +} CMake.prototype.verifyIfAvailable = function () { - if (!this.isAvailable) { - throw new Error("CMake executable is not found. Please use your system's package manager to install it, or you can get installers from there: http://cmake.org."); - } -}; + if (!this.isAvailable) { + throw new Error( + "CMake executable is not found. Please use your system's package manager to install it, or you can get installers from there: http://cmake.org.", + ) + } +} CMake.prototype.getConfigureCommand = async function () { - - // Create command: - let command = [this.path, this.projectRoot, "--no-warn-unused-cli"]; - - const D = []; - - // CMake.js watermark - D.push({"CMAKE_JS_VERSION": environment.cmakeJsVersion}); - - // Build configuration: - D.push({"CMAKE_BUILD_TYPE": this.config}); - if (environment.isWin) { - D.push({"CMAKE_RUNTIME_OUTPUT_DIRECTORY": this.workDir}); - } - else if (this.workDir.endsWith(this.config)) { - D.push({"CMAKE_LIBRARY_OUTPUT_DIRECTORY": this.workDir}); - } - else { - D.push({"CMAKE_LIBRARY_OUTPUT_DIRECTORY": this.buildDir}); - } - - // In some configurations MD builds will crash upon attempting to free memory. - // This tries to encourage MT builds which are larger but less likely to have this crash. - D.push({"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>"}) - - // Includes: - const includesString = await this.getCmakeJsIncludeString(); - D.push({ "CMAKE_JS_INC": includesString }); - - // Sources: - const srcsString = this.getCmakeJsSrcString(); - D.push({ "CMAKE_JS_SRC": srcsString }); - - // Runtime: - D.push({"NODE_RUNTIME": this.targetOptions.runtime}); - D.push({"NODE_RUNTIMEVERSION": this.targetOptions.runtimeVersion}); - D.push({"NODE_ARCH": this.targetOptions.arch}); - - if (environment.isOSX) { - if (this.targetOptions.arch) { - let xcodeArch = this.targetOptions.arch - if (xcodeArch === 'x64') xcodeArch = 'x86_64' - D.push({CMAKE_OSX_ARCHITECTURES: xcodeArch}) - } - } - - // Custom options - for (const [key, value] of Object.entries(this.cMakeOptions)) { - D.push({ [key]: value }); - } - - // Toolset: - await this.toolset.initialize(false); - - const libsString = this.getCmakeJsLibString() - D.push({ "CMAKE_JS_LIB": libsString }); - - if (environment.isWin) { - const nodeLibDefPath = this.getNodeLibDefPath() - if (nodeLibDefPath) { - const nodeLibPath = path.join(this.workDir, 'node.lib') - D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) - D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) - } - } - - if (this.toolset.generator) { - command.push("-G", this.toolset.generator); - } - if (this.toolset.platform) { - command.push("-A", this.toolset.platform); - } - if (this.toolset.toolset) { - command.push("-T", this.toolset.toolset); - } - if (this.toolset.cppCompilerPath) { - D.push({"CMAKE_CXX_COMPILER": this.toolset.cppCompilerPath}); - } - if (this.toolset.cCompilerPath) { - D.push({"CMAKE_C_COMPILER": this.toolset.cCompilerPath}); - } - if (this.toolset.compilerFlags.length) { - D.push({"CMAKE_CXX_FLAGS": this.toolset.compilerFlags.join(" ")}); - } - if (this.toolset.linkerFlags.length) { - D.push({"CMAKE_SHARED_LINKER_FLAGS": this.toolset.linkerFlags.join(" ")}); - } - if (this.toolset.makePath) { - D.push({"CMAKE_MAKE_PROGRAM": this.toolset.makePath}); - } - - // Load NPM config - for (const [key, value] of Object.entries(npmConfigData)) { - if (key.startsWith("cmake_")) { - const sk = key.substr(6); - if (sk && value) { - D.push({ [sk]: value }); - } - } - } - - command = command.concat(D.map(function (p) { - return "-D" + Object.keys(p)[0] + "=" + Object.values(p)[0]; - })); - - return command.concat(this.extraCMakeArgs); -}; + // Create command: + let command = [this.path, this.projectRoot, '--no-warn-unused-cli'] + + const D = [] + + // CMake.js watermark + D.push({ CMAKE_JS_VERSION: environment.cmakeJsVersion }) + + // Build configuration: + D.push({ CMAKE_BUILD_TYPE: this.config }) + if (environment.isWin) { + D.push({ CMAKE_RUNTIME_OUTPUT_DIRECTORY: this.workDir }) + } else if (this.workDir.endsWith(this.config)) { + D.push({ CMAKE_LIBRARY_OUTPUT_DIRECTORY: this.workDir }) + } else { + D.push({ CMAKE_LIBRARY_OUTPUT_DIRECTORY: this.buildDir }) + } + + // In some configurations MD builds will crash upon attempting to free memory. + // This tries to encourage MT builds which are larger but less likely to have this crash. + D.push({ CMAKE_MSVC_RUNTIME_LIBRARY: 'MultiThreaded$<$:Debug>' }) + + // Includes: + const includesString = await this.getCmakeJsIncludeString() + D.push({ CMAKE_JS_INC: includesString }) + + // Sources: + const srcsString = this.getCmakeJsSrcString() + D.push({ CMAKE_JS_SRC: srcsString }) + + // Runtime: + D.push({ NODE_RUNTIME: this.targetOptions.runtime }) + D.push({ NODE_RUNTIMEVERSION: this.targetOptions.runtimeVersion }) + D.push({ NODE_ARCH: this.targetOptions.arch }) + + if (environment.isOSX) { + if (this.targetOptions.arch) { + let xcodeArch = this.targetOptions.arch + if (xcodeArch === 'x64') xcodeArch = 'x86_64' + D.push({ CMAKE_OSX_ARCHITECTURES: xcodeArch }) + } + } + + // Custom options + for (const [key, value] of Object.entries(this.cMakeOptions)) { + D.push({ [key]: value }) + } + + // Toolset: + await this.toolset.initialize(false) + + const libsString = this.getCmakeJsLibString() + D.push({ CMAKE_JS_LIB: libsString }) + + if (environment.isWin) { + const nodeLibDefPath = this.getNodeLibDefPath() + if (nodeLibDefPath) { + const nodeLibPath = path.join(this.workDir, 'node.lib') + D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) + D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) + } + } + + if (this.toolset.generator) { + command.push('-G', this.toolset.generator) + } + if (this.toolset.platform) { + command.push('-A', this.toolset.platform) + } + if (this.toolset.toolset) { + command.push('-T', this.toolset.toolset) + } + if (this.toolset.cppCompilerPath) { + D.push({ CMAKE_CXX_COMPILER: this.toolset.cppCompilerPath }) + } + if (this.toolset.cCompilerPath) { + D.push({ CMAKE_C_COMPILER: this.toolset.cCompilerPath }) + } + if (this.toolset.compilerFlags.length) { + D.push({ CMAKE_CXX_FLAGS: this.toolset.compilerFlags.join(' ') }) + } + if (this.toolset.linkerFlags.length) { + D.push({ CMAKE_SHARED_LINKER_FLAGS: this.toolset.linkerFlags.join(' ') }) + } + if (this.toolset.makePath) { + D.push({ CMAKE_MAKE_PROGRAM: this.toolset.makePath }) + } + + // Load NPM config + for (const [key, value] of Object.entries(npmConfigData)) { + if (key.startsWith('cmake_')) { + const sk = key.substr(6) + if (sk && value) { + D.push({ [sk]: value }) + } + } + } + + command = command.concat( + D.map(function (p) { + return '-D' + Object.keys(p)[0] + '=' + Object.values(p)[0] + }), + ) + + return command.concat(this.extraCMakeArgs) +} CMake.prototype.getCmakeJsLibString = function () { - - const libs = [] - if (environment.isWin) { - const nodeLibDefPath = this.getNodeLibDefPath() - if (nodeLibDefPath) { - libs.push(path.join(this.workDir, 'node.lib')) - } else { - libs.push(...this.dist.winLibs) - } - } - return libs.join(";"); -}; + const libs = [] + if (environment.isWin) { + const nodeLibDefPath = this.getNodeLibDefPath() + if (nodeLibDefPath) { + libs.push(path.join(this.workDir, 'node.lib')) + } else { + libs.push(...this.dist.winLibs) + } + } + return libs.join(';') +} CMake.prototype.getCmakeJsIncludeString = async function () { - let incPaths = []; - if (!this.options.isNodeApi) { - // Include and lib: - if (this.dist.headerOnly) { - incPaths = [path.join(this.dist.internalPath, "/include/node")]; - } - else { - const nodeH = path.join(this.dist.internalPath, "/src"); - const v8H = path.join(this.dist.internalPath, "/deps/v8/include"); - const uvH = path.join(this.dist.internalPath, "/deps/uv/include"); - incPaths = [nodeH, v8H, uvH]; - } - - // NAN - const nanH = await locateNAN(this.projectRoot); - if (nanH) { - incPaths.push(nanH); - } - } else { - // Base headers - const apiHeaders = require('node-api-headers') - incPaths.push(apiHeaders.include_dir) - - // Node-api - const napiH = await locateNodeApi(this.projectRoot) - if (napiH) { - incPaths.push(napiH) - } - } - - return incPaths.join(";"); -}; + let incPaths = [] + if (!this.options.isNodeApi) { + // Include and lib: + if (this.dist.headerOnly) { + incPaths = [path.join(this.dist.internalPath, '/include/node')] + } else { + const nodeH = path.join(this.dist.internalPath, '/src') + const v8H = path.join(this.dist.internalPath, '/deps/v8/include') + const uvH = path.join(this.dist.internalPath, '/deps/uv/include') + incPaths = [nodeH, v8H, uvH] + } + + // NAN + const nanH = await locateNAN(this.projectRoot) + if (nanH) { + incPaths.push(nanH) + } + } else { + // Base headers + const apiHeaders = require('node-api-headers') + incPaths.push(apiHeaders.include_dir) + + // Node-api + const napiH = await locateNodeApi(this.projectRoot) + if (napiH) { + incPaths.push(napiH) + } + } + + return incPaths.join(';') +} CMake.prototype.getCmakeJsSrcString = function () { - const srcPaths = []; - if (environment.isWin) { - const delayHook = path.normalize(path.join(__dirname, 'cpp', 'win_delay_load_hook.cc')); + const srcPaths = [] + if (environment.isWin) { + const delayHook = path.normalize(path.join(__dirname, 'cpp', 'win_delay_load_hook.cc')) - srcPaths.push(delayHook.replace(/\\/gm, '/')); - } + srcPaths.push(delayHook.replace(/\\/gm, '/')) + } - return srcPaths.join(";"); -}; + return srcPaths.join(';') +} CMake.prototype.getNodeLibDefPath = function () { - return environment.isWin && this.options.isNodeApi ? headers.def_paths.node_api_def : undefined + return environment.isWin && this.options.isNodeApi ? headers.def_paths.node_api_def : undefined } CMake.prototype.configure = async function () { - this.verifyIfAvailable(); - - this.log.info("CMD", "CONFIGURE"); - const listPath = path.join(this.projectRoot, "CMakeLists.txt"); - const command = await this.getConfigureCommand(); - - try { - await fs.lstat(listPath); - } - catch (e) { - throw new Error("'" + listPath + "' not found."); - } - - try { - await fs.ensureDir(this.workDir); - } - catch (e) { - // Ignore - } - - const cwd = process.cwd(); - process.chdir(this.workDir); - try { - await this._run(command); - } - finally { - process.chdir(cwd); - } -}; + this.verifyIfAvailable() + + this.log.info('CMD', 'CONFIGURE') + const listPath = path.join(this.projectRoot, 'CMakeLists.txt') + const command = await this.getConfigureCommand() + + try { + await fs.lstat(listPath) + } catch (e) { + throw new Error("'" + listPath + "' not found.") + } + + try { + await fs.ensureDir(this.workDir) + } catch (e) { + // Ignore + } + + const cwd = process.cwd() + process.chdir(this.workDir) + try { + await this._run(command) + } finally { + process.chdir(cwd) + } +} CMake.prototype.ensureConfigured = async function () { - try { - await fs.lstat(path.join(this.workDir, "CMakeCache.txt")); - } - catch (e) { - await this.configure(); - } -}; + try { + await fs.lstat(path.join(this.workDir, 'CMakeCache.txt')) + } catch (e) { + await this.configure() + } +} CMake.prototype.getBuildCommand = function () { - const command = [this.path, "--build", this.workDir, "--config", this.config]; - if (this.options.target) { - command.push("--target", this.options.target); - } - if (this.options.parallel) { - command.push("--parallel", this.options.parallel); - } - return Promise.resolve(command.concat(this.extraCMakeArgs)); -}; + const command = [this.path, '--build', this.workDir, '--config', this.config] + if (this.options.target) { + command.push('--target', this.options.target) + } + if (this.options.parallel) { + command.push('--parallel', this.options.parallel) + } + return Promise.resolve(command.concat(this.extraCMakeArgs)) +} CMake.prototype.build = async function () { - this.verifyIfAvailable(); + this.verifyIfAvailable() - await this.ensureConfigured(); - const buildCommand = await this.getBuildCommand(); - this.log.info("CMD", "BUILD"); - await this._run(buildCommand); -}; + await this.ensureConfigured() + const buildCommand = await this.getBuildCommand() + this.log.info('CMD', 'BUILD') + await this._run(buildCommand) +} CMake.prototype.getCleanCommand = function () { - return [this.path, "-E", "remove_directory", this.workDir].concat(this.extraCMakeArgs); -}; + return [this.path, '-E', 'remove_directory', this.workDir].concat(this.extraCMakeArgs) +} CMake.prototype.clean = function () { - this.verifyIfAvailable(); + this.verifyIfAvailable() - this.log.info("CMD", "CLEAN"); - return this._run(this.getCleanCommand()); -}; + this.log.info('CMD', 'CLEAN') + return this._run(this.getCleanCommand()) +} CMake.prototype.reconfigure = async function () { - this.extraCMakeArgs = []; - await this.clean(); - await this.configure(); -}; + this.extraCMakeArgs = [] + await this.clean() + await this.configure() +} CMake.prototype.rebuild = async function () { - this.extraCMakeArgs = []; - await this.clean(); - await this.build(); -}; + this.extraCMakeArgs = [] + await this.clean() + await this.build() +} CMake.prototype.compile = async function () { - this.extraCMakeArgs = []; - try { - await this.build(); - } - catch (e) { - this.log.info("REP", "Build has been failed, trying to do a full rebuild."); - await this.rebuild(); - } -}; + this.extraCMakeArgs = [] + try { + await this.build() + } catch (e) { + this.log.info('REP', 'Build has been failed, trying to do a full rebuild.') + await this.rebuild() + } +} CMake.prototype._run = function (command) { - this.log.info("RUN", command); - return processHelpers.run(command, {silent: this.silent}); -}; + this.log.info('RUN', command) + return processHelpers.run(command, { silent: this.silent }) +} -module.exports = CMake; +module.exports = CMake diff --git a/lib/cmLog.js b/lib/cmLog.js index f1e85a60..eba5c9b2 100644 --- a/lib/cmLog.js +++ b/lib/cmLog.js @@ -1,76 +1,69 @@ -"use strict"; -const log = require("npmlog"); +'use strict' +const log = require('npmlog') function CMLog(options) { - this.options = options || {}; - this.debug = require("debug")(this.options.logName || "cmake-js"); + this.options = options || {} + this.debug = require('debug')(this.options.logName || 'cmake-js') } Object.defineProperties(CMLog.prototype, { - level: { - get: function() { - if (this.options.noLog) { - return "silly"; - } - else { - return log.level; - } - } - } -}); + level: { + get: function () { + if (this.options.noLog) { + return 'silly' + } else { + return log.level + } + }, + }, +}) -CMLog.prototype.silly = function(cat, msg) { - if (this.options.noLog) { - this.debug(cat + ": " + msg); - } - else { - log.silly(cat, msg); - } -}; +CMLog.prototype.silly = function (cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.silly(cat, msg) + } +} -CMLog.prototype.verbose = function(cat, msg) { - if (this.options.noLog) { - this.debug(cat + ": " + msg); - } - else { - log.verbose(cat, msg); - } -}; +CMLog.prototype.verbose = function (cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.verbose(cat, msg) + } +} -CMLog.prototype.info = function(cat, msg) { - if (this.options.noLog) { - this.debug(cat + ": " + msg); - } - else { - log.info(cat, msg); - } -}; +CMLog.prototype.info = function (cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.info(cat, msg) + } +} -CMLog.prototype.warn = function(cat, msg) { - if (this.options.noLog) { - this.debug(cat + ": " + msg); - } - else { - log.warn(cat, msg); - } -}; +CMLog.prototype.warn = function (cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.warn(cat, msg) + } +} -CMLog.prototype.http = function(cat, msg) { - if (this.options.noLog) { - this.debug(cat + ": " + msg); - } - else { - log.http(cat, msg); - } -}; +CMLog.prototype.http = function (cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.http(cat, msg) + } +} -CMLog.prototype.error = function(cat, msg) { - if (this.options.noLog) { - this.debug(cat + ": " + msg); - } - else { - log.error(cat, msg); - } -}; +CMLog.prototype.error = function (cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.error(cat, msg) + } +} -module.exports = CMLog; \ No newline at end of file +module.exports = CMLog diff --git a/lib/dist.js b/lib/dist.js index a019e017..bf5ae1ac 100644 --- a/lib/dist.js +++ b/lib/dist.js @@ -1,195 +1,191 @@ -"use strict"; -const environment = require("./environment"); -const path = require("path"); -const urljoin = require("url-join"); -const fs = require("fs-extra"); -const CMLog = require("./cmLog"); -const TargetOptions = require("./targetOptions"); -const runtimePaths = require("./runtimePaths"); -const Downloader = require("./downloader"); +'use strict' +const environment = require('./environment') +const path = require('path') +const urljoin = require('url-join') +const fs = require('fs-extra') +const CMLog = require('./cmLog') +const TargetOptions = require('./targetOptions') +const runtimePaths = require('./runtimePaths') +const Downloader = require('./downloader') function testSum(sums, sum, fPath) { - const serverSum = sums.find(function (s) { - return s.getPath === fPath; - }); - if (serverSum && serverSum.sum === sum) { - return; - } - throw new Error("SHA sum of file '" + fPath + "' mismatch!"); + const serverSum = sums.find(function (s) { + return s.getPath === fPath + }) + if (serverSum && serverSum.sum === sum) { + return + } + throw new Error("SHA sum of file '" + fPath + "' mismatch!") } function Dist(options) { - this.options = options || {}; - this.log = new CMLog(this.options); - this.targetOptions = new TargetOptions(this.options); - this.downloader = new Downloader(this.options); + this.options = options || {} + this.log = new CMLog(this.options) + this.targetOptions = new TargetOptions(this.options) + this.downloader = new Downloader(this.options) } // Props Object.defineProperties(Dist.prototype, { - internalPath: { - get: function () { - const cacheDirectory = ".cmake-js"; - const runtimeArchDirectory = (this.targetOptions.runtime) + "-" + this.targetOptions.arch; - const runtimeVersionDirectory = "v" + this.targetOptions.runtimeVersion; - - return this.options.runtimeDirectory || - path.join(environment.home, - cacheDirectory, - runtimeArchDirectory, - runtimeVersionDirectory); - } - }, - externalPath: { - get: function () { - return runtimePaths.get(this.targetOptions).externalPath; - } - }, - downloaded: { - get: function () { - let headers = false; - let libs = true; - let stat = getStat(this.internalPath); - if (stat.isDirectory()) { - if (this.headerOnly) { - stat = getStat(path.join(this.internalPath, "include/node/node.h")); - headers = stat.isFile(); - } - else { - stat = getStat(path.join(this.internalPath, "src/node.h")); - if (stat.isFile()) { - stat = getStat(path.join(this.internalPath, "deps/v8/include/v8.h")); - headers = stat.isFile(); - } - } - if (environment.isWin) { - for (const libPath of this.winLibs) { - stat = getStat(libPath); - libs = libs && stat.isFile(); - } - } - } - return headers && libs; - - function getStat(path) { - try { - return fs.statSync(path); - } - catch (e) { - return { - isFile: () => false, - isDirectory: () => false - }; - } - } - } - }, - winLibs: { - get: function () { - const libs = runtimePaths.get(this.targetOptions).winLibs; - const result = []; - for (const lib of libs) { - result.push(path.join(this.internalPath, lib.dir, lib.name)); - } - return result; - } - }, - headerOnly: { - get: function () { - return runtimePaths.get(this.targetOptions).headerOnly; - } - } -}); + internalPath: { + get: function () { + const cacheDirectory = '.cmake-js' + const runtimeArchDirectory = this.targetOptions.runtime + '-' + this.targetOptions.arch + const runtimeVersionDirectory = 'v' + this.targetOptions.runtimeVersion + + return ( + this.options.runtimeDirectory || + path.join(environment.home, cacheDirectory, runtimeArchDirectory, runtimeVersionDirectory) + ) + }, + }, + externalPath: { + get: function () { + return runtimePaths.get(this.targetOptions).externalPath + }, + }, + downloaded: { + get: function () { + let headers = false + let libs = true + let stat = getStat(this.internalPath) + if (stat.isDirectory()) { + if (this.headerOnly) { + stat = getStat(path.join(this.internalPath, 'include/node/node.h')) + headers = stat.isFile() + } else { + stat = getStat(path.join(this.internalPath, 'src/node.h')) + if (stat.isFile()) { + stat = getStat(path.join(this.internalPath, 'deps/v8/include/v8.h')) + headers = stat.isFile() + } + } + if (environment.isWin) { + for (const libPath of this.winLibs) { + stat = getStat(libPath) + libs = libs && stat.isFile() + } + } + } + return headers && libs + + function getStat(path) { + try { + return fs.statSync(path) + } catch (e) { + return { + isFile: () => false, + isDirectory: () => false, + } + } + } + }, + }, + winLibs: { + get: function () { + const libs = runtimePaths.get(this.targetOptions).winLibs + const result = [] + for (const lib of libs) { + result.push(path.join(this.internalPath, lib.dir, lib.name)) + } + return result + }, + }, + headerOnly: { + get: function () { + return runtimePaths.get(this.targetOptions).headerOnly + }, + }, +}) // Methods Dist.prototype.ensureDownloaded = async function () { - if (!this.downloaded) { - await this.download(); - } -}; + if (!this.downloaded) { + await this.download() + } +} Dist.prototype.download = async function () { - const log = this.log; - log.info("DIST", "Downloading distribution files to: " + this.internalPath); - await fs.ensureDir(this.internalPath); - const sums = await this._downloadShaSums(); - await Promise.all([this._downloadLibs(sums), this._downloadTar(sums)]); -}; + const log = this.log + log.info('DIST', 'Downloading distribution files to: ' + this.internalPath) + await fs.ensureDir(this.internalPath) + const sums = await this._downloadShaSums() + await Promise.all([this._downloadLibs(sums), this._downloadTar(sums)]) +} Dist.prototype._downloadShaSums = async function () { - if (this.targetOptions.runtime === "node") { - const sumUrl = urljoin(this.externalPath, "SHASUMS256.txt"); - const log = this.log; - log.http("DIST", "\t- " + sumUrl); - return (await this.downloader.downloadString(sumUrl)) - .split("\n") - .map(function (line) { - const parts = line.split(/\s+/); - return { - getPath: parts[1], - sum: parts[0] - }; - }) - .filter(function (i) { - return i.getPath && i.sum; - }); - } - else { - return null; - } -}; + if (this.targetOptions.runtime === 'node') { + const sumUrl = urljoin(this.externalPath, 'SHASUMS256.txt') + const log = this.log + log.http('DIST', '\t- ' + sumUrl) + return (await this.downloader.downloadString(sumUrl)) + .split('\n') + .map(function (line) { + const parts = line.split(/\s+/) + return { + getPath: parts[1], + sum: parts[0], + } + }) + .filter(function (i) { + return i.getPath && i.sum + }) + } else { + return null + } +} Dist.prototype._downloadTar = async function (sums) { - const log = this.log; - const self = this; - const tarLocalPath = runtimePaths.get(self.targetOptions).tarPath; - const tarUrl = urljoin(self.externalPath, tarLocalPath); - log.http("DIST", "\t- " + tarUrl); - - const sum = await this.downloader.downloadTgz(tarUrl, { - hash: sums ? "sha256" : null, - cwd: self.internalPath, - strip: 1, - filter: function (entryPath) { - if (entryPath === self.internalPath) { - return true; - } - const ext = path.extname(entryPath); - return ext && ext.toLowerCase() === ".h"; - } - }); - - if (sums) { - testSum(sums, sum, tarLocalPath); - } -}; + const log = this.log + const self = this + const tarLocalPath = runtimePaths.get(self.targetOptions).tarPath + const tarUrl = urljoin(self.externalPath, tarLocalPath) + log.http('DIST', '\t- ' + tarUrl) + + const sum = await this.downloader.downloadTgz(tarUrl, { + hash: sums ? 'sha256' : null, + cwd: self.internalPath, + strip: 1, + filter: function (entryPath) { + if (entryPath === self.internalPath) { + return true + } + const ext = path.extname(entryPath) + return ext && ext.toLowerCase() === '.h' + }, + }) + + if (sums) { + testSum(sums, sum, tarLocalPath) + } +} Dist.prototype._downloadLibs = async function (sums) { - const log = this.log; - const self = this; - if (!environment.isWin) { - return; - } - - const paths = runtimePaths.get(self.targetOptions); - for (const dirs of paths.winLibs) { - const subDir = dirs.dir; - const fn = dirs.name; - const fPath = subDir ? urljoin(subDir, fn) : fn; - const libUrl = urljoin(self.externalPath, fPath); - log.http("DIST", "\t- " + libUrl); - - await fs.ensureDir(path.join(self.internalPath, subDir)); - - const sum = await this.downloader.downloadFile(libUrl, { - path: path.join(self.internalPath, fPath), - hash: sums ? "sha256" : null - }); - - if (sums) { - testSum(sums, sum, fPath); - } - } -}; - -module.exports = Dist; + const log = this.log + const self = this + if (!environment.isWin) { + return + } + + const paths = runtimePaths.get(self.targetOptions) + for (const dirs of paths.winLibs) { + const subDir = dirs.dir + const fn = dirs.name + const fPath = subDir ? urljoin(subDir, fn) : fn + const libUrl = urljoin(self.externalPath, fPath) + log.http('DIST', '\t- ' + libUrl) + + await fs.ensureDir(path.join(self.internalPath, subDir)) + + const sum = await this.downloader.downloadFile(libUrl, { + path: path.join(self.internalPath, fPath), + hash: sums ? 'sha256' : null, + }) + + if (sums) { + testSum(sums, sum, fPath) + } + } +} + +module.exports = Dist diff --git a/lib/downloader.js b/lib/downloader.js index 2c109ac6..46d48093 100644 --- a/lib/downloader.js +++ b/lib/downloader.js @@ -1,95 +1,95 @@ -"use strict"; -const crypto = require("crypto"); -const axios = require("axios"); -const MemoryStream = require("memory-stream"); -const zlib = require("zlib"); -const tar = require("tar"); -const fs = require("fs"); -const CMLog = require("./cmLog"); +'use strict' +const crypto = require('crypto') +const axios = require('axios') +const MemoryStream = require('memory-stream') +const zlib = require('zlib') +const tar = require('tar') +const fs = require('fs') +const CMLog = require('./cmLog') function Downloader(options) { - this.options = options || {}; - this.log = new CMLog(this.options); + this.options = options || {} + this.log = new CMLog(this.options) } -Downloader.prototype.downloadToStream = function(url, stream, hash) { - const self = this; - const shasum = hash ? crypto.createHash(hash) : null; - return new Promise(function (resolve, reject) { - let length = 0; - let done = 0; - let lastPercent = 0; - axios - .get(url, { responseType: "stream" }) - .then(function (response) { - length = parseInt(response.headers["content-length"]); - if (typeof length !== 'number') { - length = 0; - } +Downloader.prototype.downloadToStream = function (url, stream, hash) { + const self = this + const shasum = hash ? crypto.createHash(hash) : null + return new Promise(function (resolve, reject) { + let length = 0 + let done = 0 + let lastPercent = 0 + axios + .get(url, { responseType: 'stream' }) + .then(function (response) { + length = parseInt(response.headers['content-length']) + if (typeof length !== 'number') { + length = 0 + } - response.data.on('data', function (chunk) { - if (shasum) { - shasum.update(chunk); - } - if (length) { - done += chunk.length; - let percent = done / length * 100; - percent = Math.round(percent / 10) * 10 + 10; - if (percent > lastPercent) { - self.log.verbose("DWNL", "\t" + lastPercent + "%"); - lastPercent = percent; - } - } - }); + response.data.on('data', function (chunk) { + if (shasum) { + shasum.update(chunk) + } + if (length) { + done += chunk.length + let percent = (done / length) * 100 + percent = Math.round(percent / 10) * 10 + 10 + if (percent > lastPercent) { + self.log.verbose('DWNL', '\t' + lastPercent + '%') + lastPercent = percent + } + } + }) - response.data.pipe(stream); - }) - .catch(function (err) { - reject(err); - }); + response.data.pipe(stream) + }) + .catch(function (err) { + reject(err) + }) - stream.once("error", function (err) { - reject(err); - }); + stream.once('error', function (err) { + reject(err) + }) - stream.once("finish", function () { - resolve(shasum ? shasum.digest("hex") : undefined); - }); - }); -}; + stream.once('finish', function () { + resolve(shasum ? shasum.digest('hex') : undefined) + }) + }) +} Downloader.prototype.downloadString = async function (url) { - const result = new MemoryStream(); - await this.downloadToStream(url, result); - return result.toString(); -}; + const result = new MemoryStream() + await this.downloadToStream(url, result) + return result.toString() +} Downloader.prototype.downloadFile = async function (url, options) { - if (typeof options === 'string') { - options.path = options; - } - const result = fs.createWriteStream(options.path); - const sum = await this.downloadToStream(url, result, options.hash); - this.testSum(url, sum, options); - return sum; -}; + if (typeof options === 'string') { + options.path = options + } + const result = fs.createWriteStream(options.path) + const sum = await this.downloadToStream(url, result, options.hash) + this.testSum(url, sum, options) + return sum +} Downloader.prototype.downloadTgz = async function (url, options) { - if (typeof options === 'string') { - options.cwd = options; - } - const gunzip = zlib.createGunzip(); - const extractor = tar.extract(options); - gunzip.pipe(extractor); - const sum = await this.downloadToStream(url, gunzip, options.hash); - this.testSum(url, sum, options); - return sum; -}; + if (typeof options === 'string') { + options.cwd = options + } + const gunzip = zlib.createGunzip() + const extractor = tar.extract(options) + gunzip.pipe(extractor) + const sum = await this.downloadToStream(url, gunzip, options.hash) + this.testSum(url, sum, options) + return sum +} -Downloader.prototype.testSum = function(url, sum, options) { - if (options.hash && sum && options.sum && options.sum !== sum) { - throw new Error(options.hash.toUpperCase() + " sum of download '" + url + "' mismatch!"); - } -}; +Downloader.prototype.testSum = function (url, sum, options) { + if (options.hash && sum && options.sum && options.sum !== sum) { + throw new Error(options.hash.toUpperCase() + " sum of download '" + url + "' mismatch!") + } +} -module.exports = Downloader; +module.exports = Downloader diff --git a/lib/environment.js b/lib/environment.js index d8e32d5c..efb96572 100644 --- a/lib/environment.js +++ b/lib/environment.js @@ -1,107 +1,103 @@ -"use strict"; -const os = require("os"); -const which = require("which"); +'use strict' +const os = require('os') +const which = require('which') -const environment = module.exports = { - cmakeJsVersion: require("../package.json").version, - platform: os.platform(), - isWin: os.platform() === "win32", - isLinux: os.platform() === "linux", - isOSX: os.platform() === "darwin", - arch: os.arch(), - isX86: os.arch() === "ia32" || os.arch() === "x86", - isX64: os.arch() === "x64", - isArm: os.arch() === "arm", - runtime: "node", - runtimeVersion: process.versions.node, - home: process.env[(os.platform() === "win32") ? "USERPROFILE" : "HOME"], - EOL: os.EOL -}; +const environment = (module.exports = { + cmakeJsVersion: require('../package.json').version, + platform: os.platform(), + isWin: os.platform() === 'win32', + isLinux: os.platform() === 'linux', + isOSX: os.platform() === 'darwin', + arch: os.arch(), + isX86: os.arch() === 'ia32' || os.arch() === 'x86', + isX64: os.arch() === 'x64', + isArm: os.arch() === 'arm', + runtime: 'node', + runtimeVersion: process.versions.node, + home: process.env[os.platform() === 'win32' ? 'USERPROFILE' : 'HOME'], + EOL: os.EOL, +}) Object.defineProperties(environment, { - isPosix: { - get: function () { - return !this.isWin; - } - }, - _isNinjaAvailable: { - value: null, - writable: true - }, - isNinjaAvailable: { - get: function() { - if (this._isNinjaAvailable === null) { - this._isNinjaAvailable = false; - try { - if (which.sync("ninja")) { - this._isNinjaAvailable = true; - } - } - catch (e) { - // Ignore - } - } - return this._isNinjaAvailable; - } - }, - _isMakeAvailable: { - value: null, - writable: true - }, - isMakeAvailable: { - get: function() { - if (this._isMakeAvailable === null) { - this._isMakeAvailable = false; - try { - if (which.sync("make")) { - this._isMakeAvailable = true; - } - } - catch (e) { - // Ignore - } - } - return this._isMakeAvailable; - } - }, - _isGPPAvailable: { - value: null, - writable: true - }, - isGPPAvailable: { - get: function() { - if (this._isGPPAvailable === null) { - this._isGPPAvailable = false; - try { - if (which.sync("g++")) { - this._isGPPAvailable = true; - } - } - catch (e) { - // Ignore - } - } - return this._isGPPAvailable; - } - }, - _isClangAvailable: { - value: null, - writable: true - }, - isClangAvailable: { - get: function() { - if (this._isClangAvailable === null) { - this._isClangAvailable = false; - try { - if (which.sync("clang++")) { - this._isClangAvailable = true; - } - } - catch (e) { - // Ignore - } - } - return this._isClangAvailable; - } - } -}); \ No newline at end of file + isPosix: { + get: function () { + return !this.isWin + }, + }, + _isNinjaAvailable: { + value: null, + writable: true, + }, + isNinjaAvailable: { + get: function () { + if (this._isNinjaAvailable === null) { + this._isNinjaAvailable = false + try { + if (which.sync('ninja')) { + this._isNinjaAvailable = true + } + } catch (e) { + // Ignore + } + } + return this._isNinjaAvailable + }, + }, + _isMakeAvailable: { + value: null, + writable: true, + }, + isMakeAvailable: { + get: function () { + if (this._isMakeAvailable === null) { + this._isMakeAvailable = false + try { + if (which.sync('make')) { + this._isMakeAvailable = true + } + } catch (e) { + // Ignore + } + } + return this._isMakeAvailable + }, + }, + _isGPPAvailable: { + value: null, + writable: true, + }, + isGPPAvailable: { + get: function () { + if (this._isGPPAvailable === null) { + this._isGPPAvailable = false + try { + if (which.sync('g++')) { + this._isGPPAvailable = true + } + } catch (e) { + // Ignore + } + } + return this._isGPPAvailable + }, + }, + _isClangAvailable: { + value: null, + writable: true, + }, + isClangAvailable: { + get: function () { + if (this._isClangAvailable === null) { + this._isClangAvailable = false + try { + if (which.sync('clang++')) { + this._isClangAvailable = true + } + } catch (e) { + // Ignore + } + } + return this._isClangAvailable + }, + }, +}) diff --git a/lib/import/find-visualstudio.js b/lib/import/find-visualstudio.js index f48a5ac6..4244c7e4 100644 --- a/lib/import/find-visualstudio.js +++ b/lib/import/find-visualstudio.js @@ -4,441 +4,436 @@ const log = require('npmlog') const { existsSync } = require('fs') const { win32: path } = require('path') const { regSearchKeys, execFile, logWithPrefix } = require('./util') -const semver = require("semver"); +const semver = require('semver') class VisualStudioFinder { - static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio() - - log = logWithPrefix(log, 'find VS') - - regSearchKeys = regSearchKeys - - constructor (nodeSemver, configMsvsVersion) { - this.nodeSemver = nodeSemver - this.configMsvsVersion = configMsvsVersion - this.errorLog = [] - this.validVersions = [] - } - - // Logs a message at verbose level, but also saves it to be displayed later - // at error level if an error occurs. This should help diagnose the problem. - addLog (message) { - this.log.verbose(message) - this.errorLog.push(message) - } - - async findVisualStudio () { - this.configVersionYear = null - this.configPath = null - if (this.configMsvsVersion) { - this.addLog('msvs_version was set from command line or npm config') - if (this.configMsvsVersion.match(/^\d{4}$/)) { - this.configVersionYear = parseInt(this.configMsvsVersion, 10) - this.addLog( - `- looking for Visual Studio version ${this.configVersionYear}`) - } else { - this.configPath = path.resolve(this.configMsvsVersion) - this.addLog( - `- looking for Visual Studio installed in "${this.configPath}"`) - } - } else { - this.addLog('msvs_version not set from command line or npm config') - } - - if (process.env.VCINSTALLDIR) { - this.envVcInstallDir = - path.resolve(process.env.VCINSTALLDIR, '..') - this.addLog('running in VS Command Prompt, installation path is:\n' + - `"${this.envVcInstallDir}"\n- will only use this version`) - } else { - this.addLog('VCINSTALLDIR not set, not running in VS Command Prompt') - } - - const checks = [ - () => this.findVisualStudio2017OrNewer(), - () => this.findVisualStudio2015(), - () => this.findVisualStudio2013() - ] - - for (const check of checks) { - const info = await check() - if (info) { - return this.succeed(info) - } - } - - return this.fail() - } - - succeed (info) { - this.log.info(`using VS${info.versionYear} (${info.version}) found at:` + - `\n"${info.path}"` + - '\nrun with --verbose for detailed information') - return info - } - - fail () { - if (this.configMsvsVersion && this.envVcInstallDir) { - this.errorLog.push( - 'msvs_version does not match this VS Command Prompt or the', - 'installation cannot be used.') - } else if (this.configMsvsVersion) { - // If msvs_version was specified but finding VS failed, print what would - // have been accepted - this.errorLog.push('') - if (this.validVersions) { - this.errorLog.push('valid versions for msvs_version:') - this.validVersions.forEach((version) => { - this.errorLog.push(`- "${version}"`) - }) - } else { - this.errorLog.push('no valid versions for msvs_version were found') - } - } - - const errorLog = this.errorLog.join('\n') - - // For Windows 80 col console, use up to the column before the one marked - // with X (total 79 chars including logger prefix, 62 chars usable here): - // X - const infoLog = [ - '**************************************************************', - 'You need to install the latest version of Visual Studio', - 'including the "Desktop development with C++" workload.', - 'For more information consult the documentation at:', - 'https://github.com/nodejs/node-gyp#on-windows', - '**************************************************************' - ].join('\n') - - this.log.error(`\n${errorLog}\n\n${infoLog}\n`) - throw new Error('Could not find any Visual Studio installation to use') - } - - // Invoke the PowerShell script to get information about Visual Studio 2017 - // or newer installations - async findVisualStudio2017OrNewer () { - const ps = path.join(process.env.SystemRoot, 'System32', - 'WindowsPowerShell', 'v1.0', 'powershell.exe') - const csFile = path.join(__dirname, 'Find-VisualStudio.cs') - const psArgs = [ - '-ExecutionPolicy', - 'Unrestricted', - '-NoProfile', - '-Command', - '&{Add-Type -Path \'' + csFile + '\';' + '[VisualStudioConfiguration.Main]::PrintJson()}' - ] - - this.log.silly('Running', ps, psArgs) - const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' }) - return this.parseData(err, stdout, stderr) - } - - // Parse the output of the PowerShell script and look for an installation - // of Visual Studio 2017 or newer to use - parseData (err, stdout, stderr) { - this.log.silly('PS stderr = %j', stderr) - - const failPowershell = () => { - this.addLog( - 'could not use PowerShell to find Visual Studio 2017 or newer, try re-running with \'--loglevel silly\' for more details') - return null - } - - if (err) { - this.log.silly('PS err = %j', err && (err.stack || err)) - return failPowershell() - } - - let vsInfo - try { - vsInfo = JSON.parse(stdout) - } catch (e) { - this.log.silly('PS stdout = %j', stdout) - this.log.silly(e) - return failPowershell() - } - - if (!Array.isArray(vsInfo)) { - this.log.silly('PS stdout = %j', stdout) - return failPowershell() - } - - vsInfo = vsInfo.map((info) => { - this.log.silly(`processing installation: "${info.path}"`) - info.path = path.resolve(info.path) - const ret = this.getVersionInfo(info) - ret.path = info.path - ret.msBuild = this.getMSBuild(info, ret.versionYear) - ret.toolset = this.getToolset(info, ret.versionYear) - ret.sdk = this.getSDK(info) - return ret - }) - this.log.silly('vsInfo:', vsInfo) - - // Remove future versions or errors parsing version number - vsInfo = vsInfo.filter((info) => { - if (info.versionYear) { - return true - } - this.addLog(`unknown version "${info.version}" found at "${info.path}"`) - return false - }) - - // Sort to place newer versions first - vsInfo.sort((a, b) => b.versionYear - a.versionYear) - - for (let i = 0; i < vsInfo.length; ++i) { - const info = vsInfo[i] - this.addLog(`checking VS${info.versionYear} (${info.version}) found ` + - `at:\n"${info.path}"`) - - if (info.msBuild) { - this.addLog('- found "Visual Studio C++ core features"') - } else { - this.addLog('- "Visual Studio C++ core features" missing') - continue - } - - if (info.toolset) { - this.addLog(`- found VC++ toolset: ${info.toolset}`) - } else { - this.addLog('- missing any VC++ toolset') - continue - } - - if (info.sdk) { - this.addLog(`- found Windows SDK: ${info.sdk}`) - } else { - this.addLog('- missing any Windows SDK') - continue - } - - if (!this.checkConfigVersion(info.versionYear, info.path)) { - continue - } - - return info - } - - this.addLog( - 'could not find a version of Visual Studio 2017 or newer to use') - return null - } - - // Helper - process version information - getVersionInfo (info) { - const match = /^(\d+)\.(\d+)\..*/.exec(info.version) - if (!match) { - this.log.silly('- failed to parse version:', info.version) - return {} - } - this.log.silly('- version match = %j', match) - const ret = { - version: info.version, - versionMajor: parseInt(match[1], 10), - versionMinor: parseInt(match[2], 10) - } - if (ret.versionMajor === 15) { - ret.versionYear = 2017 - return ret - } - if (ret.versionMajor === 16) { - ret.versionYear = 2019 - return ret - } - if (ret.versionMajor === 17) { - ret.versionYear = 2022 - return ret - } - this.log.silly('- unsupported version:', ret.versionMajor) - return {} - } - - msBuildPathExists (path) { - return existsSync(path) - } - - // Helper - process MSBuild information - getMSBuild (info, versionYear) { - const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base' - const msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') - const msbuildPathArm64 = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'arm64', 'MSBuild.exe') - if (info.packages.indexOf(pkg) !== -1) { - this.log.silly('- found VC.MSBuild.Base') - if (versionYear === 2017) { - return path.join(info.path, 'MSBuild', '15.0', 'Bin', 'MSBuild.exe') - } - if (versionYear === 2019) { - return msbuildPath - } - } - /** - * Visual Studio 2022 doesn't have the MSBuild package. - * Support for compiling _on_ ARM64 was added in MSVC 14.32.31326, - * so let's leverage it if the user has an ARM64 device. - */ - if (process.arch === 'arm64' && this.msBuildPathExists(msbuildPathArm64)) { - return msbuildPathArm64 - } else if (this.msBuildPathExists(msbuildPath)) { - return msbuildPath - } - return null - } - - // Helper - process toolset information - getToolset (info, versionYear) { - const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' - const express = 'Microsoft.VisualStudio.WDExpress' - - if (info.packages.indexOf(pkg) !== -1) { - this.log.silly('- found VC.Tools.x86.x64') - } else if (info.packages.indexOf(express) !== -1) { - this.log.silly('- found Visual Studio Express (looking for toolset)') - } else { - return null - } - - if (versionYear === 2017) { - return 'v141' - } else if (versionYear === 2019) { - return 'v142' - } else if (versionYear === 2022) { - return 'v143' - } - this.log.silly('- invalid versionYear:', versionYear) - return null - } - - // Helper - process Windows SDK information - getSDK (info) { - const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK' - const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.' - const win11SDKPrefix = 'Microsoft.VisualStudio.Component.Windows11SDK.' - - let Win10or11SDKVer = 0 - info.packages.forEach((pkg) => { - if (!pkg.startsWith(win10SDKPrefix) && !pkg.startsWith(win11SDKPrefix)) { - return - } - const parts = pkg.split('.') - if (parts.length > 5 && parts[5] !== 'Desktop') { - this.log.silly('- ignoring non-Desktop Win10/11SDK:', pkg) - return - } - const foundSdkVer = parseInt(parts[4], 10) - if (isNaN(foundSdkVer)) { - // Microsoft.VisualStudio.Component.Windows10SDK.IpOverUsb - this.log.silly('- failed to parse Win10/11SDK number:', pkg) - return - } - this.log.silly('- found Win10/11SDK:', foundSdkVer) - Win10or11SDKVer = Math.max(Win10or11SDKVer, foundSdkVer) - }) - - if (Win10or11SDKVer !== 0) { - return `10.0.${Win10or11SDKVer}.0` - } else if (info.packages.indexOf(win8SDK) !== -1) { - this.log.silly('- found Win8SDK') - return '8.1' - } - return null - } - - // Find an installation of Visual Studio 2015 to use - async findVisualStudio2015 () { - if (semver.gte(this.nodeSemver, "19.0.0")) { - this.addLog( - 'not looking for VS2015 as it is only supported up to Node.js 18') - return null - } - return this.findOldVS({ - version: '14.0', - versionMajor: 14, - versionMinor: 0, - versionYear: 2015, - toolset: 'v140' - }) - } - - // Find an installation of Visual Studio 2013 to use - async findVisualStudio2013 () { - if (semver.gte(this.nodeSemver, "9.0.0")) { - this.addLog( - 'not looking for VS2013 as it is only supported up to Node.js 8') - return null - } - return this.findOldVS({ - version: '12.0', - versionMajor: 12, - versionMinor: 0, - versionYear: 2013, - toolset: 'v120' - }) - } - - // Helper - common code for VS2013 and VS2015 - async findOldVS (info) { - const regVC7 = ['HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7', - 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7'] - const regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions' - - this.addLog(`looking for Visual Studio ${info.versionYear}`) - try { - let res = await this.regSearchKeys(regVC7, info.version, []) - const vsPath = path.resolve(res, '..') - this.addLog(`- found in "${vsPath}"`) - const msBuildRegOpts = process.arch === 'ia32' ? [] : ['/reg:32'] - - try { - res = await this.regSearchKeys([`${regMSBuild}\\${info.version}`], 'MSBuildToolsPath', msBuildRegOpts) - } catch (err) { - this.addLog('- could not find MSBuild in registry for this version') - return null - } - - const msBuild = path.join(res, 'MSBuild.exe') - this.addLog(`- MSBuild in "${msBuild}"`) - - if (!this.checkConfigVersion(info.versionYear, vsPath)) { - return null - } - - info.path = vsPath - info.msBuild = msBuild - info.sdk = null - return info - } catch (err) { - this.addLog('- not found') - return null - } - } - - // After finding a usable version of Visual Studio: - // - add it to validVersions to be displayed at the end if a specific - // version was requested and not found; - // - check if this is the version that was requested. - // - check if this matches the Visual Studio Command Prompt - checkConfigVersion (versionYear, vsPath) { - this.validVersions.push(versionYear) - this.validVersions.push(vsPath) - - if (this.configVersionYear && this.configVersionYear !== versionYear) { - this.addLog('- msvs_version does not match this version') - return false - } - if (this.configPath && - path.relative(this.configPath, vsPath) !== '') { - this.addLog('- msvs_version does not point to this installation') - return false - } - if (this.envVcInstallDir && - path.relative(this.envVcInstallDir, vsPath) !== '') { - this.addLog('- does not match this Visual Studio Command Prompt') - return false - } - - return true - } + static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio() + + log = logWithPrefix(log, 'find VS') + + regSearchKeys = regSearchKeys + + constructor(nodeSemver, configMsvsVersion) { + this.nodeSemver = nodeSemver + this.configMsvsVersion = configMsvsVersion + this.errorLog = [] + this.validVersions = [] + } + + // Logs a message at verbose level, but also saves it to be displayed later + // at error level if an error occurs. This should help diagnose the problem. + addLog(message) { + this.log.verbose(message) + this.errorLog.push(message) + } + + async findVisualStudio() { + this.configVersionYear = null + this.configPath = null + if (this.configMsvsVersion) { + this.addLog('msvs_version was set from command line or npm config') + if (this.configMsvsVersion.match(/^\d{4}$/)) { + this.configVersionYear = parseInt(this.configMsvsVersion, 10) + this.addLog(`- looking for Visual Studio version ${this.configVersionYear}`) + } else { + this.configPath = path.resolve(this.configMsvsVersion) + this.addLog(`- looking for Visual Studio installed in "${this.configPath}"`) + } + } else { + this.addLog('msvs_version not set from command line or npm config') + } + + if (process.env.VCINSTALLDIR) { + this.envVcInstallDir = path.resolve(process.env.VCINSTALLDIR, '..') + this.addLog( + 'running in VS Command Prompt, installation path is:\n' + + `"${this.envVcInstallDir}"\n- will only use this version`, + ) + } else { + this.addLog('VCINSTALLDIR not set, not running in VS Command Prompt') + } + + const checks = [ + () => this.findVisualStudio2017OrNewer(), + () => this.findVisualStudio2015(), + () => this.findVisualStudio2013(), + ] + + for (const check of checks) { + const info = await check() + if (info) { + return this.succeed(info) + } + } + + return this.fail() + } + + succeed(info) { + this.log.info( + `using VS${info.versionYear} (${info.version}) found at:` + + `\n"${info.path}"` + + '\nrun with --verbose for detailed information', + ) + return info + } + + fail() { + if (this.configMsvsVersion && this.envVcInstallDir) { + this.errorLog.push('msvs_version does not match this VS Command Prompt or the', 'installation cannot be used.') + } else if (this.configMsvsVersion) { + // If msvs_version was specified but finding VS failed, print what would + // have been accepted + this.errorLog.push('') + if (this.validVersions) { + this.errorLog.push('valid versions for msvs_version:') + this.validVersions.forEach((version) => { + this.errorLog.push(`- "${version}"`) + }) + } else { + this.errorLog.push('no valid versions for msvs_version were found') + } + } + + const errorLog = this.errorLog.join('\n') + + // For Windows 80 col console, use up to the column before the one marked + // with X (total 79 chars including logger prefix, 62 chars usable here): + // X + const infoLog = [ + '**************************************************************', + 'You need to install the latest version of Visual Studio', + 'including the "Desktop development with C++" workload.', + 'For more information consult the documentation at:', + 'https://github.com/nodejs/node-gyp#on-windows', + '**************************************************************', + ].join('\n') + + this.log.error(`\n${errorLog}\n\n${infoLog}\n`) + throw new Error('Could not find any Visual Studio installation to use') + } + + // Invoke the PowerShell script to get information about Visual Studio 2017 + // or newer installations + async findVisualStudio2017OrNewer() { + const ps = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe') + const csFile = path.join(__dirname, 'Find-VisualStudio.cs') + const psArgs = [ + '-ExecutionPolicy', + 'Unrestricted', + '-NoProfile', + '-Command', + "&{Add-Type -Path '" + csFile + "';" + '[VisualStudioConfiguration.Main]::PrintJson()}', + ] + + this.log.silly('Running', ps, psArgs) + const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' }) + return this.parseData(err, stdout, stderr) + } + + // Parse the output of the PowerShell script and look for an installation + // of Visual Studio 2017 or newer to use + parseData(err, stdout, stderr) { + this.log.silly('PS stderr = %j', stderr) + + const failPowershell = () => { + this.addLog( + "could not use PowerShell to find Visual Studio 2017 or newer, try re-running with '--loglevel silly' for more details", + ) + return null + } + + if (err) { + this.log.silly('PS err = %j', err && (err.stack || err)) + return failPowershell() + } + + let vsInfo + try { + vsInfo = JSON.parse(stdout) + } catch (e) { + this.log.silly('PS stdout = %j', stdout) + this.log.silly(e) + return failPowershell() + } + + if (!Array.isArray(vsInfo)) { + this.log.silly('PS stdout = %j', stdout) + return failPowershell() + } + + vsInfo = vsInfo.map((info) => { + this.log.silly(`processing installation: "${info.path}"`) + info.path = path.resolve(info.path) + const ret = this.getVersionInfo(info) + ret.path = info.path + ret.msBuild = this.getMSBuild(info, ret.versionYear) + ret.toolset = this.getToolset(info, ret.versionYear) + ret.sdk = this.getSDK(info) + return ret + }) + this.log.silly('vsInfo:', vsInfo) + + // Remove future versions or errors parsing version number + vsInfo = vsInfo.filter((info) => { + if (info.versionYear) { + return true + } + this.addLog(`unknown version "${info.version}" found at "${info.path}"`) + return false + }) + + // Sort to place newer versions first + vsInfo.sort((a, b) => b.versionYear - a.versionYear) + + for (let i = 0; i < vsInfo.length; ++i) { + const info = vsInfo[i] + this.addLog(`checking VS${info.versionYear} (${info.version}) found ` + `at:\n"${info.path}"`) + + if (info.msBuild) { + this.addLog('- found "Visual Studio C++ core features"') + } else { + this.addLog('- "Visual Studio C++ core features" missing') + continue + } + + if (info.toolset) { + this.addLog(`- found VC++ toolset: ${info.toolset}`) + } else { + this.addLog('- missing any VC++ toolset') + continue + } + + if (info.sdk) { + this.addLog(`- found Windows SDK: ${info.sdk}`) + } else { + this.addLog('- missing any Windows SDK') + continue + } + + if (!this.checkConfigVersion(info.versionYear, info.path)) { + continue + } + + return info + } + + this.addLog('could not find a version of Visual Studio 2017 or newer to use') + return null + } + + // Helper - process version information + getVersionInfo(info) { + const match = /^(\d+)\.(\d+)\..*/.exec(info.version) + if (!match) { + this.log.silly('- failed to parse version:', info.version) + return {} + } + this.log.silly('- version match = %j', match) + const ret = { + version: info.version, + versionMajor: parseInt(match[1], 10), + versionMinor: parseInt(match[2], 10), + } + if (ret.versionMajor === 15) { + ret.versionYear = 2017 + return ret + } + if (ret.versionMajor === 16) { + ret.versionYear = 2019 + return ret + } + if (ret.versionMajor === 17) { + ret.versionYear = 2022 + return ret + } + this.log.silly('- unsupported version:', ret.versionMajor) + return {} + } + + msBuildPathExists(path) { + return existsSync(path) + } + + // Helper - process MSBuild information + getMSBuild(info, versionYear) { + const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base' + const msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') + const msbuildPathArm64 = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'arm64', 'MSBuild.exe') + if (info.packages.indexOf(pkg) !== -1) { + this.log.silly('- found VC.MSBuild.Base') + if (versionYear === 2017) { + return path.join(info.path, 'MSBuild', '15.0', 'Bin', 'MSBuild.exe') + } + if (versionYear === 2019) { + return msbuildPath + } + } + /** + * Visual Studio 2022 doesn't have the MSBuild package. + * Support for compiling _on_ ARM64 was added in MSVC 14.32.31326, + * so let's leverage it if the user has an ARM64 device. + */ + if (process.arch === 'arm64' && this.msBuildPathExists(msbuildPathArm64)) { + return msbuildPathArm64 + } else if (this.msBuildPathExists(msbuildPath)) { + return msbuildPath + } + return null + } + + // Helper - process toolset information + getToolset(info, versionYear) { + const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' + const express = 'Microsoft.VisualStudio.WDExpress' + + if (info.packages.indexOf(pkg) !== -1) { + this.log.silly('- found VC.Tools.x86.x64') + } else if (info.packages.indexOf(express) !== -1) { + this.log.silly('- found Visual Studio Express (looking for toolset)') + } else { + return null + } + + if (versionYear === 2017) { + return 'v141' + } else if (versionYear === 2019) { + return 'v142' + } else if (versionYear === 2022) { + return 'v143' + } + this.log.silly('- invalid versionYear:', versionYear) + return null + } + + // Helper - process Windows SDK information + getSDK(info) { + const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK' + const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.' + const win11SDKPrefix = 'Microsoft.VisualStudio.Component.Windows11SDK.' + + let Win10or11SDKVer = 0 + info.packages.forEach((pkg) => { + if (!pkg.startsWith(win10SDKPrefix) && !pkg.startsWith(win11SDKPrefix)) { + return + } + const parts = pkg.split('.') + if (parts.length > 5 && parts[5] !== 'Desktop') { + this.log.silly('- ignoring non-Desktop Win10/11SDK:', pkg) + return + } + const foundSdkVer = parseInt(parts[4], 10) + if (isNaN(foundSdkVer)) { + // Microsoft.VisualStudio.Component.Windows10SDK.IpOverUsb + this.log.silly('- failed to parse Win10/11SDK number:', pkg) + return + } + this.log.silly('- found Win10/11SDK:', foundSdkVer) + Win10or11SDKVer = Math.max(Win10or11SDKVer, foundSdkVer) + }) + + if (Win10or11SDKVer !== 0) { + return `10.0.${Win10or11SDKVer}.0` + } else if (info.packages.indexOf(win8SDK) !== -1) { + this.log.silly('- found Win8SDK') + return '8.1' + } + return null + } + + // Find an installation of Visual Studio 2015 to use + async findVisualStudio2015() { + if (semver.gte(this.nodeSemver, '19.0.0')) { + this.addLog('not looking for VS2015 as it is only supported up to Node.js 18') + return null + } + return this.findOldVS({ + version: '14.0', + versionMajor: 14, + versionMinor: 0, + versionYear: 2015, + toolset: 'v140', + }) + } + + // Find an installation of Visual Studio 2013 to use + async findVisualStudio2013() { + if (semver.gte(this.nodeSemver, '9.0.0')) { + this.addLog('not looking for VS2013 as it is only supported up to Node.js 8') + return null + } + return this.findOldVS({ + version: '12.0', + versionMajor: 12, + versionMinor: 0, + versionYear: 2013, + toolset: 'v120', + }) + } + + // Helper - common code for VS2013 and VS2015 + async findOldVS(info) { + const regVC7 = [ + 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7', + 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7', + ] + const regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions' + + this.addLog(`looking for Visual Studio ${info.versionYear}`) + try { + let res = await this.regSearchKeys(regVC7, info.version, []) + const vsPath = path.resolve(res, '..') + this.addLog(`- found in "${vsPath}"`) + const msBuildRegOpts = process.arch === 'ia32' ? [] : ['/reg:32'] + + try { + res = await this.regSearchKeys([`${regMSBuild}\\${info.version}`], 'MSBuildToolsPath', msBuildRegOpts) + } catch (err) { + this.addLog('- could not find MSBuild in registry for this version') + return null + } + + const msBuild = path.join(res, 'MSBuild.exe') + this.addLog(`- MSBuild in "${msBuild}"`) + + if (!this.checkConfigVersion(info.versionYear, vsPath)) { + return null + } + + info.path = vsPath + info.msBuild = msBuild + info.sdk = null + return info + } catch (err) { + this.addLog('- not found') + return null + } + } + + // After finding a usable version of Visual Studio: + // - add it to validVersions to be displayed at the end if a specific + // version was requested and not found; + // - check if this is the version that was requested. + // - check if this matches the Visual Studio Command Prompt + checkConfigVersion(versionYear, vsPath) { + this.validVersions.push(versionYear) + this.validVersions.push(vsPath) + + if (this.configVersionYear && this.configVersionYear !== versionYear) { + this.addLog('- msvs_version does not match this version') + return false + } + if (this.configPath && path.relative(this.configPath, vsPath) !== '') { + this.addLog('- msvs_version does not point to this installation') + return false + } + if (this.envVcInstallDir && path.relative(this.envVcInstallDir, vsPath) !== '') { + this.addLog('- does not match this Visual Studio Command Prompt') + return false + } + + return true + } } -module.exports = VisualStudioFinder \ No newline at end of file +module.exports = VisualStudioFinder diff --git a/lib/import/util.js b/lib/import/util.js index cf7cb9fe..4afc10a0 100644 --- a/lib/import/util.js +++ b/lib/import/util.js @@ -4,68 +4,67 @@ const log = require('npmlog') const cp = require('child_process') const path = require('path') +const execFile = async (...args) => + new Promise((resolve) => { + const child = cp.execFile(...args, (...a) => resolve(a)) + child.stdin.end() + }) -const execFile = async (...args) => new Promise((resolve) => { - const child = cp.execFile(...args, (...a) => resolve(a)) - child.stdin.end() -}) - - -function logWithPrefix (log, prefix) { - function setPrefix (logFunction) { - return (...args) => logFunction.apply(null, [ prefix, ...args ]) // eslint-disable-line - } - return { - silly: setPrefix(log.silly), - verbose: setPrefix(log.verbose), - info: setPrefix(log.info), - warn: setPrefix(log.warn), - error: setPrefix(log.error) - } +function logWithPrefix(log, prefix) { + function setPrefix(logFunction) { + return (...args) => logFunction.apply(null, [prefix, ...args]) // eslint-disable-line + } + return { + silly: setPrefix(log.silly), + verbose: setPrefix(log.verbose), + info: setPrefix(log.info), + warn: setPrefix(log.warn), + error: setPrefix(log.error), + } } -async function regGetValue (key, value, addOpts) { - const outReValue = value.replace(/\W/g, '.') - const outRe = new RegExp(`^\\s+${outReValue}\\s+REG_\\w+\\s+(\\S.*)$`, 'im') - const reg = path.join(process.env.SystemRoot, 'System32', 'reg.exe') - const regArgs = ['query', key, '/v', value].concat(addOpts) +async function regGetValue(key, value, addOpts) { + const outReValue = value.replace(/\W/g, '.') + const outRe = new RegExp(`^\\s+${outReValue}\\s+REG_\\w+\\s+(\\S.*)$`, 'im') + const reg = path.join(process.env.SystemRoot, 'System32', 'reg.exe') + const regArgs = ['query', key, '/v', value].concat(addOpts) - log.silly('reg', 'running', reg, regArgs) - const [err, stdout, stderr] = await execFile(reg, regArgs, { encoding: 'utf8' }) + log.silly('reg', 'running', reg, regArgs) + const [err, stdout, stderr] = await execFile(reg, regArgs, { encoding: 'utf8' }) - log.silly('reg', 'reg.exe stdout = %j', stdout) - if (err || stderr.trim() !== '') { - log.silly('reg', 'reg.exe err = %j', err && (err.stack || err)) - log.silly('reg', 'reg.exe stderr = %j', stderr) - if (err) { - throw err - } - throw new Error(stderr) - } + log.silly('reg', 'reg.exe stdout = %j', stdout) + if (err || stderr.trim() !== '') { + log.silly('reg', 'reg.exe err = %j', err && (err.stack || err)) + log.silly('reg', 'reg.exe stderr = %j', stderr) + if (err) { + throw err + } + throw new Error(stderr) + } - const result = outRe.exec(stdout) - if (!result) { - log.silly('reg', 'error parsing stdout') - throw new Error('Could not parse output of reg.exe') - } + const result = outRe.exec(stdout) + if (!result) { + log.silly('reg', 'error parsing stdout') + throw new Error('Could not parse output of reg.exe') + } - log.silly('reg', 'found: %j', result[1]) - return result[1] + log.silly('reg', 'found: %j', result[1]) + return result[1] } -async function regSearchKeys (keys, value, addOpts) { - for (const key of keys) { - try { - return await regGetValue(key, value, addOpts) - } catch { - continue - } - } +async function regSearchKeys(keys, value, addOpts) { + for (const key of keys) { + try { + return await regGetValue(key, value, addOpts) + } catch { + continue + } + } } module.exports = { - logWithPrefix: logWithPrefix, - regGetValue: regGetValue, - regSearchKeys: regSearchKeys, - execFile: execFile -} \ No newline at end of file + logWithPrefix: logWithPrefix, + regGetValue: regGetValue, + regSearchKeys: regSearchKeys, + execFile: execFile, +} diff --git a/lib/index.js b/lib/index.js index 945db7c4..3773db00 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,12 +1,12 @@ -"use strict"; +'use strict' module.exports = { - BuildSystem: require("./buildSystem"), - CMLog: require("./cmLog"), - environment: require("./environment"), - TargetOptions: require("./targetOptions"), - Dist: require("./dist"), - CMake: require("./cMake"), - downloader: require("./downloader"), - Toolset: require("./toolset"), -}; + BuildSystem: require('./buildSystem'), + CMLog: require('./cmLog'), + environment: require('./environment'), + TargetOptions: require('./targetOptions'), + Dist: require('./dist'), + CMake: require('./cMake'), + downloader: require('./downloader'), + Toolset: require('./toolset'), +} diff --git a/lib/locateNAN.js b/lib/locateNAN.js index 0776c7d0..c8d0b0bf 100644 --- a/lib/locateNAN.js +++ b/lib/locateNAN.js @@ -1,65 +1,63 @@ -"use strict"; -const fs = require("fs-extra"); -const path = require("path"); +'use strict' +const fs = require('fs-extra') +const path = require('path') const isNANModule = async function (dir) { - const h = path.join(dir, "nan.h"); - try { - const stat = await fs.stat(h); - return stat.isFile(); - } - catch (e) { - return false; - } -}; + const h = path.join(dir, 'nan.h') + try { + const stat = await fs.stat(h) + return stat.isFile() + } catch (e) { + return false + } +} -async function isNodeJSProject (dir) { - const pjson = path.join(dir, "package.json"); - const node_modules = path.join(dir, "node_modules"); - try { - let stat = await fs.stat(pjson); - if (stat.isFile()) { - return true; - } - stat = await fs.stat(node_modules); - if (stat.isDirectory()) { - return true; - } - } - catch (e) { - // Ignore - } - return false; -}; +async function isNodeJSProject(dir) { + const pjson = path.join(dir, 'package.json') + const node_modules = path.join(dir, 'node_modules') + try { + let stat = await fs.stat(pjson) + if (stat.isFile()) { + return true + } + stat = await fs.stat(node_modules) + if (stat.isDirectory()) { + return true + } + } catch (e) { + // Ignore + } + return false +} -const locateNAN = module.exports = async function (projectRoot) { - if (locateNAN.__projectRoot) { - // Override for unit tests - projectRoot = locateNAN.__projectRoot; - } +const locateNAN = (module.exports = async function (projectRoot) { + if (locateNAN.__projectRoot) { + // Override for unit tests + projectRoot = locateNAN.__projectRoot + } - let result = await isNodeJSProject(projectRoot); - if (!result) { - return null; - } + let result = await isNodeJSProject(projectRoot) + if (!result) { + return null + } - const nanModulePath = path.join(projectRoot, "node_modules", "nan"); - result = await isNANModule(nanModulePath); - if (result) { - return nanModulePath; - } + const nanModulePath = path.join(projectRoot, 'node_modules', 'nan') + result = await isNANModule(nanModulePath) + if (result) { + return nanModulePath + } - // Goto upper level: - return await locateNAN(goUp(projectRoot)); -}; + // Goto upper level: + return await locateNAN(goUp(projectRoot)) +}) function goUp(dir) { - const items = dir.split(path.sep); - const scopeItem = items[items.length - 2]; - if (scopeItem && scopeItem[0] === "@") { - // skip scope - dir = path.join(dir, ".."); - } - dir = path.join(dir, "..", ".."); - return path.normalize(dir); + const items = dir.split(path.sep) + const scopeItem = items[items.length - 2] + if (scopeItem && scopeItem[0] === '@') { + // skip scope + dir = path.join(dir, '..') + } + dir = path.join(dir, '..', '..') + return path.normalize(dir) } diff --git a/lib/locateNodeApi.js b/lib/locateNodeApi.js index ee1fbf30..04c6f084 100644 --- a/lib/locateNodeApi.js +++ b/lib/locateNodeApi.js @@ -1,18 +1,18 @@ -"use strict"; -const path = require("path"); +'use strict' +const path = require('path') -const locateNodeApi = module.exports = async function (projectRoot) { - if (locateNodeApi.__projectRoot) { - // Override for unit tests - projectRoot = locateNodeApi.__projectRoot; - } +const locateNodeApi = (module.exports = async function (projectRoot) { + if (locateNodeApi.__projectRoot) { + // Override for unit tests + projectRoot = locateNodeApi.__projectRoot + } - try { - const tmpRequire = require('module').createRequire(path.join(projectRoot, 'package.json')) - const inc = tmpRequire('node-addon-api') - return inc.include.replace(/"/g, '') - } catch (e) { - // It most likely wasn't found - return null; - } -}; \ No newline at end of file + try { + const tmpRequire = require('module').createRequire(path.join(projectRoot, 'package.json')) + const inc = tmpRequire('node-addon-api') + return inc.include.replace(/"/g, '') + } catch (e) { + // It most likely wasn't found + return null + } +}) diff --git a/lib/npmConfig.js b/lib/npmConfig.js index 6b17badf..0fb6cf2d 100644 --- a/lib/npmConfig.js +++ b/lib/npmConfig.js @@ -1,31 +1,31 @@ -"use strict"; +'use strict' function getNpmConfig() { - const npmOptions = {}; - const npmConfigPrefix = 'npm_config_'; - Object.keys(process.env).forEach(function (name) { - if (name.indexOf(npmConfigPrefix) !== 0) { - return; - } - const value = process.env[name]; - name = name.substring(npmConfigPrefix.length); - if (name) { - npmOptions[name] = value; - } - }, this); + const npmOptions = {} + const npmConfigPrefix = 'npm_config_' + Object.keys(process.env).forEach(function (name) { + if (name.indexOf(npmConfigPrefix) !== 0) { + return + } + const value = process.env[name] + name = name.substring(npmConfigPrefix.length) + if (name) { + npmOptions[name] = value + } + }, this) - return npmOptions; + return npmOptions } module.exports = function (log) { - log.verbose("CFG", "Looking for NPM config."); - const options = getNpmConfig(); + log.verbose('CFG', 'Looking for NPM config.') + const options = getNpmConfig() - if (options) { - log.silly("CFG", "NPM options:", options); - }else { - log.verbose("CFG", "There are no NPM options available."); - } + if (options) { + log.silly('CFG', 'NPM options:', options) + } else { + log.verbose('CFG', 'There are no NPM options available.') + } - return options; -}; + return options +} diff --git a/lib/processHelpers.js b/lib/processHelpers.js index d039418a..1f237ad5 100644 --- a/lib/processHelpers.js +++ b/lib/processHelpers.js @@ -1,55 +1,53 @@ -"use strict"; -const spawn = require("child_process").spawn; -const execFile = require("child_process").execFile; +'use strict' +const spawn = require('child_process').spawn +const execFile = require('child_process').execFile const processHelpers = { - run: function (command, options) { - if (!options) options = {}; + run: function (command, options) { + if (!options) options = {} - return new Promise(function (resolve, reject) { - const env = Object.assign({}, process.env); - if (env.Path && env.PATH) { - if(env.Path !== env.PATH) { - env.PATH = env.Path + ';' + env.PATH; - } - delete env.Path; - } - const child = spawn(command[0], command.slice(1), { - stdio: options.silent ? "ignore" : "inherit", - env - }); - let ended = false; - child.on("error", function (e) { - if (!ended) { - reject(e); - ended = true; - } - }); - child.on("exit", function (code, signal) { - if (!ended) { - if (code === 0) { - resolve(); - } - else { - reject(new Error("Process terminated: " + code || signal)); - } - ended = true; - } - }); - }); - }, - execFile: function(command) { - return new Promise(function (resolve, reject) { - execFile(command[0], command.slice(1), function (err, stdout, stderr) { - if (err) { - reject(new Error(err.message + "\n" + (stdout || stderr))); - } - else { - resolve(stdout); - } - }); - }); - } -}; + return new Promise(function (resolve, reject) { + const env = Object.assign({}, process.env) + if (env.Path && env.PATH) { + if (env.Path !== env.PATH) { + env.PATH = env.Path + ';' + env.PATH + } + delete env.Path + } + const child = spawn(command[0], command.slice(1), { + stdio: options.silent ? 'ignore' : 'inherit', + env, + }) + let ended = false + child.on('error', function (e) { + if (!ended) { + reject(e) + ended = true + } + }) + child.on('exit', function (code, signal) { + if (!ended) { + if (code === 0) { + resolve() + } else { + reject(new Error('Process terminated: ' + code || signal)) + } + ended = true + } + }) + }) + }, + execFile: function (command) { + return new Promise(function (resolve, reject) { + execFile(command[0], command.slice(1), function (err, stdout, stderr) { + if (err) { + reject(new Error(err.message + '\n' + (stdout || stderr))) + } else { + resolve(stdout) + } + }) + }) + }, +} -module.exports = processHelpers; +module.exports = processHelpers diff --git a/lib/runtimePaths.js b/lib/runtimePaths.js index ccde788f..24d030ef 100644 --- a/lib/runtimePaths.js +++ b/lib/runtimePaths.js @@ -1,86 +1,93 @@ -"use strict"; -const assert = require("assert"); -const semver = require("semver"); -const isPlainObject = require("lodash.isplainobject"); +'use strict' +const assert = require('assert') +const semver = require('semver') +const isPlainObject = require('lodash.isplainobject') -const NODE_MIRROR = process.env.NVM_NODEJS_ORG_MIRROR || "https://nodejs.org/dist"; -const ELECTRON_MIRROR = process.env.ELECTRON_MIRROR || "https://artifacts.electronjs.org/headers/dist"; +const NODE_MIRROR = process.env.NVM_NODEJS_ORG_MIRROR || 'https://nodejs.org/dist' +const ELECTRON_MIRROR = process.env.ELECTRON_MIRROR || 'https://artifacts.electronjs.org/headers/dist' const runtimePaths = { - node: function (targetOptions) { - if (semver.lt(targetOptions.runtimeVersion, "4.0.0")) { - return { - externalPath: NODE_MIRROR + "/v" + targetOptions.runtimeVersion + "/", - winLibs: [{ - dir: targetOptions.isX64 ? "x64" : "", - name: targetOptions.runtime + ".lib" - }], - tarPath: targetOptions.runtime + "-v" + targetOptions.runtimeVersion + ".tar.gz", - headerOnly: false - }; - } - else { - return { - externalPath: NODE_MIRROR + "/v" + targetOptions.runtimeVersion + "/", - winLibs: [{ - dir: targetOptions.isX64 ? "win-x64" : "win-x86", - name: targetOptions.runtime + ".lib" - }], - tarPath: targetOptions.runtime + "-v" + targetOptions.runtimeVersion + "-headers.tar.gz", - headerOnly: true - }; - } - }, - nw: function (targetOptions) { - if (semver.gte(targetOptions.runtimeVersion, "0.13.0")) { - return { - externalPath: "https://node-webkit.s3.amazonaws.com/v" + targetOptions.runtimeVersion + "/", - winLibs: [ - { - dir: targetOptions.isX64 ? "x64" : "", - name: targetOptions.runtime + ".lib" - }, - { - dir: targetOptions.isX64 ? "x64" : "", - name: "node.lib" - } - ], - tarPath: "nw-headers-v" + targetOptions.runtimeVersion + ".tar.gz", - headerOnly: false - }; - } - return { - externalPath: "https://node-webkit.s3.amazonaws.com/v" + targetOptions.runtimeVersion + "/", - winLibs: [{ - dir: targetOptions.isX64 ? "x64" : "", - name: targetOptions.runtime + ".lib" - }], - tarPath: "nw-headers-v" + targetOptions.runtimeVersion + ".tar.gz", - headerOnly: false - }; - }, - electron: function (targetOptions) { - return { - externalPath: ELECTRON_MIRROR + "/v" + targetOptions.runtimeVersion + "/", - winLibs: [{ - dir: targetOptions.isX64 ? "x64" : "", - name: "node.lib" - }], - tarPath: "node" + "-v" + targetOptions.runtimeVersion + ".tar.gz", - headerOnly: semver.gte(targetOptions.runtimeVersion, '4.0.0-alpha') - }; - }, - get: function (targetOptions) { - assert(targetOptions && typeof targetOptions === 'object'); + node: function (targetOptions) { + if (semver.lt(targetOptions.runtimeVersion, '4.0.0')) { + return { + externalPath: NODE_MIRROR + '/v' + targetOptions.runtimeVersion + '/', + winLibs: [ + { + dir: targetOptions.isX64 ? 'x64' : '', + name: targetOptions.runtime + '.lib', + }, + ], + tarPath: targetOptions.runtime + '-v' + targetOptions.runtimeVersion + '.tar.gz', + headerOnly: false, + } + } else { + return { + externalPath: NODE_MIRROR + '/v' + targetOptions.runtimeVersion + '/', + winLibs: [ + { + dir: targetOptions.isX64 ? 'win-x64' : 'win-x86', + name: targetOptions.runtime + '.lib', + }, + ], + tarPath: targetOptions.runtime + '-v' + targetOptions.runtimeVersion + '-headers.tar.gz', + headerOnly: true, + } + } + }, + nw: function (targetOptions) { + if (semver.gte(targetOptions.runtimeVersion, '0.13.0')) { + return { + externalPath: 'https://node-webkit.s3.amazonaws.com/v' + targetOptions.runtimeVersion + '/', + winLibs: [ + { + dir: targetOptions.isX64 ? 'x64' : '', + name: targetOptions.runtime + '.lib', + }, + { + dir: targetOptions.isX64 ? 'x64' : '', + name: 'node.lib', + }, + ], + tarPath: 'nw-headers-v' + targetOptions.runtimeVersion + '.tar.gz', + headerOnly: false, + } + } + return { + externalPath: 'https://node-webkit.s3.amazonaws.com/v' + targetOptions.runtimeVersion + '/', + winLibs: [ + { + dir: targetOptions.isX64 ? 'x64' : '', + name: targetOptions.runtime + '.lib', + }, + ], + tarPath: 'nw-headers-v' + targetOptions.runtimeVersion + '.tar.gz', + headerOnly: false, + } + }, + electron: function (targetOptions) { + return { + externalPath: ELECTRON_MIRROR + '/v' + targetOptions.runtimeVersion + '/', + winLibs: [ + { + dir: targetOptions.isX64 ? 'x64' : '', + name: 'node.lib', + }, + ], + tarPath: 'node' + '-v' + targetOptions.runtimeVersion + '.tar.gz', + headerOnly: semver.gte(targetOptions.runtimeVersion, '4.0.0-alpha'), + } + }, + get: function (targetOptions) { + assert(targetOptions && typeof targetOptions === 'object') - const runtime = targetOptions.runtime; - const func = runtimePaths[runtime]; - let paths; - if (typeof func === 'function' && isPlainObject(paths = func(targetOptions))) { - return paths; - } - throw new Error("Unknown runtime: " + runtime); - } -}; + const runtime = targetOptions.runtime + const func = runtimePaths[runtime] + let paths + if (typeof func === 'function' && isPlainObject((paths = func(targetOptions)))) { + return paths + } + throw new Error('Unknown runtime: ' + runtime) + }, +} -module.exports = runtimePaths; +module.exports = runtimePaths diff --git a/lib/targetOptions.js b/lib/targetOptions.js index b611f12d..69307f69 100644 --- a/lib/targetOptions.js +++ b/lib/targetOptions.js @@ -1,42 +1,42 @@ -"use strict"; +'use strict' -const environment = require("./environment"); +const environment = require('./environment') function TargetOptions(options) { - this.options = options || {}; + this.options = options || {} } Object.defineProperties(TargetOptions.prototype, { - arch: { - get: function () { - return this.options.arch || environment.arch; - } - }, - isX86: { - get: function () { - return this.arch === "ia32" || this.arch === "x86"; - } - }, - isX64: { - get: function () { - return this.arch === "x64"; - } - }, - isArm: { - get: function () { - return this.arch === "arm"; - } - }, - runtime: { - get: function () { - return this.options.runtime || environment.runtime; - } - }, - runtimeVersion: { - get: function () { - return this.options.runtimeVersion || environment.runtimeVersion; - } - } -}); + arch: { + get: function () { + return this.options.arch || environment.arch + }, + }, + isX86: { + get: function () { + return this.arch === 'ia32' || this.arch === 'x86' + }, + }, + isX64: { + get: function () { + return this.arch === 'x64' + }, + }, + isArm: { + get: function () { + return this.arch === 'arm' + }, + }, + runtime: { + get: function () { + return this.options.runtime || environment.runtime + }, + }, + runtimeVersion: { + get: function () { + return this.options.runtimeVersion || environment.runtimeVersion + }, + }, +}) -module.exports = TargetOptions; \ No newline at end of file +module.exports = TargetOptions diff --git a/lib/toolset.js b/lib/toolset.js index c6cbad42..981327e0 100644 --- a/lib/toolset.js +++ b/lib/toolset.js @@ -1,226 +1,225 @@ -"use strict"; -const TargetOptions = require("./targetOptions"); -const environment = require("./environment"); -const assert = require("assert"); -const CMLog = require("./cmLog"); -const util = require('util') +'use strict' +const TargetOptions = require('./targetOptions') +const environment = require('./environment') +const assert = require('assert') +const CMLog = require('./cmLog') const { findVisualStudio } = environment.isWin ? require('./import/find-visualstudio') : {} - function Toolset(options) { - this.options = options || {}; - this.targetOptions = new TargetOptions(this.options); - this.generator = options.generator; - this.toolset = options.toolset; - this.platform = options.platform; - this.target = options.target; - this.cCompilerPath = options.cCompilerPath; - this.cppCompilerPath = options.cppCompilerPath; - this.compilerFlags = []; - this.linkerFlags = []; - this.makePath = null; - this.log = new CMLog(this.options); - this._initialized = false; + this.options = options || {} + this.targetOptions = new TargetOptions(this.options) + this.generator = options.generator + this.toolset = options.toolset + this.platform = options.platform + this.target = options.target + this.cCompilerPath = options.cCompilerPath + this.cppCompilerPath = options.cppCompilerPath + this.compilerFlags = [] + this.linkerFlags = [] + this.makePath = null + this.log = new CMLog(this.options) + this._initialized = false } Toolset.prototype.initialize = async function (install) { - if (!this._initialized) { - if (environment.isWin) { - await this.initializeWin(install); - } - else { - this.initializePosix(install); - } - this._initialized = true; - } -}; + if (!this._initialized) { + if (environment.isWin) { + await this.initializeWin(install) + } else { + this.initializePosix(install) + } + this._initialized = true + } +} Toolset.prototype.initializePosix = function (install) { - if (!this.cCompilerPath || !this.cppCompilerPath) { - // 1: Compiler - if (!environment.isGPPAvailable && !environment.isClangAvailable) { - if (environment.isOSX) { - throw new Error("C++ Compiler toolset is not available. Install Xcode Commandline Tools from Apple Dev Center, or install Clang with homebrew by invoking: 'brew install llvm --with-clang --with-asan'."); - } - else { - throw new Error("C++ Compiler toolset is not available. Install proper compiler toolset with your package manager, eg. 'sudo apt-get install g++'."); - } - } - - if (this.options.preferClang && environment.isClangAvailable) { - if (install) { - this.log.info("TOOL", "Using clang++ compiler, because preferClang option is set, and clang++ is available."); - } - this.cppCompilerPath = this.cppCompilerPath || "clang++"; - this.cCompilerPath = this.cCompilerPath || "clang"; - } - else if (this.options.preferGnu && environment.isGPPAvailable) { - if (install) { - this.log.info("TOOL", "Using g++ compiler, because preferGnu option is set, and g++ is available."); - } - this.cppCompilerPath = this.cppCompilerPath || "g++"; - this.cCompilerPath = this.cCompilerPath || "gcc"; - } - } - // if it's already set because of options... - if (this.generator) { - if (install) { - this.log.info("TOOL", "Using " + this.generator + " generator, as specified from commandline."); - } - } - // 2: Generator - else if (environment.isOSX) { - if (this.options.preferXcode) { - if (install) { - this.log.info("TOOL", "Using Xcode generator, because preferXcode option is set."); - } - this.generator = "Xcode"; - } - else if (this.options.preferMake && environment.isMakeAvailable) { - if (install) { - this.log.info("TOOL", "Using Unix Makefiles generator, because preferMake option is set, and make is available."); - } - this.generator = "Unix Makefiles"; - } - else if (environment.isNinjaAvailable) { - if (install) { - this.log.info("TOOL", "Using Ninja generator, because ninja is available."); - } - this.generator = "Ninja"; - } - else { - if (install) { - this.log.info("TOOL", "Using Unix Makefiles generator."); - } - this.generator = "Unix Makefiles"; - } - } - else { - if (this.options.preferMake && environment.isMakeAvailable) { - if (install) { - this.log.info("TOOL", "Using Unix Makefiles generator, because preferMake option is set, and make is available."); - } - this.generator = "Unix Makefiles"; - } - else if (environment.isNinjaAvailable) { - if (install) { - this.log.info("TOOL", "Using Ninja generator, because ninja is available."); - } - this.generator = "Ninja"; - } - else { - if (install) { - this.log.info("TOOL", "Using Unix Makefiles generator."); - } - this.generator = "Unix Makefiles"; - } - } - - // 3: Flags - if (environment.isOSX) { - if (install) { - this.log.verbose("TOOL", "Setting default OSX compiler flags."); - } - - this.compilerFlags.push("-D_DARWIN_USE_64_BIT_INODE=1"); - this.compilerFlags.push("-D_LARGEFILE_SOURCE"); - this.compilerFlags.push("-D_FILE_OFFSET_BITS=64"); - this.linkerFlags.push("-undefined dynamic_lookup"); - } - - this.compilerFlags.push("-DBUILDING_NODE_EXTENSION"); - - // 4: Build target - if (this.options.target) { - this.log.info("TOOL", "Building only the " + this.options.target + " target, as specified from the command line."); - } -}; + if (!this.cCompilerPath || !this.cppCompilerPath) { + // 1: Compiler + if (!environment.isGPPAvailable && !environment.isClangAvailable) { + if (environment.isOSX) { + throw new Error( + "C++ Compiler toolset is not available. Install Xcode Commandline Tools from Apple Dev Center, or install Clang with homebrew by invoking: 'brew install llvm --with-clang --with-asan'.", + ) + } else { + throw new Error( + "C++ Compiler toolset is not available. Install proper compiler toolset with your package manager, eg. 'sudo apt-get install g++'.", + ) + } + } + + if (this.options.preferClang && environment.isClangAvailable) { + if (install) { + this.log.info('TOOL', 'Using clang++ compiler, because preferClang option is set, and clang++ is available.') + } + this.cppCompilerPath = this.cppCompilerPath || 'clang++' + this.cCompilerPath = this.cCompilerPath || 'clang' + } else if (this.options.preferGnu && environment.isGPPAvailable) { + if (install) { + this.log.info('TOOL', 'Using g++ compiler, because preferGnu option is set, and g++ is available.') + } + this.cppCompilerPath = this.cppCompilerPath || 'g++' + this.cCompilerPath = this.cCompilerPath || 'gcc' + } + } + // if it's already set because of options... + if (this.generator) { + if (install) { + this.log.info('TOOL', 'Using ' + this.generator + ' generator, as specified from commandline.') + } + } + // 2: Generator + else if (environment.isOSX) { + if (this.options.preferXcode) { + if (install) { + this.log.info('TOOL', 'Using Xcode generator, because preferXcode option is set.') + } + this.generator = 'Xcode' + } else if (this.options.preferMake && environment.isMakeAvailable) { + if (install) { + this.log.info( + 'TOOL', + 'Using Unix Makefiles generator, because preferMake option is set, and make is available.', + ) + } + this.generator = 'Unix Makefiles' + } else if (environment.isNinjaAvailable) { + if (install) { + this.log.info('TOOL', 'Using Ninja generator, because ninja is available.') + } + this.generator = 'Ninja' + } else { + if (install) { + this.log.info('TOOL', 'Using Unix Makefiles generator.') + } + this.generator = 'Unix Makefiles' + } + } else { + if (this.options.preferMake && environment.isMakeAvailable) { + if (install) { + this.log.info( + 'TOOL', + 'Using Unix Makefiles generator, because preferMake option is set, and make is available.', + ) + } + this.generator = 'Unix Makefiles' + } else if (environment.isNinjaAvailable) { + if (install) { + this.log.info('TOOL', 'Using Ninja generator, because ninja is available.') + } + this.generator = 'Ninja' + } else { + if (install) { + this.log.info('TOOL', 'Using Unix Makefiles generator.') + } + this.generator = 'Unix Makefiles' + } + } + + // 3: Flags + if (environment.isOSX) { + if (install) { + this.log.verbose('TOOL', 'Setting default OSX compiler flags.') + } + + this.compilerFlags.push('-D_DARWIN_USE_64_BIT_INODE=1') + this.compilerFlags.push('-D_LARGEFILE_SOURCE') + this.compilerFlags.push('-D_FILE_OFFSET_BITS=64') + this.linkerFlags.push('-undefined dynamic_lookup') + } + + this.compilerFlags.push('-DBUILDING_NODE_EXTENSION') + + // 4: Build target + if (this.options.target) { + this.log.info('TOOL', 'Building only the ' + this.options.target + ' target, as specified from the command line.') + } +} Toolset.prototype.initializeWin = async function (install) { - if (!this.generator) { - const foundVsInfo = await this._getTopSupportedVisualStudioGenerator(); - if (foundVsInfo) { - if (install) { - this.log.info("TOOL", `Using ${foundVsInfo.generator} generator.`); - } - this.generator = foundVsInfo.generator; - - const isAboveVS16 = foundVsInfo.versionMajor >= 16; - - // The CMake Visual Studio Generator does not support the Win64 or ARM suffix on - // the generator name. Instead the generator platform must be set explicitly via - // the platform parameter - if (!this.platform && isAboveVS16) { - switch(this.targetOptions.arch) { - case "ia32": - case "x86": - this.platform = "Win32"; - break; - case "x64": - this.platform = "x64"; - break; - case "arm": - this.platform = "ARM"; - break; - case "arm64": - this.platform = "ARM64"; - break; - default: - this.log.warn("TOOL", "Unknown NodeJS architecture: " + this.targetOptions.arch); - break; - } - } - } else { - throw new Error("There is no Visual C++ compiler installed. Install Visual C++ Build Toolset or Visual Studio."); - } - } else { - // if it's already set because of options... - if (install) { - this.log.info("TOOL", "Using " + this.options.generator + " generator, as specified from commandline."); - } - } - - this.linkerFlags.push("/DELAYLOAD:NODE.EXE"); - - if (this.targetOptions.isX86) { - if (install) { - this.log.verbose("TOOL", "Setting SAFESEH:NO linker flag."); - } - this.linkerFlags.push("/SAFESEH:NO"); - } -}; + if (!this.generator) { + const foundVsInfo = await this._getTopSupportedVisualStudioGenerator() + if (foundVsInfo) { + if (install) { + this.log.info('TOOL', `Using ${foundVsInfo.generator} generator.`) + } + this.generator = foundVsInfo.generator + + const isAboveVS16 = foundVsInfo.versionMajor >= 16 + + // The CMake Visual Studio Generator does not support the Win64 or ARM suffix on + // the generator name. Instead the generator platform must be set explicitly via + // the platform parameter + if (!this.platform && isAboveVS16) { + switch (this.targetOptions.arch) { + case 'ia32': + case 'x86': + this.platform = 'Win32' + break + case 'x64': + this.platform = 'x64' + break + case 'arm': + this.platform = 'ARM' + break + case 'arm64': + this.platform = 'ARM64' + break + default: + this.log.warn('TOOL', 'Unknown NodeJS architecture: ' + this.targetOptions.arch) + break + } + } + } else { + throw new Error('There is no Visual C++ compiler installed. Install Visual C++ Build Toolset or Visual Studio.') + } + } else { + // if it's already set because of options... + if (install) { + this.log.info('TOOL', 'Using ' + this.options.generator + ' generator, as specified from commandline.') + } + } + + this.linkerFlags.push('/DELAYLOAD:NODE.EXE') + + if (this.targetOptions.isX86) { + if (install) { + this.log.verbose('TOOL', 'Setting SAFESEH:NO linker flag.') + } + this.linkerFlags.push('/SAFESEH:NO') + } +} Toolset.prototype._getTopSupportedVisualStudioGenerator = async function () { - const CMake = require("./cMake"); - assert(environment.isWin); - - const selectedVs = await findVisualStudio(environment.runtimeVersion, this.options.msvsVersion) - if (!selectedVs) return null; - - const list = await CMake.getGenerators(this.options, this.log); - for (const gen of list) { - const found = gen.startsWith(`Visual Studio ${selectedVs.versionMajor}`) - if (!found) { - continue; - } - - // unlike previous versions "Visual Studio 16 2019" and onwards don't end with arch name - const isAboveVS16 = selectedVs.versionMajor >= 16; - if (!isAboveVS16) { - const is64Bit = gen.endsWith("Win64"); - if ((this.targetOptions.isX86 && is64Bit) || (this.targetOptions.isX64 && !is64Bit)) { - continue; - } - } - - return { - ...selectedVs, - generator: gen - } - } - - // Nothing matched - return null; -}; - -module.exports = Toolset; + const CMake = require('./cMake') + assert(environment.isWin) + + const selectedVs = await findVisualStudio(environment.runtimeVersion, this.options.msvsVersion) + if (!selectedVs) return null + + const list = await CMake.getGenerators(this.options, this.log) + for (const gen of list) { + const found = gen.startsWith(`Visual Studio ${selectedVs.versionMajor}`) + if (!found) { + continue + } + + // unlike previous versions "Visual Studio 16 2019" and onwards don't end with arch name + const isAboveVS16 = selectedVs.versionMajor >= 16 + if (!isAboveVS16) { + const is64Bit = gen.endsWith('Win64') + if ((this.targetOptions.isX86 && is64Bit) || (this.targetOptions.isX64 && !is64Bit)) { + continue + } + } + + return { + ...selectedVs, + generator: gen, + } + } + + // Nothing matched + return null +} + +module.exports = Toolset diff --git a/package.json b/package.json index 6f69f0de..c1ab3230 100644 --- a/package.json +++ b/package.json @@ -1,74 +1,77 @@ -{ - "name": "cmake-js", - "description": "CMake.js - a Node.js native addon build tool", - "license": "MIT", - "keywords": [ - "native", - "addon", - "module", - "c", - "c++", - "bindings", - "build", - "buildtools", - "cmake", - "nw.js", - "electron", - "boost", - "nan", - "napi", - "node-api", - "node-addon-api" - ], - "main": "lib", - "version": "7.3.0", - "author": "Gábor Mező aka unbornchikken", - "maintainers": [ - { - "name": "Julian Waller", - "email": "git@julusian.co.uk", - "url": "https://github.com/julusian/" - } - ], - "repository": { - "type": "git", - "url": "git://github.com/cmake-js/cmake-js.git" - }, - "bin": { - "cmake-js": "./bin/cmake-js" - }, - "engines": { - "node": ">= 14.15.0" - }, - "dependencies": { - "axios": "^1.6.5", - "debug": "^4", - "fs-extra": "^11.2.0", - "lodash.isplainobject": "^4.0.6", - "memory-stream": "^1.0.0", - "node-api-headers": "^1.1.0", - "npmlog": "^6.0.2", - "rc": "^1.2.7", - "semver": "^7.5.4", - "tar": "^6.2.0", - "url-join": "^4.0.1", - "which": "^2.0.2", - "yargs": "^17.7.2" - }, - "devDependencies": { - "mocha": "*", - "nan": "^2.18.0", - "node-addon-api": "^6.1.0" - }, - "scripts": { - "test": "mocha tests", - "lint": "eslint lib bin/cmake-js tests" - }, - "files": [ - "lib", - "bin", - "*.md", - "bindings.js", - "bindings.d.ts" - ] -} +{ + "name": "cmake-js", + "description": "CMake.js - a Node.js native addon build tool", + "license": "MIT", + "keywords": [ + "native", + "addon", + "module", + "c", + "c++", + "bindings", + "build", + "buildtools", + "cmake", + "nw.js", + "electron", + "boost", + "nan", + "napi", + "node-api", + "node-addon-api" + ], + "main": "lib", + "version": "7.3.0", + "author": "Gábor Mező aka unbornchikken", + "maintainers": [ + { + "name": "Julian Waller", + "email": "git@julusian.co.uk", + "url": "https://github.com/julusian/" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/cmake-js/cmake-js.git" + }, + "bin": { + "cmake-js": "./bin/cmake-js" + }, + "engines": { + "node": ">= 14.15.0" + }, + "dependencies": { + "axios": "^1.6.5", + "debug": "^4", + "fs-extra": "^11.2.0", + "lodash.isplainobject": "^4.0.6", + "memory-stream": "^1.0.0", + "node-api-headers": "^1.1.0", + "npmlog": "^6.0.2", + "rc": "^1.2.7", + "semver": "^7.5.4", + "tar": "^6.2.0", + "url-join": "^4.0.1", + "which": "^2.0.2", + "yargs": "^17.7.2" + }, + "devDependencies": { + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "mocha": "*", + "nan": "^2.18.0", + "node-addon-api": "^6.1.0", + "prettier": "^3.2.2" + }, + "scripts": { + "test": "mocha tests", + "lint": "eslint lib bin/cmake-js tests" + }, + "files": [ + "lib", + "bin", + "*.md", + "bindings.js", + "bindings.d.ts" + ] +} diff --git a/tests/es6/buildSystem.js b/tests/es6/buildSystem.js index a8b28310..1fe4df42 100644 --- a/tests/es6/buildSystem.js +++ b/tests/es6/buildSystem.js @@ -1,59 +1,62 @@ -"use strict"; -/* global describe,it,before */ - -const assert = require("assert"); -const lib = require("../../"); -const locateNAN = require("../../lib/locateNAN"); -const CMake = lib.CMake; -const path = require("path"); -const log = require("npmlog"); -const testRunner = require("./testRunner"); -const testCases = require("./testCases"); - -describe("BuildSystem", function () { - this.timeout(300000); - - before(function() { - if (process.env.UT_LOG_LEVEL) { - log.level = process.env.UT_LOG_LEVEL; - log.resume(); - } - locateNAN.__projectRoot = path.resolve(path.join(__dirname, "../../")); - }); - - after(function() { - locateNAN.__projectRoot = undefined; - }); - - describe("Build with various options", function() { - testRunner.runCase(testCases.buildPrototypeWithDirectoryOption); - }); - - it("should provide list of generators", async function () { - const gens = await CMake.getGenerators(); - assert(Array.isArray(gens)); - assert(gens.length > 0); - assert.equal(gens.filter(function (g) { return g.length; }).length, gens.length); - }); - - it("should rebuild prototype if cwd is the source directory", async function () { - await testCases.buildPrototype2WithCWD(); - }); - - it("should build prototpye with nodeapi", async function () { - await testCases.buildPrototypeNapi(); - }); - - - it("should run with old GNU compilers", async function () { - await testCases.shouldConfigurePreC11Properly(); - }); - - it("should configure with custom option", async function () { - await testCases.configureWithCustomOptions(); - }); - - it("should forward extra arguments to CMake", async function () { - await testCases.shouldForwardExtraCMakeArgs(); - }); -}); +'use strict' + +const assert = require('assert') +const lib = require('../../') +const locateNAN = require('../../lib/locateNAN') +const CMake = lib.CMake +const path = require('path') +const log = require('npmlog') +const testRunner = require('./testRunner') +const testCases = require('./testCases') + +describe('BuildSystem', function () { + this.timeout(300000) + + before(function () { + if (process.env.UT_LOG_LEVEL) { + log.level = process.env.UT_LOG_LEVEL + log.resume() + } + locateNAN.__projectRoot = path.resolve(path.join(__dirname, '../../')) + }) + + after(function () { + locateNAN.__projectRoot = undefined + }) + + describe('Build with various options', function () { + testRunner.runCase(testCases.buildPrototypeWithDirectoryOption) + }) + + it('should provide list of generators', async function () { + const gens = await CMake.getGenerators() + assert(Array.isArray(gens)) + assert(gens.length > 0) + assert.equal( + gens.filter(function (g) { + return g.length + }).length, + gens.length, + ) + }) + + it('should rebuild prototype if cwd is the source directory', async function () { + await testCases.buildPrototype2WithCWD() + }) + + it('should build prototpye with nodeapi', async function () { + await testCases.buildPrototypeNapi() + }) + + it('should run with old GNU compilers', async function () { + await testCases.shouldConfigurePreC11Properly() + }) + + it('should configure with custom option', async function () { + await testCases.configureWithCustomOptions() + }) + + it('should forward extra arguments to CMake', async function () { + await testCases.shouldForwardExtraCMakeArgs() + }) +}) diff --git a/tests/es6/dist.js b/tests/es6/dist.js index fe3ff610..f9822136 100644 --- a/tests/es6/dist.js +++ b/tests/es6/dist.js @@ -1,26 +1,23 @@ -"use strict"; -/* global describe,it */ +'use strict' -const fs = require("fs-extra"); -const Dist = require("../../").Dist; -const assert = require("assert"); +const fs = require('fs-extra') +const Dist = require('../../').Dist +const assert = require('assert') -const testDownload = process.env.TEST_DOWNLOAD === "1"; +const testDownload = process.env.TEST_DOWNLOAD === '1' -describe("dist", function () { - it("should download dist files if needed", async function () { - this.timeout(60000); +describe('dist', function () { + it('should download dist files if needed', async function () { + this.timeout(60000) - const dist = new Dist(); - if (testDownload) { - await fs.remove(dist.internalPath); - assert(dist.downloaded === false); - await dist.ensureDownloaded(); - assert(dist.downloaded); - } - else { - await dist.ensureDownloaded(); - } - }); - -}); + const dist = new Dist() + if (testDownload) { + await fs.remove(dist.internalPath) + assert(dist.downloaded === false) + await dist.ensureDownloaded() + assert(dist.downloaded) + } else { + await dist.ensureDownloaded() + } + }) +}) diff --git a/tests/es6/index.js b/tests/es6/index.js index 3bfa0254..c7af0097 100644 --- a/tests/es6/index.js +++ b/tests/es6/index.js @@ -1,4 +1,4 @@ -"use strict"; -require("./dist"); -require("./buildSystem"); -require("./locateNAN"); +'use strict' +require('./dist') +require('./buildSystem') +require('./locateNAN') diff --git a/tests/es6/locateNAN.js b/tests/es6/locateNAN.js index aa633db4..661742f8 100644 --- a/tests/es6/locateNAN.js +++ b/tests/es6/locateNAN.js @@ -1,9 +1,8 @@ -"use strict"; -/* global describe,it */ +'use strict' -const locateNAN = require("../../lib/locateNAN"); -const path = require("path"); -const assert = require("assert"); +const locateNAN = require('../../lib/locateNAN') +const path = require('path') +const assert = require('assert') /* @@ -16,29 +15,28 @@ fixtures/project */ -describe("locateNAN", function () { - const PROJECT_DIR = path.resolve(__dirname, "..", "fixtures", "project"); - const NAN_DIR = path.join(PROJECT_DIR, "node_modules", "nan"); +describe('locateNAN', function () { + const PROJECT_DIR = path.resolve(__dirname, '..', 'fixtures', 'project') + const NAN_DIR = path.join(PROJECT_DIR, 'node_modules', 'nan') - it("should locate NAN from dependency", async function () { - const dir = path.join(PROJECT_DIR, "node_modules", "dep-1"); - - const nan = await locateNAN(dir); - assert.equal(nan, NAN_DIR); - }); + it('should locate NAN from dependency', async function () { + const dir = path.join(PROJECT_DIR, 'node_modules', 'dep-1') - it("should locate NAN from nested dependency", async function () { - const dir = path.join(PROJECT_DIR, "node_modules", "dep-1", "node_modules", "dep-3"); + const nan = await locateNAN(dir) + assert.equal(nan, NAN_DIR) + }) - const nan = await locateNAN(dir); - assert.equal(nan, NAN_DIR); - - }); + it('should locate NAN from nested dependency', async function () { + const dir = path.join(PROJECT_DIR, 'node_modules', 'dep-1', 'node_modules', 'dep-3') - it("should locate NAN from scoped dependency", async function () { - const dir = path.join(PROJECT_DIR, "node_modules", "@scope", "dep-2"); + const nan = await locateNAN(dir) + assert.equal(nan, NAN_DIR) + }) - const nan = await locateNAN(dir); - assert.equal(nan, NAN_DIR); - }); -}); + it('should locate NAN from scoped dependency', async function () { + const dir = path.join(PROJECT_DIR, 'node_modules', '@scope', 'dep-2') + + const nan = await locateNAN(dir) + assert.equal(nan, NAN_DIR) + }) +}) diff --git a/tests/es6/prototype-napi/package.json b/tests/es6/prototype-napi/package.json index 2a750b1a..ace6167f 100644 --- a/tests/es6/prototype-napi/package.json +++ b/tests/es6/prototype-napi/package.json @@ -1,5 +1,5 @@ { - "binary": { - "napi_versions": 7 - } -} \ No newline at end of file + "binary": { + "napi_versions": 7 + } +} diff --git a/tests/es6/testCases.js b/tests/es6/testCases.js index 6f8af6b1..c359cec0 100644 --- a/tests/es6/testCases.js +++ b/tests/es6/testCases.js @@ -1,87 +1,85 @@ -"use strict"; -const assert = require("assert"); -const lib = require("../../"); -const BuildSystem = lib.BuildSystem; -const path = require("path"); -const fs = require("fs-extra"); +'use strict' +const assert = require('assert') +const lib = require('../../') +const BuildSystem = lib.BuildSystem +const path = require('path') +const fs = require('fs-extra') const testCases = { - buildPrototypeWithDirectoryOption: async function(options) { - options = { - directory: path.resolve(path.join(__dirname, "./prototype")), - ...options - }; - const buildSystem = new BuildSystem(options); - await buildSystem.rebuild(); - assert.ok((await fs.stat(path.join(__dirname, "prototype/build/Release/addon.node"))).isFile()); - }, - buildPrototype2WithCWD: async function(options) { - const cwd = process.cwd(); - process.chdir(path.resolve(path.join(__dirname, "./prototype2"))); - const buildSystem = new BuildSystem(options); - try { - await buildSystem.rebuild(); - assert.ok((await fs.stat(path.join(__dirname, "prototype2/build/Release/addon2.node"))).isFile()); - } - finally { - process.chdir(cwd); - } - }, - buildPrototypeNapi: async function(options) { - const cwd = process.cwd(); - process.chdir(path.resolve(path.join(__dirname, "./prototype-napi"))); - const buildSystem = new BuildSystem(options); - try { - await buildSystem.rebuild(); - assert.ok((await fs.stat(path.join(__dirname, "prototype-napi/build/Release/addon_napi.node"))).isFile()); - } - finally { - process.chdir(cwd); - } - }, - shouldConfigurePreC11Properly: async function(options) { - options = { - directory: path.resolve(path.join(__dirname, "./prototype")), - std: "c++98", - ...options - }; - const buildSystem = new BuildSystem(options); - if (!/visual studio/i.test(buildSystem.toolset.generator)) { - const command = await buildSystem.getConfigureCommand(); - assert.equal(command.indexOf("-std=c++"), -1, "c++ version still forced"); - } - }, - configureWithCustomOptions: async function(options) { - options = { - directory: path.resolve(path.join(__dirname, "./prototype")), - cMakeOptions: { - foo: "bar" - }, - ...options - }; - const buildSystem = new BuildSystem(options); + buildPrototypeWithDirectoryOption: async function (options) { + options = { + directory: path.resolve(path.join(__dirname, './prototype')), + ...options, + } + const buildSystem = new BuildSystem(options) + await buildSystem.rebuild() + assert.ok((await fs.stat(path.join(__dirname, 'prototype/build/Release/addon.node'))).isFile()) + }, + buildPrototype2WithCWD: async function (options) { + const cwd = process.cwd() + process.chdir(path.resolve(path.join(__dirname, './prototype2'))) + const buildSystem = new BuildSystem(options) + try { + await buildSystem.rebuild() + assert.ok((await fs.stat(path.join(__dirname, 'prototype2/build/Release/addon2.node'))).isFile()) + } finally { + process.chdir(cwd) + } + }, + buildPrototypeNapi: async function (options) { + const cwd = process.cwd() + process.chdir(path.resolve(path.join(__dirname, './prototype-napi'))) + const buildSystem = new BuildSystem(options) + try { + await buildSystem.rebuild() + assert.ok((await fs.stat(path.join(__dirname, 'prototype-napi/build/Release/addon_napi.node'))).isFile()) + } finally { + process.chdir(cwd) + } + }, + shouldConfigurePreC11Properly: async function (options) { + options = { + directory: path.resolve(path.join(__dirname, './prototype')), + std: 'c++98', + ...options, + } + const buildSystem = new BuildSystem(options) + if (!/visual studio/i.test(buildSystem.toolset.generator)) { + const command = await buildSystem.getConfigureCommand() + assert.equal(command.indexOf('-std=c++'), -1, 'c++ version still forced') + } + }, + configureWithCustomOptions: async function (options) { + options = { + directory: path.resolve(path.join(__dirname, './prototype')), + cMakeOptions: { + foo: 'bar', + }, + ...options, + } + const buildSystem = new BuildSystem(options) - const command = await buildSystem.getConfigureCommand(); - assert.notEqual(command.indexOf("-Dfoo=bar"), -1, "custom options added"); - }, - shouldForwardExtraCMakeArgs: async function(options) { - options = { - directory: path.resolve(path.join(__dirname, "./prototype")), - ...options - }; + const command = await buildSystem.getConfigureCommand() + assert.notEqual(command.indexOf('-Dfoo=bar'), -1, 'custom options added') + }, + shouldForwardExtraCMakeArgs: async function (options) { + options = { + directory: path.resolve(path.join(__dirname, './prototype')), + ...options, + } - options.extraCMakeArgs = ["--debug-find-pkg=Boost", "--trace-source=FindBoost.cmake"]; - const configure = await (new BuildSystem(options)).getConfigureCommand(); - assert.deepEqual(configure.slice(-2), options.extraCMakeArgs, "extra CMake args appended"); + options.extraCMakeArgs = ['--debug-find-pkg=Boost', '--trace-source=FindBoost.cmake'] + const configure = await new BuildSystem(options).getConfigureCommand() + assert.deepEqual(configure.slice(-2), options.extraCMakeArgs, 'extra CMake args appended') - options.extraCMakeArgs = ["--", "CMakeFiles/x.dir/y.cpp.o"]; - const build = await (new BuildSystem(options)).getBuildCommand(); - assert.deepEqual(build.slice(-2), options.extraCMakeArgs, "extra CMake args appended"); + options.extraCMakeArgs = ['--', 'CMakeFiles/x.dir/y.cpp.o'] + const build = await new BuildSystem(options).getBuildCommand() + assert.deepEqual(build.slice(-2), options.extraCMakeArgs, 'extra CMake args appended') - options.extraCMakeArgs = [".cache", "/tmp/jest_rs"]; - const clean = await (new BuildSystem(options)).getCleanCommand(); - assert.deepEqual(clean.slice(-2), options.extraCMakeArgs, "extra CMake args appended"); - } -}; + options.extraCMakeArgs = ['.cache', '/tmp/jest_rs'] + const clean = await new BuildSystem(options).getCleanCommand() + assert.deepEqual(clean.slice(-2), options.extraCMakeArgs, 'extra CMake args appended') + }, +} -module.exports = testCases; +module.exports = testCases diff --git a/tests/es6/testRunner.js b/tests/es6/testRunner.js index d54cec8f..34edc6f1 100644 --- a/tests/es6/testRunner.js +++ b/tests/es6/testRunner.js @@ -1,105 +1,103 @@ -"use strict"; -/* global it */ -const lib = require("../../"); -const environment = lib.environment; -const log = require("npmlog"); -const util = require("util"); +'use strict' + +const lib = require('../../') +const environment = lib.environment +const log = require('npmlog') +const util = require('util') function* generateRuntimeOptions() { - function* generateForNode(arch) { - // Old: - yield { - runtime: "node", - runtimeVersion: "14.0.0", - arch: arch - }; - - // LTS: - yield { - runtime: "node", - runtimeVersion: "16.0.0", - arch: arch - }; - - // Current: - yield { - runtime: "node", - runtimeVersion: "18.13.0", - arch: arch - }; - } - - function* generateForNWJS(arch) { - // Latest: - yield { - runtime: "nw", - runtimeVersion: "0.64.0", - arch: arch - }; - } - - function* generateForElectron(arch) { - // Latest: - yield { - runtime: "electron", - runtimeVersion: "18.2.1", - arch: arch - }; - } - - function* generateForArch(arch) { - yield* generateForNode(arch); - yield* generateForNWJS(arch); - yield* generateForElectron(arch); - } - - if (environment.isWin) { - yield* generateForArch("x64"); - yield* generateForArch("ia32"); - } - else { - yield* generateForArch(); - } - - // Actual: - yield {}; + function* generateForNode(arch) { + // Old: + yield { + runtime: 'node', + runtimeVersion: '14.0.0', + arch: arch, + } + + // LTS: + yield { + runtime: 'node', + runtimeVersion: '16.0.0', + arch: arch, + } + + // Current: + yield { + runtime: 'node', + runtimeVersion: '18.13.0', + arch: arch, + } + } + + function* generateForNWJS(arch) { + // Latest: + yield { + runtime: 'nw', + runtimeVersion: '0.64.0', + arch: arch, + } + } + + function* generateForElectron(arch) { + // Latest: + yield { + runtime: 'electron', + runtimeVersion: '18.2.1', + arch: arch, + } + } + + function* generateForArch(arch) { + yield* generateForNode(arch) + yield* generateForNWJS(arch) + yield* generateForElectron(arch) + } + + if (environment.isWin) { + yield* generateForArch('x64') + yield* generateForArch('ia32') + } else { + yield* generateForArch() + } + + // Actual: + yield {} } function* generateOptions() { - for (const runtimeOptions of generateRuntimeOptions()) { - if (environment.isWin) { - // V C++: - yield runtimeOptions; - } - else { - // Clang, Make - yield { ...runtimeOptions, preferClang: true, preferMake: true }; - - // Clang, Ninja - yield { ...runtimeOptions, preferClang: true }; - - // g++, Make - yield { ...runtimeOptions, preferGnu: true, preferMake: true }; - - // g++, Ninja - yield { ...runtimeOptions, preferGnu: true }; - - // Default: - yield runtimeOptions; - } - } + for (const runtimeOptions of generateRuntimeOptions()) { + if (environment.isWin) { + // V C++: + yield runtimeOptions + } else { + // Clang, Make + yield { ...runtimeOptions, preferClang: true, preferMake: true } + + // Clang, Ninja + yield { ...runtimeOptions, preferClang: true } + + // g++, Make + yield { ...runtimeOptions, preferGnu: true, preferMake: true } + + // g++, Ninja + yield { ...runtimeOptions, preferGnu: true } + + // Default: + yield runtimeOptions + } + } } const testRunner = { - runCase: function (testCase, options) { - for (const testOptions of generateOptions()) { - const currentOptions = { ...testOptions, ...(options || {}) }; - it("should build with: " + util.inspect(currentOptions), async function () { - log.info("TEST", "Running case for options of: " + util.inspect(currentOptions)); - await testCase(currentOptions); - }); - } - } -}; - -module.exports = testRunner; \ No newline at end of file + runCase: function (testCase, options) { + for (const testOptions of generateOptions()) { + const currentOptions = { ...testOptions, ...(options || {}) } + it('should build with: ' + util.inspect(currentOptions), async function () { + log.info('TEST', 'Running case for options of: ' + util.inspect(currentOptions)) + await testCase(currentOptions) + }) + } + }, +} + +module.exports = testRunner diff --git a/tests/fixtures/project/package.json b/tests/fixtures/project/package.json index e69de29b..0967ef42 100644 --- a/tests/fixtures/project/package.json +++ b/tests/fixtures/project/package.json @@ -0,0 +1 @@ +{} diff --git a/tests/index.js b/tests/index.js index 7e14f01f..1b7069c8 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1 +1 @@ -require("./es6"); \ No newline at end of file +require('./es6') From 859c4757c653f036ccb7e589e6a58f7a94928b4d Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 18 Jan 2024 20:08:27 +0000 Subject: [PATCH 35/50] chore: convert some classes to es6 --- lib/buildSystem.js | 202 +++++++-------- lib/cMake.js | 598 +++++++++++++++++++++---------------------- lib/cmLog.js | 104 ++++---- lib/dist.js | 278 ++++++++++---------- lib/downloader.js | 141 +++++----- lib/targetOptions.js | 58 ++--- lib/toolset.js | 357 +++++++++++++------------- 7 files changed, 831 insertions(+), 907 deletions(-) diff --git a/lib/buildSystem.js b/lib/buildSystem.js index b94d8893..2f7c14af 100644 --- a/lib/buildSystem.js +++ b/lib/buildSystem.js @@ -19,121 +19,107 @@ function isNodeApi(log, projectRoot) { } } -function BuildSystem(options) { - this.options = options || {} - this.options.directory = path.resolve(this.options.directory || process.cwd()) - this.options.out = path.resolve(this.options.out || path.join(this.options.directory, 'build')) - this.log = new CMLog(this.options) - this.options.isNodeApi = isNodeApi(this.log, this.options.directory) - const appConfig = appCMakeJSConfig(this.options.directory, this.log) - const npmOptions = npmConfig(this.log) - - if (isPlainObject(npmOptions) && Object.keys(npmOptions).length) { - this.options.runtimeDirectory = npmOptions['nodedir'] - this.options.msvsVersion = npmOptions['msvs_version'] - } - if (isPlainObject(appConfig)) { - if (Object.keys(appConfig).length) { - this.log.verbose('CFG', 'Applying CMake.js config from root package.json:') - this.log.verbose('CFG', JSON.stringify(appConfig)) - // Applying applications's config, if there is no explicit runtime related options specified - this.options.runtime = this.options.runtime || appConfig.runtime - this.options.runtimeVersion = this.options.runtimeVersion || appConfig.runtimeVersion - this.options.arch = this.options.arch || appConfig.arch +class BuildSystem { + constructor(options) { + this.options = options || {} + this.options.directory = path.resolve(this.options.directory || process.cwd()) + this.options.out = path.resolve(this.options.out || path.join(this.options.directory, 'build')) + this.log = new CMLog(this.options) + this.options.isNodeApi = isNodeApi(this.log, this.options.directory) + const appConfig = appCMakeJSConfig(this.options.directory, this.log) + const npmOptions = npmConfig(this.log) + + if (isPlainObject(npmOptions) && Object.keys(npmOptions).length) { + this.options.runtimeDirectory = npmOptions['nodedir'] + this.options.msvsVersion = npmOptions['msvs_version'] + } + if (isPlainObject(appConfig)) { + if (Object.keys(appConfig).length) { + this.log.verbose('CFG', 'Applying CMake.js config from root package.json:') + this.log.verbose('CFG', JSON.stringify(appConfig)) + // Applying applications's config, if there is no explicit runtime related options specified + this.options.runtime = this.options.runtime || appConfig.runtime + this.options.runtimeVersion = this.options.runtimeVersion || appConfig.runtimeVersion + this.options.arch = this.options.arch || appConfig.arch + } } + this.log.verbose('CFG', 'Build system options:') + this.log.verbose('CFG', JSON.stringify(this.options)) + this.cmake = new CMake(this.options) + this.dist = new Dist(this.options) + this.toolset = new Toolset(this.options) } - this.log.verbose('CFG', 'Build system options:') - this.log.verbose('CFG', JSON.stringify(this.options)) - this.cmake = new CMake(this.options) - this.dist = new Dist(this.options) - this.toolset = new Toolset(this.options) -} - -BuildSystem.prototype._ensureInstalled = async function () { - try { - await this.toolset.initialize(true) - if (!this.options.isNodeApi) { - await this.dist.ensureDownloaded() + async _ensureInstalled() { + try { + await this.toolset.initialize(true) + if (!this.options.isNodeApi) { + await this.dist.ensureDownloaded() + } + } catch (e) { + this._showError(e) + throw e } - } catch (e) { - this._showError(e) - throw e } -} - -BuildSystem.prototype._showError = function (e) { - if (this.log === undefined) { - // handle internal errors (init failed) - console.error('OMG', e.stack) - return + _showError(e) { + if (this.log === undefined) { + // handle internal errors (init failed) + console.error('OMG', e.stack) + return + } + if (this.log.level === 'verbose' || this.log.level === 'silly') { + this.log.error('OMG', e.stack) + } else { + this.log.error('OMG', e.message) + } } - if (this.log.level === 'verbose' || this.log.level === 'silly') { - this.log.error('OMG', e.stack) - } else { - this.log.error('OMG', e.message) + install() { + return this._ensureInstalled() } -} - -BuildSystem.prototype.install = function () { - return this._ensureInstalled() -} - -BuildSystem.prototype._invokeCMake = async function (method) { - try { - await this._ensureInstalled() - return await this.cmake[method]() - } catch (e) { - this._showError(e) - throw e + async _invokeCMake(method) { + try { + await this._ensureInstalled() + return await this.cmake[method]() + } catch (e) { + this._showError(e) + throw e + } + } + getConfigureCommand() { + return this._invokeCMake('getConfigureCommand') + } + getCmakeJsLibString() { + return this._invokeCMake('getCmakeJsLibString') + } + getCmakeJsIncludeString() { + return this._invokeCMake('getCmakeJsIncludeString') + } + getCmakeJsSrcString() { + return this._invokeCMake('getCmakeJsSrcString') + } + configure() { + return this._invokeCMake('configure') + } + getBuildCommand() { + return this._invokeCMake('getBuildCommand') + } + build() { + return this._invokeCMake('build') + } + getCleanCommand() { + return this._invokeCMake('getCleanCommand') + } + clean() { + return this._invokeCMake('clean') + } + reconfigure() { + return this._invokeCMake('reconfigure') + } + rebuild() { + return this._invokeCMake('rebuild') + } + compile() { + return this._invokeCMake('compile') } -} - -BuildSystem.prototype.getConfigureCommand = function () { - return this._invokeCMake('getConfigureCommand') -} - -BuildSystem.prototype.getCmakeJsLibString = function () { - return this._invokeCMake('getCmakeJsLibString') -} - -BuildSystem.prototype.getCmakeJsIncludeString = function () { - return this._invokeCMake('getCmakeJsIncludeString') -} - -BuildSystem.prototype.getCmakeJsSrcString = function () { - return this._invokeCMake('getCmakeJsSrcString') -} - -BuildSystem.prototype.configure = function () { - return this._invokeCMake('configure') -} - -BuildSystem.prototype.getBuildCommand = function () { - return this._invokeCMake('getBuildCommand') -} - -BuildSystem.prototype.build = function () { - return this._invokeCMake('build') -} - -BuildSystem.prototype.getCleanCommand = function () { - return this._invokeCMake('getCleanCommand') -} - -BuildSystem.prototype.clean = function () { - return this._invokeCMake('clean') -} - -BuildSystem.prototype.reconfigure = function () { - return this._invokeCMake('reconfigure') -} - -BuildSystem.prototype.rebuild = function () { - return this._invokeCMake('rebuild') -} - -BuildSystem.prototype.compile = function () { - return this._invokeCMake('compile') } module.exports = BuildSystem diff --git a/lib/cMake.js b/lib/cMake.js index 6042787f..6ec0a79a 100644 --- a/lib/cMake.js +++ b/lib/cMake.js @@ -13,372 +13,350 @@ const npmConfigData = require('rc')('npm') const Toolset = require('./toolset') const headers = require('node-api-headers') -function CMake(options) { - this.options = options || {} - this.log = new CMLog(this.options) - this.dist = new Dist(this.options) - this.projectRoot = path.resolve(this.options.directory || process.cwd()) - this.workDir = path.resolve(this.options.out || path.join(this.projectRoot, 'build')) - this.config = this.options.config || (this.options.debug ? 'Debug' : 'Release') - this.buildDir = path.join(this.workDir, this.config) - this._isAvailable = null - this.targetOptions = new TargetOptions(this.options) - this.toolset = new Toolset(this.options) - this.cMakeOptions = this.options.cMakeOptions || {} - this.extraCMakeArgs = this.options.extraCMakeArgs || [] - this.silent = !!options.silent -} - -Object.defineProperties(CMake.prototype, { - path: { - get: function () { - return this.options.cmakePath || 'cmake' - }, - }, - isAvailable: { - get: function () { - if (this._isAvailable === null) { - this._isAvailable = CMake.isAvailable(this.options) - } - return this._isAvailable - }, - }, -}) - -CMake.isAvailable = function (options) { - options = options || {} - try { - if (options.cmakePath) { - const stat = fs.lstatSync(options.cmakePath) - return !stat.isDirectory() - } else { - which.sync('cmake') - return true +class CMake { + get path() { + return this.options.cmakePath || 'cmake' + } + get isAvailable() { + if (this._isAvailable === null) { + this._isAvailable = CMake.isAvailable(this.options) } - } catch (e) { - // Ignore + return this._isAvailable } - return false -} -CMake.getGenerators = async function (options, log) { - const arch = ' [arch]' - options = options || {} - const gens = [] - if (CMake.isAvailable(options)) { - // try parsing machine-readable capabilities (available since CMake 3.7) + constructor(options) { + this.options = options || {} + this.log = new CMLog(this.options) + this.dist = new Dist(this.options) + this.projectRoot = path.resolve(this.options.directory || process.cwd()) + this.workDir = path.resolve(this.options.out || path.join(this.projectRoot, 'build')) + this.config = this.options.config || (this.options.debug ? 'Debug' : 'Release') + this.buildDir = path.join(this.workDir, this.config) + this._isAvailable = null + this.targetOptions = new TargetOptions(this.options) + this.toolset = new Toolset(this.options) + this.cMakeOptions = this.options.cMakeOptions || {} + this.extraCMakeArgs = this.options.extraCMakeArgs || [] + this.silent = !!options.silent + } + static isAvailable(options) { + options = options || {} try { - const stdout = await processHelpers.execFile([options.cmakePath || 'cmake', '-E', 'capabilities']) - const capabilities = JSON.parse(stdout) - return capabilities.generators.map((x) => x.name) - } catch (error) { - if (log) { - log.verbose('TOOL', 'Failed to query CMake capabilities (CMake is probably older than 3.7)') + if (options.cmakePath) { + const stat = fs.lstatSync(options.cmakePath) + return !stat.isDirectory() + } else { + which.sync('cmake') + return true } + } catch (e) { + // Ignore } + return false + } + static async getGenerators(options, log) { + const arch = ' [arch]' + options = options || {} + const gens = [] + if (CMake.isAvailable(options)) { + // try parsing machine-readable capabilities (available since CMake 3.7) + try { + const stdout = await processHelpers.execFile([options.cmakePath || 'cmake', '-E', 'capabilities']) + const capabilities = JSON.parse(stdout) + return capabilities.generators.map((x) => x.name) + } catch (error) { + if (log) { + log.verbose('TOOL', 'Failed to query CMake capabilities (CMake is probably older than 3.7)') + } + } - // fall back to parsing help text - const stdout = await processHelpers.execFile([options.cmakePath || 'cmake', '--help']) - const hasCr = stdout.includes('\r\n') - const output = hasCr ? stdout.split('\r\n') : stdout.split('\n') - let on = false - output.forEach(function (line, i) { - if (on) { - const parts = line.split('=') - if ( - (parts.length === 2 && parts[0].trim()) || - (parts.length === 1 && i !== output.length - 1 && output[i + 1].trim()[0] === '=') - ) { - let gen = parts[0].trim() - if (gen.endsWith(arch)) { - gen = gen.substr(0, gen.length - arch.length) + // fall back to parsing help text + const stdout = await processHelpers.execFile([options.cmakePath || 'cmake', '--help']) + const hasCr = stdout.includes('\r\n') + const output = hasCr ? stdout.split('\r\n') : stdout.split('\n') + let on = false + output.forEach(function (line, i) { + if (on) { + const parts = line.split('=') + if ( + (parts.length === 2 && parts[0].trim()) || + (parts.length === 1 && i !== output.length - 1 && output[i + 1].trim()[0] === '=') + ) { + let gen = parts[0].trim() + if (gen.endsWith(arch)) { + gen = gen.substr(0, gen.length - arch.length) + } + gens.push(gen) } - gens.push(gen) } - } - if (line.trim() === 'Generators') { - on = true - } - }) - } else { - throw new Error('CMake is not installed. Install CMake.') + if (line.trim() === 'Generators') { + on = true + } + }) + } else { + throw new Error('CMake is not installed. Install CMake.') + } + return gens } - return gens -} - -CMake.prototype.getGenerators = function () { - return CMake.getGenerators(this.options, this.log) -} - -CMake.prototype.verifyIfAvailable = function () { - if (!this.isAvailable) { - throw new Error( - "CMake executable is not found. Please use your system's package manager to install it, or you can get installers from there: http://cmake.org.", - ) + verifyIfAvailable() { + if (!this.isAvailable) { + throw new Error( + "CMake executable is not found. Please use your system's package manager to install it, or you can get installers from there: http://cmake.org.", + ) + } } -} - -CMake.prototype.getConfigureCommand = async function () { - // Create command: - let command = [this.path, this.projectRoot, '--no-warn-unused-cli'] + async getConfigureCommand() { + // Create command: + let command = [this.path, this.projectRoot, '--no-warn-unused-cli'] - const D = [] + const D = [] - // CMake.js watermark - D.push({ CMAKE_JS_VERSION: environment.cmakeJsVersion }) + // CMake.js watermark + D.push({ CMAKE_JS_VERSION: environment.cmakeJsVersion }) - // Build configuration: - D.push({ CMAKE_BUILD_TYPE: this.config }) - if (environment.isWin) { - D.push({ CMAKE_RUNTIME_OUTPUT_DIRECTORY: this.workDir }) - } else if (this.workDir.endsWith(this.config)) { - D.push({ CMAKE_LIBRARY_OUTPUT_DIRECTORY: this.workDir }) - } else { - D.push({ CMAKE_LIBRARY_OUTPUT_DIRECTORY: this.buildDir }) - } + // Build configuration: + D.push({ CMAKE_BUILD_TYPE: this.config }) + if (environment.isWin) { + D.push({ CMAKE_RUNTIME_OUTPUT_DIRECTORY: this.workDir }) + } else if (this.workDir.endsWith(this.config)) { + D.push({ CMAKE_LIBRARY_OUTPUT_DIRECTORY: this.workDir }) + } else { + D.push({ CMAKE_LIBRARY_OUTPUT_DIRECTORY: this.buildDir }) + } - // In some configurations MD builds will crash upon attempting to free memory. - // This tries to encourage MT builds which are larger but less likely to have this crash. - D.push({ CMAKE_MSVC_RUNTIME_LIBRARY: 'MultiThreaded$<$:Debug>' }) + // In some configurations MD builds will crash upon attempting to free memory. + // This tries to encourage MT builds which are larger but less likely to have this crash. + D.push({ CMAKE_MSVC_RUNTIME_LIBRARY: 'MultiThreaded$<$:Debug>' }) - // Includes: - const includesString = await this.getCmakeJsIncludeString() - D.push({ CMAKE_JS_INC: includesString }) + // Includes: + const includesString = await this.getCmakeJsIncludeString() + D.push({ CMAKE_JS_INC: includesString }) - // Sources: - const srcsString = this.getCmakeJsSrcString() - D.push({ CMAKE_JS_SRC: srcsString }) + // Sources: + const srcsString = this.getCmakeJsSrcString() + D.push({ CMAKE_JS_SRC: srcsString }) - // Runtime: - D.push({ NODE_RUNTIME: this.targetOptions.runtime }) - D.push({ NODE_RUNTIMEVERSION: this.targetOptions.runtimeVersion }) - D.push({ NODE_ARCH: this.targetOptions.arch }) + // Runtime: + D.push({ NODE_RUNTIME: this.targetOptions.runtime }) + D.push({ NODE_RUNTIMEVERSION: this.targetOptions.runtimeVersion }) + D.push({ NODE_ARCH: this.targetOptions.arch }) - if (environment.isOSX) { - if (this.targetOptions.arch) { - let xcodeArch = this.targetOptions.arch - if (xcodeArch === 'x64') xcodeArch = 'x86_64' - D.push({ CMAKE_OSX_ARCHITECTURES: xcodeArch }) + if (environment.isOSX) { + if (this.targetOptions.arch) { + let xcodeArch = this.targetOptions.arch + if (xcodeArch === 'x64') xcodeArch = 'x86_64' + D.push({ CMAKE_OSX_ARCHITECTURES: xcodeArch }) + } } - } - // Custom options - for (const [key, value] of Object.entries(this.cMakeOptions)) { - D.push({ [key]: value }) - } + // Custom options + for (const [key, value] of Object.entries(this.cMakeOptions)) { + D.push({ [key]: value }) + } - // Toolset: - await this.toolset.initialize(false) + // Toolset: + await this.toolset.initialize(false) - const libsString = this.getCmakeJsLibString() - D.push({ CMAKE_JS_LIB: libsString }) + const libsString = this.getCmakeJsLibString() + D.push({ CMAKE_JS_LIB: libsString }) - if (environment.isWin) { - const nodeLibDefPath = this.getNodeLibDefPath() - if (nodeLibDefPath) { - const nodeLibPath = path.join(this.workDir, 'node.lib') - D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) - D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) + if (environment.isWin) { + const nodeLibDefPath = this.getNodeLibDefPath() + if (nodeLibDefPath) { + const nodeLibPath = path.join(this.workDir, 'node.lib') + D.push({ CMAKE_JS_NODELIB_DEF: nodeLibDefPath }) + D.push({ CMAKE_JS_NODELIB_TARGET: nodeLibPath }) + } } - } - if (this.toolset.generator) { - command.push('-G', this.toolset.generator) - } - if (this.toolset.platform) { - command.push('-A', this.toolset.platform) - } - if (this.toolset.toolset) { - command.push('-T', this.toolset.toolset) - } - if (this.toolset.cppCompilerPath) { - D.push({ CMAKE_CXX_COMPILER: this.toolset.cppCompilerPath }) - } - if (this.toolset.cCompilerPath) { - D.push({ CMAKE_C_COMPILER: this.toolset.cCompilerPath }) - } - if (this.toolset.compilerFlags.length) { - D.push({ CMAKE_CXX_FLAGS: this.toolset.compilerFlags.join(' ') }) - } - if (this.toolset.linkerFlags.length) { - D.push({ CMAKE_SHARED_LINKER_FLAGS: this.toolset.linkerFlags.join(' ') }) - } - if (this.toolset.makePath) { - D.push({ CMAKE_MAKE_PROGRAM: this.toolset.makePath }) - } + if (this.toolset.generator) { + command.push('-G', this.toolset.generator) + } + if (this.toolset.platform) { + command.push('-A', this.toolset.platform) + } + if (this.toolset.toolset) { + command.push('-T', this.toolset.toolset) + } + if (this.toolset.cppCompilerPath) { + D.push({ CMAKE_CXX_COMPILER: this.toolset.cppCompilerPath }) + } + if (this.toolset.cCompilerPath) { + D.push({ CMAKE_C_COMPILER: this.toolset.cCompilerPath }) + } + if (this.toolset.compilerFlags.length) { + D.push({ CMAKE_CXX_FLAGS: this.toolset.compilerFlags.join(' ') }) + } + if (this.toolset.linkerFlags.length) { + D.push({ CMAKE_SHARED_LINKER_FLAGS: this.toolset.linkerFlags.join(' ') }) + } + if (this.toolset.makePath) { + D.push({ CMAKE_MAKE_PROGRAM: this.toolset.makePath }) + } - // Load NPM config - for (const [key, value] of Object.entries(npmConfigData)) { - if (key.startsWith('cmake_')) { - const sk = key.substr(6) - if (sk && value) { - D.push({ [sk]: value }) + // Load NPM config + for (const [key, value] of Object.entries(npmConfigData)) { + if (key.startsWith('cmake_')) { + const sk = key.substr(6) + if (sk && value) { + D.push({ [sk]: value }) + } } } - } - command = command.concat( - D.map(function (p) { - return '-D' + Object.keys(p)[0] + '=' + Object.values(p)[0] - }), - ) - - return command.concat(this.extraCMakeArgs) -} + command = command.concat( + D.map(function (p) { + return '-D' + Object.keys(p)[0] + '=' + Object.values(p)[0] + }), + ) -CMake.prototype.getCmakeJsLibString = function () { - const libs = [] - if (environment.isWin) { - const nodeLibDefPath = this.getNodeLibDefPath() - if (nodeLibDefPath) { - libs.push(path.join(this.workDir, 'node.lib')) - } else { - libs.push(...this.dist.winLibs) + return command.concat(this.extraCMakeArgs) + } + getCmakeJsLibString() { + const libs = [] + if (environment.isWin) { + const nodeLibDefPath = this.getNodeLibDefPath() + if (nodeLibDefPath) { + libs.push(path.join(this.workDir, 'node.lib')) + } else { + libs.push(...this.dist.winLibs) + } } + return libs.join(';') } - return libs.join(';') -} + async getCmakeJsIncludeString() { + let incPaths = [] + if (!this.options.isNodeApi) { + // Include and lib: + if (this.dist.headerOnly) { + incPaths = [path.join(this.dist.internalPath, '/include/node')] + } else { + const nodeH = path.join(this.dist.internalPath, '/src') + const v8H = path.join(this.dist.internalPath, '/deps/v8/include') + const uvH = path.join(this.dist.internalPath, '/deps/uv/include') + incPaths = [nodeH, v8H, uvH] + } -CMake.prototype.getCmakeJsIncludeString = async function () { - let incPaths = [] - if (!this.options.isNodeApi) { - // Include and lib: - if (this.dist.headerOnly) { - incPaths = [path.join(this.dist.internalPath, '/include/node')] + // NAN + const nanH = await locateNAN(this.projectRoot) + if (nanH) { + incPaths.push(nanH) + } } else { - const nodeH = path.join(this.dist.internalPath, '/src') - const v8H = path.join(this.dist.internalPath, '/deps/v8/include') - const uvH = path.join(this.dist.internalPath, '/deps/uv/include') - incPaths = [nodeH, v8H, uvH] + // Base headers + const apiHeaders = require('node-api-headers') + incPaths.push(apiHeaders.include_dir) + + // Node-api + const napiH = await locateNodeApi(this.projectRoot) + if (napiH) { + incPaths.push(napiH) + } } - // NAN - const nanH = await locateNAN(this.projectRoot) - if (nanH) { - incPaths.push(nanH) - } - } else { - // Base headers - const apiHeaders = require('node-api-headers') - incPaths.push(apiHeaders.include_dir) - - // Node-api - const napiH = await locateNodeApi(this.projectRoot) - if (napiH) { - incPaths.push(napiH) - } + return incPaths.join(';') } + getCmakeJsSrcString() { + const srcPaths = [] + if (environment.isWin) { + const delayHook = path.normalize(path.join(__dirname, 'cpp', 'win_delay_load_hook.cc')) - return incPaths.join(';') -} - -CMake.prototype.getCmakeJsSrcString = function () { - const srcPaths = [] - if (environment.isWin) { - const delayHook = path.normalize(path.join(__dirname, 'cpp', 'win_delay_load_hook.cc')) + srcPaths.push(delayHook.replace(/\\/gm, '/')) + } - srcPaths.push(delayHook.replace(/\\/gm, '/')) + return srcPaths.join(';') } + getNodeLibDefPath() { + return environment.isWin && this.options.isNodeApi ? headers.def_paths.node_api_def : undefined + } + async configure() { + this.verifyIfAvailable() - return srcPaths.join(';') -} - -CMake.prototype.getNodeLibDefPath = function () { - return environment.isWin && this.options.isNodeApi ? headers.def_paths.node_api_def : undefined -} + this.log.info('CMD', 'CONFIGURE') + const listPath = path.join(this.projectRoot, 'CMakeLists.txt') + const command = await this.getConfigureCommand() -CMake.prototype.configure = async function () { - this.verifyIfAvailable() + try { + await fs.lstat(listPath) + } catch (e) { + throw new Error("'" + listPath + "' not found.") + } - this.log.info('CMD', 'CONFIGURE') - const listPath = path.join(this.projectRoot, 'CMakeLists.txt') - const command = await this.getConfigureCommand() + try { + await fs.ensureDir(this.workDir) + } catch (e) { + // Ignore + } - try { - await fs.lstat(listPath) - } catch (e) { - throw new Error("'" + listPath + "' not found.") + const cwd = process.cwd() + process.chdir(this.workDir) + try { + await this._run(command) + } finally { + process.chdir(cwd) + } } - - try { - await fs.ensureDir(this.workDir) - } catch (e) { - // Ignore + async ensureConfigured() { + try { + await fs.lstat(path.join(this.workDir, 'CMakeCache.txt')) + } catch (e) { + await this.configure() + } } - - const cwd = process.cwd() - process.chdir(this.workDir) - try { - await this._run(command) - } finally { - process.chdir(cwd) + getBuildCommand() { + const command = [this.path, '--build', this.workDir, '--config', this.config] + if (this.options.target) { + command.push('--target', this.options.target) + } + if (this.options.parallel) { + command.push('--parallel', this.options.parallel) + } + return Promise.resolve(command.concat(this.extraCMakeArgs)) } -} + async build() { + this.verifyIfAvailable() -CMake.prototype.ensureConfigured = async function () { - try { - await fs.lstat(path.join(this.workDir, 'CMakeCache.txt')) - } catch (e) { - await this.configure() + await this.ensureConfigured() + const buildCommand = await this.getBuildCommand() + this.log.info('CMD', 'BUILD') + await this._run(buildCommand) } -} + getCleanCommand() { + return [this.path, '-E', 'remove_directory', this.workDir].concat(this.extraCMakeArgs) + } + clean() { + this.verifyIfAvailable() -CMake.prototype.getBuildCommand = function () { - const command = [this.path, '--build', this.workDir, '--config', this.config] - if (this.options.target) { - command.push('--target', this.options.target) + this.log.info('CMD', 'CLEAN') + return this._run(this.getCleanCommand()) } - if (this.options.parallel) { - command.push('--parallel', this.options.parallel) + async reconfigure() { + this.extraCMakeArgs = [] + await this.clean() + await this.configure() } - return Promise.resolve(command.concat(this.extraCMakeArgs)) -} - -CMake.prototype.build = async function () { - this.verifyIfAvailable() - - await this.ensureConfigured() - const buildCommand = await this.getBuildCommand() - this.log.info('CMD', 'BUILD') - await this._run(buildCommand) -} - -CMake.prototype.getCleanCommand = function () { - return [this.path, '-E', 'remove_directory', this.workDir].concat(this.extraCMakeArgs) -} - -CMake.prototype.clean = function () { - this.verifyIfAvailable() - - this.log.info('CMD', 'CLEAN') - return this._run(this.getCleanCommand()) -} - -CMake.prototype.reconfigure = async function () { - this.extraCMakeArgs = [] - await this.clean() - await this.configure() -} - -CMake.prototype.rebuild = async function () { - this.extraCMakeArgs = [] - await this.clean() - await this.build() -} - -CMake.prototype.compile = async function () { - this.extraCMakeArgs = [] - try { + async rebuild() { + this.extraCMakeArgs = [] + await this.clean() await this.build() - } catch (e) { - this.log.info('REP', 'Build has been failed, trying to do a full rebuild.') - await this.rebuild() } -} + async compile() { + this.extraCMakeArgs = [] + try { + await this.build() + } catch (e) { + this.log.info('REP', 'Build has been failed, trying to do a full rebuild.') + await this.rebuild() + } + } + _run(command) { + this.log.info('RUN', command) + return processHelpers.run(command, { silent: this.silent }) + } -CMake.prototype._run = function (command) { - this.log.info('RUN', command) - return processHelpers.run(command, { silent: this.silent }) + async getGenerators() { + return CMake.getGenerators(this.options, this.log) + } } module.exports = CMake diff --git a/lib/cmLog.js b/lib/cmLog.js index eba5c9b2..58fbdfa6 100644 --- a/lib/cmLog.js +++ b/lib/cmLog.js @@ -1,68 +1,60 @@ 'use strict' const log = require('npmlog') -function CMLog(options) { - this.options = options || {} - this.debug = require('debug')(this.options.logName || 'cmake-js') -} - -Object.defineProperties(CMLog.prototype, { - level: { - get: function () { - if (this.options.noLog) { - return 'silly' - } else { - return log.level - } - }, - }, -}) - -CMLog.prototype.silly = function (cat, msg) { - if (this.options.noLog) { - this.debug(cat + ': ' + msg) - } else { - log.silly(cat, msg) +class CMLog { + get level() { + if (this.options.noLog) { + return 'silly' + } else { + return log.level + } } -} -CMLog.prototype.verbose = function (cat, msg) { - if (this.options.noLog) { - this.debug(cat + ': ' + msg) - } else { - log.verbose(cat, msg) + constructor(options) { + this.options = options || {} + this.debug = require('debug')(this.options.logName || 'cmake-js') } -} - -CMLog.prototype.info = function (cat, msg) { - if (this.options.noLog) { - this.debug(cat + ': ' + msg) - } else { - log.info(cat, msg) + silly(cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.silly(cat, msg) + } } -} - -CMLog.prototype.warn = function (cat, msg) { - if (this.options.noLog) { - this.debug(cat + ': ' + msg) - } else { - log.warn(cat, msg) + verbose(cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.verbose(cat, msg) + } } -} - -CMLog.prototype.http = function (cat, msg) { - if (this.options.noLog) { - this.debug(cat + ': ' + msg) - } else { - log.http(cat, msg) + info(cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.info(cat, msg) + } } -} - -CMLog.prototype.error = function (cat, msg) { - if (this.options.noLog) { - this.debug(cat + ': ' + msg) - } else { - log.error(cat, msg) + warn(cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.warn(cat, msg) + } + } + http(cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.http(cat, msg) + } + } + error(cat, msg) { + if (this.options.noLog) { + this.debug(cat + ': ' + msg) + } else { + log.error(cat, msg) + } } } diff --git a/lib/dist.js b/lib/dist.js index bf5ae1ac..8fc067d2 100644 --- a/lib/dist.js +++ b/lib/dist.js @@ -18,172 +18,156 @@ function testSum(sums, sum, fPath) { throw new Error("SHA sum of file '" + fPath + "' mismatch!") } -function Dist(options) { - this.options = options || {} - this.log = new CMLog(this.options) - this.targetOptions = new TargetOptions(this.options) - this.downloader = new Downloader(this.options) -} - -// Props -Object.defineProperties(Dist.prototype, { - internalPath: { - get: function () { - const cacheDirectory = '.cmake-js' - const runtimeArchDirectory = this.targetOptions.runtime + '-' + this.targetOptions.arch - const runtimeVersionDirectory = 'v' + this.targetOptions.runtimeVersion - - return ( - this.options.runtimeDirectory || - path.join(environment.home, cacheDirectory, runtimeArchDirectory, runtimeVersionDirectory) - ) - }, - }, - externalPath: { - get: function () { - return runtimePaths.get(this.targetOptions).externalPath - }, - }, - downloaded: { - get: function () { - let headers = false - let libs = true - let stat = getStat(this.internalPath) - if (stat.isDirectory()) { - if (this.headerOnly) { - stat = getStat(path.join(this.internalPath, 'include/node/node.h')) +class Dist { + get internalPath() { + const cacheDirectory = '.cmake-js' + const runtimeArchDirectory = this.targetOptions.runtime + '-' + this.targetOptions.arch + const runtimeVersionDirectory = 'v' + this.targetOptions.runtimeVersion + + return ( + this.options.runtimeDirectory || + path.join(environment.home, cacheDirectory, runtimeArchDirectory, runtimeVersionDirectory) + ) + } + get externalPath() { + return runtimePaths.get(this.targetOptions).externalPath + } + get downloaded() { + let headers = false + let libs = true + let stat = getStat(this.internalPath) + if (stat.isDirectory()) { + if (this.headerOnly) { + stat = getStat(path.join(this.internalPath, 'include/node/node.h')) + headers = stat.isFile() + } else { + stat = getStat(path.join(this.internalPath, 'src/node.h')) + if (stat.isFile()) { + stat = getStat(path.join(this.internalPath, 'deps/v8/include/v8.h')) headers = stat.isFile() - } else { - stat = getStat(path.join(this.internalPath, 'src/node.h')) - if (stat.isFile()) { - stat = getStat(path.join(this.internalPath, 'deps/v8/include/v8.h')) - headers = stat.isFile() - } } - if (environment.isWin) { - for (const libPath of this.winLibs) { - stat = getStat(libPath) - libs = libs && stat.isFile() - } + } + if (environment.isWin) { + for (const libPath of this.winLibs) { + stat = getStat(libPath) + libs = libs && stat.isFile() } } - return headers && libs + } + return headers && libs - function getStat(path) { - try { - return fs.statSync(path) - } catch (e) { - return { - isFile: () => false, - isDirectory: () => false, - } + function getStat(path) { + try { + return fs.statSync(path) + } catch (e) { + return { + isFile: () => false, + isDirectory: () => false, } } - }, - }, - winLibs: { - get: function () { - const libs = runtimePaths.get(this.targetOptions).winLibs - const result = [] - for (const lib of libs) { - result.push(path.join(this.internalPath, lib.dir, lib.name)) - } - return result - }, - }, - headerOnly: { - get: function () { - return runtimePaths.get(this.targetOptions).headerOnly - }, - }, -}) - -// Methods -Dist.prototype.ensureDownloaded = async function () { - if (!this.downloaded) { - await this.download() + } + } + get winLibs() { + const libs = runtimePaths.get(this.targetOptions).winLibs + const result = [] + for (const lib of libs) { + result.push(path.join(this.internalPath, lib.dir, lib.name)) + } + return result + } + get headerOnly() { + return runtimePaths.get(this.targetOptions).headerOnly } -} -Dist.prototype.download = async function () { - const log = this.log - log.info('DIST', 'Downloading distribution files to: ' + this.internalPath) - await fs.ensureDir(this.internalPath) - const sums = await this._downloadShaSums() - await Promise.all([this._downloadLibs(sums), this._downloadTar(sums)]) -} + constructor(options) { + this.options = options || {} + this.log = new CMLog(this.options) + this.targetOptions = new TargetOptions(this.options) + this.downloader = new Downloader(this.options) + } -Dist.prototype._downloadShaSums = async function () { - if (this.targetOptions.runtime === 'node') { - const sumUrl = urljoin(this.externalPath, 'SHASUMS256.txt') + async ensureDownloaded() { + if (!this.downloaded) { + await this.download() + } + } + async download() { const log = this.log - log.http('DIST', '\t- ' + sumUrl) - return (await this.downloader.downloadString(sumUrl)) - .split('\n') - .map(function (line) { - const parts = line.split(/\s+/) - return { - getPath: parts[1], - sum: parts[0], - } - }) - .filter(function (i) { - return i.getPath && i.sum - }) - } else { - return null + log.info('DIST', 'Downloading distribution files to: ' + this.internalPath) + await fs.ensureDir(this.internalPath) + const sums = await this._downloadShaSums() + await Promise.all([this._downloadLibs(sums), this._downloadTar(sums)]) } -} - -Dist.prototype._downloadTar = async function (sums) { - const log = this.log - const self = this - const tarLocalPath = runtimePaths.get(self.targetOptions).tarPath - const tarUrl = urljoin(self.externalPath, tarLocalPath) - log.http('DIST', '\t- ' + tarUrl) - - const sum = await this.downloader.downloadTgz(tarUrl, { - hash: sums ? 'sha256' : null, - cwd: self.internalPath, - strip: 1, - filter: function (entryPath) { - if (entryPath === self.internalPath) { - return true - } - const ext = path.extname(entryPath) - return ext && ext.toLowerCase() === '.h' - }, - }) - - if (sums) { - testSum(sums, sum, tarLocalPath) + async _downloadShaSums() { + if (this.targetOptions.runtime === 'node') { + const sumUrl = urljoin(this.externalPath, 'SHASUMS256.txt') + const log = this.log + log.http('DIST', '\t- ' + sumUrl) + return (await this.downloader.downloadString(sumUrl)) + .split('\n') + .map(function (line) { + const parts = line.split(/\s+/) + return { + getPath: parts[1], + sum: parts[0], + } + }) + .filter(function (i) { + return i.getPath && i.sum + }) + } else { + return null + } } -} + async _downloadTar(sums) { + const log = this.log + const self = this + const tarLocalPath = runtimePaths.get(self.targetOptions).tarPath + const tarUrl = urljoin(self.externalPath, tarLocalPath) + log.http('DIST', '\t- ' + tarUrl) -Dist.prototype._downloadLibs = async function (sums) { - const log = this.log - const self = this - if (!environment.isWin) { - return + const sum = await this.downloader.downloadTgz(tarUrl, { + hash: sums ? 'sha256' : null, + cwd: self.internalPath, + strip: 1, + filter: function (entryPath) { + if (entryPath === self.internalPath) { + return true + } + const ext = path.extname(entryPath) + return ext && ext.toLowerCase() === '.h' + }, + }) + + if (sums) { + testSum(sums, sum, tarLocalPath) + } } + async _downloadLibs(sums) { + const log = this.log + const self = this + if (!environment.isWin) { + return + } - const paths = runtimePaths.get(self.targetOptions) - for (const dirs of paths.winLibs) { - const subDir = dirs.dir - const fn = dirs.name - const fPath = subDir ? urljoin(subDir, fn) : fn - const libUrl = urljoin(self.externalPath, fPath) - log.http('DIST', '\t- ' + libUrl) + const paths = runtimePaths.get(self.targetOptions) + for (const dirs of paths.winLibs) { + const subDir = dirs.dir + const fn = dirs.name + const fPath = subDir ? urljoin(subDir, fn) : fn + const libUrl = urljoin(self.externalPath, fPath) + log.http('DIST', '\t- ' + libUrl) - await fs.ensureDir(path.join(self.internalPath, subDir)) + await fs.ensureDir(path.join(self.internalPath, subDir)) - const sum = await this.downloader.downloadFile(libUrl, { - path: path.join(self.internalPath, fPath), - hash: sums ? 'sha256' : null, - }) + const sum = await this.downloader.downloadFile(libUrl, { + path: path.join(self.internalPath, fPath), + hash: sums ? 'sha256' : null, + }) - if (sums) { - testSum(sums, sum, fPath) + if (sums) { + testSum(sums, sum, fPath) + } } } } diff --git a/lib/downloader.js b/lib/downloader.js index 46d48093..182789ec 100644 --- a/lib/downloader.js +++ b/lib/downloader.js @@ -7,88 +7,85 @@ const tar = require('tar') const fs = require('fs') const CMLog = require('./cmLog') -function Downloader(options) { - this.options = options || {} - this.log = new CMLog(this.options) -} - -Downloader.prototype.downloadToStream = function (url, stream, hash) { - const self = this - const shasum = hash ? crypto.createHash(hash) : null - return new Promise(function (resolve, reject) { - let length = 0 - let done = 0 - let lastPercent = 0 - axios - .get(url, { responseType: 'stream' }) - .then(function (response) { - length = parseInt(response.headers['content-length']) - if (typeof length !== 'number') { - length = 0 - } - - response.data.on('data', function (chunk) { - if (shasum) { - shasum.update(chunk) +class Downloader { + constructor(options) { + this.options = options || {} + this.log = new CMLog(this.options) + } + downloadToStream(url, stream, hash) { + const self = this + const shasum = hash ? crypto.createHash(hash) : null + return new Promise(function (resolve, reject) { + let length = 0 + let done = 0 + let lastPercent = 0 + axios + .get(url, { responseType: 'stream' }) + .then(function (response) { + length = parseInt(response.headers['content-length']) + if (typeof length !== 'number') { + length = 0 } - if (length) { - done += chunk.length - let percent = (done / length) * 100 - percent = Math.round(percent / 10) * 10 + 10 - if (percent > lastPercent) { - self.log.verbose('DWNL', '\t' + lastPercent + '%') - lastPercent = percent + + response.data.on('data', function (chunk) { + if (shasum) { + shasum.update(chunk) } - } + if (length) { + done += chunk.length + let percent = (done / length) * 100 + percent = Math.round(percent / 10) * 10 + 10 + if (percent > lastPercent) { + self.log.verbose('DWNL', '\t' + lastPercent + '%') + lastPercent = percent + } + } + }) + + response.data.pipe(stream) + }) + .catch(function (err) { + reject(err) }) - response.data.pipe(stream) - }) - .catch(function (err) { + stream.once('error', function (err) { reject(err) }) - stream.once('error', function (err) { - reject(err) - }) - - stream.once('finish', function () { - resolve(shasum ? shasum.digest('hex') : undefined) + stream.once('finish', function () { + resolve(shasum ? shasum.digest('hex') : undefined) + }) }) - }) -} - -Downloader.prototype.downloadString = async function (url) { - const result = new MemoryStream() - await this.downloadToStream(url, result) - return result.toString() -} - -Downloader.prototype.downloadFile = async function (url, options) { - if (typeof options === 'string') { - options.path = options } - const result = fs.createWriteStream(options.path) - const sum = await this.downloadToStream(url, result, options.hash) - this.testSum(url, sum, options) - return sum -} - -Downloader.prototype.downloadTgz = async function (url, options) { - if (typeof options === 'string') { - options.cwd = options + async downloadString(url) { + const result = new MemoryStream() + await this.downloadToStream(url, result) + return result.toString() } - const gunzip = zlib.createGunzip() - const extractor = tar.extract(options) - gunzip.pipe(extractor) - const sum = await this.downloadToStream(url, gunzip, options.hash) - this.testSum(url, sum, options) - return sum -} - -Downloader.prototype.testSum = function (url, sum, options) { - if (options.hash && sum && options.sum && options.sum !== sum) { - throw new Error(options.hash.toUpperCase() + " sum of download '" + url + "' mismatch!") + async downloadFile(url, options) { + if (typeof options === 'string') { + options.path = options + } + const result = fs.createWriteStream(options.path) + const sum = await this.downloadToStream(url, result, options.hash) + this.testSum(url, sum, options) + return sum + } + async downloadTgz(url, options) { + if (typeof options === 'string') { + options.cwd = options + } + const gunzip = zlib.createGunzip() + const extractor = tar.extract(options) + gunzip.pipe(extractor) + const sum = await this.downloadToStream(url, gunzip, options.hash) + this.testSum(url, sum, options) + return sum + } + testSum(url, sum, options) { + if (options.hash && sum && options.sum && options.sum !== sum) { + throw new Error(options.hash.toUpperCase() + " sum of download '" + url + "' mismatch!") + } } } diff --git a/lib/targetOptions.js b/lib/targetOptions.js index 69307f69..c61bf6fe 100644 --- a/lib/targetOptions.js +++ b/lib/targetOptions.js @@ -2,41 +2,29 @@ const environment = require('./environment') -function TargetOptions(options) { - this.options = options || {} -} +class TargetOptions { + get arch() { + return this.options.arch || environment.arch + } + get isX86() { + return this.arch === 'ia32' || this.arch === 'x86' + } + get isX64() { + return this.arch === 'x64' + } + get isArm() { + return this.arch === 'arm' + } + get runtime() { + return this.options.runtime || environment.runtime + } + get runtimeVersion() { + return this.options.runtimeVersion || environment.runtimeVersion + } -Object.defineProperties(TargetOptions.prototype, { - arch: { - get: function () { - return this.options.arch || environment.arch - }, - }, - isX86: { - get: function () { - return this.arch === 'ia32' || this.arch === 'x86' - }, - }, - isX64: { - get: function () { - return this.arch === 'x64' - }, - }, - isArm: { - get: function () { - return this.arch === 'arm' - }, - }, - runtime: { - get: function () { - return this.options.runtime || environment.runtime - }, - }, - runtimeVersion: { - get: function () { - return this.options.runtimeVersion || environment.runtimeVersion - }, - }, -}) + constructor(options) { + this.options = options || {} + } +} module.exports = TargetOptions diff --git a/lib/toolset.js b/lib/toolset.js index 981327e0..3d9c034d 100644 --- a/lib/toolset.js +++ b/lib/toolset.js @@ -5,221 +5,220 @@ const assert = require('assert') const CMLog = require('./cmLog') const { findVisualStudio } = environment.isWin ? require('./import/find-visualstudio') : {} -function Toolset(options) { - this.options = options || {} - this.targetOptions = new TargetOptions(this.options) - this.generator = options.generator - this.toolset = options.toolset - this.platform = options.platform - this.target = options.target - this.cCompilerPath = options.cCompilerPath - this.cppCompilerPath = options.cppCompilerPath - this.compilerFlags = [] - this.linkerFlags = [] - this.makePath = null - this.log = new CMLog(this.options) - this._initialized = false -} - -Toolset.prototype.initialize = async function (install) { - if (!this._initialized) { - if (environment.isWin) { - await this.initializeWin(install) - } else { - this.initializePosix(install) - } - this._initialized = true +class Toolset { + constructor(options) { + this.options = options || {} + this.targetOptions = new TargetOptions(this.options) + this.generator = options.generator + this.toolset = options.toolset + this.platform = options.platform + this.target = options.target + this.cCompilerPath = options.cCompilerPath + this.cppCompilerPath = options.cppCompilerPath + this.compilerFlags = [] + this.linkerFlags = [] + this.makePath = null + this.log = new CMLog(this.options) + this._initialized = false } -} - -Toolset.prototype.initializePosix = function (install) { - if (!this.cCompilerPath || !this.cppCompilerPath) { - // 1: Compiler - if (!environment.isGPPAvailable && !environment.isClangAvailable) { - if (environment.isOSX) { - throw new Error( - "C++ Compiler toolset is not available. Install Xcode Commandline Tools from Apple Dev Center, or install Clang with homebrew by invoking: 'brew install llvm --with-clang --with-asan'.", - ) + async initialize(install) { + if (!this._initialized) { + if (environment.isWin) { + await this.initializeWin(install) } else { - throw new Error( - "C++ Compiler toolset is not available. Install proper compiler toolset with your package manager, eg. 'sudo apt-get install g++'.", - ) + this.initializePosix(install) } + this._initialized = true } - - if (this.options.preferClang && environment.isClangAvailable) { - if (install) { - this.log.info('TOOL', 'Using clang++ compiler, because preferClang option is set, and clang++ is available.') + } + initializePosix(install) { + if (!this.cCompilerPath || !this.cppCompilerPath) { + // 1: Compiler + if (!environment.isGPPAvailable && !environment.isClangAvailable) { + if (environment.isOSX) { + throw new Error( + "C++ Compiler toolset is not available. Install Xcode Commandline Tools from Apple Dev Center, or install Clang with homebrew by invoking: 'brew install llvm --with-clang --with-asan'.", + ) + } else { + throw new Error( + "C++ Compiler toolset is not available. Install proper compiler toolset with your package manager, eg. 'sudo apt-get install g++'.", + ) + } } - this.cppCompilerPath = this.cppCompilerPath || 'clang++' - this.cCompilerPath = this.cCompilerPath || 'clang' - } else if (this.options.preferGnu && environment.isGPPAvailable) { - if (install) { - this.log.info('TOOL', 'Using g++ compiler, because preferGnu option is set, and g++ is available.') + + if (this.options.preferClang && environment.isClangAvailable) { + if (install) { + this.log.info('TOOL', 'Using clang++ compiler, because preferClang option is set, and clang++ is available.') + } + this.cppCompilerPath = this.cppCompilerPath || 'clang++' + this.cCompilerPath = this.cCompilerPath || 'clang' + } else if (this.options.preferGnu && environment.isGPPAvailable) { + if (install) { + this.log.info('TOOL', 'Using g++ compiler, because preferGnu option is set, and g++ is available.') + } + this.cppCompilerPath = this.cppCompilerPath || 'g++' + this.cCompilerPath = this.cCompilerPath || 'gcc' } - this.cppCompilerPath = this.cppCompilerPath || 'g++' - this.cCompilerPath = this.cCompilerPath || 'gcc' - } - } - // if it's already set because of options... - if (this.generator) { - if (install) { - this.log.info('TOOL', 'Using ' + this.generator + ' generator, as specified from commandline.') } - } - // 2: Generator - else if (environment.isOSX) { - if (this.options.preferXcode) { - if (install) { - this.log.info('TOOL', 'Using Xcode generator, because preferXcode option is set.') - } - this.generator = 'Xcode' - } else if (this.options.preferMake && environment.isMakeAvailable) { + // if it's already set because of options... + if (this.generator) { if (install) { - this.log.info( - 'TOOL', - 'Using Unix Makefiles generator, because preferMake option is set, and make is available.', - ) + this.log.info('TOOL', 'Using ' + this.generator + ' generator, as specified from commandline.') } - this.generator = 'Unix Makefiles' - } else if (environment.isNinjaAvailable) { - if (install) { - this.log.info('TOOL', 'Using Ninja generator, because ninja is available.') + } + + // 2: Generator + else if (environment.isOSX) { + if (this.options.preferXcode) { + if (install) { + this.log.info('TOOL', 'Using Xcode generator, because preferXcode option is set.') + } + this.generator = 'Xcode' + } else if (this.options.preferMake && environment.isMakeAvailable) { + if (install) { + this.log.info( + 'TOOL', + 'Using Unix Makefiles generator, because preferMake option is set, and make is available.', + ) + } + this.generator = 'Unix Makefiles' + } else if (environment.isNinjaAvailable) { + if (install) { + this.log.info('TOOL', 'Using Ninja generator, because ninja is available.') + } + this.generator = 'Ninja' + } else { + if (install) { + this.log.info('TOOL', 'Using Unix Makefiles generator.') + } + this.generator = 'Unix Makefiles' } - this.generator = 'Ninja' } else { - if (install) { - this.log.info('TOOL', 'Using Unix Makefiles generator.') + if (this.options.preferMake && environment.isMakeAvailable) { + if (install) { + this.log.info( + 'TOOL', + 'Using Unix Makefiles generator, because preferMake option is set, and make is available.', + ) + } + this.generator = 'Unix Makefiles' + } else if (environment.isNinjaAvailable) { + if (install) { + this.log.info('TOOL', 'Using Ninja generator, because ninja is available.') + } + this.generator = 'Ninja' + } else { + if (install) { + this.log.info('TOOL', 'Using Unix Makefiles generator.') + } + this.generator = 'Unix Makefiles' } - this.generator = 'Unix Makefiles' } - } else { - if (this.options.preferMake && environment.isMakeAvailable) { - if (install) { - this.log.info( - 'TOOL', - 'Using Unix Makefiles generator, because preferMake option is set, and make is available.', - ) - } - this.generator = 'Unix Makefiles' - } else if (environment.isNinjaAvailable) { - if (install) { - this.log.info('TOOL', 'Using Ninja generator, because ninja is available.') - } - this.generator = 'Ninja' - } else { + + // 3: Flags + if (environment.isOSX) { if (install) { - this.log.info('TOOL', 'Using Unix Makefiles generator.') + this.log.verbose('TOOL', 'Setting default OSX compiler flags.') } - this.generator = 'Unix Makefiles' - } - } - // 3: Flags - if (environment.isOSX) { - if (install) { - this.log.verbose('TOOL', 'Setting default OSX compiler flags.') + this.compilerFlags.push('-D_DARWIN_USE_64_BIT_INODE=1') + this.compilerFlags.push('-D_LARGEFILE_SOURCE') + this.compilerFlags.push('-D_FILE_OFFSET_BITS=64') + this.linkerFlags.push('-undefined dynamic_lookup') } - this.compilerFlags.push('-D_DARWIN_USE_64_BIT_INODE=1') - this.compilerFlags.push('-D_LARGEFILE_SOURCE') - this.compilerFlags.push('-D_FILE_OFFSET_BITS=64') - this.linkerFlags.push('-undefined dynamic_lookup') - } + this.compilerFlags.push('-DBUILDING_NODE_EXTENSION') - this.compilerFlags.push('-DBUILDING_NODE_EXTENSION') - - // 4: Build target - if (this.options.target) { - this.log.info('TOOL', 'Building only the ' + this.options.target + ' target, as specified from the command line.') + // 4: Build target + if (this.options.target) { + this.log.info('TOOL', 'Building only the ' + this.options.target + ' target, as specified from the command line.') + } } -} - -Toolset.prototype.initializeWin = async function (install) { - if (!this.generator) { - const foundVsInfo = await this._getTopSupportedVisualStudioGenerator() - if (foundVsInfo) { - if (install) { - this.log.info('TOOL', `Using ${foundVsInfo.generator} generator.`) - } - this.generator = foundVsInfo.generator - - const isAboveVS16 = foundVsInfo.versionMajor >= 16 - - // The CMake Visual Studio Generator does not support the Win64 or ARM suffix on - // the generator name. Instead the generator platform must be set explicitly via - // the platform parameter - if (!this.platform && isAboveVS16) { - switch (this.targetOptions.arch) { - case 'ia32': - case 'x86': - this.platform = 'Win32' - break - case 'x64': - this.platform = 'x64' - break - case 'arm': - this.platform = 'ARM' - break - case 'arm64': - this.platform = 'ARM64' - break - default: - this.log.warn('TOOL', 'Unknown NodeJS architecture: ' + this.targetOptions.arch) - break + async initializeWin(install) { + if (!this.generator) { + const foundVsInfo = await this._getTopSupportedVisualStudioGenerator() + if (foundVsInfo) { + if (install) { + this.log.info('TOOL', `Using ${foundVsInfo.generator} generator.`) } + this.generator = foundVsInfo.generator + + const isAboveVS16 = foundVsInfo.versionMajor >= 16 + + // The CMake Visual Studio Generator does not support the Win64 or ARM suffix on + // the generator name. Instead the generator platform must be set explicitly via + // the platform parameter + if (!this.platform && isAboveVS16) { + switch (this.targetOptions.arch) { + case 'ia32': + case 'x86': + this.platform = 'Win32' + break + case 'x64': + this.platform = 'x64' + break + case 'arm': + this.platform = 'ARM' + break + case 'arm64': + this.platform = 'ARM64' + break + default: + this.log.warn('TOOL', 'Unknown NodeJS architecture: ' + this.targetOptions.arch) + break + } + } + } else { + throw new Error('There is no Visual C++ compiler installed. Install Visual C++ Build Toolset or Visual Studio.') } } else { - throw new Error('There is no Visual C++ compiler installed. Install Visual C++ Build Toolset or Visual Studio.') - } - } else { - // if it's already set because of options... - if (install) { - this.log.info('TOOL', 'Using ' + this.options.generator + ' generator, as specified from commandline.') + // if it's already set because of options... + if (install) { + this.log.info('TOOL', 'Using ' + this.options.generator + ' generator, as specified from commandline.') + } } - } - this.linkerFlags.push('/DELAYLOAD:NODE.EXE') + this.linkerFlags.push('/DELAYLOAD:NODE.EXE') - if (this.targetOptions.isX86) { - if (install) { - this.log.verbose('TOOL', 'Setting SAFESEH:NO linker flag.') + if (this.targetOptions.isX86) { + if (install) { + this.log.verbose('TOOL', 'Setting SAFESEH:NO linker flag.') + } + this.linkerFlags.push('/SAFESEH:NO') } - this.linkerFlags.push('/SAFESEH:NO') } -} + async _getTopSupportedVisualStudioGenerator() { + const CMake = require('./cMake') + assert(environment.isWin) -Toolset.prototype._getTopSupportedVisualStudioGenerator = async function () { - const CMake = require('./cMake') - assert(environment.isWin) + const selectedVs = await findVisualStudio(environment.runtimeVersion, this.options.msvsVersion) + if (!selectedVs) return null - const selectedVs = await findVisualStudio(environment.runtimeVersion, this.options.msvsVersion) - if (!selectedVs) return null + const list = await CMake.getGenerators(this.options, this.log) + for (const gen of list) { + const found = gen.startsWith(`Visual Studio ${selectedVs.versionMajor}`) + if (!found) { + continue + } - const list = await CMake.getGenerators(this.options, this.log) - for (const gen of list) { - const found = gen.startsWith(`Visual Studio ${selectedVs.versionMajor}`) - if (!found) { - continue - } + // unlike previous versions "Visual Studio 16 2019" and onwards don't end with arch name + const isAboveVS16 = selectedVs.versionMajor >= 16 + if (!isAboveVS16) { + const is64Bit = gen.endsWith('Win64') + if ((this.targetOptions.isX86 && is64Bit) || (this.targetOptions.isX64 && !is64Bit)) { + continue + } + } - // unlike previous versions "Visual Studio 16 2019" and onwards don't end with arch name - const isAboveVS16 = selectedVs.versionMajor >= 16 - if (!isAboveVS16) { - const is64Bit = gen.endsWith('Win64') - if ((this.targetOptions.isX86 && is64Bit) || (this.targetOptions.isX64 && !is64Bit)) { - continue + return { + ...selectedVs, + generator: gen, } } - return { - ...selectedVs, - generator: gen, - } + // Nothing matched + return null } - - // Nothing matched - return null } module.exports = Toolset From 7d24eabd915c3cfa10fcd53a8a208f5011875078 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 18 Jan 2024 20:21:42 +0000 Subject: [PATCH 36/50] chore: tidy --- lib/dist.js | 3 ++- lib/environment.js | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/dist.js b/lib/dist.js index 8fc067d2..6bfbd320 100644 --- a/lib/dist.js +++ b/lib/dist.js @@ -7,6 +7,7 @@ const CMLog = require('./cmLog') const TargetOptions = require('./targetOptions') const runtimePaths = require('./runtimePaths') const Downloader = require('./downloader') +const os = require('os') function testSum(sums, sum, fPath) { const serverSum = sums.find(function (s) { @@ -26,7 +27,7 @@ class Dist { return ( this.options.runtimeDirectory || - path.join(environment.home, cacheDirectory, runtimeArchDirectory, runtimeVersionDirectory) + path.join(os.homedir(), cacheDirectory, runtimeArchDirectory, runtimeVersionDirectory) ) } get externalPath() { diff --git a/lib/environment.js b/lib/environment.js index efb96572..0cc7402b 100644 --- a/lib/environment.js +++ b/lib/environment.js @@ -14,16 +14,9 @@ const environment = (module.exports = { isArm: os.arch() === 'arm', runtime: 'node', runtimeVersion: process.versions.node, - home: process.env[os.platform() === 'win32' ? 'USERPROFILE' : 'HOME'], - EOL: os.EOL, }) Object.defineProperties(environment, { - isPosix: { - get: function () { - return !this.isWin - }, - }, _isNinjaAvailable: { value: null, writable: true, From aa657314ac012de7e6e2613d828617c6c334f46a Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 22 Jan 2024 01:02:58 +0000 Subject: [PATCH 37/50] fix: remove dependency on `lodash.isplainobject` --- lib/appCMakeJSConfig.js | 3 +-- lib/buildSystem.js | 20 +++++++++----------- lib/runtimePaths.js | 8 +++++--- package.json | 1 - 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/appCMakeJSConfig.js b/lib/appCMakeJSConfig.js index ed8621dd..bee96119 100644 --- a/lib/appCMakeJSConfig.js +++ b/lib/appCMakeJSConfig.js @@ -1,6 +1,5 @@ 'use strict' const path = require('path') -const isPlainObject = require('lodash.isplainobject') function getConfig(lookPath, log) { const pjsonPath = path.join(lookPath, 'package.json') @@ -8,7 +7,7 @@ function getConfig(lookPath, log) { try { const json = require(pjsonPath) log.silly('CFG', 'Loaded:\n' + JSON.stringify(json)) - if (isPlainObject(json) && isPlainObject(json['cmake-js'])) { + if (json && json['cmake-js'] && typeof json['cmake-js'] === 'object') { log.silly('CFG', 'Config found.') return json['cmake-js'] } else { diff --git a/lib/buildSystem.js b/lib/buildSystem.js index 2f7c14af..63c93842 100644 --- a/lib/buildSystem.js +++ b/lib/buildSystem.js @@ -5,7 +5,6 @@ const CMLog = require('./cmLog') const appCMakeJSConfig = require('./appCMakeJSConfig') const npmConfig = require('./npmConfig') const path = require('path') -const isPlainObject = require('lodash.isplainobject') const Toolset = require('./toolset') function isNodeApi(log, projectRoot) { @@ -29,20 +28,19 @@ class BuildSystem { const appConfig = appCMakeJSConfig(this.options.directory, this.log) const npmOptions = npmConfig(this.log) - if (isPlainObject(npmOptions) && Object.keys(npmOptions).length) { + if (npmOptions && typeof npmOptions === 'object' && Object.keys(npmOptions).length) { this.options.runtimeDirectory = npmOptions['nodedir'] this.options.msvsVersion = npmOptions['msvs_version'] } - if (isPlainObject(appConfig)) { - if (Object.keys(appConfig).length) { - this.log.verbose('CFG', 'Applying CMake.js config from root package.json:') - this.log.verbose('CFG', JSON.stringify(appConfig)) - // Applying applications's config, if there is no explicit runtime related options specified - this.options.runtime = this.options.runtime || appConfig.runtime - this.options.runtimeVersion = this.options.runtimeVersion || appConfig.runtimeVersion - this.options.arch = this.options.arch || appConfig.arch - } + if (appConfig && typeof appConfig === 'object' && Object.keys(appConfig).length) { + this.log.verbose('CFG', 'Applying CMake.js config from root package.json:') + this.log.verbose('CFG', JSON.stringify(appConfig)) + // Applying applications's config, if there is no explicit runtime related options specified + this.options.runtime = this.options.runtime || appConfig.runtime + this.options.runtimeVersion = this.options.runtimeVersion || appConfig.runtimeVersion + this.options.arch = this.options.arch || appConfig.arch } + this.log.verbose('CFG', 'Build system options:') this.log.verbose('CFG', JSON.stringify(this.options)) this.cmake = new CMake(this.options) diff --git a/lib/runtimePaths.js b/lib/runtimePaths.js index 24d030ef..c513025c 100644 --- a/lib/runtimePaths.js +++ b/lib/runtimePaths.js @@ -1,7 +1,6 @@ 'use strict' const assert = require('assert') const semver = require('semver') -const isPlainObject = require('lodash.isplainobject') const NODE_MIRROR = process.env.NVM_NODEJS_ORG_MIRROR || 'https://nodejs.org/dist' const ELECTRON_MIRROR = process.env.ELECTRON_MIRROR || 'https://artifacts.electronjs.org/headers/dist' @@ -83,8 +82,11 @@ const runtimePaths = { const runtime = targetOptions.runtime const func = runtimePaths[runtime] let paths - if (typeof func === 'function' && isPlainObject((paths = func(targetOptions)))) { - return paths + if (typeof func === 'function') { + paths = func(targetOptions) + if (paths && typeof 'paths' === 'object') { + return paths + } } throw new Error('Unknown runtime: ' + runtime) }, diff --git a/package.json b/package.json index c1ab3230..6b9aae0e 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "axios": "^1.6.5", "debug": "^4", "fs-extra": "^11.2.0", - "lodash.isplainobject": "^4.0.6", "memory-stream": "^1.0.0", "node-api-headers": "^1.1.0", "npmlog": "^6.0.2", From 3922fb87993685ab49992343ae689f2f740d0b85 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 22 Jan 2024 21:47:50 +0000 Subject: [PATCH 38/50] chore: fix typo --- lib/runtimePaths.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runtimePaths.js b/lib/runtimePaths.js index c513025c..c9680da8 100644 --- a/lib/runtimePaths.js +++ b/lib/runtimePaths.js @@ -84,7 +84,7 @@ const runtimePaths = { let paths if (typeof func === 'function') { paths = func(targetOptions) - if (paths && typeof 'paths' === 'object') { + if (paths && typeof paths === 'object') { return paths } } From 91ed447bfa4a7f97fefd04840d8046f71c61f40d Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Wed, 16 Apr 2025 20:48:47 +0100 Subject: [PATCH 39/50] chore: ci update --- .github/dependabot.yml | 13 +++++++++++++ .github/workflows/node.yaml | 32 +++++++++++++++++--------------- 2 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..c880f4dc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'weekly' + + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'weekly' + # Disable version updates for npm dependencies (we only want security updates) + open-pull-requests-limit: 0 diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 5e19bc6f..81fd69a7 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -14,29 +14,31 @@ jobs: matrix: include: # windows - - os: windows-2019 - arch: x64 - os: windows-2022 arch: x64 - - os: windows-2019 + - os: windows-2025 + arch: x64 + - os: windows-2022 arch: x86 + - os: windows-11-arm + arch: arm64 + node-version: 20.x # macos - - os: macos-12 - arch: x64 - # - os: macos-11 - # arch: arm64 - - os: macos-11 + - os: macos-13 arch: x64 + - os: macos-14 + arch: arm64 + node-version: 16.x # linux - os: ubuntu-22.04 arch: x64 - - os: ubuntu-20.04 + - os: ubuntu-24.04 arch: x64 - # linux-libc - - os: ubuntu-latest + - os: ubuntu-22.04-arm arch: arm64 - docker-arch: linux/arm64 - docker-image: node:14-bullseye + - os: ubuntu-24.04-arm + arch: arm64 + # linux-libc - os: ubuntu-latest arch: arm docker-arch: linux/arm/v7 @@ -51,12 +53,12 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Use Node.js 14.x + - name: Use Node.js if: ${{ !matrix.docker-arch }} uses: actions/setup-node@v3 with: architecture: ${{ matrix.arch }} - node-version: 14.x + node-version: ${{ matrix.node-version || '14.x' }} - name: run tests if: ${{ !matrix.docker-arch }} From 2130796688439993760cd8cb23223af0a558b002 Mon Sep 17 00:00:00 2001 From: jaycex Date: Mon, 22 Jan 2024 18:10:50 +0800 Subject: [PATCH 40/50] fix(windows): download correct node.lib for arm64 arch on windows --- lib/environment.js | 1 + lib/runtimePaths.js | 4 ++-- lib/targetOptions.js | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/environment.js b/lib/environment.js index 0cc7402b..401b0bac 100644 --- a/lib/environment.js +++ b/lib/environment.js @@ -12,6 +12,7 @@ const environment = (module.exports = { isX86: os.arch() === 'ia32' || os.arch() === 'x86', isX64: os.arch() === 'x64', isArm: os.arch() === 'arm', + isArm64: os.arch() === 'arm64', runtime: 'node', runtimeVersion: process.versions.node, }) diff --git a/lib/runtimePaths.js b/lib/runtimePaths.js index c9680da8..73c92f26 100644 --- a/lib/runtimePaths.js +++ b/lib/runtimePaths.js @@ -24,7 +24,7 @@ const runtimePaths = { externalPath: NODE_MIRROR + '/v' + targetOptions.runtimeVersion + '/', winLibs: [ { - dir: targetOptions.isX64 ? 'win-x64' : 'win-x86', + dir: targetOptions.isArm64 ? 'arm64' : targetOptions.isX64 ? 'win-x64' : 'win-x86', name: targetOptions.runtime + '.lib', }, ], @@ -68,7 +68,7 @@ const runtimePaths = { externalPath: ELECTRON_MIRROR + '/v' + targetOptions.runtimeVersion + '/', winLibs: [ { - dir: targetOptions.isX64 ? 'x64' : '', + dir: targetOptions.isArm64 ? 'arm64' : targetOptions.isX64 ? 'x64' : '', name: 'node.lib', }, ], diff --git a/lib/targetOptions.js b/lib/targetOptions.js index c61bf6fe..70c3d135 100644 --- a/lib/targetOptions.js +++ b/lib/targetOptions.js @@ -15,6 +15,9 @@ class TargetOptions { get isArm() { return this.arch === 'arm' } + get isArm64() { + return this.arch === 'arm64' + } get runtime() { return this.options.runtime || environment.runtime } From c7dac5a02861c157b7415d4d2d9fee0f651ca429 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Wed, 16 Apr 2025 21:03:49 +0100 Subject: [PATCH 41/50] fix: update find-visualstudio script --- lib/import/find-visualstudio.js | 193 ++++++++++++++++++++++++++++---- 1 file changed, 173 insertions(+), 20 deletions(-) diff --git a/lib/import/find-visualstudio.js b/lib/import/find-visualstudio.js index 4244c7e4..d162c2f0 100644 --- a/lib/import/find-visualstudio.js +++ b/lib/import/find-visualstudio.js @@ -54,7 +54,12 @@ class VisualStudioFinder { } const checks = [ - () => this.findVisualStudio2017OrNewer(), + () => this.findVisualStudio2019OrNewerFromSpecifiedLocation(), + () => this.findVisualStudio2019OrNewerUsingSetupModule(), + () => this.findVisualStudio2019OrNewer(), + () => this.findVisualStudio2017FromSpecifiedLocation(), + () => this.findVisualStudio2017UsingSetupModule(), + () => this.findVisualStudio2017(), () => this.findVisualStudio2015(), () => this.findVisualStudio2013(), ] @@ -113,9 +118,121 @@ class VisualStudioFinder { throw new Error('Could not find any Visual Studio installation to use') } + async findVisualStudio2019OrNewerFromSpecifiedLocation() { + return this.findVSFromSpecifiedLocation([2019, 2022]) + } + + async findVisualStudio2017FromSpecifiedLocation() { + if (semver.gte(this.nodeSemver, '22.0.0')) { + this.addLog('not looking for VS2017 as it is only supported up to Node.js 21') + return null + } + return this.findVSFromSpecifiedLocation([2017]) + } + + async findVSFromSpecifiedLocation(supportedYears) { + if (!this.envVcInstallDir) { + return null + } + const info = { + path: path.resolve(this.envVcInstallDir), + // Assume the version specified by the user is correct. + // Since Visual Studio 2015, the Developer Command Prompt sets the + // VSCMD_VER environment variable which contains the version information + // for Visual Studio. + // https://learn.microsoft.com/en-us/visualstudio/ide/reference/command-prompt-powershell?view=vs-2022 + version: process.env.VSCMD_VER, + packages: [ + 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + 'Microsoft.VisualStudio.Component.VC.Tools.ARM64', + // Assume MSBuild exists. It will be checked in processing. + 'Microsoft.VisualStudio.VC.MSBuild.Base', + ], + } + + // Is there a better way to get SDK information? + const envWindowsSDKVersion = process.env.WindowsSDKVersion + const sdkVersionMatched = envWindowsSDKVersion?.match(/^(\d+)\.(\d+)\.(\d+)\..*/) + if (sdkVersionMatched) { + info.packages.push(`Microsoft.VisualStudio.Component.Windows10SDK.${sdkVersionMatched[3]}.Desktop`) + } + // pass for further processing + return this.processData([info], supportedYears) + } + + async findVisualStudio2019OrNewerUsingSetupModule() { + return this.findNewVSUsingSetupModule([2019, 2022]) + } + + async findVisualStudio2017UsingSetupModule() { + if (semver.gte(this.nodeSemver, '22.0.0')) { + this.addLog('not looking for VS2017 as it is only supported up to Node.js 21') + return null + } + return this.findNewVSUsingSetupModule([2017]) + } + + async findNewVSUsingSetupModule(supportedYears) { + const ps = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe') + const vcInstallDir = this.envVcInstallDir + + const checkModuleArgs = [ + '-NoProfile', + '-Command', + '&{@(Get-Module -ListAvailable -Name VSSetup).Version.ToString()}', + ] + this.log.silly('Running', ps, checkModuleArgs) + const [cErr] = await this.execFile(ps, checkModuleArgs) + if (cErr) { + this.addLog( + 'VSSetup module doesn\'t seem to exist. You can install it via: "Install-Module VSSetup -Scope CurrentUser"', + ) + this.log.silly('VSSetup error = %j', cErr && (cErr.stack || cErr)) + return null + } + const filterArg = vcInstallDir !== undefined ? `| where {$_.InstallationPath -eq '${vcInstallDir}' }` : '' + const psArgs = ['-NoProfile', '-Command', `&{Get-VSSetupInstance ${filterArg} | ConvertTo-Json -Depth 3}`] + + this.log.silly('Running', ps, psArgs) + const [err, stdout, stderr] = await this.execFile(ps, psArgs) + let parsedData = this.parseData(err, stdout, stderr) + if (parsedData === null) { + return null + } + this.log.silly('Parsed data', parsedData) + if (!Array.isArray(parsedData)) { + // if there are only 1 result, then Powershell will output non-array + parsedData = [parsedData] + } + // normalize output + parsedData = parsedData.map((info) => { + info.path = info.InstallationPath + info.version = `${info.InstallationVersion.Major}.${info.InstallationVersion.Minor}.${info.InstallationVersion.Build}.${info.InstallationVersion.Revision}` + info.packages = info.Packages.map((p) => p.Id) + return info + }) + // pass for further processing + return this.processData(parsedData, supportedYears) + } + + // Invoke the PowerShell script to get information about Visual Studio 2019 + // or newer installations + async findVisualStudio2019OrNewer() { + return this.findNewVS([2019, 2022]) + } + + // Invoke the PowerShell script to get information about Visual Studio 2017 + async findVisualStudio2017() { + if (semver.gte(this.nodeSemver, '22.0.0')) { + this.addLog('not looking for VS2017 as it is only supported up to Node.js 21') + return null + } + return this.findNewVS([2017]) + } + // Invoke the PowerShell script to get information about Visual Studio 2017 // or newer installations - async findVisualStudio2017OrNewer() { + async findNewVS(supportedYears) { const ps = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe') const csFile = path.join(__dirname, 'Find-VisualStudio.cs') const psArgs = [ @@ -127,25 +244,36 @@ class VisualStudioFinder { ] this.log.silly('Running', ps, psArgs) - const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' }) - return this.parseData(err, stdout, stderr) + const [err, stdout, stderr] = await this.execFile(ps, psArgs) + const parsedData = this.parseData(err, stdout, stderr, { checkIsArray: true }) + if (parsedData === null) { + return null + } + return this.processData(parsedData, supportedYears) } - // Parse the output of the PowerShell script and look for an installation - // of Visual Studio 2017 or newer to use - parseData(err, stdout, stderr) { + // Parse the output of the PowerShell script, make sanity checks + parseData(err, stdout, stderr, sanityCheckOptions) { + const defaultOptions = { + checkIsArray: false, + } + + // Merging provided options with the default options + const sanityOptions = { ...defaultOptions, ...sanityCheckOptions } + this.log.silly('PS stderr = %j', stderr) - const failPowershell = () => { + const failPowershell = (failureDetails) => { this.addLog( - "could not use PowerShell to find Visual Studio 2017 or newer, try re-running with '--loglevel silly' for more details", + `could not use PowerShell to find Visual Studio 2017 or newer, try re-running with '--loglevel silly' for more details. \n + Failure details: ${failureDetails}`, ) return null } if (err) { this.log.silly('PS err = %j', err && (err.stack || err)) - return failPowershell() + return failPowershell(`${err}`.substring(0, 40)) } let vsInfo @@ -157,11 +285,16 @@ class VisualStudioFinder { return failPowershell() } - if (!Array.isArray(vsInfo)) { + if (sanityOptions.checkIsArray && !Array.isArray(vsInfo)) { this.log.silly('PS stdout = %j', stdout) - return failPowershell() + return failPowershell('Expected array as output of the PS script') } + return vsInfo + } + // Process parsed data containing information about VS installations + // Look for the required parts, extract and output them back + processData(vsInfo, supportedYears) { vsInfo = vsInfo.map((info) => { this.log.silly(`processing installation: "${info.path}"`) info.path = path.resolve(info.path) @@ -175,11 +308,12 @@ class VisualStudioFinder { this.log.silly('vsInfo:', vsInfo) // Remove future versions or errors parsing version number + // Also remove any unsupported versions vsInfo = vsInfo.filter((info) => { - if (info.versionYear) { + if (info.versionYear && supportedYears.indexOf(info.versionYear) !== -1) { return true } - this.addLog(`unknown version "${info.version}" found at "${info.path}"`) + this.addLog(`${info.versionYear ? 'unsupported' : 'unknown'} version "${info.version}" found at "${info.path}"`) return false }) @@ -224,7 +358,7 @@ class VisualStudioFinder { // Helper - process version information getVersionInfo(info) { - const match = /^(\d+)\.(\d+)\..*/.exec(info.version) + const match = /^(\d+)\.(\d+)(?:\..*)?/.exec(info.version) if (!match) { this.log.silly('- failed to parse version:', info.version) return {} @@ -266,7 +400,11 @@ class VisualStudioFinder { return path.join(info.path, 'MSBuild', '15.0', 'Bin', 'MSBuild.exe') } if (versionYear === 2019) { - return msbuildPath + if (process.arch === 'arm64' && this.msBuildPathExists(msbuildPathArm64)) { + return msbuildPathArm64 + } else { + return msbuildPath + } } } /** @@ -284,12 +422,23 @@ class VisualStudioFinder { // Helper - process toolset information getToolset(info, versionYear) { - const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' + const vcToolsArm64 = 'VC.Tools.ARM64' + const pkgArm64 = `Microsoft.VisualStudio.Component.${vcToolsArm64}` + const vcToolsX64 = 'VC.Tools.x86.x64' + const pkgX64 = `Microsoft.VisualStudio.Component.${vcToolsX64}` const express = 'Microsoft.VisualStudio.WDExpress' - if (info.packages.indexOf(pkg) !== -1) { - this.log.silly('- found VC.Tools.x86.x64') - } else if (info.packages.indexOf(express) !== -1) { + if (process.arch === 'arm64' && info.packages.includes(pkgArm64)) { + this.log.silly(`- found ${vcToolsArm64}`) + } else if (info.packages.includes(pkgX64)) { + if (process.arch === 'arm64') { + this.addLog( + `- found ${vcToolsX64} on ARM64 platform. Expect less performance and/or link failure with ARM64 binary.`, + ) + } else { + this.log.silly(`- found ${vcToolsX64}`) + } + } else if (info.packages.includes(express)) { this.log.silly('- found Visual Studio Express (looking for toolset)') } else { return null @@ -434,6 +583,10 @@ class VisualStudioFinder { return true } + + async execFile(exec, args) { + return await execFile(exec, args, { encoding: 'utf8' }) + } } module.exports = VisualStudioFinder From 7d3182e7000dd45fef495749be66e2dc614984e5 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 17 Apr 2025 20:24:01 +0100 Subject: [PATCH 42/50] fix: win-arm64 bug add tests for more runtime versions, and include win-arm64 --- lib/runtimePaths.js | 2 +- package.json | 2 +- tests/es6/prototype-napi/CMakeLists.txt | 8 +++++ tests/es6/prototype/CMakeLists.txt | 8 +++++ tests/es6/prototype2/CMakeLists.txt | 10 ++++++- tests/es6/testRunner.js | 40 ++++++++++++++++++------- 6 files changed, 57 insertions(+), 13 deletions(-) diff --git a/lib/runtimePaths.js b/lib/runtimePaths.js index 73c92f26..4fc9f177 100644 --- a/lib/runtimePaths.js +++ b/lib/runtimePaths.js @@ -24,7 +24,7 @@ const runtimePaths = { externalPath: NODE_MIRROR + '/v' + targetOptions.runtimeVersion + '/', winLibs: [ { - dir: targetOptions.isArm64 ? 'arm64' : targetOptions.isX64 ? 'win-x64' : 'win-x86', + dir: targetOptions.isArm64 ? 'win-arm64' : targetOptions.isX64 ? 'win-x64' : 'win-x86', name: targetOptions.runtime + '.lib', }, ], diff --git a/package.json b/package.json index 6b9aae0e..bed2c5a5 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "mocha": "*", - "nan": "^2.18.0", + "nan": "^2.22.2", "node-addon-api": "^6.1.0", "prettier": "^3.2.2" }, diff --git a/tests/es6/prototype-napi/CMakeLists.txt b/tests/es6/prototype-napi/CMakeLists.txt index 53320e33..4579529f 100644 --- a/tests/es6/prototype-napi/CMakeLists.txt +++ b/tests/es6/prototype-napi/CMakeLists.txt @@ -20,3 +20,11 @@ if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) # Generate node.lib execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) endif() + +if (NODE_RUNTIME STREQUAL "node" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 20) + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) +elseif (NODE_RUNTIME STREQUAL "electron" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 32) + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) + elseif (NODE_RUNTIME STREQUAL "electron" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 29) + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) +endif() diff --git a/tests/es6/prototype/CMakeLists.txt b/tests/es6/prototype/CMakeLists.txt index 0a0e9ab3..21c78254 100644 --- a/tests/es6/prototype/CMakeLists.txt +++ b/tests/es6/prototype/CMakeLists.txt @@ -15,3 +15,11 @@ add_library(addon SHARED src/addon.cpp) set_target_properties(addon PROPERTIES PREFIX "" SUFFIX ".node") target_link_libraries(addon ${CMAKE_JS_LIB}) + +if (NODE_RUNTIME STREQUAL "node" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 20) + set_property(TARGET addon PROPERTY CXX_STANDARD 17) +elseif (NODE_RUNTIME STREQUAL "electron" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 32) + set_property(TARGET addon PROPERTY CXX_STANDARD 20) +elseif (NODE_RUNTIME STREQUAL "electron" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 29) + set_property(TARGET addon PROPERTY CXX_STANDARD 17) +endif() \ No newline at end of file diff --git a/tests/es6/prototype2/CMakeLists.txt b/tests/es6/prototype2/CMakeLists.txt index aab7498a..0978135d 100644 --- a/tests/es6/prototype2/CMakeLists.txt +++ b/tests/es6/prototype2/CMakeLists.txt @@ -14,4 +14,12 @@ add_library(${PROJECT_NAME} SHARED src/addon.cpp) set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") -target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) \ No newline at end of file +target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) + +if (NODE_RUNTIME STREQUAL "node" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 20) + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) +elseif (NODE_RUNTIME STREQUAL "electron" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 32) + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) +elseif (NODE_RUNTIME STREQUAL "electron" AND NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL 29) + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) +endif() diff --git a/tests/es6/testRunner.js b/tests/es6/testRunner.js index 34edc6f1..16e8788c 100644 --- a/tests/es6/testRunner.js +++ b/tests/es6/testRunner.js @@ -7,24 +7,36 @@ const util = require('util') function* generateRuntimeOptions() { function* generateForNode(arch) { - // Old: - yield { - runtime: 'node', - runtimeVersion: '14.0.0', - arch: arch, + if (arch !== 'arm64') { + yield { + runtime: 'node', + runtimeVersion: '14.0.0', + arch: arch, + } + + yield { + runtime: 'node', + runtimeVersion: '16.0.0', + arch: arch, + } + + yield { + runtime: 'node', + runtimeVersion: '18.13.0', + arch: arch, + } } - // LTS: yield { runtime: 'node', - runtimeVersion: '16.0.0', + runtimeVersion: '20.19.0', arch: arch, } // Current: yield { runtime: 'node', - runtimeVersion: '18.13.0', + runtimeVersion: '22.9.0', arch: arch, } } @@ -39,23 +51,31 @@ function* generateRuntimeOptions() { } function* generateForElectron(arch) { - // Latest: yield { runtime: 'electron', runtimeVersion: '18.2.1', arch: arch, } + + yield { + runtime: 'electron', + runtimeVersion: '31.7.7', + arch: arch, + } } function* generateForArch(arch) { yield* generateForNode(arch) - yield* generateForNWJS(arch) + if (!environment.isWin || arch !== 'arm64') { + yield* generateForNWJS(arch) + } yield* generateForElectron(arch) } if (environment.isWin) { yield* generateForArch('x64') yield* generateForArch('ia32') + yield* generateForArch('arm64') } else { yield* generateForArch() } From 20d9958fdd0c157553784c7ae0bb58f9e8bb20bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 21:06:22 +0100 Subject: [PATCH 43/50] chore(deps): bump actions/setup-node from 3 to 4 (#346) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/node.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 81fd69a7..537a76c7 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -55,7 +55,7 @@ jobs: - name: Use Node.js if: ${{ !matrix.docker-arch }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: architecture: ${{ matrix.arch }} node-version: ${{ matrix.node-version || '14.x' }} From cb0bff8e099f1b3e8f773901489d3328b0f27542 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 21:06:32 +0100 Subject: [PATCH 44/50] chore(deps): bump actions/checkout from 3 to 4 (#344) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/node.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 537a76c7..b06a2af3 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -51,7 +51,7 @@ jobs: libc: musl steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js if: ${{ !matrix.docker-arch }} From c579a7181cac4b4af96871a9dd6f195b8d85a75d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 21:21:39 +0100 Subject: [PATCH 45/50] chore(deps): bump docker/setup-qemu-action from 2 to 3 (#345) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/node.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index b06a2af3..97055c2a 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -71,7 +71,7 @@ jobs: npm_config_build_from_source: true - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 if: matrix.docker-arch - name: run tests (in docker) uses: addnab/docker-run-action@v3 From eda3427601a647828ee79a48597b9b8c6c8a7df3 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Thu, 17 Apr 2025 23:53:00 +0300 Subject: [PATCH 46/50] chore: Modernize CMakeLists.txt example (#342) --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 01c0d48e..f4e4609f 100644 --- a/README.md +++ b/README.md @@ -106,21 +106,18 @@ In a nutshell. _(For more complete documentation please see [the first tutorial] - Put a CMakeLists.txt file into your module root with this minimal required content: ```cmake -cmake_minimum_required(VERSION 3.15) -cmake_policy(SET CMP0091 NEW) -cmake_policy(SET CMP0042 NEW) +cmake_minimum_required(VERSION 3.15...3.31) +project(your-addon-name-here) -project (your-addon-name-here) - -add_definitions(-DNAPI_VERSION=4) - -include_directories(${CMAKE_JS_INC}) +add_compile_definitions(-DNAPI_VERSION=4) file(GLOB SOURCE_FILES "your-source files-location-here") add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") -target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_JS_INC}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_JS_LIB}) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) # Generate node.lib From 0200582a5aad5d5b4d50e9f4caa6a4dd48fb0556 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 17 Apr 2025 21:54:43 +0100 Subject: [PATCH 47/50] chore: update package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index bed2c5a5..958efe6f 100644 --- a/package.json +++ b/package.json @@ -72,5 +72,6 @@ "*.md", "bindings.js", "bindings.d.ts" - ] + ], + "packageManager": "yarn@1.22.22+sha256.c17d3797fb9a9115bf375e31bfd30058cac6bc9c3b8807a3d8cb2094794b51ca" } From 2f9f437d970882adb812f1aa54442082e45e537f Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 17 Apr 2025 21:56:26 +0100 Subject: [PATCH 48/50] v7.3.1 --- changelog.md | 7 ++++++- package.json | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 04b55332..0675981d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,9 @@ -# v7.3.0 - 15/01/23 +# v7.3.1 - 17/04/25 + +- fix(windows): support windows arm64 (Thanks to @jaycex) +- fix(windows): support newer visual studio installations + +# v7.3.0 - 15/01/24 - feat(windows): replace custom libnode.def generation with version from node-api-headers - fix: support for vs2015 with nodejs 18 and older (#317) diff --git a/package.json b/package.json index 958efe6f..aa131376 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node-addon-api" ], "main": "lib", - "version": "7.3.0", + "version": "7.3.1", "author": "Gábor Mező aka unbornchikken", "maintainers": [ { From 55ec5cde3e8d7e6c4bde61426ae05f4f95b5f763 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 21 Apr 2025 12:07:25 +0100 Subject: [PATCH 49/50] chore: remove outdated tutorials from readme --- README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index f4e4609f..a4d42720 100644 --- a/README.md +++ b/README.md @@ -100,8 +100,6 @@ Options: It is advised to use Node-API for new projects instead of NAN. It provides ABI stability making usage simpler and reducing maintainance. -In a nutshell. _(For more complete documentation please see [the first tutorial](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-01-Creating-a-native-module-by-using-CMake.js-and-NAN).)_ - - Install cmake-js for your module `npm install --save cmake-js` - Put a CMakeLists.txt file into your module root with this minimal required content: @@ -346,7 +344,7 @@ To make compatible your NW.js application with any NAN CMake.js based modules, w } ``` -That's it. There is nothing else to do either on the application's or on the module's side, CMake.js modules are compatible with NW.js out-of-the-box. For more complete documentation please see [the third tutorial](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-03-Using-CMake.js-based-native-modules-with-nw.js). +That's it. There is nothing else to do either on the application's or on the module's side, CMake.js modules are compatible with NW.js out-of-the-box. #### Heroku @@ -373,13 +371,6 @@ The least "painful" way of addressing this is to use heroku's multipack facility The `heroku-buildpack-multi` will run each buildpack in order allowing the node application to reference CMake in the Heroku build environment. -## Tutorials - -- [TUTORIAL 01 Creating a native module by using CMake.js and NAN](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-01-Creating-a-native-module-by-using-CMake.js-and-NAN) -- [TUTORIAL 02 Creating CMake.js based native addons with Qt Creator](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-02-Creating-CMake.js-based-native-addons-with-QT-Creator) -- [TUTORIAL 03 Using CMake.js based native modules with NW.js](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-03-Using-CMake.js-based-native-modules-with-nw.js) -- [TUTORIAL 04 Creating CMake.js based native modules with Boost dependency](https://github.com/unbornchikken/cmake-js/wiki/TUTORIAL-04-Creating-CMake.js-based-native-modules-with-Boost-dependency) - ## Real examples - [@julusian/jpeg-turbo](https://github.com/julusian/node-jpeg-turbo) - A Node-API wrapping around libjpeg-turbo. cmake-js was a good fit here, as libjpeg-turbo provides cmake files that can be used, and would be hard to replicate correctly in node-gyp From f257177211ab685ef98092f6b4ba3c01e8e9da64 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 21 Apr 2025 12:23:31 +0100 Subject: [PATCH 50/50] chore: add readme section about using libraries --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index a4d42720..b7d49524 100644 --- a/README.md +++ b/README.md @@ -371,6 +371,19 @@ The least "painful" way of addressing this is to use heroku's multipack facility The `heroku-buildpack-multi` will run each buildpack in order allowing the node application to reference CMake in the Heroku build environment. +## Using external C/C++ libraries + +Because you are using CMake, there are many ways to load libraries in your CMakeLists.txt. +Various places on the internet and in the CMake docs will suggest various approaches you can take. Common ones are: +* [conan](https://vcpkg.io/) (This may not work properly currently, we hope to improve support in a future release) +* [vcpkg](https://conan.io/) (This may not work properly currently, we hope to improve support in a future release) +* [hunter](https://github.com/cpp-pm/hunter) +* [CMake ExternalProject](https://cmake.org/cmake/help/latest/module/ExternalProject.html) +* If on linux, using system libraries from the system package-manager +* Importing as a git submodule + +We aim to be agnostic about how to use CMake, so it should be possible to use whatever approach you desire. + ## Real examples - [@julusian/jpeg-turbo](https://github.com/julusian/node-jpeg-turbo) - A Node-API wrapping around libjpeg-turbo. cmake-js was a good fit here, as libjpeg-turbo provides cmake files that can be used, and would be hard to replicate correctly in node-gyp