Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .agents/docs/2026-08-04-add-c-ares-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# 新增 compat.c-ares 1.34.5(2026-08-04)

[gRPC 收录可行性分析](2026-08-04-grpc-feasibility-analysis.md) P1 索引侧的最后一块,承接
[abseil+protobuf](2026-08-04-add-abseil-and-protobuf-plan.md) 与
[re2+upb](2026-08-04-add-re2-and-protobuf-upb-plan.md)。至此 `grpc-m` 的全部索引依赖备齐。

产出:`compat.c-ares@1.34.5`,91 TU,workspace 成员 `tests/examples/c-ares`。

## 1. 为什么默认要有它

一度打算第一版跳过 c-ares(上游确有官方裁剪开关 `grpc_no_ares=true` → `GRPC_ARES=0`)。**这个决定被推翻了**:
gRPC 上游默认就是启用 c-ares,行业惯例也如此,索引里的包不应该悄悄比上游少一块。正确形态是
**默认开启 + 可关闭** —— `grpc-m` 用 Cargo 式 `[features] default = ["ares"]`,消费者以
`default-features = false` 关掉。

方向不能反过来:mcpp 的 feature 是**只增不减**的,若把 `-DGRPC_ARES=0` 写进基础 flag 再让 feature
翻成 1,两个 `-DGRPC_ARES` 会同时出现在命令行。关闭那一侧因此由 `grpc-m` 的 `build.mcpp` 承担
(`mcpp::has_feature("ares")` 为假时才发 `GRPC_ARES=0` 并剔除对应 TU),依赖仍走
`[feature-deps.ares]`(build.mcpp 明确不允许添加注册表依赖)。

## 2. 形态:只缺一个 ares_config.h

c-ares 平时靠 configure/CMake 生成 `ares_config.h`。本包用**冻结的按 OS 快照**替代该步骤,即
`compat.ffmpeg` / `compat.curl` / `compat.sdl2` 已经在用的形态。

上游 release tarball(`make dist` 产物,不是 tag 归档)已经提供了另外两样,因此**不需要**合成:

- `include/ares_build.h` —— 已是生成好的成品;
- `src/lib/config-win32.h` —— Windows 的备用配置。

快照直接取自 gRPC 1.83.0 的 `third_party/cares/config_{linux,darwin,windows}/ares_config.h`。
这不是随手挪用:gRPC 把 c-ares 精确 pin 在同一个 release(submodule `d3a507e` == tag `v1.34.5`),
这三个文件正是它为"不跑 c-ares 自己的构建系统"而维护的,并在其自身 CI 的三平台上长期使用。

**已知取舍(如实记录)**:这三份快照比 1.34.5 的 `ares_config.h.cmake` 模板保守 —— 模板有 143 个
`HAVE_*`,快照只给出 96 个,缺的包括 `HAVE_EPOLL`、`HAVE_GETIFADDRS`、`HAVE_GETRANDOM`、
`HAVE_IF_NAMETOINDEX` 等。缺失项一律按 0 处理,c-ares 因此走可移植的退化路径(如 poll 而非 epoll)。
实测**91/91 TU 编译通过、零警告**,所以这是**性能/特性层面的退化,不是功能不可用**。日后可用
c-ares 自己的 CMake 重新生成更完整的快照。

## 3. 编译开关照抄上游

`cflags` 取自 gRPC 的 `third_party/cares/cares.BUILD`:

- `CARES_STATICLIB` —— 选择静态链接的 declspec;
- `HAVE_CONFIG_H` —— **没有它 `src/lib/ares_setup.h` 根本不会去 include `ares_config.h`**;
- `_GNU_SOURCE` —— 在 glibc 上是**功能性必需**:缺了它 `<unistd.h>`/`<string.h>` 不声明
`gethostname`、`clock_gettime`、`strcasecmp`、`getservbyport_r`,实测有 4 个 TU 直接报
implicit-declaration 错误;
- `_HAS_EXCEPTIONS=0`。

Windows 另加 `NOMINMAX` / `_CRT_SECURE_NO_DEPRECATE` / `_CRT_NONSTDC_NO_DEPRECATE` /
`_WIN32_WINNT=0x0600`,并链 `ws2_32` + `iphlpapi`;macOS 因快照定义了 `HAVE_LIBRESOLV` 而链 `-lresolv`;
linux 链 `-lpthread`。

`src/lib/**/*.c` 可以放心通配:上游把测试与工具放在 `test/` 和 `src/tools/`,该目录下**没有任何
TU 定义 `main()`**(已核对)。

## 4. 验证结论

与 CI 一致的配置(mcpp **2026.8.3.3**、gcc@16.1.0、`MCPP_INDEX_MIRROR=GLOBAL`、
`MCPP_BUILD_CACHE=local`),先删 `target/` 与 `.mcpp/` 冷构建:

```
c-ares test result ok. 1 passed; 0 failed
objects: 92 (= 91 源 + 1 测试)
```

