Skip to content

Commit 2ba6303

Browse files
committed
feat(toolchain): MinGW-w64 GCC via the xlings ecosystem (mingw@16.1.0) (0.0.89)
User-facing 'mingw' spec → xim:mingw-gcc (winlibs GCC 16.1.0 + MinGW-w64 14.0.0 UCRT standalone build, mirrored at xlings-res/mingw-gcc). Reuses the GCC backend end-to-end (gcm module pipeline, bits/std.cc import std); Windows adds static libstdc++/libgcc defaults so produced exes run standalone, and -static under linkage="static". archive_tool resolves the bundled ar.exe; std.cc lookup gains a version-dir scan fallback. Fixes ridden along: toolchain list 'Available' + partial-version resolution read xpkg versions for the HOST platform (was hardcoded linux — empty on win/mac); toolchain install no longer pulls glibc/linux-headers on win/mac. e2e 97 (requires: windows cap) + ci-windows MinGW step + unit tests (dialect rows, mingw spec mapping); docs + CHANGELOG; version 0.0.89.
1 parent 47e45d6 commit 2ba6303

12 files changed

Lines changed: 290 additions & 6 deletions

File tree

‎.github/workflows/ci-windows.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,16 @@ jobs:
220220
cd hello_win
221221
"$MCPP_SELF" run
222222
223+
# MinGW-w64 GCC via the xlings ecosystem (xim:mingw-gcc → xlings-res
224+
# winlibs mirror): install → default → modules build/run → standalone
225+
# exe. Same flow as e2e 97 but as a visible CI step. Payload is cached
226+
# via the mcpp sandbox cache after the first run.
227+
- name: "Toolchain: MinGW — install → build → run (xim:mingw-gcc)"
228+
shell: bash
229+
run: |
230+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
231+
MCPP="$MCPP_SELF" bash tests/e2e/97_mingw_toolchain.sh
232+
223233
# windows-latest ships VS 2022 Enterprise with the VC workload, so
224234
# msvc@system detection MUST succeed here — a failure is a regression
225235
# in the discovery/identification chain (vswhere → env → paths).

‎CHANGELOG.md‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,36 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。
55
6+
## [0.0.89] — 2026-07-13
7+
8+
### 新增
9+
10+
- **MinGW-w64 工具链入 xlings 生态(Windows 原生 GCC,无需 Visual Studio)**。
11+
`mcpp toolchain install mingw 16.1.0` / `default mingw@16.1.0`:xim 包
12+
`mingw-gcc`(winlibs GCC 16.1.0 + MinGW-w64 14.0.0 UCRT 独立构建,镜像于
13+
xlings-res/mingw-gcc,GitHub+GitCode 双端)。复用既有 GCC 后端
14+
(gcm 模块管线、libstdc++ `bits/std.cc` 的 `import std`);Windows 上
15+
libstdc++/libgcc 默认静态链接(产物免带 DLL,`[build] static_stdlib=false`
16+
可关),`linkage="static"` 升级全静态。e2e 97 覆盖 install→default→
17+
多模块 build/run→独立 exe 验证;ci-windows 新增专项步骤。
18+
连带修复:`toolchain list` 的 Available 段与部分版本解析此前硬编码按
19+
"linux" 平台读取 xpkg 版本(Windows/macOS 上恒空);`toolchain install`
20+
在 Windows/macOS 不再错误安装 glibc/linux-headers 依赖。
21+
22+
### 重构
23+
24+
- **工具链后端抽象层(Part A,对 GCC/Clang 零行为变化,build.ninja 零 diff
25+
实证)**。新增 `mcpp.toolchain.dialect`(命令行拼写 traits:gnu/msvc 两行
26+
数据,`-I/-D/-std=/-c/-o/-O/-g/ar` 及归档命令模板经其发射);`BmiTraits`
27+
并入模块旗标拼写(`compileModulesFlag`/`stdBmiUsePrefix`/
28+
`moduleOutputPrefix`/`bmiSearchPrefix`),flags.cppm 的 is_clang 分支改由
29+
数据驱动;`ProviderCapabilities.has_builtin_p1689_scan` 取代 ninja 后端的
30+
is_gcc 门;`Toolchain::envOverrides`(EnvVar 表,注入 ninja 子进程环境,
31+
为 MSVC 后端 INCLUDE/LIB/PATH 预留);gcc provider 增加与 clang 同形的
32+
`std_module_build_commands`(命令序列);`resolve_link_model` 在 Windows
33+
显式返回 PE 空模型。设计:`.agents/docs/2026-07-13-toolchain-backend-
34+
abstraction-msvc-mingw-design.md` + 同日触点审计文档。
35+
636
## [0.0.88] — 2026-07-13
737

838
### 新增

‎docs/03-toolchains.md‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,30 @@ Available (run `mcpp toolchain install <compiler> <version>`):
6969

7070
The entry marked with `*` is the current default toolchain. `@mcpp/...` is shorthand for `~/.mcpp/...`, used to keep the output narrower.
7171

72+
## MinGW (Windows-native GCC, no Visual Studio required)
73+
74+
On Windows, `mingw` installs a self-contained MinGW-w64 GCC (winlibs
75+
standalone build, UCRT runtime) into mcpp's sandbox — the same managed-xpkg
76+
model as `gcc`/`llvm`, no Visual Studio needed:
77+
78+
```bash
79+
mcpp toolchain install mingw 16.1.0
80+
mcpp toolchain default mingw@16.1.0
81+
```
82+
83+
It uses the regular GCC module pipeline (`gcm.cache`, `import std` via
84+
libstdc++'s `bits/std.cc`). Produced binaries statically link libstdc++ and
85+
libgcc by default, so they run standalone — no `libstdc++-6.dll` needs to
86+
travel next to your exe (opt out with `[build] static_stdlib = false`).
87+
`[build] linkage = "static"` upgrades that to a fully static link.
88+
89+
In a manifest:
90+
91+
```toml
92+
[toolchain]
93+
windows = "mingw@16.1.0"
94+
```
95+
7296
## MSVC (System Toolchain, Windows)
7397

7498
MSVC is different from every other toolchain mcpp manages: it is a **system

‎mcpp.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mcpp"
3-
version = "0.0.88"
3+
version = "0.0.89"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]

‎src/toolchain/fingerprint.cppm‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import mcpp.toolchain.detect;
1818

