diff --git a/CHANGELOG.md b/CHANGELOG.md index a6111f9..2c0d7a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ > 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。 > 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。 +## [0.0.107] — 2026-07-25 + +### 修复 + +- **`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 目标以隔离逐测试编译,于是这条边被静默跳过。 + + 症状离根因很远,这也是它潜伏三个版本的原因:消费者链接期报 `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 起持续失败。 + + 修法是把别名挂到消费者的隐式输入上(`plan.cppm` 的 `append_direct_shared_deps`),与被链接的 `.so` 并列。无论 ninja 的目标是 `default` 还是某个测试二进制都会生成,且不会多编译任何东西。e2e 64 补了 `mcpp test` 一段覆盖此路径 —— 它此前只覆盖 `build` + `run`,正是缺口所在。 + ## [0.0.106] — 2026-07-25 > 落地 **SPEC-001**(`docs/spec/package-identity.md`)—— 包身份的规范形态。0.0.105 为修 #278 引入的「`name` 必须写成完全限定名」是**编码约束而非设计规则**:它的唯一成因是 mcpp 构造安装目标时丢弃了已读到的字面 `name`、改用 `.<短名>` 重新渲染一遍。本版本改为直接使用字面值,规范形态回归 **`namespace` 承载层级、`name` 是单一原子段**。配套 xlings 0.4.69([#381](https://github.com/openxlings/xlings/issues/381),索引改按 `(namespace, name)` 建键)。 diff --git a/mcpp.toml b/mcpp.toml index 42294de..c1a6370 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,6 +1,6 @@ [package] name = "mcpp" -version = "0.0.106" +version = "0.0.107" description = "Modern C++ build & package management tool" license = "Apache-2.0" authors = ["mcpp-community"] diff --git a/src/build/plan.cppm b/src/build/plan.cppm index 8f1133f..4003f79 100644 --- a/src/build/plan.cppm +++ b/src/build/plan.cppm @@ -727,6 +727,19 @@ make_plan(const mcpp::manifest::Manifest& manifest, for (auto targetIndex : targetsIt->second) { auto const& dep = sharedDepTargets[targetIndex]; lu.implicitInputs.push_back(dep.output); + // The SONAME alias is a prerequisite too, not a by-product: the + // library is written as bin/libX11.so but records SONAME + // libX11.so.6, so both the linker (resolving a transitive + // NEEDED) and the loader look for the alias, never for the + // plain name. Hanging it off the consumer keeps it correct + // under explicit ninja goals — `mcpp test` names the test + // binaries, and an alias edge that nothing depends on is + // reachable only through `default`, so it was silently skipped + // (0.0.104-0.0.106). The failure surfaced far away, as + // `libX11.so: undefined reference to xcb_connect` or a test + // exiting 127. + for (auto const& alias : runtime_aliases_for_target(dep.target)) + lu.implicitInputs.push_back(alias); auto flags = shared_library_link_flags(dep.target); lu.linkFlags.insert(lu.linkFlags.end(), flags.begin(), flags.end()); } diff --git a/src/toolchain/fingerprint.cppm b/src/toolchain/fingerprint.cppm index 409e2f5..be8b2c7 100644 --- a/src/toolchain/fingerprint.cppm +++ b/src/toolchain/fingerprint.cppm @@ -18,7 +18,7 @@ import mcpp.toolchain.detect; export namespace mcpp::toolchain { -inline constexpr std::string_view MCPP_VERSION = "0.0.106"; +inline constexpr std::string_view MCPP_VERSION = "0.0.107"; struct FingerprintInputs { Toolchain toolchain; diff --git a/tests/e2e/64_shared_soname_runtime_alias.sh b/tests/e2e/64_shared_soname_runtime_alias.sh index 5a74538..500ee4c 100755 --- a/tests/e2e/64_shared_soname_runtime_alias.sh +++ b/tests/e2e/64_shared_soname_runtime_alias.sh @@ -95,4 +95,34 @@ readelf -d "$so" | grep -q 'Library soname: \[libdepShared.so.1\]' || { exit 1 } +# ── `mcpp test` must produce the alias too ────────────────────────── +# The alias edge is reachable through ninja's `default`, which is what build +# and run use. `mcpp test` names its goals explicitly (to isolate per-test +# compiles), and from 0.0.104 to 0.0.106 that skipped the alias: a test binary +# linked against libdepShared.so records NEEDED libdepShared.so.1 and then +# exits 127 because the loader cannot find it. Start from a clean target dir so +# only the test path can create it. +mkdir -p tests +cat > tests/linked_test.cpp <<'EOF' +extern "C" int dep_shared_answer(); + +int main() { + return dep_shared_answer() == 42 ? 0 : 1; +} +EOF + +rm -rf target +"$MCPP" test > test.log 2>&1 || { + cat test.log + echo "mcpp test failed — the SONAME alias is a prerequisite of anything that links the library, not a by-product of the default target" + exit 1 +} + +alias_from_test="$(find target -name 'libdepShared.so.1' | head -1)" +[ -n "$alias_from_test" ] || { + cat test.log + echo "mcpp test did not produce the ABI soname alias" + exit 1 +} + echo "OK"