Repository navigation
CMake parity for a framework and its ecosystem: per-row linkage, dependency identity, shared-library staging, multi-source tests, DMG / Setup.exe — each with a CI check #634
Description
中文摘要
HuxerUI 的 mcpp 构建链在六个平台上已默认对齐 CMake 的链接、加载与资源布局约定(fork PR 见文末)。逐项对照 CMake 链之后,仍需要 mcpp 生态补齐的共 21 条,按归属分为引擎(A)、mcpp:plugins(B)、xim-pkgindex(C)和 mcpp-index(D)四类。每条都写明实测现象、需求原因、建议形状,以及可在 GitHub 托管 runner 上跑的 CI 验收方法;不需要真机、签名证书或 secret。
- P1(每个应用和生态库都会碰到):
- A1:按平台切换依赖的 linkage,第一种直觉写法会被静默忽略;
- A2:依赖名与目标清单身份不一致时,报错是"模块重复提供";
- A3 / B1:图内构建的共享库没有被 stage(Android 清单仍写
closure = walked),Mach-O 闭包未遍历,所以 macOS 的.app带不上 dylib。
- P2:
- 测试:A5 多源文件测试程序与标签;
- payload 与 runner:A7 payload 依赖闭包对 build program 不可见,C2 模拟器控制台输出偶发丢失,C4 未合并 recipe 无法在消费方 CI 中测试;
- macOS 分发:B2 DMG,B3 / C1
mcpp run --format app拿不到程序退出码。
- P3:
- 引擎:A4 默认 SONAME,A6 模拟器/模拟机上跑测试,A8 build program 枚举依赖图与元数据,A9 依赖声明平台下限,A10
--toolchain; - 插件与 payload:B4 / C3 Setup.exe(Burn),B5 AAB,B6 metal / esbuild 规则;
- 索引:D1 发布六平台版本并改用版本范围。
- 引擎:A4 默认 SONAME,A6 模拟器/模拟机上跑测试,A8 build program 枚举依赖图与元数据,A9 依赖声明平台下限,A10
Context
#622 asked for what a UI framework needs on Android, iOS and the Web; #630 listed what the first six-platform build still hit. Both are answered: 2026.9.13.2, 2026.9.14.1 (#633) and mcpp:plugins 0.9.3 are what HuxerUI now builds on. On every row its mcpp path takes the CMake path's conventions by default:
- the framework is statically linked, except on Android, where it is its own
libhuxerui.soloaded by the Java host; - resources land where the CMake build puts them;
- CMake's own windowless UI smoke runs under
mcpp test.
The work is on three fork PRs, each green on every row: Sunrisepeak/HuxerUI#7 (framework, rule packages, CLI), Sunrisepeak/Lib-Live2D#2 (an ecosystem library) and Sunrisepeak/HuxerUI#8 (templates).
This issue is what remains when that mcpp path is compared with the CMake path item by item: linking, loading, packaging, tests and distributables. Each item gives:
- Measured: the fact, with the version it was measured on;
- Why: why it matters beyond HuxerUI;
- Proposed: a shape;
- CI check: a check that runs on a GitHub-hosted runner.
Android checks use the emulator (KVM on ubuntu-24.04) and iOS checks the simulator (macos-15). No check needs a device, a signing identity or a secret.
Measured with mcpp 2026.9.14.1, mcpp:plugins 0.9.3 and xlings 2026.9.12.1, on a Linux x86_64 host (GCC 16.1.0, xim:android-ndk 30.0.16248370) and in CI, unless an item says otherwise. CMake references point at unchanged upstream files, through the fork at 64264cb.
The only open item of #622 (B4) is carried here as B6, so #622 can be closed in favour of this issue.
| # | Where | What | Priority |
|---|---|---|---|
| A1 | engine | One identity, a different linkage per row: the first spelling is dropped silently |
P1 |
| A2 | engine | A dependency key that names a different identity than its manifest: two packages from one directory | P1 |
| A3 | engine | Shared libraries the graph built are not staged; Mach-O closures are not walked | P1 |
| A4 | engine | No SONAME / install name for a dependency made shared by linkage |
P3 |
| A5 | engine | A test program from several sources; test roots; labels | P2 |
| A6 | engine | Windowless tests on the Android emulator and the iOS simulator | P3 |
| A7 | engine | A payload's recipe-dependency closure is invisible to build programs | P2 |
| A8 | engine | Enumerating the resolved graph, and package metadata, from a build program | P3 |
| A9 | engine | A platform floor a dependency needs (min_api_level, iOS deployment target) |
P3 |
| A10 | engine | --toolchain on run / test / pack |
P3 |
| B1 | plugins | dist-apple carries graph-built dylibs (the plugin-side A3, as dist-apk 0.9.3 did) |
P1 if before A3 |
| B2 | plugins | dist-apple: DMG |
P2 |
| B3 | plugins | dist-apple: a default macOS --format app runner that returns the program's status |
P2 |
| B4 | plugins | dist-wix: a Burn bundle (Setup.exe) |
P3 |
| B5 | plugins | dist-apk: an Android App Bundle |
P3 |
| B6 | plugins | rules-metal, tools-esbuild (carried from #622 B4) |
P3 |
| C1 | xim-pkgindex | A macOS app-bundle runner program | P2 |
| C2 | xim-pkgindex | simctl-run: output lost under --console-pty; exit status unmeasured |
P2 |
| C3 | xim-pkgindex | WiX Burn native SDK payloads | P3 |
| C4 | xim-pkgindex / xlings | Resolving an unmerged recipe from a consumer's CI | P2 |
| D1 | mcpp-index | Six-row huxerui.huxerui and huxerui.live2d, so libraries declare ranges |
P3 |
A. Engine (mcpp)
A1. One identity, a different linkage per row — P1
Measured. HuxerUI's framework must be a shared library on Android and static elsewhere. The Java host loads libhuxerui.so by name, and CMake's _huxerui_select_framework_target refuses a static framework on Android (cmake/HuxerUILibraries.cmake#L14-L28). The application manifest can say this three ways:
| Application manifest | Result on x86_64-linux-android |
|---|---|
[dependencies] huxerui.huxerui = { path = "../../.." } and [target.'cfg(env = "android")'.dependencies] huxerui.huxerui = { path = "../../..", linkage = "shared" } |
Builds, and the Android linkage is dropped silently. There is no libhuxerui.so; the application's .so has no NEEDED libhuxerui.so and defines all 54 Java_org_huxerui_* symbols itself. No diagnostic. |
the same [dependencies] line and [target.'cfg(env = "android")'.dependencies] huxerui.huxerui = { linkage = "shared" } |
Refused, with a message about something else (below). |
no [dependencies] line; cfg(env = "android") with linkage = "shared" plus cfg(not(env = "android")) without it |
Works: bin/libhuxerui.so, the application has NEEDED libhuxerui.so and RUNPATH $ORIGIN. This is what HuxerUI ships. |
The second spelling is rejected like this:
warning: [dependencies] huxerui.huxerui.linkage = 'shared' is not a version RANGE (version: not a number ('shared')), so it is used as an exact index key: …
error: dependency 'huxerui.huxerui.linkage': no package found for exact selector
tried: (huxerui.huxerui, linkage)
Why.
- The spelling that works needs two complementary predicates for one dependency, in every application and every template.
- The spelling people try first succeeds, and it produces the wrong artefact on exactly the row where linkage matters. An APK built from it carries no
libhuxerui.sofor the Java host'sSystem.loadLibrary("huxerui"). - Any library with a JNI or
dlopencontract on one platform is in the same position.
Proposed.
- Merge conditional linkage. When a conditional declaration names an identity that is also declared unconditionally with the same reference, merge them: the conditional
linkageapplies on the rows its predicate selects. If merging is unwanted, refuse and name both tables. Never drop it silently. - Accept a modifier-only table. In a conditional dependency table, read
{ linkage = "shared" }(noversion,pathorgit) as "the declaration above, with this linkage on these rows". If that shape stays refused, say so, instead of parsinglinkageas a package name. - Let a dependency state the need. Follow the
requires_abishape (docs/22):[target.'cfg(env = "android")'] requires_linkage = "shared". The root still decides. A build whose resolved linkage differs is refused before compiling, naming the package and the selector.
CI check (ubuntu-24.04, xim:android-ndk).
- Fixture: an application plus one
libdependency. - Spellings 1 and 2: after
mcpp build --target x86_64-linux-android,bin/lib<dep>.soexists andllvm-readelf -dof the application's.soshowsNEEDED lib<dep>.so. The host build has nolib<dep>.so. - Proposal 3: the dependency states
requires_linkageand the root does not; the build exits non-zero with the named refusal.
A2. A dependency key that names a different identity than its manifest — P1
Measured. In mcpp/examples/03-library, both edges point at the same directory, whose manifest declares namespace = "huxerui", name = "huxerui":
- the application declares
huxerui.huxerui = { path = "../../.." }(per row, as in A1); - its component library declared
huxerui = { path = "../../../.." }, which normalises to(mcpplibs, huxerui)(docs/05).
The graph therefore holds two packages built from one directory, and the scanner fails:
error: scanner errors:
…/HuxerUI/modules/huxerui.cppm: module 'huxerui' already provided by …/HuxerUI/modules/huxerui.cppm
This happened on the macOS and Windows rows in CI (run 34772646907) and reproduces on Linux. The message names neither dependency edge; the fix (spell the library's key huxerui.huxerui) was found by reading every manifest in the graph.
Why. Every ecosystem library declares the framework it builds on. One key written without the namespace, in any library of the graph, breaks every application that also names the framework, and the diagnostic names a module rather than a manifest line.
Proposed.
- Check the key against the manifest. Read a
pathorgitdependency's identity from the manifest it resolves to. A key that normalises to a different identity is reported, for example asdependency/identity-mismatch: the key, what it normalises to, and what the manifest declares. Then either refuse, or take the manifest's identity; pick one rule and state it. - Report duplicates before scanning. Two identities that resolve to one canonical directory, or to one git URL and commit, are reported before scanning, naming both requesters.
CI check (ubuntu-24.04).
- Mismatch fixture: the application declares
ns.dep = { path }; its library declaresdep = { path }for the same directory, whose manifest isns.dep. The build shows the named diagnostic, not a scanner error. - Matching keys: the same fixture builds.
A3. Shared libraries the graph built are not staged; Mach-O closures are not walked — P1
Measured.
- Android.
mcpp pack --target x86_64-linux-android --format apkfor an application whose framework islinkage = "shared":- The stage manifest reads
closure = walked. - It lists
lib/libhuxerui_example_import.soand the deployed resource files, and nolib/libhuxerui.so, although the application's.sohasNEEDED libhuxerui.soand the build producedbin/libhuxerui.so. dist-apk0.9.3 now walksNEEDEDitself withllvm-readelf(0.9.3: dist-apk carries the graph's shared libraries, and the manifest template gains the version tokens mcpp-plugins#23), so the APK carries the library. The engine's staged tree still claims a walked closure it does not carry.
- The stage manifest reads
- Mach-O.
pack::runstep 4 reports a Mach-O program asclosure = not-walkedon every host.- The design note for Six platforms landed: the engine gaps a framework and its ecosystem library still hit (dependency identity, Mach-O staging, iOS runtime, min_api_level, host-tool cache, xpkg descriptors) #630 (§4) records that the reader (
macho_needed,resolve_macho_names) landed, and that bundling waits for theLC_RPATHmeasurement. - A macOS application with a shared dependency therefore packs a
.appthat does not contain the dylib. HuxerUI's macOS default is static, as CMake's is, so this blocks the non-default shared mode.
- Related: host tool 链接 shared 依赖后无法运行:.so 未被 stage 到工具旁边(且会被 subos 同名库悄悄顶上) #535, a host tool's shared dependency is not staged beside it.
What the CMake path does. _huxerui_deploy_runtime (cmake/HuxerUIRuntimeDependencies.cmake#L640) runs at install time:
file(GET_RUNTIME_DEPENDENCIES)finds the dependencies.- Dylibs outside
/System/Library/and/usr/lib/are copied intoContents/Frameworks. install_name_toolrewrites names to@rpath/…, deletes the old rpaths, and adds@executable_path/../Frameworksto the executable and@loader_path/…to libraries.codesign --force --sign -runs on each file and on the bundle.codesign --verify --deep --strictchecks the result.
The same function copies DLLs beside the executable on Windows and fills lib/ with patchelf on Linux.
Proposed.
- Stage what the graph built. Stage every shared library the graph built for the artefact, on every row, however the closure is discovered: those libraries are declared, not discovered.
- Walk with the static readers. Use
needed_names(ELF, PE and Mach-O) instead of running the program. Cross rows then get a real walk, andclosure = walkedmeans complete. - Mach-O specifics.
- Shared targets get the install name
@rpath/lib<name>.dylibby default. - A program packed into a bundle gets
LC_RPATH @executable_path/../Frameworks. Setting this at link time, as the §4 note suggests, may make a load-command editor unnecessary. - Any rewrite of a Mach-O file is followed by an ad-hoc re-sign, because arm64 refuses an invalidated signature.
- Shared targets get the install name
CI check.
ubuntu-24.04: pack A1's fixture forx86_64-linux-android; the stage manifest listslib/lib<dep>.so.macos-15: an application with alinkage = "shared"dependency, packed withmcpp pack --format app:Contents/Frameworks/lib<dep>.dylibexists;otool -L Contents/MacOS/<exe>names@rpath/lib<dep>.dylib, andotool -lshowsLC_RPATH @executable_path/../Frameworks;codesign --verify --deep --strictpasses, andContents/MacOS/<exe>exits 0.
A4. No SONAME / install name for a dependency made shared by linkage — P3
Measured. libhuxerui.so, produced by linkage = "shared" for x86_64-linux-android, has no DT_SONAME. The application records NEEDED libhuxerui.so from the file name, plus RUNPATH $ORIGIN. It loads on Android. [targets.<n>] soname exists, but a dependency made shared by its consumer's linkage has nowhere to state one.
Proposed. A target resolved Shared through linkage defaults to the SONAME lib<target>.so, and to the Mach-O install name from A3. [targets.<n>] soname still overrides it.
CI check.
ubuntu-24.04:llvm-readelf -d bin/lib<dep>.soshowsSONAME lib<dep>.so.macos-15:otool -Dprints@rpath/lib<dep>.dylib.
A5. A test program from several sources; test roots; labels — P2
Measured.
-
mcpp's model. In docs/08, a test is
tests/**/*.cpp, and one.cppmakes one program.[build] sourceslink into every test program. -
Why HuxerUI needs a separate package. Its tests live in
mcpp/huxerui-tests, because the repository'stests/belongs to CMake: 111 files, some driven by CMake or Python, some linking the CLI, some per platform. -
CMake's shape. CMake builds one executable per suite from a list of sources, with labels:
HuxerUIUnitTests(common unit, 19 files);HuxerUIRuntimeTests(common runtime, 41 files);HuxerUIUiTestsandHuxerUILibraryTests;- per-platform suites labelled
platformplusnativeorportable.
The release workflow selects them with
ctest -LE platform,-L portableand-L native. -
Measured today. The 41 runtime files compile and pass unmodified in the mcpp package:
All tests passed (41014 assertions in 1091 test cases) test result ok. 2 passed; 0 failed; finished in 82.14s (build 72.52s + run 3.38s)They only fit by being added to
[build] sources, which puts them into the same program as the unit suite, and into the smoke program too.
Why.
- One process per suite, as CMake has, so a suite's global state cannot leak into another suite.
- Selection by label in CI.
- Not linking every test source into every test program.
- Any project whose
tests/directory already belongs to another build system has the same problem.
Proposed.
[test]
roots = ["tests-mcpp"] # or: discover = false
[[test]] # a named program built from several sources
name = "runtime"
sources = ["../../tests/runtime/*.cpp", "../../tests/support/builtin_resources.cpp"]
defines = ["…"]
labels = ["common", "runtime"]
timeout = 120Add mcpp test --label <l> and --exclude-label <l>, and put labels into --message-format json.
CI check (ubuntu-24.04). A fixture with two [[test]] programs whose sources do not overlap:
- each defines a marker symbol, and
nmfinds each marker only in its own binary; mcpp test --label aruns one program;- the JSON records carry the labels.
A6. Windowless tests on the Android emulator and the iOS simulator — P3, verification first
Measured. Both runners already accept a bare executable:
adb-runpushes it to/data/local/tmp, runs it with oneadb shell, and parses the status back;simctl-runruns it withsimctl spawn.
mcpp test --target x86_64-linux-android and --target aarch64-ios-sim should therefore run windowless test programs, but this is unmeasured. The likely gap is the files a test reads: HuxerUI's tests compile a resource package's host-absolute path in (HUXERUI_TEST_BUILTIN_RESOURCE_PACKAGE), and no runner transfers it. CMake has platform-specific carriers for the smoke (an instrumentation .so on Android, an XCTest bundle on iOS). mcpp could cover more of the suite with bare executables.
Proposed. Measure it and document the result. If files are the gap, let a test's deployed files (mcpp::deploy) travel with it, found through a relative path (the working directory, or MCPP_TEST_DATA_DIR).
CI check. A fixture test reads a deployed file and exits 0 or 1 on its content:
ubuntu-24.04: on an x86_64 API 34 emulator (KVM),mcpp test --target x86_64-linux-androidpasses.macos-15:mcpp test --target aarch64-ios-simpasses.
A7. A payload's recipe-dependency closure is invisible to build programs — P2
Measured.
-
What HuxerUI asks for. Its Linux build asks pkg-config for
gtk4,epoxy,gio-2.0andlibsoup-3.0. -
What happens with only those declared. Declaring just the four direct payloads (
xim:gtk4,xim:libepoxy,xim:libsoup,xim:glib) is not enough. The 33 payloads that make up their.pcclosure are invisible to the build program, even though they are installed on the machine (xpkg_diranswers""), and pkg-config fails:Package 'pango', required by 'gtk4', not found Package 'pangocairo', required by 'gtk4', not found Package 'gdk-pixbuf-2.0', required by 'gtk4', not found Package 'cairo', required by 'gtk4', not found -
The workaround. HuxerUI declares and exactly pins all 37 payloads in
mcpp/huxerui-build-rules-gtk/mcpp.toml, and updates that closure by hand whenever a recipe changes.
Why. Any package that consumes a pkg-config stack through xim payloads (GTK, GStreamer, FFmpeg) has to copy its closure into its own manifest.
Proposed.
- Answer for the closure.
xpkg_diranswers for the recipe-dependency closure of a declared payload, at the versions actually installed. Those versions are recorded inmcpp.lock, so two machines still build the same binary. - Expose pkg-config directories. The build program receives the closure's
lib/pkgconfigandshare/pkgconfigdirectories, asmcpp::pkg_config_path()orMCPP_PKG_CONFIG_PATH. - Related: feat/RFC: [system_deps] —— 经 pkg-config 集成宿主系统库 #493 is the host-system counterpart.
CI check (ubuntu-24.04). A fixture declares only xim:gtk4:
- its build program runs
pkg-config --cflags --libs gtk4against the provided path and exits 0; xpkg_dir("xim", "pango")is non-empty;mcpp.lockrecords pango's version.
A8. Enumerating the resolved graph, and package metadata, from a build program — P3
Measured.
mcpp::dep_dir/MCPP_DEP_<NAME>_DIRanswer by name, for each declared dependency.- A build program cannot enumerate the resolved graph, transitive dependencies included, and cannot read anything a dependency states about itself.
- CMake's
huxerui_use_librarymerges each HuxerUI library's resource package into the application's. On the mcpp path, an application cannot find which of its transitive dependencies carry resources, so a library's resources stay unmerged. Lib-Live2D's string catalogue is today's case.
Proposed.
- Enumerate the graph.
mcpp::dependencies()returns, for each package of the resolved graph: identity, version, kind, resolved linkage, manifest directory and requesters. - Read package metadata. A
[package.metadata.<tool>]table is readable from consumers' build programs (for examplemcpp::dep_metadata(identity, "<tool>.<key>")), and editing it re-runs those build programs.
CI check (ubuntu-24.04). Fixture: application → lib A → lib B, where B declares [package.metadata.demo] resources = "res".
- The application's build program prints the graph and B's value, and the check asserts both.
- Editing B's metadata re-runs the application's build program.
A9. A platform floor a dependency needs — P3
Measured. min_api_level ([target.<triple>]) and ios_deployment_target ([build]) belong to the artefact, so only the root states them. HuxerUI's manifests state API 23 and iOS 15.0. Every application and template restates min_api_level = 23 on both Android rows and ios_deployment_target = "15.0", because a dependency has no way to say what it needs.
Proposed. Follow the requires_abi shape:
[target.'cfg(env = "android")']
requires_platform = { api_level = 23 }
[target.'cfg(os = "ios")']
requires_platform = { deployment_target = "15.0" }A root whose resolved floor is lower is refused before compiling, naming the package. An unset iOS floor resolves to the SDK version (#633) and is compared as that.
CI check.
ubuntu-24.04: the dependency requires 23 and the root states 21, so the build is refused by name. With the root at 24, the output showsTarget x86_64-linux-android → x86_64-unknown-linux-android24.macos-15: the same foraarch64-ios-sim.
A10. --toolchain on run / test / pack — P3
Measured. mcpp build accepts --toolchain; mcpp run, mcpp test and mcpp pack answer error: unknown option: --toolchain. HuxerUI's clang CI leg therefore switches the whole job with mcpp toolchain default llvm@22.1.8.
Proposed. Accept --toolchain on run, test and pack, meaning what it means for build.
CI check (ubuntu-24.04). mcpp test --toolchain llvm@22.1.8 and mcpp pack --toolchain llvm@22.1.8 compile with clang, as mcpp why toolchain or the compile lines show, while the default toolchain is gcc.
B. mcpp:plugins
B1. dist-apple carries graph-built dylibs — P1 if it lands before A3
The plugin-side counterpart of A3, as dist-apk 0.9.3 did for Android:
- Read
LC_LOAD_DYLIBwithllvm-otool -Lorllvm-objdump --macho --dylibs-used. - Copy the dylibs the graph built from
bin/intoContents/Frameworks/(on iOS,Frameworks/). - Add
@executable_path/../Frameworks(on iOS,@executable_path/Frameworks) withllvm-install-name-tool. - Re-sign ad hoc, because arm64 refuses an invalidated signature.
CI check. The macos-15 check of A3, and the same for aarch64-ios-sim with simctl-run launching the bundle.
B2. dist-apple: DMG — P2
What the CMake path does. huxerui package macos (tools/huxerui_cli/platform/macos.cpp#L86-L101):
- installs the component into a staging directory;
- adds an
Applications → /Applicationssymlink beside the bundle; - runs
hdiutil create -volname <name> -srcfolder <staging> -format UDZO -ov <target>-<version>.dmg.
On the mcpp path dist-apple stops at the .app.
Proposed. mcpp pack --format dmg on the macOS row: the .app the member already produces, plus the symlink, through hdiutil from the base macOS install (as ditto and codesign are today). Options for the volume name, and later a background image.
CI check (macos-15).
hdiutil verify <dmg>passes.- After
hdiutil attach -nobrowse -readonly -mountpoint "$RUNNER_TEMP/m" <dmg>,<Name>.app/Contents/MacOS/<exe>exists andApplicationsis a symlink to/Applications. hdiutil detachsucceeds.
B3. dist-apple: a default macOS --format app runner that returns the program's status — P2
Measured.
- Why HuxerUI declares a runner. It resolves resources through
NSBundle, somcpp run --format appmust run the bundle; HuxerUI's manifests declare[target.aarch64-macos] runner = ["open", "-W"]. - Confirmed in CI. mcpp hands the
.apppath to that argv: a stubopenplaced first onPATHreceived-Wand/Users/runner/…/huxerui_example_import.app(run 34775426211, step "Hand the bundle to the manifest's runner"). - The problem.
openreturns its own status, not the program's, and the program's stdout and stderr do not reach the terminal.mcpp run --format appcannot fail when the application does.
Proposed. dist-apple supplies the default runner for --format app on *-macos through mcpp::runner, which reaches the consumer (docs/41). It names a program (C1) that runs the bundle's CFBundleExecutable in the foreground, with stdio attached, and returns its status. Applications then drop the table; an override stays possible.
CI check (macos-15). A fixture .app whose program prints 1-2-3 and exits 7, with no runner in its manifest: mcpp run --format app prints 1-2-3 and exits 7.
B4. dist-wix: a Burn bundle (Setup.exe) — P3
What the CMake path does. huxerui_add_windows_installer() (cmake/HuxerUIWindowsInstaller.cmake#L116):
- builds
platform/windows/windows_installer.cpp, a WiX Burn bootstrapper application whose interface is written with HuxerUI; - links it against the WiX native SDK, which it restores from NuGet with pinned sha256 (C3);
- runs
wix buildon a bundle that chains the MSI, producing<target>-Setup-<version>.exe.
wix.exe needs .NET; CMake probes for Microsoft.NETCore.App 6.0 or newer.
Measured on the mcpp side. dist-wix produces the MSI. xim:wix 5.0.2 already carries x64/burn.exe beside wix.exe (dist/wix.cppm, wix_payload_exe).
Proposed. mcpp pack --format setup (or bundle) builds a Burn bundle that chains the MSI dist-wix builds:
- by default with the standard bootstrapper (
WixToolset.BootstrapperApplications.wixext); - optionally with a custom bootstrapper program the package itself builds (
options.bootstrapper = "<target>", reached as${mcpp.target_file:<target>}).
CI check (windows-2022).
Setup.exe /quiet /norestart /log setup.logexits 0, and the installed executable is present.Setup.exe /uninstall /quietexits 0, and the executable is removed.- The same passes with a minimal custom bootstrapper fixture (C3).
B5. dist-apk: an Android App Bundle — P3
Measured. dist-apk builds and signs an APK: the published debug key by default, or a release keystore given as a package with an alias and a password environment variable. Google Play takes an Android App Bundle for new applications. An application's Gradle shell produces one with bundleRelease; mcpp pack has no aab.
Proposed. mcpp pack --format aab:
aapt2 link --proto-formatbuilds the resources;bundletool build-bundlepacks the module zip (manifest/,dex/,lib/<abi>/,assets/,res/,resources.pb);jarsignersigns it with the keystore options the APK already takes;- several
--targetvalues make one bundle, as they make one universal APK today.
This needs a bundletool payload; it is a jar, run by the xim:jdk-* payload the member already uses.
CI check (ubuntu-24.04).
bundletool validate --bundle app.aabpasses.bundletool build-apks --mode=universalproducesuniversal.apk, which containslib/x86_64/lib<app>.so.bundletool dump manifestshows the application id.
B6. rules-metal, tools-esbuild (carried from #622 B4) — P3
Measured. Lib-Live2D's mcpp/cubism package writes its Metal shader edges by hand: .metal → .air → .metallib through xcrun, 477 outputs checked in its CI. Its build program also bundles the TypeScript Web bridge with esbuild from a payload. Both are the same kind of rule as rules-spirv.
Proposed.
rules-metal: actions with depfiles, choosing the macOS, iOS or iOS-simulator SDK from the target.tools-esbuild: one action with glob inputs and a declared output.
CI check.
macos-15: two.metalfiles produce one.metallibeach foraarch64-macosand foraarch64-ios-sim; touching one.metalrebuilds only its output.ubuntu-24.04: a TypeScript entry produces a bundled.jsthat exports the entry's symbol.
C. xim-pkgindex (payloads and runner programs)
C1. A macOS app-bundle runner program — P2
For B3: macapp-run <App.app> [args…].
- It reads
CFBundleExecutablefromContents/Info.plist. - It runs
<App.app>/Contents/MacOS/<exe>in the foreground with stdio attached;NSBundlestill resolves the bundle. - It returns the program's status.
It lives in apple-simulator-tools or in a new apple-app-tools.
CI check (macos-15). The B3 check, plus a missing or non-bundle operand producing a named error and exit code 2.
C2. simctl-run: output lost under --console-pty; exit status unmeasured — P2
Measured.
- Output lost. In the CI of 0.9.3: dist-apk carries the graph's shared libraries, and the manifest template gains the version tokens mcpp-plugins#23 (run 34770579266), attempts 1 and 2 failed on the job's iOS-simulator step.
simctl-runprinted only the launch lineios-app-consumer: 29750, not the program's1-2-3. Attempt 3 passed with no change. - Exit status unknown. The recipe (
pkgs/a/apple-simulator-tools.lua, 0.2.0) records that whethersimctl launch --console-ptyreturns the launched application's status, or only simctl's, is unmeasured.
Proposed.
- Measure
--console(pipes) and--stdout=<file> --stderr=<file>followed by waiting for the process to exit, against--console-pty. - Keep the one that never loses output, print the output, and return the application's own status.
- Record the measurement in the recipe.
CI check (macos-15).
- The plugins' iOS fixture runs 20 times in a matrix, and every run prints
1-2-3. - A fixture that exits 7 makes
mcpp run --target aarch64-ios-sim --format appexit 7.
C3. WiX Burn native SDK payloads — P3
For B4 with a custom bootstrapper, two packages as HuxerUI's CMake path restores them (cmake/HuxerUIWindowsInstaller.cmake#L40-L112):
WixToolset.BootstrapperApplicationApi:BootstrapperApplication.h,build/native/v14/x64/balutil.lib,runtimes/win-x64/native/mbanative.dll;WixToolset.DUtil:dutil.h,build/native/v14/x64/dutil.lib.
Also state whether xim:wix carries the .NET runtime wix.exe needs, or which payload does.
Open question for the check. Can mcpp's Windows toolchain (clang, lld-link) link the MSVC-built v14 static libraries? If not, windows = "msvc@system" is the path (#604 is closed).
CI check (windows-2022). A minimal bootstrapper program that calls one balutil and one dutil function compiles and links under mcpp with the payloads, and B4's bundle builds with it.
C4. Resolving an unmerged recipe from a consumer's CI — P2
Measured.
- The documented limitation. docs/32 says: "A payload's own CI cannot verify that a consumer resolves it".
- The workaround used. HuxerUI's and Lib-Live2D's CI needed recipes that were still in review (
android-platform36-r2,glew,esbuild). They bridged them by copying the.luainto~/.mcpp/registry/data/xim-pkgindex/pkgs/<x>/and adding an entry to.xlings-index-cache.json, whichmcpp index updatewipes. --add-xpkgdid not help.xlings config --add-xpkgregistered the recipe underlocal:, which does not satisfy anxim:address (measured on Sept 12). xlings 2026.9.12.1 also hasconfig --index-repo <NS:URL>, andMCPP_HOMEkeepsxim-index-repos/andxim-pkgindex-local/; whether any of them can serve anxim:address from a PR branch was not evaluated.
Proposed. One documented and CI-tested way for an mcpp build to resolve xim: recipes from an unmerged xim-pkgindex branch:
- either an index overlay under
MCPP_HOME, or xlings'--index-repohonoured by mcpp; - it survives
mcpp index update; mcpp why depsreports it as coming from the overlay.
CI check (xim-pkgindex PR workflow, ubuntu-24.04).
- A recipe added on the PR branch resolves through the overlay, and a consumer fixture that declares it builds.
mcpp index updatefollowed bymcpp buildstill resolves it.- Without the overlay, the build refuses and names the index revision.
D. mcpp-index
D1. Six-row huxerui.huxerui and huxerui.live2d, so libraries declare ranges — P3, after the upstream release
State.
huxerui.huxerui0.3.0 is in mcpp-index for Linux, Windows and macOS (Android, iOS and Web for a UI framework on mcpp: the engine, plugin and payload infrastructure it needs (tracking) #622).- The six-row manifests are on the fork branches (build: HuxerUI and its applications on six platforms with mcpp, beside CMake Sunrisepeak/HuxerUI#7, build: Lib-Live2D and its preview on six platforms with mcpp, beside CMake Sunrisepeak/Lib-Live2D#2).
- The ecosystem library and the
live2dtemplate pin HuxerUI by git revision, so every framework change means re-pinning a chain of commits. - docs/05 already AND-combines two
versionconstraints for one identity.
Proposed. Once HuxerUI releases its six-row manifest:
- publish the next
huxerui.huxeruiwith sixplatforms, and publishhuxerui.live2d; - libraries declare
huxerui.huxerui = "^0.3", and templates declare published versions.
CI check (mcpp-index PR workflow).
mcpp xpkg parsepasses on both descriptors.- A consumer fixture has
huxerui.live2d's^0.3requirement and an application pin of0.3.x. It builds for the host, forx86_64-linux-androidand forwasm32-emscripten, andmcpp.lockrecords exactly onehuxerui.huxerui.
E. Not asked of mcpp (HuxerUI's side, recorded for the boundary)
- Android static framework. HuxerUI needs a Java-side switch, because
HuxerUIView's static initialiser loadslibhuxerui.sounconditionally. Measured: with the framework static, all 54Java_org_huxerui_*JNI functions are defined and exported by the application's.so. Beyond A1, nothing is needed from the engine. - Runner tables.
huxerui.rulescan supply the Android and iOS-simulator runners throughmcpp::runner(docs/41), which removes the[target.*] runnertables from application manifests. macOS waits for B3 and C1. - Test suites.
tests/runtimemoves intomcpp/huxerui-tests; measured to pass, and cleaner with A5.tests/runtime/ui_testing.cppneeds a resource-compiler fixture.tests/runtime/libraries.cppneedstests/libraries/root_serviceas an mcpp package.
- Leftover workaround. The c++23 workaround in HuxerUI's examples was for clang on Windows hardcodes
importStdMinLevel = 23instead of probing the MSVC STL #603, which is closed, and can be removed. - Installer UI. The installer interface program for B4 is HuxerUI's own to build.
F. CI checks at a glance
| Item | Runner | Fixture | Passes when |
|---|---|---|---|
| A1 | ubuntu-24.04 + android-ndk | app + lib, three spellings | NEEDED lib<dep>.so on Android for spellings 1 and 2; named refusal for requires_linkage |
| A2 | ubuntu-24.04 | app + lib, mismatched keys for one directory | named identity diagnostic before scanning |
| A3 | ubuntu-24.04, macos-15 | app + shared lib | stage manifest lists lib<dep>.so; .app has Frameworks/lib<dep>.dylib, rpath, valid signature, runs |
| A4 | ubuntu-24.04, macos-15 | shared lib via linkage |
SONAME / @rpath install name |
| A5 | ubuntu-24.04 | two [[test]] programs |
disjoint symbols; --label selects; labels in JSON |
| A6 | ubuntu-24.04 (emulator), macos-15 (simulator) | test reading a deployed file | passes on both |
| A7 | ubuntu-24.04 | only xim:gtk4 declared |
pkg-config gtk4 succeeds; lock lists the closure |
| A8 | ubuntu-24.04 | app → A → B with metadata | graph and metadata printed; metadata edit re-runs |
| A9 | ubuntu-24.04, macos-15 | lib with requires_platform |
refusal below the floor; triple at or above it |
| A10 | ubuntu-24.04 | any package | test and pack honour --toolchain |
| B1 | macos-15 | as A3 | as A3, plus iOS simulator launch |
| B2 | macos-15 | any app | hdiutil verify; mounted layout |
| B3 + C1 | macos-15 | app exits 7, prints 1-2-3 |
mcpp run --format app exits 7 and prints |
| B4 + C3 | windows-2022 | app + MSI (+ minimal bootstrapper) | silent install and uninstall |
| B5 | ubuntu-24.04 | any app | bundletool validate; universal APK contents |
| B6 | macos-15, ubuntu-24.04 | .metal sources; TypeScript entry |
outputs exist; incremental rebuild |
| C2 | macos-15 | plugins' iOS fixture ×20 | output every time; exit status propagated |
| C4 | ubuntu-24.04 | recipe on a PR branch | resolves through the overlay, survives index update |
| D1 | ubuntu-24.04 | huxerui.live2d on ^0.3 |
one huxerui.huxerui in mcpp.lock on three rows |
G. Suggested order
- A1, A2. Every application and ecosystem library hits them. The engine changes are small, and the fixtures run on ubuntu.
- A3, or B1 first. The staged tree becomes truthful on Android, and macOS gains a shared mode.
- B3 + C1, C2.
mcpp runreports the program's own status on macOS and on the iOS simulator, which makes CI results trustworthy. - A5, A7, C4. Test parity with CMake, the 37-line GTK table, and testing recipe PRs.
- B2, then C3 + B4, then B5. The distributables the CMake path still owns alone.
- A4, A6, A8, A9, A10, B6, D1.
H. Evidence
- Fork PRs: build: HuxerUI and its applications on six platforms with mcpp, beside CMake Sunrisepeak/HuxerUI#7 (framework, rule packages, CLI, CI on every row), build: Lib-Live2D and its preview on six platforms with mcpp, beside CMake Sunrisepeak/Lib-Live2D#2 (the ecosystem library), feat(templates): a Live2D application on every row, through one dependency Sunrisepeak/HuxerUI#8 (templates).
- CI runs:
- A2 duplicate module: 34772646907;
- B3 runner hand-over: 34775426211;
- C2 simulator output: mcpp-plugins 34770579266, attempts 1 and 2.
- Ecosystem changes: 0.9.3: dist-apk carries the graph's shared libraries, and the manifest template gains the version tokens mcpp-plugins#23 (
dist-apk0.9.3NEEDEDwalk and version tokens), mcpp:plugins 0.9.3: dist-apk carries the graph's shared libraries, version tokens mcpplibs/mcpp-index#412. - CMake references (unchanged upstream files):
- Related:
- Android, iOS and Web for a UI framework on mcpp: the engine, plugin and payload infrastructure it needs (tracking) #622: its remaining item is carried as B6, so it can be closed;
- Six platforms landed: the engine gaps a framework and its ecosystem library still hit (dependency identity, Mach-O staging, iOS runtime, min_api_level, host-tool cache, xpkg descriptors) #630: closed;
- host tool 链接 shared 依赖后无法运行:.so 未被 stage 到工具旁边(且会被 subos 同名库悄悄顶上) #535: host-tool shared dependencies are not staged;
- feat/RFC: [system_deps] —— 经 pkg-config 集成宿主系统库 #493: host system libraries through pkg-config;
- clang on Windows hardcodes
importStdMinLevel = 23instead of probing the MSVC STL #603: closed.
Activity
ci(temporary): run 2 of the #634 measurements
ci(temporary): run 3 of the #634 measurements
ci(temporary): run 4 of the #634 measurements
The static C++ runtime is located by asking the driver for the effect…
run, test and pack declare --toolchain (#634 A10)
[test] discover; every runner receives MCPP_RUNTIME_FILES; a format i…
docs: [test] discover, MCPP_RUNTIME_FILES, format-named runners, --to…
test(unit): [test] discover names, exclusions and refusals; the archi…
Merge feat/634-run: driver-queried static runtime, runtime files for …
fix(runner): a runner a dependency supplies and the project's build p…
ci: the Android rows run their e2e tests and mcpp test on an API 34 e…
docs(design): #634 revision 3 folded into the triage record, and the …
docs(changelog): the engine half of #634
fix(config): only a [index.repos.<name>] table is reconciled into an …
The engine half of #634: conditional dependency replacement, manifest…
mcpp's xlings invocations do not inherit XLINGS_ACTIVE_SUBOS (2026.9.…
Sunrisepeak commentedon Sep 14, 2026
中文摘要
二十一项已按归属全部处理完毕并发布:引擎 mcpp 2026.9.14.2(mcpp#637)及补丁 2026.9.14.3(mcpp#638)、官方插件 mcpp:plugins 0.10.0(mcpp-plugins#24)、载荷 xim-pkgindex#838、索引 mcpplibs/mcpp-index#426。分类与决定见设计记录
.agents/docs/2026-09-14-634-cmake-parity-items-by-home.md,计划与台账见.agents/docs/2026-09-14-634-implementation-plan.md。[target.<sel>.targets.<n>] kind,不新增requires_linkage;A5 用[test] discover加「多源套件即包」,不做[[test]]/labels;A7 实测全新 home 的 SubOS 视图已能解析 gtk4(只缺 libxml2 的.pc,已补),37 条 pin 不需要;A8 不改引擎;A9 由引擎陈述平台下限事实、依赖用version-floor拒绝,不抬高下限;C4 不改 xlings。libc++_shared.so(模拟器上无一能加载);rpath 中@executable_path被锚定到包目录;simctl launch永远返回 0;dist-apk第二次打包丢依赖库、多 ABI 失败;嵌套测试加载不到图构建的共享库;依赖与根构建程序同时提供同名 runner 时拼成一条 argv;在执行过xlings subos use的 shell 中 mcpp 的 registry 把工具与载荷装进另一个 subos(沙箱验证中发现,2026.9.14.3 修复)。What landed, per item
Each item was first located in the code and its premise measured (local probes, and the temporary measurement PR mcpp#635 on macos-15, an API 34 emulator and windows-2022); the decisions and every reading are in
.agents/docs/2026-09-14-634-cmake-parity-items-by-home.md, and the task ledger with the test that closes each task is.agents/docs/2026-09-14-634-implementation-plan.md. Routing followed one rule: the engine takes general capabilities, the official plugins take distribution formats and build rules, payloads take runner programs.A. Engine (mcpp 2026.9.14.2, mcpp#637; patch 2026.9.14.3, mcpp#638)
[target.<sel>.dependencies]declaration replaces the unconditional declaration of the same identity on its rows (the rule every conditional scalar already follows; no manifest among 509 scanned declared a key in both tables). A package states its form per row with[target.<sel>.targets.<n>] kind = "shared"; a consumer'slinkage = "static"against it is warned naming that line, and--strictrefuses. The modifier-only spellinghuxerui.huxerui = { linkage = "shared" }is reported with the dependency restated with its source. A misspelled[target.<sel>]sub-table is reported. Notrequires_linkage: every otherrequires_*key refuses, andkind = "shared"already selects the shared form.[target.'cfg(env = "android")'.targets.huxerui] kind = "shared"once; applications keep one unconditional dependency line, or write the conditional table with the source when an application rather than the framework decides.path/gitdependency takes the identity its manifest declares, with one warning per declaring edge naming the requester, the key and both identities; one directory reached asfwandhuxdemo.fwis one package compiled once. Two identities over one source (a manifest without a namespace) are refused before scanning; the scanner's duplicate-provider message names both packages.huxerui-build-rules,huxerui-tools,huxerui-testscan statehuxerui.*; until then they build with the warning.mcpp packreads the Android and Mach-O closures from the files. Android stageslib/(perlib/<abi>/with several--target), graph libraries andlibc++_shared.soincluded; Mach-O stages dylibs beside the program, with no load-command edit.<tree>.stage-manifestgains `needs<pathlines;walkedis written only for a complete closure, anddir/tarrefuse an incomplete one naming the names. rpath entries starting with@executable_path,@loader_pathor@rpath` are no longer anchored to the package directory (a defect found while measuring B1).sonamerecords its file name as SONAME (bionic enforces one from API 23); a-sonameinldflagsstill wins. The Mach-O half was already@rpath/<file>.[test] discover = [globs](the[build] sourcesvocabulary,!exclusions,[]disables); names stay path-relative. Not[[test]]with exclusive sources and labels: the target model compiles objects once and links them into every target, and sends a divergent program to a workspace member, so a suite built from several sources is a package selected withmcpp test -p.discover = ["checks/**/*.cpp"]for atests/another build owns; one package per multi-source suite.libc++_shared.so, and the device has none. The static runtime is now located by asking the driver for the effective target (the NDK'slibc++.alinker script's archives also reach--exclude-libs: 161 exported dynamic symbols became 4). Every runner ofmcpp runandmcpp testreceivesMCPP_RUNTIME_FILES, a TAB-separated list of the deployed files and linked shared libraries, andadb-runpushes them beside the program. The iOS simulator reads the host filesystem and needed nothing.-static-libstdc++from the Android test rows.ci-linux-e2ejobandroid(API 34 emulator:runsandreads_relativepass)gtk4completely; onlylibxml-2.0.pcwas missing (the recipe had no pkg-config handling, fixed in xim-pkgindex#838). The 37 pins are unnecessary. Build programs getmcpp::pkg_config_libdir(). The sandbox verification found that inside a shell that ranxlings subos use, mcpp's registry installed payloads into a SubOS of the shell's name, so the view the accessor names stayed empty; 2026.9.14.3 fixes it (mcpp#638).PKG_CONFIG_LIBDIR=<mcpp::pkg_config_libdir()> pkg-config gtk4.mcpp::deployresources already reach the application from two levels down, andmcpp why depsnow prints the resolved graph (below). A graph API for build programs waits for a need transitive deploy cannot answer.android.api-level,ios.deployment-targetandmacos.deployment-targetas facts; a dependency writes the ordinary[[runtime.requirements]] kind = "version-floor"against one and is refused before compiling, naming the key that set the value ("this build targets 21",set by: [target.x86_64-linux-android] min_api_level). Not raising the floor: it is already in the compiler's--targetwhen the graph is walked, and an application's floor is its own decision.value = "android.api-level >= 23"in the framework.run,testandpackaccept--toolchain; a refused value is named as the command line's.--toolchain llvm@22.1.8instead ofMCPP_TOOLCHAIN.resolution.jsonrecordsgraph(each package, each request's key and declaring table, a library's link form and why) andmcpp why depsprints it; path dependencies, whichmcpp.lockdoes not record, are listed.B.
mcpp:plugins0.10.0 (mcpp-plugins#24)dist-appleplaces the dylibs the stage manifest names inContents/Frameworks/(Frameworks/on iOS), links the program with@executable_path/../Frameworksthroughmcpp::link_flag, so no file is edited after the link, and signs a bundle without an identity ad hoc, frameworks first. Measured:codesign --verify --deep --strictis valid; the program exits 7 through the framework and 134 with "Library not loaded" without it.app-framework-consumer--format dmg: the bundle beside anApplicationslink in a UDZO image (hdiutil verifyvalid); refused on iOS.mcpp pack --format dmgmcpp run --format <f>uses the runner named<f>(e2e 674). Plugin: on macOSdist-applesupplies the runner namedapp, which isxim:macapp-run(C1), installed formcpp runonly;mcpp run --format appreturns the program's status (exit 7 measured). A manifest runner of that name wins.open -Wdefault runner.dist-wix--format setup: a Burn bundle chaining the MSI, with WiX's stock bootstrapper application fromxim:wix5.0.2-1; the MSIwix burn extracttakes out of the bundle is byte-identical to the built one.options::bundle_output = "setup.exe"is refused beforewixruns, sincewixrefuses that name (WIX0388).mcpp pack --format setupmsi-consumerdist-apkreads the closure the engine stages (lib/for one--target,lib/<abi>/for several) in place of its ownNEEDEDwalk and stamp: a second pack keeps the dependency's library, and one APK carries every ABI (aapt2reportsnative-code: 'arm64-v8a' 'x86_64';apksignerverifies). A stage withoutneedslines comes from an older engine and is refused naming 2026.9.14.2; every refusal is also amcpp::warning, because the engine discards a build program's output when it exits 0.--format aabbuilds an App Bundle withxim:bundletool(bundletool validatepasses; a universal APK carries the dependency's library).mcpp pack --target aarch64-linux-android --target x86_64-linux-android --format apk, or--format aabapk-consumer-sharedrules-metal(.metal): onexcrun metalaction per shader with-MMD, onexcrun metallibaction per library, deployed undermetallib/, whichdist-applemaps into the bundle's resources; a missing SDK and a missing compiler are refused naming the command. A header edit recompiled only the shaders that include it.tools-esbuildstays in the project.features = ["rules-metal"]in place of the project's Metal rulemetal-consumersimctl-run0.3.0.C.
xim-pkgindex(xim-pkgindex#838, merged)xim:macapp-run0.1.0:macapp-run <X.app> [args]requiresContents/Info.plist, readsCFBundleExecutableandexecs the executable, so output and status are the program's (exit 7 returned; abort 134).apple runners / macapp-runapple-simulator-tools0.3.0: a bundle whose executable does not load UIKit is installed and its installed executable spawned (measured: status returned, output kept 20 of 20;simctl launchreturned 0 in 140 of 140 launches for an app exiting 7, and 0 forabort()); a UIKit bundle keepssimctl launchand says the status is simctl's.apple runners / simctl-runwix5.0.2-1:mbanative.dllanchored,WixToolset.BootstrapperApplications.wixextadded, an install-verify job, and the recipe states the MSVC-ABI link with the Windows import libraries (both of mcpp's MSVC-ABI toolchains link and runDutilInitializewith them).wix build -o setup.exeis refused by WiX itself (WIX0388).wix bundleMCPP_HOMEwith[index.repos.xim] url = "<checkout>"inconfig.tomlresolves an unmerged recipe and follows commits on the branch. Engine: the same table now reaches an existing home (and its removal restores the entry), and installations from it print the source. xim-pkgindex's own PR workflow builds consumer fixtures through the override. No xlings change was needed.consumer-through-index-overridexim:bundletool1.18.3 onjdk-temurin;android-platform-tools37.0.1-4 (adb-runruntime files).bundletool,android runner / adb-runD.
mcpp-indexD1 follows HuxerUI's release (the descriptors are the project's to publish).
mcpp.plugins0.10.0 is indexed in mcpplibs/mcpp-index#426, andlatestpoints at it.Found beyond the issue, all fixed
dir/tararchives omitted their closure and saidwalked.dist-apk0.9.3 dropped the dependency's library on a second pack, and refused two ABIs without showing why.kind = "shared"statement.config.toml [index.repos.<name>]did nothing on an existing home.@executable_path/@loader_path/@rpathrpath entries were anchored to the package directory.run-A.sh run-B.sh <artifact>); refused now, naming both.tests/could not load a graph-built shared library.simctl launchnever reported an application's status;libxml2published no.pc.xlings subos use <name>, mcpp passedXLINGS_ACTIVE_SUBOSto its own xlings, which installed mcpp's tools and a project's payloads intoregistry/subos/<name>while mcpp readssubos/default(found in the sandbox; fixed in 2026.9.14.3, e2e 686).What HuxerUI can change now (not done here)
kind, or the conditional table with its source; remove the two complementary predicates.huxerui.*in the three rule/tool/test manifests.-static-libstdc++from the Android rows.--toolchaininstead ofMCPP_TOOLCHAIN; drop theopen -Wdefault runner (it hands a plainmcpp run's program toopen, which returns 0);dist-applenow suppliesapp.[test] discoverand one package per multi-source suite.Verification
latest; the plugins tag archive and its GitCode copy are byte-identical (one sha256), and the published mcpp-index artifact names 0.10.0 aslateston both hosts.xlings subos use m634-eco --sandbox, with the xlings and mcpp mirrors set to CN, the released 2026.9.14.3 installed throughxlings installandmcpp:plugins0.10.0 resolved from the index pass 22 of 22 checks (A1, A2, A3 and A6 onx86_64-linux-android, A4, A5, A7 withpkg-config gtk4, A9, A10, B3, X, anddist-apkthrough the index: two packs carrylib/x86_64/libfw.so, and one APK carries it for both ABIs), and C4 on that existing home passes 4 of 4. The same script on 2026.9.14.1 fails 18 checks. The first run, on 2026.9.14.2, failed the two pkg-config view checks, which is how item 11 above was found.mcpp emit build-database, program probes as argument vectors, and xli…
speak-agent commentedon Sep 20, 2026
Closing: all twenty-one items were delivered and released, per the triage comment above, and the follow-up work has since shipped on top of them.
mcpp:plugins0.10.0 (0.10.0: dist-apk and dist-apple read the staged closure; dmg, aab and setup formats; rules-metal (mcpp#634) mcpp-plugins#24).Records:
.agents/docs/2026-09-14-634-cmake-parity-items-by-home.mdfor the classification and the decisions,.agents/docs/2026-09-14-634-implementation-plan.mdfor the plan and the ledger.Where the delivery differs from the issue text is stated in that comment and is worth repeating here, because a reader coming back to this issue will otherwise look for shapes that were not built: A1 became "a conditional declaration replaces an unconditional one" plus per-row
[target.<sel>.targets.<n>] kind, with no newrequires_linkage; A5 became[test] discoverplus "a multi-source suite is a package", with no[[test]]and no labels; A7 turned out not to need the 37 pins, because a fresh home's SubOS view already resolves gtk4 once libxml2's.pcwas added; A8 needed no engine change; A9 has the engine state the platform floor as a fact and the dependency refuse throughversion-floor, rather than raising the floor; C4 left xlings untouched.Five defects found while implementing, none of them reported here, also landed: Android test programs depending on
libc++_shared.sobecause the NDK static libc++ was not found (none of them loadable on the emulator);@executable_pathin an rpath anchored to the package directory;simctl launchalways returning 0;dist-apklosing dependency libraries on a second pack and failing on multiple ABIs; nested tests unable to load a graph-built shared library.This issue also carried #622's last open item (B4, as B6 here), so closing it closes that line too.