1919
export namespace mcpp::toolchain {
2020

21-
inline constexpr std::string_view MCPP_VERSION = "0.0.88";
21+
inline constexpr std::string_view MCPP_VERSION = "0.0.89";
2222

2323
struct FingerprintInputs {
2424
Toolchain toolchain;

‎src/toolchain/gcc.cppm‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@ std::optional<std::filesystem::path> find_std_module_source(
7070
auto p = root / "include" / "c++" / std::string(version) / "bits" / "std.cc";
7171
if (std::filesystem::exists(p)) return p;
7272

73+
// Version-dir scan fallback: the header dir doesn't always equal the
74+
// full driver version (e.g. distro / MinGW-w64 builds using the major
75+
// version, or a patched banner). Any bits/std.cc under include/c++ is
76+
// this installation's — take the first.
77+
{
78+
std::error_code ec;
79+
auto cxxInclude = root / "include" / "c++";
80+
if (std::filesystem::exists(cxxInclude, ec)) {
81+
for (auto& entry : std::filesystem::directory_iterator(cxxInclude, ec)) {
82+
auto cand = entry.path() / "bits" / "std.cc";
83+
if (std::filesystem::exists(cand, ec)) return cand;
84+
}
85+
}
86+
}
87+
7388
auto cmd = std::format("'{}' -print-file-name=libstdc++.so 2>/dev/null",
7489
cxx_binary.string());
7590
auto r = mcpp::toolchain::run_capture(cmd);

‎src/toolchain/lifecycle.cppm‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,15 @@ list_available_xpkg_versions(const mcpp::config::GlobalConfig& cfg,
123123
if (!std::filesystem::exists(p, ec)) return std::nullopt;
124124
std::ifstream is(p);
125125
std::string body((std::istreambuf_iterator<char>(is)), {});
126-
return mcpp::manifest::list_xpkg_versions(body, "linux");
126+
// xpkg descriptors key platforms as linux / macosx / windows — list
127+
// the HOST's block (previously hardcoded "linux", which made the
128+
// Available section and partial-version resolution empty on
129+
// Windows/macOS for any package, e.g. mingw-gcc has no linux block).
130+
constexpr std::string_view xpkgPlatform =
131+
mcpp::platform::is_windows ? "windows"
132+
: mcpp::platform::is_macos ? "macosx"
133+
: "linux";
134+
return mcpp::manifest::list_xpkg_versions(body, xpkgPlatform);
127135
};
128136

129137
auto data = cfg.xlingsHome() / "data";
@@ -388,8 +396,11 @@ export int toolchain_install(const mcpp::config::GlobalConfig& cfg,
388396

389397
// Ensure sysroot dependencies (glibc, linux-headers) are installed.
390398
// These are required for C library + kernel headers during compilation.
391-
// musl-gcc is self-contained and doesn't need these.
392-
if (!spec->isMusl) {
399+
// musl-gcc is self-contained and doesn't need these; neither do
400+
// Windows (llvm/mingw — PE, own CRT) or macOS (SDK) toolchains.
401+
// Mirrors the platform guard on prepare.cppm's first-run install.
402+
if (!spec->isMusl
403+
&& !mcpp::platform::is_windows && !mcpp::platform::is_macos) {
393404
for (auto dep : {"xim:glibc", "xim:linux-headers"}) {
394405
mcpp::log::verbose("toolchain", std::format("installing dep: {}", dep));
395406
auto depPayload = fetcher.resolve_xpkg_path(dep, /*autoInstall=*/true, &progress);

‎src/toolchain/model.cppm‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ bool is_gcc(const Toolchain& tc);
6565
bool is_clang(const Toolchain& tc);
6666
bool is_musl_target(const Toolchain& tc);
6767
bool is_msvc_target(const Toolchain& tc);
68+
bool is_mingw_target(const Toolchain& tc);
6869

6970
struct BmiTraits {
7071
std::string_view bmiDir; // "gcm.cache" | "pcm.cache" | "ifc.cache"
@@ -103,6 +104,11 @@ bool is_msvc_target(const Toolchain& tc) {
103104
return tc.targetTriple.find("msvc") != std::string::npos;
104105
}
105106

107+
bool is_mingw_target(const Toolchain& tc) {
108+
// "x86_64-w64-mingw32" (mingw-w64) / legacy "*-pc-mingw32".
109+
return tc.targetTriple.find("mingw32") != std::string::npos;
110+
}
111+
106112
BmiTraits bmi_traits(const Toolchain& tc) {
107113
if (tc.compiler == CompilerId::MSVC) {
108114
// Native cl.exe builds are gated off until the .ifc pipeline lands;

‎src/toolchain/registry.cppm‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
export module mcpp.toolchain.registry;
44

55
import std;
6+
import mcpp.platform;
67
import mcpp.toolchain.clang;
78
import mcpp.toolchain.gcc;
89
import mcpp.toolchain.llvm;
@@ -101,6 +102,7 @@ std::vector<std::string> frontend_candidates_for(std::string_view ximName,
101102
if (ximName == "gcc") return {"g++"};
102103
if (ximName == "llvm") return mcpp::toolchain::llvm::frontend_candidates();
103104
if (ximName == "msvc") return {"cl.exe"};
105+
if (ximName == "mingw-gcc") return {"g++.exe", "g++"};
104106
return {"g++", "clang++", "x86_64-linux-musl-g++", "cl.exe"};
105107
}
106108

@@ -170,6 +172,10 @@ XimToolchainPackage to_xim_package(const ToolchainSpec& spec) {
170172
std::string ximCompiler = spec.compiler;
171173
if (mcpp::toolchain::llvm::is_alias(ximCompiler))
172174
ximCompiler = mcpp::toolchain::llvm::package_name();
175+
// Windows-native MinGW-w64 GCC: user-facing name `mingw`, xim package
176+
// `mingw-gcc` (winlibs GCC+MinGW-w64 UCRT builds mirrored at xlings-res).
177+
if (ximCompiler == "mingw")
178+
ximCompiler = "mingw-gcc";
173179

174180
pkg.ximName = ximCompiler;
175181
pkg.ximVersion = spec.version;
@@ -221,6 +227,8 @@ std::filesystem::path toolchain_frontend(const std::filesystem::path& binDir,
221227
std::string display_label(std::string_view compiler, std::string_view version) {
222228
if (compiler == "musl-gcc")
223229
return std::format("gcc {}-musl", version);
230+
if (compiler == "mingw-gcc")
231+
return std::format("mingw {}", version);
224232
return std::format("{} {}", compiler, version);
225233
}
226234

@@ -235,6 +243,12 @@ bool matches_default_toolchain(std::string_view configuredDefault,
235243
// The persisted msvc default is always the stable "msvc@system" (never a
236244
// concrete version), so it matches whatever version detection reports.
237245
if (compiler == "msvc" && configuredDefault == "msvc@system") return true;
246+
// Installed-payload rows report the xim name (mingw-gcc); the user-facing
247+
// spec is mingw@<ver> (same shape as the musl-gcc clause above).
248+
if (compiler == "mingw-gcc"
249+
&& configuredDefault == std::format("mingw@{}", version)) {
250+
return true;
251+
}
238252
return false;
239253
}
240254

@@ -243,11 +257,15 @@ bool is_system_toolchain(const ToolchainSpec& spec) {
243257
}
244258

245259
std::vector<std::pair<std::string, std::string>> available_toolchain_indexes() {
246-
return {
260+
std::vector<std::pair<std::string, std::string>> out{
247261
{"gcc", "gcc"},
248262
{"musl-gcc", "musl-gcc"},
249263
{mcpp::toolchain::llvm::package_name(), "llvm"},
250264
};
265+
// Windows-native toolchain — only meaningful to list on Windows hosts.
266+
if constexpr (mcpp::platform::is_windows)
267+
out.emplace_back("mingw-gcc", "mingw-gcc");
268+
return out;
251269
}
252270

253271
std::filesystem::path derive_c_compiler(const Toolchain& tc) {
@@ -257,6 +275,15 @@ std::filesystem::path derive_c_compiler(const Toolchain& tc) {
257275
std::filesystem::path archive_tool(const Toolchain& tc) {
258276
if (is_clang(tc)) return mcpp::toolchain::clang::archive_tool(tc);
259277

278+
// MinGW bundles its own binutils next to the frontend (self-contained,
279+
// like musl) — never an external binutils xpkg.
280+
if (is_mingw_target(tc)) {
281+
auto ar = tc.binaryPath.parent_path() / "ar.exe";
282+
std::error_code ec;
283+
if (std::filesystem::exists(ar, ec)) return ar;
284+
return {};
285+
}
286+
260287
if (!is_musl_target(tc)) {
261288
if (auto binutilsBin = mcpp::toolchain::gcc::find_binutils_bin(tc.binaryPath))
262289
return *binutilsBin / "ar";

‎tests/e2e/97_mingw_toolchain.sh‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env bash
2+
# requires: windows
3+
# 97_mingw_toolchain.sh — Windows-native MinGW-w64 GCC via the xlings
4+
# ecosystem (xim:mingw-gcc, winlibs GCC 16.1.0 UCRT):
5+
# - `toolchain install mingw 16.1.0` resolves + installs the xpkg
6+
# - `toolchain default mingw@16.1.0` persists; `list` stars `mingw 16.1.0`
7+
# - new → build → run works with import std + a named module (gcm pipeline)
8+
# - the produced exe is portable: runs from a clean dir without the
9+
# toolchain's bin on PATH (static libstdc++/libgcc defaults)
10+
set -e
11+
12+
CONF="${MCPP_HOME:-$HOME/.mcpp}/config.toml"
13+
ORIG_DEFAULT=""
14+
if [[ -f "$CONF" ]]; then
15+
ORIG_DEFAULT=$(sed -n '/^\[toolchain\]/,/^\[/p' "$CONF" \
16+
| grep '^default' | head -1 | cut -d'"' -f2 || true)
17+
fi
18+
TMP=$(mktemp -d)
19+
restore() {
20+
if [[ -n "$ORIG_DEFAULT" ]]; then
21+
"$MCPP" toolchain default "$ORIG_DEFAULT" >/dev/null 2>&1 || true
22+
fi
23+
rm -rf "$TMP"
24+
}
25+
trap restore EXIT
26+
27+
cd "$TMP" # neutral cwd — project mcpp.toml [toolchain] must not shadow
28+
29+
# 1) install via the xlings ecosystem (xlings-res mirror)
30+
out=$("$MCPP" toolchain install mingw 16.1.0 2>&1) \
31+
|| { echo "FAIL: install mingw: $out"; exit 1; }
32+
[[ "$out" == *"Installed"* || "$out" == *"already"* || "$out" == *"mingw"* ]] \
33+
|| { echo "FAIL: install output: $out"; exit 1; }
34+
35+
# 2) switch default + list shows it starred
36+
"$MCPP" toolchain default mingw@16.1.0 \
37+
|| { echo "FAIL: default mingw@16.1.0"; exit 1; }
38+
out=$("$MCPP" toolchain list 2>&1)
39+
echo "$out" | grep -E '\*\s*mingw 16\.1\.0' >/dev/null \
40+
|| { echo "FAIL: mingw not starred: $out"; exit 1; }
41+
42+
# 3) real build: import std + named module through the gcm pipeline
43+
"$MCPP" new hello_mingw >/dev/null 2>&1
44+
cd hello_mingw
45+
mkdir -p src
46+
cat > src/greet.cppm <<'EOF'
47+
export module hello.greet;
48+
import std;
49+
export namespace hello {
50+
std::string greet() { return "mingw-ok"; }
51+
}
52+
EOF
53+
cat > src/main.cpp <<'EOF'
54+
import std;
55+
import hello.greet;
56+
int main() { std::println("{}", hello::greet()); return 0; }
57+
EOF
58+
out=$("$MCPP" build 2>&1) || { echo "FAIL: build: $out"; exit 1; }
59+
run_out=$("$MCPP" run 2>&1) || { echo "FAIL: run: $run_out"; exit 1; }
60+
[[ "$run_out" == *"mingw-ok"* ]] || { echo "FAIL: run output: $run_out"; exit 1; }
61+
62+
# 4) portability: the exe must run from a clean dir WITHOUT the toolchain
63+
# bin dir on PATH (no libstdc++-6.dll / libgcc_s hitchhikers needed).
64+
EXE=$(find target -name "hello_mingw.exe" -path "*/bin/*" | head -1)
65+
[[ -n "$EXE" ]] || { echo "FAIL: no exe produced"; exit 1; }
66+
ISO="$TMP/iso"; mkdir -p "$ISO"
67+
cp "$EXE" "$ISO/"
68+
iso_out=$(cd "$ISO" && PATH="/usr/bin:/c/Windows/System32" ./hello_mingw.exe 2>&1) \
69+
|| { echo "FAIL: exe not standalone (static libstdc++/libgcc?): $iso_out"; exit 1; }
70+
[[ "$iso_out" == *"mingw-ok"* ]] || { echo "FAIL: standalone output: $iso_out"; exit 1; }
71+
72+
echo "PASS: mingw toolchain — install, default, modules build/run, standalone exe"

0 commit comments

Comments
 (0)