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..7a656eac 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,31 @@ 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` 曾落后五个版本),所以现在有机器校验: +`.github/tools/check_version_pins.sh` 校验版本关系和 xlings 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 版本只改常量,然后跑这个脚本找出其余落点。 +**必须用 `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 版本的唯一真源。 ## 发布步骤 @@ -71,12 +72,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,7 +91,7 @@ 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 仍是旧版,是预期状态) +# 校验:mcpp.toml 与 MCPP_VERSION 相等,.xlings.json 不领先于正在构建的版本。 bash .github/tools/check_version_pins.sh # 自查:构建产物真的报新版本。注意 target/ 目录名带指纹哈希, @@ -154,8 +158,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 +174,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 +218,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 运行时推导。 +bash .github/tools/check_version_pins.sh # 复核 pin 关系 git commit -am "ci: workspace mcpp bootstrap pin -> $NEW_VERSION (released, mirrored, indexed)" ``` @@ -254,11 +255,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 +311,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` 跑,不能用 `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) | @@ -321,6 +322,6 @@ 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 阶段。这些关系由 `bash .github/tools/check_version_pins.sh` 校验。 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..416cadcd 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) @@ -158,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 @@ -189,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) @@ -250,11 +270,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 +334,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..542f630b 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" ``` @@ -884,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 @@ -1080,7 +1099,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..3408eddb 100644 --- a/docs/09-release.md +++ b/docs/09-release.md @@ -6,18 +6,28 @@ 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. +`.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 @@ -96,8 +106,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 +131,8 @@ 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. +`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 > index no longer serves .1"* and quotes `version '2026.7.29.1' not found`. That @@ -134,7 +146,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 +[ ] `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/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..46261995 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) | | **作者/维护** | 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..20912eb4 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) @@ -159,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` 中声明: @@ -166,11 +255,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 +316,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/` @@ -244,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/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..0a3b7041 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 所述 @@ -654,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 @@ -839,7 +855,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..e85a2b73 100644 --- a/docs/zh/09-release.md +++ b/docs/zh/09-release.md @@ -6,17 +6,26 @@ 在此之前这套流程只活在 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 +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 都去装一个还不存在的版本。 @@ -87,8 +96,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 +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`。 @@ -120,7 +130,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 +[ ] `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)