Skip to content

Commit 2e0e8da

Browse files
committed
feat: Windows CI adds xmake bootstrap step (MSVC + C++23 modules)
Phase 1 validation passed: xlings LLVM on Windows works for basic compilation. Now try xmake build of mcpp with MSVC (same approach as xlings uses for its own Windows build).
1 parent ae124da commit 2e0e8da

2 files changed

Lines changed: 76 additions & 179 deletions

File tree

.agents/docs/2026-05-17-windows-llvm-support-design.md

Lines changed: 32 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -2,195 +2,48 @@
22

33
Date: 2026-05-17
44

5-
## 1. 目标
5+
## 目标
66

7-
在 Windows x86_64 上用 xlings LLVM(clang++ / clang-cl)支持 mcpp 构建 C++23 模块项目,达到与 Linux/macOS 同等的核心可用水平:
8-
- `mcpp build` / `mcpp run` / `mcpp test`
9-
- `import std` 支持
10-
- 多模块项目 + 增量编译
11-
- 自举(mcpp 编译自己)
7+
mcpp 在 Windows x86_64 上通过 xmake bootstrap 达到可用水平,产出 mcpp.exe 作为后续自举依赖。
128

13-
## 2. 现状分析
9+
## 平台特征
1410

15-
### 2.1 xlings LLVM Windows 包
11+
### Windows LLVM 包(xlings-res 20.1.7)
1612

17-
**已有**(xlings-res/llvm 20.1.7):
1813
```
19-
bin/clang++.exe, clang.exe, clang-cl.exe ← 编译器
20-
bin/lld-link.exe ← MSVC 兼容链接器
21-
bin/llvm-ar.exe, llvm-lib.exe ← 归档工具
22-
bin/llvm-rc.exe, llvm-mt.exe ← 资源编译器
23-
lib/clang/20/lib/windows/ ← compiler-rt
14+
bin/clang.exe, clang++.exe, clang-cl.exe, lld-link.exe
15+
bin/llvm-ar.exe, llvm-lib.exe, llvm-rc.exe
16+
lib/clang/20/lib/windows/clang_rt.*.lib
17+
没有 libc++(没有 include/c++/v1,没有 std.cppm)
18+
没有 clang-scan-deps.exe
2419
```
2520

26-
**没有**
27-
- ❌ libc++ 头文件和库(`include/c++/v1/` 不存在)
28-
-`std.cppm` / `std.compat.cppm` 模块源码
29-
-`clang-scan-deps.exe`(P1689 模块扫描器)
21+
Windows LLVM 包不含 libc++。Windows 上 clang 搭配 MSVC STL。
3022

31-
### 2.2 含义
23+
### Bootstrap 策略
3224

33-
Windows LLVM 包设计为 **MSVC 兼容模式**
34-
- 使用 MSVC 的 STL(`<iostream>` 等来自 Visual Studio)
35-
- 使用 MSVC 的 C runtime(ucrt)
36-
- 通过 `clang-cl.exe` 驱动(接受 `/std:c++latest``/EHsc` 等 MSVC 风格参数)
37-
- 链接用 `lld-link.exe`(MSVC `link.exe` 兼容)
25+
用 xmake + MSVC(和 xlings 自身做法一致):
26+
- GitHub Actions windows-latest 预装 Visual Studio
27+
- xmake 对 MSVC C++23 modules 支持成熟
28+
- 不需要额外安装 LLVM(MSVC 即可)
3829

39-
这是 **Windows 上的行业标准做法**——即使用 Clang,也通常走 MSVC ABI。
30+
## 代码适配清单
4031

41-
### 2.3 C++23 Modules 在 Windows MSVC STL 上的状态
32+
### 必须修改
4233

