Skip to content

Commit 82d1697

Browse files
committed
fix: build the SONAME alias under explicit ninja goals (mcpp test), 0.0.107
A shared library that declares `soname` is written as bin/libX11.so but records SONAME libX11.so.6. Both the linker resolving a transitive NEEDED and the loader starting a binary look for the alias, never for the plain name — so the alias is a prerequisite of anything that links the library, not a by-product of building it. It was modelled as a standalone ninja edge that nothing depends on, reachable only through `default`. The 0.0.104 test batch (#274) made `mcpp test` pass explicit goal targets so it could isolate per-test compiles, and that silently stopped producing the alias. `mcpp build` and `mcpp run` drive `default` and were unaffected, which is why this survived three releases: only a project driven by `mcpp test` hits it. The symptom lands three levels from the cause — a consumer failing to link with `libX11.so: undefined reference to xcb_connect` (alongside `libxcb.so.1 ... not found`), or a test binary exiting 127. mcpp-index CI has been failing this way on gui-stack, imgui-module and imgui-window since 0.0.104; two probe PRs pinning only MCPP_VERSION over unmodified descriptors reproduced it at both 0.0.104 and 0.0.106, which is what ruled out the package-identity migration as the cause. Hang the alias off the consumer's implicit inputs, beside the .so it already lists. Correct under `default` and under explicit goals alike, and it pulls in nothing that was not already being built. e2e 64 covered only `build` + `run` — precisely the two paths that still worked. It now also runs `mcpp test` from a clean target dir and asserts the alias exists; without the fix that test binary fails with exit 127. Verified: unit 37/37; e2e 64 red before / green after; the three real mcpp-index members pass with `mcpp test` alone (gui-stack 5/5, imgui-module 1/1, imgui-window 1/1). Remaining local e2e failures (22, 98, 141) reproduce identically on the released 0.0.106 binary under the same MCPP_HOME — they are this machine's gcc 15.1.0, not this change.
1 parent 96fe161 commit 82d1697

5 files changed

Lines changed: 55 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)
55
6+
## [0.0.107] — 2026-07-25
7+
8+
### 修复
9+
10+
- **`mcpp test` 不再漏建共享库的 SONAME 别名(0.0.104 起的回归)。** 声明了 `soname` 的共享库产物写作 `bin/libX11.so`,但记录的 SONAME 是 `libX11.so.6`,链接器解析传递 `NEEDED`、加载器启动程序时找的都是后者 —— 别名不是附属产物,而是**任何链接该库的目标的前置条件**。此前别名是一条无人依赖的独立 ninja 边,只能经 `default` 到达;0.0.104 的 test 能力批次([#274](https://github.com/mcpp-community/mcpp/pull/274))让 `mcpp test` 改为显式指定 ninja 目标以隔离逐测试编译,于是这条边被静默跳过。
11+
12+
症状离根因很远,这也是它潜伏三个版本的原因:消费者链接期报 `libX11.so: undefined reference to xcb_connect`(伴 `libxcb.so.1 ... not found`),或测试二进制以 `exit 127` 退出。`mcpp build` / `mcpp run``default`,一直正常,所以只有以 `mcpp test` 驱动的工程会中招 —— mcpp-index 的 CI 正是如此,`gui-stack` / `imgui-module` / `imgui-window` 三个成员自 0.0.104 起持续失败。
13+
14+
修法是把别名挂到消费者的隐式输入上(`plan.cppm``append_direct_shared_deps`),与被链接的 `.so` 并列。无论 ninja 的目标是 `default` 还是某个测试二进制都会生成,且不会多编译任何东西。e2e 64 补了 `mcpp test` 一段覆盖此路径 —— 它此前只覆盖 `build` + `run`,正是缺口所在。
15+
616
## [0.0.106] — 2026-07-25
717

818
> 落地 **SPEC-001**(`docs/spec/package-identity.md`)—— 包身份的规范形态。0.0.105 为修 #278 引入的「`name` 必须写成完全限定名」是**编码约束而非设计规则**:它的唯一成因是 mcpp 构造安装目标时丢弃了已读到的字面 `name`、改用 `<ns>.<短名>` 重新渲染一遍。本版本改为直接使用字面值,规范形态回归 **`namespace` 承载层级、`name` 是单一原子段**。配套 xlings 0.4.69([#381](https://github.com/openxlings/xlings/issues/381),索引改按 `(namespace, name)` 建键)。

mcpp.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mcpp"
3-
version = "0.0.106"
3+
version = "0.0.107"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]

src/build/plan.cppm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,19 @@ make_plan(const mcpp::manifest::Manifest& manifest,
727727
for (auto targetIndex : targetsIt->second) {
728728
auto const& dep = sharedDepTargets[targetIndex];
729729
lu.implicitInputs.push_back(dep.output);
730+
// The SONAME alias is a prerequisite too, not a by-product: the
731+
// library is written as bin/libX11.so but records SONAME
732+
// libX11.so.6, so both the linker (resolving a transitive
733+
// NEEDED) and the loader look for the alias, never for the
734+
// plain name. Hanging it off the consumer keeps it correct
735+
// under explicit ninja goals — `mcpp test` names the test
736+
// binaries, and an alias edge that nothing depends on is
737+
// reachable only through `default`, so it was silently skipped
738+
// (0.0.104-0.0.106). The failure surfaced far away, as
739+
// `libX11.so: undefined reference to xcb_connect` or a test
740+
// exiting 127.
741+
for (auto const& alias : runtime_aliases_for_target(dep.target))
742+
lu.implicitInputs.push_back(alias);
730743
auto flags = shared_library_link_flags(dep.target);
731744
lu.linkFlags.insert(lu.linkFlags.end(), flags.begin(), flags.end());
732745
}

src/toolchain/fingerprint.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import mcpp.toolchain.detect;
1818

1919
export namespace mcpp::toolchain {
2020

21-
inline constexpr std::string_view MCPP_VERSION = "0.0.106";
21+
inline constexpr std::string_view MCPP_VERSION = "0.0.107";
2222

2323
struct FingerprintInputs {
2424
Toolchain toolchain;

tests/e2e/64_shared_soname_runtime_alias.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,34 @@ readelf -d "$so" | grep -q 'Library soname: \[libdepShared.so.1\]' || {
9595
exit 1
9696
}
9797

98+
# ── `mcpp test` must produce the alias too ──────────────────────────
99+
# The alias edge is reachable through ninja's `default`, which is what build
100+
# and run use. `mcpp test` names its goals explicitly (to isolate per-test
101+
# compiles), and from 0.0.104 to 0.0.106 that skipped the alias: a test binary
102+
# linked against libdepShared.so records NEEDED libdepShared.so.1 and then
103+
# exits 127 because the loader cannot find it. Start from a clean target dir so
104+
# only the test path can create it.
105+
mkdir -p tests
106+
cat > tests/linked_test.cpp <<'EOF'
107+
extern "C" int dep_shared_answer();
108+
109+
int main() {
110+
return dep_shared_answer() == 42 ? 0 : 1;
111+
}
112+
EOF
113+
114+
rm -rf target
115+
"$MCPP" test > test.log 2>&1 || {
116+
cat test.log
117+
echo "mcpp test failed — the SONAME alias is a prerequisite of anything that links the library, not a by-product of the default target"
118+
exit 1
119+
}
120+
121+
alias_from_test="$(find target -name 'libdepShared.so.1' | head -1)"
122+
[ -n "$alias_from_test" ] || {
123+
cat test.log
124+
echo "mcpp test did not produce the ABI soname alias"
125+
exit 1
126+
}
127+
98128
echo "OK"

0 commit comments

Comments
 (0)