@flun/nodejs-mobile-gyp 1.0.1 → 1.1.0

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 CHANGED
@@ -1,5 +1,56 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2026-09-27 16:01
4
+
5
+ ### 新增
6
+
7
+ - **Windows 宿主平台支持**:此前 Windows 上无法完成 Android 原生模块的交叉编译全流程,本版起支持。
8
+
9
+ ### 修复
10
+
11
+ - **GNU make 缺 POSIX shell**:Android 原生模块通过 GNU make 构建,Makefile 会调用 `printf` / `xargs` / `sed` 等 Unix 命令。Windows 上原本因找不到 `sh.exe` 而报 `CreateProcess(NULL, printf ...) failed`。现在自动定位 Git for Windows 的 `usr/bin` 并前置到 PATH。
12
+ - **Python 探测漏检**:`find-python.js` 原本硬编码 `3.11 / 3.10 / 3.9 / 3.8` 等安装目录,装在非标准位置(如 `C:\Python314`)或更新版本时无法识别。现在改为动态探测,见下方「变更」。
13
+ - **工具集写死**:`configure` 阶段原本硬编码 `-Gmsvs_toolset=v145`,仅 VS2026 可用,VS2022 等会失败。现在从 `find-visualstudio` 的探测结果动态取用。
14
+ - **路径分隔符**:gyp 生成的 Makefile 中路径带 Windows 反斜杠(如 `src\bufferutil.o`),导致 `sh` / `sed` / `make` 解析失败。现在统一转正斜杠;该改动在非 Windows 平台为 no-op。
15
+ - **超长路径依赖**:Windows 下 Makefile 会因 gypi 依赖路径超过 260 字符而报 `No rule to make target`。现在过滤掉 `node_modules` 中的此类依赖——仅影响 Makefile 自动重新生成,不影响编译。
16
+
17
+ ### 变更
18
+
19
+ - **`lib/find-python.js`**:重写。移除硬编码版本列表,改为 `py.exe` 候选(PATH / `%SystemRoot%`)+ `py -0p` 枚举 + `%LOCALAPPDATA%\Programs\Python` 与 `%ProgramFiles%\Python` 目录扫描。`run()` 显式检查 `execFile` 的 err,避免失败被当成空输出。
20
+ - **`lib/build.js`**:区分「宿主平台」与「交叉编译目标」。Windows 宿主编译 Android 目标时走 GNU make(而非 MSBuild);自动探测 `sh.exe`;为 Python 创建 `python3.cmd` shim(Windows 上无需符号链接权限)。
21
+ - **`lib/configure.js`**:`-Gmsvs_toolset` 从探测结果动态生成,不再写死。
22
+ - **`gyp/pylib/gyp/common.py`、`gyp/pylib/gyp/generator/make.py`**:路径统一输出正斜杠;新增 `slashpath` 辅助供 `mkdir` 使用。
23
+
24
+ ### 验证
25
+
26
+ - Windows + VS2026 BuildTools + Python + Node + NDK 27.x 环境下,`configure` + `build` 全流程通过,生成的 `.node` 可被 Node 正常加载。
27
+ - WSL Ubuntu 24.04 环境下,改动不改变生成的 Makefile 路径,非 Windows 平台行为不变。
28
+
29
+ ### 说明
30
+
31
+ - 本版无破坏性变更,向后兼容。
32
+
33
+
34
+ ## [1.0.2] - 2026-09-26 22:00
35
+
36
+ ### 修复
37
+
38
+ - **lib/log.js**:新版 `proc-log` 的日志级别函数与 `pause` / `resume` 挂在默认导出的 `log` 对象下,而非包顶层。之前误用 `procLog.pause()` 等写法,导致 `configure` 阶段直接抛出 `TypeError`,全流程无法启动。
39
+ - **lib/process-release.js**:弃用 `url.resolve()` / `url.parse()`,改用 WHATWG `URL`,消除新版 Node 的 `DEP0169` 弃用警告。
40
+ - **Windows 外部头警告抑制**:Node 官方 V8 头文件会触发 `warning C4018` 等噪音。现在在 gyp 中注册 `ExternalWarningLevel` 属性,并在 `addon.gypi` 中使用 `/external:anglebrackets` + `ExternalWarningLevel: TurnOffAllWarnings`。只对 `#include <...>` 的外部头(Node / V8)静音,不影响下游自己代码的警告;且通过 MSBuild 属性而非命令行 `/external:W0`,避免与默认的 `/external:W3` 冲突产生 `D9025`。
41
+
42
+ ### 变更
43
+
44
+ - **package.json**
45
+ - `bin` 改为对象形式 `{ "nodejs-mobile-gyp": "./bin/node-gyp.js" }`,与上游命令名保持一致,避免与 `node-gyp` 包混淆。
46
+ - `engines.node` 提升到匹配依赖链(`make-fetch-happen`、`nopt`、`proc-log`、`which` 等)的最低版本要求。
47
+ - **README.md**:CLI 调用命令修正为 `npx @flun/nodejs-mobile-gyp`;补充 Node 版本要求;移除对内部 `Gyp` API 的示例承诺。
48
+
49
+ ### 验证
50
+
51
+ - 在 Windows + VS2026 BuildTools + Python 3.14 + Node 26.8.1 环境下,`configure` + `build` 全流程通过,0 warning / 0 error。
52
+ - 生成的 `.node` 可被 Node 正常 `require()` 加载并调用。
53
+
3
54
  ## [1.0.1] - 2026-09-21
