Skip to content

Releases: mcpp-community/mcpp

v2026.9.29.3

Choose a tag to compare

@github-actions github-actions released this 29 Sep 05:04
c73725b

This release completes the runtime placement of a program that a workspace
member ships through artifacts. The validation project's post-release build
of 2026.9.29.2 found it.

Fixed

  • A program shipped through artifacts no longer waits for a runtime file
    that a workspace plan never places.
    Its link edge depended on the plan's
    own deploy set, which a workspace plan does not place (bin/ holds products
    only), so a workspace whose members declare runtime files stopped with
    "missing and no known rule to make it" (e2e 833 G9).
  • The runtime files of such a program are beside it. A member's runtime
    set includes the closure of every package whose program the member ships
    through artifacts, so the program finds its own runtime files in the
    member's product directory, as it did beside a root's program in bin/
    (e2e 833 G9).

v2026.9.29.2

Choose a tag to compare

@github-actions github-actions released this 29 Sep 02:37
b8ddb55

This release corrects what a workspace plan reads from its members. The plan's
root is a virtual root that holds the values shared by the whole graph; five
statements that a member makes about itself were read from that root in
2026.9.29.1 and were therefore empty, and a member's product directory lacked
the shared libraries that its libraries need. The mcpp-index sweep of
2026.9.29.1 found the first two.

Fixed

  • A member's own relative [indices].path is anchored at the member. It
    was resolved against the workspace root, so a member that declares its own
    index (47 members of mcpp-index do) found no package. Two members that name
    one tree with different relative paths are now one configuration (e2e 120).
  • A member whose tests import std is tested with the std module built.
    The decision read the entry files of the root's targets only, and the virtual
    root has none; a member whose only sources are its tests failed to compile
    them (e2e 836).
  • A member's [resources] and windows_code_page reach its own images.
    Each selected member's resources are compiled against the member's directory
    and include directories, under res/<member>/, and embedded into the
    member's programs and shared libraries only (e2e 837). A quoted #include
    or resource file in a script is now found through the include directories as
    the resource compiler finds it, in every build.
  • A member's product directory holds every graph-built shared library of its
    closure.
    Only the libraries a member's own units link were placed, so a
    library that another library needs (libffi under libwayland-client) was
    missing and the program did not start (e2e 835 L3).
  • [build] linkage is a value of the plan. It chooses the C runtime that
    every object is compiled against, so members that differ in it are separate
    configurations, and the plan takes it from its members.
  • A member's manifest is checked as a root's. An unknown capability under
    the reserved mcpp: prefix is refused, and a cfg() predicate mcpp cannot
    evaluate and the other schema warnings are reported, for each selected
    member (e2e 836).

v2026.9.29.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 23:47
e7b3218

This release builds a workspace as one graph per configuration. The selected
members and everything they depend on are planned together under a virtual
root, so a member that several members use is compiled once; each member's
products are placed in its own product directory. It also fixes the planning
regression of 2026.9.28.3. The design, its readings and the task plan are in
.agents/docs/2026-09-29-workspace-build-graph-design.md. The xlings pin moves
to 2026.9.29.1.

Fixed

  • Planning a workspace no longer grows with the length of its dependency
    chains (2026.9.28.3 only).
    E1 planned a shared member as the root of a
    nested build, and the nested build met the same condition again, so a chain
    of n members cost 2^n plans: a nine-member workspace with nothing to build
    took 79 s instead of 6.3 s, and a chain of five libraries and a program took
    36.8 s for --workspace. E1 is removed. The same chain now plans once
    (0.5 s), and with nothing changed --workspace, -p app and a build inside
    the member are each answered by the fast path in a few milliseconds (e2e
    834).

