+54
-96
@@ -1,7 +0,11 @@ | ||
| 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) | ||
| - fix(windows): always remove Path if PATH is also defined (#319) | ||
| - fix(windows): always remove Path if PATH is also defined (#319) | ||
| - fix: Cmake arguments got converted to numbers (#314) | ||
@@ -11,9 +15,7 @@ - fix: update node-api-headers | ||
| v7.2.1 - 14/02/23 | ||
| ========== | ||
| # v7.2.1 - 14/02/23 | ||
| - fix: support Windows11SDK | ||
| v7.2.0 - 12/02/23 | ||
| ========== | ||
| # v7.2.0 - 12/02/23 | ||
@@ -24,9 +26,7 @@ - fix: `-DCMAKE_JS_VERSION=undefined` (#298) | ||
| v7.1.1 - 15/12/22 | ||
| ========== | ||
| # v7.1.1 - 15/12/22 | ||
| - fix build errors on windows | ||
| v7.1.0 - 14/12/22 | ||
| ========== | ||
| # v7.1.0 - 14/12/22 | ||
@@ -37,4 +37,3 @@ - add commands for retrieving cmake-js include and lib directories | ||
| v7.0.0 - 08/10/22 | ||
| ========== | ||
| # v7.0.0 - 08/10/22 | ||
@@ -48,4 +47,3 @@ - update dependencies | ||
| v6.3.1 - 05/06/22 | ||
| ========== | ||
| # v6.3.1 - 05/06/22 | ||
@@ -56,4 +54,3 @@ - add missing bluebird dependency | ||
| v6.3.0 - 26/11/21 | ||
| ========== | ||
| # v6.3.0 - 26/11/21 | ||
@@ -65,19 +62,15 @@ - add offline mode: https://github.com/cmake-js/cmake-js/pull/260 | ||
| v6.2.1 - 20/07/21 | ||
| ========== | ||
| # v6.2.1 - 20/07/21 | ||
| - EOL hotfix (Thx Windows!) | ||
| v6.2.0 - 19/07/21 | ||
| ========== | ||
| # v6.2.0 - 19/07/21 | ||
| - various fixes | ||
| v6.1.0 - 27/02/20 | ||
| ========== | ||
| # 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 | ||
| ================= | ||
| # v6.0.0 - 30/09/19 | ||
@@ -87,39 +80,31 @@ - Dropped compatibility of old Node.js runtimes (<10.0.0) | ||
| v5.3.2 - 21/08/19 | ||
| ================= | ||
| # v5.3.2 - 21/08/19 | ||
| - Visual Studio detection fixes | ||
| v5.3.1 - 18/07/19 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # v5.0.1 - 24/01/19 | ||
| - Linux line ending hotfix (I hate Windows!) | ||
| v5.0.0 - 24/01/19 | ||
| ================= | ||
| # v5.0.0 - 24/01/19 | ||
@@ -130,9 +115,7 @@ - [semver major] Add case sensitive NPM config integration https://github.com/cmake-js/cmake-js/pull/151 | ||
| v4.0.1 - 03/10/18 | ||
| ================= | ||
| # v4.0.1 - 03/10/18 | ||
| - log argument hotfix https://github.com/cmake-js/cmake-js/pull/145 | ||
| v4.0.0 - 14/09/18 | ||
| ================= | ||
| # v4.0.0 - 14/09/18 | ||
@@ -144,19 +127,15 @@ BREAKING CHANGES: | ||
| v3.7.3 - 16/05/18 | ||
| ================= | ||
| # v3.7.3 - 16/05/18 | ||
| - npm config hotfix https://github.com/cmake-js/cmake-js/pull/123 | ||
| v3.7.2 - 16/05/18 | ||
| ================= | ||
| # v3.7.2 - 16/05/18 | ||
| - do not use, breaks ES5 compatibility | ||
| v3.7.1 - 07/05/18 | ||
| ================= | ||
| # v3.7.1 - 07/05/18 | ||
| - Linux line ending hotfix (wat) | ||
| v3.7.0 - 07/05/18 | ||
| ================= | ||
| # v3.7.0 - 07/05/18 | ||
@@ -168,4 +147,3 @@ - PR: replace unzip with unzipper https://github.com/cmake-js/cmake-js/pull/120 | ||
| v3.6.2 - 17/02/18 | ||
| ================= | ||
| # v3.6.2 - 17/02/18 | ||
@@ -175,34 +153,27 @@ - use https distribution download urls | ||
| v3.6.1 - 11/01/18 | ||
| ================= | ||
| # v3.6.1 - 11/01/18 | ||
| - Detect 2017 Windows Build Tools | ||
| v3.6.0 - 11/27/17 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # v3.3.0 - 09/02/16 | ||
@@ -212,19 +183,15 @@ - silent option (https://github.com/cmake-js/cmake-js/pull/54) | ||
| v3.2.3 - 08/17/16 | ||
| ================= | ||
| # v3.2.3 - 08/17/16 | ||
| - Line endings | ||
| v3.2.2 - 12/08/16 | ||
| ================= | ||
| # v3.2.2 - 12/08/16 | ||
| - Multi directory support for Windows/MSVC build | ||
| v3.2.1 - 25/04/16 | ||
| ================= | ||
| # v3.2.1 - 25/04/16 | ||
| - Linux line ending hotfix | ||
| v3.2.0 - 25/04/16 | ||
| ================= | ||
| # v3.2.0 - 25/04/16 | ||
@@ -235,19 +202,15 @@ - Added NW.js 0.13+ compatibility | ||
| v3.1.2 - 03/02/16 | ||
| ================= | ||
| # v3.1.2 - 03/02/16 | ||
| - Fixed cmake-js binary ES5 compatibility. | ||
| v3.1.1 - 03/02/16 | ||
| ================= | ||
| # v3.1.1 - 03/02/16 | ||
| - Fixed line endings | ||
| v3.1.0 - 03/02/16 | ||
| ================= | ||
| # v3.1.0 - 03/02/16 | ||
| - Custom CMake parameter support (https://github.com/gerhardberger) | ||
| v3.0.0 - 20/11/15 | ||
| ================= | ||
| # v3.0.0 - 20/11/15 | ||
@@ -258,14 +221,11 @@ - Visual C++ Build Tools support | ||
| v2.1.0 - 29/10/15 | ||
| ================= | ||
| # 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 | ||
| ================= | ||
| # v2.0.2 - 22/10/15 | ||
| - Fix: print-* commands report "undefined" | ||
| - Fix: print-\* commands report "undefined" | ||
| v2.0.0 - 17/10/15 | ||
| ================= | ||
| # v2.0.0 - 17/10/15 | ||
@@ -277,11 +237,9 @@ - Fix: distribution files only gets downloaded if needed (4.0.0+) | ||
| v1.1.1 - 06/10/15 | ||
| ================= | ||
| # v1.1.1 - 06/10/15 | ||
| - Hotfix for build NW.js correctly. | ||
| v1.1.0 - 05/10/15 | ||
| ================= | ||
| # v1.1.0 - 05/10/15 | ||
| - Node.js 4.0.0+ support | ||
| - Downloads the small, header only tarball for Node.js 4+ |
+53
-58
@@ -1,63 +0,58 @@ | ||
| "use strict"; | ||
| const path = require("path"); | ||
| const isPlainObject = require("lodash.isplainobject"); | ||
| 'use strict' | ||
| const path = require('path') | ||
| 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 (json && json['cmake-js'] && typeof json['cmake-js'] === 'object') { | ||
| 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 | ||
| } |
+116
-135
@@ -1,142 +0,123 @@ | ||
| "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 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); | ||
| 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); | ||
| if (npmOptions && typeof npmOptions === 'object' && Object.keys(npmOptions).length) { | ||
| this.options.runtimeDirectory = npmOptions['nodedir'] | ||
| this.options.msvsVersion = npmOptions['msvs_version'] | ||
| } | ||
| 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) | ||
| this.dist = new Dist(this.options) | ||
| this.toolset = new Toolset(this.options) | ||
| } | ||
| async _ensureInstalled() { | ||
| try { | ||
| await this.toolset.initialize(true) | ||
| if (!this.options.isNodeApi) { | ||
| await this.dist.ensureDownloaded() | ||
| } | ||
| } catch (e) { | ||
| this._showError(e) | ||
| throw e | ||
| } | ||
| } | ||
| _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) | ||
| } | ||
| } | ||
| install() { | ||
| return this._ensureInstalled() | ||
| } | ||
| 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._ensureInstalled = async function () { | ||
| 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); | ||
| } | ||
| }; | ||
| 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; | ||
| } | ||
| }; | ||
| 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; | ||
| module.exports = BuildSystem |
+329
-359
@@ -1,392 +0,362 @@ | ||
| "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; | ||
| } | ||
| class CMake { | ||
| get path() { | ||
| return this.options.cmakePath || 'cmake' | ||
| } | ||
| get isAvailable() { | ||
| if (this._isAvailable === null) { | ||
| this._isAvailable = CMake.isAvailable(this.options) | ||
| } | ||
| return this._isAvailable | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| }); | ||
| 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 { | ||
| 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)') | ||
| } | ||
| } | ||
| 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; | ||
| }; | ||
| // 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 | ||
| } | ||
| 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.", | ||
| ) | ||
| } | ||
| } | ||
| async getConfigureCommand() { | ||
| // Create command: | ||
| let command = [this.path, this.projectRoot, '--no-warn-unused-cli'] | ||
| 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)"); | ||
| } | ||
| } | ||
| const D = [] | ||
| // 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.js watermark | ||
| D.push({ CMAKE_JS_VERSION: environment.cmakeJsVersion }) | ||
| CMake.prototype.getGenerators = function () { | ||
| return CMake.getGenerators(this.options, this.log); | ||
| }; | ||
| // 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 }) | ||
| } | ||
| 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."); | ||
| } | ||
| }; | ||
| // 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$<$<CONFIG:Debug>:Debug>' }) | ||
| CMake.prototype.getConfigureCommand = async function () { | ||
| // Includes: | ||
| const includesString = await this.getCmakeJsIncludeString() | ||
| D.push({ CMAKE_JS_INC: includesString }) | ||
| // Create command: | ||
| let command = [this.path, this.projectRoot, "--no-warn-unused-cli"]; | ||
| // Sources: | ||
| const srcsString = this.getCmakeJsSrcString() | ||
| D.push({ CMAKE_JS_SRC: srcsString }) | ||
| const D = []; | ||
| // Runtime: | ||
| D.push({ NODE_RUNTIME: this.targetOptions.runtime }) | ||
| D.push({ NODE_RUNTIMEVERSION: this.targetOptions.runtimeVersion }) | ||
| D.push({ NODE_ARCH: this.targetOptions.arch }) | ||
| // CMake.js watermark | ||
| D.push({"CMAKE_JS_VERSION": environment.cmakeJsVersion}); | ||
| if (environment.isOSX) { | ||
| if (this.targetOptions.arch) { | ||
| let xcodeArch = this.targetOptions.arch | ||
| if (xcodeArch === 'x64') xcodeArch = 'x86_64' | ||
| D.push({ CMAKE_OSX_ARCHITECTURES: xcodeArch }) | ||
| } | ||
| } | ||
| // 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}); | ||
| } | ||
| // Custom options | ||
| for (const [key, value] of Object.entries(this.cMakeOptions)) { | ||
| D.push({ [key]: value }) | ||
| } | ||
| // 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$<$<CONFIG:Debug>:Debug>"}) | ||
| // Toolset: | ||
| await this.toolset.initialize(false) | ||
| // Includes: | ||
| const includesString = await this.getCmakeJsIncludeString(); | ||
| D.push({ "CMAKE_JS_INC": includesString }); | ||
| const libsString = this.getCmakeJsLibString() | ||
| D.push({ CMAKE_JS_LIB: libsString }) | ||
| // Sources: | ||
| const srcsString = this.getCmakeJsSrcString(); | ||
| D.push({ "CMAKE_JS_SRC": srcsString }); | ||
| 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 }) | ||
| } | ||
| } | ||
| // Runtime: | ||
| D.push({"NODE_RUNTIME": this.targetOptions.runtime}); | ||
| D.push({"NODE_RUNTIMEVERSION": this.targetOptions.runtimeVersion}); | ||
| D.push({"NODE_ARCH": this.targetOptions.arch}); | ||
| 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 (environment.isOSX) { | ||
| if (this.targetOptions.arch) { | ||
| let xcodeArch = this.targetOptions.arch | ||
| if (xcodeArch === 'x64') xcodeArch = 'x86_64' | ||
| D.push({CMAKE_OSX_ARCHITECTURES: xcodeArch}) | ||
| } | ||
| } | ||
| // 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 }) | ||
| } | ||
| } | ||
| } | ||
| // Custom options | ||
| for (const [key, value] of Object.entries(this.cMakeOptions)) { | ||
| D.push({ [key]: value }); | ||
| } | ||
| command = command.concat( | ||
| D.map(function (p) { | ||
| return '-D' + Object.keys(p)[0] + '=' + Object.values(p)[0] | ||
| }), | ||
| ) | ||
| // Toolset: | ||
| await this.toolset.initialize(false); | ||
| 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(';') | ||
| } | ||
| 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] | ||
| } | ||
| const libsString = this.getCmakeJsLibString() | ||
| D.push({ "CMAKE_JS_LIB": libsString }); | ||
| // 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) | ||
| 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 }) | ||
| } | ||
| } | ||
| // Node-api | ||
| const napiH = await locateNodeApi(this.projectRoot) | ||
| if (napiH) { | ||
| incPaths.push(napiH) | ||
| } | ||
| } | ||
| 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}); | ||
| } | ||
| return incPaths.join(';') | ||
| } | ||
| getCmakeJsSrcString() { | ||
| const srcPaths = [] | ||
| if (environment.isWin) { | ||
| const delayHook = path.normalize(path.join(__dirname, 'cpp', 'win_delay_load_hook.cc')) | ||
| // 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 }); | ||
| } | ||
| } | ||
| } | ||
| srcPaths.push(delayHook.replace(/\\/gm, '/')) | ||
| } | ||
| command = command.concat(D.map(function (p) { | ||
| return "-D" + Object.keys(p)[0] + "=" + Object.values(p)[0]; | ||
| })); | ||
| return srcPaths.join(';') | ||
| } | ||
| getNodeLibDefPath() { | ||
| return environment.isWin && this.options.isNodeApi ? headers.def_paths.node_api_def : undefined | ||
| } | ||
| async configure() { | ||
| this.verifyIfAvailable() | ||
| return command.concat(this.extraCMakeArgs); | ||
| }; | ||
| this.log.info('CMD', 'CONFIGURE') | ||
| const listPath = path.join(this.projectRoot, 'CMakeLists.txt') | ||
| const command = await this.getConfigureCommand() | ||
| CMake.prototype.getCmakeJsLibString = function () { | ||
| try { | ||
| await fs.lstat(listPath) | ||
| } catch (e) { | ||
| throw new Error("'" + listPath + "' not found.") | ||
| } | ||
| 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(";"); | ||
| }; | ||
| try { | ||
| await fs.ensureDir(this.workDir) | ||
| } catch (e) { | ||
| // Ignore | ||
| } | ||
| 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]; | ||
| } | ||
| const cwd = process.cwd() | ||
| process.chdir(this.workDir) | ||
| try { | ||
| await this._run(command) | ||
| } finally { | ||
| process.chdir(cwd) | ||
| } | ||
| } | ||
| async ensureConfigured() { | ||
| try { | ||
| await fs.lstat(path.join(this.workDir, 'CMakeCache.txt')) | ||
| } catch (e) { | ||
| await this.configure() | ||
| } | ||
| } | ||
| 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() | ||
| // 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) | ||
| 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() | ||
| // Node-api | ||
| const napiH = await locateNodeApi(this.projectRoot) | ||
| if (napiH) { | ||
| incPaths.push(napiH) | ||
| } | ||
| } | ||
| this.log.info('CMD', 'CLEAN') | ||
| return this._run(this.getCleanCommand()) | ||
| } | ||
| async reconfigure() { | ||
| this.extraCMakeArgs = [] | ||
| await this.clean() | ||
| await this.configure() | ||
| } | ||
| async rebuild() { | ||
| this.extraCMakeArgs = [] | ||
| await this.clean() | ||
| await this.build() | ||
| } | ||
| 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 }) | ||
| } | ||
| 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.getNodeLibDefPath = function () { | ||
| return environment.isWin && this.options.isNodeApi ? headers.def_paths.node_api_def : undefined | ||
| async getGenerators() { | ||
| return CMake.getGenerators(this.options, this.log) | ||
| } | ||
| } | ||
| 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); | ||
| } | ||
| }; | ||
| CMake.prototype.ensureConfigured = async function () { | ||
| 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)); | ||
| }; | ||
| 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 { | ||
| 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}); | ||
| }; | ||
| module.exports = CMake; | ||
| module.exports = CMake |
+58
-73
@@ -1,76 +0,61 @@ | ||
| "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"); | ||
| class CMLog { | ||
| get level() { | ||
| if (this.options.noLog) { | ||
| return 'silly' | ||
| } else { | ||
| return log.level | ||
| } | ||
| } | ||
| constructor(options) { | ||
| this.options = options || {} | ||
| this.debug = require('debug')(this.options.logName || 'cmake-js') | ||
| } | ||
| silly(cat, msg) { | ||
| if (this.options.noLog) { | ||
| this.debug(cat + ': ' + msg) | ||
| } else { | ||
| log.silly(cat, msg) | ||
| } | ||
| } | ||
| verbose(cat, msg) { | ||
| if (this.options.noLog) { | ||
| this.debug(cat + ': ' + msg) | ||
| } else { | ||
| log.verbose(cat, msg) | ||
| } | ||
| } | ||
| info(cat, msg) { | ||
| if (this.options.noLog) { | ||
| this.debug(cat + ': ' + msg) | ||
| } else { | ||
| log.info(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) | ||
| } | ||
| } | ||
| } | ||
| 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); | ||
| } | ||
| }; | ||
| 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.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.error = function(cat, msg) { | ||
| if (this.options.noLog) { | ||
| this.debug(cat + ": " + msg); | ||
| } | ||
| else { | ||
| log.error(cat, msg); | ||
| } | ||
| }; | ||
| module.exports = CMLog; | ||
| module.exports = CMLog |
+161
-180
@@ -1,195 +0,176 @@ | ||
| "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') | ||
| const os = require('os') | ||
| 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); | ||
| } | ||
| class Dist { | ||
| get internalPath() { | ||
| const cacheDirectory = '.cmake-js' | ||
| const runtimeArchDirectory = this.targetOptions.runtime + '-' + this.targetOptions.arch | ||
| const runtimeVersionDirectory = 'v' + this.targetOptions.runtimeVersion | ||
| // 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(os.homedir(), 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() | ||
| } | ||
| } | ||
| if (environment.isWin) { | ||
| for (const libPath of this.winLibs) { | ||
| stat = getStat(libPath) | ||
| libs = libs && stat.isFile() | ||
| } | ||
| } | ||
| } | ||
| return headers && libs | ||
| 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, | ||
| } | ||
| } | ||
| } | ||
| } | ||
| 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 | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| }); | ||
| constructor(options) { | ||
| this.options = options || {} | ||
| this.log = new CMLog(this.options) | ||
| this.targetOptions = new TargetOptions(this.options) | ||
| this.downloader = new Downloader(this.options) | ||
| } | ||
| // Methods | ||
| Dist.prototype.ensureDownloaded = async function () { | ||
| if (!this.downloaded) { | ||
| await this.download(); | ||
| } | ||
| }; | ||
| async ensureDownloaded() { | ||
| if (!this.downloaded) { | ||
| await this.download() | ||
| } | ||
| } | ||
| async download() { | ||
| 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)]) | ||
| } | ||
| 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.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 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' | ||
| }, | ||
| }) | ||
| 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 (sums) { | ||
| testSum(sums, sum, tarLocalPath) | ||
| } | ||
| } | ||
| async _downloadLibs(sums) { | ||
| const log = this.log | ||
| const self = this | ||
| if (!environment.isWin) { | ||
| return | ||
| } | ||
| 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 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 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"; | ||
| } | ||
| }); | ||
| await fs.ensureDir(path.join(self.internalPath, subDir)) | ||
| if (sums) { | ||
| testSum(sums, sum, tarLocalPath); | ||
| } | ||
| }; | ||
| const sum = await this.downloader.downloadFile(libUrl, { | ||
| path: path.join(self.internalPath, fPath), | ||
| hash: sums ? 'sha256' : null, | ||
| }) | ||
| Dist.prototype._downloadLibs = async function (sums) { | ||
| const log = this.log; | ||
| const self = this; | ||
| if (!environment.isWin) { | ||
| return; | ||
| } | ||
| if (sums) { | ||
| testSum(sums, sum, fPath) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| 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; | ||
| module.exports = Dist |
+86
-89
@@ -1,95 +0,92 @@ | ||
| "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); | ||
| } | ||
| 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 | ||
| } | ||
| 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) | ||
| }) | ||
| }) | ||
| } | ||
| async downloadString(url) { | ||
| const result = new MemoryStream() | ||
| await this.downloadToStream(url, result) | ||
| return result.toString() | ||
| } | ||
| 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!") | ||
| } | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| 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!"); | ||
| } | ||
| }; | ||
| module.exports = Downloader; | ||
| module.exports = Downloader |
+94
-104
@@ -1,107 +0,97 @@ | ||
| "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', | ||
| isArm64: os.arch() === 'arm64', | ||
| runtime: 'node', | ||
| runtimeVersion: process.versions.node, | ||
| }) | ||
| 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; | ||
| } | ||
| } | ||
| }); | ||
| _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 | ||
| }, | ||
| }, | ||
| }) |
+531
-383
@@ -7,439 +7,587 @@ 'use strict' | ||
| const { regSearchKeys, execFile, logWithPrefix } = require('./util') | ||
| const semver = require("semver"); | ||
| const semver = require('semver') | ||
| class VisualStudioFinder { | ||
| static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio() | ||
| static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio() | ||
| 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 = [] | ||
| } | ||
| 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) | ||
| } | ||
| // 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') | ||
| } | ||
| 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') | ||
| } | ||
| 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() | ||
| ] | ||
| const checks = [ | ||
| () => this.findVisualStudio2019OrNewerFromSpecifiedLocation(), | ||
| () => this.findVisualStudio2019OrNewerUsingSetupModule(), | ||
| () => this.findVisualStudio2019OrNewer(), | ||
| () => this.findVisualStudio2017FromSpecifiedLocation(), | ||
| () => this.findVisualStudio2017UsingSetupModule(), | ||
| () => this.findVisualStudio2017(), | ||
| () => this.findVisualStudio2015(), | ||
| () => this.findVisualStudio2013(), | ||
| ] | ||
| for (const check of checks) { | ||
| const info = await check() | ||
| if (info) { | ||
| return this.succeed(info) | ||
| } | ||
| } | ||
| for (const check of checks) { | ||
| const info = await check() | ||
| if (info) { | ||
| return this.succeed(info) | ||
| } | ||
| } | ||
| return this.fail() | ||
| } | ||
| 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 | ||
| } | ||
| 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') | ||
| } | ||
| } | ||
| 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') | ||
| 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') | ||
| // 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') | ||
| } | ||
| 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()}' | ||
| ] | ||
| async findVisualStudio2019OrNewerFromSpecifiedLocation() { | ||
| return this.findVSFromSpecifiedLocation([2019, 2022]) | ||
| } | ||
| this.log.silly('Running', ps, psArgs) | ||
| const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' }) | ||
| return this.parseData(err, stdout, stderr) | ||
| } | ||
| 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]) | ||
| } | ||
| // 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) | ||
| 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', | ||
| ], | ||
| } | ||
| 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 | ||
| } | ||
| // 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) | ||
| } | ||
| if (err) { | ||
| this.log.silly('PS err = %j', err && (err.stack || err)) | ||
| return failPowershell() | ||
| } | ||
| async findVisualStudio2019OrNewerUsingSetupModule() { | ||
| return this.findNewVSUsingSetupModule([2019, 2022]) | ||
| } | ||
| let vsInfo | ||
| try { | ||
| vsInfo = JSON.parse(stdout) | ||
| } catch (e) { | ||
| this.log.silly('PS stdout = %j', stdout) | ||
| this.log.silly(e) | ||
| return failPowershell() | ||
| } | ||
| 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]) | ||
| } | ||
| if (!Array.isArray(vsInfo)) { | ||
| this.log.silly('PS stdout = %j', stdout) | ||
| return failPowershell() | ||
| } | ||
| async findNewVSUsingSetupModule(supportedYears) { | ||
| const ps = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe') | ||
| const vcInstallDir = this.envVcInstallDir | ||
| 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) | ||
| 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}`] | ||
| // 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 | ||
| }) | ||
| 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) | ||
| } | ||
| // Sort to place newer versions first | ||
| vsInfo.sort((a, b) => b.versionYear - a.versionYear) | ||
| // Invoke the PowerShell script to get information about Visual Studio 2019 | ||
| // or newer installations | ||
| async findVisualStudio2019OrNewer() { | ||
| return this.findNewVS([2019, 2022]) | ||
| } | ||
| 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}"`) | ||
| // 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]) | ||
| } | ||
| if (info.msBuild) { | ||
| this.addLog('- found "Visual Studio C++ core features"') | ||
| } else { | ||
| this.addLog('- "Visual Studio C++ core features" missing') | ||
| continue | ||
| } | ||
| // Invoke the PowerShell script to get information about Visual Studio 2017 | ||
| // or newer installations | ||
| 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 = [ | ||
| '-ExecutionPolicy', | ||
| 'Unrestricted', | ||
| '-NoProfile', | ||
| '-Command', | ||
| "&{Add-Type -Path '" + csFile + "';" + '[VisualStudioConfiguration.Main]::PrintJson()}', | ||
| ] | ||
| if (info.toolset) { | ||
| this.addLog(`- found VC++ toolset: ${info.toolset}`) | ||
| } else { | ||
| this.addLog('- missing any VC++ toolset') | ||
| continue | ||
| } | ||
| this.log.silly('Running', ps, psArgs) | ||
| 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) | ||
| } | ||
| if (info.sdk) { | ||
| this.addLog(`- found Windows SDK: ${info.sdk}`) | ||
| } else { | ||
| this.addLog('- missing any Windows SDK') | ||
| continue | ||
| } | ||
| // Parse the output of the PowerShell script, make sanity checks | ||
| parseData(err, stdout, stderr, sanityCheckOptions) { | ||
| const defaultOptions = { | ||
| checkIsArray: false, | ||
| } | ||
| if (!this.checkConfigVersion(info.versionYear, info.path)) { | ||
| continue | ||
| } | ||
| // Merging provided options with the default options | ||
| const sanityOptions = { ...defaultOptions, ...sanityCheckOptions } | ||
| return info | ||
| } | ||
| this.log.silly('PS stderr = %j', stderr) | ||
| this.addLog( | ||
| 'could not find a version of Visual Studio 2017 or newer to use') | ||
| return null | ||
| } | ||
| 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. \n | ||
| Failure details: ${failureDetails}`, | ||
| ) | ||
| 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 {} | ||
| } | ||
| if (err) { | ||
| this.log.silly('PS err = %j', err && (err.stack || err)) | ||
| return failPowershell(`${err}`.substring(0, 40)) | ||
| } | ||
| msBuildPathExists (path) { | ||
| return existsSync(path) | ||
| } | ||
| let vsInfo | ||
| try { | ||
| vsInfo = JSON.parse(stdout) | ||
| } catch (e) { | ||
| this.log.silly('PS stdout = %j', stdout) | ||
| this.log.silly(e) | ||
| return failPowershell() | ||
| } | ||
| // 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 | ||
| } | ||
| if (sanityOptions.checkIsArray && !Array.isArray(vsInfo)) { | ||
| this.log.silly('PS stdout = %j', stdout) | ||
| return failPowershell('Expected array as output of the PS script') | ||
| } | ||
| return vsInfo | ||
| } | ||
| // Helper - process toolset information | ||
| getToolset (info, versionYear) { | ||
| const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' | ||
| const express = 'Microsoft.VisualStudio.WDExpress' | ||
| // 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) | ||
| 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) | ||
| 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 | ||
| } | ||
| // Remove future versions or errors parsing version number | ||
| // Also remove any unsupported versions | ||
| vsInfo = vsInfo.filter((info) => { | ||
| if (info.versionYear && supportedYears.indexOf(info.versionYear) !== -1) { | ||
| return true | ||
| } | ||
| this.addLog(`${info.versionYear ? 'unsupported' : 'unknown'} version "${info.version}" found at "${info.path}"`) | ||
| return false | ||
| }) | ||
| 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 | ||
| } | ||
| // Sort to place newer versions first | ||
| vsInfo.sort((a, b) => b.versionYear - a.versionYear) | ||
| // 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.' | ||
| 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}"`) | ||
| 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 (info.msBuild) { | ||
| this.addLog('- found "Visual Studio C++ core features"') | ||
| } else { | ||
| this.addLog('- "Visual Studio C++ core features" missing') | ||
| continue | ||
| } | ||
| 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 | ||
| } | ||
| if (info.toolset) { | ||
| this.addLog(`- found VC++ toolset: ${info.toolset}`) | ||
| } else { | ||
| this.addLog('- missing any VC++ toolset') | ||
| continue | ||
| } | ||
| // 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' | ||
| }) | ||
| } | ||
| if (info.sdk) { | ||
| this.addLog(`- found Windows SDK: ${info.sdk}`) | ||
| } else { | ||
| this.addLog('- missing any Windows SDK') | ||
| continue | ||
| } | ||
| // 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' | ||
| }) | ||
| } | ||
| if (!this.checkConfigVersion(info.versionYear, info.path)) { | ||
| continue | ||
| } | ||
| // 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' | ||
| return info | ||
| } | ||
| 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'] | ||
| this.addLog('could not find a version of Visual Studio 2017 or newer to use') | ||
| return null | ||
| } | ||
| 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 | ||
| } | ||
| // 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 {} | ||
| } | ||
| const msBuild = path.join(res, 'MSBuild.exe') | ||
| this.addLog(`- MSBuild in "${msBuild}"`) | ||
| msBuildPathExists(path) { | ||
| return existsSync(path) | ||
| } | ||
| if (!this.checkConfigVersion(info.versionYear, vsPath)) { | ||
| return null | ||
| } | ||
| // 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) { | ||
| if (process.arch === 'arm64' && this.msBuildPathExists(msbuildPathArm64)) { | ||
| return msbuildPathArm64 | ||
| } else { | ||
| 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 | ||
| } | ||
| info.path = vsPath | ||
| info.msBuild = msBuild | ||
| info.sdk = null | ||
| return info | ||
| } catch (err) { | ||
| this.addLog('- not found') | ||
| return null | ||
| } | ||
| } | ||
| // Helper - process toolset information | ||
| getToolset(info, versionYear) { | ||
| 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' | ||
| // 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 (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 | ||
| } | ||
| 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 | ||
| } | ||
| 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 | ||
| } | ||
| return true | ||
| } | ||
| // 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 | ||
| } | ||
| async execFile(exec, args) { | ||
| return await execFile(exec, args, { encoding: 'utf8' }) | ||
| } | ||
| } | ||
| module.exports = VisualStudioFinder | ||
| module.exports = VisualStudioFinder |
+52
-53
@@ -7,66 +7,65 @@ 'use strict' | ||
| 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 | ||
| } | ||
| logWithPrefix: logWithPrefix, | ||
| regGetValue: regGetValue, | ||
| regSearchKeys: regSearchKeys, | ||
| execFile: execFile, | ||
| } |
+10
-10
@@ -1,12 +0,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'), | ||
| } |
+53
-55
@@ -1,65 +0,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) | ||
| } |
+16
-16
@@ -1,18 +0,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; | ||
| } | ||
| }; | ||
| 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 | ||
| } | ||
| }) |
+23
-23
@@ -1,31 +0,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 | ||
| } |
+49
-51
@@ -1,55 +0,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 |
+90
-81
@@ -1,86 +0,95 @@ | ||
| "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 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.isArm64 ? 'win-arm64' : 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.isArm64 ? 'arm64' : 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') { | ||
| paths = func(targetOptions) | ||
| if (paths && typeof paths === 'object') { | ||
| return paths | ||
| } | ||
| } | ||
| throw new Error('Unknown runtime: ' + runtime) | ||
| }, | ||
| } | ||
| module.exports = runtimePaths; | ||
| module.exports = runtimePaths |
+29
-38
@@ -1,42 +0,33 @@ | ||
| "use strict"; | ||
| 'use strict' | ||
| const environment = require("./environment"); | ||
| 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 isArm64() { | ||
| return this.arch === 'arm64' | ||
| } | ||
| get runtime() { | ||
| return this.options.runtime || environment.runtime | ||
| } | ||
| get runtimeVersion() { | ||
| return this.options.runtimeVersion || environment.runtimeVersion | ||
| } | ||
| constructor(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; | ||
| } | ||
| } | ||
| }); | ||
| module.exports = TargetOptions; | ||
| module.exports = TargetOptions |
+206
-208
@@ -1,226 +0,224 @@ | ||
| "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') : {} | ||
| 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 | ||
| } | ||
| async initialize(install) { | ||
| if (!this._initialized) { | ||
| if (environment.isWin) { | ||
| await this.initializeWin(install) | ||
| } else { | ||
| this.initializePosix(install) | ||
| } | ||
| this._initialized = true | ||
| } | ||
| } | ||
| 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++'.", | ||
| ) | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| 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.') | ||
| } | ||
| } | ||
| Toolset.prototype.initialize = async function (install) { | ||
| if (!this._initialized) { | ||
| if (environment.isWin) { | ||
| await this.initializeWin(install); | ||
| } | ||
| else { | ||
| this.initializePosix(install); | ||
| } | ||
| this._initialized = true; | ||
| } | ||
| }; | ||
| // 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' | ||
| } | ||
| } | ||
| 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++'."); | ||
| } | ||
| } | ||
| // 3: Flags | ||
| if (environment.isOSX) { | ||
| if (install) { | ||
| this.log.verbose('TOOL', 'Setting default OSX compiler flags.') | ||
| } | ||
| 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"; | ||
| } | ||
| } | ||
| 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') | ||
| } | ||
| // 3: Flags | ||
| if (environment.isOSX) { | ||
| if (install) { | ||
| this.log.verbose("TOOL", "Setting default OSX compiler flags."); | ||
| } | ||
| this.compilerFlags.push('-DBUILDING_NODE_EXTENSION') | ||
| 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.') | ||
| } | ||
| } | ||
| 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 | ||
| // 4: Build target | ||
| if (this.options.target) { | ||
| this.log.info("TOOL", "Building only the " + this.options.target + " target, as specified from the command line."); | ||
| } | ||
| }; | ||
| const isAboveVS16 = foundVsInfo.versionMajor >= 16 | ||
| 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; | ||
| // 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.') | ||
| } | ||
| } | ||
| const isAboveVS16 = foundVsInfo.versionMajor >= 16; | ||
| this.linkerFlags.push('/DELAYLOAD:NODE.EXE') | ||
| // 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."); | ||
| } | ||
| } | ||
| if (this.targetOptions.isX86) { | ||
| if (install) { | ||
| this.log.verbose('TOOL', 'Setting SAFESEH:NO linker flag.') | ||
| } | ||
| this.linkerFlags.push('/SAFESEH:NO') | ||
| } | ||
| } | ||
| async _getTopSupportedVisualStudioGenerator() { | ||
| const CMake = require('./cMake') | ||
| assert(environment.isWin) | ||
| this.linkerFlags.push("/DELAYLOAD:NODE.EXE"); | ||
| const selectedVs = await findVisualStudio(environment.runtimeVersion, this.options.msvsVersion) | ||
| if (!selectedVs) return null | ||
| if (this.targetOptions.isX86) { | ||
| if (install) { | ||
| this.log.verbose("TOOL", "Setting SAFESEH:NO linker flag."); | ||
| } | ||
| this.linkerFlags.push("/SAFESEH:NO"); | ||
| } | ||
| }; | ||
| 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 | ||
| } | ||
| Toolset.prototype._getTopSupportedVisualStudioGenerator = async function () { | ||
| const CMake = require("./cMake"); | ||
| assert(environment.isWin); | ||
| // 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 | ||
| } | ||
| } | ||
| const selectedVs = await findVisualStudio(environment.runtimeVersion, this.options.msvsVersion) | ||
| if (!selectedVs) return null; | ||
| return { | ||
| ...selectedVs, | ||
| generator: gen, | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| // Nothing matched | ||
| return null | ||
| } | ||
| } | ||
| // 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; | ||
| module.exports = Toolset |
+75
-72
| { | ||
| "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.1", | ||
| "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", | ||
| "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.22.2", | ||
| "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" | ||
| ], | ||
| "packageManager": "yarn@1.22.22+sha256.c17d3797fb9a9115bf375e31bfd30058cac6bc9c3b8807a3d8cb2094794b51ca" | ||
| } |
+45
-47
@@ -7,4 +7,5 @@ # CMake.js (MIT) | ||
| ## 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 | ||
@@ -89,9 +90,9 @@ - [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 | ||
| - 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) | ||
@@ -104,3 +105,3 @@ ## Usage | ||
| 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).)_ | ||
@@ -111,12 +112,7 @@ - Install cmake-js for your module `npm install --save cmake-js` | ||
| ```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_compile_definitions(-DNAPI_VERSION=4) | ||
| add_definitions(-DNAPI_VERSION=4) | ||
| include_directories(${CMAKE_JS_INC}) | ||
| file(GLOB SOURCE_FILES "your-source files-location-here") | ||
@@ -126,3 +122,5 @@ | ||
| 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) | ||
@@ -236,2 +234,3 @@ if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) | ||
| In command prompt: | ||
| ``` | ||
@@ -267,11 +266,11 @@ cmake-js compile --CDFOO="bar" | ||
| { | ||
| "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" | ||
| } | ||
| } | ||
@@ -283,9 +282,8 @@ ``` | ||
| - **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` | ||
@@ -298,3 +296,3 @@ | ||
| 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. | ||
@@ -306,2 +304,3 @@ To compile a native module that uses only the | ||
| You must also add the following lines to your CMakeLists.txt, to allow for building on windows | ||
| ``` | ||
@@ -352,7 +351,7 @@ if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) | ||
| { | ||
| "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" | ||
| } | ||
| } | ||
@@ -363,4 +362,4 @@ ``` | ||
| #### Heroku | ||
| #### Heroku | ||
| [Heroku](https://heroku.com) uses the concept of a [buildpack](https://devcenter.heroku.com/articles/buildpacks) to define | ||
@@ -376,9 +375,9 @@ how an application should be prepared to run in a [dyno](https://devcenter.heroku.com/articles/dynos). | ||
| - 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 | ||
@@ -390,3 +389,2 @@ - Deploy the application to have the changes take effect | ||
| ## Tutorials | ||
@@ -401,5 +399,5 @@ | ||
| * [@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 | ||
@@ -406,0 +404,0 @@ Open a PR to add your own project here. |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 7 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
12
-7.69%1976
4.66%21
-4.55%105003
-6.75%6
100%400
-0.5%4
33.33%- Removed
- Removed