Repository navigation
Packaging parity measured by a framework's build-systems conformance check: strip graph-built libraries (E5), feature tools that depend on their SDK (E6), git sub-packages (E7), dependency features on the command line (E8), a pack report (E9), the llvm row's MSVC runtime (E10) #649
Description
中文摘要
HuxerUI 给两个构建系统加了一致性验证:用同一个 CLI 分别创建 CMake 与 mcpp 应用、各自打包,比对产物的程序模型事实(六个平台)。修复之后,六个平台在 Sunrisepeak/HuxerUI#7 上全部一致。插件能修的已经发布为 mcpp:plugins 0.11.1(mcpp-community/mcpp-plugins#27)。下面是插件替代不了、需要引擎处理的部分,以及插件侧剩下的两个小项。编号接着 #647 的 E1–E4。
- E5(缺陷):
mcpp pack不剥离 Android 行的程序,也不剥离图内构建的共享库。- 输出写着 "stripped",实测
libapp.so、libdep.so、libc++_shared.so都带.symtab与.debug_*; - 桌面行只剥离可执行文件,图内构建的
libdep.so同样没剥离; - dist-apk 0.11.1 已自己剥离,作为临时方案。
- 输出写着 "stripped",实测
- E6(缺陷):feature 工具不能依赖声明它的包。
- 工具构建成功、
dep_bin也拿得到路径,随后应用构建报dependency cycle through package 'fw' while computing its build-cache key; - 场景:SDK 以 feature 提供一个依赖自身的程序(Windows 安装界面),各应用就不必再复制一份。
- 工具构建成功、
- E7(需求):git 依赖只能解析仓库根包。
- 同一仓库里的其他包(
[workspace] members)选不到; - 场景:发布到索引之前,按 git 版本钉一个包含多个包的仓库。
- 同一仓库里的其他包(
- E8(需求 + 缺陷):
--features只接受根包 feature。- 根包有
[features]表时,--features dep/feature被警告后忽略,--strict下报错; - 根包没有
[features]表时,任何名字都被静默忽略,--strict也不报错; - 场景:CLI 在打包时打开依赖的 feature,不要求每个应用清单都写转发行。
- 根包有
- E9(需求):
mcpp pack的机器可读产物清单。- 现在只能解析
Packed <路径>文本行,路径还经过缩写(项目相对、@mcpp/、~/); - 另有一处不一致:
pack不接受--release/--dev,build与run都接受。
- 现在只能解析
- E10(问题):Windows 上 llvm 行的 C/C++ 运行时模型。
- 实测 mcpp 构建的程序静态链接运行时,不导入
msvcp140.dll、vcruntime140.dll、api-ms-win-crt-*; - 文档只在 cl.exe 一节写了默认
/MD,源码里 CRT 选择只作用于msvc方言; - 想确认 llvm 行的默认值是不是设计如此,以及
cxx_runtime能否在这一行选择动态运行时。
- 实测 mcpp 构建的程序静态链接运行时,不导入
- 插件侧(低优先级):
- P1:dist-apple 默认写入的键(
NSHighResolutionCapable、LSRequiresIPhoneOS)只能改值,不能去掉; - P2:dist-web 的页面固定叫
index.html。
- P1:dist-apple 默认写入的键(
每一项都附实测输出,以及可以在 GitHub 托管 runner 上跑的 CI 检查。
Context
HuxerUI builds with CMake and with mcpp. Its specification (docs/design/build-systems-spec.md on Sunrisepeak/HuxerUI#7) states that a project built with either build system's defaults is the same program. build-systems-conformance.yml checks that on every platform:
- The same application is created for each build system.
- Both are packaged with
huxerui package <platform>. - Facts are read from the two artifacts and compared: the manifest and Info.plist entries, icons, resources with their digests, the libraries and how they are stored, the Burn bundle and MSI contents, and so on.
The first runs found differences in three places.
- HuxerUI's own rules: fixed there.
mcpp:plugins: fixed in 0.11.1 (0.11.1: dist-apk packs native libraries as the Android Gradle plugin does; dist-appimage takes an SVG icon mcpp-plugins#27):dist-apkstrips native libraries and stores them in place when the manifest asks;dist-appimagetakes an SVG icon.
- The engine: the items below. All six platforms now agree at ed896c9. Some of the fixes are workarounds this issue would let the plugins and the framework drop.
Measured with mcpp 2026.9.15.2 and mcpp:plugins 0.11.1, on Linux x86_64 unless stated. Source references are to main at 2fc7b5b. The fixtures are throwaway directories, every file is quoted, and the home directory is written as ~.
| # | What | Kind | Needed by |
|---|---|---|---|
| E5 | mcpp pack strips the program on every row and the shared libraries the graph builds |
defect | APK size parity with Gradle; --no-strip / --debug-symbols reaching every packed library |
| E6 | A feature-gated tool may depend on the package that declares it | defect | an SDK shipping a program built against itself |
| E7 | A git dependency can name another package in the repository | enhancement | multi-package repositories pinned by git |
| E8 | --features <dependency>/<feature>; an unknown name is reported with no [features] table |
enhancement + defect | a driver opening a dependency's packaging feature for one command |
| E9 | A machine-readable report from mcpp pack; --release / --dev on pack |
enhancement | tools that publish what mcpp pack produced |
| E10 | The C/C++ runtime model of the llvm row on x86_64-windows-msvc |
question | runtime parity with MSVC builds |
E5. mcpp pack leaves an Android row's program, and every graph-built shared library, unstripped
Fixture. An application with a shared dependency, packed for Android and, with a bin target, for the Linux desktop.
# dep/mcpp.toml
[package]
name = "dep"
namespace = "spike"
version = "0.1.0"
standard = "c++20"
[targets.dep]
kind = "shared"
[build]
sources = ["src/*.cpp"]// dep/src/dep.cpp
#include <string>
[[gnu::visibility("default")]] int dep_answer() { return static_cast<int>(std::to_string(42).size()) + 40; }# app/mcpp.toml
[package]
name = "app"
version = "0.1.0"
standard = "c++20"
[dependencies]
spike.dep = { path = "../dep" }
[targets.app]
kind = "app"
main = "src/main.cpp"
[target.x86_64-linux-android]
min_api_level = 23// app/src/main.cpp
#include <string>
int dep_answer();
[[gnu::visibility("default")]] int app_entry() { return dep_answer() + static_cast<int>(std::to_string(1).size()); }
int main() { return app_entry() == 43 ? 0 : 1; }hostapp/ is the same package with [targets.hostapp] kind = "bin" and int main() { return dep_answer() == 42 ? 0 : 1; }.
Measured: the Android row.
$ mcpp pack --target x86_64-linux-android --format tar
Resolved android-ndk@30.0.16248370 → x86_64-linux-android → @mcpp/registry/data/xpkgs/xim-x-android-ndk/30.0.16248370/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++
Target x86_64-linux-android → x86_64-unknown-linux-android23
Packing app v0.1.0 (vendored, stripped)
Packed target/dist/app-0.1.0-x86_64-linux-android.tar.gz
In the archive, read with file and readelf -S -W:
| file | bytes | file |
sections |
|---|---|---|---|
lib/libapp.so (the program) |
5 904 | not stripped | .symtab, .debug_line |
lib/libdep.so (built by this graph) |
5 496 | not stripped | .symtab, .debug_line |
lib/libc++_shared.so (the NDK's) |
9 091 400 | not stripped | .symtab, .debug_abbrev, .debug_info, .debug_ranges, .debug_str, .debug_line, .debug_loc |
Measured: the Linux desktop row.
$ mcpp pack --format tar
Packing hostapp v0.1.0 (vendored, stripped)
Packed target/dist/hostapp-0.1.0-x86_64-linux-gnu.tar.gz
bin/hostappis stripped: no.symtab, no.debug_*.lib/libdep.sois not stripped: it keeps.symtaband 7.debug_*sections (16 592 bytes).
In an application. HuxerUI's two-ABI APK, arm64-v8a, before dist-apk 0.11.1 stripped (bytes):
| library | mcpp | Gradle |
|---|---|---|
libconformance.so (the program) |
123 272 | 31 512 |
libhuxerui.so (the framework, a shared library the graph builds) |
8 107 904 | 5 465 256 |
libc++_shared.so (the NDK's) |
9 485 032 | 1 374 336 |
The Android Gradle plugin strips every native library it packages with llvm-strip --strip-unneeded, the NDK's libc++_shared.so included.
Cause, partly. strip_program in src/pack/pack.cppm strips the staged program with the Executable shape and nothing else. Its comment gives the reason:
A bundled
.sois somebody else's file: it came out of the store or off the host, mcpp did not build it, and stripping it would change a shared payload's bytes for no gain to this bundle.
That reason holds for store and host files. It does not hold for a shared library this graph compiled from source (libdep.so above). On the Android row the program itself is not stripped either, although the line says stripped; that path was not traced.
Why it matters beyond size.
dist-apk0.11.1 now strips withllvm-strip, which it locates itself (mcpp::toolchain_dir()/bin/llvm-strip).mcpp pack --no-strip("Ship the artifacts as built") and--debug-symbols <DIR>do not reach that step: a build program receivesMCPP_PACK_FORMATandMCPP_PACK_STAGE_DIR, and nothing about the strip decision.--format tar/dirfor Android, and every desktop pack with a graph-built shared library, still ship symbols.
Proposed (for discussion).
- Strip what the graph built: the program on every row, with the
SharedLibraryshape when the program is a shared object, and every shared library the graph compiled. Both honour--no-stripand--debug-symbols. - Decide the toolchain's own runtime libraries (
libc++_shared.so) explicitly. Gradle strips them. Either way the policy should be stated. - Expose the resolved decision to build programs (for example
mcpp::pack_strip()andmcpp::pack_debug_symbols_dir()), so a dist member that re-packs libraries follows the same switch.
CI check (ubuntu-24.04). The fixture above.
mcpp pack --target x86_64-linux-android --format targiveslibapp.soandlibdep.sowith no.symtaband no.debug_*, andreadelf --dyn-syms -Wstill listsapp_entryanddep_answer.mcpp pack --format taronhostapp/gives the same forlib/libdep.so.- With
--no-strip, all of them keep.symtab.
E6. A tool behind a feature cannot depend on the package that declares it
Fixture.
# fw/mcpp.toml
[package]
name = "fw"
namespace = "spike"
version = "0.1.0"
standard = "c++20"
[targets.fw]
kind = "lib"
[build]
sources = ["src/*.cpp"]
[features]
installer = []
[target.'cfg(os = "linux")'.feature-deps.installer]
spike.fw-installer = { path = "tool", tools = ["fw-installer"], reexport = true }# fw/tool/mcpp.toml
[package]
name = "fw-installer"
namespace = "spike"
version = "0.1.0"
standard = "c++20"
[targets.fw-installer]
kind = "bin"
main = "src/main.cpp"
[dependencies]
spike.fw = { path = ".." }# app/mcpp.toml
[package]
name = "app"
version = "0.1.0"
standard = "c++20"
[dependencies]
spike.fw = { path = "../fw" }
[features]
windows-installer = ["spike.fw/installer"]
[targets.app]
kind = "bin"
main = "src/main.cpp"// app/build.mcpp
import std;
import mcpp;
int main() {
const char* a = mcpp::dep_bin("fw-installer", "fw-installer");
const char* b = mcpp::dep_bin("spike.fw-installer", "fw-installer");
const std::string message = std::string("SPIKE short=[") + (a ? a : "") + "] qualified=[" + (b ? b : "") + "]";
mcpp::warning(message.c_str());
return 0;
}// fw/src/fw.cpp
int fw_answer() { return 42; }
// fw/tool/src/main.cpp
int fw_answer();
#include <cstdio>
int main() { std::printf("fw-installer ran %d\n", fw_answer()); return 0; }
// app/src/main.cpp
int fw_answer();
int main() { return fw_answer() == 42 ? 0 : 1; }Measured.
$ mcpp build --features windows-installer
Resolving toolchain
Resolved gcc@16.1.0 → @mcpp/registry/data/xpkgs/xim-x-gcc/16.1.0/bin/g++
Building host tool fw-installer:fw-installer from fw-installer v0.1.0 (once per package source and host toolchain)
Resolving toolchain
Resolved gcc@16.1.0 → @mcpp/registry/data/xpkgs/xim-x-gcc/16.1.0/bin/g++
Target x86_64-linux-gnu → x86_64-unknown-linux-gnu
Target x86_64-linux-gnu → x86_64-unknown-linux-gnu
build.mcpp compiling
build.mcpp running
warning: app: SPIKE short=[~/.mcpp/build-cache/v1/tool/spike/fw-installer@0.1.0+path.ca13b1af6bea7e50/203ca1e0189df641/bin/fw-installer] qualified=[]
error: dependency cycle through package 'fw' while computing its build-cache key
$ echo $?
2
$ ~/.mcpp/build-cache/v1/tool/spike/fw-installer@0.1.0+path.ca13b1af6bea7e50/203ca1e0189df641/bin/fw-installer
fw-installer ran 42
The tool is built and runs, and the application's build program receives its path. The build then fails computing fw's cache key.
From the same fixture:
- Without
reexport = true,dep_binis empty in the application's build program and the build fails with the same error. - When the tool does not depend on
fw(no[dependencies], amainthat does not callfw_answer), the build succeeds anddep_binreturns the path. dep_binwith the qualified name is empty in every case (Build programs: query the resolved dependency graph and package metadata; compile Swift sources; Android on a macOS host; feature and tool contract mismatches #647 E4, item 3).
Need. HuxerUI's Windows Setup.exe runs an installer interface: a HuxerUI program compiled against the framework with WiX's bootstrapper SDK (<name>-Installer.exe beside mbanative.dll in the bundle).
- Its source is the same in every application: a seven-line
wWinMainand a build program that names the target. Only the application's name differs. - Today every application carries that three-file
windows/installer/package: four copies across the framework's templates and an example. - The natural shape is the framework providing the program behind a feature. The program depends on the framework, so the declaration is a cycle.
Proposed (for discussion). The tool is already a separate host build, "once per package source and host toolchain", and its graph does not activate fw's installer feature. fw as the tool's dependency (host toolchain, default features) and fw as the application's dependency (target toolchain, installer) are two nodes.
- Key a package's build per (package, activated features, toolchain). The recursion then ends: the host
fwwithoutinstallerhas no tool edge. - Where the tool graph really does activate the declaring feature, refuse before building the tool, naming the edge.
CI check (ubuntu-24.04). The fixture above:
mcpp build --features windows-installerexits 0;- the application's build program prints a non-empty
dep_binpath; - the tool prints
fw-installer ran 42.
E7. A git dependency resolves only the repository's root package
Fixture. A git repository whose root package lists a member:
# repo/mcpp.toml
[package]
name = "fw"
namespace = "spike"
version = "0.1.0"
standard = "c++20"
[targets.fw]
kind = "lib"
[build]
sources = ["src/*.cpp"]
[workspace]
members = ["tool"]repo/tool/mcpp.toml is spike.fw-installer, as in E6, depending on spike.fw = { path = ".." }. The application names both by git revision:
[dependencies]
spike.fw = { git = "file:///…/repo", rev = "cc3c74c51240615da9f6d7b860e5ee2bef704cbb" }
[features]
windows-installer = []
[target.'cfg(os = "linux")'.feature-deps.windows-installer]
spike.fw-installer = { git = "file:///…/repo", rev = "cc3c74c51240615da9f6d7b860e5ee2bef704cbb", tools = ["fw-installer"] }Its build program prints mcpp::dep_bin("fw-installer", "fw-installer").
Measured.
$ mcpp build --features windows-installer
warning: 'app' declares the dependency 'spike.fw-installer', which names spike.fw-installer; the manifest '~/.mcpp/git/2493e8e7594755c7/mcpp.toml' declares spike.fw, and that identity is used.
hint: write 'spike.fw' in 'app' to state the identity the manifest declares.
Resolving toolchain
Resolved gcc@16.1.0 → @mcpp/registry/data/xpkgs/xim-x-gcc/16.1.0/bin/g++
Target x86_64-linux-gnu → x86_64-unknown-linux-gnu
build.mcpp compiling
build.mcpp running
warning: app: SPIKE short=[]
Inferred sources [src/**/*.{cppm,cpp,cc,c,S,s,asm}]
Compiling app v0.1.0 (.)
Compiling spike.fw v
Finished dev [unoptimized + debuginfo] in 0.04s
The git source always yields the root manifest's package, and no key selects another package in the repository. (Separately, the compile line for the git dependency prints spike.fw v with an empty version.)
Need. A framework repository holds several packages: the framework, its rule packages, and tools such as the installer interface in E6.
- Before they are published to an index, consumers pin the repository by git revision. HuxerUI's Live2D template and the Lib-Live2D library do so today.
- With E6 fixed, such a consumer could name the tool only by path or by published version, not by the git pin the rest of its graph uses.
Proposed (for discussion), either or both:
- By identity: when a git dependency's identity differs from the root manifest's, look for it among the root's
[workspace] members. The warning above already knows both identities. - By path: a
subdir = "tool"key on a git dependency. (Cargo resolves a git dependency by package name within the repository.)
CI check (ubuntu-24.04). A local repository created by the check with git init: spike.fw-installer = { git = "file://…", rev = "…", tools = [...] } builds the member's tool, with no identity warning.
E8. --features activates root features only, and without a [features] table accepts anything
Fixture. E6 without the cycle (the tool does not depend on fw), and the application without a [features] table.
Measured.
$ mcpp build --strict --features spike.fw/installer
…
warning: app: SPIKE short=[] qualified=[]
Finished dev [unoptimized + debuginfo] in 0.04s
$ echo $?
0
$ mcpp build --strict --features nothing-here
…
Finished dev [unoptimized + debuginfo] in 0.04s
$ echo $?
0
After adding [features] windows-installer = [] to the application:
$ mcpp build --features spike.fw/installer
warning: --features requests 'spike.fw/installer' which [features] does not declare
warning: app: SPIKE short=[] qualified=[]
$ mcpp build --strict --features spike.fw/installer
error: --features requests 'spike.fw/installer' which [features] does not declare
$ echo $?
2
mcpp build --help documents --features <LIST> as "Activate root-package features (comma-separated)", so ignoring a dependency's feature is as documented. Accepting any name, even under --strict, when the root has no [features] table is not.
Need. HuxerUI's CLI runs mcpp pack --format setup --features windows-installer for huxerui package windows, so each application declares that feature. With the tool provided by the framework (E6), each application would keep a windows-installer = ["huxerui.huxerui/windows-installer"] line only so the CLI can name it.
Proposed (for discussion).
- Accept
<dependency key>/<feature>for a direct dependency, applied exactly as a manifest forward. - Report a name that matches nothing whether or not the root has a
[features]table.
CI check.
- On the fixture,
mcpp build --features spike.fw/installerbuilds the tool anddep_binreturns its path. - With no
[features]table,mcpp build --strict --features nothing-hereexits 2.
E9. A machine-readable report of what mcpp pack produced
Measured. mcpp pack reports its artifacts as human status lines. From HuxerUI's conformance runs:
Packed target/.build-mcpp/out/conformance-0.1.0.AppImage
Packed target/.build-mcpp/out/conformance.apk
Packed leg aarch64-linux-android [arm64-v8a]
Packed target/.build-mcpp/out/conformance.app
Packed target/.build-mcpp/out/huxerui-windows/conformance-Setup-0.1.0.exe
Packed target/.build-mcpp/out/web/conformance.data
Packed target/.build-mcpp/out/web/conformance.js
Packed target/.build-mcpp/out/web/conformance.wasm
Packed target/.build-mcpp/out/web/index.html
- The path goes through
mcpp::ui::shorten_path: project-relative,@mcpp/…,~/…or absolute. A reader has to undo that. - A web pack prints one line per file, an iOS pack a directory, and a multi-row pack adds
Packed leglines. - HuxerUI's CLI publishes a pack's artifacts to
dist/<platform>/, as its CMake backend does, by parsing these lines. - docs/50's protocol spells machine output
--format json, butpack's--formatalready names the package format.mcpp testhas--message-format json;mcpp pack --message-format jsoniserror: unknown option: --message-format. - The stage manifest (docs/50) describes a staged tree, not the artifacts a format produced.
A smaller inconsistency:
$ mcpp build --release
Finished release [optimized] in 0.00s
$ mcpp pack --release --format tar
error: unknown option: --release
$ mcpp pack --dev --format tar
error: unknown option: --dev
build and run accept --release / --dev. pack accepts only --profile (default [build] default-profile, else release). A driver mapping one profile switch onto the three verbs finds this on the pack.
Proposed (for discussion).
- A report on stdout, for example
mcpp pack --message-format jsonor amcpp.packkind in docs/50's envelope: one record per artifact with the absolute path, the format, the row or rows, and whether it is a file or a directory. Intermediate legs are separate records or left out. - Accept
--releaseand--devonpack, as onbuildandrun.
CI check.
mcpp pack --format tar --message-format json(or the chosen spelling) prints a record whose path exists.mcpp pack --release --format tarexits 0.
E10. The C/C++ runtime model of the llvm row on x86_64-windows-msvc (question)
Measured on windows-2022, in HuxerUI's conformance run. mcpp pack resolved llvm@20.1.7 → x86_64-windows-msvc, with no cxx_runtime or linkage key. The application program in the MSI, read with objdump -p:
- mcpp (
conformance.exe, 5 726 720 bytes) imports system DLLs (ADVAPI32.dll,USER32.dll,d3d11.dll, …) andapi-ms-win-core-*API sets. It imports nomsvcp140.dll, novcruntime140.dll, noapi-ms-win-crt-*and noucrtbase.dll, so the C and C++ runtimes are linked into the program. The installer interface in the bundle is the same. - CMake with MSVC (
conformance.exe, 1 925 632 bytes) importsMSVCP140.dll,MSVCP140_ATOMIC_WAIT.dll,VCRUNTIME140.dll,VCRUNTIME140_1.dllandapi-ms-win-crt-*, and its MSI installs those four DLLs beside it.
The size difference was not examined.
What the docs and the source say.
- docs/20-toolchains.md describes the CRT model ("
/MD(host-coupled) by default;/MTwhen either … is written down") under "Native cl.exe builds". "On the MSVC runtime" mapscxx_runtimevalues to/MTand/MD. src/build/flags.cppmadds the CRT flag only whenisMsvcDialect = (d.id == "msvc"), and the std module build notes that "Non-MSVC dialects yield "" and the command is unchanged". The llvm row therefore gets clang's default, which the imports above show is the static runtime.
Questions.
- Is the static runtime the intended default for the llvm row on
x86_64-windows-msvc, which docs/20 names as the default toolchain for the MSVC ABI? If so, it would help to state it for that row. - Can
cxx_runtime = "host-coupled"or"toolchain-coupled"select the dynamic runtime there (for example through-fms-runtime-lib=dll)? Reading the source, the key does not reach this row; that was not measured. - Three findings left open by #641/#642: static dependencies of a shared package (F1), the payload's Mach-O private libc++ (F2), the std module initialiser in every image (F3) #646 F2 notes that the PE default for shared libraries (a private runtime per DLL) has not been examined. The answer to question 1 decides whether this row's DLLs are in that case.
HuxerUI's specification allows this difference for now: CMake installs the MSVC runtime DLLs, and mcpp links the runtime in.
mcpp:plugins follow-ups (low priority)
- P1,
dist-apple: a defaulted Info.plist key can change its value but cannot be removed.- On macOS,
NSHighResolutionCapableis always written; on iOS,LSRequiresIPhoneOS(dist/apple.cppm, thereplaced(...)checks). - CMake's Info.plist templates state neither, so HuxerUI's specification lists both as allowed differences.
- Proposed: a removal marker for a key in the project's Info.plist entries, or an option listing defaulted keys to omit.
- On macOS,
- P2,
dist-web: the page is alwaysindex.html.- CMake builds of Emscripten name the page after the target.
- Proposed: an option naming the page file, with
index.htmlas the default.
Already released in 0.11.1 (mcpp-community/mcpp-plugins#27):
dist-apkstrips native libraries (the workaround E5 would make unnecessary);dist-apkstoreslib/uncompressed on 16 KB pages when the manifest statesandroid:extractNativeLibs="false";dist-appimagetakes SVG icons.
Still open elsewhere
- Build programs: query the resolved dependency graph and package metadata; compile Swift sources; Android on a macOS host; feature and tool contract mismatches #647:
- E1: transitive libraries' resources and platform sources. HuxerUI's specification records the gap: a build program sees its direct dependencies only.
- E2: Swift.
- E3: Android on a macOS host. HuxerUI builds Android on Linux only.
- E4: the feature and tool contract mismatches; item 3 is observed again in E6.
- Three findings left open by #641/#642: static dependencies of a shared package (F1), the payload's Mach-O private libc++ (F2), the std module initialiser in every image (F3) #646 F1–F3.
- Not measured anywhere: an iOS device.
Activity
One C++ runtime per process, the graph and pack facts, bounded offlin…
Sunrisepeak commentedon Sep 16, 2026
Implemented in 2026.9.16.1 (#650) and mcpp-plugins 0.12.0. The triage record is
.agents/docs/2026-09-16-646-649-four-issues-by-home.md; the plan and its readings are in.agents/docs/2026-09-16-646-649-implementation-plan.md.E5, what
packstrips. The Android status line said "stripped" where nothing had been stripped:run_shared_programnever stripped, and the word reported the decision rather than the act.mcpp packnow strips the program on every stripping row, every shared library the graph built, and staged toolchain runtime copies; the status line reports what was done. The decision is exposed to build programs and plugins asmcpp::pack_strip()/MCPP_PACK_STRIP, withmcpp::pack_debug_symbols_dir()/MCPP_PACK_DEBUG_SYMBOLS_DIRfor--debug-symbols, sodist-apkpacks what the engine staged as staged instead of stripping it a second time. (e2e 722)E6, a feature-gated tool. A package whose targets are all programs contributed its
[dependencies]to the consumer's target graph, so a tool's own dependencies were linked into the application. Such a package now contributes nothing to the consumer's graph; its programs come from the tool sub-build alone. Package cycles are refused at resolution (package-cycle) rather than only in the global-cache key walk, and a tool cycle is refused at its first repetition. (e2e 712)E7, a git dependency. A git dependency whose identity differs from the repository root's manifest is resolved among the root's
[workspace] membersby identity, and a second declaration of the same repository merges its requests rather than dropping one. Nosubdirkey was added. (e2e 713)E8,
--features dep/feature. Adep/featuretoken is a root forward and never a macro; a plain undeclared name without[features]keeps its documented meaning. (e2e 714)E9, the pack report.
mcpp pack --message-format jsonprints onemcpp.packenvelope, andpackaccepts--releaseand--devwithbuild's precedence; every verb now shares one profile rule. (e2e 723)E10, the llvm row on the MSVC ABI. The record stated a runtime the row does not deliver. The row now records the static CRT it links, and a
cxx_runtimethe row cannot deliver is diagnosed. Whether that row adopts/MDis a separate decision with its own record; the measurement legs for C++ runtime identity across Mach-O and PE print their readings into the job summaries. (e2e 703, 704, 705)P1 and P2, in mcpp-plugins 0.12.0.
dist-appletakesoptions::omit_keys, restricted to the defaulted Info.plist keys and refusing by name a key that is derived, never written, named twice, or also set throughinfo_plist.dist-webtakesoptions::page,index.htmlby default, refusing a name with a path separator, without.html, or hidden.Verification. A SubOS sandbox with CN mirrors for both xlings and mcpp runs the scenarios against the published engine, plugins and index:
version=2026.9.16.1 fails=0. This issue's assertions arethe graph-built libdep.so is stripped,pack --release --message-format json names an artifact that exists,--features spike.fw/installer built the dependency's tool, andlibfw.so loads on its own (RTLD_NOW). In mcpp-plugins CI, the strip-decision legs--no-strip packs the library with its symbol tableand--debug-symbols: the packed library is the engine's stripped copyrun under the raised pin; they reported themselves skipped before it.#646 to #649 close: the review before the release, the sandbox readin…
speak-agent commentedon Sep 20, 2026
Closing. Every item of this issue was delivered in 2026.9.16.1 (#650) — with
mcpp:plugins0.12.0 for the plugin-side members — and the close-out record merged as #653.The verification is the part worth keeping: a SubOS sandbox with CN mirrors for both xlings and mcpp ran the scenarios against the published engine, plugins and index and read
version=2026.9.16.1 fails=0. The same script against 2026.9.15.2 readsfails=10, so each assertion measures this change rather than the setup it runs in. Two follow-ups also landed and are not open work: #651 (two spellings of one version constraint are one source, found by the review before the release) and the sandbox reading itself.Records:
.agents/docs/2026-09-16-646-649-four-issues-by-home.md(triage) and.agents/docs/2026-09-16-646-649-implementation-plan.md(plan, ledger, readings).Where the delivery contradicts the report, the comment above says so — those corrections are the reason to read it rather than this line.
tests: unit tests for the helpers prepare_build's phases share
T8 (#718 W9): the CRT model is a property of the MSVC ABI, not of cl.exe
2026.9.28.1: eight reports (#717, #718, #720, #722-#726), one downloa…