43-
MSVC STL 从 VS 2022 17.5 起支持 `import std;`
44-
- 需要 `/std:c++latest``/std:c++23`
45-
- 模块文件格式:`.ifc`(不是 `.pcm``.gcm`
46-
-**clang-cl 目前不支持 MSVC 的 `.ifc` 格式**
47-
48-
**clang++ (GNU 驱动) on Windows**
49-
- 可以用 `-stdlib=libc++` 但需要自带 libc++
50-
- 当前 xlings Windows LLVM 包没有 libc++
51-
- 如果补充 libc++,可以用与 Linux/macOS 相同的 `.pcm` 模块模型
52-
53-
### 2.4 mcpp 代码现状
54-
55-
| 组件 | Windows 状态 | 需要改动 |
56-
|------|-------------|---------|
57-
| 平台检测(`_WIN32`| ✅ 已有 ||
58-
| CompilerId::MSVC | ✅ enum 定义 | 需要实现 |
59-
| `probe.cppm` | ❌ 用 Unix shell | 需要 Windows 移植 |
60-
| `flags.cppm` | ❌ 全是 Unix flags | 需要 MSVC flags |
61-
| `ninja_backend.cppm` | ❌ shell 命令 | 需要 cmd/PowerShell 适配 |
62-
| `config.cppm` |`/proc/self/exe` | 需要 `GetModuleFileName` |
63-
| `install.sh` | ⚠️ bash only | Windows 需要 PowerShell |
64-
65-
## 3. 技术方案
66-
67-
### 3.1 两条路径对比
68-
69-
| 方案 | 路径 | 优点 | 缺点 |
70-
|------|------|------|------|
71-
| **A: clang++ + libc++** | GNU 驱动 + 自带 libc++ | 与 Linux/macOS 统一,`.pcm` 格式 | 需要补充 libc++ 到 LLVM 包 |
72-
| **B: clang-cl + MSVC STL** | MSVC 兼容驱动 | Windows 原生,ABI 兼容 | 全新编译模型(`/std:c++latest`),`.ifc` 格式不兼容 |
73-
74-
**推荐方案 A**:用 `clang++.exe`(GNU 驱动)+ 补充 libc++。理由:
75-
1. 与 Linux/macOS 共享同一套模块编译逻辑(`.pcm``-fmodule-file=`
76-
2. 不依赖 Visual Studio 安装
77-
3. mcpp 核心代码改动最小(只需要处理路径分隔符和 shell 命令差异)
78-
79-
### 3.2 前提条件
80-
81-
1. **xlings LLVM Windows 包需要补充 libc++**
82-
- `include/c++/v1/` — libc++ 头文件
83-
- `share/libc++/v1/std.cppm` + `std.compat.cppm` — 模块源
84-
- `lib/libc++.lib` (或 `.a`) — 静态库
85-
- `bin/clang-scan-deps.exe` — P1689 扫描器
86-
87-
2. **或者**:在 LLVM Windows 包中生成 `clang++.cfg` 配置 libc++ 路径
88-
89-
### 3.3 mcpp 代码改动清单
90-
91-
#### Phase 1: 核心编译(让 `mcpp build` 在 Windows 上工作)
92-
93-
| 文件 | 改动 | 优先级 |
94-
|------|------|--------|
95-
| `probe.cppm` | `probe_compiler_binary`: Windows 用 `where.exe` 替代 `command -v` | P0 |
96-
| `probe.cppm` | `run_capture`: Windows 用 `_popen`/`_pclose` | P0 |
97-
| `probe.cppm` | `discover_*_dirs`: 添加 `#if defined(_WIN32)` 分支 | P0 |
98-
| `flags.cppm` | Windows 链接 flags:无 `-rpath`(Windows 不支持) | P0 |
99-
| `ninja_backend.cppm` | Shell 命令替换:`mkdir -p``cmd /c mkdir`, `cp``copy` | P0 |
100-
| `ninja_backend.cppm` | `mcpp_exe_path`: 用 `GetModuleFileNameW` | P0 |
101-
| `config.cppm` | 同上,exe 路径获取 | P0 |
102-
| `clang.cppm` | Windows libc++ 路径发现 | P1 |
103-
| `cli.cppm` | 默认工具链 `llvm@20.1.7` for Windows | P1 |
104-
105-
#### Phase 2: 可执行文件扩展名
106-
107-
| 位置 | 改动 |
108-
|------|------|
109-
| `ninja_backend.cppm` | 输出 `bin/mcpp.exe` 而非 `bin/mcpp` |
110-
| `manifest.cppm` | `kind = "bin"` 产出 `.exe` |
111-
| `cli.cppm` | `mcpp run` 查找 `.exe` |
112-
113-
#### Phase 3: CI + Release
114-
115-
| 改动 | 说明 |
116-
|------|------|
117-
| `.github/workflows/ci-windows.yml` | Windows CI(`windows-latest` runner) |
118-
| `.github/workflows/bootstrap-windows.yml` | xmake 首次编译 |
119-
| `release.yml` | 添加 Windows job |
120-
121-
### 3.4 Ninja shell 命令移植
122-
123-
这是最复杂的部分。当前 build.ninja 中的 shell 命令:
124-
125-
| 当前 (Unix) | Windows 等价 | 说明 |
126-
|-------------|-------------|------|
127-
| `mkdir -p $(dirname $out) && cp -f $in $out` | `cmd /c if not exist "$$(dir $out)" mkdir "$$(dir $out)" && copy /y $in $out` | 复制 BMI |
128-
| `if [ -n "$bmi_out" ] && ...` | `cmd /c ...` 或 PowerShell | BMI restat 逻辑 |
129-
| `cd ... && $cxx ...` | `cmd /c cd /d ... && $cxx ...` | 编译命令 |
130-
| `env LD_LIBRARY_PATH=...` | 不需要(Windows 用 PATH) | 运行时路径 |
131-
132-
**建议**:在 `ninja_backend.cppm` 中按平台生成不同的 rule 命令,用 `#if defined(_WIN32)` 条件编译。
133-
134-
### 3.5 Windows 链接策略
135-
136-
```cpp
137-
#if defined(_WIN32)
138-
// Windows: clang++ GNU driver links against libc++ automatically
139-
// No -rpath (not a thing on Windows)
140-
// No sysroot (not needed for MSVC ucrt)
141-
// Static libc++: -static-libc++ (or statically link libc++.a)
142-
f.ld = std::format("{}{}", full_static, b_flag);
143-
#endif
144-
```
145-
146-
Windows 产出的 `.exe` 运行时依赖:
147-
- `ucrt` (Universal C Runtime) — Windows 10+ 自带
148-
- `libc++.dll` 或静态链接 `libc++.a`
149-
- `vcruntime140.dll` — 如果用 MSVC 兼容模式
150-
151-
## 4. 实施计划
152-
153-
### Step 1: 验证 xlings LLVM Windows 能否编译 C++23 模块
154-
155-
创建 `ci-windows.yml` 在 GitHub Actions `windows-latest` runner 上:
156-
1. 安装 xlings
157-
2. 安装 LLVM
158-
3. 手动用 clang++ 编译 `import std`(如果 libc++ 可用)
159-
4. 如果 libc++ 不可用,验证 clang-cl + MSVC STL
160-
161-
### Step 2: xmake bootstrap
162-
163-
用 xmake 在 Windows 上编译 mcpp(参考 mcpp-dev 的 xmake.lua)。
164-
165-
### Step 3: mcpp 代码适配
166-
167-
基于 CI 验证结果,逐步适配 probe/flags/ninja_backend。
168-
169-
### Step 4: Self-host + Release
170-
171-
mcpp 自举 → 打包 → release。
172-
173-
## 5. 风险
174-
175-
| 风险 | 影响 | 缓解 |
34+
| 文件 | 问题 | 方案 |
17635
|------|------|------|
177-
| xlings Windows LLVM 包无 libc++ | 无法用 `import std` | 需要上游补充或用 MSVC STL |
178-
| ninja shell 命令移植复杂 | build.ninja 在 Windows 上不工作 | 可用 ninja 的 `msvc_deps_prefix` 特性 |
179-
| `clang-scan-deps.exe` 缺失 | P1689 扫描不可用 | GCC 模式的 `-fdeps-format` 也可用 |
180-
| Windows path separator (`\` vs `/`) | 路径拼接问题 | `std::filesystem` 已处理大部分 |
181-
182-
## 6. 依赖关系
183-
184-
```
185-
xlings LLVM Windows 包 (libc++ 补充)
186-
187-
CI 验证 (clang++ + import std)
188-
189-
xmake bootstrap (产出 mcpp.exe)
190-
191-
mcpp 代码适配 (probe/flags/ninja)
192-
193-
self-host (mcpp.exe 编译 mcpp.exe)
194-
195-
release
196-
```
36+
| ninja_backend.cppm | POSIX shell 命令 | #if _WIN32 cmd.exe 语法 |
37+
| ninja_backend.cppm | mcpp_exe_path() 缺 Windows | GetModuleFileNameA() |
38+
| config.cppm | MCPP_HOME 路径发现缺 Windows | 同上 |
39+
| probe.cppm | command -v Unix only | where.exe |
40+
| probe.cppm | LD_LIBRARY_PATH | Windows 用 PATH |
41+
| flags.cppm | 链接 flags 缺 Windows 分支 | 无 sysroot/rpath |
42+
| xlings.cppm | popen | _popen |
43+
44+
## 执行顺序
45+
46+
1. 创建 ci-windows.yml 用 xmake 构建,看编译错误
47+
2. 根据 CI 错误逐步修代码
48+
3. 产出 mcpp.exe bootstrap binary
49+
4. 上传到 xlings-res

.github/workflows/ci-windows.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,47 @@ jobs:
183183
echo "Has clang-scan-deps: $([ -f "$LLVM_ROOT/bin/clang-scan-deps.exe" ] && echo YES || echo NO)"
184184
echo "Has clang-cl: $([ -f "$LLVM_ROOT/bin/clang-cl.exe" ] && echo YES || echo NO)"
185185
echo "Has lld-link: $([ -f "$LLVM_ROOT/bin/lld-link.exe" ] && echo YES || echo NO)"
186+
187+
- name: Install xmake
188+
shell: pwsh
189+
run: |
190+
Invoke-Expression (Invoke-WebRequest 'https://xmake.io/psget.text' -UseBasicParsing).Content
191+
xmake --version
192+
193+
- name: Build mcpp with xmake (MSVC)
194+
shell: pwsh
195+
run: |
196+
# Generate xmake.lua for bootstrap
197+
@"
198+
add_rules("mode.release")
199+
set_languages("c++23")
200+
package("cmdline")
201+
set_homepage("https://github.com/mcpplibs/cmdline")
202+
add_urls("https://github.com/mcpplibs/cmdline/archive/refs/tags/`$(version).tar.gz")
203+
add_versions("0.0.1", "3fb2f5495c1a144485b3cbb2e43e27059151633460f702af0f3851cbff387ef0")
204+
on_install(function (package)
205+
import("package.tools.xmake").install(package)
206+
end)
207+
package_end()
208+
add_requires("cmdline 0.0.1")
209+
target("mcpp")
210+
set_kind("binary")
211+
add_files("src/main.cpp")
212+
add_files("src/**.cppm")
213+
add_packages("cmdline")
214+
add_includedirs("src/libs/json")
215+
set_policy("build.c++.modules", true)
216+
"@ | Out-File -Encoding utf8 xmake.lua
217+
218+
xmake f -p windows -m release -y
219+
xmake build -y mcpp
220+
221+
$mcpp = Get-ChildItem -Recurse build -Filter mcpp.exe -ErrorAction SilentlyContinue | Select-Object -First 1
222+
if ($mcpp) {
223+
Write-Host ":: mcpp.exe built at: $($mcpp.FullName)"
224+
& $mcpp.FullName --version
225+
} else {
226+
Write-Host ":: Build produced files:"
227+
Get-ChildItem -Recurse build -Filter *.exe | ForEach-Object { Write-Host " $($_.FullName)" }
228+
exit 1
229+
}

0 commit comments

Comments
 (0)