@flun/nodejs-mobile-gyp 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -75
- package/LICENSE +9 -4
- package/README.md +198 -132
- package/addon.gypi +1 -1
- package/bin/node-gyp.js +6 -5
- package/gyp/README.md +5 -3
- package/gyp/data/win/large-pdb-shim.cc +1 -1
- package/gyp/gyp +1 -1
- package/gyp/gyp.bat +1 -1
- package/gyp/gyp_main.py +1 -1
- package/gyp/pylib/gyp/MSVSNew.py +1 -1
- package/gyp/pylib/gyp/MSVSProject.py +1 -1
- package/gyp/pylib/gyp/MSVSSettings.py +1 -1
- package/gyp/pylib/gyp/MSVSToolFile.py +1 -1
- package/gyp/pylib/gyp/MSVSUserFile.py +1 -1
- package/gyp/pylib/gyp/MSVSUtil.py +1 -1
- package/gyp/pylib/gyp/MSVSVersion.py +1 -1
- package/gyp/pylib/gyp/__init__.py +1 -1
- package/gyp/pylib/gyp/common.py +1 -1
- package/gyp/pylib/gyp/easy_xml.py +1 -1
- package/gyp/pylib/gyp/flock_tool.py +1 -1
- package/gyp/pylib/gyp/generator/analyzer.py +1 -1
- package/gyp/pylib/gyp/generator/android.py +1 -1
- package/gyp/pylib/gyp/generator/cmake.py +1 -1
- package/gyp/pylib/gyp/generator/compile_commands_json.py +1 -1
- package/gyp/pylib/gyp/generator/dump_dependency_json.py +1 -1
- package/gyp/pylib/gyp/generator/eclipse.py +1 -1
- package/gyp/pylib/gyp/generator/gypd.py +1 -1
- package/gyp/pylib/gyp/generator/gypsh.py +1 -1
- package/gyp/pylib/gyp/generator/make.py +23 -12
- package/gyp/pylib/gyp/generator/msvs.py +3 -3
- package/gyp/pylib/gyp/generator/ninja.py +1 -1
- package/gyp/pylib/gyp/generator/xcode.py +1 -1
- package/gyp/pylib/gyp/input.py +1 -1
- package/gyp/pylib/gyp/mac_tool.py +1 -1
- package/gyp/pylib/gyp/msvs_emulation.py +1 -1
- package/gyp/pylib/gyp/ninja_syntax.py +1 -1
- package/gyp/pylib/gyp/simple_copy.py +1 -1
- package/gyp/pylib/gyp/win_tool.py +1 -1
- package/gyp/pylib/gyp/xcode_emulation.py +5 -5
- package/gyp/pylib/gyp/xcode_ninja.py +1 -1
- package/gyp/pylib/gyp/xcodeproj_file.py +1 -1
- package/gyp/pylib/gyp/xml_fix.py +1 -1
- package/gyp/pylib/packaging/LICENSE +1 -1
- package/gyp/pylib/packaging/LICENSE.APACHE +1 -1
- package/gyp/pylib/packaging/LICENSE.BSD +1 -1
- package/gyp/pylib/packaging/__init__.py +1 -1
- package/gyp/pylib/packaging/_elffile.py +1 -1
- package/gyp/pylib/packaging/_manylinux.py +1 -1
- package/gyp/pylib/packaging/_musllinux.py +1 -1
- package/gyp/pylib/packaging/_parser.py +1 -1
- package/gyp/pylib/packaging/_structures.py +1 -1
- package/gyp/pylib/packaging/_tokenizer.py +1 -1
- package/gyp/pylib/packaging/markers.py +1 -1
- package/gyp/pylib/packaging/metadata.py +1 -1
- package/gyp/pylib/packaging/requirements.py +1 -1
- package/gyp/pylib/packaging/specifiers.py +1 -1
- package/gyp/pylib/packaging/tags.py +1 -1
- package/gyp/pylib/packaging/utils.py +1 -1
- package/gyp/pylib/packaging/version.py +1 -1
- package/lib/Find-VisualStudio.cs +1 -1
- package/lib/build.js +16 -16
- package/lib/clean.js +2 -2
- package/lib/configure.js +20 -17
- package/lib/create-config-gypi.js +8 -8
- package/lib/download.js +3 -3
- package/lib/find-node-directory.js +9 -9
- package/lib/find-python.js +14 -14
- package/lib/find-visualstudio.js +19 -19
- package/lib/install.js +22 -21
- package/lib/list.js +1 -1
- package/lib/log.js +2 -2
- package/lib/node-gyp.js +7 -7
- package/lib/process-release.js +14 -14
- package/lib/rebuild.js +1 -1
- package/lib/remove.js +2 -2
- package/lib/util.js +5 -4
- package/package.json +3 -3
- package/src/win_delay_load_hook.cc +1 -1
package/lib/clean.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as log from './log.js';
|
|
|
3
3
|
|
|
4
4
|
const fs = gracefulFs.promises,
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* 清理:删除 build 目录
|
|
7
7
|
*/
|
|
8
8
|
clean = async (gyp, argv) => {
|
|
9
9
|
const buildDir = 'build';
|
|
@@ -13,4 +13,4 @@ const fs = gracefulFs.promises,
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
clean.usage = 'Removes any generated build files and the "out" dir';
|
|
16
|
-
export { clean };
|
|
16
|
+
export { clean };
|
package/lib/configure.js
CHANGED
|
@@ -10,9 +10,10 @@ import { findAccessibleSync } from './util.js';
|
|
|
10
10
|
import { PythonFinder } from './find-python.js';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
|
|
13
|
-
const fs = gracefulFs.promises, win = process.platform === 'win32',
|
|
13
|
+
const fs = gracefulFs.promises, win = process.platform === 'win32',
|
|
14
|
+
__dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
15
|
|
|
15
|
-
//
|
|
16
|
+
// 条件加载:仅在 Windows 上需要
|
|
16
17
|
let findVisualStudio = null, nodeDir, vsInfo = null;
|
|
17
18
|
if (win) {
|
|
18
19
|
const { VisualStudioFinder } = await import('./find-visualstudio.js');
|
|
@@ -20,7 +21,7 @@ if (win) {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
|
-
*
|
|
24
|
+
* 生成构建文件(Makefile / MSVC 工程)
|
|
24
25
|
*/
|
|
25
26
|
const configure = async (gyp, argv) => {
|
|
26
27
|
const buildDir = path.resolve('build'), configNames = ['config.gypi', 'common.gypi'], configs = [],
|
|
@@ -40,7 +41,7 @@ const configure = async (gyp, argv) => {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
configs.forEach(config => argv.push('-I', config)); // 引入所有找到的 .gypi 文件
|
|
43
|
-
// AIX 和 z/OS
|
|
44
|
+
// AIX 和 z/OS 需要设置导出文件路径,其中包含链接所需符号
|
|
44
45
|
let nodeExpFile, nodeRootDir, candidates, logprefix = 'find exports file';
|
|
45
46
|
if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') {
|
|
46
47
|
const ext = process.platform === 'os390' ? 'x' : 'exp';
|
|
@@ -62,7 +63,7 @@ const configure = async (gyp, argv) => {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
// z/OS 需要设置 zoslib
|
|
66
|
+
// z/OS 需要设置 zoslib 头文件目录,其中包含 v8config.h 引用的头文件
|
|
66
67
|
let zoslibIncDir;
|
|
67
68
|
if (process.platform === 'os390') {
|
|
68
69
|
logprefix = "find zoslib's zos-base.h:";
|
|
@@ -110,7 +111,8 @@ const configure = async (gyp, argv) => {
|
|
|
110
111
|
if (win) outputDir = buildDir; // Windows 需要绝对路径
|
|
111
112
|
const nodeGypDir = path.resolve(__dirname, '..');
|
|
112
113
|
|
|
113
|
-
let nodeLibFile = path.join(nodeDir,
|
|
114
|
+
let nodeLibFile = path.join(nodeDir,
|
|
115
|
+
!gyp.opts.nodedir ? '<(target_arch)' : '$(Configuration)', release.name + '.lib');
|
|
114
116
|
|
|
115
117
|
argv.push('-I', addonGypi), argv.push('-I', commonGypi), argv.push('-Dlibrary=shared_library');
|
|
116
118
|
argv.push('-Dvisibility=default'), argv.push('-Dnode_root_dir=' + nodeDir);
|
|
@@ -120,18 +122,18 @@ const configure = async (gyp, argv) => {
|
|
|
120
122
|
}
|
|
121
123
|
argv.push('-Dnode_gyp_dir=' + nodeGypDir);
|
|
122
124
|
|
|
123
|
-
// 这样做是为了让 Cygwin
|
|
124
|
-
//
|
|
125
|
+
// 这样做是为了让 Cygwin 环境正常工作,否则未转义的 '\' 会被吃掉,
|
|
126
|
+
// 导致路径错误,如 c:parentFolderfolderanotherFolder 而非 c:\parentFolder\folder\anotherFolder
|
|
125
127
|
if (win) nodeLibFile = nodeLibFile.replace(/\\/g, '\\\\');
|
|
126
128
|
argv.push('-Dnode_lib_file=' + nodeLibFile), argv.push('-Dmodule_root_dir=' + process.cwd());
|
|
127
129
|
argv.push('-Dnode_engine=' + (gyp.opts.node_engine || process.jsEngine || 'v8'));
|
|
128
130
|
argv.push('--depth=.'), argv.push('--no-parallel');
|
|
129
131
|
argv.push('--generator-output', outputDir); // 告诉 gyp 把 Makefile/Solution 文件写到 output_dir
|
|
130
|
-
// 使用 find-visualstudio 探测到的实际 toolset
|
|
131
|
-
//
|
|
132
|
+
// 使用 find-visualstudio 探测到的实际 toolset(v140/v141/v142/v143/v145...),
|
|
133
|
+
// 不再硬编码,以兼容 VS2013+ 各版本
|
|
132
134
|
if (win) {
|
|
133
135
|
if (vsInfo?.toolset) argv.push('-Gmsvs_toolset=' + vsInfo.toolset);
|
|
134
|
-
else log.warn('gyp', '未能探测到 VS toolset
|
|
136
|
+
else log.warn('gyp', '未能探测到 VS toolset,将使用 gyp 默认值');
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
argv.push('-Goutput_dir=.'); // 告诉 make 把输出写到同一目录
|
|
@@ -187,21 +189,22 @@ const configure = async (gyp, argv) => {
|
|
|
187
189
|
process.env.PYTHON = python;
|
|
188
190
|
|
|
189
191
|
if (gyp.opts.nodedir) {
|
|
190
|
-
// 指定了 --nodedir
|
|
192
|
+
// 指定了 --nodedir,用它作为开发文件目录
|
|
191
193
|
nodeDir = gyp.opts.nodedir.replace(/^~/, os.homedir());
|
|
192
194
|
log.verbose('get node dir', 'compiling against specified --nodedir dev files: %s', nodeDir);
|
|
193
195
|
} else {
|
|
194
|
-
// 未指定 --nodedir
|
|
196
|
+
// 未指定 --nodedir,先确保 Node 依赖已安装
|
|
195
197
|
if ('v' + release.version !== process.version) {
|
|
196
|
-
// 指定了 --target
|
|
198
|
+
// 指定了 --target,确定目标版本
|
|
197
199
|
log.verbose('get node dir', 'compiling against --target node version: %s', release.version);
|
|
198
200
|
} else {
|
|
199
|
-
// 未指定 --target
|
|
200
|
-
log.verbose('get node dir',
|
|
201
|
+
// 未指定 --target,使用当前主机 Node 版本
|
|
202
|
+
log.verbose('get node dir',
|
|
203
|
+
'no --target version specified, falling back to host node version: %s', release.version);
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
if (!release.semver) throw new Error('版本号无效: ' + release.version); // 无法用 semver 解析版本字符串
|
|
204
|
-
// 如果指定了 tarball
|
|
207
|
+
// 如果指定了 tarball 选项,总是删除并重装 headers 到 devdir,
|
|
205
208
|
// 否则只在不存在时安装
|
|
206
209
|
gyp.opts.ensure = !gyp.opts.tarball, await gyp.commands.install([release.version]);
|
|
207
210
|
log.verbose('get node dir', 'target node version installed:', release.versionDir);
|
|
@@ -16,7 +16,7 @@ const fs = gracefulFs.promises,
|
|
|
16
16
|
* 读取基础 config.gypi
|
|
17
17
|
*/
|
|
18
18
|
getBaseConfigGypi = async ({ gyp, nodeDir }) => {
|
|
19
|
-
// 在以下情况优先读取 $nodeDir/include/node/config.gypi
|
|
19
|
+
// 在以下情况优先读取 $nodeDir/include/node/config.gypi:
|
|
20
20
|
// 1. 指定了 --dist-url 或 --nodedir
|
|
21
21
|
// 2. 且没有指定 --force-process-config
|
|
22
22
|
const useCustomHeaders = gyp.opts.nodedir || gyp.opts.disturl || gyp.opts['dist-url'],
|
|
@@ -40,8 +40,8 @@ const fs = gracefulFs.promises,
|
|
|
40
40
|
if (!config.variables) config.variables = {};
|
|
41
41
|
|
|
42
42
|
const defaults = config.target_defaults, variables = config.variables;
|
|
43
|
-
// 不要继承 base config.gypi 的 defaults
|
|
44
|
-
// 否则当 node
|
|
43
|
+
// 不要继承 base config.gypi 的 defaults,
|
|
44
|
+
// 否则当 node 可执行文件在另一台机器编译(lib/include 路径不同)时会出问题
|
|
45
45
|
defaults.cflags = [], defaults.defines = [], defaults.include_dirs = [], defaults.libraries = [];
|
|
46
46
|
|
|
47
47
|
// 设置 default_configuration
|
|
@@ -66,14 +66,14 @@ const fs = gracefulFs.promises,
|
|
|
66
66
|
else log.warn('仅 Visual Studio 2017 15.9 及以上版本支持编译 ARM64 汇编');
|
|
67
67
|
}
|
|
68
68
|
variables.msbuild_path = vsInfo.msBuild;
|
|
69
|
-
// Windows 原生构建走 MSVC
|
|
70
|
-
// 否则 common.gypi 会把 toolset 设为 ClangCL
|
|
71
|
-
//
|
|
69
|
+
// Windows 原生构建走 MSVC 工具集:必须关闭 clang 与 LTO,
|
|
70
|
+
// 否则 common.gypi 会把 toolset 设为 ClangCL,并向 MSVC 链接器传入 Clang 专用参数
|
|
71
|
+
// (-flto=thin、/opt:lldltojobs),导致 MSB8020 或 LNK1117
|
|
72
72
|
if (!gyp.opts.format || gyp.opts.format === 'msvs')
|
|
73
73
|
variables.clang = 0, variables.enable_lto = 'false', variables.enable_thin_lto = 'false', variables.lto_jobs = '';
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
//
|
|
76
|
+
// 把其余未知选项当作变量写入,支持模块自定义 configure 参数,例如:
|
|
77
77
|
// $ node-gyp configure --shared-libxml2
|
|
78
78
|
for (const [k, v] of Object.entries(gyp.opts)) {
|
|
79
79
|
if (k === 'argv' || k in gyp.configDefs) continue;
|
|
@@ -106,4 +106,4 @@ const fs = gracefulFs.promises,
|
|
|
106
106
|
return configPath;
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
export { createConfigGypi, parseConfigGypi, getCurrentConfigGypi }
|
|
109
|
+
export { createConfigGypi, parseConfigGypi, getCurrentConfigGypi }
|
package/lib/download.js
CHANGED
|
@@ -4,10 +4,10 @@ import * as log from './log.js';
|
|
|
4
4
|
|
|
5
5
|
const { promises: fs } = gracefulFs,
|
|
6
6
|
/**
|
|
7
|
-
* 读取 CA
|
|
7
|
+
* 读取 CA 证书文件,按证书边界切分为多个证书
|
|
8
8
|
*/
|
|
9
9
|
readCAFile = async filename => {
|
|
10
|
-
// CA
|
|
10
|
+
// CA 文件可能包含多份证书,按证书边界切分; [\S\s]*? 用于匹配包括换行在内的所有内容;
|
|
11
11
|
const ca = await fs.readFile(filename, 'utf8'), re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g;
|
|
12
12
|
return ca.match(re);
|
|
13
13
|
},
|
|
@@ -28,4 +28,4 @@ const { promises: fs } = gracefulFs,
|
|
|
28
28
|
return res;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export { readCAFile, download }
|
|
31
|
+
export { readCAFile, download }
|
|
@@ -7,33 +7,33 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)),
|
|
|
7
7
|
* 从脚本位置或进程对象推断 Node 根目录
|
|
8
8
|
*/
|
|
9
9
|
findNodeDirectory = (scriptLocation, processObj) => {
|
|
10
|
-
//
|
|
10
|
+
// 未传入时使用默认值,便于回归测试
|
|
11
11
|
if (scriptLocation === undefined) scriptLocation = __dirname;
|
|
12
12
|
if (processObj === undefined) processObj = process;
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// 看看上层目录结构,判断当前所处位置
|
|
15
15
|
const npmParentDirectory = path.join(scriptLocation, '../../../..');
|
|
16
16
|
log.verbose('node-gyp root', 'npm_parent_directory is ' + path.basename(npmParentDirectory));
|
|
17
17
|
log.verbose('node-gyp root', 'Finding node root directory');
|
|
18
18
|
let nodeRootDir = '';
|
|
19
19
|
if (path.basename(npmParentDirectory) === 'deps') {
|
|
20
|
-
//
|
|
20
|
+
// 处于构建目录中,脚本位于 deps/npm/node_modules/node-gyp/lib
|
|
21
21
|
nodeRootDir = path.join(npmParentDirectory, '..');
|
|
22
22
|
log.verbose('node-gyp root', 'in build directory, root = ' + nodeRootDir);
|
|
23
23
|
} else if (path.basename(npmParentDirectory) === 'node_modules') {
|
|
24
|
-
// 处于 Node
|
|
24
|
+
// 处于 Node 安装目录中,脚本位于
|
|
25
25
|
// lib/node_modules/npm/node_modules/node-gyp/lib 或
|
|
26
|
-
// node_modules/npm/node_modules/node-gyp/lib
|
|
26
|
+
// node_modules/npm/node_modules/node-gyp/lib(视平台而定)
|
|
27
27
|
if (processObj.platform === 'win32') nodeRootDir = path.join(npmParentDirectory, '..');
|
|
28
28
|
else nodeRootDir = path.join(npmParentDirectory, '../..');
|
|
29
29
|
log.verbose('node-gyp root', 'in install directory, root = ' + nodeRootDir);
|
|
30
30
|
} else {
|
|
31
|
-
//
|
|
31
|
+
// 无法判断,尝试从 node 可执行文件的位置推断
|
|
32
32
|
const nodeDir = path.dirname(processObj.execPath), directoryUp = path.basename(nodeDir);
|
|
33
33
|
if (directoryUp === 'bin') nodeRootDir = path.join(nodeDir, '..');
|
|
34
34
|
else if (directoryUp === 'Release' || directoryUp === 'Debug') {
|
|
35
|
-
// 如果是刚构建的 node
|
|
36
|
-
// Windows
|
|
35
|
+
// 如果是刚构建的 node,且目录结构为仓库结构:
|
|
36
|
+
// Windows 只需上一层,其余平台两层
|
|
37
37
|
if (processObj.platform === 'win32') nodeRootDir = path.join(nodeDir, '..');
|
|
38
38
|
else nodeRootDir = path.join(nodeDir, '../..');
|
|
39
39
|
}
|
|
@@ -42,4 +42,4 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)),
|
|
|
42
42
|
return nodeRootDir;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export { findNodeDirectory }
|
|
45
|
+
export { findNodeDirectory }
|
package/lib/find-python.js
CHANGED
|
@@ -6,7 +6,7 @@ import fs from 'fs';
|
|
|
6
6
|
|
|
7
7
|
const win = process.platform === 'win32',
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* 获取当前操作系统用户名(失败时返回 undefined)
|
|
10
10
|
*/
|
|
11
11
|
getOsUserInfo = () => {
|
|
12
12
|
try { return os.userInfo().username } catch { }
|
|
@@ -19,8 +19,8 @@ const win = process.platform === 'win32',
|
|
|
19
19
|
programFilesX86 = process.env['ProgramFiles(x86)'] || `${programFiles} (x86)`,
|
|
20
20
|
systemRoot = process.env.SystemRoot || `${systemDrive}\\Windows`,
|
|
21
21
|
/**
|
|
22
|
-
* 动态扫描 Windows 上所有已安装的 Python
|
|
23
|
-
*
|
|
22
|
+
* 动态扫描 Windows 上所有已安装的 Python,不硬编码版本号;
|
|
23
|
+
* 官方安装器目录命名:3.14 -> Python314,3.9 -> Python39,32 位加 -32 后缀;
|
|
24
24
|
*/
|
|
25
25
|
scanWindowsPythonLocations = () => {
|
|
26
26
|
const results = [], bases = [foundLocalAppData ? `${localAppData}\\Programs\\Python` : null, `${programFiles}\\Python`,
|
|
@@ -40,13 +40,13 @@ const win = process.platform === 'win32',
|
|
|
40
40
|
return results;
|
|
41
41
|
},
|
|
42
42
|
/**
|
|
43
|
-
* py.exe
|
|
44
|
-
* 避免 Gradle / CI 环境下 PATH 不含 C:\Windows
|
|
43
|
+
* py.exe 启动器候选:先试 PATH 中的,再试常见绝对路径,
|
|
44
|
+
* 避免 Gradle / CI 环境下 PATH 不含 C:\Windows 的情况;
|
|
45
45
|
*/
|
|
46
46
|
pyLauncherCandidates = win ? ['py.exe', `${systemRoot}\\py.exe`, `${systemRoot}\\System32\\py.exe`] : [];
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Python
|
|
49
|
+
* Python 查找器:按顺序尝试多种途径定位可用的 Python 3.6+
|
|
50
50
|
*/
|
|
51
51
|
class PythonFinder {
|
|
52
52
|
static findPython = (...args) => new PythonFinder(...args).findPython();
|
|
@@ -66,12 +66,12 @@ class PythonFinder {
|
|
|
66
66
|
this.configPython = configPython, this.errorLog = [];
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
// 以 verbose
|
|
69
|
+
// 以 verbose 级别记录日志,同时保存下来,出错时以 error 级别展示,便于诊断
|
|
70
70
|
addLog(message) {
|
|
71
71
|
this.log.verbose(message), this.errorLog.push(message);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
// 依次尝试多种 Python
|
|
74
|
+
// 依次尝试多种 Python 定位方式;忽略错误,直到找到可用版本
|
|
75
75
|
async findPython() {
|
|
76
76
|
const SKIP = 0, FAIL = 1,
|
|
77
77
|
toCheck = (() => {
|
|
@@ -158,7 +158,7 @@ class PythonFinder {
|
|
|
158
158
|
return this.fail();
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
// 检查命令是否为可用的 Python
|
|
161
|
+
// 检查命令是否为可用的 Python;
|
|
162
162
|
async checkCommand(command) {
|
|
163
163
|
this.log.verbose(`- executing "${command}" to get executable path`);
|
|
164
164
|
try {
|
|
@@ -171,7 +171,7 @@ class PythonFinder {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
// 检查指定 py 启动器是否能找到 Python 3
|
|
174
|
+
// 检查指定 py 启动器是否能找到 Python 3;
|
|
175
175
|
async checkPyLauncher(launcher = this.pyLauncher) {
|
|
176
176
|
this.log.verbose(`- executing "${launcher}" to get Python 3 executable path`);
|
|
177
177
|
try {
|
|
@@ -184,7 +184,7 @@ class PythonFinder {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
// 通过 "py -0p" 枚举所有已注册的 Python
|
|
187
|
+
// 通过 "py -0p" 枚举所有已注册的 Python,覆盖非标准安装位置;
|
|
188
188
|
async checkPyList() {
|
|
189
189
|
this.log.verbose('- executing "py -0p" to list registered Pythons');
|
|
190
190
|
let lastErr;
|
|
@@ -213,7 +213,7 @@ class PythonFinder {
|
|
|
213
213
|
throw lastErr || new Error('py -0p returned no usable Python');
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
// 检查某个 Python
|
|
216
|
+
// 检查某个 Python 可执行文件版本是否可用;
|
|
217
217
|
async checkExecPath(execPath) {
|
|
218
218
|
this.log.verbose(`- executing "${execPath}" to get version`);
|
|
219
219
|
try {
|
|
@@ -242,8 +242,8 @@ class PythonFinder {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
//
|
|
246
|
-
// 必须显式检查 err
|
|
245
|
+
// 执行外部命令;execFile 包装不 reject,通过 [err, stdout, stderr] 返回结果,
|
|
246
|
+
// 必须显式检查 err,否则失败会被当成"成功但空输出",掩盖真实原因;
|
|
247
247
|
async run(exec, args, shell) {
|
|
248
248
|
const env = Object.assign({}, this.env), opts = { env, shell };
|
|
249
249
|
env.TERM = 'dumb';
|
package/lib/find-visualstudio.js
CHANGED
|
@@ -6,7 +6,7 @@ import { regSearchKeys, execFile } from './util.js';
|
|
|
6
6
|
|
|
7
7
|
const path = nodePath.win32, __dirname = nodePath.dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
/**
|
|
9
|
-
* Visual Studio
|
|
9
|
+
* Visual Studio 查找器:定位可用的 VS 安装
|
|
10
10
|
*/
|
|
11
11
|
class VisualStudioFinder {
|
|
12
12
|
static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio();
|
|
@@ -17,7 +17,7 @@ class VisualStudioFinder {
|
|
|
17
17
|
this.nodeSemver = nodeSemver, this.configMsvsVersion = configMsvsVersion, this.errorLog = [], this.validVersions = [];
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// 以 verbose
|
|
20
|
+
// 以 verbose 级别记录日志,同时保存下来,出错时以 error 级别展示,便于诊断
|
|
21
21
|
addLog(message) {
|
|
22
22
|
this.log.verbose(message), this.errorLog.push(message);
|
|
23
23
|
}
|
|
@@ -66,7 +66,7 @@ class VisualStudioFinder {
|
|
|
66
66
|
this.errorLog.push(
|
|
67
67
|
'msvs_version does not match this VS Command Prompt or the', 'installation cannot be used.');
|
|
68
68
|
else if (this.configMsvsVersion) {
|
|
69
|
-
// 指定了 msvs_version 但没找到 VS
|
|
69
|
+
// 指定了 msvs_version 但没找到 VS,输出可接受的版本
|
|
70
70
|
this.errorLog.push('');
|
|
71
71
|
if (this.validVersions) {
|
|
72
72
|
this.errorLog.push('valid versions for msvs_version:');
|
|
@@ -76,7 +76,7 @@ class VisualStudioFinder {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const errorLog = this.errorLog.join('\n'),
|
|
79
|
-
// Windows 80
|
|
79
|
+
// Windows 80 列控制台,用到 X 标记列之前(含日志前缀共 79 字符,可用 62 字符):
|
|
80
80
|
infoLog = [
|
|
81
81
|
'**************************************************************',
|
|
82
82
|
'You need to install the latest version of Visual Studio',
|
|
@@ -90,7 +90,7 @@ class VisualStudioFinder {
|
|
|
90
90
|
throw new Error('未找到可用的 Visual Studio 安装');
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
// 调用 PowerShell
|
|
93
|
+
// 调用 PowerShell 脚本,获取 Visual Studio 2017 或更新版本的信息
|
|
94
94
|
async findVisualStudio2017OrNewer() {
|
|
95
95
|
const ps = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'),
|
|
96
96
|
csFile = path.join(__dirname, 'Find-VisualStudio.cs'),
|
|
@@ -102,7 +102,7 @@ class VisualStudioFinder {
|
|
|
102
102
|
return this.parseData(err, stdout, stderr);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
// 解析 PowerShell
|
|
105
|
+
// 解析 PowerShell 脚本输出,寻找可用的 Visual Studio 2017 或更新版本
|
|
106
106
|
parseData(err, stdout, stderr) {
|
|
107
107
|
this.log.silly('PS stderr = %j', stderr)
|
|
108
108
|
|
|
@@ -149,7 +149,7 @@ class VisualStudioFinder {
|
|
|
149
149
|
return false;
|
|
150
150
|
})
|
|
151
151
|
|
|
152
|
-
//
|
|
152
|
+
// 排序:新版本在前
|
|
153
153
|
vsInfo.sort((a, b) => b.versionYear - a.versionYear)
|
|
154
154
|
|
|
155
155
|
for (let i = 0; i < vsInfo.length; ++i) {
|
|
@@ -182,7 +182,7 @@ class VisualStudioFinder {
|
|
|
182
182
|
return null;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
//
|
|
185
|
+
// 辅助:解析版本信息
|
|
186
186
|
getVersionInfo(info) {
|
|
187
187
|
const match = /^(\d+)\.(\d+)\./.exec(info.version);
|
|
188
188
|
if (!match) {
|
|
@@ -206,7 +206,7 @@ class VisualStudioFinder {
|
|
|
206
206
|
return existsSync(path);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
//
|
|
209
|
+
// 辅助:解析 MSBuild 信息
|
|
210
210
|
getMSBuild(info, versionYear) {
|
|
211
211
|
const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base',
|
|
212
212
|
msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe'),
|
|
@@ -217,16 +217,16 @@ class VisualStudioFinder {
|
|
|
217
217
|
if (versionYear === 2019) return msbuildPath;
|
|
218
218
|
}
|
|
219
219
|
/**
|
|
220
|
-
* Visual Studio 2022 没有 MSBuild
|
|
221
|
-
* ARM64 上编译的支持从 MSVC 14.32.31326
|
|
222
|
-
* 因此用户设备是 ARM64
|
|
220
|
+
* Visual Studio 2022 没有 MSBuild 包;
|
|
221
|
+
* ARM64 上编译的支持从 MSVC 14.32.31326 起加入,
|
|
222
|
+
* 因此用户设备是 ARM64 时优先使用它;
|
|
223
223
|
*/
|
|
224
224
|
if (process.arch === 'arm64' && this.msBuildPathExists(msbuildPathArm64)) return msbuildPathArm64;
|
|
225
225
|
else if (this.msBuildPathExists(msbuildPath)) return msbuildPath;
|
|
226
226
|
return null;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
//
|
|
229
|
+
// 辅助:解析 toolset 信息
|
|
230
230
|
getToolset(info, versionYear) {
|
|
231
231
|
const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', express = 'Microsoft.VisualStudio.WDExpress';
|
|
232
232
|
if (info.packages.indexOf(pkg) !== -1) this.log.silly('- found VC.Tools.x86.x64');
|
|
@@ -241,7 +241,7 @@ class VisualStudioFinder {
|
|
|
241
241
|
return null;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
//
|
|
244
|
+
// 辅助:解析 Windows SDK 信息
|
|
245
245
|
getSDK(info) {
|
|
246
246
|
const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK',
|
|
247
247
|
win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.',
|
|
@@ -291,7 +291,7 @@ class VisualStudioFinder {
|
|
|
291
291
|
return this.findOldVS({ version: '12.0', versionMajor: 12, versionMinor: 0, versionYear: 2013, toolset: 'v120' });
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
//
|
|
294
|
+
// 辅助:VS2013 和 VS2015 的公共代码
|
|
295
295
|
async findOldVS(info) {
|
|
296
296
|
const regVC7 = ['HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7',
|
|
297
297
|
'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7'],
|
|
@@ -322,10 +322,10 @@ class VisualStudioFinder {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
// 找到可用的 Visual Studio
|
|
326
|
-
// - 把它加入 validVersions
|
|
327
|
-
// -
|
|
328
|
-
// - 检查是否匹配 Visual Studio
|
|
325
|
+
// 找到可用的 Visual Studio 后:
|
|
326
|
+
// - 把它加入 validVersions,用于在指定版本未找到时输出;
|
|
327
|
+
// - 检查是否是请求的版本;
|
|
328
|
+
// - 检查是否匹配 Visual Studio 命令提示符;
|
|
329
329
|
checkConfigVersion(versionYear, vsPath) {
|
|
330
330
|
this.validVersions.push(versionYear), this.validVersions.push(vsPath);
|
|
331
331
|
|
package/lib/install.js
CHANGED
|
@@ -17,15 +17,15 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
17
17
|
install = async (gyp, argv) => {
|
|
18
18
|
log.stdout();
|
|
19
19
|
const release = processRelease(argv, gyp, process.version, process.release),
|
|
20
|
-
// 按 create-config-gypi.js 的逻辑推断 target_arch
|
|
20
|
+
// 按 create-config-gypi.js 的逻辑推断 target_arch,仅 Windows 用
|
|
21
21
|
arch = win ? (gyp.opts.target_arch || gyp.opts.arch || process.arch || 'ia32') : '';
|
|
22
|
-
let shouldDownloadTarball = true; // 仅 Windows 下只需新 node.lib
|
|
22
|
+
let shouldDownloadTarball = true; // 仅 Windows 下只需新 node.lib 时,避免下载 tarball
|
|
23
23
|
|
|
24
24
|
// 确定要安装哪个版本的 Node 开发文件
|
|
25
25
|
log.verbose('install', 'input version string %j', release.version);
|
|
26
26
|
if (!release.semver) throw new Error('版本号无效: ' + release.version); // 无法用 semver 解析版本字符串
|
|
27
|
-
if (semver.lt(release.version, '0.8.0')) throw new Error('目标版本最低为 `0.8.0
|
|
28
|
-
// 0.x.y-pre
|
|
27
|
+
if (semver.lt(release.version, '0.8.0')) throw new Error('目标版本最低为 `0.8.0`,当前: ' + release.version);
|
|
28
|
+
// 0.x.y-pre 版本尚未发布,无法安装,直接退出
|
|
29
29
|
if (release.semver.prerelease[0] === 'pre') {
|
|
30
30
|
log.verbose('detected "pre" node version', release.version);
|
|
31
31
|
if (!gyp.opts.nodedir) throw new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead');
|
|
@@ -51,13 +51,13 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
51
51
|
for (const entry of entries) {
|
|
52
52
|
if (entry.isDirectory()) await copyDirectory(path.join(src, entry.name), path.join(dest, entry.name));
|
|
53
53
|
else if (entry.isFile()) {
|
|
54
|
-
//
|
|
54
|
+
// 并行安装时,Windows 上复制文件可能报文件错误,
|
|
55
55
|
// 因此用指数退避解决冲突
|
|
56
56
|
await backOff(async () => {
|
|
57
57
|
try {
|
|
58
58
|
await fs.copyFile(path.join(src, entry.name), path.join(dest, entry.name));
|
|
59
59
|
} catch (err) {
|
|
60
|
-
// 若设置了 ensure
|
|
60
|
+
// 若设置了 ensure,检查文件是否已存在,存在就足够了
|
|
61
61
|
if (gyp.opts.ensure && err.code === 'EBUSY') {
|
|
62
62
|
try {
|
|
63
63
|
await fs.stat(path.join(dest, entry.name));
|
|
@@ -72,16 +72,16 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
rollback = async err => {
|
|
75
|
-
log.warn('install', '
|
|
75
|
+
log.warn('install', '发生错误,正在回滚安装')
|
|
76
76
|
await gyp.commands.remove([release.versionDir]);
|
|
77
77
|
throw err;
|
|
78
78
|
},
|
|
79
79
|
/**
|
|
80
|
-
* EACCES
|
|
81
|
-
* npm
|
|
82
|
-
* 于是 "nobody" 用户没有权限创建 dev dir
|
|
83
|
-
*
|
|
84
|
-
*
|
|
80
|
+
* EACCES 回退方案,用于处理 npm 的 `sudo` 行为:
|
|
81
|
+
* npm 在调用任何子进程(如 node-gyp)前会先降权,
|
|
82
|
+
* 于是 "nobody" 用户没有权限创建 dev dir;
|
|
83
|
+
* 作为回退,把 tmpdir() 作为本次安装的 dev dir;
|
|
84
|
+
* 这不理想,但至少编译能成功......
|
|
85
85
|
*/
|
|
86
86
|
eaccesFallback = async err => {
|
|
87
87
|
const noretry = '--node_gyp_internal_noretry';
|
|
@@ -90,7 +90,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
90
90
|
gyp.devDir = path.resolve(tmpdir, '.node-gyp');
|
|
91
91
|
let userString = '';
|
|
92
92
|
try {
|
|
93
|
-
userString = ` ("${os.userInfo().username}")`; // os.userInfo
|
|
93
|
+
userString = ` ("${os.userInfo().username}")`; // os.userInfo 在某些系统上会失败,这里不关键
|
|
94
94
|
} catch (e) { }
|
|
95
95
|
log.warn('EACCES', '当前用户%s 没有权限访问 dev dir "%s"', userString, devDir);
|
|
96
96
|
log.warn('EACCES', '尝试用临时 dev dir 重新安装 "%s"', gyp.devDir);
|
|
@@ -115,7 +115,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
115
115
|
// 下载 Node tarball
|
|
116
116
|
let extractErrors = false, extractCount = 0;
|
|
117
117
|
const tarPath = gyp.opts.tarball, contentShasums = {}, expectShasums = {},
|
|
118
|
-
// 检查从 tarball
|
|
118
|
+
// 检查从 tarball 解出的文件是否有效;
|
|
119
119
|
// 只有 .h 头文件和 gyp 文件会被解出
|
|
120
120
|
isValid = path => {
|
|
121
121
|
const isValid = valid(path);
|
|
@@ -127,10 +127,10 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
127
127
|
onwarn = (code, message) => {
|
|
128
128
|
extractErrors = true, log.error('error while extracting tarball', code, message);
|
|
129
129
|
},
|
|
130
|
-
// 下载并解压 tarball
|
|
130
|
+
// 下载并解压 tarball!
|
|
131
131
|
// 仅 Windows 上只需新 node.lib 时跳过
|
|
132
|
-
// Windows
|
|
133
|
-
// 所以先把 tarball
|
|
132
|
+
// Windows 上并行安装时(多个原生模块,很常见)tar 会报文件错误,
|
|
133
|
+
// 所以先把 tarball 解到临时目录,再复制过去
|
|
134
134
|
tarExtractDir = win ? await fs.mkdtemp(path.join(os.tmpdir(), 'node-gyp-tmp-')) : devDir,
|
|
135
135
|
downloadShasums = async () => {
|
|
136
136
|
log.verbose('check download content checksum, need to download `SHASUMS256.txt`...');
|
|
@@ -159,7 +159,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
159
159
|
await fs.mkdir(dir, { recursive: true }), log.verbose('streaming', name, 'to:', targetLibPath);
|
|
160
160
|
|
|
161
161
|
const res = await download(gyp, libUrl);
|
|
162
|
-
// 只下载所需的 node.lib
|
|
162
|
+
// 只下载所需的 node.lib,若未拿到则抛错
|
|
163
163
|
if (res.status !== 200) throw new Error(`${res.status} status code downloading ${name}`);
|
|
164
164
|
|
|
165
165
|
return pipeline(
|
|
@@ -188,9 +188,10 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
188
188
|
tar.extract({ strip: 1, cwd: tarExtractDir, filter: isValid, onwarn })
|
|
189
189
|
)
|
|
190
190
|
} catch (err) {
|
|
191
|
-
// tarball
|
|
191
|
+
// tarball 下载出问题?
|
|
192
192
|
if (err.code === 'ENOTFOUND')
|
|
193
|
-
|
|
193
|
+
throw new Error('这通常不是 node-gyp 或包本身的问题,而是网络问题;\n' +
|
|
194
|
+
'通常是因为你在代理后,或网络设置异常;');
|
|
194
195
|
throw err;
|
|
195
196
|
}
|
|
196
197
|
}
|
|
@@ -232,7 +233,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
232
233
|
}
|
|
233
234
|
};
|
|
234
235
|
|
|
235
|
-
// 如果传了 --ensure
|
|
236
|
+
// 如果传了 --ensure,则不总是安装,只在需要时安装
|
|
236
237
|
if (gyp.opts.ensure) {
|
|
237
238
|
log.verbose('install', '--ensure was passed, so won\'t reinstall if already installed');
|
|
238
239
|
try {
|
package/lib/list.js
CHANGED
package/lib/log.js
CHANGED
|
@@ -18,7 +18,7 @@ const logLevels = Object.keys(procLog.log).filter((k) => typeof procLog.log[k] =
|
|
|
18
18
|
bg: { black: 40, red: 41, green: 42, yellow: 43, blue: 44, magenta: 45, cyan: 46, white: 47 }
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
//
|
|
21
|
+
// 日志器:负责缓冲、暂停/恢复、着色输出
|
|
22
22
|
class Logger {
|
|
23
23
|
#buffer = [];
|
|
24
24
|
#paused = null;
|
|
@@ -105,6 +105,6 @@ class Logger {
|
|
|
105
105
|
// 用于测试时抑制日志
|
|
106
106
|
const NULL_LOGGER = !!process.env.NODE_GYP_NULL_LOGGER, logger = new Logger(NULL_LOGGER ? null : process.stderr),
|
|
107
107
|
stdout = NULL_LOGGER ? () => { } : (...args) => console.log(...args),
|
|
108
|
-
{ notice, error, warn, info, verbose, http, silly, pause, resume } = procLog.log; // 解构 proc-log
|
|
108
|
+
{ notice, error, warn, info, verbose, http, silly, pause, resume } = procLog.log; // 解构 proc-log 的函数,供末尾统一导出
|
|
109
109
|
|
|
110
110
|
export { logger, stdout, withPrefix, notice, error, warn, info, verbose, http, silly, pause, resume }
|