@flun/nodejs-mobile-gyp 1.0.1 → 1.0.2

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/lib/install.js CHANGED
@@ -1,419 +1,310 @@
1
- import gracefulFs from 'graceful-fs'
2
- import os from 'os'
3
- import { backOff } from 'exponential-backoff'
4
- import * as tar from 'tar'
5
- import path from 'path'
6
- import { Transform, promises as streamPromises } from 'stream'
7
- import crypto from 'crypto'
8
- import * as log from './log.js'
9
- import semver from 'semver'
10
- import { download } from './download.js'
11
- import { processRelease } from './process-release.js'
12
-
13
- const { createWriteStream, promises: fs } = gracefulFs
14
- const { pipeline } = streamPromises
15
- const win = process.platform === 'win32'
16
-
17
- /**
18
- * 为指定 Node 版本安装开发文件
19
- */
20
- const install = async (gyp, argv) => {
21
- log.stdout()
22
- const release = processRelease(argv, gyp, process.version, process.release)
23
- // 按 create-config-gypi.js 的逻辑推断 target_arch,仅 Windows 用
24
- const arch = win ? (gyp.opts.target_arch || gyp.opts.arch || process.arch || 'ia32') : ''
25
- // 仅 Windows 下只需新 node.lib 时,避免下载 tarball
26
- let shouldDownloadTarball = true
27
-
28
- // 确定要安装哪个版本的 Node 开发文件
29
- log.verbose('install', 'input version string %j', release.version)
30
-
31
- if (!release.semver) {
32
- // 无法用 semver 解析版本字符串
33
- throw new Error('Invalid version number: ' + release.version)
34
- }
35
-
36
- if (semver.lt(release.version, '0.8.0')) {
37
- throw new Error('Minimum target version is `0.8.0` or greater. Got: ' + release.version)
38
- }
39
-
40
- // 0.x.y-pre 版本尚未发布,无法安装,直接退出
41
- if (release.semver.prerelease[0] === 'pre') {
42
- log.verbose('detected "pre" node version', release.version)
43
- if (!gyp.opts.nodedir) {
44
- throw new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead')
1
+ import gracefulFs from 'graceful-fs';
2
+ import os from 'os';
3
+ import { backOff } from 'exponential-backoff';
4
+ import * as tar from 'tar';
5
+ import path from 'path';
6
+ import { Transform, promises as streamPromises } from 'stream';
7
+ import crypto from 'crypto';
8
+ import * as log from './log.js';
9
+ import semver from 'semver';
10
+ import { download } from './download.js';
11
+ import { processRelease } from './process-release.js';
12
+
13
+ const { createWriteStream, promises: fs } = gracefulFs, { pipeline } = streamPromises, win = process.platform === 'win32',
14
+ /**
15
+ * 为指定 Node 版本安装开发文件
16
+ */
17
+ install = async (gyp, argv) => {
18
+ log.stdout();
19
+ const release = processRelease(argv, gyp, process.version, process.release),
20
+ // 按 create-config-gypi.js 的逻辑推断 target_arch,仅 Windows 用
21
+ arch = win ? (gyp.opts.target_arch || gyp.opts.arch || process.arch || 'ia32') : '';
22
+ let shouldDownloadTarball = true; // 仅 Windows 下只需新 node.lib 时,避免下载 tarball
23
+
24
+ // 确定要安装哪个版本的 Node 开发文件
25
+ log.verbose('install', 'input version string %j', release.version);
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`,当前: ' + release.version);
28
+ // 0.x.y-pre 版本尚未发布,无法安装,直接退出
29
+ if (release.semver.prerelease[0] === 'pre') {
30
+ log.verbose('detected "pre" node version', release.version);
31
+ if (!gyp.opts.nodedir) throw new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead');
32
+ log.verbose('--nodedir flag was passed; skipping install', gyp.opts.nodedir);
33
+ return;
45
34
  }
46
- log.verbose('--nodedir flag was passed; skipping install', gyp.opts.nodedir)
47
- return
48
- }
49
-
50
- // 版本拍平为字符串
51
- log.verbose('install', 'installing version: %s', release.versionDir)
52
-
53
- // 开发文件安装目录
54
- const devDir = path.resolve(gyp.devDir, release.versionDir)
55
35
 
56
- // 如果传了 --ensure,则不总是安装,只在需要时安装
57
- if (gyp.opts.ensure) {
58
- log.verbose('install', '--ensure was passed, so won\'t reinstall if already installed')
59
- try {
60
- await fs.stat(devDir)
61
- } catch (err) {
62
- if (err.code === 'ENOENT') {
63
- log.verbose('install', 'version not already installed, continuing with install', release.version)
36
+ log.verbose('install', 'installing version: %s', release.versionDir); // 版本拍平为字符串
37
+ const devDir = path.resolve(gyp.devDir, release.versionDir), // 开发文件安装目录
38
+ valid = file => {
39
+ // 头文件
40
+ const extname = path.extname(file);
41
+ return extname === '.h' || extname === '.gypi';
42
+ },
43
+ copyDirectory = async (src, dest) => {
64
44
  try {
65
- return await go()
66
- } catch (err) {
67
- return rollback(err)
68
- }
69
- } else if (err.code === 'EACCES') {
70
- return eaccesFallback(err)
71
- }
72
- throw err
73
- }
74
- log.verbose('install', 'version is already installed, need to check "installVersion"')
75
- const installVersionFile = path.resolve(devDir, 'installVersion')
76
- let installVersion = 0
77
- try {
78
- const ver = await fs.readFile(installVersionFile, 'ascii')
79
- installVersion = parseInt(ver, 10) || 0
80
- } catch (err) {
81
- if (err.code !== 'ENOENT') {
82
- throw err
83
- }
84
- }
85
- log.verbose('got "installVersion"', installVersion)
86
- log.verbose('needs "installVersion"', gyp.package.installVersion)
87
- if (installVersion < gyp.package.installVersion) {
88
- log.verbose('install', 'version is no good; reinstalling')
89
- try {
90
- return await go()
91
- } catch (err) {
92
- return rollback(err)
93
- }
94
- }
95
- log.verbose('install', 'version is good')
96
- if (win) {
97
- log.verbose('on Windows; need to check node.lib')
98
- const nodeLibPath = path.resolve(devDir, arch, 'node.lib')
99
- try {
100
- await fs.stat(nodeLibPath)
101
- } catch (err) {
102
- if (err.code === 'ENOENT') {
103
- log.verbose('install', `version not already installed for ${arch}, continuing with install`, release.version)
104
- try {
105
- shouldDownloadTarball = false
106
- return await go()
107
- } catch (err) {
108
- return rollback(err)
109
- }
110
- } else if (err.code === 'EACCES') {
111
- return eaccesFallback(err)
45
+ await fs.stat(src);
46
+ } catch {
47
+ throw new Error(`Missing source directory for copy: ${src}`);
112
48
  }
113
- throw err
114
- }
115
- }
116
- } else {
117
- try {
118
- return await go()
119
- } catch (err) {
120
- return rollback(err)
121
- }
122
- }
123
-
124
- async function copyDirectory (src, dest) {
125
- try {
126
- await fs.stat(src)
127
- } catch {
128
- throw new Error(`Missing source directory for copy: ${src}`)
129
- }
130
- await fs.mkdir(dest, { recursive: true })
131
- const entries = await fs.readdir(src, { withFileTypes: true })
132
- for (const entry of entries) {
133
- if (entry.isDirectory()) {
134
- await copyDirectory(path.join(src, entry.name), path.join(dest, entry.name))
135
- } else if (entry.isFile()) {
136
- // 并行安装时,Windows 上复制文件可能报文件错误,
137
- // 因此用指数退避解决冲突
138
- await backOff(async () => {
139
- try {
140
- await fs.copyFile(path.join(src, entry.name), path.join(dest, entry.name))
141
- } catch (err) {
142
- // 若设置了 ensure,检查文件是否已存在,存在就足够了
143
- if (gyp.opts.ensure && err.code === 'EBUSY') {
49
+ await fs.mkdir(dest, { recursive: true });
50
+ const entries = await fs.readdir(src, { withFileTypes: true });
51
+ for (const entry of entries) {
52
+ if (entry.isDirectory()) await copyDirectory(path.join(src, entry.name), path.join(dest, entry.name));
53
+ else if (entry.isFile()) {
54
+ // 并行安装时,Windows 上复制文件可能报文件错误,
55
+ // 因此用指数退避解决冲突
56
+ await backOff(async () => {
144
57
  try {
145
- await fs.stat(path.join(dest, entry.name))
146
- return
147
- } catch {}
148
- }
149
- throw err
58
+ await fs.copyFile(path.join(src, entry.name), path.join(dest, entry.name));
59
+ } catch (err) {
60
+ // 若设置了 ensure,检查文件是否已存在,存在就足够了
61
+ if (gyp.opts.ensure && err.code === 'EBUSY') {
62
+ try {
63
+ await fs.stat(path.join(dest, entry.name));
64
+ return;
65
+ } catch { }
66
+ }
67
+ throw err;
68
+ }
69
+ })
150
70
  }
151
- })
152
- } else {
153
- throw new Error('Unexpected file directory entry type')
154
- }
155
- }
156
- }
157
-
158
- async function go () {
159
- log.verbose('ensuring devDir is created', devDir)
160
-
161
- // 先创建 Node 开发文件所在目录
162
- try {
163
- const created = await fs.mkdir(devDir, { recursive: true })
164
-
165
- if (created) {
166
- log.verbose('created devDir', created)
167
- }
168
- } catch (err) {
169
- if (err.code === 'EACCES') {
170
- return eaccesFallback(err)
171
- }
172
-
173
- throw err
174
- }
71
+ else throw new Error('意外的文件目录条目类型');
72
+ }
73
+ },
74
+ rollback = async err => {
75
+ log.warn('install', '发生错误,正在回滚安装')
76
+ await gyp.commands.remove([release.versionDir]);
77
+ throw err;
78
+ },
79
+ /**
80
+ * EACCES 回退方案,用于处理 npm 的 `sudo` 行为:
81
+ * npm 在调用任何子进程(如 node-gyp)前会先降权,
82
+ * 于是 "nobody" 用户没有权限创建 dev dir。
83
+ * 作为回退,把 tmpdir() 作为本次安装的 dev dir。
84
+ * 这不理想,但至少编译能成功……
85
+ */
86
+ eaccesFallback = async err => {
87
+ const noretry = '--node_gyp_internal_noretry';
88
+ if (argv.indexOf(noretry) !== -1) throw err;
89
+ const tmpdir = os.tmpdir();
90
+ gyp.devDir = path.resolve(tmpdir, '.node-gyp');
91
+ let userString = '';
92
+ try {
93
+ userString = ` ("${os.userInfo().username}")`; // os.userInfo 在某些系统上会失败,这里不关键
94
+ } catch (e) { }
95
+ log.warn('EACCES', '当前用户%s 没有权限访问 dev dir "%s"', userString, devDir);
96
+ log.warn('EACCES', '尝试用临时 dev dir 重新安装 "%s"', gyp.devDir);
97
+ if (process.cwd() === tmpdir) {
98
+ log.verbose('tmpdir == cwd', 'automatically will remove dev files after to save disk space');
99
+ gyp.todo.push({ name: 'remove', args: argv });
100
+ }
101
+ return gyp.commands.install([noretry].concat(argv));
102
+ },
103
+ go = async () => {
104
+ log.verbose('ensuring devDir is created', devDir);
175
105
 
176
- // 下载 Node tarball
177
- const tarPath = gyp.opts.tarball
178
- let extractErrors = false
179
- let extractCount = 0
180
- const contentShasums = {}
181
- const expectShasums = {}
106
+ // 先创建 Node 开发文件所在目录
107
+ try {
108
+ const created = await fs.mkdir(devDir, { recursive: true });
109
+ if (created) log.verbose('created devDir', created);
110
+ } catch (err) {
111
+ if (err.code === 'EACCES') return eaccesFallback(err);
112
+ throw err;
113
+ }
182
114
 
183
- // 检查从 tarball 解出的文件是否有效。
184
- // 只有 .h 头文件和 gyp 文件会被解出
185
- function isValid (path) {
186
- const isValid = valid(path)
187
- if (isValid) {
188
- log.verbose('extracted file from tarball', path)
189
- extractCount++
190
- } else {
191
- // 无效
192
- log.silly('ignoring from tarball', path)
193
- }
194
- return isValid
195
- }
115
+ // 下载 Node tarball
116
+ let extractErrors = false, extractCount = 0;
117
+ const tarPath = gyp.opts.tarball, contentShasums = {}, expectShasums = {},
118
+ // 检查从 tarball 解出的文件是否有效。
119
+ // 只有 .h 头文件和 gyp 文件会被解出
120
+ isValid = path => {
121
+ const isValid = valid(path);
122
+ if (isValid) log.verbose('extracted file from tarball', path), extractCount++;
123
+ else log.silly('ignoring from tarball', path); // 无效
124
+
125
+ return isValid;
126
+ },
127
+ onwarn = (code, message) => {
128
+ extractErrors = true, log.error('error while extracting tarball', code, message);
129
+ },
130
+ // 下载并解压 tarball!
131
+ // 仅 Windows 上只需新 node.lib 时跳过
132
+ // Windows 上并行安装时(多个原生模块,很常见)tar 会报文件错误,
133
+ // 所以先把 tarball 解到临时目录,再复制过去
134
+ tarExtractDir = win ? await fs.mkdtemp(path.join(os.tmpdir(), 'node-gyp-tmp-')) : devDir,
135
+ downloadShasums = async () => {
136
+ log.verbose('check download content checksum, need to download `SHASUMS256.txt`...');
137
+ log.verbose('checksum url', release.shasumsUrl);
138
+
139
+ const res = await download(gyp, release.shasumsUrl);
140
+ if (res.status !== 200) throw new Error(`${res.status} status code downloading checksum`);
141
+
142
+ for (const line of (await res.text()).trim().split('\n')) {
143
+ const items = line.trim().split(/\s+/);
144
+ if (items.length !== 2) return;
145
+
146
+ // 0035d18e2dcf9aad669b1c7c07319e17abfe3762 ./node-v0.11.4.tar.gz
147
+ const name = items[1].replace(/^\.\//, '');
148
+ expectShasums[name] = items[0];
149
+ }
196
150
 
197
- function onwarn (code, message) {
198
- extractErrors = true
199
- log.error('error while extracting tarball', code, message)
200
- }
151
+ log.verbose('checksum data', JSON.stringify(expectShasums));
152
+ },
153
+ downloadNodeLib = async () => {
154
+ log.verbose('on Windows; need to download `' + release.name + '.lib`...');
155
+ const dir = path.resolve(tarExtractDir, arch), targetLibPath = path.resolve(dir, release.name + '.lib'),
156
+ { libUrl, libPath } = release[arch], name = `${arch} ${release.name}.lib`;
201
157
 
202
- // 下载并解压 tarball!
203
- // 仅 Windows 上只需新 node.lib 时跳过
158
+ log.verbose(name, 'dir', dir), log.verbose(name, 'url', libUrl);
159
+ await fs.mkdir(dir, { recursive: true }), log.verbose('streaming', name, 'to:', targetLibPath);
204
160
 
205
- // Windows 上并行安装时(多个原生模块,很常见)tar 会报文件错误,
206
- // 所以先把 tarball 解到临时目录,再复制过去
207
- const tarExtractDir = win ? await fs.mkdtemp(path.join(os.tmpdir(), 'node-gyp-tmp-')) : devDir
161
+ const res = await download(gyp, libUrl);
162
+ // 只下载所需的 node.lib,若未拿到则抛错
163
+ if (res.status !== 200) throw new Error(`${res.status} status code downloading ${name}`);
208
164
 
209
- try {
210
- if (shouldDownloadTarball) {
211
- if (tarPath) {
212
- await tar.extract({
213
- file: tarPath,
214
- strip: 1,
215
- filter: isValid,
216
- onwarn,
217
- cwd: tarExtractDir
218
- })
219
- } else {
220
- try {
221
- const res = await download(gyp, release.tarballUrl)
165
+ return pipeline(
166
+ res.body,
167
+ new ShaSum((_, checksum) => {
168
+ contentShasums[libPath] = checksum, log.verbose('content checksum', libPath, checksum)
169
+ }), createWriteStream(targetLibPath)
170
+ );
171
+ };
222
172
 
223
- if (res.status !== 200) {
224
- throw new Error(`${res.status} response downloading ${release.tarballUrl}`)
173
+ try {
174
+ if (shouldDownloadTarball) {
175
+ if (tarPath) await tar.extract({ file: tarPath, strip: 1, filter: isValid, onwarn, cwd: tarExtractDir });
176
+ else {
177
+ try {
178
+ const res = await download(gyp, release.tarballUrl);
179
+
180
+ if (res.status !== 200) throw new Error(`${res.status} response downloading ${release.tarballUrl}`);
181
+ await pipeline(
182
+ res.body,
183
+ // 内容校验和
184
+ new ShaSum((_, checksum) => {
185
+ const filename = path.basename(release.tarballUrl).trim();
186
+ contentShasums[filename] = checksum, log.verbose('content checksum', filename, checksum);
187
+ }),
188
+ tar.extract({ strip: 1, cwd: tarExtractDir, filter: isValid, onwarn })
189
+ )
190
+ } catch (err) {
191
+ // tarball 下载出问题?
192
+ if (err.code === 'ENOTFOUND')
193
+ throw new Error('这通常不是 node-gyp 或包本身的问题,而是网络问题;\n' + '通常是因为你在代理后,或网络设置异常;');
194
+ throw err;
195
+ }
225
196
  }
226
197
 
227
- await pipeline(
228
- res.body,
229
- // 内容校验和
230
- new ShaSum((_, checksum) => {
231
- const filename = path.basename(release.tarballUrl).trim()
232
- contentShasums[filename] = checksum
233
- log.verbose('content checksum', filename, checksum)
234
- }),
235
- tar.extract({
236
- strip: 1,
237
- cwd: tarExtractDir,
238
- filter: isValid,
239
- onwarn
240
- })
241
- )
242
- } catch (err) {
243
- // tarball 下载出问题?
244
- if (err.code === 'ENOTFOUND') {
245
- throw new Error('This is most likely not a problem with node-gyp or the package itself and\n' +
246
- 'is related to network connectivity. In most cases you are behind a proxy or have bad \n' +
247
- 'network settings.')
198
+ // tarball 解压完成后调用
199
+ if (extractErrors || extractCount === 0) throw new Error('下载/解压 tarball 时发生致命错误');
200
+ log.verbose('tarball', 'done parsing tarball');
201
+ }
202
+
203
+ const installVersionPath = path.resolve(tarExtractDir, 'installVersion');
204
+ await Promise.all([
205
+ // 需要下载 node.lib
206
+ ...(win ? [downloadNodeLib()] : []),
207
+ // 写入 "installVersion" 文件
208
+ fs.writeFile(installVersionPath, gyp.package.installVersion + '\n'),
209
+ // 只有在下载了需要 SHA 校验的东西时才下载 SHASUMS.txt
210
+ ...(!tarPath || win ? [downloadShasums()] : [])
211
+ ])
212
+
213
+ log.verbose('download contents checksum', JSON.stringify(contentShasums));
214
+ // 校验内容 shasum
215
+ for (const k in contentShasums) {
216
+ log.verbose('validating download checksum for ' + k, '(%s == %s)', contentShasums[k], expectShasums[k]);
217
+ if (contentShasums[k] !== expectShasums[k]) {
218
+ throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k]);
248
219
  }
249
- throw err
250
220
  }
251
- }
252
221
 
253
- // tarball 解压完成后调用
254
- if (extractErrors || extractCount === 0) {
255
- throw new Error('There was a fatal problem while downloading/extracting the tarball')
222
+ // 从临时解压目录复制文件到 devDir
223
+ if (tarExtractDir !== devDir) {
224
+ await copyDirectory(tarExtractDir, devDir);
225
+ }
226
+ } finally {
227
+ if (tarExtractDir !== devDir) {
228
+ try {
229
+ await fs.rm(tarExtractDir, { recursive: true }); // 尝试清理临时目录
230
+ } catch { log.warn('清理临时 tarball 解压目录失败') }
231
+ }
256
232
  }
233
+ };
257
234
 
258
- log.verbose('tarball', 'done parsing tarball')
259
- }
260
-
261
- const installVersionPath = path.resolve(tarExtractDir, 'installVersion')
262
- await Promise.all([
263
- // 需要下载 node.lib
264
- ...(win ? [downloadNodeLib()] : []),
265
- // 写入 "installVersion" 文件
266
- fs.writeFile(installVersionPath, gyp.package.installVersion + '\n'),
267
- // 只有在下载了需要 SHA 校验的东西时才下载 SHASUMS.txt
268
- ...(!tarPath || win ? [downloadShasums()] : [])
269
- ])
270
-
271
- log.verbose('download contents checksum', JSON.stringify(contentShasums))
272
- // 校验内容 shasum
273
- for (const k in contentShasums) {
274
- log.verbose('validating download checksum for ' + k, '(%s == %s)', contentShasums[k], expectShasums[k])
275
- if (contentShasums[k] !== expectShasums[k]) {
276
- throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
235
+ // 如果传了 --ensure,则不总是安装,只在需要时安装
236
+ if (gyp.opts.ensure) {
237
+ log.verbose('install', '--ensure was passed, so won\'t reinstall if already installed');
238
+ try {
239
+ await fs.stat(devDir);
240
+ } catch (err) {
241
+ if (err.code === 'ENOENT') {
242
+ log.verbose('install', 'version not already installed, continuing with install', release.version);
243
+ try {
244
+ return await go();
245
+ } catch (err) { return rollback(err) }
277
246
  }
247
+ else if (err.code === 'EACCES') return eaccesFallback(err);
248
+ throw err;
278
249
  }
279
-
280
- // 从临时解压目录复制文件到 devDir
281
- if (tarExtractDir !== devDir) {
282
- await copyDirectory(tarExtractDir, devDir)
250
+ log.verbose('install', 'version is already installed, need to check "installVersion"');
251
+ const installVersionFile = path.resolve(devDir, 'installVersion');
252
+ let installVersion = 0;
253
+ try {
254
+ const ver = await fs.readFile(installVersionFile, 'ascii');
255
+ installVersion = parseInt(ver, 10) || 0;
256
+ } catch (err) {
257
+ if (err.code !== 'ENOENT') throw err;
283
258
  }
284
- } finally {
285
- if (tarExtractDir !== devDir) {
259
+ log.verbose('got "installVersion"', installVersion);
260
+ log.verbose('needs "installVersion"', gyp.package.installVersion);
261
+ if (installVersion < gyp.package.installVersion) {
262
+ log.verbose('install', 'version is no good; reinstalling');
286
263
  try {
287
- // 尝试清理临时目录
288
- await fs.rm(tarExtractDir, { recursive: true })
289
- } catch {
290
- log.warn('failed to clean up temp tarball extract directory')
291
- }
292
- }
293
- }
294
-
295
- async function downloadShasums () {
296
- log.verbose('check download content checksum, need to download `SHASUMS256.txt`...')
297
- log.verbose('checksum url', release.shasumsUrl)
298
-
299
- const res = await download(gyp, release.shasumsUrl)
300
-
301
- if (res.status !== 200) {
302
- throw new Error(`${res.status} status code downloading checksum`)
264
+ return await go();
265
+ } catch (err) { return rollback(err) }
303
266
  }
304
-
305
- for (const line of (await res.text()).trim().split('\n')) {
306
- const items = line.trim().split(/\s+/)
307
- if (items.length !== 2) {
308
- return
267
+ log.verbose('install', 'version is good');
268
+ if (win) {
269
+ log.verbose('on Windows; need to check node.lib');
270
+ const nodeLibPath = path.resolve(devDir, arch, 'node.lib');
271
+ try {
272
+ await fs.stat(nodeLibPath);
273
+ } catch (err) {
274
+ if (err.code === 'ENOENT') {
275
+ log.verbose('install', `version not already installed for ${arch}, continuing with install`, release.version);
276
+ try {
277
+ shouldDownloadTarball = false;
278
+ return await go();
279
+ } catch (err) { return rollback(err) }
280
+ }
281
+ else if (err.code === 'EACCES') return eaccesFallback(err);
282
+ throw err;
309
283
  }
310
-
311
- // 0035d18e2dcf9aad669b1c7c07319e17abfe3762 ./node-v0.11.4.tar.gz
312
- const name = items[1].replace(/^\.\//, '')
313
- expectShasums[name] = items[0]
314
284
  }
315
-
316
- log.verbose('checksum data', JSON.stringify(expectShasums))
317
- }
318
-
319
- async function downloadNodeLib () {
320
- log.verbose('on Windows; need to download `' + release.name + '.lib`...')
321
- const dir = path.resolve(tarExtractDir, arch)
322
- const targetLibPath = path.resolve(dir, release.name + '.lib')
323
- const { libUrl, libPath } = release[arch]
324
- const name = `${arch} ${release.name}.lib`
325
- log.verbose(name, 'dir', dir)
326
- log.verbose(name, 'url', libUrl)
327
-
328
- await fs.mkdir(dir, { recursive: true })
329
- log.verbose('streaming', name, 'to:', targetLibPath)
330
-
331
- const res = await download(gyp, libUrl)
332
-
333
- // 只下载所需的 node.lib,若未拿到则抛错
334
- if (res.status !== 200) {
335
- throw new Error(`${res.status} status code downloading ${name}`)
336
- }
337
-
338
- return pipeline(
339
- res.body,
340
- new ShaSum((_, checksum) => {
341
- contentShasums[libPath] = checksum
342
- log.verbose('content checksum', libPath, checksum)
343
- }),
344
- createWriteStream(targetLibPath)
345
- )
346
- } // downloadNodeLib()
347
- } // go()
348
-
349
- /**
350
- * 检查给定文件名是否对本次安装"有效"
351
- */
352
-
353
- function valid (file) {
354
- // 头文件
355
- const extname = path.extname(file)
356
- return extname === '.h' || extname === '.gypi'
357
- }
358
-
359
- async function rollback (err) {
360
- log.warn('install', 'got an error, rolling back install')
361
- // 出错则回滚安装
362
- await gyp.commands.remove([release.versionDir])
363
- throw err
364
- }
365
-
366
- /**
367
- * EACCES 回退方案,用于处理 npm 的 `sudo` 行为:
368
- * npm 在调用任何子进程(如 node-gyp)前会先降权,
369
- * 于是 "nobody" 用户没有权限创建 dev dir。
370
- * 作为回退,把 tmpdir() 作为本次安装的 dev dir。
371
- * 这不理想,但至少编译能成功……
372
- */
373
-
374
- async function eaccesFallback (err) {
375
- const noretry = '--node_gyp_internal_noretry'
376
- if (argv.indexOf(noretry) !== -1) {
377
- throw err
378
- }
379
- const tmpdir = os.tmpdir()
380
- gyp.devDir = path.resolve(tmpdir, '.node-gyp')
381
- let userString = ''
382
- try {
383
- // os.userInfo 在某些系统上会失败,这里不关键
384
- userString = ` ("${os.userInfo().username}")`
385
- } catch (e) {}
386
- log.warn('EACCES', 'current user%s does not have permission to access the dev dir "%s"', userString, devDir)
387
- log.warn('EACCES', 'attempting to reinstall using temporary dev dir "%s"', gyp.devDir)
388
- if (process.cwd() === tmpdir) {
389
- log.verbose('tmpdir == cwd', 'automatically will remove dev files after to save disk space')
390
- gyp.todo.push({ name: 'remove', args: argv })
285
+ } else {
286
+ try {
287
+ return await go();
288
+ } catch (err) { return rollback(err) }
391
289
  }
392
- return gyp.commands.install([noretry].concat(argv))
393
290
  }
394
- }
395
291
 
396
292
  /**
397
293
  * SHA256 校验流
398
294
  */
399
295
  class ShaSum extends Transform {
400
- constructor (callback) {
401
- super()
402
- this._callback = callback
403
- this._digester = crypto.createHash('sha256')
296
+ constructor(callback) {
297
+ super(), this._callback = callback, this._digester = crypto.createHash('sha256');
404
298
  }
405
299
 
406
- _transform (chunk, _, callback) {
407
- this._digester.update(chunk)
408
- callback(null, chunk)
300
+ _transform(chunk, _, callback) {
301
+ this._digester.update(chunk), callback(null, chunk)
409
302
  }
410
303
 
411
- _flush (callback) {
412
- this._callback(null, this._digester.digest('hex'))
413
- callback()
304
+ _flush(callback) {
305
+ this._callback(null, this._digester.digest('hex')), callback()
414
306
  }
415
307
  }
416
308
 
417
- install.usage = 'Install node development files for the specified node version.'
418
-
419
- export { install }
309
+ install.usage = 'Install node development files for the specified node version.';
310
+ export { install }