Repository navigation
2682 lines (2572 loc) · 163 KB
/
Copy pathrelease.yml
File metadata and controls
2682 lines (2572 loc) · 163 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
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
name: Release
# 推 v* 标签即出包;也可以在 Actions 页面上手动给一个已存在的 tag 重跑。
# 重跑是**幂等**的:publish 用的是 `gh release upload --clobber`,已存在的
# Release 会被更新而不是报错 —— 这条很重要,因为第一次跑往往就挂在某个平台上,
# 修完只想重发那一个。
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: '要出包的 tag(须已存在)'
required: true
type: string
dry_run:
description: '只跑构建与打包,不创建/更新 Release'
required: false
default: false
type: boolean
permissions:
contents: write
# 版本号不在这里写死:出包的 zip 名与 uvcpp.pc 的 Version 由
# `package_release.py` 从 `src/uvcpp/uvcpp_version.h` 读(`_header_version()`)。
# 这里原本有一条 `env: UVCPP_VERSION: '1.1.0'`,全文件零引用,且标签打到 v1.1.5
# 时它仍然写着 1.1.0 —— 删掉,免得下次有人拿它当"版本来源"。
# ---------------------------------------------------------------------------
# 1.5.0 起:**六条腿的发布产物都是全功能的**(QUIC + HTTP3 + C API 一起开)
# ---------------------------------------------------------------------------
# 之前的版本里发布包**不带** QUIC/HTTP3(那两个开关默认 OFF,发布腿也不传)。
# 1.5.0 起官方二进制按"全功能"发,于是每条腿都要满足 QUIC 的前提:一份
# **带 QUIC API 的 mainline OpenSSL ≥ 3.2**。各腿的来路不同,逐条记在这里,
# 改任何一条之前先看这张表:
#
# linux-x64 / linux-arm64 —— runner 是 22.04,apt 的 libssl-dev 是 **3.0.2**
# (没有 QUIC API),所以这两条腿**自己在 job 里编一份 OpenSSL 3.5.0**,
# 参数是 `no-shared no-tests -fPIC`:静态(发布产物要"除 libc 外无运行时
# 依赖")、PIC(要塞进 libuvcpp.so)。同时**不装** libssl-dev,免得给
# find_package 摆一份不合用的候选。见各腿 "Build OpenSSL with the QUIC API"。
# mingw-x64 / mingw-arm64 —— 用 MSYS2 包的 openssl(今天 3.6.5,≥ 3.2)。
# 这两条腿是把静态 OpenSSL 链进 dll,与 SSL 那条路同一条,只是多了一个
# 会用到 QUIC 符号的 ngtcp2。
# msvc-x64 —— 用 runner 镜像/脚本给的那份 OpenSSL(见
# `.github/scripts/win-openssl-deps.sh`)。这份**必须是 ≥ 3.2**:不够的话
# 配置期预检会把 QUIC 静默关掉,而下面的模块断言会把这条腿判红。
# msvc-arm64 —— 镜像上没有 arm64 的 OpenSSL,这条腿自己拿
# `VC-WIN64-ARM no-asm` 从源码编 3.5.8(见 "Build OpenSSL for arm64")。
#
# **静默降级**是这五处共同的风险形状:OpenSSL 不合用时根 CMakeLists 走的是
# `message(WARNING)` + `set(UVCPP_ENABLE_QUIC OFF)`,那是**普通变量**,于是
# `CMakeCache.txt` 里照旧写着 `=ON` 而编译器看到的是 0。所以六条腿的模块断言
# **一律读生成的头**(`<tree>/include/uvcpp/uvcpp_config.h`)而不是 cache ——
# 判据得是"编译器真读到了什么"。理由写在 mingw-x64 那条断言步的注释里。
# ---------------------------------------------------------------------------
# 1.7.0 起:上面那句"全功能"才真的全 —— 补上 yyjson / brotli / MySQL+PostgreSQL
# ---------------------------------------------------------------------------
# 1.5.0 写下"全功能"时漏了三个开关,1.7.0 才补上。补之前先把口径钉死:把
# `CMakeLists.txt` 里每个 `option(UVCPP_ENABLE_*)` 与十处 configure 对一遍,
# "**默认 OFF 且发布腿没传**"的集合在 1.6.x 里**恰好有两个元素**;补完之后这个
# 集合是**空的**(本机用一段脚本对过,不是数出来的)。口径只算 `UVCPP_ENABLE_*`:
# `UVCPP_BUILD_EXAMPLES` / `UVCPP_BUILD_BENCH` 同样默认 OFF 且不传,但那两个问的是
# "要不要顺手编示例与跑分程序",不影响库本身导出什么。
#
# ⑥ UVCPP_ENABLE_YYJSON / UVCPP_ENABLE_BROTLI —— 上面那两个元素。
# yyjson 是**第二个 JSON 后端**(`src/yyjson/`,与 nlohmann **并存**、不是
# 二选一:公开头一个 yyjson 类型都不出现,它是私有依赖、单头加单 .c);
# brotli 是响应的 `Content-Encoding: br`。
#
# 这一条必须配 `UVCPP_BROTLI_FROM_SOURCE=ON`(1.6.52 新增,见
# CMakeLists.txt 里 brotli 发现那一段)。不配就是听天由命 —— 发现顺序是
# pkg-config → find_path/find_library → FetchContent,而 Linux 腿今天"干净"
# 只是因为那条 `apt-get install` 只装了 zlib1g-dev/ninja-build。哪天镜像或
# msys2 自带了 libbrotli,配置期会**静默**改走系统共享库,产物凭空多三条
# DT_NEEDED(libbrotlienc/dec/common),而 zlib 那条用的
# `-DCMAKE_DISABLE_FIND_PACKAGE_ZLIB=ON` 在这里**不管用**:它只挡
# `find_package`,挡不住 `pkg_check_modules`。下面 ldd 那份名单里也补了
# `brotli` —— 开关是"让它静态进去",名单是"万一没进去就当场红",两件事。
#
# ⑦ db 的 MySQL / PostgreSQL 两个后端 —— 1.6.x 为止十处 configure 都显式传
# `-DUVCPP_ENABLE_DB_MYSQL=OFF -DUVCPP_ENABLE_DB_PGSQL=OFF`,包里只有 SQLite。
# 这一版改成**三个后端都在**,但这条路有个陷阱,也是它比 ⑥ 麻烦得多的原因:
# 直接链 libmysqlclient/libpq 会让"自包含"当场作废,而且失败方式不是"少个
# 功能",是**进程根本起不来** ——
#
# DT_NEEDED(Windows 上是 PE 的导入表)是**加载期**解析的。产物上多一条
# `libpq.so.5`,没装过 libpq 的机器连 `libuvcpp.so` 都加载不了,哪怕那个
# 程序只跑 HTTP、一行 db 代码都不碰。
#
# 所以六条腿都配 `-DUVCPP_DB_RUNTIME_LOAD=ON`(1.6.54 新增,**默认 OFF** ⇒
# 既有使用者的构建逐字节不变):符号走 `dlopen(RTLD_NOW | RTLD_LOCAL)` /
# `LoadLibraryA` 运行时取,链接行上不出现这两个库。
#
# **但"省掉链接"省不掉编译**:头必须在(`MYSQL_BIND`/`MYSQL_FIELD`/`PQresult`
# 这些按值、按布局用到的类型不能靠手抄结构体赌 ABI —— 那正是这条路要躲开的
# 东西)。于是六条腿各自去弄一份头,三种来路:
# · mingw-x64 / mingw-arm64 —— MSYS2 的 `mingw-w64-*-libmariadbclient` 与
# `-postgresql` 进了 job 的 `install:` 列表,头在 `$MINGW_PREFIX/include/`;
# · msvc-x64 / msvc-arm64 —— 镜像上都没有(MySQL 那一项是 `Install-MysqlCli`
# 装的**命令行工具**、没有头;`windows-11-arm` 连 PostgreSQL 都没有),
# 两条腿统一跑 `.github/scripts/win-client-headers.sh` 取同一份钉死版本的
# MSYS2 `.pkg.tar.zst`;
# · linux-x64 / linux-arm64 —— `apt-get download` 取 `.deb`、`dpkg -x` 摊开,
# **一个包都不 install**:jammy 的 `libmysqlclient-dev` 与 `libpq-dev` **都**
# `Depends: libssl-dev`,装任何一个都会把上面那条"不装 libssl-dev"的规矩
# 破掉(整段理由链在 linux-x64 那一步的注释里)。
#
# 于是失败形状变成"没装客户端库的机器照常跑 HTTP/SQLite,只有真去连
# MySQL/PG 才拿到一条说人话的错误",而不是"整个库加载不了"。下面那条 ldd
# 断言里 `mysqlclient|pq` 两个名字从此**正面**证明这件事成立(谁把链接行改
# 回去,它立刻红);Linux 两条腿另外多一条 `nm -D -u` 的判据,量的是"还剩
# 几个未定义的 mysql_/PQ 符号"—— 那是 `ldd` 看不见的另一半(漏改一处调用点
# 的形状,见那一步的注释)。
#
# ---------------------------------------------------------------------------
# 1.7.2 起:db 的**第四个**后端 ODBC —— 这一节补的是"最容易被静默漏掉"的那一个
# ---------------------------------------------------------------------------
# ⑧ UVCPP_ENABLE_DB_ODBC —— 与 ⑦ 的三个后端不同,这一个**默认就是 ON**,所以它
# 不在"默认 OFF 且发布腿没传"那个差集里(那个差集在 1.7.0 之后已经是空的)。
# 加它不是为了补差集,是因为它的失败形状**恰好在差集之外**、而正确性只由生成
# 头那颗宏决定:
#
# `CMakeLists.txt` 对它的处理是"找不到 `sql.h` ⇒ `message(WARNING)` +
# `set(UVCPP_ENABLE_DB_ODBC OFF)`"。那是一句**普通变量赋值** ⇒
# `CMakeCache.txt` 里照旧写着 `UVCPP_ENABLE_DB_ODBC:BOOL=ON`,只有生成的
# `uvcpp_config.h` 里那颗宏是 0(这一条与 1.5.0 那次 QUIC/HTTP3 的静默降级
# 是同一个形状,理由写在 mingw-x64 那条断言步的注释里)。所以:**读 cache
# 永远看不出这个后端没进去,只有读生成头才看得出** —— 这就是它必须进那份
# 断言清单的原因,也是"默认 ON"不能当作"不用管"的原因。
#
# (`UVCPP_DB_RUNTIME_LOAD` 那一步为什么救不了:它管的是**链接**,编不编这个
# 后端是**头**在决定。两条腿各自独立,任一条没配好就少一个后端。)
#
# 六条腿的头**两种来路**,与 ⑦ 的三路同一条纪律(能取到就是取到,取不到当场
# 红,绝不"跳过这一步继续编"):
# · mingw-x64 / mingw-arm64 —— MSYS2 的 `mingw-w64-{x86_64,clang-aarch64}-unixodbc`
# 进了 job 的 `install:` 列表。**注意它的头不在 `include/` 下**,而在
# `$MINGW_PREFIX/include/unixodbc/`(2026-10-16 在 `packages.msys2.org` 上
# 逐条核过两个 arch 的文件表:`/mingw64/include/unixodbc/sql.h`、
# `/clangarm64/include/unixodbc/sql.h`)—— 指到 `include/` 会**找不到**,
# 然后就是上面那个静默降级,所以下面 configure 里那份 `_INCLUDE_DIR`
# 必须带着 `/unixodbc` 这一段。
# · msvc-x64 / msvc-arm64 —— 用 Windows SDK 里那份(`<SDK>/Include/<ver>/um/sql.h`),
# 由 `.github/scripts/win-odbc-headers.sh` 找出来。理由:Microsoft 的 ODBC
# 头就是 SDK 的一部分、随工具链一起装、且**已经是这个后端验证过的那一份**
# (wlzaweb 的 CI 里 libuvcpp 的 ODBC 后端就是在 SDK 头上编出来、并连上真
# SQL Server 跑通业务口径的)。
#
# **那段脚本真正承重的地方是"挑哪一版 SDK"**:按**版本号**取最大的那个,
# 不是字典序。这条是实测出来的 —— 字典序会把 `10.0.17763.0`(第一个
# compat-only 的 SDK)排在 `10.0.22621.0` 前面,而把老 SDK 的 `um` 混进新
# 工具链的头搜索路径,报出来的是 `oaidl.h`/`propidlbase.h`/`sqltypes.h` 里
# 成百条的 C4430/C2144,看上去像 ODBC 的锅,其实一行关系都没有。脚本里那段
# 注释写了怎么复现。
#
# 两条 mingw 腿拿到的是 **unixODBC** 的头、不是微软那套:这是有意的,也是
# `packages.msys2.org` 上给 mingw 编的唯一一份。两个后果都是我们要的:
# · 运行期那份 `libodbc` 也是 unixODBC —— 头与实现同一份源码,不存在"按微软
# 的头编、却在 unixODBC 上跑"的分叉(`src/db/uvcpp_db_odbc.cpp` 头部那条
# `SQLWCHAR` 宽度不假设正是为这件事写的:2 字节走 UTF-16、4 字节走 UTF-32,
# 两种头的差异在那里被消化掉);
# · Windows 上真正加载的仍然是**系统** `odbc32.dll`(`uvcpp_db_odbc.cpp` 的候选
# 名里 Windows 那一支就是它)—— 所以 mingw 包里连 unixODBC 的 .dll 都不需要,
# 头只是编译期的事。
#
# **两条 mingw 腿的导入表白名单一个字都不要动。** 那份白名单(下面
# "Assert the DLL is self-contained" 那一步)里**没有** `odbc32.dll`,这是想要
# 的性质:真被链进去时它会当场红,红了就是"运行期加载失效"的信号。为了让断言
# 变绿而把 `odbc32.dll` 加进白名单,等于把"Windows 上也按需加载"这条唯一的守卫
# 拆掉。
#
# Linux 两条腿多一条**负面**判据:`ldd` 的白名单加 `odbc` 分支(命中即红 =
# runtime load 没生效、被直接链进去了),`nm -D -u` 那条计数加 `SQL[A-Z]`
# (ODBC 符号也不该留下未定义残留)。
jobs:
# ---------------------------------------------------------------------------
# Windows x64 / MinGW-w64 —— 这是主发布产物(依赖全静态,任意 64 位 Windows 可用)
# ---------------------------------------------------------------------------
mingw-x64:
runs-on: windows-latest
# 120 → 150(1.5.0 起):这一版**六条腿全开 QUIC/HTTP3**,于是每条腿除了
# 自己的源文件与用例,还要先把 ngtcp2 / nghttp3 从源码编出来(FetchContent,
# 见 CMakeLists.txt 里那两段),linux 那两条腿还要多编一份 OpenSSL。
# 卡在上限上红一次 = 重推一次标签 + 再等一个多小时;多给的这半小时只在真
# 出问题时才用到。两个方向的代价不对称,所以给。
timeout-minutes: 150
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: false
# 最后两个是 1.7.0 起的:发布包要带 db 模块的 MySQL/PG 两个后端,而
# `UVCPP_DB_RUNTIME_LOAD=ON` 省掉的是**链接**、省不掉**编译** ——
# `MYSQL_BIND`/`MYSQL_FIELD` 这些按值按布局用到的类型要从真的
# `mysql.h` 读(见 `.github/scripts/win-client-headers.sh` 头部那段)。
# 包里的头落在 `$MINGW_PREFIX/include/mysql/mysql.h` 与
# `$MINGW_PREFIX/include/libpq-fe.h`(2026-10-14 在 `packages.msys2.org`
# 上逐个核过文件表),下面 configure 里那两个 `_INCLUDE_DIR` 就是照
# 这个布局指过去的。
#
# 最后那个 `unixodbc` 是 1.7.2 起的(db 的第四个后端)。**它的头不在
# `include/` 下而在 `include/unixodbc/` 下** —— 同一个包页上核过文件表,
# 所以下面 configure 里那份 `_INCLUDE_DIR` 带着 `/unixodbc` 这一段。
# 拿它是为了**编**(头),运行期加载的是 Windows 系统的 `odbc32.dll`,
# 与这个包里的 .dll 无关(整段理由在文件头 ⑧)。
#
# `mongo-c-driver` 是 1.7.5 起的(db 的第五个后端)。与 odbc 那条同形:
# 只拿**头**,运行期 `LoadLibraryA` 使用者装的那份客户端库。**头的布局与
# 另外四个都不同**:`$MINGW_PREFIX/include/{mongoc,bson}/…` 是**平的**
# (2026-10-19 在 `mirror.msys2.org` 的 `mingw64.db.tar.zst` 里核过文件
# 表),没有 Debian 那种 `libmongoc-1.0/` 中间层,所以 configure 里的
# `_INCLUDE_DIR` 指 `include` 本身。这个包同时带来 `libmongoc2.dll` /
# `libbson2.dll`(2.x 的 SONAME 去掉了 `-1.0`)—— 那两个**不进包**,
# 下面那条导入表白名单会拦。
install: >-
mingw-w64-x86_64-gcc
mingw-w64-x86_64-cmake
mingw-w64-x86_64-ninja
mingw-w64-x86_64-openssl
mingw-w64-x86_64-python
mingw-w64-x86_64-libmariadbclient
mingw-w64-x86_64-postgresql
mingw-w64-x86_64-unixodbc
mingw-w64-x86_64-mongo-c-driver
- name: Configure & Build
run: |
set -o pipefail
# 两个开关都是为了"依赖静态进去",缺一个产物就多一个 dll 依赖。
# ① UVCPP_BUILD_LIBUV_FROM_SOURCE=ON:不带 preset 时它默认 OFF,
# `find_library` 会撞上 runner 镜像 MSYS2 自带的 libuv.dll.a,链成
# 共享库 ⇒ 依赖 libuv-1.dll。BUILD_SHARED_LIBS 救不了这一路
# (CMakeLists.txt:1613-1613 判的是 TARGET uv_a,而 find_library 给的是路径)。
# ② BUILD_SHARED_LIBS=OFF:管 llhttp(根 CMakeLists.txt:6 默认 ON ⇒
# llhttp_shared ⇒ 依赖 libllhttp.dll)。zlib 不受影响,
# CMakeLists.txt:2556-2557 明确链 zlibstatic。
# 本机所有树都走 CMakePresets(它有 ①),所以这两条从来没在本机复现过。
#
# ③ UVCPP_ENABLE_NGHTTP2=ON:这个开关**默认 OFF**(CMakeLists.txt:159-159),
# 而这几个打包装的 job 一直没传它 —— 于是包里根本没有 h2,README 却在
# 介绍它。nghttp2 是唯一被强制静态的依赖(CMakeLists.txt:653-667),
# 所以开它不会给产物添任何 dll。六个打包 job 都必须传,任何一个漏了
# 就会发一个"同版本号但少一半功能"的包。
#
# ④ UVCPP_ENABLE_CAPI=ON:它**默认也是 OFF**,而"发布出来的库支持导出 C
# 类型接口"是这一版的一条**发布判据**(需求原话),不是可选项。与
# ③ 同一条纪律:**十个 configure 一个都不许漏**(六个打包 job,其中四个
# 各自还有一棵 debug 树)。漏一个的后果比 ③ 更隐蔽 —— 包里的头照样有
# `include/capi/`(那由 `package_release.py` 的 MODULES 决定,与这一趟
# 开关无关),于是"头在、符号不在",使用方在**运行时**才拿到
# `EntryPointNotFoundException`。C 层是薄包装:不加外部依赖、不多产物、
# 不多一个 dll,所以开它不改变发布包的模块集合。
#
# ⑤ UVCPP_ENABLE_DB=ON + UVCPP_DB_SQLITE_FROM_SOURCE=ON(1.7.0 起
# MySQL/PG 也显式 ON,1.7.2 起 ODBC 同样):
# "发布包带数据库模块"是这一版的一条**发布判据**,而 SQLite 走**自编
# amalgamation**。四条理由全是本机实测出来的,
# 完整版写在 CMakeLists.txt 那段 SQLite 发现上面与 doc/db-guide.md
# 「预编译包里的 SQLite 是从源码编的」:
# · 链系统 `libsqlite3.so` ⇒ 产物多一条 DT_NEEDED,正是下面那条 ldd
# 断言在拦的形状(名单原先**没有** sqlite3,已一并补上);
# · 链系统 `libsqlite3.a` ⇒ Ubuntu 24.04 上那份不是 PIC,塞进 .so
# 当场 `relocation R_X86_64_PC32 … can not be used when making a
# shared object`;
# · MySQL/PG 在 1.7.0 **之前**是显式 `=OFF` 的:runner 上装着
# libpq-dev 时 `find_package(PostgreSQL QUIET)` 会**静默**成功,
# 包里就多一条 `libpq.so.5` —— 那种包在开发机上照样跑得动,只有
# 换台机器才炸。1.7.0 起改成"显式 `=ON` + 显式给头目录 + 靠
# `UVCPP_DB_RUNTIME_LOAD=ON` 把链接行擦干净"(文件头 ⑦ 那三种
# 头的来路):要的是**两个后端都在包里**,而不是拿 `=OFF` 换一条
# 好看的链接行。这一条今天仍然承重 —— 谁把头目录那一行删掉,
# `find_package` 那一路就又有了机会。
# · ODBC 是同一形状的第四个:它**默认 ON**,但找不到 `sql.h` 时
# 只 WARNING 一句就自己关掉(cache 里照旧写着 ON)—— 这一组里
# 最容易"少一个后端而没有任何东西响"的就是它。所以它除了显式
# `=ON`,还必须同时给头目录,并被下面那条断言盯着(文件头 ⑧)。
# · 三个都关掉时整个模块会被强制关掉,所以 SQLite 那一条不能省。
# 与 ③④ 同一条纪律:**十个 configure 一个都不许漏**。SQLite 这条路
# 不需要任何新包、不添任何步骤 —— 依赖是配置期从 sqlite.org 取的一份
# 钉死哈希的 zip(本机实测:哈希与 sqlite.org 下载页上印的那个逐字相同)。
cmake -S . -B build-mingw -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DUVCPP_BUILD_LIBUV_FROM_SOURCE=ON \
-DUVCPP_BUILD_TESTS=ON -DUVCPP_BUILD_SHARED=ON -DUVCPP_BUILD_STATIC=OFF \
-DUVCPP_BUILD_WEB=ON -DUVCPP_BUILD_WEBAPP=ON \
-DUVCPP_ENABLE_ZLIB=ON -DUVCPP_ENABLE_OPENSSL=ON \
-DUVCPP_ENABLE_NGHTTP2=ON \
-DUVCPP_ENABLE_CAPI=ON \
-DUVCPP_ENABLE_QUIC=ON -DUVCPP_ENABLE_HTTP3=ON \
-DUVCPP_ENABLE_WSDL=ON \
-DUVCPP_ENABLE_YYJSON=ON \
-DUVCPP_ENABLE_BROTLI=ON -DUVCPP_BROTLI_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB=ON -DUVCPP_DB_SQLITE_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB_MYSQL=ON -DUVCPP_ENABLE_DB_PGSQL=ON \
-DUVCPP_ENABLE_DB_ODBC=ON \
-DUVCPP_DB_RUNTIME_LOAD=ON \
-DUVCPP_DB_MYSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/mysql" \
-DUVCPP_DB_PGSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_DB_ODBC_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/unixodbc" \
-DUVCPP_ENABLE_DB_MONGODB=ON \
-DUVCPP_DB_MONGODB_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_BUILD_EXPAND=ON -DUVCPP_STATIC_RUNTIME=ON \
-DOPENSSL_USE_STATIC_LIBS=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_ZLIB=ON
cmake --build build-mingw --parallel 4
# 调试档:**另开一棵**单配置树(`CMAKE_BUILD_TYPE=Debug`),开关列表与上面
# 逐字相同 —— 上面那三条注释(依赖静态)对调试档**一样**成立,少传任何一个
# 就等于发一份"能调但起不来"的调试库。
# 不换成 Ninja Multi-Config:那会把 Release 产物从 build-mingw/ 挪到
# build-mingw/Release/,上面那条 objdump 自检与 --tree 实参全要跟着动。
# `-DUVCPP_BUILD_TESTS=OFF`:这条腿要的是**产物**,调试档的测试不在这里跑。
cmake -S . -B build-mingw-dbg -G Ninja -DCMAKE_BUILD_TYPE=Debug \
-DBUILD_SHARED_LIBS=OFF \
-DUVCPP_BUILD_LIBUV_FROM_SOURCE=ON \
-DUVCPP_BUILD_TESTS=OFF -DUVCPP_BUILD_SHARED=ON -DUVCPP_BUILD_STATIC=OFF \
-DUVCPP_BUILD_WEB=ON -DUVCPP_BUILD_WEBAPP=ON \
-DUVCPP_ENABLE_ZLIB=ON -DUVCPP_ENABLE_OPENSSL=ON \
-DUVCPP_ENABLE_NGHTTP2=ON \
-DUVCPP_ENABLE_CAPI=ON \
-DUVCPP_ENABLE_QUIC=ON -DUVCPP_ENABLE_HTTP3=ON \
-DUVCPP_ENABLE_WSDL=ON \
-DUVCPP_ENABLE_YYJSON=ON \
-DUVCPP_ENABLE_BROTLI=ON -DUVCPP_BROTLI_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB=ON -DUVCPP_DB_SQLITE_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB_MYSQL=ON -DUVCPP_ENABLE_DB_PGSQL=ON \
-DUVCPP_ENABLE_DB_ODBC=ON \
-DUVCPP_DB_RUNTIME_LOAD=ON \
-DUVCPP_DB_MYSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/mysql" \
-DUVCPP_DB_PGSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_DB_ODBC_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/unixodbc" \
-DUVCPP_ENABLE_DB_MONGODB=ON \
-DUVCPP_DB_MONGODB_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_BUILD_EXPAND=ON -DUVCPP_STATIC_RUNTIME=ON \
-DOPENSSL_USE_STATIC_LIBS=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_ZLIB=ON
cmake --build build-mingw-dbg --parallel 4
# 发布产物必须是"任意 64 位 Windows 直接可用"。这条只有在这里断言才拦得住
# —— 静态链接被改坏时,产物在 CI 机上照样跑得动。
#
# 断言写成**白名单**而不是黑名单:黑名单一开始只 grep 了
# libgcc|libstdc|libwinpthread,漏掉了 OpenSSL —— MSYS2 同时装了 `libssl.a`
# 和 `libssl.dll.a`,find_library 默认挑 `.dll.a`,产物会凭空多一个
# libssl-3-x64.dll 而断言照样放行。两个方向的代价不对称:白名单误判时挡住
# 发布(看得见,改一行就好),黑名单漏判时发一个干净机器起不来的包。
- name: Assert the DLL is self-contained
run: |
# 两份都要过:调试档与发布档是**两次独立的 configure**,上面那三个开关
# 任何一份漏传,就只有那一份多出 dll 依赖 —— 只查发布档会把它放过去,
# 而那恰恰是使用者拿去调试时才会撞上的那份。`-g` 本身不改导入表。
rc=0
for d in build-mingw/libuvcpp.dll build-mingw-dbg/libuvcppd.dll; do
echo "--- $d"
deps=$(objdump -p "$d" | awk '/DLL Name/ {print $3}' | sort -u)
echo "$deps"
bad=$(echo "$deps" | grep -viE '^(ADVAPI32|bcrypt|bcryptprimitives|CRYPT32|dbghelp|DNSAPI|IPHLPAPI|KERNEL32|msvcrt|ntdll|ole32|PSAPI|RPCRT4|SECUR32|SHELL32|SHLWAPI|USER32|USERENV|WINMM|WS2_32|api-ms-win-[a-z0-9-]+)\.dll$' || true)
if [ -n "$bad" ]; then
echo "::error title=mingw-not-self-contained::$d 依赖了非 Windows 自带的模块: $bad"
rc=1
fi
done
exit $rc
# **这份判据取生成的头,不取 CMakeCache** —— 1.5.0 起三个新开关里有两个走
# 静默降级链(QUIC/HTTP3:缺依赖或那份 OpenSSL 不带 QUIC API 时是
# `message(WARNING)` + `set(... OFF)`,**普通变量、不是 cache 写**),于是
# `CMakeCache.txt` 里**照旧**写着 `=ON`,而编译器看到的是生成头里的 0。
# 只 grep cache 的断言会放行一个缺功能的包 —— 而"发出去的包缺功能"正是它
# 要拦的那件事,放行等于没写。生成的头是编译器真正读到的那个数,也是
# config-contract 门禁读的那个数。
#
# 名字映射(cache 键 → 生成头里的宏,别照着 cache 键去 grep)。**这张表是
# 逐条对着 `CMakeLists.txt` 的 `_uvcpp_literal01(...)` 那一段抄的**,不是
# 按名字想当然:
# UVCPP_BUILD_EXPAND⇒**UVCPP_ENABLE_MEMORY_POOL**(**不是** WSDL —— expand
# 是内存池那个模块,名字像纯属巧合。第一版就是按名字猜的,本机对着真
# 生成头实测当场红了一条,而六条腿会一起红);
# UVCPP_BUILD_WEB⇒UVCPP_WEB_ENABLE、UVCPP_BUILD_WEBAPP⇒UVCPP_WEBAPP_ENABLE、
# UVCPP_BUILD_NET⇒UVCPP_NET_ENABLE、UVCPP_ENABLE_ZLIB⇒UVCPP_ZLIB_ENABLE;
# 其余同名加 `_ENABLE` 后缀(`UVCPP_ENABLE_QUIC`⇒`UVCPP_QUIC_ENABLE`)。
#
# **`UVCPP_ENABLE_WSDL`⇒`UVCPP_WSDL_ENABLE` 现在必须在下面那一串里。** 它归
# 自己那个开关(不是 `UVCPP_BUILD_EXPAND`),默认 OFF,而发布腿自 1.5.2 起
# **显式开**它:`1.5.0` 起发布包带上 QUIC/HTTP3/C ABI,WSDL/SOAP 是当时漏下
# 的唯一一块 —— 包里的 `include/wsdl/*.h` 照装不误(那由 `package_release.py`
# 的 MODULES 决定,与开关无关),但生成头里 `UVCPP_WSDL_ENABLE` 是 0,于是那些
# 头的内容整段落在 `#if` 外面**编出来是空的**,使用方拿到的是"头在、功能不在"。
# 打开它同时要满足 `UVCPP_BUILD_WEBAPP=ON`(WSDL 的前提,否则配置期静默降级),
# 而六条腿本来就都开着。
#
# **`UVCPP_DB_ENABLE` / 四个后端宏同理。** db 模块的开关与宏同名加后缀,
# **但这一族的内层宏是按后端各一枚**(`UVCPP_DB_SQLITE_ENABLE` /
# `UVCPP_DB_MYSQL_ENABLE` / `UVCPP_DB_PGSQL_ENABLE` /
# `UVCPP_DB_ODBC_ENABLE`)。**1.7.0 起前三枚都在这一串里** —— 在那之前这里只量
# `UVCPP_DB_SQLITE_ENABLE`,因为另两个被显式 `=OFF`。为什么现在开着、以及"开着"
# 与"链接行上有它"为什么不是一回事,见 mingw-x64 配置那一步 ⑤ 那段注释。
#
# **`UVCPP_DB_ODBC_ENABLE` 是 1.7.2 新加的第四枚**,也是这一串里唯一一个
# **"开关默认 ON、仍必须量"**的:它不走"默认 OFF + 发布腿显式开"那条路,而是
# 走"找不到 `sql.h` 就 `set(... OFF)`"那条静默降级 —— 于是**这正是**上面那段
# 说的"cache 里照旧 ON、生成头里是 0"的形状。换句话说:对前三枚,这条断言拦的
# 是"发布腿忘了传开关";对这一枚,它拦的是"这条腿没弄到头"。两者的判据是同一个
# (生成头),但第二种更隐蔽 —— 开关默认 ON 这件事本身会让人以为它不用管。
# 整段理由(六条腿各自去哪儿弄头、mingw 那份为什么在 `include/unixodbc/` 下)
# 在文件头 ⑧。
#
# **`UVCPP_YYJSON_ENABLE` / `UVCPP_BROTLI_ENABLE` 是 1.7.0 新加的两枚。** 它们
# 与 WSDL 那次同形(默认 OFF、发布腿显式开),但更隐蔽一点:yyjson 在没有这两行
# 之前是**发布腿一条都不开**的,所以"包里没有它"曾经是发布配置的事实而不是缺陷。
# 这两枚进了清单,就等于把"包里必须真有 yyjson / brotli"变成一条会红的断言。
# 注意 brotli 的宏是 `UVCPP_BROTLI_ENABLE`(跟着 `UVCPP_ENABLE_BROTLI`),
# 而它还有一个派生宏 `UVCPP_HTTP_COMPRESS_ENABLE` —— 这里量的是前者:派生宏
# 在"没开 zlib"的构建里也是 1,分不出是哪个编码器在库里。
#
# 两份产物都量:调试档与发布档是**两次独立的 configure**,漏传只让其中一份
# 缺功能 —— 而那一份正是使用者拿去调试时才撞上的。
- name: Assert the requested modules are actually on
run: |
rc=0
check_tree() {
tree="$1"; shift
hdr="$tree/include/uvcpp/uvcpp_config.h"
if [ ! -f "$hdr" ]; then
echo "::error title=mingw-x64-no-config-header::找不到 $hdr —— 这一档的 configure 没落地"
rc=1; return
fi
for k in "$@"; do
if ! grep -qE "^# define ${k} 1$" "$hdr"; then
echo "::error title=mingw-x64-module-off::${k} 在 $tree 里不是 1,包会缺功能"
grep -E "^# define ${k} " "$hdr" || echo " (生成头里没有这一项)"
rc=1
fi
done
}
for t in build-mingw build-mingw-dbg; do
check_tree "$t" UVCPP_NET_ENABLE UVCPP_WEB_ENABLE UVCPP_WEBAPP_ENABLE \
UVCPP_ENABLE_MEMORY_POOL UVCPP_ZLIB_ENABLE UVCPP_OPENSSL_ENABLE \
UVCPP_NGHTTP2_ENABLE UVCPP_CAPI_ENABLE UVCPP_QUIC_ENABLE \
UVCPP_HTTP3_ENABLE UVCPP_WSDL_ENABLE \
UVCPP_DB_ENABLE UVCPP_DB_SQLITE_ENABLE \
UVCPP_DB_MYSQL_ENABLE UVCPP_DB_PGSQL_ENABLE \
UVCPP_DB_ODBC_ENABLE \
UVCPP_DB_MONGODB_ENABLE UVCPP_REDIS_ENABLE \
UVCPP_YYJSON_ENABLE UVCPP_BROTLI_ENABLE
done
exit $rc
# **出包之前必须全绿**。发布一个测试挂掉的库比不发更坏。
# 排除项与四个平台文件一致:test_shutdown_func 是 ~1% 的既有间歇性失败
# (doc/ci-guide.md §4),拿它卡发布只会卡住发布本身。
- name: Test
run: |
# 续行必须写在块标量里。`run: cmd \` 是 YAML 普通标量,普通标量把换行
# **折成空格**,`\` 于是只转义掉后面那个空格:shell 收到的是一条
# `... --timeout 60 \ --exclude-regex "..."`,ctest 把这两个都当裸参数
# 忽略 ⇒ 排除静默失效(实测过一次,80 个测试全跑了)。
ctest --test-dir build-mingw --output-on-failure --timeout 60 \
--exclude-regex "test_shutdown_func"
- name: Package
run: |
# 同上:普通标量会把续行折成空格,argparse 收不到 --tree 直接退出 2。
python tests/tools/package_release.py \
--tree build-mingw --platform mingw-x64 \
--debug-tree build-mingw-dbg --out dist
- uses: actions/upload-artifact@v4
with:
name: mingw-x64
path: dist/*.zip
if-no-files-found: error
# ---------------------------------------------------------------------------
# Windows arm64 / MinGW-w64(MSYS2 CLANGARM64)
#
# 这条腿不是"换个架构的 MINGW64",而是换了一整套 C++ 运行库:MSYS2 没有
# aarch64 的 gcc,arm64 的 MinGW 工具链只有 clang,而 CLANGARM64 是
# libc++ + libunwind 环境,不是 libstdc++。所以下面那条自检比 x64 那边更要紧。
# ---------------------------------------------------------------------------
mingw-arm64:
runs-on: windows-11-arm
timeout-minutes: 150
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: CLANGARM64
update: false
# 最后两个与 mingw-x64 同一件事:发布包要带 db 的 MySQL/PG 两个后端,而
# `UVCPP_DB_RUNTIME_LOAD=ON` 省的是链接、省不掉编译 —— 头必须在。
# 这两个包名 2026-10-14 在 `packages.msys2.org` 上核过(`clang-aarch64`
# 仓里都在,版本与 x86_64 那条仓同步发布)。
#
# `unixodbc` 是 1.7.2 起的第四个后端,2026-10-16 在同一个站上核过
# (`clangarm64` 仓里有,2.3.14-1,与 `mingw64` 那份同版本),**头的布局
# 也一样在 `include/unixodbc/` 下** —— 下面 configure 里那份 `_INCLUDE_DIR`
# 与 mingw-x64 逐字相同。理由(为什么 mingw 两条腿用 unixODBC 的头而不是
# 微软 SDK 那份)在文件头 ⑧。
#
# `mongo-c-driver` 是 1.7.5 起的第五个后端,2026-10-19 在同一个站的
# `clangarm64.db.tar.zst` 里核过(2.5.4-1,与 `mingw64` 那份同版本),头的
# 布局也是**平**的 `include/{mongoc,bson}/`。只拿头:运行期是使用者那份。
install: >-
mingw-w64-clang-aarch64-clang
mingw-w64-clang-aarch64-cmake
mingw-w64-clang-aarch64-ninja
mingw-w64-clang-aarch64-openssl
mingw-w64-clang-aarch64-python
mingw-w64-clang-aarch64-libc++
mingw-w64-clang-aarch64-libunwind
mingw-w64-clang-aarch64-libmariadbclient
mingw-w64-clang-aarch64-postgresql
mingw-w64-clang-aarch64-unixodbc
mingw-w64-clang-aarch64-mongo-c-driver
# 这条腿的机器本机不存在(没有 arm64 的 Windows 开发机),三个探点只能在这里
# 实测。这一步**故意不设断言**:要的是数据,把关交给下面真正会拦人的白名单
# 自检与 ctest;第一轮 CI 的输出就是探针结果本身。
#
# 关键的是第二段 —— 它用一个真跑 libc++ 的最小 dll,直接量
# `-static -static-libgcc -static-libstdc++` 之后导入表里还剩什么。clang 把
# 这三个都当普通 Flag 收下(Options.td 里就是裸 Flag,不会报 unknown
# argument),所以"接受"不代表"生效",只有真编一个出来看导入表才知道。
# 用的解析器就是下面那道断言用的同一个 pe_imports()。
- name: Probe toolchain
run: |
clang++ --version | head -1
echo "target: $(clang++ -dumpmachine)"
cat > probe_dll.cpp <<'CPP'
#include <string>
#include <stdexcept>
extern "C" __declspec(dllexport) int probe()
{
try { throw std::runtime_error(std::string("probe")); }
catch (const std::exception&) { return 1; }
}
CPP
clang++ -shared -static -static-libgcc -static-libstdc++ \
probe_dll.cpp -o probe_dll.dll
python - <<'PY'
import sys
sys.path.insert(0, 'tests/tools')
from package_release import pe_imports
print('probe_dll.dll imports:', pe_imports('probe_dll.dll'))
PY
rm -f probe_dll.cpp probe_dll.dll
- name: Configure & Build
run: |
set -o pipefail
# 与 mingw-x64 同一个理由链:BUILD_SHARED_LIBS=OFF 管 llhttp、
# UVCPP_BUILD_LIBUV_FROM_SOURCE=ON 管 libuv,缺一个产物就多一个 dll 依赖。
#
# UVCPP_STATIC_RUNTIME=ON 在这里比 x64 更要紧:CLANGARM64 的 libc++.dll /
# libunwind.dll 是随工具链装进来的,不静态链就会出现在导入表里,产物在
# 没装 MSYS2 的机器上直接起不来。CMakeLists.txt:30-37 的 MINGW 分支给的是
# `-static -static-libgcc -static-libstdc++`,其中真正干活的是 -static
# (另两个是 GCC 的名字,clang 收下但它们指的不是 libc++)。
cmake -S . -B build-mingw -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DUVCPP_BUILD_LIBUV_FROM_SOURCE=ON \
-DUVCPP_BUILD_TESTS=ON -DUVCPP_BUILD_SHARED=ON -DUVCPP_BUILD_STATIC=OFF \
-DUVCPP_BUILD_WEB=ON -DUVCPP_BUILD_WEBAPP=ON \
-DUVCPP_ENABLE_ZLIB=ON -DUVCPP_ENABLE_OPENSSL=ON \
-DUVCPP_ENABLE_NGHTTP2=ON \
-DUVCPP_ENABLE_CAPI=ON \
-DUVCPP_ENABLE_QUIC=ON -DUVCPP_ENABLE_HTTP3=ON \
-DUVCPP_ENABLE_WSDL=ON \
-DUVCPP_ENABLE_YYJSON=ON \
-DUVCPP_ENABLE_BROTLI=ON -DUVCPP_BROTLI_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB=ON -DUVCPP_DB_SQLITE_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB_MYSQL=ON -DUVCPP_ENABLE_DB_PGSQL=ON \
-DUVCPP_ENABLE_DB_ODBC=ON \
-DUVCPP_DB_RUNTIME_LOAD=ON \
-DUVCPP_DB_MYSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/mysql" \
-DUVCPP_DB_PGSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_DB_ODBC_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/unixodbc" \
-DUVCPP_ENABLE_DB_MONGODB=ON \
-DUVCPP_DB_MONGODB_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_BUILD_EXPAND=ON -DUVCPP_STATIC_RUNTIME=ON \
-DOPENSSL_USE_STATIC_LIBS=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_ZLIB=ON
cmake --build build-mingw --parallel 4
# 调试档:另开一棵单配置树,开关与上面逐字相同(理由链同 mingw-x64)。
# 这一条腿的 `-static` 尤其要跟着传:CLANGARM64 的 libc++.dll /
# libunwind.dll 不静态链就会出现在导入表里。
cmake -S . -B build-mingw-dbg -G Ninja -DCMAKE_BUILD_TYPE=Debug \
-DBUILD_SHARED_LIBS=OFF \
-DUVCPP_BUILD_LIBUV_FROM_SOURCE=ON \
-DUVCPP_BUILD_TESTS=OFF -DUVCPP_BUILD_SHARED=ON -DUVCPP_BUILD_STATIC=OFF \
-DUVCPP_BUILD_WEB=ON -DUVCPP_BUILD_WEBAPP=ON \
-DUVCPP_ENABLE_ZLIB=ON -DUVCPP_ENABLE_OPENSSL=ON \
-DUVCPP_ENABLE_NGHTTP2=ON \
-DUVCPP_ENABLE_CAPI=ON \
-DUVCPP_ENABLE_QUIC=ON -DUVCPP_ENABLE_HTTP3=ON \
-DUVCPP_ENABLE_WSDL=ON \
-DUVCPP_ENABLE_YYJSON=ON \
-DUVCPP_ENABLE_BROTLI=ON -DUVCPP_BROTLI_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB=ON -DUVCPP_DB_SQLITE_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB_MYSQL=ON -DUVCPP_ENABLE_DB_PGSQL=ON \
-DUVCPP_ENABLE_DB_ODBC=ON \
-DUVCPP_DB_RUNTIME_LOAD=ON \
-DUVCPP_DB_MYSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/mysql" \
-DUVCPP_DB_PGSQL_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_DB_ODBC_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include/unixodbc" \
-DUVCPP_ENABLE_DB_MONGODB=ON \
-DUVCPP_DB_MONGODB_INCLUDE_DIR="$(cygpath -m "$MINGW_PREFIX")/include" \
-DUVCPP_BUILD_EXPAND=ON -DUVCPP_STATIC_RUNTIME=ON \
-DOPENSSL_USE_STATIC_LIBS=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_ZLIB=ON
cmake --build build-mingw-dbg --parallel 4
# 与 mingw-x64 同一张白名单,但**不用 objdump**:gcc 依赖 binutils 所以
# MINGW64 上 objdump 一定在,而 clang 不依赖 GNU binutils,CLANG64/CLANGARM64
# 的 `mingw-w64-clang-aarch64-binutils` 是个虚拟包(没有可安装的包体)。
# 改用仓库自己的 pe_imports():打包器的导入表自检用的就是它,于是断言和
# 打包共用一份解析实现,少一个"两边对同一件事各说各话"的自由度。
- name: Assert the DLL is self-contained
run: |
python - <<'PY'
import re
import sys
sys.path.insert(0, "tests/tools")
from package_release import pe_imports
system = re.compile(
r"^(advapi32|bcrypt|bcryptprimitives|crypt32|dbghelp|dnsapi|iphlpapi|"
r"kernel32|msvcrt|ntdll|ole32|psapi|rpcrt4|secur32|shell32|shlwapi|"
r"user32|userenv|winmm|ws2_32|api-ms-win-[a-z0-9-]+)\.dll$")
# 两份都要过:调试档与发布档是两次独立的 configure,开关漏传只会让
# **那一份**多出 dll 依赖。见 mingw-x64 那边的同一段注释。
rc = 0
for dll in ("build-mingw/libuvcpp.dll", "build-mingw-dbg/libuvcppd.dll"):
print("--- %s" % dll)
deps = pe_imports(dll)
if deps is None:
print("%s 不是 PE,断言无从谈起,按失败处理" % dll)
rc = 1
continue
print("\n".join(deps))
bad = [d for d in deps if not system.match(d)]
if bad:
print("::error title=mingw-arm64-not-self-contained::"
"%s 依赖了非 Windows 自带的模块: %s" % (dll, bad))
rc = 1
sys.exit(rc)
PY
# 与 mingw-x64 那一同名步骤逐字同构(判据为什么取生成的头、名字怎么映射、
# 为什么两份产物都要量 —— 全在那边的注释里),只换了标题里的腿名。
- name: Assert the requested modules are actually on
run: |
rc=0
check_tree() {
tree="$1"; shift
hdr="$tree/include/uvcpp/uvcpp_config.h"
if [ ! -f "$hdr" ]; then
echo "::error title=mingw-arm64-no-config-header::找不到 $hdr —— 这一档的 configure 没落地"
rc=1; return
fi
for k in "$@"; do
if ! grep -qE "^# define ${k} 1$" "$hdr"; then
echo "::error title=mingw-arm64-module-off::${k} 在 $tree 里不是 1,包会缺功能"
grep -E "^# define ${k} " "$hdr" || echo " (生成头里没有这一项)"
rc=1
fi
done
}
for t in build-mingw build-mingw-dbg; do
check_tree "$t" UVCPP_NET_ENABLE UVCPP_WEB_ENABLE UVCPP_WEBAPP_ENABLE \
UVCPP_ENABLE_MEMORY_POOL UVCPP_ZLIB_ENABLE UVCPP_OPENSSL_ENABLE \
UVCPP_NGHTTP2_ENABLE UVCPP_CAPI_ENABLE UVCPP_QUIC_ENABLE \
UVCPP_HTTP3_ENABLE UVCPP_WSDL_ENABLE \
UVCPP_DB_ENABLE UVCPP_DB_SQLITE_ENABLE \
UVCPP_DB_MYSQL_ENABLE UVCPP_DB_PGSQL_ENABLE \
UVCPP_DB_ODBC_ENABLE \
UVCPP_DB_MONGODB_ENABLE UVCPP_REDIS_ENABLE \
UVCPP_YYJSON_ENABLE UVCPP_BROTLI_ENABLE
done
exit $rc
- name: Test
run: |
# 续行必须写在块标量里。`run: cmd \` 是 YAML 普通标量,普通标量把换行
# **折成空格**,`\` 于是只转义掉后面那个空格:shell 收到的是一条
# `... --timeout 60 \ --exclude-regex "..."`,ctest 把这两个都当裸参数
# 忽略 ⇒ 排除静默失效(实测过一次,80 个测试全跑了)。
ctest --test-dir build-mingw --output-on-failure --timeout 60 \
--exclude-regex "test_shutdown_func"
- name: Package
run: |
python tests/tools/package_release.py \
--tree build-mingw --platform mingw-arm64 \
--debug-tree build-mingw-dbg --out dist
- uses: actions/upload-artifact@v4
with:
name: mingw-arm64
path: dist/*.zip
if-no-files-found: error
# ---------------------------------------------------------------------------
# Windows x64 / MSVC (VS2022) —— 给 MSVC 使用者用;/MD + 包内自带运行库
# ---------------------------------------------------------------------------
msvc-x64:
# 钉 windows-2022 而不是 windows-latest:后者现在指向 "windows-2025-vs2026"
# 镜像,那张镜像里**只有 VS 2026**,下面写死的 `-G "Visual Studio 17 2022"`
# 会直接报 "could not find any instance of Visual Studio"(首跑就是这么挂的)。
# 也不改成"去掉 -G 让 CMake 自己挑":那会把发布产物的 ABI 绑在 GitHub 换镜像
# 的节奏上(今天 VS2026,下次不知道)。windows-2022 上只有 VS2022,
# 生成器是写死还是自动挑结果相同,但写出来在日志里可核对。
runs-on: windows-2022
timeout-minutes: 150
steps:
- uses: actions/checkout@v4
# 不靠镜像里"碰巧装了 OpenSSL":没找到时 `find_package(OpenSSL QUIET)` 会把
# UVCPP_ENABLE_OPENSSL 静默置 OFF(CMakeLists.txt:580-580),那样发出去的包
# 一句 SSL 都没有 —— 而它的生成头会**如实**说 0,使用者照配也拿不到 SSL。
#
# 这条要求由 `.github/scripts/win-openssl-deps.sh` 兑现,而且比原来那一行更硬:
# 原来 `choco install` 退出 0 就算数,现在**必须真找到一份 openssl.exe**,
# 找不到就 exit 1(并把找过哪几处、choco 的原话折进 `::error`)。镜像自带的那份
# 也认,但它要跟 choco 那份一样先被"找到"才放行 —— 所以"静静发一个没有 SSL 的
# 包"这条路仍然堵着。
#
# 为什么不再直接用裸 choco:2026-09-29 20:46 UTC 起它在两张 windows 镜像上都
# 恒非 0 退出,理由与实测写在那个脚本的头部。
- name: Install OpenSSL
shell: bash
run: bash .github/scripts/win-openssl-deps.sh
# MySQL/PG/MongoDB 的**头**。这条腿的机器上一份都没有:镜像里 MySQL 那一项是
# `Install-MysqlCli.ps1` 装的命令行工具(没有头),PostgreSQL 14 虽然装着
# 但只有 x64 这一条腿有、另一条(windows-11-arm)连 PostgreSQL 都没有 ——
# 所以两条 MSVC 腿统一用 `win-client-headers.sh` 取同一份钉死版本的头,
# 而不是一条用镜像自带的、一条用别处来的。理由与"为什么不用 MSYS2 那份"
# 全在脚本头部。MongoDB 那一份(1.7.5 起)是同一个脚本、同一个来路。
- name: Install MySQL / PostgreSQL / MongoDB client headers
shell: bash
run: bash .github/scripts/win-client-headers.sh
- name: Configure & Build
shell: bash
run: |
set -o pipefail
# 同 mingw-x64:① 让 libuv 从源码静态编(runner 上若有系统 libuv,
# 不带这个开关就会链成 uv.dll),② 让 llhttp 静态(否则 llhttp.dll)。
# zlib 不受 ② 影响(CMakeLists.txt:2556-2557 明确链 zlibstatic)。
# MSVC 侧 uvcpp 自己仍走 /MD + 包内自带 VC++ 运行库,与此无关。
#
# ODBC 的**头**:db 的第四个后端(1.7.2 起进发布包),头在 Windows SDK
# 里,但要自己找出来 —— `find_path(sql.h)` 不会去翻 SDK 的 `um` 目录,
# 找不到就直接静默降级(生成头里那颗宏是 0,cache 里照旧 ON)。整段理由
# (为什么用 SDK 那份而不是 unixODBC、为什么**必须按版本号取最大**、别改成
# 字典序)在 `.github/scripts/win-odbc-headers.sh` 头部。
# 取不到时它 exit 1 ⇒ 这一步当场停(`shell: bash` 是 `-eo pipefail`),
# 不会带着一份少后端的配置继续编下去。
odbc_inc=$(bash .github/scripts/win-odbc-headers.sh)
cmake -S . -B build-msvc -G "Visual Studio 17 2022" -A x64 \
-DBUILD_SHARED_LIBS=OFF \
-DUVCPP_BUILD_LIBUV_FROM_SOURCE=ON \
-DUVCPP_BUILD_TESTS=ON -DUVCPP_BUILD_SHARED=ON -DUVCPP_BUILD_STATIC=OFF \
-DUVCPP_BUILD_WEB=ON -DUVCPP_BUILD_WEBAPP=ON \
-DUVCPP_ENABLE_ZLIB=ON -DUVCPP_ENABLE_OPENSSL=ON \
-DUVCPP_ENABLE_NGHTTP2=ON \
-DUVCPP_ENABLE_CAPI=ON \
-DUVCPP_ENABLE_QUIC=ON -DUVCPP_ENABLE_HTTP3=ON \
-DUVCPP_ENABLE_WSDL=ON \
-DUVCPP_ENABLE_YYJSON=ON \
-DUVCPP_ENABLE_BROTLI=ON -DUVCPP_BROTLI_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB=ON -DUVCPP_DB_SQLITE_FROM_SOURCE=ON \
-DUVCPP_ENABLE_DB_MYSQL=ON -DUVCPP_ENABLE_DB_PGSQL=ON \
-DUVCPP_ENABLE_DB_ODBC=ON \
-DUVCPP_DB_RUNTIME_LOAD=ON \
-DUVCPP_DB_MYSQL_INCLUDE_DIR=C:/client-headers/mysql \
-DUVCPP_DB_PGSQL_INCLUDE_DIR=C:/client-headers/pgsql \
-DUVCPP_DB_ODBC_INCLUDE_DIR="$odbc_inc" \
-DUVCPP_ENABLE_DB_MONGODB=ON \
-DUVCPP_DB_MONGODB_INCLUDE_DIR=C:/client-headers/mongo \
-DUVCPP_BUILD_EXPAND=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_ZLIB=ON
cmake --build build-msvc --config Release --parallel 4
# 调试档:同一棵树换配置,产物落在 build-msvc/Debug/(含 uvcppd.pdb)。
# 只建 `uvcpp` 这一个目标,不建 Debug 的测试 —— 这条腿要的是**产物**,
# 而 Debug 全套测试会把这一步的墙钟翻倍(Release 那份测试在上面已经跑过)。
# 必须**显式**建 Debug,不能指望 `--config Release` 顺带产出:DEBUG_POSTFIX
# 只在 Debug 配置下加那个 `d`。
cmake --build build-msvc --config Debug --target uvcpp --parallel 4
# 上面那条注释说的静默降级,在这里被钉死。
- name: Assert the requested modules are actually on
shell: bash
run: |
# 判据从 CMakeCache 改成**生成的头**(1.5.0 起):旧写法只 grep cache,
# 而这一版新开的 QUIC/HTTP3 走的正是"cache 里照旧 ON、真正生效的是 0"
# 那条静默降级链 —— 旧断言会放行一个缺功能的包。完整理由、名字映射与
# "两份产物都要量"都在 mingw-x64 那个同名步骤的注释里。
#
# MSVC 这里只有**一棵**多配置树(Release/Debug 共用一次 configure),
# 所以 `for t` 只有一格 —— 与 mingw/linux 那边两份树的形状不同,
# 那是生成器差异(单配置 vs 多配置),不是漏了一只。
rc=0
check_tree() {
tree="$1"; shift
hdr="$tree/include/uvcpp/uvcpp_config.h"
if [ ! -f "$hdr" ]; then
echo "::error title=msvc-x64-no-config-header::找不到 $hdr —— configure 没落地"
rc=1; return
fi
for k in "$@"; do
if ! grep -qE "^# define ${k} 1$" "$hdr"; then
echo "::error title=msvc-x64-module-off::${k} 在 $tree 里不是 1,包会缺功能"
grep -E "^# define ${k} " "$hdr" || echo " (生成头里没有这一项)"
rc=1
fi
done
}
check_tree build-msvc UVCPP_NET_ENABLE UVCPP_WEB_ENABLE \
UVCPP_WEBAPP_ENABLE UVCPP_ENABLE_MEMORY_POOL UVCPP_ZLIB_ENABLE \
UVCPP_OPENSSL_ENABLE UVCPP_NGHTTP2_ENABLE UVCPP_CAPI_ENABLE \
UVCPP_QUIC_ENABLE UVCPP_HTTP3_ENABLE UVCPP_WSDL_ENABLE \
UVCPP_DB_ENABLE UVCPP_DB_SQLITE_ENABLE \
UVCPP_DB_MYSQL_ENABLE UVCPP_DB_PGSQL_ENABLE \
UVCPP_DB_ODBC_ENABLE \
UVCPP_DB_MONGODB_ENABLE UVCPP_REDIS_ENABLE \
UVCPP_YYJSON_ENABLE UVCPP_BROTLI_ENABLE
exit $rc
- name: Test
shell: bash
run: |
# 内存池在 Windows 上出过问题(MinGW 侧,见 RELEASE.md),
# 所以这里的 test_memory_pool 不是可选项。
ctest --test-dir build-msvc -C Release --output-on-failure \
--timeout 60 --exclude-regex "test_shutdown_func"
- name: Package
shell: bash
run: |
# MSVC 的运行库在 VCToolsRedistDir 下,packager 靠这个环境变量定位;
# 找不到就报错退出,而不是发一个缺 vcruntime 的包。
export VCToolsRedistDir
python tests/tools/package_release.py \
--tree build-msvc --platform msvc-x64 --config Release \
--debug-tree build-msvc --out dist
# 安装程序的内容就是上面那一步产出的 stage 目录 —— 不再重新组装文件清单。
# `packaging/windows/libuvcpp.iss` 里写了为什么(那是已经过全部自检的那棵
# 树,再抄一份清单就是第二个真相来源)。
#
# 签名证书是**可选**的:没有 `WINDOWS_CERT_PFX_BASE64` 时只打一行 warning、
# 安装包照出。缺证书是配置问题,不该让整条发布腿红 —— 但也不能悄悄跳过,
# 所以 warning 里点名了该看 RELEASE.md 的哪一节。
- name: Build installer
shell: pwsh
env:
WINDOWS_CERT_PFX_BASE64: ${{ secrets.WINDOWS_CERT_PFX_BASE64 }}
WINDOWS_CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
run: |
$ErrorActionPreference = 'Stop'
$arch = 'x64'
$zip = Get-ChildItem "dist/libuvcpp-*-msvc-$arch.zip" | Select-Object -First 1
if (-not $zip) { throw "dist 下没有 libuvcpp-*-msvc-$arch.zip —— Package 那一步没产出" }
$base = $zip.BaseName
# 用正则而不是 `-split`:版本号里一旦出现连字符(预发布那种),split
# 取到的是半截版本号,而它只影响**文件名**,ISCC 那边照样能编过。
if ($base -notmatch '-(\d+\.\d+\.\d+)-msvc-') { throw "从 $base 里取不出版本号" }
$ver = $Matches[1]
$stage = Join-Path (Get-Location) "dist/$base"
if (-not (Test-Path $stage)) { throw "没有 stage 目录 $stage" }
Write-Host "version=$ver stage=$stage"
choco install innosetup --no-progress -y | Out-Host
$iscc = Get-ChildItem 'C:\Program Files (x86)\Inno Setup*\ISCC.exe' |
Sort-Object FullName -Descending | Select-Object -First 1
if (-not $iscc) { throw "找不到 ISCC.exe(choco innosetup 装了但没落到预期路径)" }
Write-Host "ISCC: $($iscc.FullName) $($iscc.VersionInfo.FileVersion)"
# ArchAllowed 必须显式给:.iss 的默认值是 `x64compatible`,arm64 那一步
# 不给它就会编出一个自称 x64compatible 的 arm64 安装包。
$isccArgs = @("/DMyVersion=$ver", "/DMyArch=$arch", "/DArchAllowed=x64compatible",
"/DStageDir=$stage", "/DOutputDir=$((Get-Location).Path)\dist")
$pfx = $null
$wrap = $null
if ($env:WINDOWS_CERT_PFX_BASE64) {
$pfx = Join-Path $env:RUNNER_TEMP 'codesign.pfx'
[IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String($env:WINDOWS_CERT_PFX_BASE64))
$signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" |
Sort-Object FullName -Descending | Select-Object -First 1
if (-not $signtool) { throw "找不到 signtool.exe(Windows SDK 装了吗?)" }
# ISCC 的 /S 值里一旦同时有空格和引号(signtool 在 `Program Files
# (x86)` 下、命令行本身也得带引号),传过去会被拆成多段,ISCC 报
# "You may not specify more than one script filename" —— 2026-10-18
# 用自签证书第一次真跑签名时踩到。落成一个路径无空格的 .cmd 包装器,
# 值里只留路径 + `$f`(后者是 ISCC 强制的占位符,缺了报
# "$f sequence is missing"),文件名由 ISCC 追加、包装器里收 %1。
$wrap = Join-Path $env:RUNNER_TEMP 'sign.cmd'
Set-Content -Path $wrap -Encoding ascii -Value @(
'@echo off',
('"{0}" sign /fd sha256 /td sha256 /tr http://timestamp.digicert.com /f "{1}" /p "{2}" %1' -f $signtool.FullName, $pfx, $env:WINDOWS_CERT_PASSWORD)
)
$isccArgs += '/DSignSetup=1'
$isccArgs += "/Ssigntool=$wrap `$f"
Write-Host "签名工具: $($signtool.FullName)"
} else {
Write-Host "::warning title=no-signing-cert::没有 WINDOWS_CERT_PFX_BASE64,安装包不签名(见 packaging/README.md 的「首次发布前的一次性准备」)"
}
# 脚本文件必须当**位置参数**给它 —— `$isccArgs` 里全是 `/D`/`/S` 选项。
# 2026-10-18 的 v1.7.4 发布第一次跑这一步就漏了它:ISCC 收到零个位置
# 参数,打印用法后以 1 退出,看着像编译失败、其实是没给脚本。
& $iscc.FullName @isccArgs packaging/windows/libuvcpp.iss
if ($LASTEXITCODE -ne 0) { throw "ISCC 失败,退出码 $LASTEXITCODE" }
# 包装器里是明文口令,和 PFX 一起删掉
if ($pfx) { Remove-Item $pfx -Force }
if ($wrap) { Remove-Item $wrap -Force }
$exe = Join-Path (Get-Location) "dist/libuvcpp-$ver-msvc-$arch-setup.exe"
if (-not (Test-Path $exe)) { throw "ISCC 报成功但没有 $exe" }
Write-Host "built: $exe ($((Get-Item $exe).Length) 字节)"
if ($pfx -or $env:WINDOWS_CERT_PFX_BASE64) {
$sig = Get-AuthenticodeSignature $exe
Write-Host "signature: $($sig.Status) / $($sig.SignerCertificate.Subject)"
if ($sig.Status -ne 'Valid') {
# 自签名证书天生的"不受信任":链停在一张没人信过的根上,Status 是
# `UnknownError`(`HashMismatch` 才是包被改过,那个照旧判死)。
# 自签只警告 —— 它存在的意义是先把签名链路跑通。
$c = $sig.SignerCertificate
$selfSigned = $c -and ($c.Subject -eq $c.Issuer)
if ($selfSigned -and $sig.Status -in @('UnknownError', 'NotTrusted')) {
Write-Host "::warning title=self-signed-installer::安装包用自签名证书($($c.Subject))签的:签名与哈希完整,但证书不受信任,用户仍会看到未知发布者"
} else {
throw "安装包签名无效:$($sig.Status)"
}
}
}
- uses: actions/upload-artifact@v4
with:
name: msvc-x64
path: |
dist/*.zip
dist/*.exe
if-no-files-found: error
# ---------------------------------------------------------------------------
# Windows arm64 / MSVC (VS2022 v143) —— 与 msvc-x64 同源,只差架构。
# 真正的卡点是 OpenSSL:choco 的 openssl 只有 x64/x86,arm64 那份没有现成的,
# 只能自己编。其余开关与 msvc-x64 逐字一致。
#
# **工具集是 v143,但它现在只能靠 `-T` 钉住、不能靠镜像,这里把为什么写清楚。**
# `windows-11-arm` 这个标签在 2026-09-21~30 之间从 VS2022 镜像切成了 VS2026 镜像
# (GitHub 的公告;本仓第一次撞上它是 2026-10-04 那次 release 测试,红在下面那条
# ABI 断言上)。VS2026 的**默认**工具集不再是 v143,所以"让 CMake 自己挑生成器"
# 这条腿会静默把 ABI 升上去 —— 那边 msvc-x64 钉在 `windows-2022` 上照旧是 v143,
# 两个包就对不上了,而 `RELEASE.md` 的平台表把两个 Windows 包都写成 VS2022。
#
# 好在**镜像里装着 v143 的 ARM64 工具集**:
# `Microsoft.VisualStudio.Component.VC.14.44.17.14.ARM64`(见 actions/runner-images
# 的 `Windows11-VS2026-Arm64-Readme.md`;14.44 就是 VS2022 17.14 的那版 MSVC)。
# 所以这条腿改成三处一起钉 v143:OpenSSL 那步的 `vcvarsall -vcvars_ver`、
# 下面 configure 的 `-T v143`、以及那条断言(断言**工具集**而不是生成器名)。
# 注意**别**照着 msvc-x64 的样子写死 `-G "Visual Studio 17 2022"`:那张镜像上
# 没有 VS2022,生成器名只会是 18 2026 —— 要钉的是工具集,不是生成器。
#
# 若哪天镜像连这个兼容组件也撤了,失败是响亮的:configure 期 `MSB8020`
# ("cannot be found for Platform Toolset = 'v143'"),而不是发一个 ABI 不同的包。
# ---------------------------------------------------------------------------
msvc-arm64:
runs-on: windows-11-arm
timeout-minutes: 180
steps:
- uses: actions/checkout@v4
- name: Fetch OpenSSL source
uses: actions/checkout@v4
with:
repository: openssl/openssl
ref: openssl-3.5.8
path: openssl-src