@flun/nodejs-mobile-gyp 1.1.3 → 1.2.0
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 +23 -26
- package/LICENSE +9 -4
- package/README.md +291 -229
- package/addon.gypi +1 -1
- package/bin/node-gyp.js +15 -6
- 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 +17 -17
- package/lib/clean.js +3 -3
- package/lib/configure.js +21 -18
- package/lib/create-config-gypi.js +9 -9
- package/lib/download.js +4 -4
- package/lib/find-node-directory.js +10 -10
- package/lib/find-python.js +14 -14
- package/lib/find-visualstudio.js +20 -20
- package/lib/install.js +23 -22
- package/lib/list.js +2 -2
- package/lib/log.js +2 -2
- package/lib/node-gyp.js +37 -11
- package/lib/process-release.js +14 -14
- package/lib/rebuild.js +2 -2
- package/lib/remove.js +3 -3
- package/lib/util.js +72 -71
- package/package.json +3 -2
- package/src/win_delay_load_hook.cc +1 -1
package/lib/install.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import gracefulFs from 'graceful-fs';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import { backOff } from 'exponential-backoff';
|
|
4
4
|
import * as tar from 'tar';
|
|
@@ -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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import gracefulFs from 'graceful-fs';
|
|
2
2
|
import * as log from './log.js';
|
|
3
3
|
|
|
4
4
|
const fs = gracefulFs.promises,
|
|
@@ -21,4 +21,4 @@ const fs = gracefulFs.promises,
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
list.usage = 'Prints a listing of the currently installed node development files';
|
|
24
|
-
export { list }
|
|
24
|
+
export { list }
|
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 }
|
package/lib/node-gyp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import path from 'path';
|
|
2
2
|
import nopt from 'nopt';
|
|
3
|
+
import chalk from 'chalk';
|
|
3
4
|
import * as log from './log.js';
|
|
4
5
|
import childProcess from 'child_process';
|
|
5
6
|
import { EventEmitter } from 'events';
|
|
@@ -15,11 +16,11 @@ import { list } from './list.js';
|
|
|
15
16
|
import { remove } from './remove.js';
|
|
16
17
|
|
|
17
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url)),
|
|
18
|
-
//
|
|
19
|
+
// 命令名到实现函数的静态映射(ESM 不支持动态 require)
|
|
19
20
|
commandModules = { build, clean, configure, rebuild, install, list, remove }, commands = Object.keys(commandModules);
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* node-gyp
|
|
23
|
+
* node-gyp 主类:解析参数、分发命令、生成 usage
|
|
23
24
|
*/
|
|
24
25
|
class Gyp extends EventEmitter {
|
|
25
26
|
/**
|
|
@@ -39,7 +40,7 @@ class Gyp extends EventEmitter {
|
|
|
39
40
|
make: String, // 'build'
|
|
40
41
|
'msvs-version': String, // 'configure'
|
|
41
42
|
ensure: Boolean, // 'install'
|
|
42
|
-
solution: String, // 'build'
|
|
43
|
+
solution: String, // 'build'(仅 Windows)
|
|
43
44
|
proxy: String, // 'install'
|
|
44
45
|
noproxy: String, // 'install'
|
|
45
46
|
devdir: String, // 各处
|
|
@@ -50,6 +51,7 @@ class Gyp extends EventEmitter {
|
|
|
50
51
|
tarball: String, // 'install'
|
|
51
52
|
jobs: String, // 'build'
|
|
52
53
|
thin: String, // 'configure'
|
|
54
|
+
target: String, // 'rebuild'、'remove'、'install' 等处
|
|
53
55
|
'force-process-config': Boolean // 'configure'
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -83,7 +85,7 @@ class Gyp extends EventEmitter {
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
/**
|
|
86
|
-
* 解析传入的 argv
|
|
88
|
+
* 解析传入的 argv 数组,设置 'opts'、'argv' 和 'command' 属性
|
|
87
89
|
*/
|
|
88
90
|
parseArgv(argv) {
|
|
89
91
|
this.opts = nopt(this.configDefs, this.shorthands, argv), this.argv = this.opts.argv.remain.slice();
|
|
@@ -91,12 +93,12 @@ class Gyp extends EventEmitter {
|
|
|
91
93
|
const commands = this.todo = [];
|
|
92
94
|
// 创建一份把别名映射后的 argv 副本
|
|
93
95
|
argv = this.argv.map(arg => {
|
|
94
|
-
//
|
|
96
|
+
// 如果当前参数是命令别名(如 ls/rm),就替换成真实命令名
|
|
95
97
|
if (arg in this.aliases) arg = this.aliases[arg];
|
|
96
98
|
return arg
|
|
97
99
|
})
|
|
98
100
|
|
|
99
|
-
// 把映射后的参数处理成 "command"
|
|
101
|
+
// 把映射后的参数处理成 "command" 对象(name 和 args 属性)
|
|
100
102
|
argv.slice().forEach(arg => {
|
|
101
103
|
if (arg in this.commands) {
|
|
102
104
|
const args = argv.splice(0, argv.indexOf(arg));
|
|
@@ -115,7 +117,7 @@ class Gyp extends EventEmitter {
|
|
|
115
117
|
if (name === npmConfigPrefix + 'loglevel') log.logger.level = val
|
|
116
118
|
else {
|
|
117
119
|
name = name.substring(npmConfigPrefix.length); // 把用户定义的选项加入配置
|
|
118
|
-
// gyp@741b7f1
|
|
120
|
+
// gyp@741b7f1 遇到零长度选项会死循环,确保过滤掉
|
|
119
121
|
if (name) {
|
|
120
122
|
// 把 force_process_config 这类名字转成 force-process-config
|
|
121
123
|
if (name.includes('_')) name = name.replace(/_/g, '-');
|
|
@@ -143,10 +145,34 @@ class Gyp extends EventEmitter {
|
|
|
143
145
|
* 返回 node-gyp 的用法说明
|
|
144
146
|
*/
|
|
145
147
|
usage() {
|
|
148
|
+
const gold = chalk.hex('#B8860B'),
|
|
149
|
+
wide = cp => (cp >= 0x1100 && cp <= 0x115F) || (cp >= 0x2E80 && cp <= 0xA4CF) ||
|
|
150
|
+
(cp >= 0xAC00 && cp <= 0xD7A3) || (cp >= 0xF900 && cp <= 0xFAFF) ||
|
|
151
|
+
(cp >= 0xFE30 && cp <= 0xFE4F) || (cp >= 0xFF00 && cp <= 0xFF60) || (cp >= 0xFFE0 && cp <= 0xFFE6),
|
|
152
|
+
width = s => [...s].reduce((n, ch) => n + (wide(ch.codePointAt(0)) ? 2 : 1), 0),
|
|
153
|
+
pad = (s, n) => s + ' '.repeat(Math.max(0, n - width(s))),
|
|
154
|
+
cmd = (name, desc) => ` ${chalk.cyanBright(pad(name, 19))}${chalk.gray(desc)}`,
|
|
155
|
+
opt = (flag, desc) => ` ${chalk.cyanBright(pad(flag, 19))}${chalk.gray(desc)}`;
|
|
146
156
|
return [
|
|
147
|
-
''
|
|
148
|
-
|
|
149
|
-
'
|
|
157
|
+
`${chalk.blueBright('用法:')} nodejs-mobile-gyp <命令> [选项]`, '',
|
|
158
|
+
` ${chalk.cyan('命令:')}`,
|
|
159
|
+
cmd('build', '构建模块(Windows 调 msbuild,其它平台调 make)'),
|
|
160
|
+
cmd('clean', '清理生成文件与 out 目录'),
|
|
161
|
+
cmd('configure', '为当前模块生成项目文件(Windows 为 MSVC 项目,其它平台为 Makefile)'),
|
|
162
|
+
cmd('rebuild', '依次执行 clean、configure、build'),
|
|
163
|
+
cmd('install', '安装指定 Node 版本的开发文件'),
|
|
164
|
+
cmd('list', '列出已安装的 Node 开发文件;别名 ls'),
|
|
165
|
+
cmd('remove', '移除指定版本的 Node 开发文件;别名 rm'), '',
|
|
166
|
+
` ${chalk.cyan('选项:')}`,
|
|
167
|
+
opt('-C <路径>', '切换到该目录后执行;完整写法 --directory'),
|
|
168
|
+
opt('--devdir <路径>', '指定开发文件缓存目录,默认取系统缓存目录'),
|
|
169
|
+
opt('-j <数量>', '并行构建任务数,仅 build;完整写法 --jobs'),
|
|
170
|
+
opt('--target <版本>', '目标 Node.js 版本,默认当前主机版本'),
|
|
171
|
+
opt('--arch <架构>', '目标架构,如 arm64、ia32'),
|
|
172
|
+
opt('--debug', '输出详细日志'),
|
|
173
|
+
opt('-v', '输出版本号;完整写法 --version'), '',
|
|
174
|
+
` ${chalk.gray('无参数、help、-h、--help 输出本帮助。')}`, '',
|
|
175
|
+
`${gold('nodejs-mobile-gyp@' + this.version)} ${gold('node@' + process.versions.node)}`
|
|
150
176
|
].join('\n');
|
|
151
177
|
}
|
|
152
178
|
}
|
package/lib/process-release.js
CHANGED
|
@@ -4,7 +4,7 @@ import semver from 'semver';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import * as log from './log.js';
|
|
6
6
|
|
|
7
|
-
// 开始发布 -headers.tar.gz 的版本(io.js v3.x.x 里用的是 "ia32"
|
|
7
|
+
// 开始发布 -headers.tar.gz 的版本(io.js v3.x.x 里用的是 "ia32",本应是 "x86")
|
|
8
8
|
const headersTarballRange = '>= 3.0.0 || ~0.12.10 || ~0.10.42', bitsre = /\/win-(x86|x64|arm64)\//,
|
|
9
9
|
bitsreV3 = /\/win-(x86|ia32|x64)\//, normalizePath = (p) => path.normalize(p).replace(/\\/g, '/'), // 统一分隔符为 /
|
|
10
10
|
/**
|
|
@@ -17,16 +17,16 @@ const headersTarballRange = '>= 3.0.0 || ~0.12.10 || ~0.10.42', bitsre = /\/win-
|
|
|
17
17
|
if (!hasLibUrl) {
|
|
18
18
|
// 假设传入的是 baseUrl
|
|
19
19
|
if (versionMajor >= 1) return new URL('win-' + arch + '/' + name + '.lib', base).href;
|
|
20
|
-
// io.js@1.0.0
|
|
20
|
+
// io.js@1.0.0 之前,32 位 node.lib 在根目录,64 位在 /x64/
|
|
21
21
|
const prefix = arch === 'x86' ? '' : arch + '/';
|
|
22
22
|
return new URL(prefix + name + '.lib', base).href;
|
|
23
23
|
}
|
|
24
|
-
// 否则传入的已经是 .lib
|
|
24
|
+
// 否则传入的已经是 .lib 地址,只需保证架构正确
|
|
25
25
|
return defaultUrl.replace(versionMajor === 3 ? bitsreV3 : bitsre, '/win-' + arch + '/');
|
|
26
26
|
},
|
|
27
27
|
/**
|
|
28
|
-
* 负责确定下载 URL
|
|
29
|
-
*
|
|
28
|
+
* 负责确定下载 URL、本地目录和文件名;
|
|
29
|
+
* 输入来自命令行开关(--target、--dist-url)、process.version 和 process.release;
|
|
30
30
|
*/
|
|
31
31
|
processRelease = (argv, gyp, defaultVersion, defaultRelease) => {
|
|
32
32
|
let version = (semver.valid(argv[0]) && argv[0]) || gyp.opts.target || defaultVersion
|
|
@@ -37,18 +37,18 @@ const headersTarballRange = '>= 3.0.0 || ~0.12.10 || ~0.10.42', bitsre = /\/win-
|
|
|
37
37
|
if (!versionSemver) return { version }; // 如果 semver 不合法,那么原样返回 version
|
|
38
38
|
version = versionSemver.version; // 把 version 拍平为字符串
|
|
39
39
|
|
|
40
|
-
// defaultVersion 来自 process.version
|
|
40
|
+
// defaultVersion 来自 process.version,应当是合法 semver
|
|
41
41
|
const isDefaultVersion = version === semver.parse(defaultVersion).version;
|
|
42
42
|
if (!isDefaultVersion) defaultRelease = null; // 用了 --target=x.y.z 就不能用 process.release
|
|
43
43
|
if (defaultRelease) name = defaultRelease.name.replace(/io\.js/, 'iojs'); // 去掉 '.' 以便目录命名
|
|
44
44
|
else {
|
|
45
45
|
// 老版本 Node 或自定义 --target
|
|
46
|
-
// semver.satisfies()
|
|
46
|
+
// semver.satisfies() 不支持预发布标签,因此直接判断主版本
|
|
47
47
|
isNamedForLegacyIojs = versionSemver.major >= 1 && versionSemver.major < 4;
|
|
48
|
-
// isNamedForLegacyIojs 是为了支持 Electron < 4
|
|
49
|
-
// 早期逻辑用它保证 iojs 版本下载 "iojs"
|
|
50
|
-
//
|
|
51
|
-
// Electron@3 EOL
|
|
48
|
+
// isNamedForLegacyIojs 是为了支持 Electron < 4(特别是 Electron 3)
|
|
49
|
+
// 早期逻辑用它保证 iojs 版本下载 "iojs",Node 版本下载 "node";
|
|
50
|
+
// 但该逻辑过宽,导致 electron@3 的发布物命名为 "iojs" 才能让 node-gyp 正常工作;
|
|
51
|
+
// Electron@3 EOL 之后(2019 年底)应移除这个 hack;
|
|
52
52
|
name = isNamedForLegacyIojs ? 'iojs' : 'node';
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -59,7 +59,7 @@ const headersTarballRange = '>= 3.0.0 || ~0.12.10 || ~0.10.42', bitsre = /\/win-
|
|
|
59
59
|
else distBaseUrl = 'https://nodejs.org/dist';
|
|
60
60
|
distBaseUrl += '/v' + version + '/'
|
|
61
61
|
|
|
62
|
-
//
|
|
62
|
+
// 新式:基于 process.release,大部分数据已有
|
|
63
63
|
if (defaultRelease?.headersUrl && !overrideDistUrl) {
|
|
64
64
|
baseUrl = new URL('./', defaultRelease.headersUrl).href;
|
|
65
65
|
libUrl32 = resolveLibUrl(name, defaultRelease.libUrl || baseUrl || distBaseUrl, 'x86', versionSemver.major);
|
|
@@ -67,14 +67,14 @@ const headersTarballRange = '>= 3.0.0 || ~0.12.10 || ~0.10.42', bitsre = /\/win-
|
|
|
67
67
|
libUrlArm64 = resolveLibUrl(name, defaultRelease.libUrl || baseUrl || distBaseUrl, 'arm64', versionSemver.major);
|
|
68
68
|
tarballUrl = defaultRelease.headersUrl;
|
|
69
69
|
} else {
|
|
70
|
-
// 没有 process.release
|
|
70
|
+
// 没有 process.release 的老版本,需要做很多假设;
|
|
71
71
|
// 另外 --target=x.y.z 时也无法使用当前 process.release
|
|
72
72
|
baseUrl = distBaseUrl;
|
|
73
73
|
libUrl32 = resolveLibUrl(name, baseUrl, 'x86', versionSemver.major);
|
|
74
74
|
libUrl64 = resolveLibUrl(name, baseUrl, 'x64', versionSemver.major);
|
|
75
75
|
libUrlArm64 = resolveLibUrl(name, baseUrl, 'arm64', versionSemver.major);
|
|
76
76
|
|
|
77
|
-
//
|
|
77
|
+
// 大胆假设:任何版本号 >3.0.0 的版本,其 dist 目录都有 *-headers.tar.gz
|
|
78
78
|
canGetHeaders = semver.satisfies(versionSemver, headersTarballRange);
|
|
79
79
|
tarballUrl = new URL(name + '-v' + version + (canGetHeaders ? '-headers' : '') + '.tar.gz', baseUrl).href;
|
|
80
80
|
}
|
package/lib/rebuild.js
CHANGED
package/lib/remove.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import gracefulFs from 'graceful-fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import * as log from './log.js';
|
|
4
4
|
import semver from 'semver';
|
|
@@ -14,7 +14,7 @@ const fs = gracefulFs.promises,
|
|
|
14
14
|
// 获取用户指定的版本号
|
|
15
15
|
let version = argv[0] || gyp.opts.target;
|
|
16
16
|
log.verbose('remove', 'removing target version:', version);
|
|
17
|
-
if (!version) throw new Error('
|
|
17
|
+
if (!version) throw new Error('必须指定要删除的版本号,例如: "' + process.version + '"');
|
|
18
18
|
|
|
19
19
|
const versionSemver = semver.parse(version);
|
|
20
20
|
if (versionSemver) version = versionSemver.version; // 把版本数组拍平为字符串
|
|
@@ -34,4 +34,4 @@ const fs = gracefulFs.promises,
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
remove.usage = 'Removes the node development files for the specified version';
|
|
37
|
-
export { remove }
|
|
37
|
+
export { remove }
|
package/lib/util.js
CHANGED
|
@@ -1,72 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import gracefulFs from 'graceful-fs';
|
|
4
|
-
import * as log from './log.js';
|
|
5
|
-
|
|
6
|
-
const { openSync, closeSync } = gracefulFs,
|
|
7
|
-
/**
|
|
8
|
-
* 执行外部命令并返回 [err, stdout, stderr]
|
|
9
|
-
*/
|
|
10
|
-
execFile = async (...args) => new Promise(resolve => {
|
|
11
|
-
const child = cp.execFile(...args, (...a) => resolve(a));
|
|
12
|
-
child.stdin.end();
|
|
13
|
-
}),
|
|
14
|
-
/**
|
|
15
|
-
* 读取 Windows 注册表指定键的值
|
|
16
|
-
*/
|
|
17
|
-
regGetValue = async (key, value, addOpts) => {
|
|
18
|
-
const outReValue = value.replace(/\W/g, '.'), outRe = new RegExp(`^\\s+${outReValue}\\s+REG_\\w+\\s+(\\S.*)$`, 'im'),
|
|
19
|
-
reg = path.join(process.env.SystemRoot, 'System32', 'reg.exe'),
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
log.silly('reg', 'reg.exe
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
1
|
+
import cp from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import gracefulFs from 'graceful-fs';
|
|
4
|
+
import * as log from './log.js';
|
|
5
|
+
|
|
6
|
+
const { openSync, closeSync } = gracefulFs,
|
|
7
|
+
/**
|
|
8
|
+
* 执行外部命令并返回 [err, stdout, stderr]
|
|
9
|
+
*/
|
|
10
|
+
execFile = async (...args) => new Promise(resolve => {
|
|
11
|
+
const child = cp.execFile(...args, (...a) => resolve(a));
|
|
12
|
+
child.stdin.end();
|
|
13
|
+
}),
|
|
14
|
+
/**
|
|
15
|
+
* 读取 Windows 注册表指定键的值
|
|
16
|
+
*/
|
|
17
|
+
regGetValue = async (key, value, addOpts) => {
|
|
18
|
+
const outReValue = value.replace(/\W/g, '.'), outRe = new RegExp(`^\\s+${outReValue}\\s+REG_\\w+\\s+(\\S.*)$`, 'im'),
|
|
19
|
+
reg = path.join(process.env.SystemRoot, 'System32', 'reg.exe'),
|
|
20
|
+
regArgs = ['query', key, '/v', value].concat(addOpts);
|
|
21
|
+
|
|
22
|
+
log.silly('reg', 'running', reg, regArgs);
|
|
23
|
+
const [err, stdout, stderr] = await execFile(reg, regArgs, { encoding: 'utf8' });
|
|
24
|
+
|
|
25
|
+
log.silly('reg', 'reg.exe stdout = %j', stdout);
|
|
26
|
+
if (err || stderr.trim() !== '') {
|
|
27
|
+
log.silly('reg', 'reg.exe err = %j', err && (err.stack || err));
|
|
28
|
+
log.silly('reg', 'reg.exe stderr = %j', stderr);
|
|
29
|
+
if (err) throw err;
|
|
30
|
+
throw new Error(stderr);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = outRe.exec(stdout)
|
|
34
|
+
if (!result) {
|
|
35
|
+
log.silly('reg', 'error parsing stdout');
|
|
36
|
+
throw new Error('无法解析 reg.exe 的输出');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
log.silly('reg', 'found: %j', result[1]);
|
|
40
|
+
return result[1];
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* 依次搜索多个注册表键,返回第一个可读的值
|
|
44
|
+
*/
|
|
45
|
+
regSearchKeys = async (keys, value, addOpts) => {
|
|
46
|
+
for (const key of keys) {
|
|
47
|
+
try {
|
|
48
|
+
return await regGetValue(key, value, addOpts);
|
|
49
|
+
} catch { continue }
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* 返回候选列表中第一个当前用户可读的文件或目录,都不存在则返回 undefined
|
|
54
|
+
*/
|
|
55
|
+
findAccessibleSync = (logprefix, dir, candidates) => {
|
|
56
|
+
for (let next = 0; next < candidates.length; next++) {
|
|
57
|
+
const candidate = path.resolve(dir, candidates[next]);
|
|
58
|
+
let fd;
|
|
59
|
+
try {
|
|
60
|
+
fd = openSync(candidate, 'r');
|
|
61
|
+
} catch (e) {
|
|
62
|
+
// 该候选文件不存在或不可读,继续检查下一个
|
|
63
|
+
log.silly(logprefix, 'Could not open %s: %s', candidate, e.message);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
closeSync(fd), log.silly(logprefix, 'Found readable %s', candidate);
|
|
67
|
+
return candidate;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
72
73
|
export { execFile, regGetValue, regSearchKeys, findAccessibleSync }
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"bindings",
|
|
12
12
|
"gyp"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.2.0",
|
|
15
15
|
"installVersion": 11,
|
|
16
16
|
"author": "flun <open@flun.top>",
|
|
17
17
|
"bin": {
|
|
@@ -39,9 +39,10 @@
|
|
|
39
39
|
"LICENSE"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"chalk": "^6.0.1",
|
|
42
43
|
"env-paths": "^4.0.0",
|
|
43
44
|
"exponential-backoff": "^3.1.3",
|
|
44
|
-
"glob": "^
|
|
45
|
+
"glob": "^14.0.3",
|
|
45
46
|
"graceful-fs": "^4.2.11",
|
|
46
47
|
"make-fetch-happen": "^16.0.1",
|
|
47
48
|
"nopt": "^10.0.1",
|