Repository navigation
503 lines (483 loc) · 28.9 KB
/
Copy pathci-macos.yml
File metadata and controls
503 lines (483 loc) · 28.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
name: CI · macOS
# 一平台一个文件 —— 这个文件里**只有** macOS(Apple clang / arm64)这条腿的测试。
# 四个文件:ci-linux-ubuntu.yml / ci-windows-msvc.yml / ci-mingw64.yml / ci-macos.yml。
#
# 文件内按**功能分格**:矩阵里一格一个 feature,格的名字就是 check 名
# (`CI · macOS / quic`)。骨架只写一次,格与格的差别全在矩阵条目里。
#
# 不设 `concurrency:`:取消一次正在跑的 run 等于**丢一组门禁**。
#
# 布局规范(含**未覆盖的格**和为什么)在 doc/ci-guide.md §1。这一条腿**跑不了**
# 使能宏契约那条链(`package_release.py` 的 PLATFORMS 里没有 macOS 的 key),
# 那是已知缺口而不是遗漏 —— 见 doc/ci-guide.md §5。
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
macos:
name: CI · macOS / ${{ matrix.feature }}
runs-on: macos-latest
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
# 基础版:net 默认 ON、web=OFF。
- feature: basic-static
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_STATIC=ON
- -DUVCPP_BUILD_SHARED=OFF
- -DUVCPP_BUILD_WEB=OFF
- feature: basic-shared
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_STATIC=OFF
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_WEB=OFF
# web:`UVCPP_ENABLE_ZLIB` 必须留 ON —— webapp 的压缩用例断言 gzip 真的
# 发生过,zlib 关着那两个组会红。zlib 在 find_package 落空时走 FetchContent。
- feature: web
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_BUILD_WEBAPP=ON
- -DUVCPP_ENABLE_ZLIB=ON
# yyjson:与 `web` 的 `flags` **逐字相同**,只多一个
# `-DUVCPP_ENABLE_YYJSON=ON`。`UVCPP_ENABLE_YYJSON` 默认 OFF,别的格
# **一行 `src/yyjson/` 都不编**,所以这一格是那个模块在这条腿上的唯一一次
# 编译 —— 少了它,用例里「`uvcpp_json_write` 与 nlohmann `dump()` 逐字节
# 相同」那一条在 Apple clang / libc++ 下从没跑过(而它与 libstdc++ 的
# `double` 格式化路径不是同一条)。
#
# 与 wsdl 不同:macOS **没有** wsdl 格(§1 的"未覆盖的格"表里那一行写的
# 是"不发版、风险低"),而 yyjson 这一格加得起 —— 它不需要任何新包。
#
# yyjson 走 FetchContent(根 `CMakeLists.txt:2081`,`GIT_TAG` 钉在
# `YYJSON_VERSION`),**不需要新包**;装包表与 `web` 逐字相同 —— 这样两边
# 红绿不同只可能归因到那个开关。
# 四个门禁串见 doc/ci-guide.md §5。
- feature: yyjson
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_BUILD_WEBAPP=ON
- -DUVCPP_ENABLE_ZLIB=ON
- -DUVCPP_ENABLE_YYJSON=ON
gate_name: yyjson
gate_switch: 'UVCPP_ENABLE_YYJSON:BOOL=ON'
gate_integrated: yyjson integrated
gate_module: Including yyjson module in build
gate_test: test_yyjson_doc_func
# brotli:与 `web` 的 `flags` **逐字相同**,只多一个
# `-DUVCPP_ENABLE_BROTLI=ON`。`UVCPP_ENABLE_BROTLI` 默认 OFF,
# 别的格一行 brotli 的代码都不编。
#
# **这一格与 Ubuntu 那一格的差别是"brotli 从哪来"**:macOS 上不装
# `brotli` 这个 Homebrew 包(装了会走 pkg-config / find_library 那条,
# 就量不到取源码那条支路了),于是它落进 FetchContent —— 而取源码那条
# 支路此前在 generate 阶段就因 `install(EXPORT uvcppTargets)` 失败
# (见 CHANGELOG「性能」那一节的第二条)。这一格是它在 CI 里的唯一一次
# 运行,也是**唯一用 libc++ 编它**的一次。
# 四个门禁串见 doc/ci-guide.md §5。
- feature: brotli
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_BUILD_WEBAPP=ON
- -DUVCPP_ENABLE_ZLIB=ON
- -DUVCPP_ENABLE_BROTLI=ON
gate_name: brotli
gate_switch: 'UVCPP_ENABLE_BROTLI:BOOL=ON'
gate_integrated: brotli enabled
gate_module: 'HTTP body compressor: UVCPP_HTTP_COMPRESS_ENABLE=(1||1)'
gate_test: test_web_http_client_func
- feature: ssl
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_BUILD_WEBAPP=ON
- -DUVCPP_ENABLE_OPENSSL=ON
- -DUVCPP_ENABLE_ZLIB=ON
# h2:门禁的四个串见 doc/ci-guide.md §5。
# ★ 批 3a(1.4.3)给这一格加了一行 `-DUVCPP_ENABLE_CAPI=ON`:h2 是
# **唯一**带 NGHTTP2 的一格,而 `src/capi/uvcpp_c_http2.cpp` 跟着那个
# 开关走(顶层那张 `UVCPP_CAPI_DISABLED` 表)—— 不加这一行,
# `tests/capi/capi_h2_func.c` 这个目标在 Apple clang 下不存在,
# "h2 的 C 面能编、能跑"在这里就是零。WEB / WEBAPP 本来就是 ON,
# 所以配出来的模块集合与本机那棵 `build-capi-h2` 逐字相同。
- feature: h2
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_BUILD_WEBAPP=ON
- -DUVCPP_ENABLE_OPENSSL=ON
- -DUVCPP_ENABLE_ZLIB=ON
- -DUVCPP_ENABLE_NGHTTP2=ON
- -DUVCPP_ENABLE_CAPI=ON
gate_name: nghttp2
gate_switch: 'UVCPP_ENABLE_NGHTTP2:BOOL=ON'
gate_integrated: nghttp2 integrated
gate_module: Including http2 module in build
gate_test: test_h2_session_func
# full:`UVCPP_BUILD_EXPAND` 自 v1.1.0 起默认 OFF(默认开时,使用者 TU 忘了
# 定义 `UVCPP_ENABLE_MEMORY_POOL` 会静默拿到与 dll 不同的分配器 —— 见
# RELEASE.md)。这里显式打开:不打开的话 tests/expand 那批用例在 CI 里
# 一条都不跑,内存池就成了没人测的死代码。**别顺手把这行删掉。**
# 另一条:这一格也是带 C ABI 的那一格("release 与 full 都支持 C 接口
# 导出"是需求本身)。ubuntu 那条腿上有同样的一行,理由写在那里 ——
# 包括**为什么不在这里补 `-DUVCPP_BUILD_WEBAPP=ON`**:这一格是
# "CAPI 开、WEBAPP 关"这个组合的唯一一站,1.4.3 批 2 的那次红就是它
# 与 ubuntu 那条腿一起报出来的。
- feature: full
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_ENABLE_OPENSSL=ON
- -DUVCPP_ENABLE_ZLIB=ON
- -DUVCPP_BUILD_EXPAND=ON
- -DUVCPP_ENABLE_CAPI=ON
# quic(1.4.1 新增的腿):和 ubuntu 那条腿的区别**只有 OpenSSL 的来源**。
# ubuntu 让 ssl 那一格各自装 `libssl-dev`(3.0.13,两个 QUIC 符号都没有),
# 所以那边必须从源码编一份;macOS 用 Homebrew 的 `openssl@3`(>= 3.2,带
# QUIC API),所以这里不需要自建,只要显式把 root 钉过去。
#
# **`-DOPENSSL_ROOT_DIR` 在这里是必需而不是可选**:`openssl@3` 是 keg-only,
# 而 CMake 的 `FindOpenSSL` 没有任何 Homebrew 提示。`brew --prefix openssl@3`
# 给出空串时 `-DOPENSSL_ROOT_DIR=` 会让 find_package 落到别处、QUIC 被
# 静默降级 —— 所以下面那一步先自检(空串当红)。
- feature: quic
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_NET=ON
- -DUVCPP_BUILD_WEB=OFF
- -DUVCPP_ENABLE_OPENSSL=ON
- -DUVCPP_ENABLE_QUIC=ON
openssl_root: $(brew --prefix openssl@3)
gate_name: ngtcp2
gate_switch: 'UVCPP_ENABLE_QUIC:BOOL=ON'
gate_integrated: ngtcp2 integrated
gate_module: Including quic module in build
gate_test: test_quic_api_func
# http3(1.4.1 新增的腿):web 层多出来的那条 h3 传输。与 quic 那一格
# 的差别就是 **WEB=ON** —— h3 的公开面长在 `uvcpp_http_client` /
# `uvcpp_http_server` 上,web 关着时 `CMakeLists.txt` 会把它强制关掉。
# 这一格是 macOS 上**唯一**编译 `src/http3/` 的配置(也是 Apple clang
# 第一次编它)。
#
# 1.4.4 起它也带 `-DUVCPP_ENABLE_CAPI=ON`:批 3b 的 C 面(quic + http3)
# 长在这两个模块上,而 `tests/capi/capi_quic_h3_func.c`(纯 C 写的
# UDP 两端)只有在这一格编得出来。判据是"它真的注册进去了并跑绿"
# —— 符号面锁只在 ubuntu 那一格跑(见下面 `capi` 那格的说明)。
- feature: http3
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_NET=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_ENABLE_OPENSSL=ON
- -DUVCPP_ENABLE_ZLIB=ON
- -DUVCPP_ENABLE_QUIC=ON
- -DUVCPP_ENABLE_HTTP3=ON
- -DUVCPP_ENABLE_CAPI=ON
openssl_root: $(brew --prefix openssl@3)
gate_name: nghttp3
gate_switch: 'UVCPP_ENABLE_HTTP3:BOOL=ON'
gate_integrated: nghttp3 integrated
gate_module: Including http3 module in build
gate_test: test_http3_web_func
# capi(1.4.1 新增的腿):**C ABI 那一层**。这一格在 macOS 上的价值与
# MSVC 那格不同 —— Apple clang 与 Linux 的 gcc/clang 是两套 C 前端,
# 而这一层的判据全靠**一个真正的 C 编译器**(`tests/capi/` 是 C99)。
#
# ★ 1.5.3 起这一格**同时开 DB**(只开 SQLite,两个远程后端显式 OFF),
# 于是 `tests/capi/capi_db_func.c`(纯 C)也在 Apple clang 下过一遍。
# 依赖**一个都不加**:macOS 的 SQLite 来自 SDK —— `Modules/Platform/
# Darwin.cmake` 把 `${CMAKE_OSX_SYSROOT}/usr` 插在
# `CMAKE_SYSTEM_PREFIX_PATH` 最前面,`sqlite3.h` 与 `/usr/lib/libsqlite3.tbd`
# 就在那儿(理由与 `db` 那一格同一段,Homebrew 的 `sqlite` 是 keg-only,
# 装了反而要钉 root)。所以下面那个 `case` 里 `capi` 仍与 `web` 同行。
#
# 符号面锁(`check_capi_symbols.py`)**只在 ubuntu 那一格跑**:它按 GNU
# nm 的方言解析那条腿的动态符号表,而 Mach-O 与 PE 各是另一套方言
# (脚本里那三条分支写得很清楚)。那两条腿守的是"编得出、链得上",
# 见 doc/ci-guide.md §5 —— db 那一片在它们上面同样**判不了**,所以
# 这一格的 db 开关不是为了锁,而是为了"这层在 Apple clang 下真的编、
# 真的跑"。
- feature: capi
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_BUILD_NET=ON
- -DUVCPP_BUILD_WEB=ON
- -DUVCPP_BUILD_WEBAPP=ON
- -DUVCPP_ENABLE_ZLIB=ON
- -DUVCPP_ENABLE_CAPI=ON
- -DUVCPP_ENABLE_DB=ON
- -DUVCPP_ENABLE_DB_SQLITE=ON
- -DUVCPP_ENABLE_DB_MYSQL=OFF
- -DUVCPP_ENABLE_DB_PGSQL=OFF
gate_name: capi
gate_switch: 'UVCPP_ENABLE_CAPI:BOOL=ON'
gate_integrated: C ABI enabled
gate_module: Including capi module in build
gate_test: test_capi_net_func
# db:数据库模块(`src/db/`)。`UVCPP_ENABLE_DB` **默认 OFF**,所以别的
# 格一行 `src/db/` 都不编 —— 少了这一格,整个模块可以带着 CI 全绿推上去
# 而从未在这条工具链上被编过一次。
#
# SQLite 走 `find_package(SQLite3)` 那条默认路,而 macOS 上**一个包都
# 不用装**:CMake 的 `Modules/Platform/Darwin.cmake` 在
# `_CMAKE_OSX_SYSROOT_PATH` 存在时把 `${sysroot}/usr` **插在
# `CMAKE_SYSTEM_PREFIX_PATH` 最前面**,于是 `find_path(sqlite3.h)` 命中
# SDK 里那份头、`find_library(sqlite3)` 命中 `/usr/lib/libsqlite3.tbd`。
# (Homebrew 的 `sqlite` 是 keg-only,它自己那句说明就是"macOS 自带
# SQLite";装它反而要多传一个 root。)所以装包表与 `basic-*` 同一行。
#
# MySQL / PostgreSQL 两个远程后端在这里**显式 OFF**:这一档没有服务端,
# 开了只会拿到两条退 3(未判定)的用例,而"未判定"在 job 结论里是**非红**
# —— 那不是覆盖,是假覆盖。两个远程后端由 Ubuntu 那个单开的 `db-servers`
# job 用服务容器量(见 doc/ci-guide.md §5)。
- feature: db
flags:
- -DUVCPP_BUILD_TESTS=ON
- -DUVCPP_BUILD_SHARED=ON
- -DUVCPP_ENABLE_DB=ON
- -DUVCPP_ENABLE_DB_SQLITE=ON
- -DUVCPP_ENABLE_DB_MYSQL=OFF
- -DUVCPP_ENABLE_DB_PGSQL=OFF
gate_name: sqlite3
gate_switch: 'UVCPP_ENABLE_DB:BOOL=ON'
gate_integrated: 'db: SQLite 后端开'
gate_module: Including db module in build
gate_test: test_db_sqlite_func
steps:
- uses: actions/checkout@v4
# 装包**按格**:`openssl@3` 只给 quic / http3 那两格显式点名(别的格用
# `openssl`,它今天就是 openssl@3 的别名,但那两格不需要钉 root)。
# `capi` 与 `web` 同一份 —— C 层不加任何外部依赖。
# `db` 与 `basic-*` 同一份 —— SQLite 在 macOS 上来自 SDK(理由写在矩阵里
# 那一格的注释里),Homebrew 的 `sqlite` 是 keg-only,装了反而要钉 root。
- name: Install deps
run: |
case "${{ matrix.feature }}" in
basic-static|basic-shared|db) brew install libuv ninja ;;
# yyjson 与 `web` 同一份:yyjson 走 FetchContent(见矩阵里那一格的注释),
# 不是 Homebrew 的包 —— 装成一样,两边红绿不同就只归因到那个 `-D`。
# brotli 也与 `web` 同一份:这一格**故意不装** Homebrew 的 `brotli`
# 包 —— 装了就走系统那条支路,而这一格要量的正是取源码那条
# (FetchContent,见矩阵里那一格的注释)。
web|capi|yyjson|brotli) brew install libuv zlib ninja ;;
ssl|h2|full) brew install libuv openssl zlib ninja ;;
quic|http3) brew install libuv zlib ninja openssl@3 ;;
*) echo "::error title=deps::装包表里没有这一格:${{ matrix.feature }}"; exit 1 ;;
esac
# 只有 quic / http3 那两格需要:先证明这份 Homebrew OpenSSL 真的带 QUIC API
# (h3 跑在 QUIC 上,要的是同一份)。不在这一步拦住的话,配置期的
# "缺 QUIC API → WARNING + 强制关闭"会把整格变成一次**静默的空转**
# (cache 照样写着 ON),而症状会落到门禁 ②(`ngtcp2 integrated` 缺席)上
# —— 那时注解说的是"ngtcp2 没被集成",而真正的原因(这份 OpenSSL 不对)
# 没人从那条输出里看得出来。
- name: Check the Homebrew OpenSSL has the QUIC API (quic + http3)
if: matrix.feature == 'quic' || matrix.feature == 'http3'
shell: bash
run: |
set -o pipefail
prefix="$(brew --prefix openssl@3)"
echo "prefix=$prefix"
# 这一步**只提示,不判红**(全部分支都用 `::warning`,一次 `exit 1`
# 都没有)。这是 d40e124 第一次 macOS 运行换来的设计:
#
# 那次它红在 `nm -gU … | grep -q _SSL_set_quic_tls_cbs` 上。而
# **`-U` 的含义在两族 nm 之间是反的**(LLVM 是 `--defined-only`,
# GNU 那边"只看未定义"写作 `-u`),再加上 Mach-O 的 dylib 可能被 strip
# 掉符号表(只剩导出 trie,`nm` 没得读)—— 也就是说,这条自检有**至少
# 两条**与 OpenSSL 好坏无关的红法。押错一次就是烧掉一整轮 macOS
# (7 条腿),而它原本只是想让报错早一点、话说清楚一点。
#
# 真判据在下面配置期那条门禁 ②(`ngtcp2 integrated`):OpenSSL 不对时
# 根 CMakeLists 的预检会 WARNING + 强制关掉 QUIC,于是配置消息里没有
# 那一行 —— 那才是判据,而且它报的时候会把配置期的**原话**一起带出来
# (见 Configure 那一步的门禁 ②)。所以这里退成纯诊断是对的:
# 一条自检绝不该比它所服务的门禁更容易红。
#
# 证据照样收集 —— 一条腿红了以后不登录能读到的只有注解(公仓的
# artifact zip 匿名下载是 401,实测过),所以"多少行、哪个版本"要么
# 出现在这里,要么就没人看得到。
lib="$prefix/lib/libssl.dylib"
if [ -z "$prefix" ] || [ ! -d "$prefix" ]; then
echo "::warning title=quic-openssl::brew --prefix openssl@3 是空的或不存在 —— 上一步的 openssl@3 没装上;**这一步不判红**,配置期的 ngtcp2 集成门禁会报"
elif [ ! -e "$lib" ]; then
echo "::warning title=quic-openssl::找不到 $lib($("$prefix/bin/openssl" version 2>/dev/null))—— 布局与预期不同;**这一步不判红**,判据见配置期的 ngtcp2 集成门禁"
else
"$prefix/bin/openssl" version
hits=$(nm -g "$lib" 2>/dev/null | grep -c '_SSL_set_quic_tls_cbs' || true)
echo "nm -g 命中 SSL_set_quic_tls_cbs 的行数:$hits(nm -g 共 $(nm -g "$lib" 2>/dev/null | wc -l | tr -d ' ') 行)"
nm -g "$lib" 2>/dev/null | grep '_SSL_set_quic_tls_cbs' \
|| echo "(nm -g 没有打出这一行)"
if [ "$hits" = "0" ]; then
echo "::warning title=quic-openssl::这份 openssl@3($("$prefix/bin/openssl" version 2>/dev/null))的 libssl 里 nm -g 看不到 SSL_set_quic_tls_cbs —— 可能真缺(需要 >= 3.2),也可能只是 nm 读不到(strip / 语义);**这一步不判红**,下面配置期的 ngtcp2 集成门禁会连原话一起报"
fi
fi
- name: Configure & Build
shell: bash
run: |
set -o pipefail
feat="${{ matrix.feature }}"
tree="build-$feat"
mkdir -p ci-logs
{
echo "=== toolchain ==="
cmake --version
openssl version 2>/dev/null || echo "(openssl not on PATH)"
echo "=== configure ==="
cfg=0
extra=()
if [ -n "${{ matrix.openssl_root }}" ]; then
extra+=("-DOPENSSL_ROOT_DIR=${{ matrix.openssl_root }}")
fi
cmake -S . -B "$tree" -DCMAKE_BUILD_TYPE=Release \
"${extra[@]}" ${{ join(matrix.flags, ' ') }} \
> "ci-logs/$feat-configure.log" 2>&1 || cfg=$?
echo "CONFIGURE_RC=$cfg"
cat "ci-logs/$feat-configure.log"
if [ "$cfg" != "0" ]; then exit "$cfg"; fi
# 门禁 1:这一格开的模块**真的**被集成了。三条判据缺一不可(理由见
# doc/ci-guide.md §5):缓存里开关是 ON、配置消息里出现了
# `<dep> integrated`、`src/<module>/` 真的进了源列表。
if [ -n "${{ matrix.gate_switch }}" ]; then
grep -q '${{ matrix.gate_switch }}' "$tree/CMakeCache.txt" || {
echo "::error title=$feat::CMakeCache 里 ${{ matrix.gate_switch }} 不是 ON —— 这一格没在测 ${{ matrix.gate_name }}"; exit 1; }
grep -q '${{ matrix.gate_integrated }}' "ci-logs/$feat-configure.log" || {
# 把**配置期的原话**折进注解:这条门禁最常被 OpenSSL/依赖不对触发,
# 而那种情形下 root CMakeLists 的预检只会 WARNING 一句、把开关
# 静默关掉(cache 里照样写着 ON),真正的原因只在那句 WARNING 里。
# 公仓的 artifact zip 匿名下载是 401(实测),所以注解是唯一
# 不登录读得到的地方 —— 原话必须跟着走,否则下一次红又要靠猜。
why=$(grep -E '(CMake (Warning|Error)|QUIC API|ngtcp2|nghttp2|Cannot|Unsupported|No such)' \
"ci-logs/$feat-configure.log" | head -10 | tr '\n' ' ' | sed 's/%/%25/g' || true)
echo "::error title=$feat::配置消息里没有 '${{ matrix.gate_integrated }}' —— ${{ matrix.gate_name }} 没被集成${why:+;配置期的原话:$why}"; exit 1; }
grep -q '${{ matrix.gate_module }}' "ci-logs/$feat-configure.log" || {
echo "::error title=$feat::src/ 里那一块没进源列表 —— 编出来的库不含 ${{ matrix.gate_name }}"; exit 1; }
fi
echo "=== resolved toolchain (CMakeCache) ==="
grep -E '^(CMAKE_(GENERATOR|LINKER|CXX_COMPILER|C_COMPILER)|OPENSSL_|NGHTTP2_|NGTCP2_)' \
"$tree/CMakeCache.txt" || true
echo "=== build ==="
bld=0
cmake --build "$tree" --config Release --parallel 4 2>&1 \
| tee "ci-logs/$feat-build.log" || bld=$?
echo "BUILD_RC=$bld"
if [ "$bld" != "0" ]; then
# 这一手是被 e3c0a0d 打的:四个 mac job 全挂在编译上(macOS 的
# sigemptyset 是宏,`::sigemptyset` 直接是语法错误),而本机 MSVC
# 整段跳过那个 `#if`,注解里只有一句 "exit code 2",白等一轮。
msg=$(grep -E 'error:|error C[0-9]|error LNK|Error|FAILED|undefined reference' \
"ci-logs/$feat-build.log" | head -40 | tr '\n' ' ' | sed 's/%/%25/g' \
| head -c 6000 || true)
echo "::error title=build-$feat::${msg:-rc=$bld (no error line matched)}"
fi
exit "$bld"
} 2>&1 | tee "ci-logs/$feat.log"
# 名字必须带 feat:upload-artifact 的名字在**一次 run 内唯一**。
- name: Upload build log
if: failure()
uses: actions/upload-artifact@v4
with:
name: build-log-macos-${{ matrix.feature }}
path: ci-logs/
if-no-files-found: ignore
# ★ 1.5.3:C ABI 这条腿同时开着 db 那一片,而"开没开成"不会自己变红。
# macOS 的 SQLite 来自 SDK,理论上不用装任何东西 —— 但 SDK 布局变了、
# 或 `CMAKE_OSX_SYSROOT` 没插进搜索路径时,守卫会把 `UVCPP_ENABLE_DB`
# 强制关掉,C 面的 db 符号一条都不编,而整格**照旧全绿**(`gate_test`
# 指的 `test_capi_net_func` 还在)。判据同 `db` 那一格的三个门禁串。
- name: Gate — the C ABI leg really has the db slice open
if: matrix.feature == 'capi'
shell: bash
run: |
log="ci-logs/capi-configure.log"
if [ ! -f "$log" ]; then
echo "::error title=capi-db::找不到 $log —— 这条步骤依赖上面 configure 那步写的日志"
ls -la ci-logs/ 2>/dev/null || true
exit 1
fi
fail=0
grep -q 'db: SQLite 后端开' "$log" || {
echo "::error title=capi-db::配置日志里没有 'db: SQLite 后端开' —— SDK 里那份 SQLite 没被找到,UVCPP_ENABLE_DB 会被守卫强制关掉,C 面的 db 符号一条都不编而整格全绿"
grep -iE 'db|sqlite|warning' "$log" | head -20; fail=1; }
grep -q 'Including db module in build' "$log" || {
echo "::error title=capi-db::没有 'Including db module in build' —— src/db/ 没进源列表,C 面那 81 个符号编不出来"
fail=1; }
ctest --test-dir build-capi -N 2>/dev/null | grep -q 'test_capi_db_func' || {
echo "::error title=capi-db::test_capi_db_func 没注册 —— 「模块被过滤掉了」和「用例跑过了」在 ctest 的输出里长得一样"
ctest --test-dir build-capi -N 2>&1 | tail -30; fail=1; }
if [ "$fail" != "0" ]; then exit 1; fi
echo "这一格同时开着 CAPI 与 DB:SDK 的 SQLite 找到了、模块进了源列表、C 面的 db 用例注册了 —— C 面的 db 包装在 Apple clang 下真的被编过一遍"
- name: Test
shell: bash
run: |
feat="${{ matrix.feature }}"
tree="build-$feat"
# 这一格的 OpenSSL 是 Homebrew 的 dylib,路径由 brew 的链接目录提供,
# 不需要 LD_LIBRARY_PATH(DYLD_* 那套也不要加:runner 上会连带影响别的)。
# 门禁 2:这一格的用例真的注册了。只跑 ctest 不足以发现"它们根本没被注册"——
# 那种情况下总数少几个,而 ctest 照旧报 "100% tests passed"。
if [ -n "${{ matrix.gate_test }}" ]; then
if ! ctest --test-dir "$tree" -N 2>&1 | grep -q "${{ matrix.gate_test }}"; then
echo "::error title=$feat::${{ matrix.gate_test }} 没被注册 —— 模块关了,还是 tests/functional/CMakeLists.txt 的过滤正则又写错了?"
ctest --test-dir "$tree" -N 2>&1 | tail -20
exit 1
fi
fi
# Only test_shutdown_func is excluded (flaky ~1%, not hanging) — see
# doc/ci-guide.md §4.
rc=0
out=$(ctest --test-dir "$tree" --output-on-failure -C Release \
--timeout 30 --exclude-regex "test_shutdown_func" 2>&1) || rc=$?
printf '%s\n' "$out"
if [ "$rc" != "0" ]; then
msg=$(printf '%s\n' "$out" | grep -A40 'The following tests FAILED' | tr '\n' ' ' | sed 's/%/%25/g' || true)
echo "::error title=ctest-build-$feat::${msg:-rc=$rc (no FAILED block)}"
# 失败用例的**子进程输出**:`--output-on-failure` 把它的 stdout/stderr 紧跟在
# 自己的 `***`(SegFault / Subprocess aborted / Timeout)结果行之后。job 日志
# 匿名读不到(`/actions/jobs/<id>/logs` 是 403),所以把它折进注解 —— 否则
# 注解里只有用例名,没有崩溃现场(断言文案 / libc++ 的 `libc++abi: terminating`
# 全在那段里)。
#
# 块的两端都承重:起点是那条 `***` 结果行,终点是下一条 `NN/MM Test #`
# / `Start N:` / `N% tests passed` —— 不是"起点之后固定 30 行"。
# **取头 20 行 + 尾 25 行**,因为崩溃现场在输出的**末尾**(进程是打印到
# 一半死的):只取头,对"输出比窗口长"的用例恰好把现场切掉,注解里剩下
# 一串 `[ ok ]`,看不出崩在哪一句 —— 1.5.3 首次 macOS 的
# `test_db_sqlite_pool_func` 就这么红的(注解正好 30 行,末行停在第 30 条
# 断言上,第 31 行之后是什么无从得知)。
#
# awk **不 `exit`**:提前退出会让上面的 `printf` 吃 SIGPIPE,配上
# `set -o pipefail` 就是"注解没打出来、步骤先死在赋值上"。`head` 那一支同理。
blk=$(printf '%s\n' "$out" | awk '
/^[0-9]+\/[0-9]+ Test #/ || /^ *Start [0-9]+: / || /^[0-9]+% tests passed/ { if (f) f = 0 }
/\*\*\*/ { f = 1 }
f { print }' | tr -d '\r')
cnt=$(printf '%s\n' "$blk" | wc -l | tr -d ' ')
if [ "$cnt" -gt 50 ]; then
blk="$(printf '%s\n' "$blk" | head -20 || true)
……(中间 $((cnt - 45)) 行省略)……
$(printf '%s\n' "$blk" | tail -25 || true)"
fi
blk=$(printf '%s\n' "$blk" | sed '/^[[:space:]]*$/d' | tr '\n' ' ' \
| sed 's/%/%25/g' | head -c 6000 || true)
if [ -n "$blk" ]; then
echo "::error title=ctest-build-$feat-output::${blk}"
fi
fi
exit "$rc"