From 43587ae6afb81f4bd24e7f37e048f9819385b2c0 Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:55:50 +0800 Subject: [PATCH 1/2] docs: sync documentation with current behavior --- .agents/skills/mcpp-contributing/SKILL.md | 51 ++++++++----- .agents/skills/mcpp-release/SKILL.md | 93 +++++++++++------------ .agents/skills/mcpp-usage/SKILL.md | 35 +++++---- README.md | 42 ++++++---- README.zh-CN.md | 40 ++++++---- docs/00-getting-started.md | 53 +++++++------ docs/01-examples.md | 7 +- docs/02-pack-and-release.md | 30 +++++--- docs/03-toolchains.md | 44 ++++++----- docs/04-build-from-source.md | 41 +++++++--- docs/05-mcpp-toml.md | 17 ++++- docs/06-workspace.md | 4 +- docs/08-toolchain-internals.md | 21 +++-- docs/09-release.md | 23 +++--- docs/spec/README.md | 2 +- docs/spec/package-identity.md | 19 +++-- docs/zh/00-getting-started.md | 48 +++++++----- docs/zh/01-examples.md | 5 +- docs/zh/02-pack-and-release.md | 60 ++++++++++----- docs/zh/03-toolchains.md | 37 ++++----- docs/zh/04-build-from-source.md | 39 ++++++---- docs/zh/05-mcpp-toml.md | 13 +++- docs/zh/06-workspace.md | 4 +- docs/zh/08-toolchain-internals.md | 27 ++++--- docs/zh/09-release.md | 18 +++-- 25 files changed, 465 insertions(+), 308 deletions(-) diff --git a/.agents/skills/mcpp-contributing/SKILL.md b/.agents/skills/mcpp-contributing/SKILL.md index 7dd58b6e..9ab7c9d2 100644 --- a/.agents/skills/mcpp-contributing/SKILL.md +++ b/.agents/skills/mcpp-contributing/SKILL.md @@ -11,8 +11,8 @@ mcpp 项目的贡献流程:先创建 Issue → 切分支 → 实现改动 → - 仓库:https://github.com/mcpp-community/mcpp - 构建:`mcpp build`(C++23 模块自举) -- 测试:`tests/e2e/` 下的 bash 脚本 -- CI:GitHub Actions,base 为 `main` 的 PR 自动触发 +- 测试:`mcpp test` 覆盖 `tests/**/*.cpp`,`tests/e2e/` 提供真实二进制的端到端脚本 +- CI:GitHub Actions,base 为 `main` 的 PR 触发分平台构建、测试与 E2E 检查 ## 核心原则 @@ -126,20 +126,28 @@ git checkout -b / **构建验证** ```bash -# 找到 mcpp 二进制 -ls target/x86_64-linux-gnu/*/bin/mcpp -# 构建 - build +# 用现有 bootstrap mcpp 自举构建 +mcpp build +# 选择刚生成的 target/**/bin/mcpp(Windows 为 mcpp.exe),不要硬编码宿主 triple + --version ``` **测试** ```bash -bash tests/e2e/01_help_and_version.sh # 基础测试 -bash tests/e2e/.sh # 相关测试 -# 新功能应创建对应 E2E 测试 +# C++ 单元/集成测试:由刚构建的二进制发现 tests/**/*.cpp + test +# 端到端测试:显式把刚构建的二进制交给脚本 +# 路径必须是刚构建产物的绝对路径;Windows 使用 mcpp.exe。 +MCPP= bash tests/e2e/01_help_and_version.sh +MCPP= bash tests/e2e/.sh +# 新功能按变更契约补充 focused unit/integration 和/或 E2E 覆盖 ``` +E2E 并不保证完全离线:部分脚本需要工具链、索引或 capability provider。 +按 CI 等价方式设置 `MCPP_HOME`、镜像和其他 capability 后再运行;不要让缓存命中 +或空 workspace 选择冒充行为覆盖。 + ### 4. 提交 PR **提交信息前缀**:`feat:` / `fix:` / `refactor:` / `test:` / `docs:` / `chore:` @@ -154,8 +162,9 @@ gh pr create \ Closes # ## Test plan -- [ ] mcpp build 通过 -- [ ] E2E 测试通过" +- [ ] 文档-only:示例与链接已按当前实现复核,无运行时行为变更 +- [ ] 涉及行为或测试文档时:`mcpp test`(unit/integration)通过 +- [ ] 涉及行为或测试文档时:相关 E2E 脚本使用 fresh `MCPP` 通过" ``` **PR 要求**: @@ -173,14 +182,16 @@ gh pr checks # 查看状态 gh run view --log-failed # 查看失败日志 ``` -CI 包含三个平台: +CI 由分平台的基础构建/单元集成检查与独立 E2E 检查组成: | Workflow | 平台 | 内容 | |----------|------|------| -| `ci` | Linux x86_64 | 自举构建 + E2E 测试 | -| `ci-macos` | macOS ARM64 | 自举构建 + E2E 测试 | -| `ci-windows` | Windows x86_64 | 自举构建 + E2E 测试 | +| `ci-linux` / `ci-linux-e2e` | Linux x86_64 | 自举构建、unit/integration / 分片 E2E | +| `ci-macos` / `ci-macos-e2e` | macOS ARM64 | 自举构建、unit/integration / E2E | +| `ci-windows` / `ci-windows-e2e` | Windows x86_64 | 自举构建、toolchain 回归 / E2E | +| `cross-build-test` | Linux/Windows cross targets | 交叉构建、产物运行与 MinGW/Wine 检查 | +| `ci-aarch64-fresh-install` | Linux ARM64 native | path-filtered fresh install、原生自举与 musl `build.mcpp` host-helper 回归 | -**三个平台全部通过才能合入。** 如果某个平台失败: +**以 PR 实际 required checks 为准,所有未跳过的 required checks 必须通过。** 如果某个平台失败: 1. 下载日志分析原因 2. 修复后 push 到同一分支,CI 自动重跑 3. 如果是 flaky test,在 PR 中说明 @@ -242,7 +253,7 @@ gh pr merge --merge src/ ├── cli.cppm ← 命令行入口 ├── config.cppm ← 全局配置 -├── manifest.cppm ← mcpp.toml 解析 +├── manifest/ ← manifest 模型、TOML/xpkg 解析 ├── platform/ ← 平台抽象层(所有平台相关代码) │ ├── platform.cppm ← 统一外观模块 │ ├── common.cppm ← 平台常量与检测 @@ -260,7 +271,8 @@ src/ ├── modgraph/ ← 模块图扫描验证 ├── pack/ ← 打包发布 └── xlings.cppm ← xlings 抽象层 -tests/e2e/ ← E2E 测试脚本 +tests/unit/ ← C++ unit/integration tests (`mcpp test`) +tests/e2e/ ← E2E 测试脚本 (`MCPP=...` + `run_all.sh`) docs/ ← 用户文档 .agents/docs/ ← 设计文档 .agents/skills/ ← Agent 技能文档 @@ -270,6 +282,7 @@ docs/ ← 用户文档 - C++23 模块项目,修改模块时注意 import 依赖顺序 - 平台相关代码统一放 `src/platform/`,不在其他模块中直接使用 `#if defined` -- E2E 测试应独立运行,不依赖网络 +- E2E 测试应声明所需 capability,并使用隔离的 `MCPP_HOME`;需要网络/索引的脚本 + 不得被描述为完全离线 - 不确定方向时先在 Issue 讨论再动手 - **永远走 PR 流程,不直接 push main** diff --git a/.agents/skills/mcpp-release/SKILL.md b/.agents/skills/mcpp-release/SKILL.md index 9be37e0f..24dfd403 100644 --- a/.agents/skills/mcpp-release/SKILL.md +++ b/.agents/skills/mcpp-release/SKILL.md @@ -24,8 +24,9 @@ description: Use when releasing a new version of mcpp — bumps version, creates ## Overview -mcpp 的版本号存在于 **四个位置**,但它们分属**两组,在发布流程的两个不同时刻更新**。 -把四处一起 bump 是一个会让全部 CI 变红的经典错误 —— 见下面第二组的解释。 +mcpp 有 **三个持久化版本位置**,以及 `ci-fresh-install` 的一个运行时推导值。它们分属 +两组,在不同时间更新;把"正在构建的版本"与 bootstrap pin 一起前移会让 CI 尝试安装 +尚未发布的 mcpp。 **第一组:正在构建的版本**(发布时改,走 bump PR) @@ -35,31 +36,22 @@ mcpp 的版本号存在于 **四个位置**,但它们分属**两组,在发 这两处必须**在同一个 commit 里**一起改:`tests/e2e/01_help_and_version.sh` 交叉比对 `mcpp.toml` 与 `mcpp --version`,只改一处 CI 立刻红。 -**第二组:bootstrap pin —— CI 用哪个 mcpp 来自举**(发布并进索引之后才改) +**第二组:bootstrap pin —— CI 用哪个 mcpp 来自举**(发布并进索引之后才可改) 3. `.xlings.json` → `workspace.mcpp` — CI bootstrap 装哪个 mcpp -4. `.github/workflows/ci-fresh-install.yml` → `MCPP_PIN` — 全新安装验证的目标版本 -这两处指向的是一个**已经发布、且已经进了索引**的版本。在 bump PR 里把它们一起挪到新版, -等于让每一个 CI job 去 `xlings install` 一个还不存在的 mcpp —— 全线红。 -所以它们在 bump PR 里保持**上一个已发布版本**不动,直到发布收尾那一步才前移 -(见「发布后的收尾」第 3 步)。`check_version_pins.sh` 正是按这个语义校验的:它只要求 -两处 pin **彼此相等**、且**不得新于**正在构建的版本,并不要求等于它。 +`.xlings.json` 必须指向一个**已经发布、镜像并进入索引**的版本。因此它在 bump PR 中 +保持已有的可安装版本,直到发布收尾时才可前移。 -对照最近一次发布:`fd27314`(bump 到 2026.7.29.1)只动了第一组两个文件,第二组仍停在 -2026.7.28.2;`fde3b70` 才在发布、镜像、进索引之后把 pin 推到 2026.7.29.1。 +`ci-fresh-install.yml` 的 `MCPP_PIN` 不是持久化 pin:`wait-index` 从最新 GitHub +Release 推导一次,所有安装 job 消费同一个输出。绝不能手工编辑或恢复字面量 +`MCPP_PIN`,否则 index guard 和实际安装版本会再次漂移。 -**版本不一致会导致 release smoke test 失败**(CI 检查 `mcpp --version` 是否匹配 tag)。 -第二组历史上多次漂移(`MCPP_PIN` 曾落后五个版本),所以现在有机器校验: - -```bash -bash .github/tools/check_version_pins.sh -``` - -它同时校验第二组不变量:**`.github/` 下所有 xlings pin 必须等于 `src/xlings.cppm` 的 -`pinned::kXlingsVersion`**(当前 16 个 pin 点、7 个文件,含 release.yml 里三处硬编码的 -aarch64 tarball 字面量)。`kXlingsVersion` 是唯一真源,也是 release 打进 -`/registry/bin/xlings` 的那一份。改 xlings 版本只改常量,然后跑这个脚本找出其余落点。 +`.github/tools/check_version_pins.sh` 的目标是校验版本关系和 xlings pin;但当前版本在 +第 95 行有 Bash 语法错误,不能把它的执行当成有效验证。修复该实现前,手工核对 +`mcpp.toml` 与 `MCPP_VERSION` 相等、`.xlings.json` 不领先于正在构建的版本;也不要 +通过修改文档或 workflow 绕开动态 `MCPP_PIN` 设计。`src/xlings.cppm` 的 +`pinned::kXlingsVersion` 仍是 xlings 版本的唯一真源。 ## 发布步骤 @@ -71,12 +63,15 @@ git checkout main && git pull origin main gh run list --branch main --limit 3 ``` -所有 CI(ci / ci-macos / ci-windows)必须为 `success`。不要在 CI 红的时候发版。 +以分支保护和 `gh pr checks ` 显示的 actual required checks 为准。 +在 main 上监控当前运行时,检查 `ci-linux`、`ci-linux-e2e`、`ci-macos`、 +`ci-macos-e2e`、`ci-windows`、`ci-windows-e2e` 与 `cross-build-test` 的结果; +跳过或非 required 的 workflow 不是合入 gate。不要在 required CI 红的时候发版。 ### 2. bump 版本号(第一组两处,单个 commit,走 PR) -**只改第一组的两个文件**,并且在同一个 commit 里。bootstrap pin(`.xlings.json`、 -`MCPP_PIN`)**不要动** —— 它们指向上一个已发布版本,见 Overview。 +**只改第一组的两个文件**,并且在同一个 commit 里。`.xlings.json` bootstrap pin +**不要动**;`MCPP_PIN` 是 workflow 运行时推导值,绝不能手工编辑,见 Overview。 ```bash # 日期版本:当天序号从 .1 起;.0 仅用于正式/稳定版 @@ -87,8 +82,8 @@ git checkout -b "chore/bump-$NEW_VERSION" sed -i "s/^version.*=.*/version = \"$NEW_VERSION\"/" mcpp.toml sed -i "s/MCPP_VERSION = \".*\"/MCPP_VERSION = \"$NEW_VERSION\"/" src/toolchain/fingerprint.cppm -# 机器校验(building 是新版、bootstrap pin 仍是旧版,是预期状态) -bash .github/tools/check_version_pins.sh +# 当前 check_version_pins.sh 有 Bash 语法错误;在它修复前手工确认: +# mcpp.toml 与 MCPP_VERSION 相等,.xlings.json 仍是已发布的 bootstrap 版本。 # 自查:构建产物真的报新版本。注意 target/ 目录名带指纹哈希, # 版本一变就是新目录 —— 用 `ls -dt` 取最新的那个,`head -1` 会拿到旧二进制。 @@ -154,8 +149,8 @@ gh release view "v$NEW_VERSION" - `install.sh` - `SHA256SUMS` -**顺带核对体积**(2026.7.29.1 起,见下方"载荷瘦身"):linux 两个 tarball 应在 -**5MB 上下**。如果又回到 30MB 量级,说明 strip 断言被绕过了,先查再发。 +同时比较 Linux 资产与最近一次成功 release 的体积。若出现明显回升,先确认 +strip 和打包步骤的断言仍然执行,再继续发布。 ## Release CI 详解 @@ -170,20 +165,18 @@ gh release view "v$NEW_VERSION" 5. Linux: `mcpp self env` 中 MCPP_HOME 正确解析 6. xlings 二进制已捆绑 -### 载荷瘦身(2026.7.29.1 起) +### 载荷瘦身 每个 linux 平台在**打包后、打 tar 前**调用 `.github/tools/slim_linux_payload.sh`, strip `bin/mcpp` 与 `registry/bin/xlings` 并**断言结果**(`file` 不得再含 `not stripped`)。 -为什么必须断言:在此之前,vendored 的 xlings 从来没被 strip 过(97.3MB,带 -`debug_info`),而 x86_64 那句 `strip` 跑在 `mcpp pack` **之前** —— pack 会重建 -二进制把它覆盖掉,于是直到 2026.7.28.2 发布的 `bin/mcpp` 一直是未 strip 的。 -一个不校验效果的 `strip` 等于注释。修完 linux-x86_64 tarball 从 **34.81MB 降到 -4.62MB(7.5×)**。 +为什么必须断言:单独执行一次 `strip` 不足以证明最终 tarball 已变小,后续的 +`mcpp pack` 可能重建并覆盖二进制。检查最终 payload 的 `file` 输出和资产体积,而非 +依赖固定的 MB 数或历史发布大小。 -macOS / Windows **故意不做**:载荷本来就 6.1MB / 4.2MB,且 strip Mach-O 会让 -ad-hoc 签名失效。 +macOS / Windows **故意不做**:strip Mach-O 会让 ad-hoc 签名失效;按各平台的 +release 规则验证最终资产,不要套用 Linux 的 strip 判断。 ### publish-ecosystem:镜像 + 索引(发布的后半程) @@ -216,11 +209,10 @@ gh pr merge --repo openxlings/xim-pkgindex --squash --admin # 2) 真实验证(注意:不带 @版本 不会升级已装的旧版) xlings update && xlings install mcpp@$NEW_VERSION -y -# 3) bootstrap pin 收尾 —— 第二组两处,到这一步才前移 -# 新版此时已发布、已镜像、已进索引,CI 装得到,pin 才可以指向它 +# 3) bootstrap pin 收尾 —— 仅 .xlings.json;新版此时已发布、已镜像、已进索引 sed -i "s/\"mcpp\": \"[^\"]*\"/\"mcpp\": \"$NEW_VERSION\"/" .xlings.json -sed -i "s/MCPP_PIN: '[^']*'/MCPP_PIN: '$NEW_VERSION'/" .github/workflows/ci-fresh-install.yml -bash .github/tools/check_version_pins.sh +# 不编辑 ci-fresh-install.yml 的 MCPP_PIN:它由 wait-index 运行时推导。 +# 当前 check_version_pins.sh 有 Bash 语法错误;手工确认 pin 关系。 git commit -am "ci: workspace mcpp bootstrap pin -> $NEW_VERSION (released, mirrored, indexed)" ``` @@ -254,11 +246,11 @@ gh workflow run bump-formula.yml -R mcpp-community/homebrew-mcpp | 症状 | 原因 | 修复 | |------|------|------| | `mcpp X.Y.Z-1` 但 tag 是 `vX.Y.Z` | `fingerprint.cppm` 版本未更新 | 更新 `MCPP_VERSION`,重新打 tag | -| bump PR 里**所有** CI job 都红在 bootstrap,报 `package 'mcpp@X.Y.Z' not found` | 把第二组的 bootstrap pin 也一起 bump 了,CI 去装一个还没发布的版本 | 把 `.xlings.json` / `MCPP_PIN` 回退到上一个已发布版本,发布收尾时再前移 | +| bump PR 里**所有** CI job 都红在 bootstrap,报 `package 'mcpp@X.Y.Z' not found` | 把 `.xlings.json` bootstrap pin 一起 bump 了,CI 去装一个还没发布的版本 | 把 `.xlings.json` 回退到上一个已发布版本;不要修改运行时推导的 `MCPP_PIN` | | 自查 `--version` 显示旧版本,但源码已改 | `target//<指纹>/` 的指纹随版本变,`ls \| head -1` 取到了上一次构建的目录 | 用 `ls -dt … \| head -1` 取最新构建 | | Smoke test 输出旧版本 | CI 缓存了旧的 sandbox/target | 删除 GitHub Actions cache 后重跑 | -| e2e `01_help_and_version.sh` 挂 | 只改了 `mcpp.toml` 没改 `fingerprint.cppm`(它把两者交叉比对) | 同步四处版本;注意这个 e2e 只在部分分片里跑,可能表现为"只有某个平台红" | -| xlings bootstrap 失败 | xlings 版本不兼容 | 改 `src/xlings.cppm::kXlingsVersion`(**唯一真源**)后跑 `check_version_pins.sh` 找出其余 15 个 pin 点 | +| e2e `01_help_and_version.sh` 挂 | 只改了 `mcpp.toml` 没改 `fingerprint.cppm`(它把两者交叉比对) | 同步两处正在构建的版本;注意这个 e2e 只在部分分片里跑,可能表现为"只有某个平台红" | +| xlings bootstrap 失败 | xlings 版本不兼容 | 改 `src/xlings.cppm::kXlingsVersion`(唯一真源),再核对引用它的 workflow 与脚本;当前 pin-check 脚本修复前不能依赖它完成扫描 | | macOS/Windows 构建失败 | 需要等 Linux job 先完成 | 检查 Linux job 是否成功 | | `slim: FAIL: ... still not stripped` | strip 工具没生效/被 pack 覆盖 | 别绕过断言——它就是为了拦住 34.8MB 的 tarball 再次发出去 | | mirror leg 报 `missing/unverified` | 资产没传上去或还没传播 | 先 GET 核验(**必须 GET,`curl -I` 会骗你**),gitcode 用 `gitcode.com` 直链而非 `api.` 主机;确认缺件后本地补传再 `gh run rerun --failed`(脚本幂等,已验证的资产会跳过) | @@ -310,9 +302,9 @@ gh workflow run release.yml --ref "v$NEW_VERSION" | `mcpp.toml` | `version = "X.Y.Z"` — 项目版本,release.yml 由它推导 tag | | `src/toolchain/fingerprint.cppm` | `MCPP_VERSION = "X.Y.Z"` — 编译期版本常量 | | `.xlings.json` | `workspace.mcpp` — CI bootstrap 装哪个 mcpp(发布**后**才 bump) | -| `.github/workflows/ci-fresh-install.yml` | `MCPP_PIN` — 全新安装验证目标(发布**后**才 bump) | -| `src/xlings.cppm` | `kXlingsVersion` — xlings pin 的**唯一真源**(其余 15 处由脚本校验) | -| `.github/tools/check_version_pins.sh` | 机器校验上述两组不变量,别靠肉眼 | +| `.github/workflows/ci-fresh-install.yml` | `MCPP_PIN` — 由 `wait-index` 从最新 release 推导,**从不手工 bump** | +| `src/xlings.cppm` | `kXlingsVersion` — xlings pin 的**唯一真源** | +| `.github/tools/check_version_pins.sh` | 版本/pin 校验的预期 guard;当前有 Bash 语法错误,修复前须手工核对 | | `.github/tools/slim_linux_payload.sh` | linux 载荷 strip + 断言 | | `.github/tools/mirror_res.sh` | 双端镜像(并发上传 + leg deadline + 完整性 gate) | | `.github/tools/gtc` | GitCode CLI(release create/upload、PR) | @@ -321,6 +313,7 @@ gh workflow run release.yml --ref "v$NEW_VERSION" | `CHANGELOG.md` | Release notes 来源(按 `## [X.Y.Z]` 提取) | > **注意版本 bump 的两个阶段**:`mcpp.toml` + `fingerprint.cppm` 在发版**前**改 -> (它们定义要发什么);`.xlings.json` + `MCPP_PIN` 在发版**成功后**改(它们指向 -> bootstrap 用哪个已发布版本)。`check_version_pins.sh` 认得这个差异,不会因为 -> bootstrap pin 落后一版就报错。 +> (它们定义要发什么);`.xlings.json` 只在发版成功、镜像并进索引后才可更新 +> (它指定 bootstrap 使用的已发布版本)。`MCPP_PIN` 是被测版本的运行时推导值, +> 不属于任何手工 bump 阶段。当前 `check_version_pins.sh` 的 Bash 语法错误修复前, +> 这些关系须手工核对。 diff --git a/.agents/skills/mcpp-usage/SKILL.md b/.agents/skills/mcpp-usage/SKILL.md index d5472dce..4ca8f052 100644 --- a/.agents/skills/mcpp-usage/SKILL.md +++ b/.agents/skills/mcpp-usage/SKILL.md @@ -21,7 +21,7 @@ mcpp 是一个现代 C++ 模块化构建工具,纯 C++23 模块编写,已实 | `mcpp new ` | 创建项目 | | `mcpp build` | 构建 | | `mcpp run [-- args]` | 构建并运行 | -| `mcpp test [-- args]` | 运行测试 | +| `mcpp test [pattern] [-- args]` | 发现并运行 `tests/**/*.cpp` 测试 | | `mcpp add [@ver]` | 添加依赖 | | `mcpp remove ` | 移除依赖 | | `mcpp update [pkg]` | 更新依赖 | @@ -29,20 +29,25 @@ mcpp 是一个现代 C++ 模块化构建工具,纯 C++23 模块编写,已实 | `mcpp toolchain list` | 查看工具链 | | `mcpp toolchain install gcc 16` | 安装工具链 | | `mcpp pack` | 打包 | +| `mcpp why [toolchain|runtime|deps]` | 解释解析出的构建决策 | +| `mcpp --offline` | 只使用已有本地状态 | | `mcpp self doctor` | 环境诊断 | | `mcpp explain ` | 错误码解释 | ## 安装 ```bash -# 推荐 +# 推荐;Windows 请在 PowerShell 中运行 xlings install mcpp -y -# 或一键脚本 +# 或 Unix release 一键脚本(仅 Linux x86_64/aarch64 与 macOS ARM64) curl -fsSL https://github.com/mcpp-community/mcpp/releases/latest/download/install.sh | bash ``` -安装到 `~/.mcpp/`,自动加入 PATH。首次运行自动安装 GCC 工具链到隔离沙盒。 +一键脚本不支持 Windows;Windows 使用 PowerShell 的 xlings 安装命令。安装到 +`~/.mcpp/` 后会自动加入 PATH。首次使用时,mcpp 按宿主选择默认工具链并 +安装到隔离沙盒:Linux 通常为 GCC,macOS 为 LLVM,Windows 在有可用 MSVC +时为 LLVM,否则为面向 `x86_64-windows-gnu` 的 MinGW-w64 GCC。 ## 创建项目 @@ -52,17 +57,19 @@ mcpp build mcpp run ``` -生成的 `mcpp.toml`: +生成的项目包含最小 manifest 和可立即运行的 smoke test: ```toml [package] name = "hello" - -[targets.hello] -kind = "bin" -main = "src/main.cpp" +version = "0.1.0" +description = "A modular C++23 package" +license = "Apache-2.0" ``` +`src/main.cpp` 会自动推断为 binary target,`tests/test_smoke.cpp` 会由 +`mcpp test` 自动发现;无需手写 `[targets.hello]`。 + ## mcpp.toml 配置 ```toml @@ -71,11 +78,11 @@ name = "myapp" version = "0.1.0" [targets.myapp] -kind = "bin" # bin / lib / shared / test +kind = "bin" # bin / lib / shared; tests are discovered from tests/**/*.cpp main = "src/main.cpp" -[dependencies] -gtest = "1.15.2" # SemVer: ^, ~, 范围, 精确 +[dev-dependencies] +gtest = "1.15.2" # 仅测试使用;SemVer: ^, ~, 范围, 精确 [toolchain] default = "gcc@16.1.0" @@ -90,6 +97,7 @@ mcpp toolchain list # 查看已装 mcpp toolchain install gcc 16 # 装 GCC 16 mcpp toolchain install llvm 20 # 装 LLVM 20 mcpp toolchain default gcc@16.1.0 # 设默认 +mcpp build --target x86_64-linux-musl # 需要全静态 Linux 产物时显式选择 ``` ## 工作空间 @@ -122,9 +130,10 @@ internal-lib = "1.0.0" | 问题 | 解决 | |---|---| | 首次构建慢 | 正常,需下载工具链。后续使用缓存 | -| command not found | 重启终端或 `source ~/.bashrc` | +| command not found | 重开终端。Unix release 安装脚本应确认 `~/.mcpp/bin` 在当前 shell 的 `PATH` 中;经 xlings 安装则确认 xlings 当前激活的 bin 目录。Windows 不要执行 `source`,重开 PowerShell 后用 `Get-Command mcpp.exe` 验证命令已激活。 | | 编译错误 | `mcpp clean && mcpp build`,确认 `mcpp toolchain list` | | 依赖找不到 | `mcpp index update`,确认 `mcpp search ` | +| 需要无网络构建 | 使用 `mcpp --offline` 或设置 `MCPP_OFFLINE=1`;缺失的工具链/依赖会直接报错 | ## 问题反馈 diff --git a/README.md b/README.md index 9ae9e768..96c0ab06 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ## Why mcpp -mcpp is built specifically for **C++23 module-first development**. If you want to use `import std`, module interface units (`.cppm`), module partitions, and other modern C++ features in your project, mcpp gives you a smooth, friendly experience on Linux and macOS ARM64: +mcpp is built specifically for **C++23 module-first development**. If you want to use `import std`, module interface units (`.cppm`), module partitions, and other modern C++ features in your project, mcpp gives you a smooth, friendly experience on Linux, macOS ARM64, and Windows x86_64: - **Modular by default** — projects created by `mcpp new` use C++23 modules directly; `import std` just works - **File-level incremental builds** — three-layer optimization based on P1689 dyndep (front-end dirty check + per-file scanning + BMI restat); only the modules that actually changed get recompiled @@ -102,13 +102,15 @@ binary, so `xlings use mcpp ` switches them too. **Other options**
-Option 1 — one-line install script +Option 1 — one-line installer (Linux x86_64/aarch64, macOS ARM64) ```bash curl -fsSL https://github.com/mcpp-community/mcpp/releases/latest/download/install.sh | bash ``` -Installs into `~/.mcpp/` and adds it to your shell PATH. Deleting `~/.mcpp` uninstalls cleanly. +This installer does not support Windows; use the PowerShell xlings route above. +It installs into `~/.mcpp/` and adds it to your shell PATH. Deleting `~/.mcpp` +uninstalls cleanly.
@@ -171,20 +173,25 @@ mcpp run ``` hello/ ├── mcpp.toml ← project manifest -└── src/ - └── main.cpp ← import std; works directly +├── src/ +│ └── main.cpp ← import std; works directly +└── tests/ + └── test_smoke.cpp ← discovered by `mcpp test` ``` ```toml # mcpp.toml [package] -name = "hello" - -[targets.hello] -kind = "bin" -main = "src/main.cpp" +name = "hello" +version = "0.1.0" +description = "A modular C++23 package" +license = "Apache-2.0" ``` +The built-in scaffold relies on convention: it does not write `[targets.hello]`. +`src/main.cpp` infers the binary target, and `mcpp test` automatically discovers +`tests/test_smoke.cpp`. + ### Using module libraries Add a two-line dependency to `mcpp.toml` to pull in a community module library from [mcpplibs](https://github.com/mcpplibs): @@ -207,7 +214,7 @@ import mcpplibs.cmdline;
Build system -- Native C++20/23 module support (interface units, implementation units, module partitions) +- Native C++20/23/26 module support (interface units, implementation units, module partitions), plus `c++latest` / `c++fly` experimental modes - Fully automatic precompilation and caching of `import std` / `import std.compat` - Three-layer incremental optimization: front-end dirty check + per-file P1689 dyndep + BMI copy-if-different restat - Fingerprinted BMI cache: hashed by compiler/flags/standard library, shared across projects @@ -222,7 +229,7 @@ import mcpplibs.cmdline; Toolchain management - Bundled GCC 16.1.0 + LLVM/Clang 20.1.7, one-command install -- Fully static musl-gcc toolchain (default) +- Host-aware defaults: native glibc GCC on Linux x86_64, musl GCC on other Linux architectures, LLVM on macOS and on Windows with usable MSVC, MinGW-w64 GCC on bare Windows - Multiple versions side by side: `mcpp toolchain install gcc 16` / `mcpp toolchain install llvm 20` - Isolated sandbox: all toolchains live in `~/.mcpp/registry/`, leaving the system untouched - Per-platform selection: `linux = "gcc@16"`, `macos = "llvm@20"` @@ -257,8 +264,8 @@ import mcpplibs.cmdline;
Packaging & publishing -- `mcpp pack`: three Linux release modes — static (fully static musl) / bundle-project / bundle-all -- Fully static musl binaries: single-file distribution, no glibc dependency (Linux x86_64) +- `mcpp pack`: four Linux release modes — system / vendored (default) / self-contained / static; `bundle-project` and `bundle-all` remain compatibility aliases +- Fully static musl binaries: single-file distribution, no glibc dependency (matching Linux x86_64 or aarch64 target) - `mcpp publish`: generates xpkg.lua + publishes to a package index - Automatic RPATH fix-up via patchelf (Linux) @@ -272,6 +279,8 @@ import mcpplibs.cmdline; - `mcpp test [pattern] [-- args]` — auto-discover and run tests (filter by name; `--list`, `--timeout `, `--message-format json`) - `mcpp search` — search package indices - `mcpp add / remove / update` — dependency management +- `mcpp why [toolchain|runtime|deps]` — explain resolved build decisions +- `mcpp --offline` / `MCPP_OFFLINE=1` — use only already available local state - `mcpp explain E0001` — detailed error-code explanations - `mcpp self doctor` — environment self-diagnosis @@ -303,7 +312,8 @@ the right toolchain payload is resolved and installed automatically. ✅ verified — CI builds **and executes** the artifact end-to-end (qemu/wine included) | 🔄 planned -> Release binaries for Linux are fully static musl builds (`x86_64-linux-musl`). +> Linux release binaries are fully static musl builds for x86_64 and aarch64 +> (`x86_64-linux-musl` and `aarch64-linux-musl`). > Legacy spellings — `x86_64-w64-mingw32`, `gcc@16.1.0-musl`, `mingw-cross@…`, > `musl-gcc@…` — stay permanently accepted as aliases and normalize to the > canonical forms above. @@ -359,7 +369,7 @@ Contributions via issues and PRs are welcome. The project accepts contributions **Basic workflow** 1. Open an issue — for bug fixes, new features, or improvements, start a discussion in [issues](https://github.com/mcpp-community/mcpp/issues) first -2. Implement the change — fork the repo, create a branch, implement and verify (`mcpp build` + E2E tests) +2. Implement the change — fork the repo, create a branch, and verify according to scope (`mcpp build` plus relevant tests for behavior changes; examples and links for documentation-only changes) 3. Submit a PR — use `gh pr create` and make sure CI passes 4. CI must pass — PRs with failing CI will not be merged diff --git a/README.zh-CN.md b/README.zh-CN.md index 787db9ae..721f6e50 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -28,7 +28,7 @@ ## 为什么选择 mcpp -mcpp 专门为 **C++23 模块化开发** 打造。如果你想在项目中使用 `import std`、模块接口单元(`.cppm`)、模块分区等现代 C++ 特性,mcpp 在 Linux 和 macOS ARM64 上能为你提供便捷且友好的开发体验: +mcpp 专门为 **C++23 模块化开发** 打造。如果你想在项目中使用 `import std`、模块接口单元(`.cppm`)、模块分区等现代 C++ 特性,mcpp 在 Linux、macOS ARM64 和 Windows x86_64 上能为你提供便捷且友好的开发体验: - **默认模块化** — `mcpp new` 创建的项目模板直接使用 C++23 模块,`import std` 开箱即用 - **文件级增量构建** — 基于 P1689 dyndep 的三层优化(前端脏检查 + 逐文件扫描 + BMI restat),只重编真正变化的模块 @@ -101,13 +101,14 @@ xlings install mcpp-short-cmd -y **其他方式**
-方式 1 — 一键安装脚本 +方式 1 — 一键安装脚本(Linux x86_64/aarch64、macOS ARM64) ```bash curl -fsSL https://github.com/mcpp-community/mcpp/releases/latest/download/install.sh | bash ``` -安装到 `~/.mcpp/`,自动加进 shell PATH。删除 `~/.mcpp` 即可干净卸载。 +该脚本不支持 Windows;请使用上方 PowerShell 的 xlings 安装方式。它会安装到 +`~/.mcpp/`,并自动加入 shell PATH。删除 `~/.mcpp` 即可干净卸载。
@@ -170,20 +171,24 @@ mcpp run ``` hello/ ├── mcpp.toml ← 工程描述 -└── src/ - └── main.cpp ← import std; 直接可用 +├── src/ +│ └── main.cpp ← import std; 直接可用 +└── tests/ + └── test_smoke.cpp ← `mcpp test` 自动发现 ``` ```toml # mcpp.toml [package] -name = "hello" - -[targets.hello] -kind = "bin" -main = "src/main.cpp" +name = "hello" +version = "0.1.0" +description = "A modular C++23 package" +license = "Apache-2.0" ``` +内置脚手架采用约定优于配置,不写 `[targets.hello]`:`src/main.cpp` 会推断出 +binary target,`mcpp test` 会自动发现 `tests/test_smoke.cpp`。 + ### 使用模块化库 在 `mcpp.toml` 中添加两行依赖,即可引用 [mcpplibs](https://github.com/mcpplibs) 社区模块化库: @@ -206,7 +211,7 @@ import mcpplibs.cmdline;
构建系统 -- C++20/23 模块原生支持(接口单元、实现单元、模块分区) +- C++20/23/26 模块原生支持(接口单元、实现单元、模块分区),另有 `c++latest` / `c++fly` 实验模式 - `import std` / `import std.compat` 全自动预编译与缓存 - 三层增量优化:前端脏检查 + 逐文件 P1689 dyndep + BMI copy-if-different restat - 指纹化 BMI 缓存:按编译器/标志/标准库哈希,跨项目共享 @@ -221,7 +226,7 @@ import mcpplibs.cmdline; 工具链管理 - 内置 GCC 16.1.0 + LLVM/Clang 20.1.7,一键安装 -- musl-gcc 全静态工具链(默认) +- 首次运行按宿主选择:Linux x86_64 使用原生 glibc GCC,其他 Linux 架构使用 musl GCC,macOS 与具备可用 MSVC 的 Windows 使用 LLVM,裸 Windows 使用 MinGW-w64 GCC - 多版本共存:`mcpp toolchain install gcc 16` / `mcpp toolchain install llvm 20` - 隔离沙盒:所有工具链在 `~/.mcpp/registry/`,不影响系统 - 按平台指定:`linux = "gcc@16"`, `macos = "llvm@20"` @@ -256,8 +261,8 @@ import mcpplibs.cmdline;
打包与发布 -- `mcpp pack`:三种 Linux 发布模式 — static(musl全静态)/ bundle-project / bundle-all -- musl 全静态二进制:单文件可分发,无 glibc 依赖(Linux x86_64) +- `mcpp pack`:四种 Linux 发布模式 — system / vendored(默认)/ self-contained / static;`bundle-project` 与 `bundle-all` 仍是兼容别名 +- musl 全静态二进制:单文件可分发,无 glibc 依赖(匹配的 Linux x86_64 或 aarch64 target) - `mcpp publish`:生成 xpkg.lua + 发布到包索引 - 自动 patchelf 修正 RPATH(Linux) @@ -271,6 +276,8 @@ import mcpplibs.cmdline; - `mcpp test [pattern] [-- args]` — 自动发现并运行测试(按名字过滤;`--list`、`--timeout `、`--message-format json`) - `mcpp search` — 搜索包索引 - `mcpp add / remove / update` — 依赖管理 +- `mcpp why [toolchain|runtime|deps]` — 解释已解析的构建决策 +- `mcpp --offline` / `MCPP_OFFLINE=1` — 仅使用已存在的本地状态 - `mcpp explain E0001` — 错误码详细解释 - `mcpp self doctor` — 环境自诊断 @@ -300,7 +307,8 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family ✅ 已验证——CI 端到端构建**并真实执行**产物(含 qemu/wine)| 🔄 计划中 -> Linux release 二进制为 musl 全静态构建(`x86_64-linux-musl`)。 +> Linux release 二进制为 x86_64 与 aarch64 的 musl 全静态构建 +> (`x86_64-linux-musl` 与 `aarch64-linux-musl`)。 > 旧拼写——`x86_64-w64-mingw32`、`gcc@16.1.0-musl`、`mingw-cross@…`、`musl-gcc@…`—— > 作为别名**永久接受**,归一到上表的 canonical 形式。 > @@ -353,7 +361,7 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family **基本流程** 1. 创建 Issue — Bug 修复、新功能、优化等,先在 [issues](https://github.com/mcpp-community/mcpp/issues) 创建讨论 -2. 实现改动 — Fork 仓库,创建分支,实现并验证(`mcpp build` + E2E 测试) +2. 实现改动 — Fork 仓库,创建分支,并按改动范围验证(行为改动运行 `mcpp build` 与相关测试;纯文档改动复核示例和链接) 3. 提交 PR — 使用 `gh pr create`,确保 CI 通过 4. CI 必须通过 — CI 不通过的 PR 不会被合入 diff --git a/docs/00-getting-started.md b/docs/00-getting-started.md index d53f958d..6b7b23a5 100644 --- a/docs/00-getting-started.md +++ b/docs/00-getting-started.md @@ -4,9 +4,8 @@ ## Installation -You only need a Linux x86_64 or macOS ARM64 environment — no need to install GCC, xlings, or any other dependencies beforehand. -On its first run, mcpp installs the default toolchain into an isolated sandbox (`~/.mcpp/`). -Linux defaults to musl-gcc, while macOS defaults to LLVM/Clang. +Supported hosts are Linux x86_64 / aarch64, macOS ARM64, and Windows x86_64. You do not need to install GCC, xlings, or any other build dependency beforehand. +On its first run, mcpp installs a default toolchain into an isolated sandbox (`~/.mcpp/`). The choice is host-aware: Linux x86_64 uses `gcc@16.1.0`; other Linux architectures use `gcc@15.1.0-musl`; macOS uses `llvm@20.1.7`; Windows uses `llvm@20.1.7` when usable MSVC is available and otherwise uses `gcc@16.1.0` for `x86_64-windows-gnu`. We recommend installing via [xlings](https://xlings.d2learn.org), which keeps mcpp isolated from your system environment: @@ -14,27 +13,35 @@ We recommend installing via [xlings](https://xlings.d2learn.org), which keeps mc xlings install mcpp -y ``` -Alternatively, use the one-line installer script (xlings is bundled, and everything is installed under `~/.mcpp/`): +Alternatively, on Linux x86_64/aarch64 or macOS ARM64, use the one-line +installer script (xlings is bundled, and everything is installed under +`~/.mcpp/`): ```bash curl -fsSL https://github.com/mcpp-community/mcpp/releases/latest/download/install.sh | bash ``` +The script does not support Windows; install through the PowerShell xlings +command in the README instead. + For full installation instructions (including xlings install commands, Windows support, and more), see the ["Installation" section of the README](../README.md#install). -Once installation is complete, start a new shell session or run `source ~/.bashrc`, then verify: +Once installation is complete, start a new shell session, then verify: ```bash mcpp --version -# mcpp 2026.7.29.1 +# mcpp ``` > [!TIP] -> If you get `command not found`, it usually means `~/.mcpp/bin` has not yet -> been added to the current shell's PATH. Restart your terminal, or run -> `source ~/.bashrc` (use `~/.zshrc` for zsh, or `exec fish` for fish) to -> apply the change. You can also invoke mcpp directly via its absolute path -> `~/.mcpp/bin/mcpp`. +> If the Unix release installer reports `command not found`, `~/.mcpp/bin` has +> not yet been added to the current shell's PATH. Restart your terminal, or run +> `source ~/.bashrc` (use `~/.zshrc` for zsh, or `exec fish` for fish) to apply +> the change; `~/.mcpp/bin/mcpp` is the direct path for that installer. If you +> installed through xlings, use the active xlings bin directory instead. On +> Windows, install through the PowerShell xlings command, restart PowerShell +> rather than using `source`, and verify the active command with +> `Get-Command mcpp.exe`. ## Creating a Project @@ -46,12 +53,14 @@ This generates the following directory structure: ``` hello/ -├── mcpp.toml ← project manifest -└── src/ - └── main.cpp +├── mcpp.toml ← project manifest +├── src/ +│ └── main.cpp +└── tests/ + └── test_smoke.cpp ← runs with `mcpp test` ``` -By default, `src/main.cpp` is a C++23 modular hello world: +The generated manifest contains only package metadata; mcpp infers a binary target from `src/main.cpp`. By default, that file is a C++23 modular hello world: ```cpp import std; @@ -67,15 +76,13 @@ int main() { ```bash mcpp build # Compiling hello v0.1.0 (.) -# Finished release [optimized] in 1.6s mcpp run # Hello from hello! # Built with import std + std::println on modular C++23. ``` -The first build downloads the default toolchain (musl-gcc 15.1 on Linux, LLVM/Clang 20.1 on macOS), -showing progress and speed along the way. Once downloaded, all mcpp projects share the same sandbox. +The first build downloads the host-aware default toolchain, showing progress and speed along the way. Once downloaded, all mcpp projects share the same sandbox. ## Incremental Compilation and Testing @@ -124,12 +131,13 @@ and adds it to the build graph. For a complete example, see `02-with-deps` in `mcpp pack` bundles your build artifacts and runtime dependencies into a self-contained tarball that can be distributed independently: ```bash -mcpp pack # default bundle-project, includes the project's third-party .so files -mcpp pack --mode static # fully static (musl) -mcpp pack --mode bundle-all # fully self-contained, including libc and ld-linux +mcpp pack # vendored by default: bundle project third-party .so files +mcpp pack --mode system # rely on target-system libraries +mcpp pack --mode static # fully static musl build +mcpp pack --mode self-contained # bundle loader, libc, and dependencies ``` -For the differences between the three modes and their artifact layouts, see [02 — Packaging and Release](02-pack-and-release.md). +For the differences between the four modes and their artifact layouts, see [02 — Packaging and Release](02-pack-and-release.md). `bundle-project` and `bundle-all` remain accepted aliases for `vendored` and `self-contained`. ## Further Reading @@ -145,3 +153,4 @@ For the differences between the three modes and their artifact layouts, see [02 run `mcpp new --list-templates imgui` to see all templates the library provides, or use `--template imgui:docking` to select a specific one). - Explaining default decisions: `mcpp why [toolchain|runtime|deps]`; host capability checkup: `mcpp self doctor`; machine-readable resolution manifest: the build artifact `target///resolution.json`. +- Offline operation: `mcpp --offline` or `MCPP_OFFLINE=1` prevents index refreshes, downloads, and toolchain installation. diff --git a/docs/01-examples.md b/docs/01-examples.md index 20c7cdb1..91d40554 100644 --- a/docs/01-examples.md +++ b/docs/01-examples.md @@ -23,7 +23,7 @@ examples. | # | Path | Description | Key Concepts | |---|---|---|---| -| 01 | [`examples/01-hello`](../examples/01-hello/) | Minimal single-file project with `import std` | The default output structure of `mcpp new` | +| 01 | [`examples/01-hello`](../examples/01-hello/) | Minimal single-file project with `import std` | The minimal package shape (`mcpp new` also emits `tests/test_smoke.cpp`) | | 02 | [`examples/02-with-deps`](../examples/02-with-deps/) | Adds the `mcpplibs.cmdline` dependency to parse command-line arguments | `[dependencies]`, SemVer, `mcpp.lock` | | 03 | [`examples/03-pack-static`](../examples/03-pack-static/) | Produces a fully static release package via `mcpp pack --mode static` | `[target.]` and `[pack]` configuration | @@ -31,8 +31,9 @@ examples. We recommend reading them in numerical order: -1. **`01-hello`** shows the minimal skeleton of an mcpp project (`mcpp.toml` and - `src/main.cpp`) and demonstrates the basic usage of `import std`. +1. **`01-hello`** shows the minimal package skeleton (`mcpp.toml` and + `src/main.cpp`) and demonstrates the basic usage of `import std`. The current + `mcpp new` scaffold also emits `tests/test_smoke.cpp`. 2. **`02-with-deps`** builds on the previous example by introducing an external dependency, covering the lock-file mechanism and how the modular package index works. diff --git a/docs/02-pack-and-release.md b/docs/02-pack-and-release.md index f52cc129..e57b870d 100644 --- a/docs/02-pack-and-release.md +++ b/docs/02-pack-and-release.md @@ -1,9 +1,9 @@ # 02 — Packaging for Release -> The binary produced by `mcpp build` only runs on the local machine —— both -> the loader and the RUNPATH point into `~/.mcpp/`. To distribute it to other -> machines or deploy it to a server, use `mcpp pack` to produce a -> self-contained tarball. +> A default dynamically linked binary produced by `mcpp build` normally has a +> loader and RUNPATH tied to the build sandbox. To distribute it to other +> machines or deploy it to a server, use `mcpp pack` to produce a release +> tarball or directory with the appropriate runtime closure. ## Two axes: target (libc) × mode (bundling depth) @@ -19,7 +19,7 @@ Distribution is two orthogonal choices: | `system` | every `.so` (incl. third-party) | smallest | `.deb`/`.rpm`, same-distro fleet (pkg manager declares deps) | | `vendored` (default) | libc / libstdc++ / loader | +a few MB | Mainstream distros (Ubuntu 22+, Debian 12+, RHEL 9+) | | `self-contained` | nothing | +30–50 MB | Any Linux incl. older glibc; bundles closure + `run.sh` wrapper | -| `static` | nothing (single file) | +5–10 MB | musl; any Linux x86_64, Docker scratch, Alpine | +| `static` | nothing (single file) | +5–10 MB | musl; matching Linux x86_64 or aarch64 host, Docker scratch, Alpine | How to choose: @@ -44,6 +44,7 @@ mcpp pack --mode system mcpp pack --mode static mcpp pack --mode self-contained # alias: --mode bundle-all mcpp pack --target x86_64-linux-musl # equivalent to --mode static +mcpp pack --target aarch64-linux-musl # ARM64 equivalent mcpp pack --format dir # output as a directory, no tarball mcpp pack -o myapp.tar.gz # filename only: lands at target/dist/myapp.tar.gz mcpp pack -o /abs/path/myapp.tar.gz # includes a directory: output to the literal path @@ -73,7 +74,7 @@ target/dist/myapp-0.1.0-x86_64-linux-musl-static.tar.gz └── LICENSE ``` -### Mode `bundle-project` (default) +### Mode `vendored` (default; alias: `bundle-project`) ``` target/dist/myapp-0.1.0-x86_64-linux-gnu.tar.gz @@ -95,7 +96,7 @@ base libraries such as `libc`, `libm`, `libstdc++`, `libgcc_s`, and `ld-linux-*` are assumed to already exist on the target system and are not bundled into the tarball. -### Mode `bundle-all` +### Mode `self-contained` (alias: `bundle-all`) ``` target/dist/myapp-0.1.0-x86_64-linux-gnu-bundle-all.tar.gz @@ -117,13 +118,16 @@ With `-o foo.tar.gz`, the top-level directory name also becomes `foo` (the package name and directory name always stay in sync). The ELF specification forbids `PT_INTERP` from using `$ORIGIN`, so in -bundle-all mode the loader is invoked by absolute path through `run.sh` (and +`self-contained` mode the loader is invoked by absolute path through `run.sh` (and the top-level wrapper of the same name): ```sh exec "$here/lib/ld-linux-x86-64.so.2" --library-path "$here/lib" "$here/bin/myapp" "$@" ``` +The layout and wrapper above use an x86_64 example. The packer derives the +loader name from the target; for aarch64 it is `ld-linux-aarch64.so.1`. + ## Configuration Packaging behavior is configured via the `[pack]` section in `mcpp.toml`. The @@ -131,16 +135,22 @@ common fields are: ```toml [pack] -default_mode = "static" # default mode when --mode is omitted +default_mode = "static" # override the normal vendored default for bare `mcpp pack` include = ["share/**", "config/*.toml"] # extra files to bundle exclude = ["debug/**"] -# Fine-tune the bundle-project filtering policy +# Fine-tune the vendored filtering policy. The configuration key keeps its +# established `bundle-project` spelling. [pack.bundle-project] also_skip = ["libcustom.so"] # libraries assumed to exist on the target system force_bundle = ["libfoo.so"] # bundle even if matched by the PEP 600 list ``` +`[pack].default_mode` currently accepts the established manifest spellings +`static`, `bundle-project`, and `bundle-all`; the `system` mode is selected +explicitly with `mcpp pack --mode system`. CLI input accepts both the canonical +and compatibility names described above. + The `static` mode additionally requires a musl toolchain configured under `[target.]`; for the full setup, see the `mcpp.toml` in [`examples/03-pack-static`](../examples/03-pack-static/). diff --git a/docs/03-toolchains.md b/docs/03-toolchains.md index cf79c42f..174abf66 100644 --- a/docs/03-toolchains.md +++ b/docs/03-toolchains.md @@ -8,24 +8,28 @@ C++23 modules are fairly sensitive to compiler versions, and different releases ## Automatic Installation -The first time you run `mcpp build`, if no toolchain is configured yet, mcpp automatically installs the default toolchain for your platform and sets it as the global default: - -``` -First run no toolchain configured — installing gcc@15.1.0-musl (musl, static) as default -Downloading xim:musl-gcc@15.1.0 [====> ] 312 MB / 808 MB 3.7 MB/s -Default set to gcc@15.1.0-musl -``` - -The first-run default is host-aware: Linux x86_64 → `gcc@16.1.0` (glibc — the -native ABI, so X11/GL/system libraries link out of the box); other Linux -arches (aarch64, …) → `gcc@15.1.0-musl` (self-contained, fully static); -macOS and Windows → `llvm@20.1.7`. Fully-static musl output stays one flag -away on any Linux host: `mcpp build --target x86_64-linux-musl`. +The first time you run `mcpp build`, if no toolchain is configured yet, mcpp +installs and persists a default pair for the current host. The choice is +host-aware: + +- Linux x86_64 uses `gcc@16.1.0` for the native glibc ABI, so X11, OpenGL, and + system libraries work out of the box. +- Other Linux architectures use `gcc@15.1.0-musl`, a self-contained static + toolchain. +- macOS uses `llvm@20.1.7`. +- Windows with a usable MSVC installation uses `llvm@20.1.7` for the MSVC ABI. + Without usable MSVC, it uses `gcc@16.1.0` with target + `x86_64-windows-gnu` (MinGW-w64, static by default). + +Fully static musl output remains one flag away on a Linux host: +`mcpp build --target x86_64-linux-musl`. Subsequent builds do not trigger this process again. > [!TIP] -> In CI or offline environments, you can disable automatic installation by setting `MCPP_NO_AUTO_INSTALL=1`. With this set, if no toolchain is installed, `mcpp build` fails immediately instead of making any network requests. +> In CI, set `MCPP_NO_AUTO_INSTALL=1` to disable only automatic toolchain +> installation. For a fully offline command, use `mcpp --offline` or +> `MCPP_OFFLINE=1`; these also prevent index refreshes and downloads. ## The Identity Model: Toolchain × Target @@ -46,7 +50,7 @@ this model with a one-line `note:` hint. ```bash mcpp toolchain install gcc 16.1.0 # host target (GNU libc on Linux) -mcpp toolchain install llvm 20.1.7 # LLVM/Clang, the default on macOS/Windows +mcpp toolchain install llvm 20.1.7 # LLVM/Clang, default on macOS and Windows with usable MSVC mcpp toolchain install gcc 16 --target x86_64-linux-musl # musl target payload mcpp toolchain install --target x86_64-windows-gnu # family omitted → the # target's convention pin (gcc@16.1.0) @@ -250,11 +254,8 @@ If a project needs to pin a specific version rather than rely on the global defa ```toml [toolchain] default = "gcc@16.1.0" - -# you can also dispatch by platform -[toolchain] -linux = "gcc@15.1.0-musl" -macos = "llvm@20" +linux = "gcc@16.1.0" +macos = "llvm@20.1.7" ``` A project-level declaration takes precedence over the global default configuration. @@ -317,8 +318,9 @@ mcpp's runtime behavior can be adjusted with the following environment variables |---|---| | `MCPP_HOME` | Override the sandbox location (default `~/.mcpp/`); an absolute path takes top priority | | `MCPP_NO_AUTO_INSTALL=1` | Disable automatic toolchain installation; useful for CI and offline environments | +| `MCPP_OFFLINE=1` | Never touch the network; equivalent to global `--offline` | | `MCPP_NO_COLOR=1` / `NO_COLOR=1` | Disable colored output | -| `MCPP_LOG=trace\|debug\|info\|warn\|error` | Log level | +| `MCPP_LOG_LEVEL=debug\|info\|warn\|error\|off` | Log level | When `MCPP_HOME` is not set explicitly, mcpp locates the sandbox automatically based on the parent directory of the binary (after a release tarball is extracted to `~/.mcpp/`, `~/.mcpp/` is the home), so the release build runs without any environment variable configuration. diff --git a/docs/04-build-from-source.md b/docs/04-build-from-source.md index 3d88d7e8..df50a451 100644 --- a/docs/04-build-from-source.md +++ b/docs/04-build-from-source.md @@ -17,9 +17,12 @@ cd mcpp ```bash mcpp build # compile the current source with the existing mcpp → ./target/.../bin/mcpp mcpp run -- --version # run the artifact you just built -mcpp test # run tests/unit and tests/e2e +mcpp test # build and run C++ tests discovered under tests/**/*.cpp (including tests/unit) ``` +`mcpp test` does not run the shell end-to-end suite under `tests/e2e/`; run +those separately against the freshly built binary. + The first build automatically fetches the default toolchain; see [03 — Toolchain Management](03-toolchains.md) for details. To produce a fully static binary identical to a release (the path taken by `release.yml`): @@ -35,23 +38,30 @@ mcpp build --target x86_64-linux-musl src/ ├── main.cpp entry point ├── cli.cppm command dispatch and argument parsing -├── manifest.cppm mcpp.toml parsing +├── cli/ command implementations +├── manifest/ manifest model, TOML parsing, and xpkg descriptors ├── lockfile.cppm mcpp.lock ├── version_req.cppm SemVer constraints -├── fetcher.cppm dependency download (git / index / path) +├── fetcher.cppm fetcher façade +├── fetcher/ package/index download and installation ├── config.cppm ~/.mcpp/config.toml ├── bmi_cache.cppm cross-project BMI cache +├── bmi_cache/ cache storage and invalidation ├── dyndep.cppm ninja dyndep generation ├── ui.cppm progress bars and output formatting ├── build/ build orchestration and ninja backend +├── fallback/ fallback resolution paths ├── modgraph/ P1689 module scanning and dependency graph +├── pm/ dependency resolver and package-management commands +├── platform/ platform and process abstractions +├── scaffold/ `mcpp new` templates and project creation ├── toolchain/ toolchain detection, fingerprinting, and std module ├── pack/ mcpp pack implementation ├── publish/ mcpp publish and xpkg generation └── libs/ third-party dependencies (toml parsing, etc.) tests/ -├── unit/ gtest unit tests for each .cppm module +├── unit/ C++ unit and integration tests, generally grouped by subsystem └── e2e/ end-to-end shell scripts (run_all.sh is the CI entry point) ``` @@ -59,18 +69,24 @@ tests/ Tests are split into two layers: -- **Unit tests** live in `tests/unit/test_.cpp`, corresponding one-to-one with `src/.cppm` per module. -- **e2e tests** live in `tests/e2e/NN_.sh` and cover end-to-end behavior by exercising the real `mcpp` binary; `run_all.sh` is the CI entry point. +- **Unit and integration tests** are C++ files discovered by `mcpp test` under + `tests/**/*.cpp`. They are generally named for the subsystem or module they + exercise (for example, `test_pm_lock_io.cpp` and `test_toolchain_triple.cpp`). +- **E2E tests** live in `tests/e2e/NN_.sh` and exercise a real `mcpp` + binary; `run_all.sh` is the CI entry point. -When changing any `.cppm` module under `src/`, check that the corresponding unit test covers your changes; for new features, prefer adding e2e cases. +Choose focused unit and/or E2E coverage according to the contract changed. E2E +scripts may require the same sandbox, mirror, and capability setup used by CI. Run a single e2e script: ```bash -cd tests/e2e -MCPP=$(realpath ../../target/x86_64-linux-musl/*/bin/mcpp) ./02_new_build_run.sh +MCPP= bash tests/e2e/02_new_build_run.sh ``` +Replace `` with the absolute path to the binary built in the +previous step; on Windows that path names `mcpp.exe`. + ## Issue and PR Guidelines ### Issues @@ -78,7 +94,7 @@ MCPP=$(realpath ../../target/x86_64-linux-musl/*/bin/mcpp) ./02_new_build_run.sh File issues at [github.com/mcpp-community/mcpp/issues](https://github.com/mcpp-community/mcpp/issues), ideally including the following: - The full output of `mcpp self env` -- The full output of the failing command (`MCPP_LOG=debug` gives more detail) +- The full output of the failing command (`MCPP_LOG_LEVEL=debug` gives more detail) - Your operating system, distribution, and glibc version (check with `ldd --version`) ### Pull Requests @@ -87,7 +103,10 @@ mcpp is in early iteration and its interfaces may change. Before submitting a PR 1. For changes touching the CLI or the `mcpp.toml` schema, open an issue first to align on direction. 2. Keep each PR focused on a single change; write commit titles in English imperative form (`fix: ...` / `feat: ...`). -3. Confirm that `mcpp test` passes in full before submitting. +3. For behavior changes or test documentation, run `mcpp test` and the relevant + E2E scripts against a fresh binary before submitting. For documentation-only + changes, recheck the examples and links; use `gh pr checks ` for + the PR's actual required checks. ## Community Resources diff --git a/docs/05-mcpp-toml.md b/docs/05-mcpp-toml.md index 66eff6cd..92466f92 100644 --- a/docs/05-mcpp-toml.md +++ b/docs/05-mcpp-toml.md @@ -87,7 +87,7 @@ kind = "lib" # Shared library [targets.mylib] kind = "shared" -soname = "libmylib.so.1" # Optional: ELF/Mach-O ABI name; an alias of the same name is generated at runtime +soname = "libmylib.so.1" # Optional: Linux/ELF ABI name; an alias of the same name is generated at runtime ``` `soname` is the ABI name for a shared library, analogous to `SOVERSION`/`SONAME` in @@ -97,6 +97,12 @@ downstream programs can load the library via its standard ABI name through `DT_NEEDED` or `dlopen()`. This field only applies to `kind = "shared"`, and the value must be a filename basename. +Shared-library targets are currently supported only for Linux/ELF targets. A +`kind = "shared"` target for macOS or Windows (including a cross build) is +rejected before planning because mcpp does not yet model Mach-O install names +or PE import libraries. Use `kind = "lib"` for a static library on those +targets, or build the shared library for Linux. + #### Per-target keys ```toml @@ -227,6 +233,11 @@ on request). `static_stdlib` is the older spelling and still works: `true` means `self-contained`, `false` means `host-coupled`. An explicit `cxx_runtime` wins. +> **Current implementation limitation.** The parser recognizes `cxx_runtime`, +> but the current `[build]` unknown-key allowlist omits it. A normal build can +> therefore emit an unsupported-key warning, and `--strict` rejects the manifest. +> This is an implementation defect, not a different spelling or contract. + **A contract that cannot be honored is reported, never silently downgraded.** If a toolchain ships no `libc++.a`, or a contract has no mechanism on that platform (`self-contained` under the MSVC runtime would need `/MT`, which mcpp does not emit @@ -550,7 +561,7 @@ default = "gcc@16.1.0" # Cross-compilation target override [target.x86_64-linux-musl] -toolchain = "gcc@15.1.0-musl" +toolchain = "gcc@16.1.0" linkage = "static" ``` @@ -1080,7 +1091,7 @@ version = "1.0.0" default = "gcc@16.1.0" [target.x86_64-linux-musl] -toolchain = "gcc@15.1.0-musl" +toolchain = "gcc@16.1.0" linkage = "static" ``` diff --git a/docs/06-workspace.md b/docs/06-workspace.md index 79813468..7b917719 100644 --- a/docs/06-workspace.md +++ b/docs/06-workspace.md @@ -141,14 +141,14 @@ Configuration precedence (highest to lowest): default = "gcc@16.1.0" [target.x86_64-linux-musl] -toolchain = "gcc@15.1.0-musl" +toolchain = "gcc@16.1.0" linkage = "static" ``` ```toml # a member overrides the toolchain [toolchain] -default = "clang@19.0" +default = "llvm@20.1.7" ``` ## 5. Build Commands diff --git a/docs/08-toolchain-internals.md b/docs/08-toolchain-internals.md index cfb7e575..f4bdcb1b 100644 --- a/docs/08-toolchain-internals.md +++ b/docs/08-toolchain-internals.md @@ -23,16 +23,20 @@ ToolchainLinkModel (single resolver for the C-library axis) ├──► build_program (build.mcpp host compiles) └──► cfg regeneration (the human-facing clang++.cfg) ▼ -hermetic link check (`-###` dry-run) ← asserts CRT/loader resolve inside the sandbox +hermetic link check (`-###` dry-run) ← checks sandbox CRT/loader resolution ``` Two principles run through everything: -1. **Sandbox toolchains are self-contained.** A produced binary's CRT startup - objects, libc, and dynamic linker come from sandbox payloads — never - silently from the host. On a machine with no compiler and no - `/usr/lib/**/Scrt1.o` (fresh WSL2, minimal containers), everything still - works; on a machine *with* a host toolchain, nothing leaks in. +1. **Sandbox toolchains are hermetically checked by default.** For the normal + payload-first or sysroot path, a produced binary's CRT startup objects, + libc, and dynamic linker must resolve under allowed sandbox prefixes. This + is not an unconditional containment guarantee: `CLibMode::None` falls back + to host defaults, system/PATH compilers are an explicit host-world choice, + and `[build] allow_host_libs = true` or `MCPP_ALLOW_HOST_LIBS=1` opt out of + the host-library check. On a machine with no compiler and no + `/usr/lib/**/Scrt1.o` (fresh WSL2, minimal containers), the normal sandbox + path still works. 2. **Path knowledge has one owner per layer.** What used to be four divergent copies of "how to link against the payload glibc" is now one resolver (`linkmodel`); what used to be per-entry-path fixup behavior is now one @@ -82,8 +86,9 @@ CLibMode::PayloadFirst glibc/linux-headers xpkgs found (the normal bundled-LLV -L [+ -rpath + --dynamic-linker for clang] CLibMode::Sysroot a usable --sysroot (GCC include-fixed world, self-contained musl sysroots, the macOS SDK) -CLibMode::None nothing usable — host defaults apply and the hermetic - check (§6) reports whatever leaks in +CLibMode::None nothing usable — host defaults apply; the hermetic + check (§6) rejects that leakage unless an explicit + host-library exception is in effect ``` `ClangDriverModel` is the companion for bundled LLVM: mcpp always passes diff --git a/docs/09-release.md b/docs/09-release.md index 26f3c328..0ea4ac49 100644 --- a/docs/09-release.md +++ b/docs/09-release.md @@ -6,18 +6,20 @@ packaging *your own* project see [02 — Packaging for Release](02-pack-and-rele Until now this process lived only in commit messages and workflow comments. One of those commit messages contains a misdiagnosis that is corrected in §5. -## 1. The four version sites are two groups +## 1. Three persistent version sites, plus one derived CI value | Site | Group | Moves when | |---|---|---| | `mcpp.toml` `[package].version` | **being built** | you start work on a new version | | `src/toolchain/fingerprint.cppm` `MCPP_VERSION` | **being built** | same commit as above (compiled-in copy) | | `.xlings.json` `[workspace].mcpp` | **bootstrapped from** | separately, *after* a release is installable | -| `ci-fresh-install.yml` `MCPP_PIN` | ~~bootstrapped from~~ | **nothing — it is derived at run time** (§4) | +| `ci-fresh-install.yml` `MCPP_PIN` | **version under test** | **nothing — it is derived at run time** (§5) | -`.github/tools/check_version_pins.sh` enforces what is left mechanically. The two -"being built" sites must be equal; the bootstrap pin must never be **newer** than -the version being built. +The intended `.github/tools/check_version_pins.sh` guard covers the persistent +relationships: the two "being built" sites must be equal, and the bootstrap +pin must never be **newer** than the version being built. At this revision the +script has a Bash syntax error, so it cannot currently provide that verification; +check these relationships manually until its implementation is repaired. The two groups are deliberately allowed to differ. Bumping them together is what an earlier revision of the pin checker required, and it sent every CI job to @@ -96,8 +98,9 @@ mcpp itself on every platform. Treat it as a *useful check*, not a prerequisite. **The one hard constraint is direction**: the pin must never name a version that is not yet installable. Bump it only after the release is published, mirrored, **and merged into xim-pkgindex** — otherwise every CI job fails with -`package 'mcpp@' not found`. `check_version_pins.sh` enforces the -weaker "never newer than the version being built"; the index condition is on you. +`package 'mcpp@' not found`. Once its syntax issue is repaired, +`check_version_pins.sh` enforces the weaker "never newer than the version being +built"; the index condition is on you. ## 5. `MCPP_PIN` is derived, and why that matters @@ -120,7 +123,9 @@ hardcoded literal only bought the first: The guard was already deriving the right answer and throwing it away. Feeding both from one value makes that disagreement structurally impossible. -`check_version_pins.sh` fails if a literal `MCPP_PIN:` reappears. +The intended `check_version_pins.sh` guard rejects a literal `MCPP_PIN:`. Do +not reintroduce one while its current syntax error is being repaired: a literal +would again let the index guard and the installed version drift apart. > **Correction.** Commit `3b1cb6b` ("bootstrap pin -> 2026.7.29.2") states *"the > index no longer serves .1"* and quotes `version '2026.7.29.1' not found`. That @@ -134,7 +139,7 @@ hardcoded literal only bought the first: ``` [ ] version bumped in mcpp.toml + fingerprint.cppm (one commit) [ ] CHANGELOG entry -[ ] bash .github/tools/check_version_pins.sh +[ ] manually verify `mcpp.toml` = `MCPP_VERSION` and `.xlings.json` is not newer (the current `check_version_pins.sh` has a Bash syntax error) [ ] merge to main, CI green [ ] gh workflow run release.yml --ref main [ ] release.yml green (4 builds + publish-ecosystem) diff --git a/docs/spec/README.md b/docs/spec/README.md index f9ffdc74..1cc4e8a7 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -30,7 +30,7 @@ | 编号 | 标题 | 状态 | 最后修改 | 对应实现 | |---|---|---|---|---| -| [SPEC-001](package-identity.md) | 包身份(`package.namespace` / `package.name`)、`[dependencies]` 选择器与匹配机制 | 评审中 v1.0 | 2026-07-25 | mcpp 0.0.106 | +| [SPEC-001](package-identity.md) | 包身份(`package.namespace` / `package.name`)、`[dependencies]` 选择器与匹配机制 | 评审中 v1.1 | 2026-08-03 | mcpp >= 0.0.106 | ## 文档约定 diff --git a/docs/spec/package-identity.md b/docs/spec/package-identity.md index a261165c..3087cefb 100644 --- a/docs/spec/package-identity.md +++ b/docs/spec/package-identity.md @@ -5,9 +5,9 @@ | **规范编号** | SPEC-001 | | **标题** | 包身份(`package.namespace` / `package.name`)、`[dependencies]` 选择器与匹配机制 | | **状态** | **评审中(Review)** —— 已实现 | -| **版本** | 1.0 | -| **最后修改** | 2026-07-25 | -| **对应实现** | mcpp **0.0.106**(xlings >= 0.4.69) | +| **版本** | 1.1 | +| **最后修改** | 2026-08-03 | +| **最低实现版本** | mcpp **0.0.106**(xlings >= 0.4.69;当前实现为 2026.8.3.2) | | **作者/维护** | mcpp-community | | **相关设计文档** | `.agents/docs/2026-06-20-package-resolution-architecture.md` §4
`.agents/docs/2026-06-26-identity-first-resolution-no-filename.md`
`.agents/docs/2026-07-25-issue278-descriptor-name-form-canonicalization-design.md`
`.agents/docs/2026-07-25-name-namespace-bidirectional-verification-report.md`
`.agents/docs/2026-07-25-name-namespace-canonical-implementation-spec.md` | | **相关 issue** | [mcpp#278](https://github.com/mcpp-community/mcpp/issues/278)
[xlings#381](https://github.com/openxlings/xlings/issues/381) —— 索引键缺命名空间维度(§3.3) | @@ -26,11 +26,13 @@ | 标记 | 含义 | |---|---| -| ✅ **已实现** | 0.0.106 的行为与本规范一致 | +| ✅ **已实现** | 自 mcpp 0.0.106 起的行为与本规范一致 | | ⚠️ **部分实现** | 已有实现,但语义或覆盖面与本规范有差异(差异已注明) | | ❌ **未实现** | 本规范要求但尚未支持;当前行为已注明 | -> **本规范已在 mcpp 0.0.106 全部实现。** 索引作者按 §3 书写即可。0.0.105 及更早版本要求的过渡形态(`name` 必须写成 `.`)仍被接受为**兼容写法**,见 §8。 +> **本规范所需行为自 mcpp 0.0.106 起已全部实现,当前实现继续符合。** 索引作者按 §3 +> 书写即可。0.0.105 及更早版本要求的过渡形态(`name` 必须写成 +> `.`)仍被接受为**兼容写法**,见 §8。 --- @@ -213,7 +215,7 @@ error: dependency 'asio': no package found under the namespaces mcpp searched 有序候选身份列表 [(ns₁,n₁), (ns₂,n₂), …] │ 逐个尝试 ▼ -① 发现:在候选所属索引里定位描述符文件 ← 当前受文件名约束(§3.4) +① 发现:先探测推荐文件名,落空后按声明身份扫描描述符(§3.4) ② 校验:xpkg_lua_identity_matches 复核声明身份 ← §5.2 ③ 收敛:INV-RESOLVE 拒绝裸名命中第三方 ns ← §4.2 ④ 回填:用描述符**声明的** namespace 定身份 ← §5.3 @@ -336,7 +338,7 @@ asio = "1.38.1" → 校验:声明 (chriskohlhoff, chriskohlhoff.asio) 归一化 → (chriskohlhoff, asio) == 候选 ✓ → 身份 (chriskohlhoff, asio) - → wire key chriskohlhoff.asio (§7.2 后:asio) + → wire key chriskohlhoff.asio → target chriskohlhoff:chriskohlhoff.asio@1.38.1 → store dir chriskohlhoff-x-chriskohlhoff.asio (§7.2 后:chriskohlhoff-x-asio) ``` @@ -379,6 +381,7 @@ lua = "0.0.3" | 版本 | 日期 | 变更 | |---|---|---| +| 1.1 | 2026-08-03 | 按当前实现复核:澄清文件名发现是快路径加身份回退扫描,修正 legacy `package.name` 的 wire key 示例,并将 0.0.106 明确为最低实现版本 | | 0.1 | 2026-07-25 | 首版草案。整合 #278 的双向验证结论:确立「身份 = `(namespace, name)`、层级归 `namespace`、`name` 为原子段」为规范形态,并如实标注 0.0.105 的过渡形态(强制 FQN)与全部待实现项 | | 1.0 | 2026-07-25 | **mcpp 0.0.106 全部实现**:身份归一化去 split-on-last-dot、target 用字面 `name`、store 目录、文件名自由(快路径+身份扫描)、`name` 形态校验反转。xlings 0.4.69 修好 #381 后 §3.3 的 `(namespace, name)` 唯一自然成立。状态 草案 → 评审中 | | 0.6 | 2026-07-25 | §3.3 改按 **`(namespace, name)` 唯一**表述(与身份数据模型、xlings 寻址模型一致);单仓同名冲突重新定位为 xlings ≤0.4.68 的**实现缺口**(xlings#381 修复中),不再作为规范约束或索引侧 lint 要求 | @@ -389,5 +392,5 @@ lua = "0.0.3" --- -> **本规范已在 mcpp 0.0.106 全部实现**,状态为「评审中」。 +> **本规范所需行为自 mcpp 0.0.106 起已全部实现,当前实现仍符合**,状态为「评审中」。 > 英文版待补(`docs/spec/` 顶层按仓库惯例为英文,本文档先以中文成稿)。 diff --git a/docs/zh/00-getting-started.md b/docs/zh/00-getting-started.md index 79ea22da..3d5024d6 100644 --- a/docs/zh/00-getting-started.md +++ b/docs/zh/00-getting-started.md @@ -4,9 +4,8 @@ ## 安装 -仅需 Linux x86_64 或 macOS ARM64 环境,无需预先安装 GCC、xlings 或其他依赖。 -mcpp 在首次运行时会将默认工具链安装至独立沙盒(`~/.mcpp/`)。 -Linux 默认使用 musl-gcc,macOS 默认使用 LLVM/Clang。 +支持的宿主为 Linux x86_64 / aarch64、macOS ARM64 与 Windows x86_64,无需预先安装 GCC、xlings 或其他构建依赖。 +mcpp 在首次运行时会将默认工具链安装至独立沙盒(`~/.mcpp/`)。选择会随宿主变化:Linux x86_64 使用 `gcc@16.1.0`; 其他 Linux 架构使用 `gcc@15.1.0-musl`; macOS 使用 `llvm@20.1.7`; Windows 在存在可用 MSVC 时使用 `llvm@20.1.7`,否则使用面向 `x86_64-windows-gnu` 的 `gcc@16.1.0`。 推荐通过 [xlings](https://xlings.d2learn.org) 进行安装,可与系统 环境保持隔离: @@ -15,27 +14,32 @@ Linux 默认使用 musl-gcc,macOS 默认使用 LLVM/Clang。 xlings install mcpp -y ``` -或使用一键安装脚本(内置 xlings,统一安装至 `~/.mcpp/`): +Linux x86_64/aarch64 或 macOS ARM64 也可使用一键安装脚本(内置 xlings,统一安装至 +`~/.mcpp/`): ```bash curl -fsSL https://github.com/mcpp-community/mcpp/releases/latest/download/install.sh | bash ``` +该脚本不支持 Windows;请改用 README 中的 PowerShell xlings 安装命令。 + 完整安装说明(包括 xlings 安装命令、Windows 支持等)参见 [README 的"安装"小节](../../README.zh-CN.md#安装)。 -安装完成后,启动新的 shell 会话或执行 `source ~/.bashrc`,然后验证: +安装完成后,启动新的 shell 会话,然后验证: ```bash mcpp --version -# mcpp 2026.7.29.1 +# mcpp ``` > [!TIP] -> 若提示 `command not found`,通常是 `~/.mcpp/bin` 尚未加入当前 shell -> 的 PATH。重启终端,或执行 `source ~/.bashrc`(zsh 对应 `~/.zshrc`, -> fish 使用 `exec fish`)即可生效。也可直接通过绝对路径 -> `~/.mcpp/bin/mcpp` 调用。 +> Unix release 安装脚本若提示 `command not found`,通常是 `~/.mcpp/bin` +> 尚未加入当前 shell 的 PATH。重启终端,或执行 `source ~/.bashrc`(zsh 对应 +> `~/.zshrc`,fish 使用 `exec fish`)即可生效;该安装方式可直接通过 +> `~/.mcpp/bin/mcpp` 调用。若经 xlings 安装,应使用 xlings 当前激活的 bin +> 目录。Windows 请使用 PowerShell 的 xlings 安装命令,重启 PowerShell 而不是 +> 执行 `source`,并用 `Get-Command mcpp.exe` 确认当前命令。 ## 创建项目 @@ -47,12 +51,14 @@ mcpp new hello && cd hello ``` hello/ -├── mcpp.toml ← 工程描述 -└── src/ - └── main.cpp +├── mcpp.toml ← 工程描述 +├── src/ +│ └── main.cpp +└── tests/ + └── test_smoke.cpp ← 可由 `mcpp test` 运行 ``` -`src/main.cpp` 默认为 C++23 模块化的 hello world: +生成的 manifest 只包含包元数据;mcpp 会从 `src/main.cpp` 推断 binary target。该文件默认为 C++23 模块化的 hello world: ```cpp import std; @@ -68,15 +74,13 @@ int main() { ```bash mcpp build # Compiling hello v0.1.0 (.) -# Finished release [optimized] in 1.6s mcpp run # Hello from hello! # Built with import std + std::println on modular C++23. ``` -首次构建需下载默认工具链(Linux 为 musl-gcc 15.1,macOS 为 LLVM/Clang 20.1), -期间显示进度与速度。下载完成后,所有 mcpp 项目共用同一份沙盒。 +首次构建需下载随宿主选择的默认工具链,期间显示进度与速度。下载完成后,所有 mcpp 项目共用同一份沙盒。 ## 增量编译与测试 @@ -121,12 +125,13 @@ linux 1019s、windows 1289s)。给它一个默认上限会把「慢但正确」 `mcpp pack` 将构建产物与运行期依赖打包为可独立分发的 tarball: ```bash -mcpp pack # 默认 bundle-project,包含项目第三方 .so -mcpp pack --mode static # 全静态(musl) -mcpp pack --mode bundle-all # 全自包含,含 libc 与 ld-linux +mcpp pack # 默认 vendored,打包项目第三方 .so +mcpp pack --mode system # 依赖目标系统提供库 +mcpp pack --mode static # musl 全静态构建 +mcpp pack --mode self-contained # 打包 loader、libc 与依赖 ``` -三种模式的差异及产物布局参见 [02 — 发布打包](02-pack-and-release.md)。 +四种模式的差异及产物布局参见 [02 — 发布打包](02-pack-and-release.md)。`bundle-project` 与 `bundle-all` 仍分别是 `vendored` 与 `self-contained` 的兼容别名。 ## 后续阅读 @@ -142,3 +147,4 @@ mcpp pack --mode bundle-all # 全自包含,含 libc 与 ld-linux `mcpp new --list-templates imgui` 查看库提供的全部模板,`--template imgui:docking` 选指定模板)。 - 解释默认决策:`mcpp why [toolchain|runtime|deps]`;主机能力体检:`mcpp self doctor`; 机器可读解析清单:构建产物 `target///resolution.json`。 +- 离线运行:`mcpp --offline` 或 `MCPP_OFFLINE=1` 可阻止索引刷新、下载和工具链安装。 diff --git a/docs/zh/01-examples.md b/docs/zh/01-examples.md index 7bb74088..98800213 100644 --- a/docs/zh/01-examples.md +++ b/docs/zh/01-examples.md @@ -20,7 +20,7 @@ mcpp build && mcpp run | # | 路径 | 说明 | 涉及的关键概念 | |---|---|---|---| -| 01 | [`examples/01-hello`](../../examples/01-hello/) | 单文件 + `import std` 的最小工程 | `mcpp new` 的默认产物结构 | +| 01 | [`examples/01-hello`](../../examples/01-hello/) | 单文件 + `import std` 的最小工程 | 最小工程形态(`mcpp new` 还会生成 `tests/test_smoke.cpp`) | | 02 | [`examples/02-with-deps`](../../examples/02-with-deps/) | 引入依赖 `mcpplibs.cmdline` 解析命令行参数 | `[dependencies]`、SemVer、`mcpp.lock` | | 03 | [`examples/03-pack-static`](../../examples/03-pack-static/) | 通过 `mcpp pack --mode static` 生成全静态发布包 | `[target.]` 与 `[pack]` 配置 | @@ -29,7 +29,8 @@ mcpp build && mcpp run 建议按编号依次阅读: 1. **`01-hello`** 展示 mcpp 工程的最小骨架(`mcpp.toml` 与 `src/main.cpp`), - 并演示 `import std` 的基本用法。 + 并演示 `import std` 的基本用法。当前 `mcpp new` 脚手架还会生成 + `tests/test_smoke.cpp`。 2. **`02-with-deps`** 在前一示例基础上引入外部依赖,涵盖锁文件机制 与模块化包索引的工作方式。 3. **`03-pack-static`** 演示如何将构建产物打包为可独立分发的单文件 diff --git a/docs/zh/02-pack-and-release.md b/docs/zh/02-pack-and-release.md index 6e7e00df..fe7e285a 100644 --- a/docs/zh/02-pack-and-release.md +++ b/docs/zh/02-pack-and-release.md @@ -1,30 +1,47 @@ # 02 — 发布打包 -> `mcpp build` 产生的二进制仅可在本机运行 —— loader 与 RUNPATH 均指向 -> `~/.mcpp/`。如需分发至其他机器或部署至服务器,应使用 `mcpp pack` -> 生成自包含 tarball。 +> 默认的动态链接 `mcpp build` 产物通常会把 loader 与 RUNPATH 指向构建沙盒。 +> 如需分发至其他机器或部署至服务器,应使用 `mcpp pack` 生成带有适当运行时闭包的 +> 发布 tarball 或目录。 -## 三种模式 +## 两条轴:target(libc) × mode(打包深度) -| 模式 | 说明 | 体积增量 | 兼容性 | +发布有两项正交选择: + +- **libc / static** 是*构建 target* 属性:`--target …-linux-gnu`(glibc) 与 + `--target …-linux-musl`(musl,static)。`--target …-musl` 隐含 `static`。 +- **打包深度** 是*pack* 属性:产物携带多少共享库闭包,由 `--mode` 选择。 + +| 模式 | 宿主必须提供 | 体积 | 使用场景 | |---|---|---|---| -| `static` | musl 全静态,无运行期依赖 | +5–10 MB | 任意 Linux x86_64 | -| `bundle-project`(默认) | 仅打包项目第三方 .so | +几 MB | 主流发行版(Ubuntu 22+, Debian 12+, RHEL 9+ 等) | -| `bundle-all` | 包含 ld-linux、libc、libstdc++ 与项目 .so | +30–50 MB | 包含老旧版本在内的任意 Linux | +| `system` | 所有 `.so`(含第三方) | 最小 | `.deb`/`.rpm`,同发行版集群(包管理器声明依赖) | +| `vendored`(默认) | libc / libstdc++ / loader | +几 MB | 主流发行版(Ubuntu 22+,Debian 12+,RHEL 9+) | +| `self-contained` | 无 | +30–50 MB | 任意 Linux(含旧 glibc);携带闭包与 `run.sh` wrapper | +| `static` | 无(单文件) | +5–10 MB | musl;匹配架构的 Linux x86_64 或 aarch64,Docker scratch,Alpine | 选择建议: -- 命令行工具,或目标为 Docker scratch、Alpine 等最小镜像 → `static` -- 桌面或服务端发布,目标为主流 Linux 发行版 → `bundle-project`(默认) -- 需兼容老旧 CentOS、麒麟等 glibc 版本较低的环境 → `bundle-all` +- `.deb`/`.rpm` 或同发行版内部部署 → `system` +- 桌面或服务端发布,目标为主流 Linux 发行版 → `vendored`(默认) +- 需兼容老旧 CentOS、麒麟等 glibc 版本较低的环境 → `self-contained` +- 单个便携文件、无宿主依赖 → `static` + +### 模式名兼容性 + +上表是规范名称。旧名称仍是**永久兼容别名**:`bundle-project` = `vendored`, +`bundle-all` = `self-contained`。tarball 后缀是被冻结的 wire 格式(由 +`install.sh` 消费),不会跟随名称改变:`vendored` 无后缀,`self-contained` 是 +`-bundle-all`,`static` 是 `-static`,`system` 是 `-system`。 ## 命令 ```bash -mcpp pack # 默认 bundle-project +mcpp pack # 默认 vendored +mcpp pack --mode system mcpp pack --mode static -mcpp pack --mode bundle-all +mcpp pack --mode self-contained # 别名:--mode bundle-all mcpp pack --target x86_64-linux-musl # 等价 --mode static +mcpp pack --target aarch64-linux-musl # ARM64 等价写法 mcpp pack --format dir # 输出为目录,不打包 tarball mcpp pack -o myapp.tar.gz # 仅文件名:落到 target/dist/myapp.tar.gz mcpp pack -o /abs/path/myapp.tar.gz # 含目录:按字面路径输出 @@ -52,7 +69,7 @@ target/dist/myapp-0.1.0-x86_64-linux-musl-static.tar.gz └── LICENSE ``` -### Mode `bundle-project`(默认) +### Mode `vendored`(默认;别名:`bundle-project`) ``` target/dist/myapp-0.1.0-x86_64-linux-gnu.tar.gz @@ -73,7 +90,7 @@ target/dist/myapp-0.1.0-x86_64-linux-gnu.tar.gz `libc`、`libm`、`libstdc++`、`libgcc_s`、`ld-linux-*` 等基础库默认 假设目标系统已具备,不打包进 tarball。 -### Mode `bundle-all` +### Mode `self-contained`(别名:`bundle-all`) ``` target/dist/myapp-0.1.0-x86_64-linux-gnu-bundle-all.tar.gz @@ -93,29 +110,36 @@ target/dist/myapp-0.1.0-x86_64-linux-gnu-bundle-all.tar.gz `-o foo.tar.gz` 时顶层目录名也会变成 `foo`(包名 - 目录名 始终一致)。 -ELF 规范限制 `PT_INTERP` 不能使用 `$ORIGIN`,因此 bundle-all 模式 +ELF 规范限制 `PT_INTERP` 不能使用 `$ORIGIN`,因此 `self-contained` 模式 通过 `run.sh`(及顶层同名 wrapper)以绝对路径方式调用 loader: ```sh exec "$here/lib/ld-linux-x86-64.so.2" --library-path "$here/lib" "$here/bin/myapp" "$@" ``` +上面的布局与 wrapper 以 x86_64 为例。打包器会按 target 推导 loader 名称;aarch64 +对应 `ld-linux-aarch64.so.1`。 + ## 配置项 打包行为通过 `mcpp.toml` 中的 `[pack]` 节配置,常用字段如下: ```toml [pack] -default_mode = "static" # 不带 --mode 时的默认模式 +default_mode = "static" # 覆盖裸 `mcpp pack` 的正常 vendored 默认值 include = ["share/**", "config/*.toml"] # 额外打包的文件 exclude = ["debug/**"] -# 微调 bundle-project 的过滤策略 +# 微调 vendored 的过滤策略。配置键保留既有的 `bundle-project` 拼写。 [pack.bundle-project] also_skip = ["libcustom.so"] # 假定目标系统已具备的库 force_bundle = ["libfoo.so"] # 即使命中 PEP 600 名单也强制打包 ``` +`[pack].default_mode` 当前接受既有 manifest 拼写 `static`、`bundle-project` 和 +`bundle-all`; `system` 通过 `mcpp pack --mode system` 显式选择。CLI 输入同时接受 +上文的规范名称和兼容名称。 + `static` 模式还需在 `[target.]` 中配置 musl 工具链,完整写法 参见 [`examples/03-pack-static`](../../examples/03-pack-static/) 的 `mcpp.toml`。 diff --git a/docs/zh/03-toolchains.md b/docs/zh/03-toolchains.md index 2f8f9c03..4cac79d1 100644 --- a/docs/zh/03-toolchains.md +++ b/docs/zh/03-toolchains.md @@ -12,27 +12,24 @@ C++23 模块对编译器版本较为敏感,不同版本的 GCC / Clang 在模块 ## 自动安装 -首次运行 `mcpp build` 时,若尚未配置工具链,mcpp 会自动安装当前平台 -的默认工具链并将其设为全局默认: +首次运行 `mcpp build` 时,若尚未配置工具链,mcpp 会安装并持久化一对与当前 +宿主匹配的默认值: -``` -First run no toolchain configured — installing gcc@15.1.0-musl (musl, static) as default -Downloading xim:musl-gcc@15.1.0 [====> ] 312 MB / 808 MB 3.7 MB/s -Default set to gcc@15.1.0-musl -``` +- Linux x86_64 使用面向原生 glibc ABI 的 `gcc@16.1.0`,X11、OpenGL 与系统库 + 可直接链接。 +- 其他 Linux 架构使用 `gcc@15.1.0-musl`,这是自包含的全静态工具链。 +- macOS 使用 `llvm@20.1.7`。 +- Windows 存在可用 MSVC 时使用面向 MSVC ABI 的 `llvm@20.1.7`;没有可用 MSVC + 时使用 `gcc@16.1.0` 和 `x86_64-windows-gnu` target(MinGW-w64,默认 static)。 -首跑默认是 host-aware 的:Linux x86_64 → `gcc@16.1.0`(glibc——平台原生 -ABI,X11/GL/系统库开箱即链);其他 Linux arch(aarch64 等)→ -`gcc@15.1.0-musl`(自包含,全静态);macOS 与 Windows → `llvm@20.1.7`。 -任何 Linux 宿主上,全静态 musl 产物始终只差一个参数: +在 Linux 宿主上,全静态 musl 产物始终只差一个参数: `mcpp build --target x86_64-linux-musl`。 后续构建不再触发该流程。 > [!TIP] -> 在 CI 或离线环境中,可通过设置 `MCPP_NO_AUTO_INSTALL=1` 关闭自动 -> 安装行为。此时若未安装工具链,`mcpp build` 将直接报错而不会发起 -> 网络请求。 +> 在 CI 中可设置 `MCPP_NO_AUTO_INSTALL=1` 只关闭工具链自动安装。需要完整 +> 离线时,使用 `mcpp --offline` 或 `MCPP_OFFLINE=1`;它们还会禁止索引刷新和下载。 ## 身份模型:Toolchain × Target @@ -52,7 +49,7 @@ ABI,X11/GL/系统库开箱即链);其他 Linux arch(aarch64 等)→ ```bash mcpp toolchain install gcc 16.1.0 # host target(Linux 上为 GNU libc) -mcpp toolchain install llvm 20.1.7 # LLVM/Clang,macOS/Windows 默认工具链 +mcpp toolchain install llvm 20.1.7 # LLVM/Clang,macOS 与有可用 MSVC 的 Windows 默认工具链 mcpp toolchain install gcc 16 --target x86_64-linux-musl # musl target 的链 mcpp toolchain install --target x86_64-windows-gnu # 省略 family → # 取该 target 的约定 pin(gcc@16.1.0) @@ -166,11 +163,8 @@ windows = "gcc@16" # Windows 上的 gcc family = MinGW-w64 ```toml [toolchain] default = "gcc@16.1.0" - -# 也可按平台分发 -[toolchain] -linux = "gcc@15.1.0-musl" -macos = "llvm@20" +linux = "gcc@16.1.0" +macos = "llvm@20.1.7" ``` 项目级声明优先于全局默认配置。 @@ -230,8 +224,9 @@ mcpp 的运行行为可通过以下环境变量调整: |---|---| | `MCPP_HOME` | 覆盖沙盒位置(默认 `~/.mcpp/`),绝对路径优先级最高 | | `MCPP_NO_AUTO_INSTALL=1` | 禁用工具链自动安装,适用于 CI 与离线环境 | +| `MCPP_OFFLINE=1` | 完全不访问网络,等价于全局 `--offline` | | `MCPP_NO_COLOR=1` / `NO_COLOR=1` | 禁用彩色输出 | -| `MCPP_LOG=trace\|debug\|info\|warn\|error` | 日志级别 | +| `MCPP_LOG_LEVEL=debug\|info\|warn\|error\|off` | 日志级别 | 未显式设置 `MCPP_HOME` 时,mcpp 将基于二进制所在目录的上一级路径 自动定位沙盒位置(release tarball 解压至 `~/.mcpp/` 后,`~/.mcpp/` diff --git a/docs/zh/04-build-from-source.md b/docs/zh/04-build-from-source.md index 5f011228..8ab0125f 100644 --- a/docs/zh/04-build-from-source.md +++ b/docs/zh/04-build-from-source.md @@ -18,9 +18,11 @@ cd mcpp ```bash mcpp build # 使用现成 mcpp 编译当前源码 → ./target/.../bin/mcpp mcpp run -- --version # 运行刚构建出的产物 -mcpp test # 执行 tests/unit 与 tests/e2e +mcpp test # 构建并运行 tests/**/*.cpp 中发现的 C++ 测试(包含 tests/unit) ``` +`mcpp test` 不执行 `tests/e2e/` 下的 shell 端到端套件;应单独让它使用刚构建的二进制。 + 首次构建会自动拉取默认工具链,详见 [03 — 工具链管理](03-toolchains.md)。 @@ -37,23 +39,30 @@ mcpp build --target x86_64-linux-musl src/ ├── main.cpp 入口 ├── cli.cppm 命令分发与参数解析 -├── manifest.cppm mcpp.toml 解析 +├── cli/ 命令实现 +├── manifest/ manifest 模型、TOML 解析与 xpkg 描述符 ├── lockfile.cppm mcpp.lock ├── version_req.cppm SemVer 约束 -├── fetcher.cppm 依赖下载(git / index / path) +├── fetcher.cppm fetcher 门面 +├── fetcher/ 包/索引下载与安装 ├── config.cppm ~/.mcpp/config.toml ├── bmi_cache.cppm 跨项目 BMI 缓存 +├── bmi_cache/ 缓存存储与失效 ├── dyndep.cppm ninja dyndep 生成 ├── ui.cppm 进度条与输出格式 ├── build/ 构建编排与 ninja 后端 +├── fallback/ 回退解析路径 ├── modgraph/ P1689 模块扫描与依赖图 +├── pm/ 依赖解析器与包管理命令 +├── platform/ 平台与进程抽象 +├── scaffold/ `mcpp new` 模板与工程创建 ├── toolchain/ 工具链探测、指纹与 std 模块 ├── pack/ mcpp pack 实现 ├── publish/ mcpp publish 与 xpkg 生成 └── libs/ 第三方依赖(toml 解析等) tests/ -├── unit/ 各 .cppm 模块的 gtest 单元测试 +├── unit/ C++ 单元/集成测试,通常按子系统分组 └── e2e/ 端到端 shell 脚本(run_all.sh 为 CI 入口) ``` @@ -61,21 +70,23 @@ tests/ 测试分为两层: -- **单元测试** 位于 `tests/unit/test_.cpp`,与 `src/.cppm` - 按模块一一对应。 -- **e2e 测试** 位于 `tests/e2e/NN_.sh`,通过执行真实的 `mcpp` +- **单元/集成测试** 是 `tests/**/*.cpp` 下由 `mcpp test` 发现的 C++ 文件。它们通常 + 按所测子系统或模块命名(例如 `test_pm_lock_io.cpp`、`test_toolchain_triple.cpp`)。 +- **E2E 测试** 位于 `tests/e2e/NN_.sh`,通过执行真实的 `mcpp` 二进制覆盖端到端行为;`run_all.sh` 为 CI 调用入口。 -修改 `src/` 下任意 `.cppm` 模块时,应同步检查对应单元测试是否覆盖了 -变更点;新增功能优先补充 e2e 用例。 +根据变更的契约选择有针对性的单元和/或 E2E 覆盖。E2E 脚本可能需要 CI 使用的 +同一套沙盒、镜像与 capability 配置。 执行单个 e2e 脚本: ```bash -cd tests/e2e -MCPP=$(realpath ../../target/x86_64-linux-musl/*/bin/mcpp) ./02_new_build_run.sh +MCPP= bash tests/e2e/02_new_build_run.sh ``` +`` 必须替换为前一步刚构建二进制的绝对路径;Windows 上该文件为 +`mcpp.exe`。 + ## Issue 与 PR 提交规范 ### Issue @@ -84,7 +95,7 @@ MCPP=$(realpath ../../target/x86_64-linux-musl/*/bin/mcpp) ./02_new_build_run.sh 建议附带以下信息: - `mcpp self env` 的完整输出 -- 失败命令的完整输出(配合 `MCPP_LOG=debug` 可获得更详细信息) +- 失败命令的完整输出(配合 `MCPP_LOG_LEVEL=debug` 可获得更详细信息) - 操作系统、发行版、glibc 版本(可通过 `ldd --version` 查看) ### Pull Request @@ -94,7 +105,9 @@ mcpp 处于早期迭代阶段,接口可能调整,提交 PR 前请注意: 1. 涉及 CLI 或 `mcpp.toml` schema 的改动,建议先开 issue 对齐方向。 2. 单个 PR 聚焦单一改动;commit 标题使用英文 imperative 形式 (`fix: ...` / `feat: ...`)。 -3. 提交前确认 `mcpp test` 全部通过。 +3. 行为改动或测试文档改动在提交前运行 `mcpp test`,并让相关 E2E 脚本使用刚构建的 + 二进制通过;纯文档改动复核示例和链接,并用 `gh pr checks ` 确认 PR + 实际 required checks。 ## 社区资源 diff --git a/docs/zh/05-mcpp-toml.md b/docs/zh/05-mcpp-toml.md index 04ffdcdd..18fd215f 100644 --- a/docs/zh/05-mcpp-toml.md +++ b/docs/zh/05-mcpp-toml.md @@ -85,7 +85,7 @@ kind = "lib" # 共享库 [targets.mylib] kind = "shared" -soname = "libmylib.so.1" # 可选: ELF/Mach-O ABI 名称,运行时会生成同名 alias +soname = "libmylib.so.1" # 可选: Linux/ELF ABI 名称,运行时会生成同名 alias ``` `soname` 用于共享库的 ABI 名称,类似 Autotools/CMake 中的 @@ -94,6 +94,11 @@ soname = "libmylib.so.1" # 可选: ELF/Mach-O ABI 名称,运行时会生成同 让下游程序可通过标准 ABI 名称 `DT_NEEDED` 或 `dlopen()` 加载该库。 该字段只对 `kind = "shared"` 有效,值必须是文件名 basename。 +当前共享库目标只支持 Linux/ELF。面向 macOS 或 Windows 的 +`kind = "shared"` 目标(包括交叉构建)会在规划阶段直接拒绝,因为 mcpp +尚未建模 Mach-O install name 或 PE import library。若目标是这些平台,请使用 +`kind = "lib"` 构建静态库,或将共享库目标设为 Linux。 + #### 按目标的键(per-target keys) ```toml @@ -210,6 +215,10 @@ libc++.a/libc++abi.a/libunwind.a。更低的 macOS floor(11–13)需自建 libc+ `static_stdlib` 是旧拼写,仍然有效:`true` 等价于 `self-contained`,`false` 等价于 `host-coupled`。显式写了 `cxx_runtime` 时以后者为准。 +> **当前实现限制。** 解析器能识别 `cxx_runtime`,但当前 `[build]` 未知键白名单漏了 +> 它。因此普通构建可能输出 unsupported-key warning,`--strict` 会拒绝该 manifest。 +> 这是实现缺陷,不是另一种拼写或不同的运行时契约。 + **兑现不了的契约会被报出来,绝不静默降级。** 若工具链不带 `libc++.a`,或某个 契约在该平台上没有对应机制(MSVC 运行时的 `self-contained` 需要 `/MT`,mcpp 目前不发射),构建会打印实际退到了哪一档,而不是悄悄交付一个与 manifest 所述 @@ -839,7 +848,7 @@ version = "1.0.0" default = "gcc@16.1.0" [target.x86_64-linux-musl] -toolchain = "gcc@15.1.0-musl" +toolchain = "gcc@16.1.0" linkage = "static" ``` diff --git a/docs/zh/06-workspace.md b/docs/zh/06-workspace.md index 695b917f..fb05a4bf 100644 --- a/docs/zh/06-workspace.md +++ b/docs/zh/06-workspace.md @@ -141,14 +141,14 @@ mbedtls = "4.0.0" # 覆盖,不使用 workspace 版本 default = "gcc@16.1.0" [target.x86_64-linux-musl] -toolchain = "gcc@15.1.0-musl" +toolchain = "gcc@16.1.0" linkage = "static" ``` ```toml # 某成员覆盖工具链 [toolchain] -default = "clang@19.0" +default = "llvm@20.1.7" ``` ## 5. 构建命令 diff --git a/docs/zh/08-toolchain-internals.md b/docs/zh/08-toolchain-internals.md index ae88beb9..4a61a326 100644 --- a/docs/zh/08-toolchain-internals.md +++ b/docs/zh/08-toolchain-internals.md @@ -22,24 +22,32 @@ ToolchainLinkModel(C 库轴的唯一解析器) ├──► build_program (build.mcpp 宿主编译) └──► cfg 再生 (供人类直接使用的 clang++.cfg) ▼ -hermetic 链接校验(`-###` 干跑) ← 断言 CRT/loader 全部解析进沙箱 +hermetic 链接校验(`-###` 干跑) ← 校验沙箱 CRT/loader 的解析结果 ``` 贯穿一切的两条原则: -1. **沙箱工具链自包含。** 产物的 CRT 启动对象、libc、动态链接器全部来自沙箱 - payload——绝不静默落到宿主。在没有编译器、没有 `/usr/lib/**/Scrt1.o` 的机器 - (全新 WSL2、精简容器)上一切照常;在装了宿主工具链的机器上也不会有任何泄漏。 +1. **沙箱工具链默认接受 hermetic 校验。** 在正常的 payload-first 或 sysroot 路径中, + 产物的 CRT 启动对象、libc、动态链接器必须解析在允许的沙箱前缀内。这不是无条件的 + 隔离保证:`CLibMode::None` 会落到宿主默认,系统/PATH 编译器是显式选择宿主世界, + `[build] allow_host_libs = true` 或 `MCPP_ALLOW_HOST_LIBS=1` 会退出宿主库校验。 + 在没有编译器、没有 `/usr/lib/**/Scrt1.o` 的机器(全新 WSL2、精简容器)上,正常的 + 沙箱路径仍可工作。 2. **每层路径知识只有一个属主。** 过去"如何对 payload glibc 链接"有四份漂移副本, 现在收敛为一个解析器(`linkmodel`);过去 fixup 行为按入口路径各自为政,现在 是一条管线。副本间漂移正是一整类 bug 的来源(issue #195)。 ## 2. 工具链解析 -工具链 spec(`gcc@16.1.0`、`llvm@22.1.8`、`gcc@15.1.0-musl`)映射为 xim 包 -(`src/toolchain/registry.cppm`:`parse_toolchain_spec` → `to_xim_package`, -产出含 xim 包名、版本、前端候选的 `XimToolchainPackage`)。payload 经 xlings -后端解析/自动安装到沙箱 +自 0.0.93 起,身份由两条正交轴构成:`ToolchainSpec` 是 +`(family ∈ gcc|llvm|msvc,version,target Triple)`。`triple.cppm` 负责唯一的 +triple 解析器与封闭的已知 target 词汇表;`compat.cppm` 只处理旧拼写 +(`gcc@15.1.0-musl`、`musl-gcc`、`mingw`、`mingw-cross`、`clang`、 +`-gcc`),解析时归一化且永久兼容。`to_xim_package` 把 +`(family,target,host)` 映射为含 xim 包名、版本、前端候选的 +`XimToolchainPackage`;这也是 Linux 宿主的 `mingw-cross-gcc` 与 Windows +宿主的 `mingw-gcc` 等分发层名称存在的位置,它们不是面向用户的写法。payload 经 +xlings 后端解析/自动安装到沙箱 (`$MCPP_HOME/registry/data/xpkgs/xim-x-//`)。 `detect`/`probe`(`src/toolchain/detect.cppm`、`probe.cppm`)随后推导: @@ -68,7 +76,8 @@ CLibMode::PayloadFirst 找到 glibc/linux-headers xpkg(bundled LLVM 与 -L [clang 另加 -rpath 与 --dynamic-linker] CLibMode::Sysroot 可用的 --sysroot(GCC include-fixed 世界、自包含 musl sysroot、macOS SDK) -CLibMode::None 无可用来源——落宿主默认,由 hermetic 校验(§6)报告泄漏 +CLibMode::None 无可用来源——落宿主默认;除非显式允许宿主库,否则由 + hermetic 校验(§6)拒绝该泄漏 ``` `ClangDriverModel` 服务 bundled LLVM:mcpp 构建永远传 `--no-default-config` diff --git a/docs/zh/09-release.md b/docs/zh/09-release.md index 2c99275a..83df9305 100644 --- a/docs/zh/09-release.md +++ b/docs/zh/09-release.md @@ -6,17 +6,18 @@ 在此之前这套流程只活在 commit message 和 workflow 注释里,其中一条 commit message 里的诊断是错的,已在 §5 更正。 -## 1. 四处版本号 = 两组 +## 1. 三处持久化版本号,加一个运行时推导的 CI 值 | 位置 | 组 | 何时变 | |---|---|---| | `mcpp.toml` `[package].version` | **正在构建的** | 开始做新版本时 | | `src/toolchain/fingerprint.cppm` `MCPP_VERSION` | **正在构建的** | 与上一行同一个 commit(编译进二进制的副本) | | `.xlings.json` `[workspace].mcpp` | **自举起点** | 单独地、在某个版本**已可安装之后** | -| `ci-fresh-install.yml` `MCPP_PIN` | ~~自举起点~~ | **不变 —— 运行时推导**(§4) | +| `ci-fresh-install.yml` `MCPP_PIN` | **被测版本** | **不变 —— 运行时推导**(§5) | -`.github/tools/check_version_pins.sh` 机器校验剩下的部分:两处"正在构建的"必须相等; -自举 pin 永远不得**新于**正在构建的版本。 +`.github/tools/check_version_pins.sh` 的预期职责是机器校验剩下的关系:两处"正在构建的" +必须相等,自举 pin 永远不得**新于**正在构建的版本。当前版本的脚本存在 Bash 语法错误, +因此暂时不能提供这项校验;在实现修复前请手工核对这些关系。 两组刻意允许不同。把它们一起 bump 正是 pin 校验器早期版本要求过的做法, 结果是所有 CI 都去装一个还不存在的版本。 @@ -87,8 +88,8 @@ $(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version **唯一的硬约束是方向**:pin 绝不能指向一个尚不可安装的版本。只在发布已完成、 已镜像、**且已合入 xim-pkgindex 之后**再 bump —— 否则所有 CI 会以 -`package 'mcpp@' not found` 失败。`check_version_pins.sh` 能卡住较弱的 -「不得新于正在构建的版本」;索引那个条件靠你自己把关。 +`package 'mcpp@' not found` 失败。待其语法问题修复后, +`check_version_pins.sh` 能卡住较弱的「不得新于正在构建的版本」;索引那个条件靠你自己把关。 ## 5. `MCPP_PIN` 改为推导,以及这为什么重要 @@ -107,7 +108,8 @@ $(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version 守卫本来就推导出了正确答案,然后把它扔掉了。让两者吃同一个值, 使这种不一致在结构上不可能发生。 -`check_version_pins.sh` 会在字面量 `MCPP_PIN:` 重新出现时报错。 +预期的 `check_version_pins.sh` 会在字面量 `MCPP_PIN:` 重新出现时报错。在当前语法错误 +修复前也不要重新引入字面量,否则索引守卫与实际安装版本又会发生漂移。 > **更正。** commit `3b1cb6b`("bootstrap pin -> 2026.7.29.2")写着 > *"the index no longer serves .1"* 并引用了 `version '2026.7.29.1' not found`。 @@ -120,7 +122,7 @@ $(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version ``` [ ] mcpp.toml + fingerprint.cppm 版本号已 bump(同一个 commit) [ ] CHANGELOG 条目 -[ ] bash .github/tools/check_version_pins.sh +[ ] 手工核对 `mcpp.toml` = `MCPP_VERSION` 且 `.xlings.json` 未领先(当前 `check_version_pins.sh` 有 Bash 语法错误) [ ] 合入 main,CI 全绿 [ ] gh workflow run release.yml --ref main [ ] release.yml 全绿(4 个构建 + publish-ecosystem) From 640e8bc6327468441bbca9fa81db5f57a0e4f768 Mon Sep 17 00:00:00 2001 From: speak-agent Date: Mon, 3 Aug 2026 19:29:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20sync=20with=202026.8.3.4=20?= =?UTF-8?q?=E2=80=94=20ZH=20windows=E2=86=92linux=20section,=20artifact=20?= =?UTF-8?q?naming,=20correct=20the=20pin-checker=20claim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review of #348 against the current tree (2026.8.3.4, three releases newer than when the sync was written). Four things needed adjusting. 1. The ZH toolchain doc was missing THREE sections the EN one has, including the whole `x86_64-linux-musl`-from-Windows feature (2026.8.3.2). A Chinese reader had no way to learn that Windows can produce Linux ELF at all. Added, mirroring EN: "Windows 上产出 Linux ELF", "MSVC(系统工具链,Windows)", and the Clang 20+ operator-template hazard. EN/ZH are now at 16 sections each, and every other doc pair already matched. The EN section's claims were re-derived from `toolchain::host_can_serve` before translating: Windows serves linux targets only when musl AND same arch, which is exactly "no x86_64-linux-gnu, no cross-arch". Both languages now also state the fact that function makes explicit and the docs omitted — **a macOS host has no Linux-targeting payload at all** — and point at `mcpp toolchain list` as the authority so nobody has to memorize the matrix. 2. Artifact naming, undocumented since 2026.8.3.3 changed it. `kind = "lib"` for `x86_64-windows-gnu` produces `libfoo.a`, not `foo.lib`: the static-lib convention splits on the triple's *env* segment, not its OS. That release fixed a name that was wrong (a GNU archive wearing an MSVC name, which MSVC cannot consume), so anyone globbing `*.lib` out of a mingw build needs to know. Added as a table to both languages. 3. `docs/spec/package-identity.md` carried "当前实现为 2026.8.3.2" — already stale two releases later. A spec should not restate a value that expires on every release; the minimum-implementation version (0.0.106) is the load bearing one and it is still there. Dropped the parenthetical rather than bumping it, so it cannot go stale again. 4. **`check_version_pins.sh` does not have a Bash syntax error.** It parses (`bash -n`), runs, and exits 0 — verified on this tree. The failure is `sh check_version_pins.sh`: the script uses process substitution (`done < <(...)`), which dash cannot parse, so it reports "line 95: Syntax error: redirection unexpected". That is the invoking shell, not the script — its shebang is `#!/usr/bin/env bash` and CI invokes it as bash. This mattered enough to chase down because the claim appeared in six places across three files, each instructing the releaser to skip the guard and check pins by hand. That guard is the only machine check for pin drift, and pin drift has broken releases here before. All six now show the correct invocation and explain the sh-vs-bash trap instead. Also noted the cache-entry layout change from 2026.8.3.4 in the `[build] cache` section (both languages): entry layout is versioned, an upgrade that changes it retires older entries and the next build repopulates — nothing to clean by hand. The PR's other two findings were re-verified against the source and are correct: `kind = "shared"` really is refused off Linux/ELF (plan.cppm:513), and `[build].cxx_runtime` really is parsed but missing from `kKnownBuildKeys` — the allowlist's own comment says it "MUST stay in sync with the doc->get_* reads", and it is not. Left as-is; this PR touches no source. --- .agents/skills/mcpp-release/SKILL.md | 28 ++++-- docs/03-toolchains.md | 22 ++++- docs/05-mcpp-toml.md | 8 ++ docs/09-release.md | 23 +++-- docs/spec/package-identity.md | 2 +- docs/zh/03-toolchains.md | 140 +++++++++++++++++++++++++++ docs/zh/05-mcpp-toml.md | 7 ++ docs/zh/09-release.md | 20 ++-- 8 files changed, 222 insertions(+), 28 deletions(-) diff --git a/.agents/skills/mcpp-release/SKILL.md b/.agents/skills/mcpp-release/SKILL.md index 24dfd403..7a656eac 100644 --- a/.agents/skills/mcpp-release/SKILL.md +++ b/.agents/skills/mcpp-release/SKILL.md @@ -47,10 +47,19 @@ mcpp 有 **三个持久化版本位置**,以及 `ci-fresh-install` 的一个 Release 推导一次,所有安装 job 消费同一个输出。绝不能手工编辑或恢复字面量 `MCPP_PIN`,否则 index guard 和实际安装版本会再次漂移。 -`.github/tools/check_version_pins.sh` 的目标是校验版本关系和 xlings pin;但当前版本在 -第 95 行有 Bash 语法错误,不能把它的执行当成有效验证。修复该实现前,手工核对 -`mcpp.toml` 与 `MCPP_VERSION` 相等、`.xlings.json` 不领先于正在构建的版本;也不要 -通过修改文档或 workflow 绕开动态 `MCPP_PIN` 设计。`src/xlings.cppm` 的 +`.github/tools/check_version_pins.sh` 校验版本关系和 xlings pin: + +```bash +bash .github/tools/check_version_pins.sh +``` + +**必须用 `bash` 跑,不能用 `sh`。** 脚本用了进程替换(`done < <(...)`),POSIX +`sh`/dash 解析不了,用 `sh` 调用会在第 95 行附近报 `Syntax error: redirection +unexpected`。那是调用它的 shell 的问题,不是脚本的缺陷 —— 它的 shebang 是 +`#!/usr/bin/env bash`,CI 也是用 `bash` 调的。别据此把这条 guard 当成坏的而跳过: +它是唯一能机器化捕捉 pin 漂移的东西。 + +也不要通过修改文档或 workflow 绕开动态 `MCPP_PIN` 设计。`src/xlings.cppm` 的 `pinned::kXlingsVersion` 仍是 xlings 版本的唯一真源。 ## 发布步骤 @@ -82,8 +91,8 @@ git checkout -b "chore/bump-$NEW_VERSION" sed -i "s/^version.*=.*/version = \"$NEW_VERSION\"/" mcpp.toml sed -i "s/MCPP_VERSION = \".*\"/MCPP_VERSION = \"$NEW_VERSION\"/" src/toolchain/fingerprint.cppm -# 当前 check_version_pins.sh 有 Bash 语法错误;在它修复前手工确认: -# mcpp.toml 与 MCPP_VERSION 相等,.xlings.json 仍是已发布的 bootstrap 版本。 +# 校验:mcpp.toml 与 MCPP_VERSION 相等,.xlings.json 不领先于正在构建的版本。 +bash .github/tools/check_version_pins.sh # 自查:构建产物真的报新版本。注意 target/ 目录名带指纹哈希, # 版本一变就是新目录 —— 用 `ls -dt` 取最新的那个,`head -1` 会拿到旧二进制。 @@ -212,7 +221,7 @@ xlings update && xlings install mcpp@$NEW_VERSION -y # 3) bootstrap pin 收尾 —— 仅 .xlings.json;新版此时已发布、已镜像、已进索引 sed -i "s/\"mcpp\": \"[^\"]*\"/\"mcpp\": \"$NEW_VERSION\"/" .xlings.json # 不编辑 ci-fresh-install.yml 的 MCPP_PIN:它由 wait-index 运行时推导。 -# 当前 check_version_pins.sh 有 Bash 语法错误;手工确认 pin 关系。 +bash .github/tools/check_version_pins.sh # 复核 pin 关系 git commit -am "ci: workspace mcpp bootstrap pin -> $NEW_VERSION (released, mirrored, indexed)" ``` @@ -304,7 +313,7 @@ gh workflow run release.yml --ref "v$NEW_VERSION" | `.xlings.json` | `workspace.mcpp` — CI bootstrap 装哪个 mcpp(发布**后**才 bump) | | `.github/workflows/ci-fresh-install.yml` | `MCPP_PIN` — 由 `wait-index` 从最新 release 推导,**从不手工 bump** | | `src/xlings.cppm` | `kXlingsVersion` — xlings pin 的**唯一真源** | -| `.github/tools/check_version_pins.sh` | 版本/pin 校验的预期 guard;当前有 Bash 语法错误,修复前须手工核对 | +| `.github/tools/check_version_pins.sh` | 版本/pin 校验 guard(**用 `bash` 跑,不能用 `sh`**) | | `.github/tools/slim_linux_payload.sh` | linux 载荷 strip + 断言 | | `.github/tools/mirror_res.sh` | 双端镜像(并发上传 + leg deadline + 完整性 gate) | | `.github/tools/gtc` | GitCode CLI(release create/upload、PR) | @@ -315,5 +324,4 @@ gh workflow run release.yml --ref "v$NEW_VERSION" > **注意版本 bump 的两个阶段**:`mcpp.toml` + `fingerprint.cppm` 在发版**前**改 > (它们定义要发什么);`.xlings.json` 只在发版成功、镜像并进索引后才可更新 > (它指定 bootstrap 使用的已发布版本)。`MCPP_PIN` 是被测版本的运行时推导值, -> 不属于任何手工 bump 阶段。当前 `check_version_pins.sh` 的 Bash 语法错误修复前, -> 这些关系须手工核对。 +> 不属于任何手工 bump 阶段。这些关系由 `bash .github/tools/check_version_pins.sh` 校验。 diff --git a/docs/03-toolchains.md b/docs/03-toolchains.md index 174abf66..416cadcd 100644 --- a/docs/03-toolchains.md +++ b/docs/03-toolchains.md @@ -162,6 +162,18 @@ windows = "gcc@16" # gcc family on Windows = MinGW-w64 # legacy value "mingw@16.1.0" keeps working ``` +Artifact names follow the **target**, and for static libraries the convention +splits on the *env* segment, not on the OS: + +| Target | `kind = "lib"` produces | +|---|---| +| `x86_64-windows-gnu` | `libfoo.a` (GNU convention) | +| `x86_64-windows-msvc` | `foo.lib` (MSVC convention) | + +Before 2026.8.3.3 a mingw build on a Windows host emitted `foo.lib` — a GNU +archive wearing an MSVC name, which MSVC cannot consume. If you have a script +that globs `*.lib` out of a `windows-gnu` build, it needs to glob `*.a` now. + ## Linux ELF from Windows (`x86_64-linux-musl`, no WSL required) The mirror of the section above: a Windows machine producing a **fully static @@ -193,9 +205,13 @@ mcpp: ELF 64-bit LSB executable, x86-64, statically linked, stripped Linux hosts only. The musl target is self-contained and needs neither. Cross-arch from Windows (e.g. `aarch64-linux-musl`) is not available either — -the canadian-cross payload is built per host arch. `mcpp toolchain list` shows -only what the current host can actually install, so if a target is missing from -the Targets block, that host genuinely cannot serve it. +the canadian-cross payload is built per host arch. **A macOS host has no +Linux-targeting payload at all**, so no Linux target is reachable from there. + +You do not have to memorize any of this: `mcpp toolchain list` shows only what +the current host can actually install, so if a target is missing from the +Targets block, that host genuinely cannot serve it (implemented by +`toolchain::host_can_serve`). ## MSVC (System Toolchain, Windows) diff --git a/docs/05-mcpp-toml.md b/docs/05-mcpp-toml.md index 92466f92..542f630b 100644 --- a/docs/05-mcpp-toml.md +++ b/docs/05-mcpp-toml.md @@ -895,6 +895,14 @@ mcpp cache gc --older-than 30d # ...or by how long since they were last use mcpp cache clean [--deps|--std|--all|--legacy] ``` +The on-disk entry layout is versioned. An mcpp release that changes it retires +every older entry at once, so the first build after such an upgrade rebuilds +its dependencies and repopulates — nothing to clean by hand. 2026.8.3.4 did +exactly that: an entry's object paths are now addressed relative to the +*package*, never to the build directory of whichever project happened to +populate the entry first. `mcpp cache verify` additionally reports any entry +whose recorded addresses escape it, so a recurrence is auditable offline. + ### 2.11 `[runtime]` — Host Runtime Capabilities ```toml diff --git a/docs/09-release.md b/docs/09-release.md index 0ea4ac49..3408eddb 100644 --- a/docs/09-release.md +++ b/docs/09-release.md @@ -15,11 +15,19 @@ of those commit messages contains a misdiagnosis that is corrected in §5. | `.xlings.json` `[workspace].mcpp` | **bootstrapped from** | separately, *after* a release is installable | | `ci-fresh-install.yml` `MCPP_PIN` | **version under test** | **nothing — it is derived at run time** (§5) | -The intended `.github/tools/check_version_pins.sh` guard covers the persistent -relationships: the two "being built" sites must be equal, and the bootstrap -pin must never be **newer** than the version being built. At this revision the -script has a Bash syntax error, so it cannot currently provide that verification; -check these relationships manually until its implementation is repaired. +`.github/tools/check_version_pins.sh` machine-checks the persistent +relationships: the two "being built" sites must be equal, and the bootstrap pin +must never be **newer** than the version being built. + +```bash +bash .github/tools/check_version_pins.sh +``` + +Run it with **bash**, not `sh`. It uses process substitution (`done < <(...)`), +which POSIX `sh`/dash cannot parse — `sh check_version_pins.sh` fails with +`Syntax error: redirection unexpected` around line 95. That is the invoking +shell, not a defect in the script: its shebang is `#!/usr/bin/env bash` and CI +invokes it as `bash`. The two groups are deliberately allowed to differ. Bumping them together is what an earlier revision of the pin checker required, and it sent every CI job to @@ -123,8 +131,7 @@ hardcoded literal only bought the first: The guard was already deriving the right answer and throwing it away. Feeding both from one value makes that disagreement structurally impossible. -The intended `check_version_pins.sh` guard rejects a literal `MCPP_PIN:`. Do -not reintroduce one while its current syntax error is being repaired: a literal +`check_version_pins.sh` rejects a literal `MCPP_PIN:`. Do not reintroduce one: a literal would again let the index guard and the installed version drift apart. > **Correction.** Commit `3b1cb6b` ("bootstrap pin -> 2026.7.29.2") states *"the @@ -139,7 +146,7 @@ would again let the index guard and the installed version drift apart. ``` [ ] version bumped in mcpp.toml + fingerprint.cppm (one commit) [ ] CHANGELOG entry -[ ] manually verify `mcpp.toml` = `MCPP_VERSION` and `.xlings.json` is not newer (the current `check_version_pins.sh` has a Bash syntax error) +[ ] `bash .github/tools/check_version_pins.sh` passes (verifies `mcpp.toml` = `MCPP_VERSION`, and `.xlings.json` is not newer) [ ] merge to main, CI green [ ] gh workflow run release.yml --ref main [ ] release.yml green (4 builds + publish-ecosystem) diff --git a/docs/spec/package-identity.md b/docs/spec/package-identity.md index 3087cefb..46261995 100644 --- a/docs/spec/package-identity.md +++ b/docs/spec/package-identity.md @@ -7,7 +7,7 @@ | **状态** | **评审中(Review)** —— 已实现 | | **版本** | 1.1 | | **最后修改** | 2026-08-03 | -| **最低实现版本** | mcpp **0.0.106**(xlings >= 0.4.69;当前实现为 2026.8.3.2) | +| **最低实现版本** | mcpp **0.0.106**(xlings >= 0.4.69) | | **作者/维护** | mcpp-community | | **相关设计文档** | `.agents/docs/2026-06-20-package-resolution-architecture.md` §4
`.agents/docs/2026-06-26-identity-first-resolution-no-filename.md`
`.agents/docs/2026-07-25-issue278-descriptor-name-form-canonicalization-design.md`
`.agents/docs/2026-07-25-name-namespace-bidirectional-verification-report.md`
`.agents/docs/2026-07-25-name-namespace-canonical-implementation-spec.md` | | **相关 issue** | [mcpp#278](https://github.com/mcpp-community/mcpp/issues/278)
[xlings#381](https://github.com/openxlings/xlings/issues/381) —— 索引键缺命名空间维度(§3.3) | diff --git a/docs/zh/03-toolchains.md b/docs/zh/03-toolchains.md index 4cac79d1..20912eb4 100644 --- a/docs/zh/03-toolchains.md +++ b/docs/zh/03-toolchains.md @@ -156,6 +156,98 @@ windows = "gcc@16" # Windows 上的 gcc family = MinGW-w64 # 旧值 "mingw@16.1.0" 原样可用 ``` +产物名跟随 **target**;静态库的命名约定分岔点是 triple 的 *env* 段,而不是 OS: + +| Target | `kind = "lib"` 产出 | +|---|---| +| `x86_64-windows-gnu` | `libfoo.a`(GNU 约定) | +| `x86_64-windows-msvc` | `foo.lib`(MSVC 约定) | + +2026.8.3.3 之前,Windows 宿主上的 mingw 构建产出的是 `foo.lib` —— 一个 GNU +archive 顶着 MSVC 的名字,MSVC 拿不去用。如果你有脚本按 `*.lib` 去捞 +`windows-gnu` 的产物,现在要改成 `*.a`。 + +## Windows 上产出 Linux ELF(`x86_64-linux-musl`,无需 WSL) + +上一节的镜像:一台 Windows 机器直接产出**完全静态的 Linux 二进制**, +不需要 WSL、不需要容器,也不往系统里装任何东西。 + +```bash +mcpp build --target x86_64-linux-musl # Windows 或 Linux 上皆可 +``` + +两种宿主上这条命令**逐字相同**,因为 "交叉" 在 mcpp 里不是一个名字, +它只是 `host ≠ target` 这个关系。由哪个 payload 承接目标是自动分流的: +Linux x86_64 宿主装原生 `musl-gcc`;Windows 宿主装一条 **canadian-cross** +GCC(以 `x86_64-linux-gnu` 构建 → 运行于 `x86_64-w64-mingw32` → 产出 +`x86_64-linux-musl`)。两者都是 GCC 16.1.0,也都带 `bits/std.cc`, +所以 `import std` 在两边行为一致。 + +产物是没有 `PT_INTERP` 的全静态 ELF —— 不挑发行版、不挑 libc, +这正是 musl 成为第一个被打通的 Linux target 的原因: + +```console +$ file mcpp +mcpp: ELF 64-bit LSB executable, x86-64, statically linked, stripped +``` + +Windows 上**不支持** `x86_64-linux-gnu`:glibc target 还需要 `xim:glibc` +与 `xim:linux-headers` 两个 sysroot payload,而它们只为 Linux 宿主发布。 +musl target 自包含,两者都不需要。 + +Windows 上也**不支持跨 arch**(如 `aarch64-linux-musl`)—— canadian-cross +payload 是按宿主 arch 构建的。**macOS 宿主则完全没有面向 Linux 的 payload**, +任何 Linux target 都不可用。 + +判据不必靠记:`mcpp toolchain list` 只列出当前宿主真正装得上的 target, +Targets 一栏里没有的,就是这台机器确实服务不了(实现见 +`toolchain::host_can_serve`)。 + +## MSVC(系统工具链,Windows) + +MSVC 与 mcpp 管理的其它工具链都不同:它是一条**系统工具链**。mcpp 只负责 +定位并识别已安装的 Visual Studio / Build Tools —— **从不**安装、升级或卸载 +MSVC 本身。 + +```bash +mcpp toolchain default msvc +``` + +在装有 MSVC 的机器上,mcpp 会自动定位(依次尝试 `vswhere.exe`、 +`VSINSTALLDIR`/`VS*COMNTOOLS`、标准安装路径),识别涉及的各个版本, +并持久化为稳定 spec `msvc@system`: + +``` +Detected msvc 19.44.35211 (VS 2022 BuildTools) (VC tools 14.44.35207) + cl: C:\Program Files\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\cl.exe + import std: available (std.ixx) +Default set to msvc@system (was: llvm@20.1.7) +``` + +若机器上**没有** MSVC,mcpp 打印安装指引(Visual Studio Installer 勾选 +*Desktop development with C++* 负载,或 `winget install +Microsoft.VisualStudio.2022.BuildTools`)并以非零码退出 —— 需要你自己装好, +再重跑该命令。 + +`mcpp toolchain list` 会把检测到的 MSVC 列在单独的 `System:` 分区, +`mcpp self doctor` 在 Windows 上会报告它的状态。manifest 里可按平台 pin: + +```toml +[toolchain] +windows = "msvc@system" +``` + +`msvc@<前缀>`(如 `msvc@19.44`)是一个 **pin-verify**:mcpp 仍然使用已安装 +的最新 VC tools,但检测到的版本与前缀不符时报错。 + +自 0.0.90 起,**原生 cl.exe 构建可用**:mcpp 从检测到的 VC tools + Windows +SDK 合成 INCLUDE/LIB 环境(不经 `vcvarsall`),把 `std.ixx`/`std.compat.ixx` +staging 成 `.ifc` BMI,用 `/interface /TP /ifcOutput` 编译 `.cppm` 模块单元, +用 `/scanDependencies` 扫描,并通过 response file 调 `link.exe`/`lib.exe` +链接。选择 `/MT` CRT 用 `[target.x86_64-windows-msvc] linkage = "static"` +(或 `mcpp build --static`)—— 不是 `[build] linkage`,那个键不存在。 +缺 Windows SDK 会让构建失败并给出安装指引(`mcpp self doctor` 会报告 SDK 状态)。 + ## 项目级版本锁定 若项目需固定特定版本而不依赖全局默认,可在项目的 `mcpp.toml` 中声明: @@ -239,3 +331,51 @@ mcpp 的运行行为可通过以下环境变量调整: 工具链 ABI 不满足任一依赖的 abi 要求时,构建会**尽早失败**并给出修复建议 (例如 musl-static 工具链遇到 abi:glibc 依赖),取代深层的链接/头文件报错。 查看:`mcpp why toolchain`。 + +## 已知工具链风险:模块接口中的运算符模板(Clang 20+) + +一个导出**替换性运算符模板**的模块,在 Clang 20 或 22 下会毒化所有导入者 +中该运算符的名字查找:任何 `import` 了这个模块、并用到该运算符的 TU +——**无论作用在什么类型上**——都会让前端崩溃(SIGSEGV)。GCC 16 与 +Clang 18 不受影响,所以这是 Clang 18 到 20 之间的一处回归。 + +它正好打在 module-package 这个模式上。包装一个运算符是 `static inline` +模板的上游头文件,再用一个恒真约束镜像它们的签名(跨 TU 包含关系的标准配方), +恰恰就是踩中它的写法。 + +**经验判据:**每个模板形参都应由**第一个**函数实参定死。破坏这一点的形状就是有毒的: + +```cpp +// 有毒 —— `n` 与 `l` 不由第 1 个实参决定 +template +Matx operator*(const Matx& a, const Matx& b); + +// 有毒 —— 第二个 typename 只出现在第 2 个实参里 +template +Vec& operator+=(Vec& a, const Vec& b); + +// 没问题 —— 每个形参都由第 1 个实参定死 +template +Matx operator+(const Matx& a, const Matx& b); +``` + +崩溃是**按名字**触发的:一处被毒化的 `operator*` 声明,会让每个导入者里的 +每一个 `x * y` 都崩,哪怕类型完全无关。函数体本身无关紧要。 + +**绕法**是整体推导操作数类型再加约束,而不是在形参列表里把它们拆开。 +这样保持调用兼容,跨 TU 语义也仍然成立 —— 上游那个精确匹配的 +`static inline` 更特化,在那边照样胜出: + +```cpp +template + requires pick + && __is_same(MA, typename MA::mat_type) + && __is_same(MB, Matx) +inline MA& operator+=(MA& a, const MB& b); +``` + +跟踪于 [mcpp#256](https://github.com/mcpp-community/mcpp/issues/256)。 +`tests/e2e/150_clang_module_operator_template.sh` 是一只跑在内置 LLVM +工具链上的金丝雀 —— 未来某次 Clang 升级修好(或再次弄坏)这一点时, +它会显式暴露出来,而不是悄悄改变包能表达的东西。 diff --git a/docs/zh/05-mcpp-toml.md b/docs/zh/05-mcpp-toml.md index 18fd215f..0a3b7041 100644 --- a/docs/zh/05-mcpp-toml.md +++ b/docs/zh/05-mcpp-toml.md @@ -663,6 +663,13 @@ mcpp cache gc --older-than 30d # 或按"多久没用过"回收 mcpp cache clean [--deps|--std|--all|--legacy] ``` +条目的磁盘布局是带版本的。改动布局的 mcpp 版本会**一次性作废全部旧条目**, +所以升级后的第一次构建会重编依赖并重新填充 —— 不需要手工清理。 +2026.8.3.4 就是这样一次:条目里对象的地址现在相对**包**自身, +而不再相对"最先填充这个条目的那个工程"的构建目录。 +`mcpp cache verify` 另外会报告任何逃出条目的记录地址, +使这条不变量可以离线审计。 + ### 2.11 `[runtime]` — 主机运行时能力 ```toml diff --git a/docs/zh/09-release.md b/docs/zh/09-release.md index 83df9305..e85a2b73 100644 --- a/docs/zh/09-release.md +++ b/docs/zh/09-release.md @@ -15,9 +15,17 @@ | `.xlings.json` `[workspace].mcpp` | **自举起点** | 单独地、在某个版本**已可安装之后** | | `ci-fresh-install.yml` `MCPP_PIN` | **被测版本** | **不变 —— 运行时推导**(§5) | -`.github/tools/check_version_pins.sh` 的预期职责是机器校验剩下的关系:两处"正在构建的" -必须相等,自举 pin 永远不得**新于**正在构建的版本。当前版本的脚本存在 Bash 语法错误, -因此暂时不能提供这项校验;在实现修复前请手工核对这些关系。 +`.github/tools/check_version_pins.sh` 机器校验剩下的关系:两处"正在构建的" +必须相等,自举 pin 永远不得**新于**正在构建的版本。 + +```bash +bash .github/tools/check_version_pins.sh +``` + +必须用 **bash** 跑,不能用 `sh`。脚本用了进程替换(`done < <(...)`), +POSIX `sh`/dash 解析不了 —— `sh check_version_pins.sh` 会在第 95 行附近报 +`Syntax error: redirection unexpected`。那是**调用它的 shell** 的问题, +不是脚本的缺陷:它的 shebang 是 `#!/usr/bin/env bash`,CI 也是用 `bash` 调的。 两组刻意允许不同。把它们一起 bump 正是 pin 校验器早期版本要求过的做法, 结果是所有 CI 都去装一个还不存在的版本。 @@ -108,8 +116,8 @@ $(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version 守卫本来就推导出了正确答案,然后把它扔掉了。让两者吃同一个值, 使这种不一致在结构上不可能发生。 -预期的 `check_version_pins.sh` 会在字面量 `MCPP_PIN:` 重新出现时报错。在当前语法错误 -修复前也不要重新引入字面量,否则索引守卫与实际安装版本又会发生漂移。 +`check_version_pins.sh` 会在字面量 `MCPP_PIN:` 重新出现时报错。不要重新引入字面量, +否则索引守卫与实际安装版本又会发生漂移。 > **更正。** commit `3b1cb6b`("bootstrap pin -> 2026.7.29.2")写着 > *"the index no longer serves .1"* 并引用了 `version '2026.7.29.1' not found`。 @@ -122,7 +130,7 @@ $(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version ``` [ ] mcpp.toml + fingerprint.cppm 版本号已 bump(同一个 commit) [ ] CHANGELOG 条目 -[ ] 手工核对 `mcpp.toml` = `MCPP_VERSION` 且 `.xlings.json` 未领先(当前 `check_version_pins.sh` 有 Bash 语法错误) +[ ] `bash .github/tools/check_version_pins.sh` 通过(校验 `mcpp.toml` = `MCPP_VERSION`,且 `.xlings.json` 未领先) [ ] 合入 main,CI 全绿 [ ] gh workflow run release.yml --ref main [ ] release.yml 全绿(4 个构建 + publish-ecosystem)