- **测试全程离线**,不发任何 DNS 查询,因此不依赖 runner 的网络状况。覆盖面按"每一项落在库的不同部分"
挑选:`ares_library_init/cleanup`、`ares_init_options`+`ares_destroy`、
CSV 服务器列表的**设置与回读往返**(真正走字符串与记录解析)、`ares_inet_pton/ntop` 双向、
`ares_strerror`。并且断言**畸形输入必须被拒**(非法服务器 CSV、`999.1.1.1`),否则前面的"解析成功"
什么也证明不了。版本号也断言到 1.34.5,避免悄悄换版本还能通过。
- **CN 镜像已闭环**:`mcpp-res/c-ares@1.34.5` 返回 `http=200` 且与 GLOBAL **字节一致**。
- **跨包 basename 撞名检查**:c-ares 与索引中已有的 abseil / protobuf / upb / re2 **零撞名**。
(顺带记录:其余包之间存在若干撞名,如 abseil×protobuf 的 `parser.cc`、abseil×gRPC 的
`status.cc`/`time.cc`,mcpp 会做嵌套消歧,且 CI 已用 `MCPP_BUILD_CACHE=local` 绕开 mcpp#344
的缓存键问题 —— 见 [re2+upb 文档](2026-08-04-add-re2-and-protobuf-upb-plan.md) §6.2。)

## 5. gRPC 整库编译验证(索引侧到此为止的意义)

在本包落地的同时,已用 mcpp 的 gcc@16.1.0 对 gRPC 1.83.0 做了**全量编译验证**:

```
1001 个 TU 全部通过(999 个 gRPC 源 + 2 个 third_party/address_sorting)
```

其中包含走**真实 c-ares 头文件与本包配置快照**的解析器路径。所需的额外 include 只有
`third_party/address_sorting/include` 与 `third_party/xxhash`(纯头),两者都在 gRPC 自带的
third_party 里有实体内容。gRPC 的 1000 个源文件**没有一个来自 third_party**,因此 `grpc-m`
只需 vendor `src/` + `include/`,abseil/protobuf/re2/c-ares/openssl 全部由索引提供。

唯一必须排除的是 `src/core/ext/upb-gen/google/protobuf/descriptor.upb_minitable.c` ——
它与 `compat.protobuf` 的 `upb` feature 带入的 bootstrap 版本**逐字节完全相同**,同时编入会重复符号。
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Two kinds of packages live here:
| 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) · [`compat.re2`](pkgs/c/compat.re2.lua) (22 TUs, upstream's own `RE2_SOURCES`) |
| header-only (with `features`) | [`compat.eigen`](pkgs/c/compat.eigen.lua) |
| 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) |
| 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) |
| 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) |
| Upstream codegen frozen into the mirror archive | [`compat.godot-cpp`](pkgs/c/compat.godot-cpp.lua) (two versions: `4.5.0` = the `godot-4.5-stable` bindings, `10.0.0-rc1` = godot-cpp's own 10.x line, whose bindings target Godot 4.6. The ~1000 GDExtension classes under `gen/` exist in no upstream tag archive — upstream's `binding_generator.py` emits them at build time. Running it once offline and publishing upstream's tree byte-for-byte **plus** `gen/` keeps Python off the consumer side entirely; `tools/godot-cpp/repack.sh` reproduces the archive deterministically and refuses to publish if any upstream file differs) |
| Header package filling a gap in the index | [`compat.glx-headers`](pkgs/c/compat.glx-headers.lua) (libglvnd's `GL/glx.h`, absent from the Khronos registry and required by SDL's X11 backend) |
| C++ application framework compat (dependencies reuse packages already in the index) | [`compat.eui-neo`](pkgs/e/compat.eui-neo.lua) (upstream's `3rd/` ships 8 vendored dependencies; none of them is compiled here — all are redirected to the same-version `compat.*` packages in this index) |
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上
| 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 运行时) · [`compat.re2`](pkgs/c/compat.re2.lua)(22 TU,取自上游自身的 `RE2_SOURCES`) |
| header-only(含 `features`) | [`compat.eigen`](pkgs/c/compat.eigen.lua) |
| 运行时 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) |
| 全源码直编 + 生成 config(仅缺口平台) | [`compat.curl`](pkgs/c/compat.curl.lua)(win32 用上游签入 config,unix 生成) · [`compat.sdl2`](pkgs/c/compat.sdl2.lua)(win/mac 用上游签入 config,linux 生成 + 手工开 X11) |
| 全源码直编 + 生成 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`) |
| 上游 codegen 前置冻结进镜像归档 | [`compat.godot-cpp`](pkgs/c/compat.godot-cpp.lua)(两个版本:`4.5.0` 是 `godot-4.5-stable` 的绑定,`10.0.0-rc1` 是 godot-cpp 自己的 10.x 线、对应 Godot 4.6。`gen/` 下约 1000 个 GDExtension 类不在任何上游 tag 归档里,由上游 `binding_generator.py` 在构建时生成。改为离线跑一次,把上游源码树逐字节原样 **加上** `gen/` 一起发布,消费侧就完全不需要 Python;`tools/godot-cpp/repack.sh` 可确定性复现该归档,且上游文件一旦有出入即拒绝打包) |
| 补索引空缺的头文件包 | [`compat.glx-headers`](pkgs/c/compat.glx-headers.lua)(libglvnd 的 `GL/glx.h`,Khronos registry 不含,SDL 的 X11 后端必需) |
| C++ 应用框架 compat(依赖复用索引内既有包) | [`compat.eui-neo`](pkgs/e/compat.eui-neo.lua)(上游 `3rd/` 自带 8 个 vendored 依赖,此处一个不编,全部改指索引内同版本 `compat.*`) |
Expand Down
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ members = [
"tests/examples/asio-ssl",
"tests/examples/boost-ext.ut",
"tests/examples/build-mcpp",
"tests/examples/c-ares",
"tests/examples/catch2",
"tests/examples/catch2-main",
"tests/examples/catch2-v2",
Expand Down
Loading
Loading