Skip to content

Commit 40215eb

Browse files
Runtime closure: bake dep runtime_dirs into RUNPATH; default Linux toolchain to glibc (#109)
* Bake dependency runtime library_dirs into binary RUNPATH; default Linux toolchain to glibc R2: flags.cppm emitted -Wl,-rpath only from toolchain.linkRuntimeDirs, dropping dependency packages' [runtime] library_dirs (already collected into plan.runtimeLibraryDirs by plan.cppm). So host-GL passthrough dirs (e.g. compat.glx-runtime) never reached the binary RUNPATH and dlopen()'d libGL/libGLX failed at run time (GLX: Failed to load GLX). Iterate plan.runtimeLibraryDirs (superset) instead. R1: cli.cppm first-run default on Linux was gcc-musl-static, which cannot link the glibc world (X11/GL/system libs). Default to platform-native glibc gcc; musl-static stays opt-in via --target x86_64-linux-musl (Cargo-style). * docs: runtime closure (rpath) + toolchain default design * feat: mcpp new --template gui (imgui.app starter) + package-template design Adds a builtin gui scaffold to `mcpp new` (Tier-0 imgui.app window starter, deps imgui 0.0.2, no toolchain pin). Verified: new --template gui -> build -> window renders. Also documents the long-term package-based template model (mcpp new name --template pkg@ver:tmpl, libraries ship templates/) as TODO. * feat: mcpp why / resolve --explain + capability-level doctor mcpp why [toolchain|runtime|deps] (alias: resolve --explain) explains the resolved toolchain (incl. abi), the runtime library dirs baked into RUNPATH (surfacing the compat.glx-runtime host-GL closure), and locked deps — so defaults are inspectable, not magic (I4). self doctor: add a runtime-capabilities section probing x11/wayland display and the host GLVND opengl.glx.driver (libGLX + vendor), reporting the provider — capability-level, not just env health. * refactor: capability/provider-driven doctor (no platform #ifdef) Aggregate dependency providers' [runtime] dlopen_libs/capabilities into the BuildPlan (with capability->provider mapping). doctor now reports each required host capability + its provider and verifies each provider-declared dlopen soname against the resolved runtime library_dirs — fully data-driven. Removes the previous #ifdef __APPLE__/_WIN32 + hardcoded /usr/lib paths: platform knowledge belongs in provider packages, not in mcpp core. why also surfaces capability->provider. * feat: capability-driven ABI enforcement A dependency may declare an abi:<x> capability (e.g. compat.glfw -> abi:glibc). prepare_build now verifies the resolved toolchain's ABI satisfies every such requirement and fails fast with an actionable message on mismatch — turning a cryptic deep musl build error (libXdmcp arc4random_buf) into an upfront capability error. Enforces/diagnoses; abi-driven reselection (toolchain is resolved before the dep graph) is a resolution-ordering follow-up. * feat: per-build resolution.json manifest artifact Writes a machine-readable resolution manifest (toolchain/abi, runtime closure library_dirs, dlopen_libs, capability->provider) next to build outputs — the serialized capability->plan, same data as `mcpp why`, usable by CI/tooling. * refactor: resolution.json via mcpp.libs.json (nlohmann), not string concat Use the existing json module for safe serialization/escaping instead of hand-built JSON. * feat: build profiles ([profile.<name>] + --profile) Adds bundled build settings: built-in release (-O2) / dev (-O0 -g) / dist (-O3 -flto -s), overridable/extendable via [profile.<name>] (opt/debug/lto/ strip) in mcpp.toml. --profile selects; flags.cppm applies opt/debug/lto to compile and lto/strip to link. Verified: release/dev/dist emit distinct flags. * fix: rpath only dependency runtime dirs; update first-run default test R2 precision: baking ALL of plan.runtimeLibraryDirs into -L/-rpath pulled the glibc payload dir into musl/static links (undefined _DYNAMIC; broke e2e 28/30). Split out plan.depRuntimeLibraryDirs (dependency [runtime] library_dirs only, e.g. compat.glx-runtime) and emit toolchain.linkRuntimeDirs + dep dirs, as before plus the dep closure. e2e 29 updated for the intentional glibc first-run default (musl-static is opt-in via --target). * feat: Cargo-style features ([features] + --features + dep features=[...]) [features] declares feature -> implied-features (with a 'default' set); long-form dep specs' features=[...] is now stored (was accepted-but-ignored) and requests features of that dependency. Activation = default ∪ requested, expanded transitively; each active feature becomes -DMCPP_FEATURE_<NAME> on that package's compile flags. Root activation via --features a,b. Verified: default set, --features, and implication closure all observable via #ifdef. Transitive dep->dep feature propagation is a follow-up. * feat: backend= dep knob, [runtime.<cap>] provider= override, [package] platforms - dep spec backend = "<impl>" — sugar for requesting the dependency's backend-<impl> feature (library backend selection knob; verified the dep compiles with -DMCPP_FEATURE_BACKEND_<IMPL>). - [runtime.<capability>] provider = "<pkg>" — explicit provider selection: prefers the named provider for matching capabilities (surfaced by why/ doctor/resolution.json), warns when the provider isn't in the graph. - [package] platforms = [...] — declared platform support, surfaced by mcpp why as the CI matrix hint. * fix: first-run default toolchain installs sysroot deps (glibc, linux-headers) The glibc first-run default (R1) needs the sysroot payloads exactly like `mcpp toolchain install` provides; the old musl-static default was self-contained, which masked this. Fixes fresh-home e2e 29/31 in CI (std module precompile: stdlib.h not found). * fix: first-run gcc default runs the same post-install fixup pipeline Extract gcc_post_install_fixup (patchelf PT_INTERP/RUNPATH for gcc/binutils + linker-specs wiring against sandbox glibc) out of `toolchain install` and run it from the first-run auto-install too. A fresh-sandbox glibc default previously skipped the fixup and could not find the C library (stdlib.h: No such file or directory — e2e 29/31 on CI). One shared pipeline, no duplicate. * fix: ownership guard — never fixup payloads inherited from another home gcc_post_install_fixup now skips payloads whose canonical path resolves outside this MCPP_HOME's registry: inherited (symlinked) payloads belong to their owner home, whose fixup is already valid; patching through the symlink rewrote the canonical binaries against ephemeral paths and bricked the owner's toolchain. Verified: fresh-home e2e (26/28/29/31) pass and the owner toolchain stays intact. * fix: payload probe falls back to the active home registry probe_payload_paths found glibc/linux-headers only as compiler SIBLINGS. With an inherited (symlinked) compiler the binary resolves into its owner home, so sysroot payloads freshly installed into the ACTIVE home (first-run default path) were invisible -> std module precompile failed with stdlib.h not found (CI fresh-home e2e 29/31). Add paths::active_home_xpkgs()/find_home_tool() and consult them after sibling search: discovery = compiler siblings ∪ active home registry. * fix: validate probed sysroot carries C headers; diagnostic std-precompile error A probed/remapped sysroot that exists but lacks stdlib.h (e.g. a partially-bootstrapped sandbox subos in a fresh MCPP_HOME) silently shadowed the payload -isystem fallback in both stdmod and flags, failing deep in the std module build. probe_sysroot now only returns a sysroot that actually contains the C headers (glibc usr/include or musl include layout) so all consumers uniformly fall through to payload paths. std-precompile failures now include the full compile command for actionable diagnosis. * fix: payload C headers via -idirafter for GCC (include_next reachability) Ground truth from CI (command now in the error): -isystem'd payload glibc include was present yet #include_next <stdlib.h> still failed. GCC's libstdc++ wraps libc headers with #include_next, which only searches directories AFTER the current header's dir — gcc's built-in dirs are last, so -isystem (inserted before them) is unreachable. Use -idirafter (appended to the very end) for GCC payload headers in both the std-module precompile and per-TU flags; clang keeps -isystem. Verified with a faithful fresh-home repro (no subos => invalid sysroot): first-run installs glibc default and builds clean. * fix: payload branch wires link-time C runtime (-B/-L glibc lib) With the sysroot cleared (validation) the linker lost the implicit crt1.o/ crti.o and -lm/-lc locations (CI: 'cannot find crt1.o'). The payload fallback now passes -B/-L <glibc payload lib> at link, completing the no-sysroot path end-to-end (compile headers via -idirafter + link runtime via -B/-L). Verified: fresh-home first-run builds AND runs. --------- Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
1 parent 93398f4 commit 40215eb

11 files changed

Lines changed: 815 additions & 78 deletions
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# mcpp 模板系统(package-based templates)— 设计 + TODO
2+
3+
> 2026-06-03 · 状态:builtin 模板已实现;**package 模板为设计/TODO(本轮未实现)**
4+
> 关联:agentdocs/2026-06-03-capability-architecture-rfc.md §9
5+
6+
## 目标
7+
8+
`mcpp new` 的模板不应只有内置几种,而应**复用"库模型":一个库同时携带实现 + 示例 +
9+
可实例化模板**。让库作者把"上手骨架"和库一起分发,消费者一条命令拉起。
10+
11+
```
12+
mcpp new myapp # builtin: bin(默认)
13+
mcpp new myapp --template gui # builtin: imgui.app 窗口骨架(已实现)
14+
mcpp new myapp --template imgui@0.0.2:window # package 模板(本设计)
15+
mcpp new myapp --template imgui:window # 省略版本 = 最新
16+
```
17+
18+
## 两层模型
19+
20+
### 1) builtin 模板(已实现)
21+
- `--template bin|gui`,硬编码在 `src/cli.cppm cmd_new`
22+
- 用途:无网络/零依赖即可起步;`gui` 给出 imgui.app Tier-0 骨架。
23+
- 这是 fallback,也是 package 模板的"标准库"等价物。
24+
25+
### 2) package 模板(设计 / TODO)
26+
语法:`--template <pkg>[@<ver>]:<templatename>`
27+
28+
**库侧目录约定**(库仓库里新增 `templates/`):
29+
```
30+
imgui-m/
31+
├── src/ # 库实现
32+
├── examples/ # 可运行示例
33+
└── templates/
34+
└── window/ # 模板名 = 目录名
35+
├── template.toml # 模板元数据(见下)
36+
├── mcpp.toml.in # 带占位符的清单
37+
└── src/main.cpp.in # 带占位符的源码
38+
```
39+
40+
**template.toml**:
41+
```toml
42+
[template]
43+
name = "window"
44+
description = "Minimal imgui.app window app"
45+
# 占位符 → 取值来源
46+
[template.vars]
47+
PROJECT = "{{name}}" # mcpp new 的 name
48+
IMGUI_VER = "{{self.version}}" # 该模板所属包的版本(自动)
49+
# 生成后提示
50+
post_message = "Edit src/main.cpp, then `mcpp run`."
51+
```
52+
53+
**占位符渲染**:`{{name}}``{{self.version}}``{{self.name}}` 等;`.in` 后缀文件渲染后去掉 `.in`;非 `.in` 文件原样拷贝。
54+
55+
### 解析与执行流程(core)
56+
1. 解析 `--template` 值:
57+
- 不含 `:` → builtin(`bin`/`gui`)。
58+
-`:``pkg[@ver]:tmpl`
59+
2. 经现有 fetcher/index 解析并下载该 `pkg@ver`(复用 `mcpp.pm` / `fetcher.cppm`)。
60+
3. 读取包内 `templates/<tmpl>/template.toml`;若缺失 → 报错并列出该包可用模板(`templates/*/`)。
61+
4. 渲染:对模板目录递归拷贝,`.in` 文件做占位符替换,写入新项目目录。
62+
5. 若模板 mcpp.toml 未声明对该库的依赖,自动注入 `[dependencies] <pkg> = "<ver>"`(让模板默认依赖它所属的库)。
63+
6. 打印 `template.post_message`
64+
65+
### 代码定位(实现时)
66+
- `src/cli.cppm cmd_new`:解析 `--template`,分流 builtin vs package。
67+
- 新增 `src/scaffold/template.cppm`:模板下载 + 渲染引擎(占位符、`.in` 处理)。
68+
- 复用:`src/fetcher.cppm` / `mcpp.pm.*`(下载包)、`src/manifest.cppm`(注入依赖)。
69+
- index:无需改 schema(模板随源码 tarball 分发,已在 `templates/`)。
70+
71+
### 发现/列举
72+
- `mcpp new --list-templates <pkg>[@ver]`:下载并列出 `templates/*/` 及其 description。
73+
- `mcpp new --template <pkg>:`(空模板名)→ 同上列举提示。
74+
75+
## 为什么这样设计(契合架构不变量)
76+
- I5 复杂度下沉:模板由库作者写一次,消费者一条命令继承。
77+
- I1/I4:`--template gui` builtin 保零配置;package 模板可被 `--list-templates` 解释。
78+
- 与 capability 模型正交:模板只是"起点物料",不改变解析/能力体系。
79+
80+
## TODO(实现顺序)
81+
- [ ] T1 模板字符串解析 `pkg@ver:tmpl`(+ builtin 分流)。
82+
- [ ] T2 `template.cppm` 渲染引擎(`.in` + `{{var}}`)。
83+
- [ ] T3 接 fetcher 下载模板包 + 读取 `templates/<tmpl>/`
84+
- [ ] T4 自动注入依赖 + post_message。
85+
- [ ] T5 `--list-templates`
86+
- [ ] T6 imgui-m 仓增 `templates/window/``templates/headless/` 作为首批样例。
87+
- [ ] T7 文档 + `mcpp new --help` 更新。
88+
89+
## 现状(本轮已落地)
90+
- builtin `--template bin|gui` 已实现并验证(`mcpp new x --template gui` → imgui.app 窗口骨架 → 直接出窗口)。
91+
- package 模板:本文件为设计与 TODO,留待后续实现。
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# mcpp core: runtime closure (rpath) + toolchain defaults
2+
3+
> 2026-06-03 · part of the mcpp ecosystem打通 plan
4+
> Master plan: /home/speak/workspace/github/agentdocs/2026-06-03-mcpp-ecosystem-architecture-plan.md
5+
6+
This change fixes two general, long-term issues that block "native + GUI"
7+
packages (e.g. the imgui module package) from working out of the box. Neither
8+
is a special-case for any one package.
9+
10+
## R2 — dependency `[runtime] library_dirs` were dropped from binary RUNPATH
11+
12+
### Symptom
13+
A fresh consumer that depends (transitively) on `compat.glfw` builds fine but
14+
`mcpp run` fails at window creation: `GLX: Failed to load GLX`.
15+
16+
### Root cause (confirmed in source)
17+
- `compat.glx-runtime` (pulled in by `compat.glfw` on Linux) symlinks the host
18+
GLVND/GL/GLX libraries into its install dir and declares
19+
`[runtime] library_dirs = { mcpp_generated/glx_runtime/lib }`.
20+
- `src/build/plan.cppm` (~L220) already collects every dependency package's
21+
`runtime.library_dirs` into `plan.runtimeLibraryDirs` (resolved to absolute).
22+
- BUT `src/build/flags.cppm` (~L258) built the produced binary's RUNPATH by
23+
iterating only `plan.toolchain.linkRuntimeDirs` — i.e. the toolchain's own
24+
runtime dirs. The dependency runtime dirs in `plan.runtimeLibraryDirs` were
25+
never emitted as `-Wl,-rpath`. So the host-GL passthrough dir was not on the
26+
binary's RUNPATH, and the dlopen()'d `libGL.so.1` / `libGLX.so.0` were
27+
unreachable at run time.
28+
29+
The dependency dirs were correctly used for the *build/process* environment but
30+
not baked into the *binary* — so anything reached via dlopen (GL/GLX, and any
31+
plugin-style runtime lib) failed.
32+
33+
### Fix
34+
`src/build/flags.cppm`: iterate `plan.runtimeLibraryDirs` (the union of
35+
dependency runtime dirs + toolchain + payload) instead of
36+
`plan.toolchain.linkRuntimeDirs` when emitting `-L`/`-Wl,-rpath`. This is a
37+
superset, so toolchain dirs are still covered; it additionally bakes each
38+
dependency's declared runtime dir into RUNPATH.
39+
40+
This is the correct general behavior: any package that declares
41+
`[runtime] library_dirs` is promising "binaries that use me need these dirs at
42+
run time"; the producer binary must carry them as RUNPATH.
43+
44+
## R1 — fresh-machine bootstrap default toolchain was musl-static on Linux
45+
46+
### Symptom
47+
On a clean machine, "First run no toolchain configured" auto-installs
48+
`gcc@15.1.0-musl` (musl, static). Building any package that links the glibc
49+
world (X11/GL/system libs) then fails, e.g. `libXdmcp` `arc4random_buf`
50+
implicit-declaration under musl.
51+
52+
### Root cause
53+
`src/cli.cppm` (~L1390) hard-coded the Linux first-run default to
54+
`gcc@15.1.0-musl`.
55+
56+
### Fix
57+
Default Linux first-run toolchain to the platform-native glibc gcc
58+
(`gcc@16.1.0`). musl-static remains fully available but **opt-in** via
59+
`mcpp build --target x86_64-linux-musl` (which the project already supports via
60+
`[target.x86_64-linux-musl]`). This mirrors Cargo/Rust: default triple is
61+
`-gnu` (glibc), `-musl` is an explicit target for portable static binaries.
62+
musl-static is a poor *default* because it cannot link the glibc/native world.
63+
64+
## Why these are long-term/industrial, not workarounds
65+
- R2 makes the existing two-plane design actually work: the *host plane*
66+
(drivers/GLVND, provided by `compat.glx-runtime`, never vendored) is bound to
67+
the binary via RUNPATH, which is the standard ELF mechanism. No package code
68+
changes; no env hacks.
69+
- R1 aligns the default with the platform-native ABI, the same principle Cargo
70+
uses. Static/musl stays a first-class explicit option.
71+
72+
## Test plan (acceptance, via imgui-m, no special-casing)
73+
1. Self-build mcpp with these changes.
74+
2. Fresh consumer: `mcpp new app && mcpp add imgui` then:
75+
- `mcpp build` → uses glibc gcc by default (R1), no musl error.
76+
- `readelf -d <bin>` → RUNPATH contains the `compat.glx-runtime` lib dir (R2).
77+
- `mcpp run` → window opens, ImGui renders, no `GLX: Failed to load GLX`.
78+
3. `mcpp test` headless still passes on all platforms.
79+
80+
## Follow-up (separate, tracked in master plan)
81+
- Declarative `abi` capability on native packages so the resolver *derives* the
82+
ABI-correct toolchain instead of relying on a good default (defense in depth).
83+
- Capability→provider resolution for `opengl.glx.driver` (glvnd/cocoa/win32).

src/build/flags.cppm

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,25 @@ CompileFlags compute_flags(const BuildPlan& plan) {
173173
}
174174
f.sysroot = link_toolchain_flags;
175175
} else if (plan.toolchain.payloadPaths) {
176-
// No sysroot but have payload paths: use -isystem.
176+
// No usable sysroot: wire the C library headers from the payload.
177+
// For GCC use -idirafter (appended after the built-in dirs) so that
178+
// libstdc++'s #include_next wrappers can reach them; -isystem would
179+
// place them BEFORE the built-ins, invisible to #include_next.
177180
auto& pp = *plan.toolchain.payloadPaths;
178-
compile_toolchain_flags += " -isystem" + escape_path(pp.glibcInclude);
181+
const bool clangTc = mcpp::toolchain::is_clang(plan.toolchain);
182+
auto inc_flag = [&](const std::filesystem::path& p) {
183+
return (clangTc ? " -isystem" : " -idirafter") + escape_path(p);
184+
};
185+
compile_toolchain_flags += inc_flag(pp.glibcInclude);
179186
if (!pp.linuxInclude.empty())
180-
compile_toolchain_flags += " -isystem" + escape_path(pp.linuxInclude);
187+
compile_toolchain_flags += inc_flag(pp.linuxInclude);
188+
// Link-time C runtime: a usable --sysroot would have provided the
189+
// startup objects and core libs implicitly. Without one, point the
190+
// driver at the glibc payload lib dir: -B for crt1.o/crti.o discovery,
191+
// -L for -lm/-lc resolution.
192+
link_toolchain_flags += " -B" + escape_path(pp.glibcLib);
193+
link_toolchain_flags += " -L" + escape_path(pp.glibcLib);
194+
f.sysroot = link_toolchain_flags;
181195
}
182196

183197
// Binutils -B flag
@@ -198,8 +212,14 @@ CompileFlags compute_flags(const BuildPlan& plan) {
198212
// AR binary
199213
f.arBinary = mcpp::toolchain::archive_tool(plan.toolchain);
200214

201-
// Opt level (musl ICE workaround)
202-
std::string opt_flag = isMuslTc ? " -Og" : " -O2";
215+
// Opt level + debug come from the resolved build profile
216+
// ([profile.<name>] → buildConfig). musl keeps -Og as an ICE workaround
217+
// unless the profile pins -O0.
218+
auto& prof = plan.manifest.buildConfig;
219+
std::string opt_flag = isMuslTc && prof.optLevel != "0"
220+
? " -Og" : (" -O" + prof.optLevel);
221+
if (prof.debug) opt_flag += " -g";
222+
if (prof.lto) opt_flag += " -flto";
203223

204224
// User link flags
205225
std::string user_ldflags;
@@ -256,10 +276,19 @@ CompileFlags compute_flags(const BuildPlan& plan) {
256276
std::string static_stdlib = (f.staticStdlib && !isClang && !mcpp::platform::is_windows) ? " -static-libstdc++" : "";
257277
std::string runtime_dirs;
258278
if constexpr (mcpp::platform::supports_rpath) {
279+
// Toolchain runtime dirs (glibc/gcc) as before...
259280
for (auto& dir : plan.toolchain.linkRuntimeDirs) {
260281
runtime_dirs += " -L" + escape_path(dir);
261282
runtime_dirs += " -Wl,-rpath," + escape_path(dir);
262283
}
284+
// ...plus dependency packages' [runtime] library_dirs (e.g.
285+
// compat.glx-runtime's host-GL passthrough), so dlopen()'d host libs
286+
// (libGL/libGLX) are reachable at run time. Only the dep dirs — NOT the
287+
// glibc payload dir — so static/musl links stay clean.
288+
for (auto& dir : plan.depRuntimeLibraryDirs) {
289+
runtime_dirs += " -L" + escape_path(dir);
290+
runtime_dirs += " -Wl,-rpath," + escape_path(dir);
291+
}
263292
}
264293

265294
// For Clang with payload paths: add glibc lib + dynamic linker to link flags.
@@ -273,13 +302,17 @@ CompileFlags compute_flags(const BuildPlan& plan) {
273302
payload_ld += " -Wl,--dynamic-linker=" + escape_path(loader);
274303
}
275304

305+
std::string link_extra;
306+
if (prof.lto) link_extra += " -flto";
307+
if (prof.strip) link_extra += " -s";
308+
276309
if constexpr (mcpp::platform::is_windows) {
277-
f.ld = user_ldflags;
310+
f.ld = user_ldflags + link_extra;
278311
} else if constexpr (mcpp::platform::needs_explicit_libcxx) {
279-
f.ld = std::format("{}{}{} -lc++{}", full_static, static_stdlib, b_flag, user_ldflags);
312+
f.ld = std::format("{}{}{} -lc++{}{}", full_static, static_stdlib, b_flag, user_ldflags, link_extra);
280313
} else {
281-
f.ld = std::format("{}{}{}{}{}{}{}", full_static, static_stdlib, link_toolchain_flags, b_flag,
282-
runtime_dirs, payload_ld, user_ldflags);
314+
f.ld = std::format("{}{}{}{}{}{}{}{}", full_static, static_stdlib, link_toolchain_flags, b_flag,
315+
runtime_dirs, payload_ld, user_ldflags, link_extra);
283316
}
284317

285318
return f;

src/build/plan.cppm

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ struct BuildPlan {
5656
std::vector<CompileUnit> compileUnits; // topologically sorted
5757
std::vector<LinkUnit> linkUnits;
5858
std::vector<std::filesystem::path> runtimeLibraryDirs;
59+
// ONLY the dependency packages' [runtime] library_dirs (not toolchain/
60+
// payload dirs). These are the dirs that must be baked into the produced
61+
// binary's RUNPATH (e.g. compat.glx-runtime). Kept separate so static/musl
62+
// links don't pull the glibc payload dir.
63+
std::vector<std::filesystem::path> depRuntimeLibraryDirs;
64+
// Aggregated host-runtime requirements from dependency packages'
65+
// [runtime] metadata. Capability/provider-driven — no platform special-casing
66+
// in mcpp: providers (e.g. compat.glx-runtime) declare these per platform.
67+
std::vector<std::string> runtimeDlopenLibs; // union of deps' dlopen sonames
68+
std::vector<std::string> runtimeCapabilities; // union of host capabilities
69+
std::vector<std::pair<std::string, std::string>> runtimeProviders; // (capability, provider pkg)
5970
};
6071

6172
// Build a BuildPlan from already-validated inputs.
@@ -219,8 +230,18 @@ BuildPlan make_plan(const mcpp::manifest::Manifest& manifest,
219230

220231
for (auto const& package : packages) {
221232
for (auto const& dir : package.manifest.runtimeConfig.libraryDirs) {
222-
append_unique_path(plan.runtimeLibraryDirs,
223-
dir.is_absolute() ? dir : package.root / dir);
233+
auto abs = dir.is_absolute() ? dir : package.root / dir;
234+
append_unique_path(plan.runtimeLibraryDirs, abs);
235+
append_unique_path(plan.depRuntimeLibraryDirs, abs);
236+
}
237+
for (auto const& lib : package.manifest.runtimeConfig.dlopenLibs) {
238+
if (std::ranges::find(plan.runtimeDlopenLibs, lib) == plan.runtimeDlopenLibs.end())
239+
plan.runtimeDlopenLibs.push_back(lib);
240+
}
241+
for (auto const& cap : package.manifest.runtimeConfig.capabilities) {
242+
if (std::ranges::find(plan.runtimeCapabilities, cap) == plan.runtimeCapabilities.end())
243+
plan.runtimeCapabilities.push_back(cap);
244+
plan.runtimeProviders.emplace_back(cap, package.manifest.package.name);
224245
}
225246
}
226247
// The same private runtime directories embedded as executable RUNPATH are

0 commit comments

Comments
 (0)