@flun/nodejs-mobile-gyp 1.2.0 → 1.2.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/build.js CHANGED
@@ -7,8 +7,7 @@ import os from 'os';
7
7
  import { spawnSync } from 'child_process';
8
8
  import fsSync from 'fs';
9
9
 
10
- const fs = gracefulFs.promises,
11
- _hostIsWindows = process.platform === 'win32',
10
+ const fs = gracefulFs.promises, _hostIsWindows = process.platform === 'win32',
12
11
  // Android 交叉编译即使宿主是 Windows,也必须走 make(make-android 生成器)
13
12
  _isAndroidCrossBuild = process.env.npm_config_platform === 'android' || process.env.npm_config_format === 'make-android',
14
13
  // win 在此文件中语义为"使用 MSBuild":仅 Windows 宿主且非 Android 交叉编译
@@ -20,12 +19,11 @@ const fs = gracefulFs.promises,
20
19
  */
21
20
  findWindowsShellDir = () => {
22
21
  if (process.platform !== 'win32') return null;
23
- const tryShDir = (dir) => {
22
+ const tryShDir = dir => {
24
23
  try { return fsSync.existsSync(path.join(dir, 'sh.exe')) ? dir : null } catch { return null }
25
- };
26
- const toShDir = (gitRoot) => tryShDir(path.join(gitRoot, 'usr', 'bin'));
27
- // 1) 注册表:Git for Windows 安装时必写,与 PATH 无关(最可靠)
28
- const reg = process.env.SystemRoot ? path.join(process.env.SystemRoot, 'System32', 'reg.exe') : 'reg.exe';
24
+ }, toShDir = (gitRoot) => tryShDir(path.join(gitRoot, 'usr', 'bin')),
25
+ // 1) 注册表:Git for Windows 安装时必写,与 PATH 无关(最可靠)
26
+ reg = process.env.SystemRoot ? path.join(process.env.SystemRoot, 'System32', 'reg.exe') : 'reg.exe';
29
27
  for (const hive of ['HKLM', 'HKCU']) {
30
28
  for (const key of ['SOFTWARE\\GitForWindows', 'SOFTWARE\\WOW6432Node\\GitForWindows']) {
31
29
  try {
@@ -44,8 +42,7 @@ const fs = gracefulFs.promises,
44
42
  try {
45
43
  const gitPath = which.sync('git');
46
44
  if (gitPath) {
47
- const dir = path.resolve(path.dirname(gitPath), '..', 'usr', 'bin');
48
- const r = tryShDir(dir);
45
+ const dir = path.resolve(path.dirname(gitPath), '..', 'usr', 'bin'), r = tryShDir(dir);
49
46
  if (r) return r;
50
47
  }
51
48
  } catch { }
@@ -54,16 +51,13 @@ const fs = gracefulFs.promises,
54
51
  if (!pd) continue;
55
52
  try {
56
53
  if (fsSync.existsSync(path.join(pd, 'git.exe'))) {
57
- const dir = path.resolve(pd, '..', 'usr', 'bin');
58
- const r = tryShDir(dir);
54
+ const dir = path.resolve(pd, '..', 'usr', 'bin'), r = tryShDir(dir);
59
55
  if (r) return r;
60
56
  }
61
57
  } catch { }
62
58
  }
63
59
  // 4) 全盘符常见位置兜底
64
- const suffixes = [
65
- 'Program Files\\Git', 'Program Files (x86)\\Git', 'Git', 'msys64',
66
- ];
60
+ const suffixes = ['Program Files\\Git', 'Program Files (x86)\\Git', 'Git', 'msys64'];
67
61
  for (const drv of ['C:', 'D:', 'E:', 'F:']) {
68
62
  for (const sfx of suffixes) {
69
63
  const dir = tryShDir(drv + '\\' + sfx);
@@ -104,7 +98,7 @@ const fs = gracefulFs.promises,
104
98
  // 注意:msbuild 的 Condition 字符串相等比较不区分大小写
105
99
  const archLower = arch.toLowerCase(), p = archLower === 'x64' ? 'x64' : (archLower === 'arm' ? 'ARM'
106
100
  : (archLower === 'arm64' ? 'ARM64' : 'Win32'));
107
- argv.push('/p:Configuration=' + buildType + ';Platform=' + p)
101
+ argv.push('/p:Configuration=' + buildType + ';Platform=' + p);
108
102
  if (jobs) {
109
103
  j = parseInt(jobs, 10)
110
104
  if (!isNaN(j) && j > 0) argv.push('/m:' + j);
@@ -134,21 +128,18 @@ const fs = gracefulFs.promises,
134
128
  const shellDir = findWindowsShellDir();
135
129
  if (shellDir) {
136
130
  process.env.PATH = `${shellDir};${process.env.PATH}`;
137
- log.verbose('bin shell', `prepended ${shellDir} to PATH for make`);
138
- } else {
139
- log.warn('bin shell', 'no sh.exe found; GNU make 的 Unix 命令可能失败;建议安装 Git for Windows');
131
+ log.verbose('bin 环境', `已将 ${shellDir} 加入 PATH,供 make 使用`);
140
132
  }
133
+ else log.warn('shell 环境', '未找到 sh.exe;GNU make 的 Unix 命令可能失败;建议安装 Git for Windows');
141
134
  }
142
- process.env.PATH = _hostIsWindows
143
- ? `${buildBinsDir};${process.env.PATH}`
144
- : `${buildBinsDir}:${process.env.PATH}`;
135
+ process.env.PATH = _hostIsWindows ? `${buildBinsDir};${process.env.PATH}` : `${buildBinsDir}:${process.env.PATH}`;
145
136
  await fs.mkdir(buildBinsDir, { recursive: true });
146
137
 
147
138
  if (_hostIsWindows) {
148
139
  // Windows 无 python3 名称,且创建符号链接需要管理员权限;改用 .cmd 桥接
149
140
  const shimPath = path.join(buildBinsDir, 'python3.cmd');
150
141
  await fs.writeFile(shimPath, `@echo off\r\n"${python}" %*\r\n`);
151
- log.verbose('bin shims', `created python3.cmd shim to "${python}" and added to PATH`);
142
+ log.verbose('bin 垫片', `已创建 python3.cmd 指向 "${python}" 并加入 PATH`);
152
143
  } else {
153
144
  const symlinkDestination = path.join(buildBinsDir, 'python3');
154
145
  try {
@@ -157,16 +148,15 @@ const fs = gracefulFs.promises,
157
148
  if (err.code !== 'ENOENT') throw err;
158
149
  }
159
150
  await fs.symlink(python, symlinkDestination);
160
- log.verbose('bin symlinks', `created symlink to "${python}" in "${buildBinsDir}" and added to PATH`);
151
+ log.verbose('bin 符号链接', `已在 "${buildBinsDir}" 创建指向 "${python}" 的符号链接并加入 PATH`);
161
152
  }
162
153
  }
163
154
 
164
155
  const proc = gyp.spawn(command, argv);
165
156
  await new Promise((resolve, reject) => proc.on('exit', async (code, signal) => {
166
157
  if (buildBinsDir) await fs.rm(buildBinsDir, { recursive: true }); // 清理构建期依赖的符号链接
167
-
168
- if (code !== 0) return reject(new Error('`' + command + '` failed with exit code: ' + code));
169
- if (signal) return reject(new Error('`' + command + '` got signal: ' + signal));
158
+ if (code !== 0) return reject(new Error('`' + command + '` 执行失败,退出码: ' + code));
159
+ if (signal) return reject(new Error('`' + command + '` 收到信号: ' + signal));
170
160
  resolve();
171
161
  }))
172
162
  },
@@ -174,16 +164,16 @@ const fs = gracefulFs.promises,
174
164
  * 用 node-which 定位 msbuild / make 可执行文件
175
165
  */
176
166
  doWhich = async () => {
177
- // Windows 使用 node-gyp configure 提供的 msbuild
167
+ // Windows 使用 nodejs-mobile-gyp configure 提供的 msbuild
178
168
  if (win) {
179
- if (!config.variables.msbuild_path) throw new Error('MSBuild 未设置,请先运行 `node-gyp configure`');
180
- command = config.variables.msbuild_path, log.verbose('using MSBuild:', command), await doBuild();
169
+ if (!config.variables.msbuild_path) throw new Error('MSBuild 未设置,请先运行 `nodejs-mobile-gyp configure`');
170
+ command = config.variables.msbuild_path, log.verbose('使用 MSBuild:', command), await doBuild();
181
171
  return;
182
172
  }
183
173
 
184
174
  // 先确认构建命令在 PATH 中
185
175
  const execPath = await which(command);
186
- log.verbose('`which` succeeded for `' + command + '`', execPath), await doBuild();
176
+ log.verbose('`which` 成功解析 `' + command + '`', execPath), await doBuild();
187
177
  },
188
178
  /**
189
179
  * Windows 下查找第一个 build/*.sln 文件
@@ -191,7 +181,7 @@ const fs = gracefulFs.promises,
191
181
  findSolutionFile = async () => {
192
182
  const files = await glob('build/*.sln')
193
183
  if (files.length === 0) throw new Error('未找到 *.sln 文件,是否运行过 configure?');
194
- guessedSolution = files[0], log.verbose('found first Solution file', guessedSolution), await doWhich();
184
+ guessedSolution = files[0], log.verbose('找到首个解决方案文件', guessedSolution), await doWhich();
195
185
  },
196
186
  /**
197
187
  * 加载 configure 阶段生成的 config.gypi
@@ -202,7 +192,7 @@ const fs = gracefulFs.promises,
202
192
  const configPath = path.resolve('build', 'config.gypi');
203
193
  data = await fs.readFile(configPath, 'utf8');
204
194
  } catch (err) {
205
- if (err.code === 'ENOENT') throw new Error('请先运行 `node-gyp configure`');
195
+ if (err.code === 'ENOENT') throw new Error('请先运行 `nodejs-mobile-gyp configure`');
206
196
  else throw err;
207
197
  }
208
198
 
@@ -213,13 +203,13 @@ const fs = gracefulFs.promises,
213
203
  if ('debug' in gyp.opts) buildType = gyp.opts.debug ? 'Debug' : 'Release';
214
204
  if (!buildType) buildType = 'Release';
215
205
 
216
- log.verbose('build type', buildType), log.verbose('architecture', arch), log.verbose('node dev dir', nodeDir);
217
- log.verbose('python', python);
206
+ log.verbose('构建类型', buildType), log.verbose('架构', arch), log.verbose('node 开发目录', nodeDir);
207
+ log.verbose('Python', python);
218
208
 
219
209
  win ? await findSolutionFile() : await doWhich();
220
210
  };
221
211
  await loadConfigGypi();
222
212
  };
223
213
 
224
- build.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module';
214
+ build.usage = '调用 `' + (win ? 'msbuild' : 'make') + '` 构建模块';
225
215
  export { build };
package/lib/clean.js CHANGED
@@ -8,9 +8,9 @@ const fs = gracefulFs.promises,
8
8
  clean = async (gyp, argv) => {
9
9
  const buildDir = 'build';
10
10
 
11
- log.verbose('clean', 'removing "%s" directory', buildDir);
11
+ log.verbose('清理', '正在删除 "%s" 目录', buildDir);
12
12
  await fs.rm(buildDir, { recursive: true, force: true });
13
13
  }
14
14
 
15
- clean.usage = 'Removes any generated build files and the "out" dir';
15
+ clean.usage = '删除生成的构建文件与 "out" 目录';
16
16
  export { clean };
package/lib/configure.js CHANGED
@@ -30,11 +30,11 @@ const configure = async (gyp, argv) => {
30
30
  runGyp = async () => {
31
31
  if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
32
32
  if (win) {
33
- log.verbose('gyp', 'gyp format was not specified; forcing "msvs"');
33
+ log.verbose('gyp', '未指定 gyp 格式,强制使用 "msvs"');
34
34
  // 非 Windows 强制使用 make 目标
35
35
  argv.push('-f', ('format' in gyp.opts) ? gyp.opts.format : 'msvs');
36
36
  } else {
37
- log.verbose('gyp', 'gyp format was not specified; forcing "make"');
37
+ log.verbose('gyp', '未指定 gyp 格式,强制使用 "make"');
38
38
  // 非 Windows 强制使用 make 目标
39
39
  argv.push('-f', ('format' in gyp.opts) ? gyp.opts.format : 'make');
40
40
  }
@@ -42,7 +42,7 @@ const configure = async (gyp, argv) => {
42
42
 
43
43
  configs.forEach(config => argv.push('-I', config)); // 引入所有找到的 .gypi 文件
44
44
  // AIX 和 z/OS 需要设置导出文件路径,其中包含链接所需符号
45
- let nodeExpFile, nodeRootDir, candidates, logprefix = 'find exports file';
45
+ let nodeExpFile, nodeRootDir, candidates, logprefix = '查找导出文件';
46
46
  if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') {
47
47
  const ext = process.platform === 'os390' ? 'x' : 'exp';
48
48
  nodeRootDir = findNodeDirectory();
@@ -55,10 +55,10 @@ const configure = async (gyp, argv) => {
55
55
  'out/Debug/obj.target/libnode', 'lib/libnode'].map(file => file + '.' + ext);
56
56
 
57
57
  nodeExpFile = findAccessibleSync(logprefix, nodeRootDir, candidates);
58
- if (nodeExpFile !== undefined) log.verbose(logprefix, 'Found exports file: %s', nodeExpFile);
58
+ if (nodeExpFile !== undefined) log.verbose(logprefix, '已找到导出文件: %s', nodeExpFile);
59
59
  else {
60
- const msg = msgFormat('Could not find node.%s file in %s', ext, nodeRootDir);
61
- log.error(logprefix, 'Could not find exports file');
60
+ const msg = msgFormat('未找到 node.%s 文件,目录: %s', ext, nodeRootDir);
61
+ log.error(logprefix, '未找到导出文件');
62
62
  throw new Error(msg);
63
63
  }
64
64
  }
@@ -66,14 +66,14 @@ const configure = async (gyp, argv) => {
66
66
  // z/OS 需要设置 zoslib 头文件目录,其中包含 v8config.h 引用的头文件
67
67
  let zoslibIncDir;
68
68
  if (process.platform === 'os390') {
69
- logprefix = "find zoslib's zos-base.h:";
69
+ logprefix = "查找 zoslib 的 zos-base.h:";
70
70
  let msg, zoslibIncPath = process.env.ZOSLIB_INCLUDES;
71
71
  if (zoslibIncPath) {
72
72
  zoslibIncPath = findAccessibleSync(logprefix, zoslibIncPath, ['zos-base.h']);
73
73
  if (zoslibIncPath === undefined) {
74
- msg = msgFormat('Could not find zos-base.h file in the directory set ' +
75
- 'in ZOSLIB_INCLUDES environment variable: %s; set it ' +
76
- 'to the correct path, or unset it to search %s', process.env.ZOSLIB_INCLUDES, nodeRootDir)
74
+ msg = msgFormat('在 ZOSLIB_INCLUDES 指定的目录中未找到 zos-base.h 文件: ' +
75
+ '%s; 请将其设为正确路径,或取消设置以便在 ' +
76
+ '%s 中搜索', process.env.ZOSLIB_INCLUDES, nodeRootDir)
77
77
  }
78
78
  } else {
79
79
  candidates = [
@@ -82,14 +82,14 @@ const configure = async (gyp, argv) => {
82
82
  ]
83
83
  zoslibIncPath = findAccessibleSync(logprefix, nodeRootDir, candidates);
84
84
  if (zoslibIncPath === undefined) {
85
- msg = msgFormat('Could not find any of %s in directory %s; set ' +
86
- 'environmant variable ZOSLIB_INCLUDES to the path ' +
87
- 'that contains zos-base.h', candidates.toString(), nodeRootDir)
85
+ msg = msgFormat('未找到 %s 中列出的任意一项(目录: %s); ' +
86
+ '请设置环境变量 ZOSLIB_INCLUDES 指向包含 zos-base.h 的路径',
87
+ candidates.toString(), nodeRootDir);
88
88
  }
89
89
  }
90
90
  if (zoslibIncPath !== undefined) {
91
91
  zoslibIncDir = path.dirname(zoslibIncPath);
92
- log.verbose(logprefix, "Found zoslib's zos-base.h in: %s", zoslibIncDir);
92
+ log.verbose(logprefix, "在 %s 中找到 zoslib 的 zos-base.h", zoslibIncDir);
93
93
  } else if (release.version.split('.')[0] >= 16) {
94
94
  // zoslib 只随 Node v16 及以上发布
95
95
  log.error(logprefix, msg);
@@ -148,7 +148,7 @@ const configure = async (gyp, argv) => {
148
148
  await new Promise((resolve, reject) => {
149
149
  const cp = gyp.spawn(python, argv);
150
150
  cp.on('exit', code => {
151
- if (code !== 0) reject(new Error('`gyp` failed with exit code: ' + code));
151
+ if (code !== 0) reject(new Error('`gyp` 执行失败,退出码: ' + code));
152
152
  else resolve(); // 完成
153
153
  })
154
154
  })
@@ -158,9 +158,9 @@ const configure = async (gyp, argv) => {
158
158
  if (!name) return runGyp();
159
159
 
160
160
  const fullPath = path.resolve(name);
161
- log.verbose(name, 'checking for gypi file: %s', fullPath);
161
+ log.verbose(name, '正在检查 gypi 文件: %s', fullPath);
162
162
  try {
163
- await fs.stat(fullPath), log.verbose(name, 'found gypi file'), configs.push(fullPath);
163
+ await fs.stat(fullPath), log.verbose(name, '已找到 gypi 文件'), configs.push(fullPath);
164
164
  } catch (err) {
165
165
  if (err.code !== 'ENOENT') throw err; // ENOENT 时继续检查下一个 gypi 文件名
166
166
  }
@@ -177,10 +177,10 @@ const configure = async (gyp, argv) => {
177
177
  return findConfigs();
178
178
  },
179
179
  createBuildDir = async () => {
180
- log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir);
180
+ log.verbose('build 目录', '正在创建 "build" 目录: %s', buildDir);
181
181
 
182
182
  const isNew = await fs.mkdir(buildDir, { recursive: true });
183
- log.verbose('build dir', '"build" dir needed to be created?', isNew ? 'Yes' : 'No');
183
+ log.verbose('build 目录', '"build" 目录是否需要创建?', isNew ? '是' : '否');
184
184
  vsInfo = win ? await findVisualStudio(release.semver, gyp.opts['msvs-version']) : null;
185
185
  return createConfigFile();
186
186
  },
@@ -191,23 +191,23 @@ const configure = async (gyp, argv) => {
191
191
  if (gyp.opts.nodedir) {
192
192
  // 指定了 --nodedir,用它作为开发文件目录
193
193
  nodeDir = gyp.opts.nodedir.replace(/^~/, os.homedir());
194
- log.verbose('get node dir', 'compiling against specified --nodedir dev files: %s', nodeDir);
194
+ log.verbose('获取 node 目录', '使用指定的 --nodedir 开发文件编译: %s', nodeDir);
195
195
  } else {
196
196
  // 未指定 --nodedir,先确保 Node 依赖已安装
197
197
  if ('v' + release.version !== process.version) {
198
198
  // 指定了 --target,确定目标版本
199
- log.verbose('get node dir', 'compiling against --target node version: %s', release.version);
199
+ log.verbose('获取 node 目录', '按 --target 指定的 node 版本编译: %s', release.version);
200
200
  } else {
201
201
  // 未指定 --target,使用当前主机 Node 版本
202
- log.verbose('get node dir',
203
- 'no --target version specified, falling back to host node version: %s', release.version);
202
+ log.verbose('获取 node 目录',
203
+ '未指定 --target 版本,回退到宿主 node 版本: %s', release.version);
204
204
  }
205
205
 
206
206
  if (!release.semver) throw new Error('版本号无效: ' + release.version); // 无法用 semver 解析版本字符串
207
207
  // 如果指定了 tarball 选项,总是删除并重装 headers 到 devdir,
208
208
  // 否则只在不存在时安装
209
209
  gyp.opts.ensure = !gyp.opts.tarball, await gyp.commands.install([release.version]);
210
- log.verbose('get node dir', 'target node version installed:', release.versionDir);
210
+ log.verbose('获取 node 目录', '目标 node 版本已安装:', release.versionDir);
211
211
  nodeDir = path.resolve(gyp.devDir, release.versionDir);
212
212
  }
213
213
 
@@ -216,5 +216,5 @@ const configure = async (gyp, argv) => {
216
216
  return getNodeDir();
217
217
  };
218
218
 
219
- configure.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' for the current module';
219
+ configure.usage = '为当前模块生成 ' + (win ? 'MSVC 项目文件' : 'Makefile') + ';';
220
220
  export { configure };
@@ -25,7 +25,7 @@ const fs = gracefulFs.promises,
25
25
  try {
26
26
  const baseConfigGypi = await fs.readFile(path.resolve(nodeDir, 'include/node/config.gypi'));
27
27
  return parseConfigGypi(baseConfigGypi.toString());
28
- } catch (err) { log.warn('read config.gypi', err.message) }
28
+ } catch (err) { log.warn('读取 config.gypi', err.message) }
29
29
  }
30
30
 
31
31
  return JSON.parse(JSON.stringify(process.config)); // 回退到 process.config
@@ -89,7 +89,7 @@ const fs = gracefulFs.promises,
89
89
  createConfigGypi = async ({ gyp, buildDir, nodeDir, vsInfo, python }) => {
90
90
  const configFilename = 'config.gypi', configPath = path.resolve(buildDir, configFilename);
91
91
 
92
- log.verbose('build/' + configFilename, 'creating config file');
92
+ log.verbose('build/' + configFilename, '正在创建配置文件');
93
93
  const config = await getCurrentConfigGypi({ gyp, nodeDir, vsInfo, python }),
94
94
  // 保证 config.gypi 中的布尔值都转成字符串
95
95
  boolsToString = (k, v) => {
@@ -101,7 +101,7 @@ const fs = gracefulFs.promises,
101
101
  // 把 config.gypi 写入 build/ 目录
102
102
  const prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step',
103
103
  json = JSON.stringify(config, boolsToString, 2);
104
- log.verbose('build/' + configFilename, 'writing out config file: %s', configPath);
104
+ log.verbose('build/' + configFilename, '正在写出配置文件: %s', configPath);
105
105
  await fs.writeFile(configPath, [prefix, json, ''].join('\n'));
106
106
  return configPath;
107
107
  };
@@ -13,20 +13,20 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)),
13
13
 
14
14
  // 看看上层目录结构,判断当前所处位置
15
15
  const npmParentDirectory = path.join(scriptLocation, '../../../..');
16
- log.verbose('node-gyp root', 'npm_parent_directory is ' + path.basename(npmParentDirectory));
17
- log.verbose('node-gyp root', 'Finding node root directory');
16
+ log.verbose('nodejs-mobile-gyp 根目录', 'npm 上级目录为 ' + path.basename(npmParentDirectory));
17
+ log.verbose('nodejs-mobile-gyp 根目录', '正在查找 node 根目录');
18
18
  let nodeRootDir = '';
19
19
  if (path.basename(npmParentDirectory) === 'deps') {
20
20
  // 处于构建目录中,脚本位于 deps/npm/node_modules/node-gyp/lib
21
21
  nodeRootDir = path.join(npmParentDirectory, '..');
22
- log.verbose('node-gyp root', 'in build directory, root = ' + nodeRootDir);
22
+ log.verbose('nodejs-mobile-gyp 根目录', '在构建目录中,根目录 = ' + nodeRootDir);
23
23
  } else if (path.basename(npmParentDirectory) === 'node_modules') {
24
24
  // 处于 Node 安装目录中,脚本位于
25
25
  // lib/node_modules/npm/node_modules/node-gyp/lib 或
26
26
  // node_modules/npm/node_modules/node-gyp/lib(视平台而定)
27
27
  if (processObj.platform === 'win32') nodeRootDir = path.join(npmParentDirectory, '..');
28
28
  else nodeRootDir = path.join(npmParentDirectory, '../..');
29
- log.verbose('node-gyp root', 'in install directory, root = ' + nodeRootDir);
29
+ log.verbose('nodejs-mobile-gyp 根目录', '在安装目录中,根目录 = ' + nodeRootDir);
30
30
  } else {
31
31
  // 无法判断,尝试从 node 可执行文件的位置推断
32
32
  const nodeDir = path.dirname(processObj.execPath), directoryUp = path.basename(nodeDir);
@@ -51,7 +51,7 @@ const win = process.platform === 'win32',
51
51
  class PythonFinder {
52
52
  static findPython = (...args) => new PythonFinder(...args).findPython();
53
53
 
54
- log = log.withPrefix('find Python');
54
+ log = log.withPrefix('查找 Python');
55
55
  argsExecutable = ['-c', 'import sys; print(sys.executable);'];
56
56
  argsVersion = ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);'];
57
57
  semverRange = '>=3.6.0';
@@ -78,8 +78,8 @@ class PythonFinder {
78
78
  if (this.env.NODE_GYP_FORCE_PYTHON) {
79
79
  return [{
80
80
  before: () => {
81
- this.addLog('checking Python explicitly set from NODE_GYP_FORCE_PYTHON');
82
- this.addLog('- process.env.NODE_GYP_FORCE_PYTHON is ' + `"${this.env.NODE_GYP_FORCE_PYTHON}"`);
81
+ this.addLog('检查是否通过 NODE_GYP_FORCE_PYTHON 显式指定 Python');
82
+ this.addLog('- process.env.NODE_GYP_FORCE_PYTHON 为 ' + `"${this.env.NODE_GYP_FORCE_PYTHON}"`);
83
83
  }, check: () => this.checkCommand(this.env.NODE_GYP_FORCE_PYTHON)
84
84
  }];
85
85
  }
@@ -87,21 +87,21 @@ class PythonFinder {
87
87
  {
88
88
  before: () => {
89
89
  if (!this.configPython) {
90
- this.addLog('Python is not set from command line or npm configuration');
90
+ this.addLog('未从命令行或 npm 配置指定 Python');
91
91
  return SKIP;
92
92
  }
93
- this.addLog('checking Python explicitly set from command line or ' + 'npm configuration');
94
- this.addLog('- "--python=" or "npm config get python" is ' + `"${this.configPython}"`);
93
+ this.addLog('检查是否从命令行或 npm 配置显式指定 Python');
94
+ this.addLog('- "--python=" 或 "npm config get python" 的值为 ' + `"${this.configPython}"`);
95
95
  }, check: () => this.checkCommand(this.configPython)
96
96
  },
97
97
  {
98
98
  before: () => {
99
99
  if (!this.env.PYTHON) {
100
- this.addLog('Python is not set from environment variable ' + 'PYTHON');
100
+ this.addLog('未从环境变量 PYTHON 指定 Python');
101
101
  return SKIP;
102
102
  }
103
- this.addLog('checking Python explicitly set from environment ' + 'variable PYTHON');
104
- this.addLog(`- process.env.PYTHON is "${this.env.PYTHON}"`);
103
+ this.addLog('检查是否从环境变量 PYTHON 显式指定 Python');
104
+ this.addLog(`- process.env.PYTHON 为 "${this.env.PYTHON}"`);
105
105
  },
106
106
  check: () => this.checkCommand(this.env.PYTHON)
107
107
  }
@@ -110,23 +110,23 @@ class PythonFinder {
110
110
  if (this.win) {
111
111
  for (const launcher of pyLauncherCandidates) {
112
112
  checks.push({
113
- before: () => this.addLog(`checking if "${launcher}" can be used to find Python 3`),
113
+ before: () => this.addLog(`检查能否用 "${launcher}" 查找 Python 3`),
114
114
  check: () => this.checkPyLauncher(launcher)
115
115
  });
116
116
  }
117
117
  checks.push({
118
- before: () => this.addLog('enumerating installed Pythons via "py -0p"'),
118
+ before: () => this.addLog('通过 "py -0p" 枚举已安装的 Python'),
119
119
  check: () => this.checkPyList()
120
120
  });
121
121
  }
122
122
 
123
123
  checks.push(...[
124
124
  {
125
- before: () => { this.addLog('checking if "python3" can be used'); },
125
+ before: () => { this.addLog('检查能否使用 "python3"'); },
126
126
  check: () => this.checkCommand('python3')
127
127
  },
128
128
  {
129
- before: () => { this.addLog('checking if "python" can be used'); },
129
+ before: () => { this.addLog('检查能否使用 "python"'); },
130
130
  check: () => this.checkCommand('python')
131
131
  }
132
132
  ]);
@@ -135,7 +135,7 @@ class PythonFinder {
135
135
  for (let i = 0; i < this.winDefaultLocations.length; ++i) {
136
136
  const location = this.winDefaultLocations[i];
137
137
  checks.push({
138
- before: () => this.addLog(`checking if Python is ${location}`),
138
+ before: () => this.addLog(`检查 Python 是否位于 ${location}`),
139
139
  check: () => this.checkExecPath(location)
140
140
  });
141
141
  }
@@ -160,33 +160,33 @@ class PythonFinder {
160
160
 
161
161
  // 检查命令是否为可用的 Python;
162
162
  async checkCommand(command) {
163
- this.log.verbose(`- executing "${command}" to get executable path`);
163
+ this.log.verbose(`- 执行 "${command}" 以获取可执行文件路径`);
164
164
  try {
165
165
  const execPath = await this.run(command, this.argsExecutable, false);
166
- this.addLog(`- executable path is "${execPath}"`);
166
+ this.addLog(`- 可执行文件路径为 "${execPath}"`);
167
167
  return this.checkExecPath(execPath);
168
168
  } catch (err) {
169
- this.addLog(`- "${command}" is not in PATH or produced an error`);
169
+ this.addLog(`- "${command}" 不在 PATH 中或执行出错`);
170
170
  throw err;
171
171
  }
172
172
  }
173
173
 
174
174
  // 检查指定 py 启动器是否能找到 Python 3;
175
175
  async checkPyLauncher(launcher = this.pyLauncher) {
176
- this.log.verbose(`- executing "${launcher}" to get Python 3 executable path`);
176
+ this.log.verbose(`- 执行 "${launcher}" 以获取 Python 3 可执行文件路径`);
177
177
  try {
178
178
  const execPath = await this.run(launcher, ['-3', ...this.argsExecutable], false);
179
- this.addLog(`- executable path is "${execPath}"`);
179
+ this.addLog(`- 可执行文件路径为 "${execPath}"`);
180
180
  return this.checkExecPath(execPath);
181
181
  } catch (err) {
182
- this.addLog(`- "${launcher}" is not in PATH or produced an error`);
182
+ this.addLog(`- "${launcher}" 不在 PATH 中或执行出错`);
183
183
  throw err;
184
184
  }
185
185
  }
186
186
 
187
187
  // 通过 "py -0p" 枚举所有已注册的 Python,覆盖非标准安装位置;
188
188
  async checkPyList() {
189
- this.log.verbose('- executing "py -0p" to list registered Pythons');
189
+ this.log.verbose('- 执行 "py -0p" 以列出已注册的 Python');
190
190
  let lastErr;
191
191
  for (const launcher of pyLauncherCandidates) {
192
192
  try {
@@ -196,29 +196,29 @@ class PythonFinder {
196
196
  const m = line.match(/([A-Za-z]:\\[^\s]+)/);
197
197
  if (m && /python\.exe$/i.test(m[1])) paths.push(m[1]);
198
198
  }
199
- this.addLog(`- "py -0p" (via ${launcher}) found ${paths.length} path(s)`);
199
+ this.addLog(`- "py -0p" (via ${launcher}) 找到 ${paths.length} 个路径`);
200
200
  for (const p of paths) {
201
201
  try {
202
202
  return await this.checkExecPath(p);
203
203
  } catch (err) {
204
204
  lastErr = err;
205
- this.log.silly('checkPyList: skipping %s: %s', p, err.message);
205
+ this.log.silly('checkPyList: 跳过 %s: %s', p, err.message);
206
206
  }
207
207
  }
208
208
  } catch (err) {
209
209
  lastErr = err;
210
- this.addLog(`- "py -0p" via "${launcher}" failed`);
210
+ this.addLog(`- 经 "${launcher}" 执行 "py -0p" 失败`);
211
211
  }
212
212
  }
213
- throw lastErr || new Error('py -0p returned no usable Python');
213
+ throw lastErr || new Error('py -0p 未返回可用的 Python');
214
214
  }
215
215
 
216
216
  // 检查某个 Python 可执行文件版本是否可用;
217
217
  async checkExecPath(execPath) {
218
- this.log.verbose(`- executing "${execPath}" to get version`);
218
+ this.log.verbose(`- 执行 "${execPath}" 以获取版本`);
219
219
  try {
220
220
  const version = await this.run(execPath, this.argsVersion, false);
221
- this.addLog(`- version is "${version}"`);
221
+ this.addLog(`- 版本为 "${version}"`);
222
222
 
223
223
  const range = new semver.Range(this.semverRange);
224
224
  let valid = false;
@@ -226,18 +226,18 @@ class PythonFinder {
226
226
  valid = range.test(version);
227
227
  } catch (err) {
228
228
  this.log.silly('range.test() threw:\n%s', err.stack);
229
- this.addLog(`- "${execPath}" does not have a valid version`);
230
- this.addLog('- is it a Python executable?');
229
+ this.addLog(`- "${execPath}" 的版本无效`);
230
+ this.addLog('- 它是不是 Python 可执行文件?');
231
231
  throw err;
232
232
  }
233
233
  if (!valid) {
234
- this.addLog(`- version is ${version} - should be ${this.semverRange}`);
235
- this.addLog('- THIS VERSION OF PYTHON IS NOT SUPPORTED');
236
- throw new Error(`Found unsupported Python version ${version}`);
234
+ this.addLog(`- 版本为 ${version},应为 ${this.semverRange}`);
235
+ this.addLog('- 此版本的 Python 不受支持');
236
+ throw new Error(`不支持的 Python 版本 ${version}`);
237
237
  }
238
238
  return this.succeed(execPath, version);
239
239
  } catch (err) {
240
- this.addLog(`- "${execPath}" could not be run`);
240
+ this.addLog(`- "${execPath}" 无法运行`);
241
241
  throw err;
242
242
  }
243
243
  }
@@ -251,9 +251,9 @@ class PythonFinder {
251
251
  this.log.silly('execFile: args = %j', args);
252
252
  this.log.silly('execFile: opts = %j', opts);
253
253
  const [err, stdout, stderr] = await this.execFile(exec, args, opts);
254
- this.log.silly('execFile result: err = %j', (err && err.stack) || err);
255
- this.log.silly('execFile result: stdout = %j', stdout);
256
- this.log.silly('execFile result: stderr = %j', stderr);
254
+ this.log.silly('execFile 结果: 错误 = %j', (err && err.stack) || err);
255
+ this.log.silly('execFile 结果: 标准输出 = %j', stdout);
256
+ this.log.silly('execFile 结果: 标准错误 = %j', stderr);
257
257
  if (err) throw err;
258
258
  return stdout.trim();
259
259
  }
@@ -268,15 +268,15 @@ class PythonFinder {
268
268
  pathExample = this.win ? 'C:\\Path\\To\\python.exe' : '/path/to/pythonexecutable',
269
269
  info = [
270
270
  '**********************************************************',
271
- 'You need to install the latest version of Python.',
272
- 'Node-gyp should be able to find and use Python. If not,',
273
- 'you can try one of the following options:',
274
- `- Use the switch --python="${pathExample}"`,
275
- ' (accepted by both node-gyp and npm)',
276
- '- Set the environment variable PYTHON',
277
- '- Set the npm configuration variable python:',
271
+ '需要安装最新版本的 Python;',
272
+ '应能自动找到并使用 Python;若找不到,',
273
+ '可尝试以下任一方式:',
274
+ `- 使用 --python="${pathExample}" 开关`,
275
+ ' (npm 与本包均接受该参数)',
276
+ '- 设置环境变量 PYTHON',
277
+ '- 设置 npm 配置项 python:',
278
278
  ` npm config set python "${pathExample}"`,
279
- 'For more information consult the documentation at:',
279
+ '更多说明见文档:',
280
280
  'https://github.com/nodejs/node-gyp#installation',
281
281
  '**********************************************************'
282
282
  ].join('\n');