Skip to content

Commit 4daff2d

Browse files
committed
Merge origin/main into feat/protobuf-protoc-tool
main 上合入了 #159(compat.websocket)与 #160(修 xpkg.lua 里那个把 0.0.47/0.0.48 一起吞掉的畸形 0.0.49 条目),分支落后并冲突。 **冲突让 CI 一个 run 都不建** —— GitHub 在 PR 有冲突时算不出 merge ref,于是 `pull_request` 触发的 workflow 完全不启动。表现是 "no checks reported",极易被 误读成 CI 挂了或 push 没生效。 冲突只在两个 README 的同一张表:main 新增了 websocket 那一行,而我改的是同表的 protobuf 那一行。两边都保留。 合并后核验:74 个描述符全部解析通过;members 同时含 protobuf-protoc 与 websocket / websocket-features。
2 parents e839bac + e760383 commit 4daff2d

10 files changed

Lines changed: 1259 additions & 0 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# 新增 compat.websocket 12.0.1(2026-08-06)
2+
3+
产出:`compat.websocket@12.0.1`(IXWebSocket,上游 MIT),32 个 client TU 的 header-compat
4+
Form B 包,workspace 成员 `tests/examples/websocket`,以及一份自带 echo server 的离线测试。
5+
6+
## 1. 形态判定:C++ 源码 compat,纯客户端,零依赖
7+
8+
IXWebSocket 上游**没有 mcpp 支持**(CMake 构建、C++11、`#include <ixwebsocket/IXWebSocket.h>`
9+
的头文件式消费),属第三方上游库 → Form B。上游源码是扁平的 `ixwebsocket/` 目录,头文件与源码同处一室;
10+
`include_dirs = { "*" }` 把 verdir 根暴露出去,消费端 include 路径与上游安装布局一致
11+
(`<includedir>/ixwebsocket/…`)。
12+
13+
**编译开关三项全关,是零外部依赖的关键**:
14+
15+
- **TLS 关**(不定义 `IXWEBSOCKET_USE_TLS`):TLS socket 三组 TU(OpenSSL/MbedTLS/AppleSSL)根本不编入;
16+
`IXSocketFactory.cpp` 只在 `#ifdef IXWEBSOCKET_USE_TLS` 下引用它们。计划稿"关掉 TLS 就不拉 OpenSSL"由此兑现。
17+
- **zlib 关**(不定义 `IXWEBSOCKET_USE_ZLIB`):`IXGzipCodec.cpp` 与 per-message-deflate codec 的所有
18+
zlib 调用都包在这个宏后,不定义则编译为透传 no-op —— 客户端在握手里不再提议压缩扩展,是保守且合法的默认。
19+
- **server 不提供**(见下节)。
20+
21+
**源列表 = 上游 CMake `IXWEBSOCKET_SOURCES` 的 36 项去掉 4 个 server TU**,余 32 项逐条列出
22+
(与 `compat.zlib`/`compat.protobuf` 的显式列法一致;`compat.abseil` 用通配是因为上游文件命名可裁剪,
23+
这里没有可裁剪的通配,显式列反而可审)。
24+
25+
## 2. server TU 排除有链接层论证,不是拍脑袋
26+
27+
剔除的四个 TU:`IXWebSocketServer.cpp``IXWebSocketProxyServer.cpp``IXHttpServer.cpp`
28+
`IXSocketServer.cpp`。排除前先在整个 client 侧做了符号引用检查 —— 除这 4 个文件自身外,**没有任何
29+
保留的 `.cpp` 引用这些类名**(`grep -l 'IXWebSocketServer\|IXHttpServer\|IXSocketServer\|IXWebSocketProxyServer'
30+
` 只命中那 4 个文件自己)。这是"client 不反依赖 server"的直接证据,链接期不会 undefined reference。
31+
32+
`IXGetFreePort.cpp` **保留**:它是独立工具(找空闲端口),不属于 server 那一侧,且 32 个保留 TU 的
33+
basename 全局唯一,不会触发 mcpp#233/#240 的 flat-obj 撞名。
34+
35+
## 3. 关键决定(含两处与计划稿的偏离)
36+
37+
|| 计划稿 | 本包实际 | 理由 |
38+
|---|---|---|---|
39+
| 版本 | 未指定 | `12.0.1` | 上游最新稳定 tag(`git ls-remote --tags``sort -V \| tail`) |
40+
| 目录 | `pkgs/w/` | **`pkgs/c/`** | 仓库约定是**完整包名首字母**(`compat.*``c/`,见 skill 与 docs);`pkgs/e/``compat.eui-neo` 是唯一用短名首字母的例外。放错目录会让本地 path index 报 not found |
41+
| `language` | `c++17` | **`c++23`** | 全仓 59 个描述符统一 `c++23`(本仓 floor);IXWebSocket 是 C++11 源码,在 CI 工具链 clang 22.1.8 的 `c++23`**32/32 TU 零错误**通过,无 char8_t/弃用 API 问题,没必要为它开 c++17 特例 |
42+
| CN 镜像 | GLOBAL+CN 双 mirror | **plain-string url** | 本机无 `gtc`/gitcode 配置、无 `mcpp-res` 写权限,按 [docs/cn-mirror.md](../../docs/cn-mirror.md) 回退为纯字符串上游 url(与 `eui-neo` 0.5.5 同做法);sha 不受影响,维护者有权限后补 `{ GLOBAL, CN }` 即可 |
43+
44+
## 4. 平台开关
45+
46+
- linux:`-lpthread`(上游 `Threads::Threads`);无 `-ldl`(全库无 `dlopen`/`dlsym`)。
47+
- macosx:`-lpthread`;TLS 关则无需 Foundation/Security。
48+
- windows:`-lws2_32 -lwsock32` + `-D_CRT_SECURE_NO_WARNINGS``shlwapi` **不链**:它只被剔除的
49+
`IXSocketOpenSSL.cpp` 用到(其 `PathFileExists` 等);保留的 TU 里没有任何 shlwapi 引用。`NOMINMAX`
50+
也不需要:唯一 include `windows.h` 的保留 TU 是 `IXSetThreadName.cpp`,其源码不用 min/max。
51+
52+
## 5. 测试设计:自带最小 RFC 6455 echo server,全程离线
53+
54+
包只编 client,测试要实测"握手/掩码/分片/关闭"就不能依赖外部服务。`tests/examples/websocket/tests/ws_test.cpp`
55+
**进程内**用原始 loopback socket 起了一个最小 echo server,并刻意做成**与被测库相互独立**:
56+
57+
- `Sec-WebSocket-Accept` 用测试自带的 SHA-1 + base64 计算,`HTTP/1.1 101` 响应手写;
58+
- 帧协议手写(掩码位/长度扩展/控制帧),server 端能验证**客户端的掩码位确实置位**;
59+
- 收到特殊载荷 `FRAG` 时回一条**分片消息**(text FIN=0 + 两个 continuation),验证客户端重组;
60+
- 收到 `PINGME` 时回一条 ping,验证客户端默认 `enablePong` 的自动 pong;
61+
- close 回显 code 后关闭连接。
62+
63+
断言 11 项,全部带超时:`initNetSystem`、握手打开、text echo、**客户端掩码位**、binary echo 逐字节、
64+
客户端 ping→pong、服务端 ping→客户端自动 pong、分片重组、分片帧先于重组消息、close 握手完成、
65+
server 收到 close。CI 三平台离线可跑,不依赖 runner 网络。
66+
67+
**两个实测踩坑**(都已修进最终测试):
68+
69+
1. **IXWebSocket 的 `_automaticReconnection` 默认是 `true`**(构造函数 `_automaticReconnection(true)`),
70+
不是关闭。close 握手完成后状态转 `Closed`,若测试在收到 Close 消息与 `ws.stop()` 之间留空窗,
71+
run 循环会**再次 `checkConnection` 并重连** —— 而本测试的 server 线程在 close 后已退出,重连的
72+
握手读响应会永久阻塞,表现为进程挂死。修法:close 测试直接调 `ws.stop()`(它内部先 `close()` 再置
73+
`_stop`),run 循环因 `_stop` 而退出,Close 消息在 `stop()` 返回前已投递到 collector。
74+
2. **"客户端掩码位"断言过早**:Open 消息之后客户端还没发过任何数据帧,server 看不到掩码位。把该断言
75+
挪到 text echo 之后(echo 成功 = server 必然已收到客户端数据帧)。
76+
77+
## 6. 验证结论
78+
79+
两轮验证都通过:
80+
81+
**与 CI 完全一致的一轮(mcpp 2026.8.3.3 + gcc@16.1.0 + `MCPP_INDEX_MIRROR=GLOBAL` +
82+
`MCPP_BUILD_CACHE=local`,冷删 `target/`/`.mcpp/`):**
83+
84+
```
85+
mcpp xpkg parse pkgs/c/compat.websocket.lua → parse OK(compat.websocket / 三平台 12.0.1 /
86+
c++23 / import_std=false / 32 sources / target websocket)
87+
mcpp test -p websocket → test result ok. 1 passed; 0 failed
88+
```
89+
90+
该轮实际下载了 CI 的 gcc@16.1.0 工具链并以之编译 compat.websocket 的 32 个 TU,再编译并运行
91+
`ws_test` —— **11 项断言全部通过**,`ALL WEBSOCKET ASSERTIONS PASSED`,exit 0。
92+
93+
**独立的一轮(clang 22.1.8,standalone):** 32/32 TU 在 `c++23` 下逐 TU 编译零错误;同一测试二进制的
94+
11 项断言全过。
95+
96+
lint:`lua` 语法、`check_mirror_urls`(plain-string url 不触发镜像约束)、`check_package_name` 全过;
97+
`mcpp xpkg parse` 在本地较新 mcpp(2026.8.4.1)与 CI pin(2026.8.3.3)上均 OK。
98+
99+
## 7. 后续:server + zlib features(同一天,独立 PR 叠加在 #158 上)
100+
101+
基座合流后,又补了两个可选 feature,`pkgs/c/compat.websocket.lua` 增加 `features` 表:
102+
103+
- **`server`**:把基座剔掉的 4 个 TU 编回来。逐文件核对过,它们只 include 标准库与库内已编的 IX 头,
104+
零新增外部依赖。消费端 `features = ["server"]` 即获得 `ix::WebSocketServer`(`SocketServer` 派生,
105+
自带 `getPort()` 等)。
106+
- **`zlib`**:`defines = { "IXWEBSOCKET_USE_ZLIB=1" }` + `deps = { ["compat.zlib"] = "1.3.2" }`,
107+
把 gzip codec 从 no-op 变成真正的 permessage-deflate 压缩。define 只到包自身 TU,消费端无需
108+
拿到 —— 客户端 `enablePerMessageDeflate()`、server 默认开。
109+
110+
**`server` implies `zlib` —— 一个靠读源码才发现的正确性坑**:`IXWebSocketServer` 默认开启
111+
permessage-deflate(`_enablePerMessageDeflate = true`),而 transport 的扩展协商**不受**
112+
`IXWEBSOCKET_USE_ZLIB` 门控(只有 codec 被门控)。若 server 编了而 zlib 没编,server 会宣称压缩、
113+
却执行不了 —— 因此用具名 feature 的 `implies = { "zlib" }` 强制一起开(参考 `compat.eigen`
114+
`use_blas`;`default` feature 的 implies 恒生效是另一个已知坑,具名 feature 不受影响)。
115+
116+
### 验证(与 CI 一致:mcpp 2026.8.3.3 + gcc@16.1.0 + `MCPP_BUILD_CACHE=local`)
117+
118+
```
119+
mcpp test -p websocket → test result ok. 1 passed; 0 failed (默认零依赖基座,回归)
120+
mcpp test -p websocket-features → test result ok. 1 passed; 0 failed (server + zlib)
121+
```
122+
123+
- `websocket-features` 成员:起真实 `ix::WebSocketServer` 于 loopback,text/binary 往返;**压缩在
124+
线路上可观测**:64 KiB 重复 `'a'` 往返,`message.str.size()` = 65536 而 `wireSize` = **80** ——
125+
该字段在 transport 里取原始帧负载大小(压缩后),`str` 是解压后内容,比值即压缩证据。
126+
- **负向验证**:默认成员(无 feature)的构建产物里,4 个 server TU 的 `.o` 不存在,`nm` 全量
127+
`WebSocketServer`/`SocketServer`/`HttpServer` 符号 —— 消费端不开 `server` 却引用它会链接报错,
128+
feature 门控真实生效。
129+
130+
## 8. 后续待办
131+
132+
- **CN 镜像**:有 `mcpp-res` 写权限后,`gtc``mcpp-res/websocket`、传与 GLOBAL 字节一致的
133+
`IXWebSocket-12.0.1.tar.gz`,把三平台 url 改写为 `{ GLOBAL, CN }`(sha 不变)。
134+
- **TLS feature**(最后再考虑):索引里有 `compat.openssl`/`compat.mbedtls`,若要支持 wss,可加 feature
135+
把对应 TLS TU 与依赖编入(linux+openssl / windows+mbedtls / mac+SecureTransport)。它是唯一会显著
136+
拖慢 CI 的选项(openssl 走 install() 重型构建),故排在最后。
137+
- **C++23 薄封装头**(可选):用户计划稿提到的 `websocket.hpp`(span/format/RAII 薄层)不阻塞本包,可后续单独加。

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Two kinds of packages live here:
4040
| Native module library (Form A) | [`mcpplibs.xpkg`](pkgs/x/xpkg.lua) · [`mcpplibs.tinyhttps`](pkgs/t/tinyhttps.lua) · [`tensorvia-cpu`](pkgs/t/tensorvia-cpu.lua) · [`ffmpeg`](pkgs/f/ffmpeg.lua) (module layer; sources compiled directly through `compat.ffmpeg`) · [`opencv`](pkgs/o/opencv.lua) (single repository: the module layer and the full OpenCV 5 source build both live in the package, and only this descriptor stays on the index side) · [`mcpplibs.grpc`](pkgs/g/grpc.lua) (gRPC 1.83.0 — the one library here that CANNOT be a compat descriptor: upstream publishes no self-contained source artifact, its tag archive carrying abseil/protobuf/re2/boringssl/zlib as empty submodule placeholders, so [grpc-m](https://github.com/mcpplibs/grpc-m)'s release tarball IS that artifact. It vendors only gRPC's own source and takes the five dependencies from this index, so a consumer that also uses protobuf links one copy rather than two) |
4141
| C-source compat (with `features`) | [`compat.cjson`](pkgs/c/compat.cjson.lua) · [`compat.zlib`](pkgs/c/compat.zlib.lua) |
4242
| C++-source compat, one depending on the other | [`compat.abseil`](pkgs/c/compat.abseil.lua) (151 TUs; a wildcard over `absl/**` trimmed by upstream's test/benchmark naming conventions) · [`compat.protobuf`](pkgs/c/compat.protobuf.lua) (the libprotobuf runtime, 79 TUs transcribed from upstream's own `src/file_lists.cmake`; declares `compat.abseil` as a dependency because protobuf's public headers include `absl/…`, and its `gzip` feature defines `HAVE_ZLIB` and pulls `compat.zlib`, while `upb` adds protobuf's 64-TU C runtime out of the same tarball. It also exposes **`protoc`** as a `kind = "bin"` target, so a consumer writing `tools = ["protoc"]` gets the compiler built for its own machine out of the same package it links — making a generator/runtime version mismatch inexpressible) · [`compat.re2`](pkgs/c/compat.re2.lua) (22 TUs, upstream's own `RE2_SOURCES`) |
43+
| C++-source compat, zero-dep client + optional components | [`compat.websocket`](pkgs/c/compat.websocket.lua) (IXWebSocket 12.0.1 — a pure RFC 6455 client compiled from upstream's `IXWEBSOCKET_SOURCES` minus the four server TUs, so the **base build has zero external dependencies**: TLS off (the OpenSSL/MbedTLS/AppleSSL TUs aren't built) and `IXWEBSOCKET_USE_ZLIB` unset, so the gzip codec compiles to a no-op. Two optional features add on top: `server` (the four server TUs — `IXWebSocketServer`, `IXSocketServer`, `IXHttpServer`, `IXWebSocketProxyServer` — needing nothing external, and it **implies `zlib`** because upstream's server advertises permessage-deflate by default, which the transport negotiates regardless of the define) and `zlib` (deps `compat.zlib` and turns the codec into real per-message-deflate compression). The default-feature test brings its own minimal RFC 6455 echo server on loopback sockets (handshake, masking, fragmentation and close all exercised offline); a second member, `websocket-features`, runs a real `ix::WebSocketServer` and asserts the compression is observable on the wire — a 64 KiB repeated payload round-trips with `wireSize` = 80) |
4344
| header-only (with `features`) | [`compat.eigen`](pkgs/c/compat.eigen.lua) |
4445
| Runtime loader compat (pure sources, sidestepping upstream codegen/asm) | [`compat.vulkan`](pkgs/c/compat.vulkan.lua) (the Khronos loader: `loader/generated/` is checked in, and the assembly path degrades to plain C through `UNKNOWN_FUNCTIONS_SUPPORTED`, so no CMake/Python/assembler is needed; windows deferred) · [`compat.vulkan-headers`](pkgs/c/compat.vulkan-headers.lua) |
4546
| Whole-source direct build + generated config (only where a platform lacks one) | [`compat.curl`](pkgs/c/compat.curl.lua) (win32 uses upstream's checked-in config, unix generates one) · [`compat.sdl2`](pkgs/c/compat.sdl2.lua) (win/mac use upstream's checked-in config; linux generates one and enables X11 by hand) · [`compat.c-ares`](pkgs/c/compat.c-ares.lua) (91 TUs; the release tarball already ships `ares_build.h` and a Windows config, so only `ares_config.h` is snapshotted per OS) |

README.zh-CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上
3737
| 原生模块库(Form A) | [`mcpplibs.xpkg`](pkgs/x/xpkg.lua) · [`mcpplibs.tinyhttps`](pkgs/t/tinyhttps.lua) · [`tensorvia-cpu`](pkgs/t/tensorvia-cpu.lua) · [`ffmpeg`](pkgs/f/ffmpeg.lua)(模块层,源码经 `compat.ffmpeg` 直编) · [`opencv`](pkgs/o/opencv.lua)(单仓库:模块层与 OpenCV 5 全源码构建同在包内,索引侧只留本描述符) · [`mcpplibs.grpc`](pkgs/g/grpc.lua)(gRPC 1.83.0 —— 本索引里唯一**无法**做成 compat 描述符的库:上游不发布任何自包含源码产物,其 tag 归档里 abseil/protobuf/re2/boringssl/zlib 全是空 submodule 占位,因此 [grpc-m](https://github.com/mcpplibs/grpc-m) 的 release tarball 才是那个产物。它只 vendor gRPC 自己的源码,五个依赖全取自本索引,故同时直接使用 protobuf 的消费者链进去的是同一份而非两份)|
3838
| C 源码 compat(含 `features`) | [`compat.cjson`](pkgs/c/compat.cjson.lua) · [`compat.zlib`](pkgs/c/compat.zlib.lua) |
3939
| C++ 源码 compat(彼此依赖) | [`compat.abseil`](pkgs/c/compat.abseil.lua)(151 TU;对 `absl/**` 取通配后,按上游自身的 test/benchmark 命名约定裁剪) · [`compat.protobuf`](pkgs/c/compat.protobuf.lua)(libprotobuf 运行时,79 TU 逐条转录自上游 `src/file_lists.cmake`;因 protobuf 公开头文件 include 了 `absl/…`,故显式依赖 `compat.abseil`;`gzip` feature 定义 `HAVE_ZLIB` 并拉入 `compat.zlib`,`upb` feature 则从同一个 tarball 里再编出 protobuf 的 64 TU C 运行时;还以 `kind = "bin"` target 暴露 **`protoc`**,消费者写 `tools = ["protoc"]` 即可从「自己链接的那个包」拿到为本机构建的编译器,使生成器与运行时的版本错配无法表达) · [`compat.re2`](pkgs/c/compat.re2.lua)(22 TU,取自上游自身的 `RE2_SOURCES`) |
40+
| C++ 源码 compat(零依赖客户端 + 可选组件) | [`compat.websocket`](pkgs/c/compat.websocket.lua)(IXWebSocket 12.0.1 —— 从上游 `IXWEBSOCKET_SOURCES` 剔掉 4 个 server TU 后直编的纯 RFC 6455 客户端,**基座零外部依赖**:TLS 关闭(OpenSSL/MbedTLS/AppleSSL 三组 TU 均不编),`IXWEBSOCKET_USE_ZLIB` 不定义(gzip codec 编译为 no-op)。两个可选 feature 在基座上叠加:`server`(4 个 server TU —— `IXWebSocketServer`/`IXSocketServer`/`IXHttpServer`/`IXWebSocketProxyServer`,零新增外部依赖,且 **implies `zlib`** —— 因为上游 server 默认就宣称 permessage-deflate,而 transport 的协商不受宏门控)与 `zlib`(依赖 `compat.zlib`,把 codec 变成真正的 permessage-deflate 压缩)。默认构建的测试自带基于 loopback 原始 socket 的最小 RFC 6455 echo server(握手/掩码/分片/关闭全部离线实测);第二个成员 `websocket-features` 跑真实的 `ix::WebSocketServer`,并断言压缩在线路上可观测 —— 64 KiB 重复载荷往返,`wireSize` = 80) |
4041
| header-only(含 `features`) | [`compat.eigen`](pkgs/c/compat.eigen.lua) |
4142
| 运行时 loader compat(纯源码,绕开上游 codegen/asm) | [`compat.vulkan`](pkgs/c/compat.vulkan.lua)(Khronos loader:`loader/generated/` 已签入,汇编路径经 `UNKNOWN_FUNCTIONS_SUPPORTED` 降级为纯 C,故无需 CMake/Python/汇编器;windows 延后)· [`compat.vulkan-headers`](pkgs/c/compat.vulkan-headers.lua) |
4243
| 全源码直编 + 生成 config(仅缺口平台) | [`compat.curl`](pkgs/c/compat.curl.lua)(win32 用上游签入 config,unix 生成) · [`compat.sdl2`](pkgs/c/compat.sdl2.lua)(win/mac 用上游签入 config,linux 生成 + 手工开 X11) · [`compat.c-ares`](pkgs/c/compat.c-ares.lua)(91 TU;release tarball 已自带 `ares_build.h` 与 Windows 配置,故只需按 OS 冻结 `ares_config.h`) |

mcpp.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ members = [
6363
"tests/examples/spdlog-compiled",
6464
"tests/examples/tinyhttps",
6565
"tests/examples/vulkan",
66+
"tests/examples/websocket",
67+
"tests/examples/websocket-features",
6668
"tests/examples/tray",
6769
"tests/examples/yyjson",
6870
"tests/examples/magic_enum",

0 commit comments

Comments
 (0)