4
55
 
5
56
  ### 首发
@@ -25,7 +76,7 @@
25
76
 
26
77
  ### 依赖升级
27
78
 
28
- - `tar`: `^6.1.2` → `^7.5.21`
79
+ - `tar`: `^6.1.2` → `^7.5.22`
29
80
  - `glob`: `^10.3.10` → `^13.0.6`
30
81
  - `make-fetch-happen`: `^13.0.0` → `^16.0.1`
31
82
  - 连带消除 `inflight`、`rimraf@3`、旧版 `glob` 等停更依赖
@@ -33,4 +84,4 @@
33
84
  ### 验证
34
85
 
35
86
  - Windows + VS2026 BuildTools + Python 3.14 + Node 26.8.1 环境下,`configure` + `build` 全流程通过
36
- - 生成的 `.node` 能被 Node 正常加载
87
+ - 生成的 `.node` 能被 Node 正常加载
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  用于为 [nodejs-mobile](https://github.com/nodejs-mobile/nodejs-mobile) 环境编译 C/C++ 原生模块,支持 Android 与 iOS 的交叉编译。
6
6
 
7
+ 宿主平台支持 **Windows** / **macOS** / **Linux**,各平台所需的环境与工具见「[支持的系统与配置要求](#支持的系统与配置要求)」。
8
+
7
9
  [`@flun/nodejs-mobile-react-native`](https://www.npmjs.com/package/@flun/nodejs-mobile-react-native) 在构建时会自动调用本包,无需手动安装。
8
10
 
9
11
  本包是 [`nodejs-mobile-gyp`](https://github.com/nodejs-mobile/nodejs-mobile-gyp) 的 flun fork,后者又基于上游 [`node-gyp`](https://github.com/nodejs/node-gyp)。
@@ -33,8 +35,8 @@
33
35
  │ ├── process-release.js # 解析下载 URL、本地目录名、headers 与 node.lib 地址
34
36
  │ ├── download.js # 基于 make-fetch-happen 的 HTTP 下载与 CA 读取
35
37
  │ ├── find-node-directory.js # 推断 Node 安装根目录(用于 AIX / zOS)
36
- │ ├── find-python.js # PythonFinder 类:按顺序定位可用的 Python 3.6+
37
- │ ├── find-visualstudio.js # VisualStudioFinder 类:定位可用的 VS 安装(含 VS2026)
38
+ │ ├── find-python.js # PythonFinder 类:动态定位 Python 3.6+(py 启动器 / 目录扫描)
39
+ │ ├── find-visualstudio.js # VisualStudioFinder 类:定位可用的 VS 安装(VS2013+,含 VS2026)
38
40
  │ ├── Find-VisualStudio.cs # PowerShell 调用的 C# 脚本,枚举 VS 安装信息
39
41
  │ ├── log.js # Logger 类 + proc-log 包装,分级着色日志
40
42
  │ └── util.js # 通用工具:execFile 包装、注册表读取、候选文件查找
@@ -61,7 +63,7 @@
61
63
  │ └── win_delay_load_hook.cc # Windows 加载延迟钩子(编译进每个原生模块)
62
64
  │
63
65
  ├── addon.gypi # 所有原生模块共用的 gyp 配置
64
- ├── package.json # type: module,bin 指向 bin/node-gyp.js
66
+ ├── package.json # type: module,bin 命令名为 nodejs-mobile-gyp
65
67
  ├── LICENSE # 本包许可证(ISC)
66
68
  ├── README.md # 本文件
67
69
  ├── CHANGELOG.md # 版本变更记录
@@ -96,20 +98,57 @@
96
98
 
97
99
  ### Windows 构建适配
98
100
 
99
- 原版在 Windows 上默认走 ClangCL 工具集,且继承 Node 官方二进制的 LTO 配置。Node.js 24+ 官方二进制由 ClangCL + ThinLTO 编译,其 `common.gypi` 会在 `clang==1` 时把 MSBuild 工具集设为 `ClangCL`,并在 `enable_lto==true` / `enable_thin_lto==true` 时向链接器传入 `-flto=thin`、`/opt:lldltojobs` 等 Clang 专用参数。若用户本机没装 ClangCL,或用的是传统 MSVC 工具链,会报以下错误:
101
+ 原版 `nodejs-mobile-gyp` 在 Windows 上有若干问题,本 fork 逐一解决,涉及三方面。
102
+
103
+ #### 1. 工具集与 LTO
104
+
105
+ 原版默认走 ClangCL 工具集,且继承 Node 官方二进制的 LTO 配置。Node.js 24+ 官方二进制由 ClangCL + ThinLTO 编译,其 `common.gypi` 会在 `clang==1` 时把 MSBuild 工具集设为 `ClangCL`,并在 `enable_lto==true` / `enable_thin_lto==true` 时向链接器传入 `-flto=thin`、`/opt:lldltojobs` 等 Clang 专用参数。若用户本机没装 ClangCL,或用的是传统 MSVC 工具链,会报:
100
106
 
101
107
  - `MSB8020: 无法找到 ClangCL 的生成工具`
102
108
  - `LNK1117: 选项"opt:lldltojobs=2"中的语法错误`
103
109
 
104
- 本 fork 强制走 MSVC 工具集并关闭 LTO,避免上述报错。
110
+ 本 fork 强制走 MSVC 工具集并关闭 LTO,避免上述报错。工具集版本从 `find-visualstudio` 的探测结果**动态获取**(`v140` / `v141` / `v142` / `v143` / `v145`...),不写死。
111
+
112
+ #### 2. GNU make 的 POSIX 环境
113
+
114
+ Android 原生模块通过 GNU make 构建,其 Makefile 会调用 `printf`、`xargs`、`sed` 等 Unix 命令。GNU make 需要一个 POSIX shell(`sh.exe`)来执行它们;Windows 上若无,会直接报 `CreateProcess(NULL, printf ...) failed`。本 fork 自动定位 Git for Windows 的 `usr/bin` 并前置到 PATH,详见「[支持的系统与配置要求](#支持的系统与配置要求) → 自动探测」。
115
+
116
+ #### 3. 路径分隔符
117
+
118
+ gyp 生成的 Makefile 中,路径原本会带上 Windows 的反斜杠(如 `src\bufferutil.o`),导致 `sh` / `sed` / `make` 解析失败。本 fork 在 gyp 的路径生成环节统一转成正斜杠;该改动在非 Windows 平台上是 no-op。
105
119
 
106
120
  ### 具体改动清单
107
121
 
108
- | 文件 | 改动 |
109
- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
110
- | `lib/create-config-gypi.js` | 生成 `config.gypi` 时,把 `clang` 置 0,`enable_lto` / `enable_thin_lto` 置 `'false'`,`lto_jobs` 置空。避免 `common.gypi` 把工具集改成 ClangCL、向 MSVC 传 LTO 参数 |
111
- | `lib/configure.js` | 调用 gyp 时追加 `-Gmsvs_toolset=v145`,显式指定 VS2026 工具集 |
112
- | `lib/find-visualstudio.js` | 支持 VS2026(版本号 18,`versionYear=2026`,toolset `v145`) |
122
+ #### 构建工具链
123
+
124
+ | 文件 | 改动 |
125
+ | --- | --- |
126
+ | `lib/create-config-gypi.js` | 生成 `config.gypi` 时把 `clang` 置 0、`enable_lto` / `enable_thin_lto` 置 `'false'`、`lto_jobs` 置空,避免 `common.gypi` 切到 ClangCL 并向 MSVC 传 LTO 参数 |
127
+ | `lib/configure.js` | 调用 gyp 时追加 `-Gmsvs_toolset=<探测到的 toolset>`,不再写死 `v145` |
128
+ | `lib/find-visualstudio.js` | 支持 VS2013+,含最新 VS2026(版本号 18,`versionYear=2026`,toolset `v145`) |
129
+ | `gyp/pylib/gyp/MSVSSettings.py` | 注册 `ExternalWarningLevel`(MSBuild 属性),供 `addon.gypi` 使用 |
130
+ | `addon.gypi` | Windows 下追加 `/external:anglebrackets` + `ExternalWarningLevel: TurnOffAllWarnings`,只静音外部头(Node / V8)的警告 |
131
+
132
+ #### 跨平台路径
133
+
134
+ | 文件 | 改动 |
135
+ | --- | --- |
136
+ | `gyp/pylib/gyp/common.py` | `RelativePath` 返回值统一转正斜杠 |
137
+ | `gyp/pylib/gyp/generator/make.py` | `Absolutify` 返回正斜杠;新增 `slashpath` 供 `mkdir` 使用;Windows 下跳过 `node_modules` 中的超长 gypi 依赖 |
138
+
139
+ #### Windows 运行时探测
140
+
141
+ | 文件 | 改动 |
142
+ | --- | --- |
143
+ | `lib/find-python.js` | 重写:移除硬编码版本列表,改为 `py.exe` 候选 + `py -0p` 枚举 + 目录扫描;`run()` 显式检查 `execFile` 的 err |
144
+ | `lib/build.js` | 区分「宿主 Windows」与「Android 交叉编译」;自动探测 `sh.exe` 并前置到 PATH;为 Python 创建 `python3.cmd` shim |
145
+
146
+ #### 兼容性与健壮性
147
+
148
+ - **`proc-log` 新版本兼容**:新版把日志级别函数与 `pause` / `resume` 挂在默认导出的 `log` 对象下。本 fork 已按此调整 `lib/log.js`,否则 `configure` 阶段会抛 `TypeError`。
149
+ - **弃用 API 清理**:`lib/process-release.js` 不再用 `url.resolve()` / `url.parse()`,改用 WHATWG `URL`,消除 `DEP0169` 警告。
150
+ - **依赖脚本放行**:`package.json` 显式声明 `allowScripts`,适配新版 npm 默认禁止依赖执行安装脚本的行为。
151
+
113
152
 
114
153
  ### 迁移踩过的坑
115
154
 
@@ -134,6 +173,80 @@
134
173
  7. **非 Windows 平台不引入 `find-visualstudio.js`**
135
174
  该模块依赖 Windows 专属的 `path.win32` 和注册表操作,保持原版的"仅在 Windows 上条件加载"逻辑。
136
175
 
176
+ ## 支持的系统与配置要求
177
+
178
+ 本包面向 **Android / iOS 交叉编译**,宿主平台支持情况:
179
+
180
+ | 宿主平台 | 目标:Android | 目标:iOS |
181
+ | -------- | ------------- | ---------- |
182
+ | Windows | ✅ 完整支持 | ❌ 需 macOS |
183
+ | macOS | ✅ 完整支持 | ✅ 完整支持 |
184
+ | Linux | ✅ 完整支持 | ❌ 需 macOS |
185
+
186
+ 各平台所需的构建环境如下。
187
+
188
+ ### 通用要求
189
+
190
+ - **Node.js**:最低版本见 `package.json` 的 `engines.node` 字段
191
+ - **Python 3.6+**:gyp 本身是 Python 程序,构建时必需
192
+ - **目标平台 SDK**:
193
+ - Android:Android NDK(已在 NDK **27.x** 上验证)
194
+ - iOS:Xcode Command Line Tools(仅 macOS)
195
+
196
+ ### Windows
197
+
198
+ | 组件 | 要求 | 说明 |
199
+ | --------------- | --------------------------------------- | ----------------------------------------------------------- |
200
+ | Node.js | 见 `engines.node` | |
201
+ | Python | 3.6+ | 支持任意安装位置,见下方「自动探测」 |
202
+ | Visual Studio | **VS2026**(版本号 18,toolset `v145`) | 需勾选「使用 C++ 的桌面开发」工作负载,含 Windows SDK |
203
+ | Android NDK | 27.x(已验证) | 由 `@flun/nodejs-mobile-react-native` 自动定位 |
204
+ | Git for Windows | 任意近期版本 | 提供 make 所需的 `sh.exe` / `printf` / `xargs` 等 Unix 工具 |
205
+
206
+ > **为什么需要 Git for Windows?** Android 原生模块通过 GNU make 构建,Makefile 中会调用 `printf`、`xargs`、`sed` 等 Unix 命令。GNU make 需要一个 POSIX shell(`sh.exe`)来执行它们,而 Git for Windows 自带完整的这套工具链,无需额外安装 MSYS2 或 Cygwin。
207
+
208
+ ### macOS
209
+
210
+ | 组件 | 要求 |
211
+ | ----------- | -------------------------------- |
212
+ | Node.js | 见 `engines.node` |
213
+ | Python | 3.6+ |
214
+ | Xcode | 完整 Xcode 或 Command Line Tools |
215
+ | Android NDK | 仅构建 Android 目标时需 27.x |
216
+
217
+ ### Linux
218
+
219
+ | 组件 | 要求 |
220
+ | ----------- | ------------------------------------------------------ |
221
+ | Node.js | 见 `engines.node` |
222
+ | Python | 3.6+ |
223
+ | 构建工具 | `make`、`g++`(`apt install build-essential python3`) |
224
+ | Android NDK | 仅构建 Android 目标时需 27.x |
225
+
226
+ ### 自动探测(Windows)
227
+
228
+ Windows 上以下路径 / 版本由本包动态探测,**无需手动配置**:
229
+
230
+ - **Python**,依次尝试:
231
+ 1. `NODE_GYP_FORCE_PYTHON` 环境变量
232
+ 2. 命令行 `--python=`
233
+ 3. `PYTHON` 环境变量
234
+ 4. `py.exe` 启动器(PATH 中、`%SystemRoot%\py.exe`、`%SystemRoot%\System32\py.exe`)
235
+ 5. `py -0p` 枚举所有已注册 Python
236
+ 6. `python3` / `python` 命令
237
+ 7. 扫描 `%LOCALAPPDATA%\Programs\Python\Python*` 与 `%ProgramFiles%\Python*`
238
+
239
+ - **POSIX shell(`sh.exe`)**,依次尝试:
240
+ 1. 注册表 `HKLM\SOFTWARE\GitForWindows` 的 `InstallPath`
241
+ 2. `which git` 反推 `../usr/bin`
242
+ 3. `PATH` 中的 `git.exe` 反推 `../usr/bin`
243
+ 4. 常见安装位置兜底
244
+
245
+ - **Android NDK**:由 `@flun/nodejs-mobile-react-native` 通过 `android.ndkDirectory` 定位,并注入 `make.exe`、`clang` 等工具路径。
246
+
247
+ > 本包不依赖任何随包分发的硬编码配置文件。上述探测均为运行时动态完成;仅 Visual Studio 的「版本号 → 版本年份 → toolset」对照表以常量形式内联在 `lib/find-visualstudio.js` 中——这类信息由微软固定发布,无枚举接口,且随 VS 版本发布而更新,不适合做成外部资源。
248
+
249
+
137
250
  ## 安装
138
251
 
139
252
  ```bash
@@ -142,12 +255,14 @@ npm install @flun/nodejs-mobile-gyp
142
255
 
143
256
  通常你不需要手动安装本包——它由 [`@flun/nodejs-mobile-react-native`](https://www.npmjs.com/package/@flun/nodejs-mobile-react-native) 在编译原生模块时自动调用。
144
257
 
258
+ > **Node 版本要求**:本 fork 的依赖链(`make-fetch-happen`、`nopt`、`proc-log`、`which` 等)已升级到较新版本,要求比原版 `nodejs-mobile-gyp` 更高的 Node。具体最低版本见 `package.json` 的 `engines.node` 字段。若你的项目仍在使用较旧的 Node,请改用原版 `nodejs-mobile-gyp`。
259
+
145
260
  ## 使用
146
261
 
147
262
  ### 命令行
148
263
 
149
264
  ```bash
150
- npx node-gyp <command> [options]
265
+ npx @flun/nodejs-mobile-gyp <command> [options]
151
266
  ```
152
267
 
153
268
  或在 `package.json` 的 `scripts` 中调用:
@@ -155,20 +270,14 @@ npx node-gyp <command> [options]
155
270
  ```json
156
271
  {
157
272
  "scripts": {
158
- "build-addon": "node-gyp rebuild"
273
+ "build-addon": "nodejs-mobile-gyp rebuild"
159
274
  }
160
275
  }
161
276
  ```
162
277
 
163
278
  ### 从 Node.js 代码调用
164
279
 
165
- ```js
166
- import createGyp from '@flun/nodejs-mobile-gyp'
167
-
168
- const gyp = createGyp()
169
- gyp.parseArgv(process.argv)
170
- // gyp.todo 是待执行的命令队列
171
- ```
280
+ 本包主要作为 CLI 工具使用,推荐通过 spawn / execFile 调用其命令行入口。包本身也导出了 Gyp 类,但属于内部实现,接口可能随版本变化,不建议直接在代码中依赖。
172
281
 
173
282
  ## 命令
174
283
 
@@ -187,6 +296,7 @@ gyp.parseArgv(process.argv)
187
296
 
188
297
  | 选项 | 说明 |
189
298
  | --------------------------------- | -------------------------------------------------- |
299
+ | `-v`, `--version` | 打印本包版本号 |
190
300
  | `-j n`, `--jobs n` | 并行运行 `make`;`max` 表示使用所有 CPU 核心 |
191
301
  | `--target=v6.2.1` | 目标 Node.js 版本(默认 `process.version`) |
192
302
  | `--silly`, `--loglevel=silly` | 输出全部进度日志 |
@@ -207,7 +317,7 @@ gyp.parseArgv(process.argv)
207
317
  | `--cafile=$cafile` | 覆盖默认 CA 链(用于下载 tarball) |
208
318
  | `--nodedir=$path` | 指定 Node 源码路径 |
209
319
  | `--python=$path` | 指定 Python 二进制路径 |
210
- | `--msvs_version=$version` | 指定 Visual Studio 版本(仅 Windows) |
320
+ | `--msvs_version=$version` | 指定 Visual Studio 版本或安装路径(仅 Windows;可填 `2022` / `2026` 或 `C:\...\BuildTools`) |
211
321
  | `--solution=$solution` | 指定 Visual Studio Solution 版本(仅 Windows) |
212
322
  | `--force-process-config` | 强制用运行时的 `process.config` 生成 `config.gypi` |
213
323
 
@@ -237,7 +347,7 @@ set npm_config_devdir=c:\temp\.gyp
237
347
  npm config set [--global] devdir /tmp/.gyp
238
348
  ```
239
349
 
240
- **注意**:通过 `npm config` 设置的配置,只在 `node-gyp` **由 npm 调用时**生效,直接运行 `node-gyp` 不生效。
350
+ **注意**:通过 `npm config` 设置的配置,只在本包 **由 npm 调用时**生效,直接运行 `nodejs-mobile-gyp` 不生效。
241
351
 
242
352
  ## 上游参考
243
353
 
package/addon.gypi CHANGED
@@ -146,6 +146,18 @@
146
146
  ],
147
147
  }],
148
148
  [ 'OS=="win"', {
149
+ 'msvs_settings': {
150
+ 'VCCLCompilerTool': {
151
+ # /external:anglebrackets:把 <...> 引用的 Node / V8 头视为外部头,
152
+ # 只对它们抑制警告;下游自己代码(#include "...")不受影响。
153
+ # 警告级别用 ExternalWarningLevel 属性设置,而不是命令行 /external:W0,
154
+ # 避免与 MSBuild 默认的 /external:W3 冲突而产生 D9025。
155
+ 'AdditionalOptions': [
156
+ '/external:anglebrackets'
157
+ ],
158
+ 'ExternalWarningLevel': 'TurnOffAllWarnings'
159
+ }
160
+ },
149
161
  'conditions': [
150
162
  ['node_engine=="chakracore"', {
151
163
  'library_dirs': [ '<(node_root_dir)/$(ConfigurationName)' ],
@@ -154,7 +166,7 @@
154
166
  ['node_with_ltcg=="true"', {
155
167
  'msvs_settings': {
156
168
  'VCCLCompilerTool': {
157
- 'WholeProgramOptimization': 'true' # /GL, whole program optimization, needed for LTCG
169
+ 'WholeProgramOptimization': 'true', # /GL, whole program optimization, needed for LTCG
158
170
  },
159
171
  'VCLibrarianTool': {
160
172
  'AdditionalOptions': [
package/bin/node-gyp.js CHANGED
@@ -1,68 +1,51 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import envPaths from 'env-paths'
4
- import os from 'os'
5
- import fs from 'fs'
6
- import { Gyp } from '../lib/node-gyp.js'
7
- import * as log from '../lib/log.js'
3
+ import envPaths from 'env-paths';
4
+ import os from 'os';
5
+ import fs from 'fs';
6
+ import { Gyp } from '../lib/node-gyp.js';
7
+ import * as log from '../lib/log.js';
8
8
 
9
- process.title = 'node-gyp'
9
+ process.title = 'node-gyp';
10
10
 
11
11
  /**
12
12
  * 处理并执行选中的命令。
13
13
  */
14
14
 
15
- const prog = new Gyp()
16
- let completed = false
17
- prog.parseArgv(process.argv)
18
- prog.devDir = prog.opts.devdir
15
+ const prog = new Gyp(), homeDir = os.homedir();
16
+ let completed = false;
17
+ prog.parseArgv(process.argv), prog.devDir = prog.opts.devdir;
19
18
 
20
- const homeDir = os.homedir()
21
- if (prog.devDir) {
22
- prog.devDir = prog.devDir.replace(/^~/, homeDir)
23
- } else if (homeDir) {
24
- prog.devDir = envPaths('node-gyp', { suffix: '' }).cache
25
- } else {
19
+ if (prog.devDir) prog.devDir = prog.devDir.replace(/^~/, homeDir);
20
+ else if (homeDir) prog.devDir = envPaths('node-gyp', { suffix: '' }).cache;
21
+ else {
26
22
  throw new Error(
27
23
  "node-gyp requires that the user's home directory is specified " +
28
24
  'in either of the environmental variables HOME or USERPROFILE. ' +
29
- 'Overide with: --devdir /path/to/.node-gyp')
25
+ 'Overide with: --devdir /path/to/.node-gyp');
30
26
  }
31
27
 
32
28
  if (prog.todo.length === 0) {
33
- if (~process.argv.indexOf('-v') || ~process.argv.indexOf('--version')) {
34
- log.stdout('v%s', prog.version)
35
- } else {
36
- log.stdout('%s', prog.usage())
37
- }
38
- process.exit(0)
29
+ if (~process.argv.indexOf('-v') || ~process.argv.indexOf('--version')) log.stdout('v%s', prog.version);
30
+ else log.stdout('%s', prog.usage());
31
+ process.exit(0);
39
32
  }
40
33
 
41
- log.info('it worked if it ends with', 'ok')
42
- log.verbose('cli', process.argv)
43
- log.info('using', 'node-gyp@%s', prog.version)
44
- log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch)
34
+ log.info('it worked if it ends with', 'ok'), log.verbose('cli', process.argv), log.info('using', 'node-gyp@%s', prog.version);
35
+ log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch);
45
36
 
46
37
  /**
47
- * 如果传了 -C/--directory,切换到该目录。
38
+ * 如果传了 -C/--directory,切换到该目录;
48
39
  */
49
-
50
- const dir = prog.opts.directory
40
+ const dir = prog.opts.directory;
51
41
  if (dir) {
52
42
  try {
53
- const stat = fs.statSync(dir)
54
- if (stat.isDirectory()) {
55
- log.info('chdir', dir)
56
- process.chdir(dir)
57
- } else {
58
- log.warn('chdir', dir + ' is not a directory')
59
- }
43
+ const stat = fs.statSync(dir);
44
+ if (stat.isDirectory()) log.info('chdir', dir), process.chdir(dir);
45
+ else log.warn('chdir', dir + ' 不是目录');
60
46
  } catch (e) {
61
- if (e.code === 'ENOENT') {
62
- log.warn('chdir', dir + ' is not a directory')
63
- } else {
64
- log.warn('chdir', 'error during chdir() "%s"', e.message)
65
- }
47
+ if (e.code === 'ENOENT') log.warn('chdir', dir + ' 不是目录');
48
+ else log.warn('chdir', 'chdir() 出错 "%s"', e.message);
66
49
  }
67
50
  }
68
51
 
@@ -71,74 +54,48 @@ if (dir) {
71
54
  */
72
55
  const errorMessage = () => {
73
56
  // 复制自 npm 的 lib/utils/error-handler.js
74
- log.error('System', os.type() + ' ' + os.release())
75
- log.error('command', process.argv
76
- .map(JSON.stringify).join(' '))
77
- log.error('cwd', process.cwd())
78
- log.error('node -v', process.version)
79
- log.error('node-gyp -v', 'v' + prog.package.version)
80
- }
81
-
82
- /**
83
- * 打印最终诊断信息
84
- */
85
- const issueMessage = () => {
86
- errorMessage()
87
- log.error('', ['Node-gyp failed to build your package.',
88
- 'Try to update npm and/or node-gyp and if it does not help file an issue with the package author.'
89
- ].join('\n'))
90
- }
91
-
92
- /**
93
- * 依次执行队列中的命令
94
- */
95
- const run = async () => {
96
- const command = prog.todo.shift()
97
- if (!command) {
98
- // 完成!
99
- completed = true
100
- log.info('ok')
101
- return
102
- }
103
-
104
- try {
105
- const args = await prog.commands[command.name](command.args) ?? []
106
-
107
- if (command.name === 'list') {
108
- if (args.length) {
109
- args.forEach((version) => log.stdout(version))
110
- } else {
111
- log.stdout('No node development files installed. Use `node-gyp install` to install a version.')
57
+ log.error('System', os.type() + ' ' + os.release());
58
+ log.error('command', process.argv.map(JSON.stringify).join(' ')), log.error('cwd', process.cwd());
59
+ log.error('node -v', process.version), log.error('node-gyp -v', 'v' + prog.package.version);
60
+ },
61
+ /**
62
+ * 打印最终诊断信息
63
+ */
64
+ issueMessage = () => {
65
+ errorMessage();
66
+ log.error('', ['node-gyp 构建你的包失败!', '请尝试更新 npm 和/或 node-gyp;若仍无法解决,请向包作者反馈;'].join('\n'));
67
+ },
68
+ /**
69
+ * 依次执行队列中的命令
70
+ */
71
+ run = async () => {
72
+ const command = prog.todo.shift()
73
+ if (!command) {
74
+ // 完成!
75
+ completed = true, log.info('ok');
76
+ return;
77
+ };
78
+
79
+ try {
80
+ const args = await prog.commands[command.name](command.args) ?? [];
81
+
82
+ if (command.name === 'list') {
83
+ if (args.length) args.forEach((version) => log.stdout(version));
84
+ else log.stdout('尚未安装任何 Node 开发文件;可用 `node-gyp install` 安装指定版本;')
112
85
  }
113
- } else if (args.length >= 1) {
114
- log.stdout(...args.slice(1))
86
+ else if (args.length >= 1) log.stdout(...args.slice(1))
87
+ return run(); // 继续执行队列中的下一条命令
88
+ } catch (err) {
89
+ log.error(command.name + ' error'), log.error('stack', err.stack), errorMessage(), log.error('not ok');
90
+ return process.exit(1);
115
91
  }
92
+ };
116
93
 
117
- // 继续执行队列中的下一条命令
118
- return run()
119
- } catch (err) {
120
- log.error(command.name + ' error')
121
- log.error('stack', err.stack)
122
- errorMessage()
123
- log.error('not ok')
124
- return process.exit(1)
125
- }
126
- }
127
-
128
- process.on('exit', function (code) {
129
- if (!completed && !code) {
130
- log.error('Completion callback never invoked!')
131
- issueMessage()
132
- process.exit(6)
133
- }
134
- })
135
-
136
- process.on('uncaughtException', function (err) {
137
- log.error('UNCAUGHT EXCEPTION')
138
- log.error('stack', err.stack)
139
- issueMessage()
140
- process.exit(7)
141
- })
94
+ process.on('exit', code => {
95
+ if (!completed && !code) log.error('完成回调未被调用!'), issueMessage(), process.exit(6);
96
+ });
97
+ process.on('uncaughtException', err => {
98
+ log.error('未捕获的异常'), log.error('stack', err.stack), issueMessage(), process.exit(7);
99
+ });
142
100
 
143
- // 开始执行给定命令!
144
- run()
101
+ run(); // 开始执行给定命令!
@@ -797,6 +797,19 @@ _MSBuildOnly(_compile, "MultiProcessorCompilation", _boolean) # /MP
797
797
  _MSBuildOnly(_compile, "PreprocessOutputPath", _string) # /Fi
798
798
  _MSBuildOnly(_compile, "ProcessorNumber", _integer) # the number of processors
799
799
  _MSBuildOnly(_compile, "TrackerLogDirectory", _folder_name)
800
+ _MSBuildOnly(
801
+ _compile,
802
+ "ExternalWarningLevel",
803
+ _Enumeration(
804
+ [
805
+ "TurnOffAllWarnings", # /external:W0
806
+ "Level1", # /external:W1
807
+ "Level2", # /external:W2
808
+ "Level3", # /external:W3
809
+ "Level4", # /external:W4
810
+ ]
811
+ ),
812
+ ) # /external:W
800
813
  _MSBuildOnly(_compile, "TreatSpecificWarningsAsErrors", _string_list) # /we
801
814
  _MSBuildOnly(_compile, "UseUnicodeForAssemblerListing", _boolean) # /FAu
802
815
 
@@ -153,7 +153,9 @@ def RelativePath(path, relative_to, follow_path_symlink=True):
153
153
  os.path.splitdrive(path)[0].lower()
154
154
  != os.path.splitdrive(relative_to)[0].lower()
155
155
  ):
156
- return path
156
+ # Makefile / cmake / ninja 等生成器要求 POSIX 路径,统一用正斜杠。
157
+ # MSBuild 也接受正斜杠,故对 msvs 无影响。
158
+ return path.replace(os.sep, "/")
157
159
 
158
160
  # Split the paths into components.
159
161
  path_split = path.split(os.path.sep)
@@ -173,7 +175,8 @@ def RelativePath(path, relative_to, follow_path_symlink=True):
173
175
  return ""
174
176
 
175
177
  # Turn it back into a string and we're done.
176
- return os.path.join(*relative_split)
178
+ # Makefile / cmake / ninja 等生成器要求 POSIX 路径,统一用正斜杠。
179
+ return os.path.join(*relative_split).replace(os.sep, "/")
177
180
 
178
181
 
179
182
  @memoize
@@ -24,6 +24,7 @@
24
24
 
25
25
  import os
26
26
  import re
27
+ import sys
27
28
  import subprocess
28
29
  import gyp
29
30
  import gyp.common
@@ -410,6 +411,7 @@ unreplace_spaces = $(subst """
410
411
  + SPACE_REPLACEMENT
411
412
  + """,$(space),$1)
412
413
  dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1)))
414
+ slashpath = $(subst \\,/,$(1))
413
415
 
414
416
  # Flags to make gcc output dependency info. Note that you need to be
415
417
  # careful here to use the flags that ccache and distcc can understand.
@@ -547,7 +549,7 @@ endef
547
549
  define do_cmd
548
550
  $(if $(or $(command_changed),$(prereq_changed)),
549
551
  @$(call exact_echo, $($(quiet)cmd_$(1)))
550
- @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))"
552
+ @mkdir -p "$(call slashpath,$(call dirx,$@))" "$(call slashpath,$(dir $(depfile)))"
551
553
  $(if $(findstring flock,$(word %(flock_index)d,$(cmd_$1))),
552
554
  @$(cmd_$(1))
553
555
  @echo " $(quiet_cmd_$(1)): Finished",
@@ -2318,7 +2320,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
2318
2320
  # path too aggressively if it features '..'. However it's still
2319
2321
  # important to strip trailing slashes.
2320
2322
  return path.rstrip("/")
2321
- return os.path.normpath(os.path.join(self.path, path))
2323
+ # Makefile 要求 POSIX 路径;Windows 上 normpath 会把 "/" 变成 "\",
2324
+ # 导致编译产物路径(.o / .d.raw)带反斜杠,sh 与 sed 解析失败。
2325
+ return os.path.normpath(os.path.join(self.path, path)).replace(os.sep, "/")
2322
2326
 
2323
2327
  def ExpandInputRoot(self, template, expansion, dirname):
2324
2328
  if "%(INPUT_ROOT)s" not in template and "%(INPUT_DIRNAME)s" not in template:
@@ -2361,6 +2365,17 @@ def WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files)
2361
2365
  if not gyp_binary.startswith(os.sep):
2362
2366
  gyp_binary = os.path.join(".", gyp_binary)
2363
2367
 
2368
+ # Windows 下 GNU make(NDK 自带)对深层相对路径的解析有缺陷,会把
2369
+ # 形如 $(srcdir)/../../.. 的外部 gypi 判为 "No rule to make target"。
2370
+ # 这里跳过所有来自 node_modules 的依赖——它们是只读的外部 gypi,
2371
+ # 内容不会在编译过程中变化;如确需重新生成 Makefile,重跑 configure 即可。
2372
+ # 本地的 binding.gyp 与 build/config.gypi 仍会保留,足以触发自动重新生成。
2373
+ deps_list = []
2374
+ for bf in build_files:
2375
+ if sys.platform == "win32" and "node_modules" in bf.replace("\\", "/"):
2376
+ continue
2377
+ deps_list.append(SourceifyAndQuoteSpaces(bf))
2378
+
2364
2379
  root_makefile.write(
2365
2380
  "quiet_cmd_regen_makefile = ACTION Regenerating $@\n"
2366
2381
  "cmd_regen_makefile = cd $(srcdir); %(cmd)s\n"
@@ -2368,7 +2383,7 @@ def WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files)
2368
2383
  "\t$(call do_cmd,regen_makefile)\n\n"
2369
2384
  % {
2370
2385
  "makefile_name": makefile_name,
2371
- "deps": " ".join(SourceifyAndQuoteSpaces(bf) for bf in build_files),
2386
+ "deps": " ".join(deps_list),
2372
2387
  "cmd": gyp.common.EncodePOSIXShellList(
2373
2388
  [gyp_binary, "-fmake"] + gyp.RegenerateFlags(options) + build_files_args
2374
2389
  ),