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
76 changes: 76 additions & 0 deletions .agents/docs/2026-07-13-single-pr-090-implementation-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 0.0.90 单 PR 实施计划(post-089 路线图全量)

> 2026-07-13 · 执行 `2026-07-13-post-089-roadmap-and-std-dialect-flags-design.md` 的
> **全部内容**于一个 PR。步骤是顺序实施的里程碑(每步本地 build+test 过),不拆 PR。

## 步骤

**S1 — D1 方言旗标(#210)**
manifest 解析 `[build] dialect_cxxflags`;`extract_dialect_flags(cxxflags)`
(known-list:reflection/contracts/char8_t/`-D_GLIBCXX_USE_CXX11_ABI=`;
fno-exceptions/rtti 首版不入);`BuildPlan.dialectFlags`;注入:flags.cppm 全局
`$cxxflags`(cxx_std_flag 旁)、`ensure_built` 新形参 → gcc/clang/msvc std 命令、
metadata 自然失效。单测 + e2e `98_reflection_import_std.sh`(两变体,flag 探测 SKIP)。

**S2 — A 债务**
stdmod `shellEsc` → `shq`;std/scan 命令的 `env …` 前缀 → argv+env(执行层带
tc.envOverrides + runtime dirs);build_program/p1689 的 is_clang → caps/dialect。

**S3 — C1 环境模型**
msvc.cppm:`find_windows_sdk()`(注册表→Windows Kits 目录扫描,取最高版本)+
`build_env(inst, sdk)` → INCLUDE/LIB/PATH(+`VSLANG=1033`);`enrich_toolchain_from_cl`
填 `tc.envOverrides`;SDK 缺失=检测时警告、构建时硬错误。doctor msvc 段加 SDK 行。

**S4 — 对象/标准旗标方言化(C2 前置)**
`object_filename_for` 接 objExt(.o/.m.o ↔ .obj/.m.obj);ninja std staging
`obj/std.o` 名参数化;`cppStandardFlag` 由 dialect.stdPrefix 产出
(msvc:>c++20 → `/std:c++latest`,20→`/std:c++20`);defines/`/D` 走 dialect;
CRT:`/MD` 默认、linkage=static → `/MT`。

**S5 — C2/C5 msvc 规则发射**
ninja_backend:LinkStyle::SeparateLinker 时 `cxx_link` = `$ld /nologo /OUT:$out @rsp`
(rspfile/rspfile_content),`cxx_shared` = `/DLL /IMPLIB:`,`cxx_archive` =
dial.archiveCmd(lib.exe @rsp);compile 规则 `deps = msvc` + `/showIncludes`;
CompileFlags 增 `ldBinary`(link.exe,cl 同目录);registry archive_tool msvc →
lib.exe;flags.cppm msvc ld 分支(dep runtime dirs → /LIBPATH,user ldflags 透传)。

**S6 — C3 std/std.compat staging**
msvc.cppm `std_module_build_commands`(cl /c std.ixx /ifcOutput /Fo:)+
std.compat.ixx 同法;`std_bmi_path`/`staged_std_bmi_path`/compat 变体 registry 分发
(flags.cppm 的 clang 硬编码 staging 调用一并解除);stdmod:objectPath 扩展名
per-dialect、msvc 分支走新命令、执行带 envOverrides;`hasImportStd` 已由 0.0.88 检测。

**S7 — C4 .ifc 管线 + /scanDependencies**
ninja `cxx_module` msvc 形态(moduleOutputPrefix=` /ifcOutput ` 已就绪;确认
`/Fo:` 同行共存);`/ifcSearchDir`(bmiSearchPrefix,已就绪);cxx_scan msvc 变体
`cl /scanDependencies $out /std:… /c $in`;provider msvc `has_builtin_p1689_scan=true`;
dyndep DyndepOptions(ifc.cache/.ifc)零改动。

**S8 — C7 删门 + fast-path env**
删 prepare.cppm MSVC 构建门;BuildResult/fast-path 缓存的单对 runtime env 推广为
env 列表(持久化 tc.envOverrides,否则 msvc 增量构建丢 INCLUDE/LIB)。

**S9 — MinGW 打磨 + 基建**
lifecycle:mingw 在非 Windows 明确报 windows-only;e2e 97 objdump 路径 glob;
doctor mingw 段;release.yml publish-ecosystem timeout 20→30。

**S10 — 测试与 CI 面**
e2e `99_msvc_native_build.sh`(requires: msvc):default msvc → new → build → run →
多模块 → import std → 增量;**95 号改造**:门断言(“not yet supported”)翻转为构建
成功断言;ci-windows.yml MSVC 步骤同步改为 build+run 矩阵(llvm+mingw+msvc);
单测:dialect 提取器、SDK 路径解析纯函数、std: 映射、msvc std 命令拼装。

**S11 — 版本与文档**
0.0.90(mcpp.toml/fingerprint.cppm/CHANGELOG);docs/03-toolchains.md MSVC 节
撤 “not yet supported”、补构建说明;#210 关联说明。

**S12 — 流程**
临时分支(仅 ci-windows+release.yml)迭代绿 → 真 PR(全 CI)→ --admin 合入 →
tag v0.0.90 → release(修复版 mirror 的实战验收,publish-ecosystem 应全自动)→
`xlings install mcpp@0.0.90` 验证 → bootstrap pin → 总汇报。

## 风险与回退
- MSVC 模块管线在真实 CI 的未知坑(cl 版本细节、/showIncludes 本地化、rsp 转义):
全部集中在 S5-S7,靠临时分支 CI 循环消化;门在 S8 才删,之前任何一步可安全暂停。
- e2e 98 的 -freflection 依赖 gcc16 驱动接受度:探测 SKIP 兜底。
- fast-path 缓存格式变更(S8):版本化格式头,老缓存自然失效重建。
12 changes: 3 additions & 9 deletions .github/workflows/ci-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,11 @@ jobs:
"$MCPP_SELF" self doctor 2>&1 | tee doctor.txt || true
grep -qi "msvc" doctor.txt

# native cl.exe builds are gated with one owned message
"$MCPP_SELF" new hello_msvc && cd hello_msvc
set +e
bout=$("$MCPP_SELF" build 2>&1); brc=$?
set -e
echo "$bout"
test $brc -ne 0
grep -q "not yet supported" <<<"$bout"
# native cl.exe build: full e2e (modules, import std, incremental)
cd "$GITHUB_WORKSPACE"
MCPP="$MCPP_SELF" bash tests/e2e/99_msvc_native_build.sh

# restore the LLVM default for the remaining steps
cd "$GITHUB_WORKSPACE"
"$MCPP_SELF" toolchain default llvm@20.1.7

- name: "Toolchain: LLVM — build mcpp (self-host)"
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -708,8 +708,10 @@ jobs:
needs: [build-release, build-linux-aarch64, build-macos, build-windows]
runs-on: ubuntu-latest
# A4 hardening: a single stuck upload once held this job >1h (6h default
# ceiling). The mirror script now has per-file timeouts, so 20min is ample.
timeout-minutes: 20
# ceiling). The mirror script has per-file timeouts and (post-0.0.89)
# batch-upload + ranged-GET verification — normal runs are minutes; 30
# is the generous backstop (20 was hit by the old per-asset verify loop).
timeout-minutes: 30
env:
XLINGS_RES_TOKEN: ${{ secrets.XLINGS_RES_TOKEN }}
GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
Expand Down
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,42 @@
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。

## [0.0.90] — 2026-07-13

### 新增

- **MSVC 原生构建后端(cl.exe)落地,0.0.88 的构建门移除**。选定 `msvc@system`
后 `mcpp build/run` 直接用系统 MSVC 编译链接:
- 环境模型:`find_windows_sdk()` + 从检测到的 VC tools/SDK 直接合成
INCLUDE/LIB/PATH(+`VSLANG=1033`),不跑 vcvarsall;SDK 缺失时检测/选择仍可用,
构建报带指引的明确错误;doctor 新增 SDK 与 mingw 检查行。
- 模块管线:std/std.compat.ixx 单命令 staging(`/ifcOutput` → ifc.cache),
命名模块 `.cppm` 经 `/interface /TP` 编译、`/ifcSearchDir` 消费;
`/scanDependencies` 作为第三个编译器内建 P1689 扫描驱动接入 dyndep。
- 链接:link.exe/lib.exe(SeparateLinker)+ 响应文件(绕 cmd 8191 限制),
DLL=`/DLL /IMPLIB:`;`deps=msvc`(/showIncludes)头文件依赖;`/MD|/MT` CRT
随 linkage;`/std:c++20|c++latest` 映射;`.obj` 扩展名全链路。
- fast-path 增量:构建缓存 env 槽新增 `@env` 多变量编码,增量构建重建
INCLUDE/LIB 环境。e2e 99(模块/import std/增量)+ 95 改造为真实构建断言。
- **`[build] dialect_cxxflags` + 方言旗标全图化(issue #210 修复)**。
`-freflection` 等"改变标准库头声明集"的 flag 现随 `-std=` 的通道到达:
全局 cxxflags(项目+依赖所有 TU)、std/std.compat BMI 预构建命令、P1689 扫描。
known-list 自动提升(reflection/contracts/char8_t/`_GLIBCXX_USE_CXX11_ABI`)+
显式 `dialect_cxxflags` 逃生舱;指纹早已包含这些 flag,修的是命令构造。
实证:#210 的最小复现(gcc16 + `import std;` + `std::meta`)输出 `x 2/y 3`;
e2e 98 含依赖模块变体。

### 修复与优化

- mingw 在非 Windows 主机的 `toolchain install/default` 现在明确报
windows-only(此前是 `invalid xpkg target 'xim:mingw-gcc@'`)。
- std 模块 staging 命令在 Windows 用 `cd /d`(跨盘;工作区 D: + 缓存 C: 的
真实 CI 布局)。
- release 的 publish-ecosystem:镜像脚本改为批量上传+带耐心的 ranged-GET 验证、
**验证超时不再删除资产**(0.0.89 因逐资产"18s 即删重传"+全量 GET 探测触顶
20min 被杀);timeout 兜底 20→30。
- stdmod 执行层支持工具链声明环境(capture_with_env);shell 引用平台化。

## [0.0.89] — 2026-07-13

### 新增
Expand Down
14 changes: 8 additions & 6 deletions docs/03-toolchains.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,14 @@ windows = "msvc@system"
newest installed VC tools, but errors if the detected version doesn't match
the prefix.

> [!NOTE]
> Selection and detection are supported today; **building with native MSVC
> (cl.exe) is not yet supported** — `mcpp build` fails with a clear message
> naming the detected version. For building on Windows use the MSVC-ABI Clang
> toolchain: `mcpp toolchain default llvm@20.1.7` (it borrows the MSVC STL
> from the same detected installation).
Since 0.0.90, **native cl.exe builds work**: mcpp synthesizes the
INCLUDE/LIB environment from the detected VC tools + Windows SDK (no
`vcvarsall` involved), stages `std.ixx`/`std.compat.ixx` as `.ifc` BMIs,
compiles `.cppm` module units via `/interface /TP /ifcOutput`, scans with
`/scanDependencies`, and links with `link.exe`/`lib.exe` through response
files. `[build] linkage = "static"` selects the `/MT` CRT. A missing Windows
SDK fails the build with installation guidance (`mcpp self doctor` reports
SDK status).

## Project-Level Version Pinning

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.89"
version = "0.0.90"
description = "Modern C++ build & package management tool"
license = "Apache-2.0"
authors = ["mcpp-community"]
Expand Down
16 changes: 15 additions & 1 deletion src/build/execute.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,22 @@ export std::optional<int> try_fast_build(const std::filesystem::path& projectRoo
if (verbose) argv.push_back("-v");

std::vector<std::pair<std::string, std::string>> childEnv;
if (runtimeEnvKey != "-" && !runtimeEnvValue.empty())
if (runtimeEnvKey == "@env") {
// Multi-var encoding (MSVC INCLUDE/LIB/PATH/VSLANG + optional runtime
// pair): \x1f-separated k=v records in the single value slot.
std::string_view rest = runtimeEnvValue;
while (!rest.empty()) {
auto sep = rest.find('\x1f');
auto rec = rest.substr(0, sep);
if (auto eq = rec.find('='); eq != std::string_view::npos && eq > 0)
childEnv.emplace_back(std::string(rec.substr(0, eq)),
std::string(rec.substr(eq + 1)));
if (sep == std::string_view::npos) break;
rest.remove_prefix(sep + 1);
}
} else if (runtimeEnvKey != "-" && !runtimeEnvValue.empty()) {
childEnv.emplace_back(runtimeEnvKey, runtimeEnvValue);
}

auto t0 = std::chrono::steady_clock::now();
// capture_exec merges stderr into the captured output (replacing `2>&1`),
Expand Down
61 changes: 48 additions & 13 deletions src/build/flags.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ struct CompileFlags {
std::string cxx; // full cxxflags string
std::string cc; // full cflags string
std::string ld; // ldflags string
std::filesystem::path cxxBinary; // g++ / clang++
std::filesystem::path ccBinary; // gcc / clang (derived)
std::filesystem::path arBinary; // ar path (may be empty → use PATH)
std::filesystem::path cxxBinary; // g++ / clang++ / cl.exe
std::filesystem::path ccBinary; // gcc / clang (derived; cl.exe = same)
std::filesystem::path arBinary; // ar / llvm-ar / lib.exe (empty → PATH)
std::filesystem::path ldBinary; // link.exe (SeparateLinker dialects only)
std::string sysroot; // --sysroot=... (for ninja ldflags)
std::string bFlag; // -B<binutils> (for ninja ldflags)
bool staticStdlib = true;
Expand Down Expand Up @@ -142,15 +143,17 @@ CompileFlags compute_flags(const BuildPlan& plan) {
f.cxxBinary = plan.toolchain.binaryPath;
f.ccBinary = mcpp::toolchain::derive_c_compiler(plan.toolchain);

// PIC?
const bool isMsvcDialect = (d.id == "msvc");

// PIC? (GNU-only concept; PE code is position independent by design.)
bool need_pic = false;
for (auto& lu : plan.linkUnits) {
if (lu.kind == LinkUnit::SharedLibrary) {
need_pic = true;
break;
}
}
std::string pic_flag = need_pic ? " -fPIC" : "";
std::string pic_flag = (need_pic && !isMsvcDialect) ? " -fPIC" : "";

// Include dirs
std::string include_flags;
Expand Down Expand Up @@ -235,9 +238,21 @@ CompileFlags compute_flags(const BuildPlan& plan) {
// unless the profile pins -O0.
auto& prof = plan.manifest.buildConfig;
std::string opt_flag = isMuslTc && prof.optLevel != "0"
? " -Og" : std::format(" {}{}", d.optPrefix, prof.optLevel);
? " -Og"
: (isMsvcDialect && prof.optLevel == "0")
? " /Od" // MSVC's no-opt spelling (there is no /O0)
: std::format(" {}{}", d.optPrefix, prof.optLevel);
if (prof.debug) opt_flag += std::format(" {}", d.debugFlags);
if (prof.lto) opt_flag += " -flto";
if (prof.lto && !isMsvcDialect) opt_flag += " -flto";

// MSVC baseline: /nologo /EHsc /utf-8 (dialect alwaysFlags) + the CRT
// model — /MD default, /MT under static linkage (portable-by-default is
// impossible on MSVC-ABI; /MT at least removes the vcruntime DLL dep).
std::string msvc_base;
if (isMsvcDialect) {
msvc_base = std::format(" {}", d.alwaysFlags);
msvc_base += (plan.manifest.buildConfig.linkage == "static") ? " /MT" : " /MD";
}

// User link flags
std::string user_ldflags;
Expand All @@ -264,9 +279,8 @@ CompileFlags compute_flags(const BuildPlan& plan) {
}
std::string std_compat_module_flag;
if (!traits.stdCompatBmiUsePrefix.empty() && !plan.stdCompatBmiPath.empty()) {
// NOTE: staging path is Clang's today; registry-dispatch when the
// MSVC backend lands (std.compat.ixx staging).
auto compatDst = mcpp::toolchain::clang::staged_std_compat_bmi_path(plan.outputDir);
auto compatDst = mcpp::toolchain::staged_std_compat_bmi_path(
plan.toolchain, plan.outputDir);
std_compat_module_flag = std::string(traits.stdCompatBmiUsePrefix)
+ escape_path(compatDst);
}
Expand All @@ -287,11 +301,20 @@ CompileFlags compute_flags(const BuildPlan& plan) {
std::string cxx_std_flag =
plan.cppStandardFlag.empty()
? std::format("{}c++23", d.stdPrefix) : plan.cppStandardFlag;
f.cxx = std::format("{}{}{}{}{}{}{}{}{}{}", cxx_std_flag, module_flag, std_module_flag,
// plan.dialectFlags rides right behind -std= (issue #210): module-graph-
// global dialect flags reach every TU (deps included) via this global
// cxxflags string, exactly like the standard flag itself.
f.cxx = std::format("{}{}{}{}{}{}{}{}{}{}{}{}", cxx_std_flag, plan.dialectFlags,
msvc_base, module_flag, std_module_flag,
std_compat_module_flag, prebuilt_module_flag,
opt_flag, pic_flag, compile_toolchain_flags, b_flag, include_flags);
f.cc = std::format("{}{}{}{}{}{}{}", d.stdPrefix, c_std, opt_flag, pic_flag,
compile_toolchain_flags, b_flag, include_flags);
// MSVC compiles C with cl.exe too; /std: for C uses cN spellings — skip
// the C standard flag there (cl defaults are fine for the C entry TUs).
f.cc = isMsvcDialect
? std::format("{}{}{}{}{}", msvc_base, opt_flag, compile_toolchain_flags,
b_flag, include_flags)
: std::format("{}{}{}{}{}{}{}", d.stdPrefix, c_std, opt_flag, pic_flag,
compile_toolchain_flags, b_flag, include_flags);

// Link flags
f.staticStdlib = plan.manifest.buildConfig.staticStdlib;
Expand Down Expand Up @@ -339,6 +362,18 @@ CompileFlags compute_flags(const BuildPlan& plan) {
if (prof.strip) link_extra += " -s";

if constexpr (mcpp::platform::is_windows) {
if (isMsvcDialect) {
// Native cl.exe: link.exe does the link (SeparateLinker). Search
// paths for dependency runtime import libs via /LIBPATH; user
// ldflags pass through verbatim; GNU link_extra (-flto/-s) does
// not apply.
f.ldBinary = mcpp::toolchain::link_tool(plan.toolchain);
std::string libpaths;
for (auto& dir : plan.depRuntimeLibraryDirs)
libpaths += " /LIBPATH:" + escape_path(dir);
f.ld = libpaths + user_ldflags;
return f;
}
// PE link: no rpath/loader/payload model. MSVC-ABI Clang needs
// nothing extra (MSVC STL/SDK via the driver); MinGW adds the static
// libstdc++/libgcc pair (static_stdlib above) and -B so its own
Expand Down
Loading
Loading