@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.
@@ -1,27 +1,21 @@
1
- import * as log from './log.js'
2
- import semver from 'semver'
3
- import { execFile } from './util.js'
4
- import os from 'os'
1
+ import * as log from './log.js';
2
+ import semver from 'semver';
3
+ import { execFile } from './util.js';
4
+ import os from 'os';
5
5
 
6
- const win = process.platform === 'win32'
6
+ const win = process.platform === 'win32',
7
+ /**
8
+ * 获取当前操作系统用户名(失败时返回 undefined)
9
+ */
10
+ getOsUserInfo = () => {
11
+ try { return os.userInfo().username } catch { }
12
+ },
13
+ systemDrive = process.env.SystemDrive || 'C:', username = process.env.USERNAME || process.env.USER || getOsUserInfo(),
14
+ localAppData = process.env.LOCALAPPDATA || `${systemDrive}\\${username}\\AppData\\Local`,
15
+ foundLocalAppData = process.env.LOCALAPPDATA || username,
16
+ programFiles = process.env.ProgramW6432 || process.env.ProgramFiles || `${systemDrive}\\Program Files`,
17
+ programFilesX86 = process.env['ProgramFiles(x86)'] || `${programFiles} (x86)`, winDefaultLocationsArray = [];
7
18
 
8
- /**
9
- * 获取当前操作系统用户名(失败时返回 undefined)
10
- */
11
- const getOsUserInfo = () => {
12
- try {
13
- return os.userInfo().username
14
- } catch {}
15
- }
16
-
17
- const systemDrive = process.env.SystemDrive || 'C:'
18
- const username = process.env.USERNAME || process.env.USER || getOsUserInfo()
19
- const localAppData = process.env.LOCALAPPDATA || `${systemDrive}\\${username}\\AppData\\Local`
20
- const foundLocalAppData = process.env.LOCALAPPDATA || username
21
- const programFiles = process.env.ProgramW6432 || process.env.ProgramFiles || `${systemDrive}\\Program Files`
22
- const programFilesX86 = process.env['ProgramFiles(x86)'] || `${programFiles} (x86)`
23
-
24
- const winDefaultLocationsArray = []
25
19
  for (const majorMinor of ['311', '310', '39', '38']) {
26
20
  if (foundLocalAppData) {
27
21
  winDefaultLocationsArray.push(
@@ -30,13 +24,13 @@ for (const majorMinor of ['311', '310', '39', '38']) {
30
24
  `${localAppData}\\Programs\\Python\\Python${majorMinor}-32\\python.exe`,
31
25
  `${programFiles}\\Python${majorMinor}-32\\python.exe`,
32
26
  `${programFilesX86}\\Python${majorMinor}-32\\python.exe`
33
- )
27
+ );
34
28
  } else {
35
29
  winDefaultLocationsArray.push(
36
30
  `${programFiles}\\Python${majorMinor}\\python.exe`,
37
31
  `${programFiles}\\Python${majorMinor}-32\\python.exe`,
38
32
  `${programFilesX86}\\Python${majorMinor}-32\\python.exe`
39
- )
33
+ );
40
34
  }
41
35
  }
42
36
 
@@ -44,155 +38,130 @@ for (const majorMinor of ['311', '310', '39', '38']) {
44
38
  * Python 查找器:按顺序尝试多种途径定位可用的 Python 3.6+
45
39
  */
46
40
  class PythonFinder {
47
- static findPython = (...args) => new PythonFinder(...args).findPython()
41
+ static findPython = (...args) => new PythonFinder(...args).findPython();
48
42
 
49
- log = log.withPrefix('find Python')
50
- argsExecutable = ['-c', 'import sys; print(sys.executable);']
51
- argsVersion = ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);']
52
- semverRange = '>=3.6.0'
43
+ log = log.withPrefix('find Python');
44
+ argsExecutable = ['-c', 'import sys; print(sys.executable);'];
45
+ argsVersion = ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);'];
46
+ semverRange = '>=3.6.0';
53
47
 
54
48
  // 以下可在测试时覆盖:
55
- execFile = execFile
56
- env = process.env
57
- win = win
58
- pyLauncher = 'py.exe'
59
- winDefaultLocations = winDefaultLocationsArray
49
+ execFile = execFile;
50
+ env = process.env;
51
+ win = win;
52
+ pyLauncher = 'py.exe';
53
+ winDefaultLocations = winDefaultLocationsArray;
60
54
 
61
- constructor (configPython) {
62
- this.configPython = configPython
63
- this.errorLog = []
55
+ constructor(configPython) {
56
+ this.configPython = configPython, this.errorLog = [];
64
57
  }
65
58
 
66
59
  // 以 verbose 级别记录日志,同时保存下来,出错时以 error 级别展示,便于诊断
67
- addLog (message) {
68
- this.log.verbose(message)
69
- this.errorLog.push(message)
60
+ addLog(message) {
61
+ this.log.verbose(message), this.errorLog.push(message);
70
62
  }
71
63
 
72
64
  // 依次尝试多种 Python 定位方式。忽略错误,直到找到可用版本
73
- async findPython () {
74
- const SKIP = 0
75
- const FAIL = 1
76
- const toCheck = (() => {
77
- if (this.env.NODE_GYP_FORCE_PYTHON) {
78
- return [{
79
- before: () => {
80
- this.addLog(
81
- 'checking Python explicitly set from NODE_GYP_FORCE_PYTHON')
82
- this.addLog('- process.env.NODE_GYP_FORCE_PYTHON is ' +
83
- `"${this.env.NODE_GYP_FORCE_PYTHON}"`)
65
+ async findPython() {
66
+ const SKIP = 0, FAIL = 1,
67
+ toCheck = (() => {
68
+ if (this.env.NODE_GYP_FORCE_PYTHON) {
69
+ return [{
70
+ before: () => {
71
+ this.addLog('checking Python explicitly set from NODE_GYP_FORCE_PYTHON');
72
+ this.addLog('- process.env.NODE_GYP_FORCE_PYTHON is ' + `"${this.env.NODE_GYP_FORCE_PYTHON}"`);
73
+ }, check: () => this.checkCommand(this.env.NODE_GYP_FORCE_PYTHON)
74
+ }]
75
+ }
76
+ const checks = [
77
+ {
78
+ before: () => {
79
+ if (!this.configPython) {
80
+ this.addLog('Python is not set from command line or npm configuration');
81
+ return SKIP;
82
+ }
83
+ this.addLog('checking Python explicitly set from command line or ' + 'npm configuration')
84
+ this.addLog('- "--python=" or "npm config get python" is ' + `"${this.configPython}"`)
85
+ }, check: () => this.checkCommand(this.configPython)
84
86
  },
85
- check: () => this.checkCommand(this.env.NODE_GYP_FORCE_PYTHON)
86
- }]
87
- }
87
+ {
88
+ before: () => {
89
+ if (!this.env.PYTHON) {
90
+ this.addLog('Python is not set from environment variable ' + 'PYTHON');
91
+ return SKIP;
92
+ }
93
+ this.addLog('checking Python explicitly set from environment ' + 'variable PYTHON');
94
+ this.addLog(`- process.env.PYTHON is "${this.env.PYTHON}"`);
95
+ },
96
+ check: () => this.checkCommand(this.env.PYTHON)
97
+ }
98
+ ];
88
99
 
89
- const checks = [
90
- {
91
- before: () => {
92
- if (!this.configPython) {
93
- this.addLog(
94
- 'Python is not set from command line or npm configuration')
95
- return SKIP
96
- }
97
- this.addLog('checking Python explicitly set from command line or ' +
98
- 'npm configuration')
99
- this.addLog('- "--python=" or "npm config get python" is ' +
100
- `"${this.configPython}"`)
101
- },
102
- check: () => this.checkCommand(this.configPython)
103
- },
104
- {
105
- before: () => {
106
- if (!this.env.PYTHON) {
107
- this.addLog('Python is not set from environment variable ' +
108
- 'PYTHON')
109
- return SKIP
110
- }
111
- this.addLog('checking Python explicitly set from environment ' +
112
- 'variable PYTHON')
113
- this.addLog(`- process.env.PYTHON is "${this.env.PYTHON}"`)
114
- },
115
- check: () => this.checkCommand(this.env.PYTHON)
100
+ if (this.win) {
101
+ checks.push({
102
+ before: () => {
103
+ this.addLog('checking if the py launcher can be used to find Python 3');
104
+ }, check: () => this.checkPyLauncher()
105
+ })
116
106
  }
117
- ]
118
107
 
119
- if (this.win) {
120
- checks.push({
121
- before: () => {
122
- this.addLog(
123
- 'checking if the py launcher can be used to find Python 3')
108
+ checks.push(...[
109
+ {
110
+ before: () => { this.addLog('checking if "python3" can be used') },
111
+ check: () => this.checkCommand('python3')
124
112
  },
125
- check: () => this.checkPyLauncher()
126
- })
127
- }
128
-
129
- checks.push(...[
130
- {
131
- before: () => { this.addLog('checking if "python3" can be used') },
132
- check: () => this.checkCommand('python3')
133
- },
134
- {
135
- before: () => { this.addLog('checking if "python" can be used') },
136
- check: () => this.checkCommand('python')
137
- }
138
- ])
113
+ {
114
+ before: () => { this.addLog('checking if "python" can be used') },
115
+ check: () => this.checkCommand('python')
116
+ }
117
+ ])
139
118
 
140
- if (this.win) {
141
- for (let i = 0; i < this.winDefaultLocations.length; ++i) {
142
- const location = this.winDefaultLocations[i]
143
- checks.push({
144
- before: () => this.addLog(`checking if Python is ${location}`),
145
- check: () => this.checkExecPath(location)
146
- })
119
+ if (this.win) {
120
+ for (let i = 0; i < this.winDefaultLocations.length; ++i) {
121
+ const location = this.winDefaultLocations[i];
122
+ checks.push({
123
+ before: () => this.addLog(`checking if Python is ${location}`),
124
+ check: () => this.checkExecPath(location)
125
+ })
126
+ }
147
127
  }
148
- }
149
128
 
150
- return checks
151
- })()
129
+ return checks;
130
+ })();
152
131
 
153
132
  for (const check of toCheck) {
154
133
  const before = check.before()
155
- if (before === SKIP) {
156
- continue
157
- }
158
- if (before === FAIL) {
159
- return this.fail()
160
- }
134
+ if (before === SKIP) continue;
135
+ if (before === FAIL) return this.fail();
161
136
  try {
162
- return await check.check()
137
+ return await check.check();
163
138
  } catch (err) {
164
- this.log.silly('runChecks: err = %j', (err && err.stack) || err)
139
+ this.log.silly('runChecks: err = %j', (err && err.stack) || err);
165
140
  }
166
141
  }
167
142
 
168
- return this.fail()
143
+ return this.fail();
169
144
  }
170
145
 
171
146
  // 检查命令是否为可用的 Python。成功则返回结果。
172
147
  // Windows 下需在 CMD shell 中运行以支持 BAT/CMD 启动器。
173
- async checkCommand (command) {
174
- let exec = command
175
- let args = this.argsExecutable
176
- let shell = false
177
- if (this.win) {
178
- // 参数需手动加引号
179
- exec = `"${exec}"`
180
- args = args.map(a => `"${a}"`)
181
- shell = true
182
- }
148
+ async checkCommand(command) {
149
+ let exec = command, args = this.argsExecutable, shell = false;
150
+ // 参数需手动加引号
151
+ if (this.win) exec = `"${exec}"`, args = args.map(a => `"${a}"`), shell = true;
183
152
 
184
- this.log.verbose(`- executing "${command}" to get executable path`)
153
+ this.log.verbose(`- executing "${command}" to get executable path`);
185
154
  // 可能结果:
186
155
  // - 报错:不在 PATH、不可执行或执行失败
187
156
  // - 乱码:后续版本检查会失败
188
157
  // - 可执行文件的绝对路径
189
158
  try {
190
- const execPath = await this.run(exec, args, shell)
191
- this.addLog(`- executable path is "${execPath}"`)
159
+ const execPath = await this.run(exec, args, shell);
160
+ this.addLog(`- executable path is "${execPath}"`);
192
161
  return this.checkExecPath(execPath)
193
162
  } catch (err) {
194
- this.addLog(`- "${command}" is not in PATH or produced an error`)
195
- throw err
163
+ this.addLog(`- "${command}" is not in PATH or produced an error`);
164
+ throw err;
196
165
  }
197
166
  }
198
167
 
@@ -200,104 +169,99 @@ class PythonFinder {
200
169
  // Windows 上的 Python 发行版默认安装 "py.exe" 启动器,比 python.exe 出现在 PATH 更常见。
201
170
  // 因为 py 启动器同时支持 Python 2 和 3,这里显式请求 Python 3:用 "-3" 作为首个参数。
202
171
  // 由于 "py.exe -3" 对 execFile 来说不是合法可执行文件,需要用启动器找出真正的 python.exe 路径。
203
- async checkPyLauncher () {
172
+ async checkPyLauncher() {
204
173
  this.log.verbose(`- executing "${this.pyLauncher}" to get Python 3 executable path`)
205
174
  // 可能结果:与 checkCommand 相同
206
175
  try {
207
- const execPath = await this.run(this.pyLauncher, ['-3', ...this.argsExecutable], false)
208
- this.addLog(`- executable path is "${execPath}"`)
209
- return this.checkExecPath(execPath)
176
+ const execPath = await this.run(this.pyLauncher, ['-3', ...this.argsExecutable], false);
177
+ this.addLog(`- executable path is "${execPath}"`);
178
+ return this.checkExecPath(execPath);
210
179
  } catch (err) {
211
- this.addLog(`- "${this.pyLauncher}" is not in PATH or produced an error`)
212
- throw err
180
+ this.addLog(`- "${this.pyLauncher}" is not in PATH or produced an error`);
181
+ throw err;
213
182
  }
214
183
  }
215
184
 
216
185
  // 检查某个 Python 可执行文件版本是否可用。成功则返回结果。
217
- async checkExecPath (execPath) {
186
+ async checkExecPath(execPath) {
218
187
  this.log.verbose(`- executing "${execPath}" to get version`)
219
188
  // 可能结果:
220
189
  // - 报错:可执行文件无法运行(多半上一步输出了乱码)
221
190
  // - 乱码:上一步不知为何输出了可执行文件路径,验证版本时会失败
222
191
  // - Python 可执行文件的版本号
223
192
  try {
224
- const version = await this.run(execPath, this.argsVersion, false)
225
- this.addLog(`- version is "${version}"`)
193
+ const version = await this.run(execPath, this.argsVersion, false);
194
+ this.addLog(`- version is "${version}"`);
226
195
 
227
- const range = new semver.Range(this.semverRange)
228
- let valid = false
196
+ const range = new semver.Range(this.semverRange);
197
+ let valid = false;
229
198
  try {
230
- valid = range.test(version)
199
+ valid = range.test(version);
231
200
  } catch (err) {
232
- this.log.silly('range.test() threw:\n%s', err.stack)
233
- this.addLog(`- "${execPath}" does not have a valid version`)
234
- this.addLog('- is it a Python executable?')
235
- throw err
201
+ this.log.silly('range.test() threw:\n%s', err.stack);
202
+ this.addLog(`- "${execPath}" does not have a valid version`);
203
+ this.addLog('- is it a Python executable?');
204
+ throw err;
236
205
  }
237
206
  if (!valid) {
238
- this.addLog(`- version is ${version} - should be ${this.semverRange}`)
239
- this.addLog('- THIS VERSION OF PYTHON IS NOT SUPPORTED')
240
- throw new Error(`Found unsupported Python version ${version}`)
207
+ this.addLog(`- version is ${version} - should be ${this.semverRange}`);
208
+ this.addLog('- THIS VERSION OF PYTHON IS NOT SUPPORTED');
209
+ throw new Error(`Found unsupported Python version ${version}`);
241
210
  }
242
- return this.succeed(execPath, version)
211
+ return this.succeed(execPath, version);
243
212
  } catch (err) {
244
- this.addLog(`- "${execPath}" could not be run`)
245
- throw err
213
+ this.addLog(`- "${execPath}" could not be run`);
214
+ throw err;
246
215
  }
247
216
  }
248
217
 
249
218
  // 运行可执行文件或 shell 命令,并去除输出两端空白
250
- async run (exec, args, shell) {
251
- const env = Object.assign({}, this.env)
252
- env.TERM = 'dumb'
253
- const opts = { env, shell }
219
+ async run(exec, args, shell) {
220
+ const env = Object.assign({}, this.env), opts = { env, shell };
254
221
 
255
- this.log.silly('execFile: exec = %j', exec)
256
- this.log.silly('execFile: args = %j', args)
257
- this.log.silly('execFile: opts = %j', opts)
222
+ env.TERM = 'dumb';
223
+ this.log.silly('execFile: exec = %j', exec);
224
+ this.log.silly('execFile: args = %j', args);
225
+ this.log.silly('execFile: opts = %j', opts);
258
226
  try {
259
227
  const [err, stdout, stderr] = await this.execFile(exec, args, opts)
260
- this.log.silly('execFile result: err = %j', (err && err.stack) || err)
261
- this.log.silly('execFile result: stdout = %j', stdout)
262
- this.log.silly('execFile result: stderr = %j', stderr)
263
- return stdout.trim()
228
+ this.log.silly('execFile result: err = %j', (err && err.stack) || err);
229
+ this.log.silly('execFile result: stdout = %j', stdout);
230
+ this.log.silly('execFile result: stderr = %j', stderr);
231
+ return stdout.trim();
264
232
  } catch (err) {
265
- this.log.silly('execFile: threw:\n%s', err.stack)
266
- throw err
233
+ this.log.silly('execFile: threw:\n%s', err.stack);
234
+ throw err;
267
235
  }
268
236
  }
269
237
 
270
- succeed (execPath, version) {
271
- this.log.info(`using Python version ${version} found at "${execPath}"`)
272
- return execPath
238
+ succeed(execPath, version) {
239
+ this.log.info(`using Python version ${version} found at "${execPath}"`);
240
+ return execPath;
273
241
  }
274
242
 
275
- fail () {
276
- const errorLog = this.errorLog.join('\n')
277
-
278
- const pathExample = this.win
279
- ? 'C:\\Path\\To\\python.exe'
280
- : '/path/to/pythonexecutable'
281
- // Windows 80 列控制台,用到 X 标记列之前(含日志前缀共 79 字符,可用 58 字符):
282
- // X
283
- const info = [
284
- '**********************************************************',
285
- 'You need to install the latest version of Python.',
286
- 'Node-gyp should be able to find and use Python. If not,',
287
- 'you can try one of the following options:',
288
- `- Use the switch --python="${pathExample}"`,
289
- ' (accepted by both node-gyp and npm)',
290
- '- Set the environment variable PYTHON',
291
- '- Set the npm configuration variable python:',
292
- ` npm config set python "${pathExample}"`,
293
- 'For more information consult the documentation at:',
294
- 'https://github.com/nodejs/node-gyp#installation',
295
- '**********************************************************'
296
- ].join('\n')
243
+ fail() {
244
+ const errorLog = this.errorLog.join('\n'),
245
+ pathExample = this.win ? 'C:\\Path\\To\\python.exe' : '/path/to/pythonexecutable',
246
+ // Windows 80 列控制台,用到 X 标记列之前(含日志前缀共 79 字符,可用 58 字符)
247
+ info = [
248
+ '**********************************************************',
249
+ 'You need to install the latest version of Python.',
250
+ 'Node-gyp should be able to find and use Python. If not,',
251
+ 'you can try one of the following options:',
252
+ `- Use the switch --python="${pathExample}"`,
253
+ ' (accepted by both node-gyp and npm)',
254
+ '- Set the environment variable PYTHON',
255
+ '- Set the npm configuration variable python:',
256
+ ` npm config set python "${pathExample}"`,
257
+ 'For more information consult the documentation at:',
258
+ 'https://github.com/nodejs/node-gyp#installation',
259
+ '**********************************************************'
260
+ ].join('\n');
297
261
 
298
- this.log.error(`\n${errorLog}\n\n${info}\n`)
299
- throw new Error('Could not find any Python installation to use')
262
+ this.log.error(`\n${errorLog}\n\n${info}\n`);
263
+ throw new Error('未找到可用的 Python 安装');
300
264
  }
301
265
  }
302
266
 
303
- export { PythonFinder }
267
+ export { PythonFinder }