Skip to content

chore: pin mcpp 0.0.109, hoist the compat index redirect, fix compat.openssl - #125

Merged
Sunrisepeak merged 4 commits into
mainfrom
chore/spec001-docs-workspace-indices-mcpp-0.0.109
Jul 27, 2026
Merged

chore: pin mcpp 0.0.109, hoist the compat index redirect, fix compat.openssl#125
Sunrisepeak merged 4 commits into
mainfrom
chore/spec001-docs-workspace-indices-mcpp-0.0.109

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Jul 26, 2026

Copy link
Copy Markdown
Member

基于 mcpp 0.0.109 的一次规范/机制对齐:CI pin 与 index floor 上移、compat 索引重定向提到 workspace 根、
compat.openssl 的身份与实现修正,以及为它补上两个真正跑得起来的成员。

1. mcpp 0.0.109

0.0.109 让裸名依赖的 wire 地址从被身份闸门接受的那份描述符里同时取两半(mcpp#286)。
这是 SPEC-001 短名迁移(#120)的客户端另一半:此前 mcpp 从描述符取 name、从请求取 namespace,于是裸写的
gtest = "1.15.2" 寻址成 mcpplibs:gtest —— 任何索引都没有这个 key。它一直能用只是因为迁移前 package.name
字面值是 compat.gtest,被硬编码的 compat.<short> 重试接住了;短名把这个巧合拿掉,于是 0.0.108 上对本索引的
每一个裸名请求都是坏的
index.tomlmin_mcpp/latest_mcpp 随 pin 一起走到 0.0.109,让拿到本索引的消费者
手里的客户端能寻址它。

2. [indices] 提到 workspace 根

13 个消费 compat 的成员不再各写一遍 [indices] compat = { path = "../../.." } —— 根级声明一次由成员继承,
相对路径按 workspace 根解析(mcpp#224,0.0.97 修复)。

只放一条、且只能是 compat,原因是这张表按命名空间取键:

  • 声明在一个没有依赖会请求的名字下,该索引根本不会被注册,解析静默回落到已发布的远程索引 ——
    此时被测的根本不是这个 checkout(实测:根级用 mcpp-index = { path = "." } 时,xlings 只看到
    1 index repo configured [mcpplibs -> github.com/mcpplibs/mcpp-index.git]);
  • 同一路径声明成多个命名空间注册,但变成 N 个各自独立的项目 repo,之后任何查找都以 N 路歧义失败
    (mcpp#238 / xlings#374;
    0.4.69 后由静默 exit 1 变为响亮报错)。

需要其他命名空间的成员保留自己的声明,该声明替换继承来的根级表而非合并 —— 这正是每个成员只保留一个项目
索引 repo 的机制。mcpp.toml / index.toml 也加进了 workflow 的 paths 过滤:两者的改动都可能影响全部成员,
此前却都不会触发 CI。

3. compat.openssl

name:compat.opensslopenssl。这是索引里最后一个 legacy 完全限定拼写(SPEC-001 §3.2)。

./config--libdir=lib —— 这是一个真 bug。不传时 OpenSSL 把安装 libdir 推导为
lib$target{multilib}(Configurations/unix-Makefile.tmpl),而 10-main.conflinux-x86_64 的是
multilib => "64":产物落到 $prefix/lib64,而 -Llib 和 install() 的自检看的是 $prefix/lib
linux-aarch64 与两个 darwin64 目标都没有 multilib、解析成 lib —— 这就是它能在 arm64 Mac 上验证通过、
却对每一个 x86_64 Linux 消费者都是坏的原因。修之前先对着已发布的描述符复现过:安装树确实是
bin/ include/ lib64/,install() 卡在自己的归档检查上。

同时:

  • linux 链接改 -l:libssl.a -l:libcrypto.a(直接点名归档,而不是让驱动去解析一个共享库会赢的名字 ——
    只要有一个 -L 排在我们前面,消费者就会拿到 host 的 libssl.so.3),并补 -ldl -lpthread(musl 与
    glibc < 2.34);
  • 构建日志移进安装前缀 —— 原来放在会被 install 前那次 os.tryrm 删掉的位置,失败时正好没有;
  • RANLIB=/usr/bin/ranlib 收敛到 macOS(llvm-ranlib 不认 -c 的只有它),Linux 精简容器不再因为没有
    /usr/bin/ranlib 而白白失败;
  • 去掉一处多余的 os.cd,它会让相对 srcroot 的回退分支必然失败;
  • install() 开头探测 perl —— OpenSSL 的 ./config 本身就是 Perl 脚本,而没有 xim:perl 可声明为
    build dep,失败时给一句能读懂的话而不是让 ./config 死在 shell 报错里。

4. 两个成员

tests/examples/openssl —— 直接依赖,继承根级 compat,所以被构建和链接的就是本 checkout 的描述符。
断言两个归档真的都链上了(SSL_CTX_new + EVP sha256),且产物没有动态 ssl/crypto 依赖。

tests/examples/asio-ssl —— asio ssl feature 的端到端,loopback 上跑真实 TLS 握手。它有意不写成员级
[indices]:继承 compat 意味着 asio 走已发布索引、而它的 feature 依赖 compat.openssl 来自本 checkout ——
这正是未合并的 compat 描述符可以通过一个已发布的消费者被验证的路径。

这个测试是从 asio-module 挪过来的,原先整体被 MCPP_FEATURE_SSL 包住。feature 的 defines 只作用于包
自身的 TU、不传播给消费者
,所以那个文件编译成了空的 —— 它在什么都没断言的情况下"通过",这也正是上面那个
libdir bug 能绿着进 main 的原因。现在它 key 在 HAVE_ASIO_SSL 上,由成员自己的 cfg 门控 cxxflags 声明
(与 openblas 成员同款写法)。asio-module 相应从 6 个测试变回 5 个(第 6 个本来就是 no-op)。

5. 文档

docs/package-types.md 还在教 name = "compat.<lib>"。README、仓库/schema 文档(新增「索引重定向」一节、
修正 features 行对 defines 作用域的描述)与 openssl 设计文档(§0 修订说明)都对齐到代码的实际行为。

本地验证(mcpp 0.0.109,gcc@16.1.0,linux-x86_64)

  • lint 全量 + mcpp xpkg parse 全部 49 个描述符:通过
  • 冷解析(先清 store)逐成员 mcpp test -p:openssl asio-ssl eigen cjson spdlog archive
    fmtlib.fmt nlohmann.json marzer.tomlplusplus tinyhttps core imgui imgui-window openblas
    spdlog-compiled asio-module —— 全绿
  • compat-x-openssl/3.5.1/lib/{libssl.a,libcrypto.a} 存在,ldd 无动态 ssl/crypto 依赖

CI:三平台全量 workspace 全绿(Workspace all 24 member(s) passed × linux / macOS / windows,
ssl ... ok + tls ... ok 均实跑非跳过),lint 与 mirror-cn-reachable 通过。

macOS 上多花的三轮,以及各自的真因

macOS 的 OpenSSL 源码构建连挂三轮,过程本身值得记一笔 —— 三次症状完全不同,前两次的判断都是错的:

轮次 症状 真因
1 下载完 43s 失败,错误为空 macOS 上 PATH 里的 cc 是 xim 的 llvm,没有配 macOS SDK
2 E_INVALID_INPUT: package 'xim:make@latest' not found 该包 linux-only;第 1 轮我加的这条依赖本身是错的
3 llvm-ranlib: error: Invalid option: '-c' 我把 RANLIB 从 make 命令行赋值降级成了环境变量

关键的结构性问题是第 1 轮根本无法诊断:这类 install() 驱动的包在自己的 Make/Configure 里构建,输出被
xim 的 interface mode 吞掉,xlings 只报 E_INTERNAL: [openssl] failed:。所以第 2 轮起加了
if: failure() 步骤把所有 mcpp_*_build.log 的尾部打出来,并在日志开头记 make / cc / perl 版本 ——
第 3 轮的真因就是它直接给出来的。这个步骤对 openblas 等同类包同样生效。

第 3 轮那个是我自己引入的回归:RANLIB=… make install_sw环境变量,打不过 Makefile 里的赋值
(除非 make -e);make RANLIB=… install_sw 才是命令行赋值。#124 原本的写法是对的,我在把 RANLIB
收敛到 macOS 时挪错了位置。根因在 OpenSSL 源码里坐实:darwin-commonranlib => "ranlib -c"
(Configurations/10-main.conf:1844),而 PATH 上的 ranlib 解析到不认 -c 的 llvm-ranlib。

顺带发现:compat.openblas 的 macosx 也声明了 xim:make@latest,同样是坏的 —— 只是它在测试面里是
Windows-only,macOS 路径从没被走到,所以一直没暴露。已写进 openssl 描述符的注释,未在本 PR 顺手改
(不在范围内,且要验证它需要给 openblas 自己的 macOS 覆盖)。

已知边界

  • compat.openssl 仍无 windows(需要预编译 MSVC 归档),两个成员在 windows 上都退化为 no-op main()。
  • asio-ssl 验证的是本地 openssl + 已发布 asio;本地 asio 描述符由 asio-module 覆盖。一个成员没法同时从
    本 checkout 解析两个命名空间,这是上面那条歧义约束的直接后果。
  • 裸名依赖无法由本索引自测(重定向按请求侧命名空间取键,裸请求走默认命名空间 → 落到远程索引);
    该路径由 mcpp 上游的 e2e 165 覆盖。

…openssl

## mcpp 0.0.109 (MCPP_VERSION + index.toml floor)

0.0.109 makes a bare dependency take BOTH halves of its wire address from the
descriptor the identity gate accepted (mcpp#286). That is the client-side other
half of the SPEC-001 short-name migration (#120): mcpp used to take the name
from the descriptor and the namespace from the request, so a bare `gtest =
"1.15.2"` addressed `mcpplibs:gtest`, a key no index has. It survived only on a
hardcoded `compat.<short>` retry that the short names removed — leaving every
bare request against this index broken on 0.0.108. min_mcpp moves with the pin
so consumers get a client that can address the index they are handed.

## [indices]: hoisted to the workspace root

The 13 members that consume `compat` no longer repeat `[indices] compat =
{ path = "../../.." }`; the root declares it once and they inherit it, with the
relative path resolved against the workspace root (mcpp#224, 0.0.97).

Exactly one entry, and `compat` specifically, because the table is keyed by
NAMESPACE:

  * declared under a name no dependency asks for, the index is never
    registered and resolution silently falls through to the published remote
    index — the checkout under test is then not what is being tested;
  * declared under several namespaces it registers N separate project repos,
    and every lookup afterwards fails with an N-way ambiguity between what is
    physically one descriptor (mcpp#238 / xlings#374).

Members needing another namespace keep their own declaration, which REPLACES
the inherited table rather than merging — which is what holds each member to
one project index repo. mcpp.toml and index.toml also join the workflow's
paths filter; a change to either can break every member and neither gated a
run before.

## compat.openssl

`name`: `compat.openssl` -> `openssl`. It was the index's last legacy
fully-qualified spelling (SPEC-001 §3.2).

`./config` now passes `--libdir=lib`. Unset, OpenSSL derives the install libdir
as `lib$target{multilib}` and `linux-x86_64` declares `multilib => "64"`, so
the archives landed in $prefix/lib64 while `-Llib` and the post-build check
looked at $prefix/lib. linux-aarch64 and both darwin64 targets declare no
multilib and resolve to plain `lib` — which is how this validated on an arm64
Mac while being broken for every x86_64 Linux consumer. Reproduced against the
published descriptor before fixing: the install tree came out as bin/ include/
lib64/ and install() failed its own archive check.

Also: linux links `-l:libssl.a -l:libcrypto.a` (naming the archive rather than
letting the driver resolve a name a shared object would win — one stray -L
ahead of ours and the consumer picks up the host libssl.so.3) plus `-ldl
-lpthread` for musl and pre-2.34 glibc; the build log moved into the install
prefix, since the old spot was deleted mid-build by the very os.tryrm that
preceded install; `RANLIB=/usr/bin/ranlib` is confined to macOS, the platform
whose llvm-ranlib rejects `-c`; a redundant `os.cd` that would have broken the
relative-srcroot fallback is gone; and install() probes for `perl` up front,
since OpenSSL's ./config IS a Perl script and no xim:perl exists to declare.

## Two members for it

`tests/examples/openssl` — direct dependency, inherits the root `compat`
redirect, so the descriptor in this checkout is what gets built. Asserts both
archives really link (SSL_CTX_new + EVP sha256) and the binary carries no
dynamic ssl/crypto dependency.

`tests/examples/asio-ssl` — the asio `ssl` feature end-to-end, a real TLS
handshake over loopback. It declares no member-level [indices] on purpose:
inheriting `compat` means asio resolves from the published index while its
feature dep, compat.openssl, comes from this checkout — which is how an
unmerged compat descriptor can be exercised through a published consumer.

The test moved out of asio-module, where it had been guarded by
MCPP_FEATURE_SSL. A feature's `defines` apply to the package's own TUs, not to
consumers, so that file compiled to nothing and passed while asserting
nothing — the reason the libdir bug reached main green. It now keys off
HAVE_ASIO_SSL, which the member sets in its own cfg-gated cxxflags.

## Docs

docs/package-types.md still taught `name = "compat.<lib>"`. README, the
repository/schema doc (new "索引重定向" section, corrected `features` row) and
the openssl design spec are updated to match what the code does.
…ailure

The macOS workspace job failed on both new members ~43s after the tarball
landed — far too early for a compile, and with nothing to go on: install()
writes everything to an on-disk log (xim's interface mode swallows subprocess
stdout) and xlings surfaces the failure as a bare
`E_INTERNAL: [openssl] failed:`.

Two changes, one for the likely cause and one so the next failure says what it
was.

macosx now declares `deps = { "xim:make@latest" }`. The previous comment
claimed it was unnecessary because "macOS ships GNU Make at /usr/bin/make" —
it ships GNU Make **3.81**, the last GPLv2 release, frozen in 2006, and
OpenSSL 3.x's generated Makefile does not build with it. That matches the
timing exactly: ~30s of Perl Configure, then make failing on sight.
compat.openblas already declares this dep on macosx.

Each build step now runs through a helper that, on failure, names the step and
prints the last 40 lines of the log. The log also opens with `make --version`,
because 3.81-vs-4.x is precisely the distinction that is invisible after the
fact. The tail is passed as one pre-formatted argument rather than a format
string — build output contains `%` often enough that formatting it is its own
failure mode.

Verified on linux (cold): log opens with "GNU Make 4.3", openssl and asio-ssl
members both pass.
…d logs in CI

`xim:make` has no macOS build — xim-pkgindex's pkgs/m/make.lua declares only an
`xpm.linux` block — so yesterday's macosx dep failed resolution outright with
`E_INVALID_INPUT: package 'xim:make@latest' not found`, before install() ran.
Reverted, with the finding written down. compat.openblas declares the same dep
on macosx and is broken identically; nobody has noticed because that package is
Windows-only in the test suite, so its macOS path is never taken.

That leaves the original macOS failure — 43s after download, no message —
still undiagnosed, so this stops guessing at it:

  * The workspace job gains an `if: failure()` step that finds every
    `mcpp_*_build.log` under the members and the registry and prints its tail.
    install()-driven packages build through their own Make/Configure system,
    xim's interface mode swallows that output, and xlings reports only
    `E_INTERNAL: [<pkg>] failed:` — so a platform-specific break otherwise
    costs one full CI round-trip per hypothesis.
  * The log now opens with make/cc/perl versions, not just make's.

And one likely cause addressed while here: OpenSSL is configured and built
outside mcpp's compile rules, so it inherits none of the resolved toolchain's
flags — it just runs `cc`. On macOS the `cc` in PATH is xim's llvm, which has
no macOS SDK wired up, so every compile would fail on <stdio.h>. macOS now
builds with `CC=/usr/bin/cc`, Apple's driver, which finds the SDK itself
(same spirit as the RANLIB pin, which is already macOS-only). resolve_make()
also prefers a Homebrew gmake when one exists, since /usr/bin/make is GNU Make
3.81.

If the SDK guess is wrong, the log tail now says what is.

Verified on linux (cold): log opens with GNU Make 4.3 / gcc 16.1.0, openssl
member passes.
…nv var

The macOS build now gets all the way through configure and compile — the CC
pin was the missing piece there — and dies in `make install_sw`, one line after
copying libcrypto.a into place:

    install libcrypto.a -> .../3.5.1/lib/libcrypto.a
    llvm-ranlib: error: Invalid option: '-c'
    make: *** [install_dev] Error 1

which is precisely what the RANLIB override exists to prevent. OpenSSL's
`darwin-common` sets `ranlib => "ranlib -c"` (Configurations/10-main.conf:1844)
and PATH resolves `ranlib` to the toolchain's llvm-ranlib, which rejects `-c`.

The override was not taking effect, and that is a regression I introduced when
confining it to macOS: I moved it from `make RANLIB=… install_sw` to
`RANLIB=… make install_sw`. The first is a command-line assignment and beats
the Makefile's own definition; the second is only an environment variable,
which a Makefile assignment overrides absent `make -e` — so it silently did
nothing and the build failed exactly as if it were not there. Restored to the
command-line form, with a comment saying why the position matters.

Also confirmed from the same log that `--libdir=lib` does its job on macOS:
the archive installs to `3.5.1/lib/`, not `lib64/`.

Verified on linux (cold): openssl member passes.
@Sunrisepeak
Sunrisepeak merged commit 0adb288 into main Jul 27, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant