Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。

## [0.0.100] — 2026-07-19

> 大型源码直编包(ffmpeg/opencv 级,数千 TU)全平台化批次:#247/#248/#249 平台三修 + 增量构建修复 + build.mcpp 指令面补全(P1)。设计见 `.agents/docs/2026-07-19-large-source-pkg-platform-fixes-and-buildmcpp-generation-design.md`。

### 修复

- **windows driver-style 链接命令行溢出**(#247):gnu 方言(g++/clang++ 作链接驱动——windows 托管 clang-MSVC 即此)的 `cxx_link`/`cxx_archive`/`cxx_shared` 此前内联 `$in`,数千对象直接溢出 CreateProcess 32 KiB 上限。现 windows 上 driver-style 也走 response file;两方言分支收敛为单一 `link_rule` 发射器(msvc 规则文本逐字节不变),POSIX 保持内联零变化。配套:ninja 节点名统一 `generic_string()` 正斜杠——rsp 内容按 GNU 文法分词,反斜杠是转义符(`obj\cli.o` 会被吃成 `objcli.o`)。
- **macOS dep/root build.mcpp 收不到 G3 契约环境**(#248,launcher-unify):非 Linux 的 `capture_exec` 走 shell 字符串拼接,`ENV… cd <cwd> && bin` 里 env 只绑给 `cd`(全仓唯一 env+cwd 双非空调用点恰是 build.mcpp)。现 macOS 与 Linux 同走 posix_spawn 直启路径(child-only env + `addchdir_np`,`environ` 经 `_NSGetEnviron()` portable-correct),顺带消掉 macOS 的 shell quoting/注入面。windows shell 回退补 `cd /d`(跨盘符)。
- **generated_files 每次构建无条件重写毁增量**:materialize 此前不比内容直接写,mtime 抖动令 ninja 把 include 该头的全部 TU 判脏——冻结快照包(config.h × 数千 TU)每次 build 都全量重编。现内容逐字节相同即跳写(变更检测本就由指纹负责)。
- xpkg feature 表未知子键(如 `features.X.include_dirs`)此前静默吞掉,现进 `xpkgUnknownKeys` 走统一告警面。

### 新增

- **`[build] include_dirs_after` → `-idirafter`**(#249):排在工具链系统目录**之后**搜索的 include 目录(descriptor/xpkg 与 mcpp.toml 双文法、`*` tarball 根 glob、沿 Public/Interface 边传播且不升级为 `-I`)。治大小写不敏感 macOS 上依赖源根 `VERSION` 顶替 libc++ `<version>` 一类系统头遮蔽;`-isystem` 不解此症(仍先于默认系统目录)。方言降级单点收敛:cl.exe → 末尾 `/I`,NASM 单元 → 普通 `-I`(NASM 会把 `-idirafter<p>` 误吞成 `-i dirafter<p>`)。附带统一主工程 include 路径的 glob 展开(此前与 dep 路径两套推导)。
- **build.mcpp 指令面补全**(P1,0.0.100+):`mcpp:source=`(选既有 payload 源入编译集,`generated=` 的"绝对路径灰色用法"正名)、`mcpp:include-dir=` / `mcpp:include-dir-after=`(私有 include,Cargo 纪律不进公共接口;typed 通道享方言降级);typed `import mcpp;` 同步 `source()/include_dir()/include_dir_after()`。契约环境拆分 `MCPP_TARGET_OS/ARCH/ENV`(免手撕三元组)。**root build.mcpp 后移至依赖解析之后**,与 dep 一样拿到 `MCPP_DEP_<NAME>_DIR`;指令落通道收敛为 root/dep 共享的单一 fold(防"同一决策两处推导")。顺修:root `build.mcpp` 变更此前被整库 fast-path 吞掉不触发重跑。
- **`mcpp xpkg parse --all-os`**:按 xpm 声明的平台集逐 OS 校验 per-OS 段(构建路径只 splice 宿主段,windows 段的 typo 在 linux CI 上原本不可见);mcpp-index CI 可单 runner lint 多平台描述符。

### 备注

- e2e 新增 141–145(-idirafter 语义/增量跳写/source= /include-dir/root dep-dirs);linux 全量 e2e 134 过(3 项为既知环境性失败)。macOS/windows 断面由平台 CI 与 mcpp-index spike 复现件(PR#89/90/91)收口。

## [0.0.99] — 2026-07-19

> #230–#243 批次收尾:#243 feature 转发落地(0.0.98 只出了设计)+ #238 根因修复随 xlings 0.4.67 vendored 入包 + #230 windows build.mcpp 次生面补齐。设计见 `.agents/docs/2026-07-19-v0.0.99-feature-forwarding-238-230-design.md`。
Expand Down
15 changes: 15 additions & 0 deletions docs/05-mcpp-toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ the package/feature boundary, not on an individual target.
[build]
sources = ["src/**/*.cppm", "src/**/*.cpp"] # Source globs (default: src/**/*.{cppm,cpp,cc,c,S,s,asm})
include_dirs = ["include", "third_party/include"] # Header search paths
include_dirs_after = ["*"] # Header dirs searched AFTER system dirs (-idirafter)
c_standard = "c11" # Standard for C source files (default c11)
cflags = ["-DFOO=1"] # Extra C compile flags
cxxflags = ["-DBAR=2"] # Extra C++ compile flags (do not put -std=... here)
Expand All @@ -146,6 +147,20 @@ target = "x86_64-linux-musl" # Default build target when no --target is pa
macos_deployment_target = "14.0" # Minimum supported OS version for macOS artifacts (macOS only)
```

`include_dirs_after` (#249) lists header directories that are searched **after**
the toolchain's system directories (emitted as `-idirafter` on GCC/Clang, as
trailing `/I` under the MSVC dialect, and as plain `-I` for NASM
assembly units — neither has an equivalent, and neither has a system-header
chain to protect). Use it instead of
`include_dirs` when the directory is an extracted source-tarball root that
contains files whose names collide with standard headers — e.g. ffmpeg's
top-level `VERSION` file shadows libc++'s `<version>` on case-insensitive macOS
filesystems when the root is put on `-I`. With `include_dirs_after` the system
header always wins while the package's real headers (`<libavutil/frame.h>`)
remain findable. Entries support the same `*` glob convention as
`include_dirs`, and they propagate to dependent packages along the same edges —
consumers receive them as after-dirs, never upgraded to `-I`.

`macos_deployment_target` sets the minimum system version in the artifact's
Mach-O header (`LC_BUILD_VERSION minos`), i.e. the oldest macOS the binary can
run on. The precedence follows ecosystem convention: the `MACOSX_DEPLOYMENT_TARGET`
Expand Down
17 changes: 16 additions & 1 deletion docs/07-build-mcpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ is ignored, so you can freely log diagnostics.
| `mcpp:link-search=<dir>` | add a library search dir (`-L`; relative dirs resolve against the project root) |
| `mcpp:cfg=<name>` | define `-D<name>` for both C and C++ |
| `mcpp:generated=<path>` | add a generated source (relative to the project root) to the build |
| `mcpp:source=<path>` *(0.0.100+)* | select a **pre-existing** source file into the build (absolute, or relative to the package root). Same downstream effect as `generated=`; use it for files the program *chose* (payload/vendored tree) rather than wrote — e.g. a per-target source selection over a large tarball |
| `mcpp:include-dir=<dir>` *(0.0.100+)* | add a **private** include directory (`-I`) for this package's own TUs (absolute, or relative to the package root; normalized). Replaces the `cxxflag=-I` + `cflag=-I` double emission |
| `mcpp:include-dir-after=<dir>` *(0.0.100+)* | like `include-dir`, but searched **after** the system directories (`-idirafter`) — for payload trees that shadow system headers |
| `mcpp:rerun-if-changed=<path>` | re-run `build.mcpp` when this file changes |
| `mcpp:rerun-if-env-changed=<VAR>` | re-run `build.mcpp` when this env var changes |

Expand All @@ -57,6 +60,12 @@ registry dependency — keep your dependency graph declarative in `mcpp.toml`
(including platform-conditional `[target.windows.dependencies]`). `build.mcpp`
is for *leaf* decisions: flags, codegen, link requirements.

`include-dir`/`include-dir-after` are deliberately **private** (Cargo
discipline): they color only this package's own TUs and are never propagated
to consumers. An include directory consumers must see is part of the public
interface and belongs in the declarative manifest/descriptor
(`[build] include_dirs`), not in a build-time program.

## Typed API: `import mcpp;` (recommended)

Instead of printing raw strings you can write `build.mcpp` **modules-first** —
Expand Down Expand Up @@ -85,6 +94,8 @@ int main() {
| `mcpp::link_lib(s)` / `mcpp::link_search(s)` | `mcpp:link-lib=` / `mcpp:link-search=` |
| `mcpp::define(s)` | `mcpp:cfg=` (i.e. `-D<s>`) |
| `mcpp::generated(p)` | `mcpp:generated=` |
| `mcpp::source(p)` | `mcpp:source=` |
| `mcpp::include_dir(d)` / `mcpp::include_dir_after(d)` | `mcpp:include-dir=` / `mcpp:include-dir-after=` |
| `mcpp::rerun_if_changed(p)` / `mcpp::rerun_if_env_changed(v)` | the matching `rerun-*` directives |

If your `build.mcpp` also needs to *write* a generated file, mix in a textual
Expand All @@ -100,12 +111,16 @@ The running program receives the build context as `MCPP_*` variables
| Variable | Typed reader | Value |
|---|---|---|
| `MCPP_TARGET` | `mcpp::target()` | resolved canonical triple (the `--target` triple under cross; the host triple natively) |
| `MCPP_TARGET_OS` *(0.0.100+)* | `mcpp::target_os()` | the target's OS segment (`linux`/`macos`/`windows`) — no need to hand-split `MCPP_TARGET` |
| `MCPP_TARGET_ARCH` *(0.0.100+)* | `mcpp::target_arch()` | the target's arch segment (GNU spelling: `x86_64`, `aarch64`, …) |
| `MCPP_TARGET_ENV` *(0.0.100+)* | `mcpp::target_env()` | the target's env segment (`gnu`/`musl`/`msvc`); empty string when the triple has none (macOS) |
| `MCPP_HOST` | `mcpp::host()` | the host triple |
| `MCPP_PROFILE` | `mcpp::profile()` | effective profile name (`dev`/`release`/…) |
| `MCPP_OUT_DIR` | `mcpp::out_dir()` | a writable scratch/output dir owned by mcpp |
| `MCPP_MANIFEST_DIR` | `mcpp::manifest_dir()` | the package root (= CWD) |
| `MCPP_FEATURE_<NAME>` | `mcpp::has_feature("name")` | set to `1` per active feature (same `<NAME>` sanitization as the `MCPP_FEATURE_` compile macro) |
| `MCPP_FEATURES` | — | comma-separated active feature list |
| `MCPP_DEP_<NAME>_DIR` | `mcpp::dep_dir("name")` | the resolved install dir of each declared dependency (canonical **and** namespace-stripped name spellings; same `<NAME>` sanitization as `MCPP_FEATURE_`). Received by dependencies' build.mcpp **and** the root project's (the root runs after dependency resolution, 0.0.100+) |

These values are folded into the re-run key **unconditionally** — changing the
target, profile, or feature set re-runs the program without any
Expand All @@ -132,7 +147,7 @@ directives and re-runs only when something it depends on changed:
- the toolchain,
- any file you declared with `rerun-if-changed`,
- any env var you declared with `rerun-if-env-changed`,
- (or a `generated` output went missing).
- (or a `generated` output / `source=` selection went missing).

So **declare your inputs**: if your program reads `config.h` or the `USE_FAST`
variable, emit `mcpp:rerun-if-changed=config.h` / `mcpp:rerun-if-env-changed=USE_FAST`.
Expand Down
11 changes: 11 additions & 0 deletions docs/zh/05-mcpp-toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ mcpp 刻意不在一次构建里把同一个共享源编译成两份:一个源
[build]
sources = ["src/**/*.cppm", "src/**/*.cpp"] # 源文件 glob(默认: src/**/*.{cppm,cpp,cc,c,S,s,asm})
include_dirs = ["include", "third_party/include"] # 头文件搜索路径
include_dirs_after = ["*"] # 排在系统目录之后搜索的头文件目录(-idirafter)
c_standard = "c11" # C 源文件的标准(默认 c11)
cflags = ["-DFOO=1"] # 额外 C 编译参数
cxxflags = ["-DBAR=2"] # 额外 C++ 编译参数(不要放 -std=...)
Expand All @@ -139,6 +140,16 @@ static_stdlib = true # 静态链接 libstdc++(默认 true)
macos_deployment_target = "14.0" # macOS 产物的最低支持系统版本(仅 macOS 生效)
```

`include_dirs_after`(#249)列出**排在工具链系统目录之后**搜索的头文件目录
(GCC/Clang 发射为 `-idirafter`;MSVC 方言退化为排在末尾的 `/I`,NASM 汇编
单元退化为普通 `-I`——两者都没有对应 flag,也都没有需要保护的系统头搜索链)。当目录是解压后的源码 tarball 根目录、且其中的文件名会与标准头冲突时,
用它代替 `include_dirs` —— 例如 ffmpeg 根目录的 `VERSION` 文件在大小写不敏感
的 macOS 文件系统上会把 libc++ 的 `<version>` 遮蔽(若该根目录挂在 `-I` 上)。
使用 `include_dirs_after` 时系统头永远优先,而包自己的真实头文件
(`<libavutil/frame.h>`)仍能找到。条目支持与 `include_dirs` 相同的 `*` glob
约定,并沿相同的依赖边传播给消费者 —— 消费者收到的仍是 after 目录,
永远不会被升级为 `-I`。

`macos_deployment_target` 设定产物 Mach-O 头里的最低系统版本
(`LC_BUILD_VERSION minos`),即二进制能运行的最老 macOS。优先级与各生态
惯例一致:环境变量 `MACOSX_DEPLOYMENT_TARGET`(单次调用的显式覆盖,
Expand Down
15 changes: 14 additions & 1 deletion docs/zh/07-build-mcpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,20 @@ mcpp build # 编译 + 运行 build.mcpp,然后构建工程
| `mcpp:link-search=<dir>` | 增加库搜索目录(`-L`;相对路径按工程根目录解析) |
| `mcpp:cfg=<name>` | 为 C 与 C++ 同时定义 `-D<name>` |
| `mcpp:generated=<path>` | 把生成的源码(相对工程根目录)加入构建 |
| `mcpp:source=<path>` *(0.0.100+)* | 把一份**既有**源文件选入构建(绝对路径,或相对包根)。下游效果与 `generated=` 相同;语义区别在于文件是程序*选中*的(tarball payload / vendored 源树)而非程序写出的——例如对大型源码包做 per-target 源选择 |
| `mcpp:include-dir=<dir>` *(0.0.100+)* | 为本包自身 TU 增加一个**私有** include 目录(`-I`;绝对路径或相对包根,自动规范化)。取代过去 `cxxflag=-I` + `cflag=-I` 的双重裸发 |
| `mcpp:include-dir-after=<dir>` *(0.0.100+)* | 同 `include-dir`,但排在系统目录**之后**搜索(`-idirafter`)——用于会遮蔽系统头的 payload 源树 |
| `mcpp:rerun-if-changed=<path>` | 该文件变化时重跑 `build.mcpp` |
| `mcpp:rerun-if-env-changed=<VAR>` | 该环境变量变化时重跑 `build.mcpp` |

程序**请求**构建边(开关、库、源码),它**不能**新增注册表依赖——请把依赖图保持在
`mcpp.toml` 里声明式管理(包括平台条件依赖 `[target.windows.dependencies]`)。
`build.mcpp` 用于*叶子*决策:开关、代码生成、链接需求。

`include-dir`/`include-dir-after` 刻意保持**私有**(Cargo 纪律):只染色本包自身的
TU,绝不向消费者传播。需要消费者可见的 include 目录属于公共接口,应写在声明式
manifest/描述符里(`[build] include_dirs`),而不是构建期程序里。

## 类型化 API:`import mcpp;`(推荐)

除了打印裸字符串,你还可以把 `build.mcpp` 写成**模块优先**——`import mcpp;`,无
Expand Down Expand Up @@ -80,6 +87,8 @@ int main() {
| `mcpp::link_lib(s)` / `mcpp::link_search(s)` | `mcpp:link-lib=` / `mcpp:link-search=` |
| `mcpp::define(s)` | `mcpp:cfg=`(即 `-D<s>`) |
| `mcpp::generated(p)` | `mcpp:generated=` |
| `mcpp::source(p)` | `mcpp:source=` |
| `mcpp::include_dir(d)` / `mcpp::include_dir_after(d)` | `mcpp:include-dir=` / `mcpp:include-dir-after=` |
| `mcpp::rerun_if_changed(p)` / `mcpp::rerun_if_env_changed(v)` | 对应的 `rerun-*` 指令 |

如果 `build.mcpp` 还需要*写*生成文件,混入一个文本 `#include <fstream>` 即可——这没问题,
Expand All @@ -94,12 +103,16 @@ int main() {
| 变量 | 类型化读取 | 值 |
|---|---|---|
| `MCPP_TARGET` | `mcpp::target()` | 解析后的 canonical 三元组(交叉构建下是 `--target` 三元组,原生构建是宿主) |
| `MCPP_TARGET_OS` *(0.0.100+)* | `mcpp::target_os()` | 目标的 OS 段(`linux`/`macos`/`windows`)——不必再手撕 `MCPP_TARGET` |
| `MCPP_TARGET_ARCH` *(0.0.100+)* | `mcpp::target_arch()` | 目标的 arch 段(GNU 拼写:`x86_64`、`aarch64`…) |
| `MCPP_TARGET_ENV` *(0.0.100+)* | `mcpp::target_env()` | 目标的 env 段(`gnu`/`musl`/`msvc`);三元组无 env 段(macOS)时为空串 |
| `MCPP_HOST` | `mcpp::host()` | 宿主三元组 |
| `MCPP_PROFILE` | `mcpp::profile()` | 生效 profile 名(`dev`/`release`/…) |
| `MCPP_OUT_DIR` | `mcpp::out_dir()` | mcpp 提供的可写输出/暂存目录 |
| `MCPP_MANIFEST_DIR` | `mcpp::manifest_dir()` | 包根(= CWD) |
| `MCPP_FEATURE_<NAME>` | `mcpp::has_feature("name")` | 每个活跃 feature 置 `1`(`<NAME>` 消毒规则与 `MCPP_FEATURE_` 编译宏一致) |
| `MCPP_FEATURES` | — | 活跃 feature 逗号列表 |
| `MCPP_DEP_<NAME>_DIR` | `mcpp::dep_dir("name")` | 每个已声明依赖解析后的安装目录(canonical 名与去命名空间短名两种拼写都可用;`<NAME>` 消毒规则同 `MCPP_FEATURE_`)。依赖包的 build.mcpp **和**根工程的 build.mcpp 都能拿到(根工程的 build.mcpp 在依赖解析之后运行,0.0.100+) |

这些契约值**无条件**折入重跑键——换 target、换 profile、开关 feature 都会触发重跑,
不需要任何 `rerun-if-env-changed` 声明。
Expand All @@ -122,7 +135,7 @@ mcpp **不会**每次构建都重跑 `build.mcpp`。它会缓存程序产出的
- 工具链,
- 任何用 `rerun-if-changed` 声明的文件,
- 任何用 `rerun-if-env-changed` 声明的环境变量,
- (或某个 `generated` 产物丢失了)。
- (或某个 `generated` 产物 / `source=` 选中的文件丢失了)。

所以请**声明你的输入**:如果程序读了 `config.h` 或 `USE_FAST` 变量,就分别 emit
`mcpp:rerun-if-changed=config.h` / `mcpp:rerun-if-env-changed=USE_FAST`。这用一份明确的
Expand Down
2 changes: 1 addition & 1 deletion mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mcpp"
version = "0.0.99"
version = "0.0.100"
description = "Modern C++ build & package management tool"
license = "Apache-2.0"
authors = ["mcpp-community"]
Expand Down
Loading
Loading