Behaviour changes

  • A workspace is one graph per configuration. A command on a workspace
    plans its selected members together: --workspace, and a virtual root
    without -p, select every member (a rooted workspace's own package
    included); -p X, and a command in X's directory, select X; a command at a
    rooted workspace's root selects its own package. Members that share their
    toolchain request, target, C++ standard and the other values that apply to a
    whole graph are one plan, with one build.ninja; members that differ are
    separate plans, built at the same time under a static share of the jobs. A
    member used by several members is compiled once (e2e 833).
  • Build directories are at the workspace root. A member builds in
    <workspace>/target/<triple>/<configuration>/ instead of its own target/.
    Its programs and shared libraries are in its product directory,
    bin/<package name>/ (bin/<namespace>.<name>/ when two members share a
    name; a rooted workspace's own package keeps bin/), with the shared
    libraries and runtime files its programs load beside them. The first build
    after the upgrade is a full build; mcpp clean --stale removes the build
    directories members held under their own target/. A member's build program
    still writes to <member>/target/.build-mcpp/.
  • -p X plans X and what X reaches, in the shared build directory.
    mcpp build --workspace followed by mcpp build -p X compiles nothing; a
    package is compiled again only when its active features differ between the
    two commands.
  • The directory name is the configuration. It hashes the toolchain, the
    target, the standard library, the runtime contract, the C++ standard, the
    dialect flags, the profile and the other values every node of one graph
    shares. A package's own cflags, cxxflags, ldflags, defines, sources and
    include directories reach its commands instead: editing them recompiles that
    package and what imports it, in the same directory, rather than moving the
    whole graph to a new one. This holds for single-package projects as well.
  • One lock and one compile database per workspace. mcpp.lock is at the
    workspace root, as it already was for a rooted workspace; --workspace
    writes the whole record and -p X updates the entries of X's graph. Where
    the workspace has no lock yet, a selected member's own lock supplies the
    git commits. The workspace root's compile_commands.json covers every member
    that has been built or configured.
  • A member keeps its duties as the project being developed. Its targets
    are built, its [dev-dependencies] are loaded under mcpp test, its
    [hooks] run around the build, its [xlings] entries with when = "dev" are
    installed, and its build program runs after every dependency's program with
    the graph document of what it reaches (in which it is the root) and the
    dependencies' link forms. --features f activates f in each selected
    member that declares it and is refused when none does.
  • Each member links its own closure. A member's programs link the flags of
    the packages they reach and place the runtime files of those packages, never
    another member's.
  • Shared libraries are placed by a hard link. A shared library placed
    beside several programs is one file with several names where the file system
    supports links, and a copy elsewhere. Other deployed files are copied, since
    a program may write a file beside itself (e2e 835).
  • Files served from the global cache are staged by a ninja pass of their
    own
    , before the build that reads them. ninja 1.12.1 crashed when a cached
    dependency was staged again in a directory whose scans were current
    (ninja-build/ninja#2662); a directory named by its configuration makes that
    the ordinary case of a dependency upgrade.
  • The fast path records the request. build.ninja states the workspace
    members and the features it was planned for, and a fast path replays it only
    for the same request; one record is kept per selection and configuration.
  • mcpp.graph. One module holds the engine's graph algorithms: the
    topological orders the engine uses, dependency levels and the transitive
    closure. Module order, host-module order, unit order by imports, the
    dependency closure, the package-cycle check and the build-key fold use it,
    each with the order it had, so no compile or link order changes; a cycle of
    modules is now reported as a -> b -> a.

v2026.9.28.3

Choose a tag to compare

@github-actions github-actions released this 28 Sep 16:57
7377dac

This release implements mcpp's part of the #734 design: the three layers of the
build-plugin architecture (mcpp.core, the official general library, plugins),
build information for build programs, workspace members built once, batched
placement, pack -p, phase 1 of the library-interface specification, and a
package's engine floor. The design, the measurements, the tasks and the
implementation record are in
.agents/docs/2026-09-28-build-cost-foreign-toolsets-and-library-surface-design.md;
the specifications are SPEC-007 §9 and the new SPEC-008. mcpp-plugins 0.17.0
states this release as its floor.

Behaviour changes

  • A workspace member used as a path dependency is built once (E1). Before a
    consumer builds, the member is built once in its own directory, as the root of
    its own build, and its ninja decides what is stale (headers outside the
    member's root included); consumers take its objects and BMIs through stage
    edges that compare content. The condition is that the member's build-key
    inputs in the consumer's graph equal its inputs as a root (package.index
    excepted, which records only where it came from); otherwise it is compiled in
    the consumer's graph as before, and -v names the differing inputs. Two
    consumers built at once take the member's directory in turn under a file
    lock. The core library of a five-member Windows workspace was compiled three
    times before.
  • One process places the files beside a program (E4). Two or more deploy
    entries of a program become one stage_list edge reading the
    placements.list the plan writes; mcpp stage --list keeps the single-file
    semantics per destination. On a first Windows build 1270 single-file
    placements took 4.5 s; one process copying the same files takes 0.5 s.
  • Phase 1 of the library interface (E6, SPEC-008). mcpp pack names the
    exported modules outside the shipped closure (W2), and its "Withheld" row lists
    every unit not shipped (it read "(nothing)" for a package without an interface
    root while two exported modules were neither shipped nor listed); mcpp build
    warns when a package imports a non-public module of a dependency that is not a
    member of its own workspace (W3); the warning for a missing interface root
    states its consequence for mcpp pack (W1). All three are warnings.
  • The fast path resumes after a confirmed edit. The build after an edit
    confirmed the graph through the full path but did not rewrite an unchanged
    build.ninja, whose time the fast path compares every source with; every
    later build was declined until the graph's text changed. Confirming the graph
    now moves build.ninja's time (e2e 832; 2026.9.28.2 shows the same on Linux).
  • The fast path sees a path dependency's whole tree. Only the dependency's
    src/ was swept, so an edit to a host module elsewhere (mcpp-plugins'
    deps/vcpkg.cppm), which is compiled into the consumer's build program and
    named by no ninja edge, was reported as "no work". The whole tree is swept
    now, skipping hidden directories, target and nested packages (e2e 831).
  • One line per download. Output that is not a terminal printed
    Downloading <item> (<size>) at the start and ... done, <size> in <time> at
    the end; it now prints the completion line only, or the line saying the item
    did not complete. On a terminal the bar is redrawn in place and ends with the
    same completion line, which now states the size and the time.
  • clang on the MSVC ABI says when no toolset is found. With no Visual
    Studio instance carrying the C++ tools, the default msvc@system continued
    silently and the build failed at 'cstdio' file not found while precompiling
    the mcpp module; the resolution now warns and names the commands that
    install and select a managed toolset (measured on a runner whose Visual
    Studio was masked).
  • mcpp.core carries no FILE into its interface. mcpp::report writes
    through printf only; GCC rejected a build program that includes <cstdio>
    after import mcpp; (e2e 651).

Features

  • mcpp.core (E8, protocol 14). The engine's interface is named
    mcpp.core; mcpp is its permanent equivalent.
  • Build information (E2, protocol 14). mcpp::tool(role), abi_tool(role),
    tool_env(), toolset_identity(), msvc_instance_dir(), ninja_program(),
    cxx_runtime() and msvc_crt_linkage() state the resolved toolchain and the
    program's C++ runtime contract as facts, read from the producers the engine's
    own command lines read.
  • Structured diagnostics (E11, protocol 14).
    mcpp::report({severity, message, impact, hint}) is rendered in the engine's
    form, enters the JSON output, and is replayed on a cache hit.
  • A missing build-program module names its feature (E7). When a module is
    provided only behind a feature the dependency edge does not enable, the error
    names the package, the feature and the line to add.
  • Plugin module names (E10). mcpp.<own namespace>.* draws no warning; the
    reserved second segments (core, plugins, deps, rules, dist,
    tools) belong to packages in namespace mcpp.
  • mcpp pack -p <member> (E3). A member is packed from the workspace root,
    with the result a pack in the member's directory gives.
  • A package's engine floor (E9). [package] mcpp = ">=<release>" and
    [workspace.package] mcpp; an engine below the floor stops before any other
    work and names the upgrade command; only >= is accepted.
  • [lib] reports unknown keys (E12). A misspelt path was accepted
    silently.
  • The fast path on macOS, Windows and SDK-sysroot targets (E5). The runtime
    validation holds ELF/glibc rules only and returned without writing its record
    on these targets, so the fast path found no validated snapshot and every build
    took the full path (read on macOS in e2e 645, 831 and 832: "no validated
    artifact snapshot is recorded"). These targets now record the artifacts'
    stamps with a Pass verdict (no rule applies); a relinked artifact still sends
    the fast path to the full build. Under -v each refusal is stated as a
    sentence.

Release process

  • The release gate holds the ecosystem's own projects only. A downstream
    project's canary is removed from .github/release-canaries.toml; a downstream
    project (a user's application, or a fork of one) validates a release in its
    own pull request after the release. xlings and mcppls remain.
  • Commit messages, pull requests, CHANGELOG entries and release notes are
    written in English
    from this release on; release notes are the CHANGELOG
    section of the release. Earlier entries remain as written.

Compatibility

  • Protocol 14: a build program that uses the new interfaces of §9 fails to
    compile on an older engine, naming the missing name; a package that states a
    floor with [package] mcpp receives an "unsupported key" warning from an
    older engine.
  • The first build after the upgrade pays once: every build program runs again,
    because its context gains variables; a shared workspace member is staged into
    its consumers and compiled once in its own directory; the placement edge
    changes shape and runs once (a file with equal content is not rewritten).

v2026.9.28.2

Choose a tag to compare

@github-actions github-actions released this 28 Sep 03:35
e251ec3

本版本实施 2026-09-28 生态设计中 mcpp 的部分(WS1、WS2、WS3、WS7、WS8、WS10 与决定 D7),关闭
#728 与 #729,并完成 #718 中运行时放置的一半。设计、任务划分与实施记录见
.agents/docs/2026-09-28-ecosystem-design-and-optimisation-plan.md,其依据见
.agents/docs/2026-09-28-ecosystem-review-of-two-days-of-mcpp-and-xlings.md。配套的 xlings
2026.9.28.2 见 openxlings/xlings#628。

行为变化

  • Windows 程序旁的文件由一个解析器决定(WS1,SPEC-006 §3.7.1)。 声明的来源([runtime] deploy)优先于工具链的来源,工具链的来源优先于推导的来源(运行时搜索目录中找到的 DLL)。MSVC C++
    运行时作为一个有版本的集合决定:放置工具集的集合,除非某个搜索目录提供版本严格更新的完整集合,
    此时放置该集合并说明一次;依赖包自带而未被放置的副本作为打包缺陷说明一次;声明的运行时文件比
    工具集的旧时给出警告;读不出的版本不参与决定。版本取自 PE 文件的 VERSIONINFO。契约决定是否
    携带:toolchain-coupled 携带;host-coupled 不放置任何副本,并拒绝声明的副本(拒绝理由
    crt-declared-under-host-coupled);self-contained 只在依赖包带来运行时名字时放置。规划、
    链接后的 place-dlls(--crt、--toolset-crt)与 mcpp pack 读同一个答案,resolution.json
    的 runtime.placement、runtime.crt_set 与 runtime.placement_notes 记录它。此前链接后的放置
    按搜索次序取第一个提供 vcruntime140.dll 的目录,Qt 载荷中比工具集更旧的副本因此被放到程序旁。
  • 每个 action 运行时,工具集的运行时目录位于 PATH 最前(WS1,决定 D3)。 面向 MSVC ABI 的
    构建经 action 包装器的 --path-prepend 设置它,与 mcpp run、mcpp test 相同;依赖包不带运行时
    发布的宿主工具(Qt 的 moc.exe)因此能够启动。系统目录在加载器的搜索次序中先于 PATH,装有
    VC++ redistributable 的机器仍使用系统的副本。
  • 每个宿主上都跟踪头文件依赖(WS2)。 GNU 方言的编译器在每个宿主上写 depfile。Windows 上的
    clang++(自 #718 起为 LLVM 行的默认)此前不写,编辑模块 purview 中 #include 的文件不会重新编译
    导入者;Windows 上 GCC 的模块 depfile 由 mcpp depfile-filter 过滤,它运行编译命令本身,不需要
    shell。emits no GNU depfile 的降级提示随之删除。
  • 每个事实每次运行陈述一次(WS3)。 mcpp.diag 在一个进程中按(域、文本)只打印一次,工作空间
    各成员共有的事实因此只打印一次;说明(note:)是独立的严重级别,--strict 不提升它。继承自
    [workspace.build] 的冗余 CRT 参数指向 [workspace.build],不再指向不含它的成员 [build]。
    构建边在成功时要陈述的事(放置比较出的差异,两个目录提供同名 DLL 时的选择)写入
    .mcpp-advice/<该边的输出>.advice,构建成功后由完整路径与快速路径共用的一个函数报告一次
    (SPEC-007 R4.5);此前这些内容只在构建失败或 -v 时可见,规划期对同一事实的第二次陈述随之删除。
    emit build-database 的信封按成员列出各自的诊断,代码由域得出(build/msvc-crt-word 为
    MCPP_BUILD_MSVC_CRT_WORD)。
  • 命中同一目标的条件表按选择器的具体程度应用(决定 D7,#728)。 更具体的后应用,因而胜出:
    三元组高于任何 cfg 表达式,OS 高于族,cfg(all(...)) 按其固定的三元组分量计数;具体程度相同时
    按选择器文本排序。此前按选择器文本的字典序应用,aarch64-unknown-linux-gnu 先于 linux,其标量
    被后者覆盖(SPEC-004 §3.1.1)。

特性

  • mcpp self env --format json 报告 defaultToolchain(WS8)。 该值由
    pins::host_default_toolchain 一个函数回答,即首次运行安装的工具链;docs/01 与 docs/20 的表格在
    每个 CI 宿主上与之核对(.github/tools/check_default_toolchain_docs.py)。

内部

  • CI 的步骤断言其名称所说的事(WS7,#729)。 .github/tools/check_workflow_assertions.py 检查
    工作流:经管道的构建须在 pipefail 下运行(W1),被丢弃的退出状态须被读取(W2),已知为红的任务须
    标注其 issue(W3);它在 origin/main 的工作流上报告 7 处。LLVM 自构建改用 llvm@22.1.8,并以
    set -o pipefail 断言构建本身,函数规模门(check_function_sizes.sh)在其后运行。xcode-27 任务
    标注 #669 并允许失败。
  • 发布门(WS10)。 .github/release-canaries.toml 列出真实工程,release-canaries.yml 以候选
    mcpp 构建它们,release.yml 的打 tag 任务依赖其结果;tests/release/verify-published.sh 在沙箱
    中验证已发布的 mcpp 与 xlings,每个发布项一节,并保留此前各版本的小节;PR 模板要求列出每条新规则
    所跨越的既有不变量与位于交点的测试。
  • 测量任务。 measure-windows-tool-crt.yml 在带 Visual Studio 与屏蔽 Visual Studio 的两个
    Windows 行上隐藏系统的 C++ 运行时,测量 Qt 的宿主工具能否只经 action 的 PATH 启动(设计 §2.9),
    它是从 xim:qt-base 中移除运行时副本的前提。
  • xlings 固定版本为 2026.9.28.2。 interface 协议 1.3:download_progress 带 stream 且发送
    频率有上限;home 以 .xlings-home 声明;update 只构建一次索引(openxlings/xlings#628)。

兼容性

  • 运行时搜索目录中带有较旧 MSVC C++ 运行时副本的 Windows 程序,现在得到工具集的副本;差异以一条
    说明陈述,不再在每次链接时警告。
  • 在 host-coupled 下声明 MSVC C++ 运行时文件的 manifest 被拒绝。
  • 若干条件表命中同一目标、且字典序与具体程度给出不同次序的 manifest,其标量取值与列表参数的次序
    随之改变。
  • Windows 上以 GNU 方言编译的工程,编译命令多出 depfile 参数,升级后第一次构建完整重建一次。
  • 面向 MSVC ABI 的构建中,每个 action 的命令行多出工具集运行时目录(__action --path-prepend),
    升级后第一次构建中每个 action(包括 check 与 prepare)重新运行一次。

v2026.9.28.1

Choose a tag to compare

@github-actions github-actions released this 27 Sep 19:32
acb9f52

本版本合入 #717、#718、#720、#722、#723、#724、#725 与 #726 的修复与特性。设计与实施记录见
.agents/docs/2026-09-27-eight-reports-by-home-and-one-optimisation-plan.md 与
.agents/docs/2026-09-27-eight-reports-implementation-plan.md。

缺陷修复

  • 带 [package] 的工作空间根的 path 依赖(#725)。 这样到达的成员此前不被识别为成员:
    2026.9.26.1 忽略其 [workspace.dependencies] 中钉住的版本,2026.9.27.1 拒绝其
    workspace = true。现在工作空间的上下文由清单所在的位置决定,这样的成员按 SPEC-004 §9 第 1 条
    继承 [workspace.package]、[workspace.build] 与 workspace = true;[toolchain]、
    [target.<triple>]、[indices] 仍只属于根。

  • -p, --package <NAME> 按包的身份解析(#725)。 顺序为限定名、包名,然后是成员的路径或
    目录名(此前唯一的写法,保留)。同一个包名在两个命名空间下出现时拒绝并给出两个限定名;一个值
    既是某成员的包名又是另一成员的目录名时选择前者并警告。

  • 宿主模块包的 lib root 参与本包单元的导入排序(#720)。 导入同包其他单元的 lib root 此前
    先于被导入者编译而失败。不导入同包单元的包顺序不变。

  • 规则认领的设备源不是编译单元(#724)。 它此前出现在 S1 文档与 mcpp build 自己的
    compile_commands.json 中,带一条 C++ 编译命令,build.ninja 也带一条无人引用的边。

  • 构建程序失败时,其诊断得以保留(#724)。 以构建程序的指令为前提的检查(设备源的认领)不再
    对构建程序已失败的包运行,规划失败路径也保留已记录的说明;此前报告的是「设备源无人编译」。

  • emit build-database 不写入工程目录(#724)。 声明了 [xlings] 载荷的工程此前会得到
    .mcpp/.xlings.json(SPEC-005 R2.1)。

  • 一个放置目标一份内容、一个写入者(#723)。 同一目标的多个来源在放置时逐字节核对,相同则
    放置一份,不同则失败并点名全部来源;此前在规划时即被拒绝,即使内容相同。链接后放置 DLL 的步骤
    不覆盖另一写入者放在程序旁的文件;规划时在运行时搜索目录中找到的同名 DLL 让位于声明的放置,
    内容不同时在规划时警告(SPEC-007 R4.2、R4.3)。PE 目标上目的地的比较不区分大小写。

  • 只导入构建规则的 build.mcpp 在 GCC 下可以编译。 它此前在工程根目录中编译,找不到位于
    构建目录 gcm.cache 中的规则 BMI;现在导入任一模块的构建程序都在构建目录中编译。

  • 要求更新 mcpp 的索引不再报告为错误。 读取处不再打印 error: ... [E0006];失败的运行在
    使其停止的消息中给出 E0006;刷新了索引而遇到下限的运行在最后打印一行 tip:,信封中为说明
    MCPP_INDEX_REQUIRES_NEWER_MCPP;mcpp self doctor 列出当前 mcpp 不满足其下限的索引。

  • Windows 上一次 xlings 调用只作用于 xlings 子进程(#726)。 此前的 Windows 实现在两处与
    POSIX 不一致:

    • 每次调用把 registry 的 subos/default/bin 加到进程 PATH 的最前面,并设置进程级的
      XLINGS_HOME,调用后不恢复。同一次构建安装过载荷后,ninja 与每个动作都先找到 xim:llvm
      注册的 cl、link、lib、rc shim,vcpkg 对宿主三元组的编译器检测因此失败。
    • xlings 在 mcpp 的工作目录中运行,从那里向上找到工程的 .xlings.json 而进入工程模式,把 mcpp
      工具链与载荷的 shim 写进工程的 SubOS。

    现在 ScopedInvocationEnv 在调用期间应用 XLINGS_HOME、作用域变量与 PATH 前缀并在调用后
    全部恢复;命令以 cd /d "<home>" && 开头,与 POSIX 前缀中的 cd 相同。

特性

  • 条件化的 dialect_cxxflags(#717)。 [target.<selector>.build] dialect_cxxflags 在命中的
    目标上把参数加入全图的方言参数:标准库 BMI、扫描与每个编译单元。只读取构建的根包;依赖包自己的
    全图键不进入其指纹。
  • MSVC ABI 的 CRT 模型(#718)。 CRT 是目标 ABI 的性质:cl 以 /MD、/MT,clang++ 以
    -fms-runtime-lib=dll、static 表达同一模型,并到达编译、std BMI 与链接。MSVC ABI 的默认契约为
    toolchain-coupled:动态 CRT,工具集的 vcruntime140.dll、msvcp140.dll 放到程序旁。
    cxx_runtime 与 linkage 之外不增加新键;手写的 CRT 参数与模型一致时提示冗余,矛盾时拒绝;
    依赖包 [build] cxxflags 中矛盾的 CRT 参数同样拒绝;调试 CRT 参数(/MDd 等)总被拒绝。依赖
    的全局缓存键包含 CRT 模型。
  • 构建数据库描述规则生成的文件(#724)。 S1 集合的 ide.generated 列出生成的文件与目录、
    同一组选择下 mcpp build 写入的路径以及生成它的步骤(S1 0.3.0)。命令仍不运行任何 action。
  • 统一的下载进度。 工具链与载荷的安装、索引中的库包、[xlings] 载荷、索引刷新、git 依赖的
    克隆与沙箱的首次引导由同一个渲染器报告。标准输出不是终端时,每一项只打印开始与结束两行,不含
    回车与擦除序列。索引刷新经 xlings interface update_packages 进行,需要 xlings 2026.9.28.1 的
    进度事件才逐步显示。

内部

  • src/build/prepare/ 的阶段函数按其小节拆分(#722)。 代码逐字移动,不改变语句顺序;七个
    夹具的 resolution.json、build.ninja 与构建数据库输出与拆分前逐字节相同。该目录下没有超过
    400 行的函数:.github/tools/check_function_sizes.sh 以 clang-tidy 的 readability-function-size
    在以 LLVM 构建得到的编译数据库上检查,拆分前报告 10 处;CI 中尚无能完整构建 mcpp 的 clang 任务,
    接入见 #729。mcpp.lock 与 resolution.json 的写入移入 records.cpp。
  • xlings 固定版本为 2026.9.28.1。 该版本的 interface 协议为 1.2:update_packages 按阶段发出
    进度事件,interface 能力运行期间写到标准输出的文本不再混入事件流(openxlings/xlings#625)。

兼容性

  • 成员的编译命令可能改变。 经 path 依赖到达、带 [package] 的工作空间根的成员,现在收到
    [workspace.build] 与 [workspace.package]。
  • Windows 上 LLVM 行的程序改用动态 CRT。 它们现在导入 vcruntime140.dll 等,文件放在程序旁;
    写 cxx_runtime = "self-contained" 可恢复静态 CRT。cl 行的编译参数不变,程序旁多出这些 DLL。
  • 新增四种拒绝。 每一种都在消息中给出一行修法:
    • 手写的 CRT 参数与解析出的模型矛盾;
    • 在没有 redistributable 目录的行上显式写 toolchain-coupled;
    • 在两个命名空间下都有成员的包名上使用 -p;
    • 手写的调试 CRT 参数,以及依赖包中与模型矛盾的 CRT 参数。
  • -p 的值同时是一个成员的包名与另一个成员的目录名时,选择前者并警告。 此前按目录名选择。
  • S1 profile 版本为 0.3.0。 0.2.0 的消费方忽略新字段。

v2026.9.27.1

Choose a tag to compare

@github-actions github-actions released this 27 Sep 09:29
b439fd9

缺陷修复(#704、#705、#710、#712 至 #716)

  • 宿主构建读取宿主三元组的行(#704)。 不带 --target 的构建此前不读取
    [target.<宿主三元组>],其中的 cxx_runtime 没有效果。现在宿主构建应用这一行,与
    --target <宿主三元组> 相同,行的查找与拼写无关;--toolchain 仍优先于行的 toolchain
    (SPEC-004 §4.6)。
  • xpkg_dir 回答 xlings 装下的载荷(#712、#716)。 版本位按 xlings 的版本文法求值,
    libglvnd@1.7 回答 1.7.0.1。xlings 在 install_targets 事件中报告每个请求解析到的载荷,
    mcpp 按地址记录并优先读取。安装记录仍在而载荷已被删除时,联网构建重新安装,离线构建拒绝并
    点名缺失的地址;构建缓存记录读取过的载荷目录,任一目录缺失时快路径不复用缓存。mcpp 以
    xlings 发布的版本选择向量测试同一规则(SPEC-001 §10.1)。
  • 宿主工具的工具链由请求方决定一次(#710)。 顺序为 --toolchain、工具包自己的声明(工作
    空间成员先继承根位置的键)、请求方的宿主工具链;结果传给子构建并写入工具库的键。工具包的源树
    摘要跳过带自己 mcpp.toml 的子目录,并以 UTF-8 计算路径(#705)。
  • 工作空间(#713、#714)。 成员隐式继承根的 [xlings.workspace] 条目与条件行,自己声明的
    同一个包优先。未解析的 workspace = true 在根包、-p 成员与各类依赖处都被点名拒绝;带
    [package] 的工作空间根解析自己的条目。[build] sources = [] 不再推断库目标。
  • 规则只经其声明的扩展名到达(#715)。 一个规则只在包含它声明的设备源扩展名时进入包的合成
    构建程序;状态行只列出实际生效的规则。

规划不构建宿主工具(#707)

mcpp emit build-database 不再构建依赖提供的宿主工具。工具库中已有的工具照常使用;没有的工具被
推迟,报告为 note MCPP_BUILD_DATABASE_HOST_TOOL_DEFERRED,请求它的构建程序收到该工具将被发布的
路径。它取代 2026.9.26.2 的警告 MCPP_BUILD_DATABASE_HOST_TOOL_UNBUILT(SPEC-005 v1.4 R2.5)。
mcpp build 不受影响。

构建程序与依赖边(#708、#709、#711)

  • action 的 env 与 cwd(协议 13,#708)。 mcpp::action::env(name, value) 与
    cwd(dir) 由引擎的 action 包装器应用;cwd 按包根解析,声明的输入、输出与 stamp 不随它
    移动;变量值改变时 action 重新运行。两者都未声明的 action 命令行与协议 12 逐字节相同。
  • 特性的 tools(#709)。 [features.<f>] tools = ["<bin>"] 陈述特性需要本包的程序在
    构建机器上运行;启用该特性的消费方得到该工具,与边上写 tools 相同。
  • artifacts 依赖边(#711)。 x = { path = "...", artifacts = ["<bin>"] } 以消费方的
    目标与 profile 构建依赖的程序,输出到消费方的 bin/,不链接依赖的代码;action 以
    ${mcpp.artifact:<x>/<bin>} 引用它;mcpp pack 把它放在程序旁(SPEC-004 §10、
    SPEC-007 R6.4)。

载荷的打包修订与自包含的 locale(xlings#620、#621)

  • 依赖的索引条目的 revision 进入依赖完整性判断:修订号不同的已安装依赖按未安装处理。修订号
    大于 0 的运行时载荷进入运行时契约(revision=<n>),修订号变化使依赖它的产物重新链接;修订号
    为 0 时契约文本不变(SPEC-001 §10.2)。
  • mcpp pack 的 bundle-all 形态随 glibc 载荷复制 lib/locale 与 lib/gconv,启动脚本设置
    LOCPATH 与 GCONV_PATH(用户已设置时保留用户的值)。

兼容性

以下变化对已有工程可见:

  • 未解析的 workspace = true 此前静默成为空版本依赖,现在在加载时报错并点名条目(#714)。
    修正方法是在工作空间根的 [workspace.dependencies] 中声明该包,或在条目上写明版本。
  • [build] sources = [] 不再推断库目标。依赖这一推断的包在 [targets] 中声明库目标。
  • note 代码 MCPP_BUILD_DATABASE_HOST_TOOL_UNBUILT 由 MCPP_BUILD_DATABASE_HOST_TOOL_DEFERRED
    取代,不保留旧代码。

其他

  • 测试:e2e 804 覆盖不属于任何工作空间的路径宿主工具包的工具链选择(#710);e2e 802 固定 gcc
    工具链,使其断言不依赖机器的默认工具链;单元测试 test_prepare_helpers 覆盖各阶段共用的纯函数
    (--features 请求语法、宏名、git 远端是否本地等)。
  • ELF 检查以一次读取载入文件。此前逐字节读入,mcpp test 的链接后检查在测试程序较多时耗时
    数十分钟。
  • src/build/prepare.cppm 的内部分解。 原文件 16,105 行,其中函数 prepare_build 占 85%。
    它拆为按阶段划分的函数,跨阶段的状态集中在 PrepareState(取代原来约 180 个共用一个栈帧的
    局部变量)。主接口 prepare.cppm 只保留导出的类型、内联函数与声明;实现位于 src/build/prepare/ 下的
    实现单元与实现分区 :state,各单元只导入自己用到的模块,每个文件不超过 2,500 行,由 CI 检查。
    代码逐段移动,不重排语句;公开接口、导出符号与默认参数不变,七个场景的规划输出与分解前逐字节
    一致。修改一个阶段只重新编译该实现单元:本机修改 P13 中的一处字符串,增量构建 12 s,分解前
    71 s。模块组织受 GCC 16.1 的内部编译器错误约束,见 #721。
  • 规范:SPEC-001 v1.5、SPEC-004 v1.8、SPEC-005 v1.4、SPEC-007 v0.3。

v2026.9.26.2

Choose a tag to compare

@github-actions github-actions released this 26 Sep 02:49
c109fdd

编译数据库:一个配置一个数据库(#699 的报告;#397 C-1、#677 B1)

compile_commands.json 此前是一份跨命令、跨工具链合并的文件:删除后的下一次构建不再写回它,
另一个工具写入的条目与上一个工具链的条目都被保留,directory 写的是工程根目录而编译器在输出目录
中运行。现在:

  • 每个配置一份数据库,位于 target/<triple>/<fingerprint>/compile_commands.json,只在本配置之内
    合并,因此 mcpp test 之后的 mcpp build 保留测试单元的条目。工程根目录的文件是当前配置数据库
    的副本,整体替换、从不合并,内容相同时不写;切换工具链或 profile 就切换整个文件,切回时恢复该
    配置的条目。根目录的符号链接照旧按其指向写入。
  • 根文件被删除后,下一次构建从配置数据库恢复它,快路径也是如此,不需要规划。
  • 被替换的根文件含有 mcpp 没有写的条目时(条目的 output 不在本工程的 target/ 或 mcpp 主目录
    之下),构建以一条警告说明其数量。
  • directory 与 S1 的 work-directory 是编译器运行的输出目录,与 JSON Compilation Database 格式、
    S1-8-2 以及 CMake、ninja 的写法一致;从 directory 重放 GCC 的条目不再在源码树中写出
    gcm.cache/。
  • 提供模块的单元在 -c 之前带上接口语言 flag(clang 为 -x c++-module,GCC 为 -x c++),
    clangd 因此能处理 .ixx 接口;MSVC 的写法待 Windows 实测后再加。
  • 导入 std 的构建在 compile_commands.json 与 emit --spec compile-commands 中列出标准库单元
    (S1-12-1),另一个版本的 clangd 因此能自己构建 std。S1 文档中标准库单元的 provides 指向
    共享 std 缓存中的 BMI,工具链带 build-id,版本与构建身份完全一致的读者可以直接复用。
  • mcpp new 写出的 .gitignore 包含 compile_commands.json。

emit build-database 按成员规划(#699)

emit build-database --workspace 此前在第一个规划失败的成员处停止,已规划成员的集合全部丢失。
现在每个选中的成员独立规划:规划失败的成员不贡献集合,贡献一条 error 诊断,path 为它的
mcpp.toml;至少一个成员规划成功时文档带 data,watch 也列出失败成员的 mcpp.toml 与
build.mcpp;有任何错误时退出码为 1。在 emit 下,构建失败的宿主工具是警告
MCPP_BUILD_DATABASE_HOST_TOOL_UNBUILT,规划继续,构建程序得到该工具将被发布的路径;构建程序失败
的包不带该程序的指令地被描述,并得到一条 MCPP_BUILD_DATABASE_PROGRAM_FAILED 错误,path 为它的
build.mcpp。mcpp build 的行为不变。带错误诊断的 data 是 S2 0.3.0 的部分回答
(Sunrisepeak/mcpp-language-server#25)。

构建程序:runtime_search_dir、prepare 角色与 stamp 规则(#701、#702)

  • mcpp::runtime_search_dir(dir)(mcpp:runtime-search-dir=,协议 12)是 [runtime] runtime_search_dirs 的构建程序形态,并入同一个字段:ELF 与 Mach-O 的运行路径、mcpp run 的加载
    路径、mcpp pack 的闭包搜索与运行时校验都照常读取它,依赖的声明到达消费方的可执行文件。目录在
    构建程序运行时不必存在。保留字段 [runtime] library_dirs 不获得指令。
  • 新角色 prepare 用于文件名在构建程序运行时未知的施工(安装一个前缀、解开一个 SDK):命令填充
    用 a.output_dir(dir) 声明的目录,引擎写 stamp。命令成功而目录不存在或除 stamp 外不含文件时,
    这条边失败并点名目录。声明包的每条编译边与计划中的每条链接边等待它,构建输出以 PREPARE
    标注。构建程序的重新运行依据落在某个 prepare 目录内时,构建给出警告。
  • 角色以常量书写:mcpp::roles::{source, check, object, artifact, prepare}。引擎拒绝这五个之外的
    角色字符串并列出它们;此前未知的字符串被静默读作 source。
  • check 与 prepare 的命令成功后,引擎创建缺失的 stamp,并把已存在的 stamp 更新到当前时间;
    此前已存在的 stamp 不被更新,一个输入改变一次之后,该 action 在此后每次构建中都会重新运行。

Windows 程序的 DLL 在链接后放到程序旁

PE 映像没有运行路径,运行时搜索目录中的 DLL 此前只服务于 mcpp run 与 mcpp pack,从构建目录直接
启动的程序找不到它。现在计划中带运行时搜索目录的 PE 程序在链接后多一条边 mcpp place-dlls:按
mcpp pack 的闭包求解与系统规则,把程序直接或间接导入、且在这些目录中解析到的 DLL 放到程序旁,
字节不同时才写;DLL 在其目录中被替换(包括被 prepare action 替换)后,同一次构建会再次放置它。
部署到程序旁的 DLL 从链接边的隐式输入改为 order-only 输入,DLL 变化不再使程序重新链接。

链接 flag 按词读取(#703)

[build] ldflags、mcpp::link_flag 与依赖传播的链接 flag 此前只为 ninja 转义,Linux 与 macOS 的
sh 展开其中的 $ORIGIN,程序的运行路径因此含有 /../lib,即宿主的 /lib。SPEC-004 §8 的词读法
现在同样适用于链接 flag:每个词原样到达链接器;依赖的链接 flag 按词传播;link_search、link_lib、
link_script 由路径构成的值是一个词,含空格的目录不再被拆开。为 shell 或 ninja 手工转义的写法
(\$ORIGIN、'$$ORIGIN')现在按写法读取,首次规划以 build/flag-words 指出这样的元素;
索引中没有这样的写法。

构建插件规范 SPEC-007

新增 docs/specs/build-plugins.md(草案 0.2):插件的配置、施工与校验各用一种机制,环境不完整时
警告而不失败,配置不依赖施工结果,运行时库以 runtime_search_dir 声明,构建期的网络访问在离线
构建中不发生。mcpp 只提供通用机制,某一个工具的知识只属于它的插件。

兼容性

  • 构建程序缓存的 epoch 升到 3,升级后每个构建程序重新运行一次。
  • 构建程序协议升到 12;使用新指令、新角色或角色常量的构建程序在旧引擎上编译失败并指出名字。

v2026.9.26.1

Choose a tag to compare

@github-actions github-actions released this 25 Sep 20:32
f61b466

路径与文本统一为 UTF-8(#693)

mcpp 此前在 Windows 上以进程 ANSI 代码页持有路径,而它写出的 compile_commands.json 只能容纳
UTF-8。于是工程目录或 mcpp 主目录只要含非 ASCII 字符,每次构建都以
internal: unhandled exception: [json.exception.type_error.316] 失败,系统代码页能拼出的名字也
不例外(代码页 936 上的中文目录名、代码页 1252 上的 café);代码页拼不出的名字则以窄化异常失败。
Linux 上名字不是 UTF-8 的目录以同样的 JSON 异常失败。现在 mcpp 在所有平台上以 UTF-8 持有文本:

  • mcpp.exe 以应用程序清单声明 UTF-8 代码页(res/mcpp.rc),在 Windows 10 1903 及以后的版本上
    它与 Windows 交换的窄字符串都是 UTF-8。build.mcpp 链接同一份清单;作为 host tool 构建的程序
    默认也带它,除非其目标写 windows_code_page = "legacy" 或包自己嵌入了清单。
  • 新的目标键 windows_code_page("utf-8" 或 "legacy")为工程自己的可执行文件声明代码页;
    不写时保持系统代码页。
  • 路径没有 UTF-8 拼法的工程目录与 MCPP_HOME 在构建之前被拒绝,消息以转义拼出该路径并给出
    本机的原因;工程内部这样的文件名被跳过并按目录汇报一次;文本不是 UTF-8 的 build.mcpp 指令
    按键名被拒绝。compile_commands.json 的序列化失败报告为该文件的失败,不再成为内部异常。
  • build.ninja 含非 ASCII 字节时,mcpp 以 ninja -t wincodepage 核对 Ninja 读取它的编码,
    不是 UTF-8 就拒绝并点名该 Ninja。cl.exe、link.exe、lib.exe 只在响应文件以字节顺序标记开头时
    按 UTF-8 读取它(在 windows-latest 上实测),msvc 方言的响应文件因此以字节顺序标记开头。
  • 在忽略清单的 Windows(早于 1903)上,受影响路径的诊断会写出进程所在的 ANSI 代码页。
  • Windows CI 新增回归任务:llvm、MSVC、MinGW 三行分别在 ASCII、café 与中文目录中构建并运行,
    并核对 build.ninja 与 compile_commands.json 中该目录名的 UTF-8 字节。

报告中的静默 0xC0000409 来自 xlings 的 shim:它在代码页之外的工作目录中启动时抛出未捕获的异常。
xlings 2026.9.26.2 声明同样的 UTF-8 代码页并在 main 中设异常边界,mcpp 内置的 xlings 版本随之更新。

c_standard 作用于声明它的包(#695)

[build] c_standard 此前写在文件级 $cflags 上,图中每个 C 编译单元都读它:根包的值到达每个依赖,
依赖自己的声明被解析、被哈希进缓存键,却没有被施加。现在每个包的 C 单元以该包自己的标准编译,
未声明的包以 c11 编译,C++ 单元不接收 C 标准。cl.exe 以其默认模式编译 C,构建会用一行列出
声明未被施加的包。#690 设计记录 §3.1 中把 c_standard 列为「只读根包」的一行已附更正说明。

链接图提供的 C 库时不搜索宿主目录(#696)

C 库来自依赖图的链接此前没有 --sysroot,clang 会从宿主推导 /usr/lib 等库目录:
x86_64-linux-musl 上的 -lm 把宿主 glibc 的目标文件静默链接进 musl 镜像,aarch64-linux-musl
上则以一条不相干的报错失败。现在由 clang 链接的 ELF 目标带上指向构建目录内空目录的 --sysroot;
密封检查拒绝指向工具链存储、构建目录与图中各包之外目录的 -L([build] allow_host_libs = true
取消这一拒绝)。图中无人应答的 -l 以链接器自己的消息失败,mcpp 另附说明;缺失的名字全属于 musl 以
libc.a 应答的八个名字时,说明会点名带有这八个空归档的 openkal-musl 0.19.2。

musl 目标按声明的优化级别编译(#694)

引擎此前在 *-linux-musl 上把每个非零优化级别替换为 -Og(一个 musl-gcc 15.1.0 内部编译器错误的
变通,也作用于 clang 与 mcpp 自己的 Linux 发布二进制),而 Finished 一行按声明的级别报告
optimized。该分支已删除:编译与报告读取同一个值(realised_opt_level),空级别即 0。
原先的触发条件在任何可得的输入上都不再复现。

v2026.9.25.1

Choose a tag to compare

@github-actions github-actions released this 24 Sep 23:33
f176abd

工作空间成员在每种位置上以相同方式编译(#690)

[workspace.build] 的继承此前在把清单固定进构建图的那一步执行,晚于 defines 的展开。
作为兄弟成员 path 依赖被编译的成员因此收不到工作空间的 defines(#690 报告的情形),
而被命令选中的根成员收到两份工作空间的 cflags、cxxflags 与 ldflags。现在成员在自己的
加载处继承,顺序与根包相同:先继承,再合并条件节,最后展开 defines;固定进构建图的一步只读取
结果,遇到未展开的 defines 时报告内部错误。

同一处还补上了两件同形的缺口:

  • 作为依赖出现的成员解析自己的 x.workspace = true 条目。此前该条目以既无版本也无路径的形式
    进入解析,报出的是「no readable index entry」。
  • 通过 git 引用的、托管在 git 上的工作空间的成员,除 [workspace.package] 外,也继承所在仓库
    的 [workspace.build] 与 [workspace.dependencies],相对路径以仓库根为锚点。同一个提交在它
    自己的检出中与在使用方的依赖图中以相同方式编译。
  • 索引描述符以 mcpp = "<子目录>/mcpp.toml" 指向归档内的成员时,该成员以同样方式取得归档中的
    工作空间;在归档内查找工作空间根不越出该版本的安装根。本机已安装的 22 个带工作空间根的索引包
    都没有声明可继承的表,这一变化不改变任何现有包。

[workspace.build] ios_deployment_target 此前被解析、被继承,却被已知键检查拒绝;可继承键现在
只在一张表里陈述,解析、已知键检查与报错文本都取自这张表。

defines 按宏名构成集合

defines 的条目按包接收它们的顺序合并:[workspace.build]、包自己的 [build]、命中的
[target.<selector>.build]。同一宏名的后一个条目在原位替换前一个,条目 !NAME 移除该宏名,
每个宏名只以一个 -D 词到达编译器。成员因此可以覆盖或移除继承来的宏,而不再产生重定义诊断
(-Werror 下是错误)。defines 条目同样取代同一个包的 cflags、cxxflags 中同名的 -D 词。
更早的 mcpp 会把 !NAME 作为 -D!NAME 交给编译器,使用它的包需要 mcpp 2026.9.25.1 或更新版本。

使用方的头文件目录不再进入依赖

根包的 include_dirs 与 include_dirs_after(包括 private_include_dirs)此前被写进
build.ninja 的文件级编译标志,图中每个编译单元都读到它们:根包目录中与系统头或依赖头同名的
文件会在依赖内部遮蔽它们。依赖缓存键不含这些目录,于是按一个工程的根包头文件编译出的依赖对象
会被另一个无关工程取用(实测:compat.cjson 的 cJSON_Compare(1.0, 1.2) 在未写任何头文件的
工程中答 1)。现在每个编译单元只收到自己的包的目录,以及它的依赖公开的目录;根包自己的编译单元
中每个目录只出现一次。

行为变化。 依赖若曾依靠使用方的头文件目录才能编译,现在报告缺少头文件;mcpp 在编译器报错
之后指出该头文件所在的使用方目录。依赖需要通过自己的 include_dirs 或它的某个依赖找到头文件。
依赖缓存的 epoch 由 3 升为 4,升级后首次构建重建一次依赖缓存,不需要任何操作。

作为 host tool 构建的依赖只合并一次条件节

依赖的 [target.<selector>.build] 条件节先按使用方的目标合并,host tool 子构建此前拿到的正是合并后的
清单,又按宿主合并一次,同时命中两者的条目因此到达工具两次。实测:条件节中的 -include once.h
(无 include guard)让单独构建正常的包作为工具时报重定义错误,2026.9.24.1 同样如此。清单现在记录
第一次合并之前的状态,子构建从该状态按宿主合并。

成员清单只有一种读法

mcpp publish、mcpp pack、mcpp emit xpkg、mcpp toolchain list、mcpp sbom、mcpp index list/update、
mcpp update 的索引刷新、快路径的身份判定与测试发现,此前直接读取成员目录中的原始清单:省略
version 的成员被拒绝,未写 [toolchain] 的成员在 toolchain list 中显示全局默认工具链,而同一
目录下的 mcpp build 用的是工作空间的工具链。现在它们与 prepare_build 经同一个函数读取继承后的
清单。

工作空间成员的发布形态自包含

在成员目录中执行 mcpp publish 或 mcpp emit xpkg 时,归档只包含成员自己的目录,此前其中的
mcpp.toml 原样照搬:继承的 version、[build] 标志不在其中,兄弟成员之间的 path 依赖原样保留
而使用方无法解析,描述符的 deps 静默略去这些依赖。现在发布的清单是归一化后的:继承来的值被写出,
x.workspace = true 条目取得解析后的来源,兄弟依赖以版本依赖发布,原清单以 mcpp.toml.orig 保留,
归一化后的文件同时写到 target/dist/<name>-<version>.mcpp.toml。归档由 git 对象按提交日期构建,
两次运行逐字节相同。不带 version 的兄弟 path 依赖、包外非成员的 path 依赖、位于成员目录之外的
继承头文件目录会被拒绝,报错给出应写的内容。不是工作空间成员的包与此前一样原样归档。
归一化后的清单只使用 2026.9.24.1 已接受的键,实测该版本的客户端可以构建它。

xlings 2026.9.20.1

内置的 xlings 由 2026.9.16.1 升为 2026.9.20.1(openxlings/xlings#610):项目 subos 中执行的命令
不再从项目的 subos 读取全局工作空间并据此删除全局 shim;下载失败的安装不再报告 installed。

测试基础设施

e2e 辅助脚本 _inherit_toolchain.sh 改为按版本链接开发者的载荷。此前它链接整个包目录,测试
新装的版本穿过链接写进开发者的 ~/.mcpp,链接脚本指向测试结束后即被删除的临时目录(#293 的
第二种形态;实测 31_transitive_deps.sh 写出了这样的 xim-x-glibc/2.44.3)。