@flun/nodejs-mobile-gyp 1.2.0 → 1.2.1
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 +26 -5
- package/LICENSE +33 -33
- package/README.md +62 -63
- package/bin/node-gyp.js +22 -24
- package/lib/Find-VisualStudio.cs +2 -1
- package/lib/build.js +25 -35
- package/lib/clean.js +2 -2
- package/lib/configure.js +25 -25
- package/lib/create-config-gypi.js +3 -3
- package/lib/find-node-directory.js +4 -4
- package/lib/find-python.js +44 -44
- package/lib/find-visualstudio.js +52 -52
- package/lib/install.js +41 -41
- package/lib/list.js +2 -2
- package/lib/node-gyp.js +22 -22
- package/lib/process-release.js +1 -1
- package/lib/rebuild.js +1 -1
- package/lib/remove.js +5 -5
- package/lib/util.js +5 -5
- package/package.json +7 -8
package/lib/find-visualstudio.js
CHANGED
|
@@ -11,7 +11,7 @@ const path = nodePath.win32, __dirname = nodePath.dirname(fileURLToPath(import.m
|
|
|
11
11
|
class VisualStudioFinder {
|
|
12
12
|
static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio();
|
|
13
13
|
|
|
14
|
-
log = log.withPrefix('
|
|
14
|
+
log = log.withPrefix('查找 VS');
|
|
15
15
|
regSearchKeys = regSearchKeys;
|
|
16
16
|
constructor(nodeSemver, configMsvsVersion) {
|
|
17
17
|
this.nodeSemver = nodeSemver, this.configMsvsVersion = configMsvsVersion, this.errorLog = [], this.validVersions = [];
|
|
@@ -25,23 +25,23 @@ class VisualStudioFinder {
|
|
|
25
25
|
async findVisualStudio() {
|
|
26
26
|
this.configVersionYear = null, this.configPath = null;
|
|
27
27
|
if (this.configMsvsVersion) {
|
|
28
|
-
this.addLog('
|
|
28
|
+
this.addLog('已从命令行或 npm 配置设置 msvs_version');
|
|
29
29
|
if (this.configMsvsVersion.match(/^\d{4}$/)) {
|
|
30
30
|
this.configVersionYear = parseInt(this.configMsvsVersion, 10);
|
|
31
|
-
this.addLog(`-
|
|
31
|
+
this.addLog(`- 正在查找 Visual Studio ${this.configVersionYear} 版本`);
|
|
32
32
|
} else {
|
|
33
33
|
this.configPath = path.resolve(this.configMsvsVersion);
|
|
34
|
-
this.addLog(`-
|
|
34
|
+
this.addLog(`- 正在查找安装在 "${this.configPath}" 的 Visual Studio`);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
else this.addLog('
|
|
37
|
+
else this.addLog('未从命令行或 npm 配置设置 msvs_version');
|
|
38
38
|
|
|
39
39
|
if (process.env.VCINSTALLDIR) {
|
|
40
40
|
this.envVcInstallDir = path.resolve(process.env.VCINSTALLDIR, '..');
|
|
41
|
-
this.addLog('
|
|
41
|
+
this.addLog('运行在 VS 命令提示符中,安装路径为:\n' +
|
|
42
42
|
`"${this.envVcInstallDir}"\n- will only use this version`);
|
|
43
43
|
}
|
|
44
|
-
else this.addLog('VCINSTALLDIR
|
|
44
|
+
else this.addLog('未设置 VCINSTALLDIR,不在 VS 命令提示符中运行');
|
|
45
45
|
|
|
46
46
|
const checks = [
|
|
47
47
|
() => this.findVisualStudio2017OrNewer(), () => this.findVisualStudio2015(), () => this.findVisualStudio2013()
|
|
@@ -56,32 +56,32 @@ class VisualStudioFinder {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
succeed(info) {
|
|
59
|
-
this.log.info(
|
|
60
|
-
`\n"${info.path}"` + '\
|
|
59
|
+
this.log.info(`使用 VS${info.versionYear} (${info.version}),路径:` +
|
|
60
|
+
`\n"${info.path}"` + '\n加 --verbose 可查看详细信息');
|
|
61
61
|
return info;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
fail() {
|
|
65
65
|
if (this.configMsvsVersion && this.envVcInstallDir)
|
|
66
66
|
this.errorLog.push(
|
|
67
|
-
'msvs_version
|
|
67
|
+
'msvs_version 与当前 VS 命令提示符不匹配,或该安装不可用');
|
|
68
68
|
else if (this.configMsvsVersion) {
|
|
69
69
|
// 指定了 msvs_version 但没找到 VS,输出可接受的版本
|
|
70
70
|
this.errorLog.push('');
|
|
71
71
|
if (this.validVersions) {
|
|
72
|
-
this.errorLog.push('
|
|
72
|
+
this.errorLog.push('msvs_version 可用的版本:');
|
|
73
73
|
this.validVersions.forEach(version => this.errorLog.push(`- "${version}"`));
|
|
74
74
|
}
|
|
75
|
-
else this.errorLog.push('
|
|
75
|
+
else this.errorLog.push('未找到 msvs_version 可用的版本');
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const errorLog = this.errorLog.join('\n'),
|
|
79
79
|
// Windows 80 列控制台,用到 X 标记列之前(含日志前缀共 79 字符,可用 62 字符):
|
|
80
80
|
infoLog = [
|
|
81
81
|
'**************************************************************',
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
'
|
|
82
|
+
'需要安装最新版本的 Visual Studio,并包含',
|
|
83
|
+
'"使用 C++ 的桌面开发"工作负载;',
|
|
84
|
+
'更多说明见文档:',
|
|
85
85
|
'https://github.com/nodejs/node-gyp#on-windows',
|
|
86
86
|
'**************************************************************'
|
|
87
87
|
].join('\n');
|
|
@@ -97,23 +97,23 @@ class VisualStudioFinder {
|
|
|
97
97
|
psArgs = ['-ExecutionPolicy', 'Unrestricted', '-NoProfile', '-Command',
|
|
98
98
|
'&{Add-Type -Path \'' + csFile + '\';' + '[VisualStudioConfiguration.Main]::PrintJson()}'];
|
|
99
99
|
|
|
100
|
-
this.log.silly('
|
|
100
|
+
this.log.silly('正在执行', ps, psArgs);
|
|
101
101
|
const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' });
|
|
102
102
|
return this.parseData(err, stdout, stderr);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// 解析 PowerShell 脚本输出,寻找可用的 Visual Studio 2017 或更新版本
|
|
106
106
|
parseData(err, stdout, stderr) {
|
|
107
|
-
this.log.silly('PS
|
|
107
|
+
this.log.silly('PS 标准错误 = %j', stderr)
|
|
108
108
|
|
|
109
109
|
const failPowershell = () => {
|
|
110
|
-
this.addLog(
|
|
111
|
-
|
|
110
|
+
this.addLog(`无法用 PowerShell 找到 Visual Studio 2017 或更新版本,
|
|
111
|
+
可加 '--loglevel silly' 重新运行以获取更多细节`);
|
|
112
112
|
return null;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
if (err) {
|
|
116
|
-
this.log.silly('PS
|
|
116
|
+
this.log.silly('PS 错误 = %j', err && (err.stack || err));
|
|
117
117
|
return failPowershell();
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -121,12 +121,12 @@ class VisualStudioFinder {
|
|
|
121
121
|
try {
|
|
122
122
|
vsInfo = JSON.parse(stdout);
|
|
123
123
|
} catch (e) {
|
|
124
|
-
this.log.silly('PS
|
|
124
|
+
this.log.silly('PS 标准输出 = %j', stdout), this.log.silly(e);
|
|
125
125
|
return failPowershell();
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
if (!Array.isArray(vsInfo)) {
|
|
129
|
-
this.log.silly('PS
|
|
129
|
+
this.log.silly('PS 标准输出 = %j', stdout);
|
|
130
130
|
return failPowershell();
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -145,7 +145,7 @@ class VisualStudioFinder {
|
|
|
145
145
|
// 去掉未来版本或版本号解析失败的项
|
|
146
146
|
vsInfo = vsInfo.filter(info => {
|
|
147
147
|
if (info.versionYear) return true;
|
|
148
|
-
this.addLog(
|
|
148
|
+
this.addLog(`在 "${info.path}" 发现未知版本 "${info.version}"`);
|
|
149
149
|
return false;
|
|
150
150
|
})
|
|
151
151
|
|
|
@@ -154,23 +154,23 @@ class VisualStudioFinder {
|
|
|
154
154
|
|
|
155
155
|
for (let i = 0; i < vsInfo.length; ++i) {
|
|
156
156
|
const info = vsInfo[i];
|
|
157
|
-
this.addLog(
|
|
157
|
+
this.addLog(`正在检查 VS${info.versionYear} (${info.version}),发现于:\n"${info.path}"`);
|
|
158
158
|
|
|
159
|
-
if (info.msBuild) this.addLog('-
|
|
159
|
+
if (info.msBuild) this.addLog('- 找到 "Visual Studio C++ 核心功能"');
|
|
160
160
|
else {
|
|
161
|
-
this.addLog('- "Visual Studio C++
|
|
161
|
+
this.addLog('- 缺少 "Visual Studio C++ 核心功能"');
|
|
162
162
|
continue;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
if (info.toolset) this.addLog(`-
|
|
165
|
+
if (info.toolset) this.addLog(`- 找到 VC++ 工具集: ${info.toolset}`);
|
|
166
166
|
else {
|
|
167
|
-
this.addLog('-
|
|
167
|
+
this.addLog('- 缺少任何 VC++ 工具集');
|
|
168
168
|
continue;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
if (info.sdk) this.addLog(`-
|
|
171
|
+
if (info.sdk) this.addLog(`- 找到 Windows SDK: ${info.sdk}`);
|
|
172
172
|
else {
|
|
173
|
-
this.addLog('-
|
|
173
|
+
this.addLog('- 缺少任何 Windows SDK');
|
|
174
174
|
continue;
|
|
175
175
|
}
|
|
176
176
|
|
|
@@ -178,7 +178,7 @@ class VisualStudioFinder {
|
|
|
178
178
|
return info
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
this.addLog('
|
|
181
|
+
this.addLog('未找到可用的 Visual Studio 2017 或更新版本');
|
|
182
182
|
return null;
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -186,16 +186,16 @@ class VisualStudioFinder {
|
|
|
186
186
|
getVersionInfo(info) {
|
|
187
187
|
const match = /^(\d+)\.(\d+)\./.exec(info.version);
|
|
188
188
|
if (!match) {
|
|
189
|
-
this.log.silly('-
|
|
189
|
+
this.log.silly('- 解析版本失败:', info.version);
|
|
190
190
|
return {};
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
this.log.silly('-
|
|
193
|
+
this.log.silly('- 版本匹配 = %j', match);
|
|
194
194
|
const versionMajor = parseInt(match[1], 10), versionMinor = parseInt(match[2], 10),
|
|
195
195
|
VERSION_YEAR = { 15: 2017, 16: 2019, 17: 2022, 18: 2026, }, versionYear = VERSION_YEAR[versionMajor];
|
|
196
196
|
|
|
197
197
|
if (!versionYear) {
|
|
198
|
-
this.log.silly('-
|
|
198
|
+
this.log.silly('- 不支持的版本:', versionMajor);
|
|
199
199
|
return {};
|
|
200
200
|
}
|
|
201
201
|
|
|
@@ -212,7 +212,7 @@ class VisualStudioFinder {
|
|
|
212
212
|
msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe'),
|
|
213
213
|
msbuildPathArm64 = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'arm64', 'MSBuild.exe');
|
|
214
214
|
if (info.packages.indexOf(pkg) !== -1) {
|
|
215
|
-
this.log.silly('-
|
|
215
|
+
this.log.silly('- 找到 VC.MSBuild.Base')
|
|
216
216
|
if (versionYear === 2017) return path.join(info.path, 'MSBuild', '15.0', 'Bin', 'MSBuild.exe');
|
|
217
217
|
if (versionYear === 2019) return msbuildPath;
|
|
218
218
|
}
|
|
@@ -229,15 +229,15 @@ class VisualStudioFinder {
|
|
|
229
229
|
// 辅助:解析 toolset 信息
|
|
230
230
|
getToolset(info, versionYear) {
|
|
231
231
|
const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', express = 'Microsoft.VisualStudio.WDExpress';
|
|
232
|
-
if (info.packages.indexOf(pkg) !== -1) this.log.silly('-
|
|
233
|
-
else if (info.packages.indexOf(express) !== -1) this.log.silly('-
|
|
232
|
+
if (info.packages.indexOf(pkg) !== -1) this.log.silly('- 找到 VC.Tools.x86.x64');
|
|
233
|
+
else if (info.packages.indexOf(express) !== -1) this.log.silly('- 找到 Visual Studio Express(正在查找工具集)');
|
|
234
234
|
else return null;
|
|
235
235
|
|
|
236
236
|
if (versionYear === 2017) return 'v141'
|
|
237
237
|
else if (versionYear === 2019) return 'v142';
|
|
238
238
|
else if (versionYear === 2022) return 'v143';
|
|
239
239
|
else if (versionYear === 2026) return 'v145';
|
|
240
|
-
this.log.silly('-
|
|
240
|
+
this.log.silly('- versionYear 无效:', versionYear);
|
|
241
241
|
return null;
|
|
242
242
|
}
|
|
243
243
|
|
|
@@ -252,22 +252,22 @@ class VisualStudioFinder {
|
|
|
252
252
|
if (!pkg.startsWith(win10SDKPrefix) && !pkg.startsWith(win11SDKPrefix)) return;
|
|
253
253
|
const parts = pkg.split('.')
|
|
254
254
|
if (parts.length > 5 && parts[5] !== 'Desktop') {
|
|
255
|
-
this.log.silly('-
|
|
255
|
+
this.log.silly('- 忽略非桌面版 Win10/11SDK:', pkg);
|
|
256
256
|
return;
|
|
257
257
|
}
|
|
258
258
|
const foundSdkVer = parseInt(parts[4], 10);
|
|
259
259
|
if (isNaN(foundSdkVer)) {
|
|
260
260
|
// Microsoft.VisualStudio.Component.Windows10SDK.IpOverUsb
|
|
261
|
-
this.log.silly('-
|
|
261
|
+
this.log.silly('- 解析 Win10/11SDK 编号失败:', pkg);
|
|
262
262
|
return;
|
|
263
263
|
}
|
|
264
|
-
this.log.silly('-
|
|
264
|
+
this.log.silly('- 找到 Win10/11SDK:', foundSdkVer);
|
|
265
265
|
Win10or11SDKVer = Math.max(Win10or11SDKVer, foundSdkVer);
|
|
266
266
|
})
|
|
267
267
|
|
|
268
268
|
if (Win10or11SDKVer !== 0) return `10.0.${Win10or11SDKVer}.0`;
|
|
269
269
|
else if (info.packages.indexOf(win8SDK) !== -1) {
|
|
270
|
-
this.log.silly('-
|
|
270
|
+
this.log.silly('- 找到 Win8SDK');
|
|
271
271
|
return '8.1';
|
|
272
272
|
}
|
|
273
273
|
return null;
|
|
@@ -276,7 +276,7 @@ class VisualStudioFinder {
|
|
|
276
276
|
// 查找可用的 Visual Studio 2015
|
|
277
277
|
async findVisualStudio2015() {
|
|
278
278
|
if (this.nodeSemver.major >= 19) {
|
|
279
|
-
this.addLog('
|
|
279
|
+
this.addLog('不查找 VS2015,它只支持到 Node.js 18');
|
|
280
280
|
return null;
|
|
281
281
|
}
|
|
282
282
|
return this.findOldVS({ version: '14.0', versionMajor: 14, versionMinor: 0, versionYear: 2015, toolset: 'v140' });
|
|
@@ -285,7 +285,7 @@ class VisualStudioFinder {
|
|
|
285
285
|
// 查找可用的 Visual Studio 2013
|
|
286
286
|
async findVisualStudio2013() {
|
|
287
287
|
if (this.nodeSemver.major >= 9) {
|
|
288
|
-
this.addLog('
|
|
288
|
+
this.addLog('不查找 VS2013,它只支持到 Node.js 8')
|
|
289
289
|
return null;
|
|
290
290
|
}
|
|
291
291
|
return this.findOldVS({ version: '12.0', versionMajor: 12, versionMinor: 0, versionYear: 2013, toolset: 'v120' });
|
|
@@ -297,27 +297,27 @@ class VisualStudioFinder {
|
|
|
297
297
|
'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7'],
|
|
298
298
|
regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions';
|
|
299
299
|
|
|
300
|
-
this.addLog(
|
|
300
|
+
this.addLog(`正在查找 Visual Studio ${info.versionYear}`);
|
|
301
301
|
try {
|
|
302
302
|
let res = await this.regSearchKeys(regVC7, info.version, []);
|
|
303
303
|
const msBuildRegOpts = process.arch === 'ia32' ? [] : ['/reg:32'], vsPath = path.resolve(res, '..');
|
|
304
|
-
this.addLog(`-
|
|
304
|
+
this.addLog(`- 发现于 "${vsPath}"`);
|
|
305
305
|
|
|
306
306
|
try {
|
|
307
307
|
res = await this.regSearchKeys([`${regMSBuild}\\${info.version}`], 'MSBuildToolsPath', msBuildRegOpts);
|
|
308
308
|
} catch (err) {
|
|
309
|
-
this.addLog('-
|
|
309
|
+
this.addLog('- 在注册表中未找到此版本的 MSBuild');
|
|
310
310
|
return null;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
const msBuild = path.join(res, 'MSBuild.exe');
|
|
314
|
-
this.addLog(`- MSBuild
|
|
314
|
+
this.addLog(`- MSBuild 位于 "${msBuild}"`);
|
|
315
315
|
|
|
316
316
|
if (!this.checkConfigVersion(info.versionYear, vsPath)) return null;
|
|
317
317
|
info.path = vsPath, info.msBuild = msBuild, info.sdk = null;
|
|
318
318
|
return info;
|
|
319
319
|
} catch (err) {
|
|
320
|
-
this.addLog('-
|
|
320
|
+
this.addLog('- 未找到');
|
|
321
321
|
return null;
|
|
322
322
|
}
|
|
323
323
|
}
|
|
@@ -330,15 +330,15 @@ class VisualStudioFinder {
|
|
|
330
330
|
this.validVersions.push(versionYear), this.validVersions.push(vsPath);
|
|
331
331
|
|
|
332
332
|
if (this.configVersionYear && this.configVersionYear !== versionYear) {
|
|
333
|
-
this.addLog('- msvs_version
|
|
333
|
+
this.addLog('- msvs_version 与此版本不匹配');
|
|
334
334
|
return false;
|
|
335
335
|
}
|
|
336
336
|
if (this.configPath && path.relative(this.configPath, vsPath) !== '') {
|
|
337
|
-
this.addLog('- msvs_version
|
|
337
|
+
this.addLog('- msvs_version 未指向此安装');
|
|
338
338
|
return false;
|
|
339
339
|
}
|
|
340
340
|
if (this.envVcInstallDir && path.relative(this.envVcInstallDir, vsPath) !== '') {
|
|
341
|
-
this.addLog('-
|
|
341
|
+
this.addLog('- 与当前 Visual Studio 命令提示符不匹配');
|
|
342
342
|
return false;
|
|
343
343
|
}
|
|
344
344
|
|
package/lib/install.js
CHANGED
|
@@ -22,18 +22,18 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
22
22
|
let shouldDownloadTarball = true; // 仅 Windows 下只需新 node.lib 时,避免下载 tarball
|
|
23
23
|
|
|
24
24
|
// 确定要安装哪个版本的 Node 开发文件
|
|
25
|
-
log.verbose('
|
|
25
|
+
log.verbose('安装', '输入版本字符串 %j', release.version);
|
|
26
26
|
if (!release.semver) throw new Error('版本号无效: ' + release.version); // 无法用 semver 解析版本字符串
|
|
27
27
|
if (semver.lt(release.version, '0.8.0')) throw new Error('目标版本最低为 `0.8.0`,当前: ' + release.version);
|
|
28
28
|
// 0.x.y-pre 版本尚未发布,无法安装,直接退出
|
|
29
29
|
if (release.semver.prerelease[0] === 'pre') {
|
|
30
|
-
log.verbose('
|
|
31
|
-
if (!gyp.opts.nodedir) throw new Error('"pre"
|
|
32
|
-
log.verbose('--nodedir
|
|
30
|
+
log.verbose('检测到 "pre" 版 node', release.version);
|
|
31
|
+
if (!gyp.opts.nodedir) throw new Error('无法安装 "pre" 版本的 node,请改用 --nodedir 参数');
|
|
32
|
+
log.verbose('传入了 --nodedir 参数;跳过安装', gyp.opts.nodedir);
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
log.verbose('
|
|
36
|
+
log.verbose('安装', '正在安装版本: %s', release.versionDir); // 版本拍平为字符串
|
|
37
37
|
const devDir = path.resolve(gyp.devDir, release.versionDir), // 开发文件安装目录
|
|
38
38
|
valid = file => {
|
|
39
39
|
// 头文件
|
|
@@ -44,7 +44,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
44
44
|
try {
|
|
45
45
|
await fs.stat(src);
|
|
46
46
|
} catch {
|
|
47
|
-
throw new Error(
|
|
47
|
+
throw new Error(`找不到要复制的源目录: ${src}`);
|
|
48
48
|
}
|
|
49
49
|
await fs.mkdir(dest, { recursive: true });
|
|
50
50
|
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
@@ -72,7 +72,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
rollback = async err => {
|
|
75
|
-
log.warn('
|
|
75
|
+
log.warn('安装', '发生错误,正在回滚安装')
|
|
76
76
|
await gyp.commands.remove([release.versionDir]);
|
|
77
77
|
throw err;
|
|
78
78
|
},
|
|
@@ -92,21 +92,21 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
92
92
|
try {
|
|
93
93
|
userString = ` ("${os.userInfo().username}")`; // os.userInfo 在某些系统上会失败,这里不关键
|
|
94
94
|
} catch (e) { }
|
|
95
|
-
log.warn('EACCES', '当前用户%s
|
|
96
|
-
log.warn('EACCES', '
|
|
95
|
+
log.warn('EACCES', '当前用户%s 没有权限访问开发文件目录 "%s"', userString, devDir);
|
|
96
|
+
log.warn('EACCES', '尝试用临时开发文件目录重新安装 "%s"', gyp.devDir);
|
|
97
97
|
if (process.cwd() === tmpdir) {
|
|
98
|
-
log.verbose('tmpdir
|
|
98
|
+
log.verbose('tmpdir 等于 cwd', '将自动删除开发文件以节省磁盘空间');
|
|
99
99
|
gyp.todo.push({ name: 'remove', args: argv });
|
|
100
100
|
}
|
|
101
101
|
return gyp.commands.install([noretry].concat(argv));
|
|
102
102
|
},
|
|
103
103
|
go = async () => {
|
|
104
|
-
log.verbose('
|
|
104
|
+
log.verbose('正在确保 devDir 已创建', devDir);
|
|
105
105
|
|
|
106
106
|
// 先创建 Node 开发文件所在目录
|
|
107
107
|
try {
|
|
108
108
|
const created = await fs.mkdir(devDir, { recursive: true });
|
|
109
|
-
if (created) log.verbose('
|
|
109
|
+
if (created) log.verbose('已创建 devDir', created);
|
|
110
110
|
} catch (err) {
|
|
111
111
|
if (err.code === 'EACCES') return eaccesFallback(err);
|
|
112
112
|
throw err;
|
|
@@ -119,13 +119,13 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
119
119
|
// 只有 .h 头文件和 gyp 文件会被解出
|
|
120
120
|
isValid = path => {
|
|
121
121
|
const isValid = valid(path);
|
|
122
|
-
if (isValid) log.verbose('
|
|
123
|
-
else log.silly('
|
|
122
|
+
if (isValid) log.verbose('已从 tarball 解出文件', path), extractCount++;
|
|
123
|
+
else log.silly('忽略 tarball 中的该项', path); // 无效
|
|
124
124
|
|
|
125
125
|
return isValid;
|
|
126
126
|
},
|
|
127
127
|
onwarn = (code, message) => {
|
|
128
|
-
extractErrors = true, log.error('
|
|
128
|
+
extractErrors = true, log.error('解压 tarball 出错', code, message);
|
|
129
129
|
},
|
|
130
130
|
// 下载并解压 tarball!
|
|
131
131
|
// 仅 Windows 上只需新 node.lib 时跳过
|
|
@@ -133,11 +133,11 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
133
133
|
// 所以先把 tarball 解到临时目录,再复制过去
|
|
134
134
|
tarExtractDir = win ? await fs.mkdtemp(path.join(os.tmpdir(), 'node-gyp-tmp-')) : devDir,
|
|
135
135
|
downloadShasums = async () => {
|
|
136
|
-
log.verbose('
|
|
137
|
-
log.verbose('
|
|
136
|
+
log.verbose('检查下载内容校验值,需要下载 `SHASUMS256.txt`...');
|
|
137
|
+
log.verbose('校验文件地址', release.shasumsUrl);
|
|
138
138
|
|
|
139
139
|
const res = await download(gyp, release.shasumsUrl);
|
|
140
|
-
if (res.status !== 200) throw new Error(
|
|
140
|
+
if (res.status !== 200) throw new Error(`下载校验文件时返回状态码 ${res.status}`);
|
|
141
141
|
|
|
142
142
|
for (const line of (await res.text()).trim().split('\n')) {
|
|
143
143
|
const items = line.trim().split(/\s+/);
|
|
@@ -148,24 +148,24 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
148
148
|
expectShasums[name] = items[0];
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
log.verbose('
|
|
151
|
+
log.verbose('校验数据', JSON.stringify(expectShasums));
|
|
152
152
|
},
|
|
153
153
|
downloadNodeLib = async () => {
|
|
154
|
-
log.verbose('
|
|
154
|
+
log.verbose('Windows 上;需要下载 `' + release.name + '.lib`...');
|
|
155
155
|
const dir = path.resolve(tarExtractDir, arch), targetLibPath = path.resolve(dir, release.name + '.lib'),
|
|
156
156
|
{ libUrl, libPath } = release[arch], name = `${arch} ${release.name}.lib`;
|
|
157
157
|
|
|
158
|
-
log.verbose(name, '
|
|
159
|
-
await fs.mkdir(dir, { recursive: true }), log.verbose('
|
|
158
|
+
log.verbose(name, '目录', dir), log.verbose(name, '地址', libUrl);
|
|
159
|
+
await fs.mkdir(dir, { recursive: true }), log.verbose('正在流式写入', name, '到:', targetLibPath);
|
|
160
160
|
|
|
161
161
|
const res = await download(gyp, libUrl);
|
|
162
162
|
// 只下载所需的 node.lib,若未拿到则抛错
|
|
163
|
-
if (res.status !== 200) throw new Error(
|
|
163
|
+
if (res.status !== 200) throw new Error(`下载 ${name} 时返回状态码 ${res.status}`);
|
|
164
164
|
|
|
165
165
|
return pipeline(
|
|
166
166
|
res.body,
|
|
167
167
|
new ShaSum((_, checksum) => {
|
|
168
|
-
contentShasums[libPath] = checksum, log.verbose('
|
|
168
|
+
contentShasums[libPath] = checksum, log.verbose('内容校验值', libPath, checksum)
|
|
169
169
|
}), createWriteStream(targetLibPath)
|
|
170
170
|
);
|
|
171
171
|
};
|
|
@@ -177,20 +177,20 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
177
177
|
try {
|
|
178
178
|
const res = await download(gyp, release.tarballUrl);
|
|
179
179
|
|
|
180
|
-
if (res.status !== 200) throw new Error(
|
|
180
|
+
if (res.status !== 200) throw new Error(`下载 ${release.tarballUrl} 时返回状态码 ${res.status}`);
|
|
181
181
|
await pipeline(
|
|
182
182
|
res.body,
|
|
183
183
|
// 内容校验和
|
|
184
184
|
new ShaSum((_, checksum) => {
|
|
185
185
|
const filename = path.basename(release.tarballUrl).trim();
|
|
186
|
-
contentShasums[filename] = checksum, log.verbose('
|
|
186
|
+
contentShasums[filename] = checksum, log.verbose('内容校验值', filename, checksum);
|
|
187
187
|
}),
|
|
188
188
|
tar.extract({ strip: 1, cwd: tarExtractDir, filter: isValid, onwarn })
|
|
189
189
|
)
|
|
190
190
|
} catch (err) {
|
|
191
191
|
// tarball 下载出问题?
|
|
192
192
|
if (err.code === 'ENOTFOUND')
|
|
193
|
-
throw new Error('这通常不是
|
|
193
|
+
throw new Error('这通常不是 nodejs-mobile-gyp 或包本身的问题,而是网络问题;\n' +
|
|
194
194
|
'通常是因为你在代理后,或网络设置异常;');
|
|
195
195
|
throw err;
|
|
196
196
|
}
|
|
@@ -198,7 +198,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
198
198
|
|
|
199
199
|
// tarball 解压完成后调用
|
|
200
200
|
if (extractErrors || extractCount === 0) throw new Error('下载/解压 tarball 时发生致命错误');
|
|
201
|
-
log.verbose('tarball', '
|
|
201
|
+
log.verbose('tarball', '解析 tarball 完成');
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
const installVersionPath = path.resolve(tarExtractDir, 'installVersion');
|
|
@@ -211,12 +211,12 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
211
211
|
...(!tarPath || win ? [downloadShasums()] : [])
|
|
212
212
|
])
|
|
213
213
|
|
|
214
|
-
log.verbose('
|
|
214
|
+
log.verbose('下载内容校验值', JSON.stringify(contentShasums));
|
|
215
215
|
// 校验内容 shasum
|
|
216
216
|
for (const k in contentShasums) {
|
|
217
|
-
log.verbose('
|
|
217
|
+
log.verbose('正在校验 ' + k + ' 的下载校验值', '(%s == %s)', contentShasums[k], expectShasums[k]);
|
|
218
218
|
if (contentShasums[k] !== expectShasums[k]) {
|
|
219
|
-
throw new Error(k + '
|
|
219
|
+
throw new Error(k + ' 本地校验值与远程不一致: ' + contentShasums[k] + ' != ' + expectShasums[k]);
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
|
|
@@ -235,12 +235,12 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
235
235
|
|
|
236
236
|
// 如果传了 --ensure,则不总是安装,只在需要时安装
|
|
237
237
|
if (gyp.opts.ensure) {
|
|
238
|
-
log.verbose('
|
|
238
|
+
log.verbose('安装', '传入了 --ensure,已安装时不再重装');
|
|
239
239
|
try {
|
|
240
240
|
await fs.stat(devDir);
|
|
241
241
|
} catch (err) {
|
|
242
242
|
if (err.code === 'ENOENT') {
|
|
243
|
-
log.verbose('
|
|
243
|
+
log.verbose('安装', '版本尚未安装,继续安装', release.version);
|
|
244
244
|
try {
|
|
245
245
|
return await go();
|
|
246
246
|
} catch (err) { return rollback(err) }
|
|
@@ -248,7 +248,7 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
248
248
|
else if (err.code === 'EACCES') return eaccesFallback(err);
|
|
249
249
|
throw err;
|
|
250
250
|
}
|
|
251
|
-
log.verbose('
|
|
251
|
+
log.verbose('安装', '版本已安装,需要检查 "installVersion"');
|
|
252
252
|
const installVersionFile = path.resolve(devDir, 'installVersion');
|
|
253
253
|
let installVersion = 0;
|
|
254
254
|
try {
|
|
@@ -257,23 +257,23 @@ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPro
|
|
|
257
257
|
} catch (err) {
|
|
258
258
|
if (err.code !== 'ENOENT') throw err;
|
|
259
259
|
}
|
|
260
|
-
log.verbose('
|
|
261
|
-
log.verbose('
|
|
260
|
+
log.verbose('得到 "installVersion"', installVersion);
|
|
261
|
+
log.verbose('需要 "installVersion"', gyp.package.installVersion);
|
|
262
262
|
if (installVersion < gyp.package.installVersion) {
|
|
263
|
-
log.verbose('
|
|
263
|
+
log.verbose('安装', '版本不可用;重新安装');
|
|
264
264
|
try {
|
|
265
265
|
return await go();
|
|
266
266
|
} catch (err) { return rollback(err) }
|
|
267
267
|
}
|
|
268
|
-
log.verbose('
|
|
268
|
+
log.verbose('安装', '版本可用');
|
|
269
269
|
if (win) {
|
|
270
|
-
log.verbose('
|
|
270
|
+
log.verbose('Windows 上;需要检查 node.lib');
|
|
271
271
|
const nodeLibPath = path.resolve(devDir, arch, 'node.lib');
|
|
272
272
|
try {
|
|
273
273
|
await fs.stat(nodeLibPath);
|
|
274
274
|
} catch (err) {
|
|
275
275
|
if (err.code === 'ENOENT') {
|
|
276
|
-
log.verbose('
|
|
276
|
+
log.verbose('安装', `版本尚未为 ${arch} 安装,继续安装`, release.version);
|
|
277
277
|
try {
|
|
278
278
|
shouldDownloadTarball = false;
|
|
279
279
|
return await go();
|
|
@@ -307,5 +307,5 @@ class ShaSum extends Transform {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
install.usage = '
|
|
310
|
+
install.usage = '安装指定 node 版本的 node 开发文件;';
|
|
311
311
|
export { install }
|
package/lib/list.js
CHANGED
|
@@ -9,7 +9,7 @@ const fs = gracefulFs.promises,
|
|
|
9
9
|
const devDir = gyp.devDir;
|
|
10
10
|
let versions = [];
|
|
11
11
|
|
|
12
|
-
log.verbose('
|
|
12
|
+
log.verbose('列表', '使用 node-gyp 目录:', devDir);
|
|
13
13
|
try {
|
|
14
14
|
const dir = await fs.readdir(devDir);
|
|
15
15
|
if (Array.isArray(dir)) versions = dir.filter((v) => v !== 'current');
|
|
@@ -20,5 +20,5 @@ const fs = gracefulFs.promises,
|
|
|
20
20
|
return versions;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
list.usage = '
|
|
23
|
+
list.usage = '列出当前已安装的 node 开发文件';
|
|
24
24
|
export { list }
|
package/lib/node-gyp.js
CHANGED
|
@@ -137,7 +137,7 @@ class Gyp extends EventEmitter {
|
|
|
137
137
|
if (!opts) opts = {};
|
|
138
138
|
if (!opts.silent && !opts.stdio) opts.stdio = [0, 1, 2];
|
|
139
139
|
const cp = childProcess.spawn(command, args, opts);
|
|
140
|
-
log.info('
|
|
140
|
+
log.info('启动命令', command), log.info('命令参数', args);
|
|
141
141
|
return cp;
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -151,28 +151,28 @@ class Gyp extends EventEmitter {
|
|
|
151
151
|
(cp >= 0xFE30 && cp <= 0xFE4F) || (cp >= 0xFF00 && cp <= 0xFF60) || (cp >= 0xFFE0 && cp <= 0xFFE6),
|
|
152
152
|
width = s => [...s].reduce((n, ch) => n + (wide(ch.codePointAt(0)) ? 2 : 1), 0),
|
|
153
153
|
pad = (s, n) => s + ' '.repeat(Math.max(0, n - width(s))),
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
row = (name, desc) => ` ${chalk.cyanBright(pad(name, 19))}${chalk.gray(desc)}`,
|
|
155
|
+
grp = name => ` ${chalk.cyan(name)}`;
|
|
156
156
|
return [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
` ${chalk.gray('无参数、help、-h、--help
|
|
175
|
-
|
|
157
|
+
` ${chalk.blueBright('用法:')} nodejs-mobile-gyp <命令> [选项]`, '',
|
|
158
|
+
grp('命令'),
|
|
159
|
+
row('build', '构建模块(Windows 调 msbuild,其它平台调 make)'),
|
|
160
|
+
row('clean', '清理生成文件与 out 目录'),
|
|
161
|
+
row('configure', '为当前模块生成项目文件(Windows 为 MSVC 项目,其它平台为 Makefile)'),
|
|
162
|
+
row('rebuild', '依次执行 clean、configure、build'),
|
|
163
|
+
row('install', '安装指定 Node 版本的开发文件'),
|
|
164
|
+
row('list', '列出已安装的 Node 开发文件;别名 ls'),
|
|
165
|
+
row('remove', '移除指定版本的 Node 开发文件;别名 rm'), '',
|
|
166
|
+
grp('选项'),
|
|
167
|
+
row('-C <path>', '切换到该目录后执行;完整写法 --directory'),
|
|
168
|
+
row('--devdir <path>', '指定开发文件缓存目录,默认取系统缓存目录'),
|
|
169
|
+
row('-j <count>', '并行构建任务数,仅 build;完整写法 --jobs'),
|
|
170
|
+
row('--target <version>', '目标 Node.js 版本,默认当前主机版本'),
|
|
171
|
+
row('--arch <arch>', '目标架构,如 arm64、ia32'),
|
|
172
|
+
row('--debug', '输出详细日志'),
|
|
173
|
+
row('-v', '输出版本号;完整写法 --version'), '',
|
|
174
|
+
` ${chalk.gray('无参数、help、-h、--help 输出本帮助;')}`, '',
|
|
175
|
+
` ${gold('nodejs-mobile-gyp@' + this.version)} ${gold('node@' + process.versions.node)}`
|
|
176
176
|
].join('\n');
|
|
177
177
|
}
|
|
178
178
|
}
|
package/lib/process-release.js
CHANGED
|
@@ -54,7 +54,7 @@ const headersTarballRange = '>= 3.0.0 || ~0.12.10 || ~0.10.42', bitsre = /\/win-
|
|
|
54
54
|
|
|
55
55
|
// 检查 nvm.sh 标准镜像环境变量
|
|
56
56
|
if (!overrideDistUrl && process.env.NODEJS_ORG_MIRROR) overrideDistUrl = process.env.NODEJS_ORG_MIRROR;
|
|
57
|
-
if (overrideDistUrl) log.verbose('
|
|
57
|
+
if (overrideDistUrl) log.verbose('下载', '使用 dist-url', overrideDistUrl);
|
|
58
58
|
if (overrideDistUrl) distBaseUrl = overrideDistUrl.replace(/\/+$/, '');
|
|
59
59
|
else distBaseUrl = 'https://nodejs.org/dist';
|
|
60
60
|
distBaseUrl += '/v' + version + '/'
|