|
| 1 | +# Windows 平台成熟度提升方案 |
| 2 | + |
| 3 | +> 基于 PR #52 code review 反馈,针对 Windows 支持从"可自举"到"生产就绪"的优化路径。 |
| 4 | +
|
| 5 | +## 当前状态 |
| 6 | + |
| 7 | +| 能力 | Linux | macOS | Windows | 差距 | |
| 8 | +|------|-------|-------|---------|------| |
| 9 | +| self-host | ✅ | ✅ | ✅ | — | |
| 10 | +| `mcpp test` (unit) | ✅ | ✅ | ❌ | 缺 clang-scan-deps | |
| 11 | +| E2E 覆盖 | 46/46 | 33/46 | 22/49 | 27 项 skip | |
| 12 | +| `mcpp pack` | ✅ (musl static) | ✅ (手动) | ❌ (CI 手写 zip) | pack 不支持 PE | |
| 13 | +| release workflow | ✅ | ✅ | ❌ | 无 build-windows job | |
| 14 | +| MSVC 工具链 | N/A | N/A | 模型预留 | detect 不支持 | |
| 15 | +| 默认工具链回退 | gcc@15.1.0-musl | llvm@20.1.7 | llvm@20.1.7 | ✅ 已修 | |
| 16 | + |
| 17 | +## 优化方案(按优先级) |
| 18 | + |
| 19 | +### P0: 补齐 release workflow + 减少 E2E skip |
| 20 | + |
| 21 | +**目标:** Windows 二进制进入正式 release 发布流程。 |
| 22 | + |
| 23 | +#### 1. release.yml 加 build-windows job |
| 24 | + |
| 25 | +参照 `build-macos` 结构,在 `release.yml` 中增加 `build-windows` job: |
| 26 | + |
| 27 | +```yaml |
| 28 | +build-windows: |
| 29 | + name: build (Windows / x64) |
| 30 | + runs-on: windows-latest |
| 31 | + needs: build-release |
| 32 | + # xlings install mcpp → mcpp build → package zip → upload |
| 33 | +``` |
| 34 | + |
| 35 | +产出 `mcpp-<version>-windows-x86_64.zip` + sha256,上传到 GitHub Release。 |
| 36 | + |
| 37 | +#### 2. 修复高价值 E2E skip 项 |
| 38 | + |
| 39 | +按投入产出排序: |
| 40 | + |
| 41 | +| 测试 | 修复方式 | 工作量 | |
| 42 | +|------|----------|--------| |
| 43 | +| `02_new_build_run.sh` | 检查 `bin/hello` 或 `bin/hello.exe` | 小 | |
| 44 | +| `16_test_failing.sh` | 调查 Windows 上 exit code 传递 | 小 | |
| 45 | +| `35_workspace.sh` | 同上,binary 名加 `.exe` 检查 | 小 | |
| 46 | +| `36_llvm_toolchain.sh` | 同上 | 小 | |
| 47 | +| `19_bmi_cache_reuse.sh` | 修复 `cp_bmi` rule 的混合路径 | 中 | |
| 48 | +| `24_git_dependency.sh` | CRLF + Windows 路径处理 | 中 | |
| 49 | +| `38_self_config_mirror.sh` | xlings mirror cmd.exe 路径 | 中 | |
| 50 | + |
| 51 | +**预计可把 E2E 从 22 passed 提升到 ~30 passed。** |
| 52 | + |
| 53 | +### P1: PlatformTraits 抽象 |
| 54 | + |
| 55 | +**目标:** 减少散落的 `#if defined(_WIN32)` / `#if defined(__APPLE__)`。 |
| 56 | + |
| 57 | +新建 `src/platform.cppm`,集中平台差异: |
| 58 | + |
| 59 | +```cpp |
| 60 | +export module mcpp.platform; |
| 61 | +import std; |
| 62 | + |
| 63 | +export namespace mcpp::platform { |
| 64 | + |
| 65 | +constexpr std::string_view exe_suffix = |
| 66 | +#if defined(_WIN32) |
| 67 | + ".exe"; |
| 68 | +#else |
| 69 | + ""; |
| 70 | +#endif |
| 71 | + |
| 72 | +constexpr std::string_view static_lib_ext = |
| 73 | +#if defined(_WIN32) |
| 74 | + ".lib"; |
| 75 | +#else |
| 76 | + ".a"; |
| 77 | +#endif |
| 78 | + |
| 79 | +constexpr std::string_view shared_lib_ext = |
| 80 | +#if defined(_WIN32) |
| 81 | + ".dll"; |
| 82 | +#elif defined(__APPLE__) |
| 83 | + ".dylib"; |
| 84 | +#else |
| 85 | + ".so"; |
| 86 | +#endif |
| 87 | + |
| 88 | +constexpr std::string_view null_redirect = |
| 89 | +#if defined(_WIN32) |
| 90 | + "2>nul"; |
| 91 | +#else |
| 92 | + "2>/dev/null"; |
| 93 | +#endif |
| 94 | + |
| 95 | +constexpr std::string_view lib_prefix = |
| 96 | +#if defined(_WIN32) |
| 97 | + ""; |
| 98 | +#else |
| 99 | + "lib"; |
| 100 | +#endif |
| 101 | + |
| 102 | +std::string shell_quote(std::string_view s); // 取代散落的 shq |
| 103 | + |
| 104 | +} // namespace mcpp::platform |
| 105 | +``` |
| 106 | +
|
| 107 | +**受益文件:** `plan.cppm`、`flags.cppm`、`ninja_backend.cppm`、`probe.cppm`、`clang.cppm`、`config.cppm` |
| 108 | +
|
| 109 | +### P2: ToolchainProvider 重构 |
| 110 | +
|
| 111 | +**目标:** 把工具链行为从散落的 `if (isClang)` / `if (isGcc)` 收敛到 provider 接口。 |
| 112 | +
|
| 113 | +当前工具链代码分散在: |
| 114 | +- `gcc.cppm` — GCC 行为 |
| 115 | +- `clang.cppm` — Clang/libc++ 行为 + MSVC STL fallback |
| 116 | +- `llvm.cppm` — xlings 包映射 |
| 117 | +- `detect.cppm` — 只处理 GCC/Clang |
| 118 | +- `flags.cppm` — 编译/链接 flags 按平台分支 |
| 119 | +- `ninja_backend.cppm` — 构建规则按平台分支 |
| 120 | +
|
| 121 | +建议拆成明确的 provider: |
| 122 | +
|
| 123 | +``` |
| 124 | +ToolchainProvider (interface) |
| 125 | + ├── GccProvider — GCC + glibc/musl |
| 126 | + ├── ClangLibcxxProvider — Clang + libc++ (Linux/macOS) |
| 127 | + ├── ClangMsvcProvider — Clang + MSVC STL (Windows) |
| 128 | + └── MsvcProvider — cl.exe (未来) |
| 129 | +``` |
| 130 | +
|
| 131 | +每个 provider 声明: |
| 132 | +- `frontend()` → 编译器路径 |
| 133 | +- `c_compiler()` → C 编译器 |
| 134 | +- `archive_tool()` → ar/llvm-ar/lib.exe |
| 135 | +- `scanner()` → clang-scan-deps 路径 |
| 136 | +- `stdlib_id()` → libc++/libstdc++/msvc-stl |
| 137 | +- `find_std_module()` → std.cppm/std.cc/std.ixx |
| 138 | +- `compile_flags()` → 平台相关编译 flags |
| 139 | +- `link_flags()` → 平台相关链接 flags |
| 140 | +- `bmi_traits()` → .gcm/.pcm/.ifc |
| 141 | +
|
| 142 | +### P3: 跨平台 Process Runner |
| 143 | +
|
| 144 | +**目标:** 消除 shell 字符串拼接,统一子进程执行。 |
| 145 | +
|
| 146 | +当前问题: |
| 147 | +- `popen` + cmd.exe 字符串拼接(路径空格、引号转义脆弱) |
| 148 | +- `shq()` 在 Windows 上有 cmd.exe 首 token 引号剥离问题 |
| 149 | +- `_putenv_s` 污染全局进程环境 |
| 150 | +
|
| 151 | +建议新建 `src/process.cppm`: |
| 152 | +
|
| 153 | +```cpp |
| 154 | +struct ProcessOptions { |
| 155 | + std::vector<std::string> argv; |
| 156 | + std::map<std::string, std::string> env; // 进程级环境变量 |
| 157 | + std::filesystem::path cwd; |
| 158 | + bool capture_stdout = true; |
| 159 | + bool capture_stderr = false; |
| 160 | +}; |
| 161 | +
|
| 162 | +struct ProcessResult { |
| 163 | + int exit_code; |
| 164 | + std::string stdout_output; |
| 165 | + std::string stderr_output; |
| 166 | +}; |
| 167 | +
|
| 168 | +ProcessResult run(const ProcessOptions& opts); |
| 169 | +``` |
| 170 | + |
| 171 | +POSIX: `fork/exec` + `pipe` |
| 172 | +Windows: `CreateProcessW` + `STARTUPINFOW` |
| 173 | + |
| 174 | +**受益范围:** `probe.cppm`、`xlings.cppm`、`stdmod.cppm`、`ninja_backend.cppm`、`config.cppm` |
| 175 | + |
| 176 | +### P4: `mcpp pack` Windows 支持 |
| 177 | + |
| 178 | +**目标:** `mcpp pack` 原生支持 Windows PE 打包。 |
| 179 | + |
| 180 | +当前 `pack.cppm` 依赖: |
| 181 | +- `LD_TRACE_LOADED_OBJECTS` (Linux ELF) |
| 182 | +- `patchelf` (RPATH 修改) |
| 183 | +- `tar -czf` (打包格式) |
| 184 | + |
| 185 | +Windows 需要: |
| 186 | +- DLL 依赖收集(`dumpbin /dependents` 或 `llvm-objdump`) |
| 187 | +- 无需 RPATH(DLL 在 exe 同目录自动找到) |
| 188 | +- `.zip` 打包 + `.bat` wrapper |
| 189 | + |
| 190 | +建议 pack 做成平台策略: |
| 191 | + |
| 192 | +``` |
| 193 | +PackStrategy (interface) |
| 194 | + ├── LinuxElfPack — ldd + patchelf + tar.gz |
| 195 | + ├── MacosMachoPack — otool + install_name_tool + tar.gz |
| 196 | + └── WindowsPePack — dumpbin + zip + .bat |
| 197 | +``` |
| 198 | + |
| 199 | +### P5: E2E 能力标签化 |
| 200 | + |
| 201 | +**目标:** 从"平台 skip 列表"升级为"能力标签"。 |
| 202 | + |
| 203 | +在每个 E2E 脚本头部声明需求: |
| 204 | + |
| 205 | +```bash |
| 206 | +# requires: elf — 需要 ELF 工具链 |
| 207 | +# requires: gcc — 需要 GCC |
| 208 | +# requires: symlink — 需要 ln -sf |
| 209 | +# requires: scan-deps — 需要 clang-scan-deps |
| 210 | +# requires: import-std — 需要 import std (std.cppm/std.ixx) |
| 211 | +# requires: pack — 需要 mcpp pack |
| 212 | +``` |
| 213 | + |
| 214 | +`run_all.sh` 读取标签,根据当前平台的能力集决定 skip,不再维护平台 skip 列表。 |
| 215 | + |
| 216 | +## 实施顺序 |
| 217 | + |
| 218 | +``` |
| 219 | +P0 release + E2E 修复 ← 立即可做,产出最大 |
| 220 | + ↓ |
| 221 | +P1 PlatformTraits ← 减少 #if 散落,降低后续维护成本 |
| 222 | + ↓ |
| 223 | +P2 ToolchainProvider ← 为 MSVC 支持打基础 |
| 224 | + ↓ |
| 225 | +P3 Process Runner ← 消除 shell 拼接风险 |
| 226 | + ↓ |
| 227 | +P4 mcpp pack Windows ← 产品化打包 |
| 228 | + ↓ |
| 229 | +P5 E2E 标签化 ← 测试治理 |
| 230 | +``` |
| 231 | + |
| 232 | +## 预期里程碑 |
| 233 | + |
| 234 | +| 阶段 | 目标 | Windows E2E 通过率 | |
| 235 | +|------|------|-------------------| |
| 236 | +| 当前 | self-host + 基础 E2E | 22/49 (45%) | |
| 237 | +| P0 完成 | release + 高价值 E2E | ~30/49 (61%) | |
| 238 | +| P1+P2 完成 | 平台抽象 + provider | ~35/49 (71%) | |
| 239 | +| P3+P4 完成 | process runner + pack | ~40/49 (82%) | |
| 240 | +| P5 完成 | 能力标签 | 动态评估 | |
0 commit comments