@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/CHANGELOG.md +56 -36
- package/README.md +256 -252
- package/addon.gypi +13 -1
- package/bin/node-gyp.js +66 -109
- package/gyp/pylib/gyp/MSVSSettings.py +13 -0
- package/lib/build.js +127 -209
- package/lib/clean.js +13 -16
- package/lib/configure.js +183 -265
- package/lib/create-config-gypi.js +102 -156
- package/lib/download.js +29 -42
- package/lib/find-node-directory.js +38 -55
- package/lib/find-python.js +162 -198
- package/lib/find-visualstudio.js +196 -295
- package/lib/install.js +269 -378
- package/lib/list.js +19 -25
- package/lib/log.js +69 -133
- package/lib/node-gyp.js +62 -107
- package/lib/process-release.js +100 -139
- package/lib/rebuild.js +3 -8
- package/lib/remove.js +34 -44
- package/lib/util.js +62 -73
- package/package.json +5 -3
- package/gyp/LICENSE +0 -28
package/lib/find-python.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
174
|
-
let exec = command
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
256
|
-
this.log.silly('execFile:
|
|
257
|
-
this.log.silly('execFile:
|
|
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
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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('
|
|
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 }
|