Windows下用MSYS2搭建C++开发环境:GCC/CMake配置实战
2026/9/16 8:26:39 网站建设 项目流程

Windows 上用 MSYS2 折腾 C++ 开发环境这件事,我前前后后花了将近两周才彻底理顺。最开始只是在 VS 里写算法题,后来项目要用 CMake 跨平台编译,又需要 GCC 才有的某些 warning 和 sanitizer,Visual Studio 那套 MSBuild 工程在 Linux CI 上一跑就各种闹脾气。当时试过 WSL,但是文件 IO 和网络代理在虚拟机层转发总有点延迟,尤其是配合 CLion 和 VSCode 的远程开发,体验始终差一口气。兜兜转转才落到 MSYS2 上,这套工具链用到现在已经成为我 Windows 上写 C++ 的主力方案。

这篇东西我会把从零安装、pacman 镜像加速、GCC/CMake 配置、路径污染避坑到 CMake 工程落地跑的完整过程全部写出来,包括那些让人血压飙升的“gcc -v 显示旧版本”“安装卡在 50%”“找不到 -lstdc++”这类问题,把排查思路和最终解法一次性讲透。适合准备入坑开源工具链的 C++ 新手,也适合被 VS 工程迁移折腾到崩溃、想换套干净利落编译流程的同学参考。

1. 整体方案设计与思路拆解

1.1 为什么 Windows 上的 C++ 开发需要折腾这套东西

先说说我为什么放着好好的 Visual Studio 不用,非要折腾这么一套“Linux 味道”的环境。VS 本身是个很优秀的 IDE,我承认它在调试器的易用性上至今无人能比。但它的构建系统——MSBuild 和 .vcxproj——存在很大的问题。

VS 工程文件动辄几千行 XML,团队协作时合并冲突让人抓狂。Windows 上改一行代码,同步到 Linux CI 上编译,大概率会冒出各种“类型不匹配”“找不到头文件”的问题。原因很简单:MSVC 和 GCC 对 C++ 标准的支持程度、模板实例化机制、宏定义的默认行为都有差异。你写的是跨平台 C++,但 VS 的工程配置把你牢牢绑死在 Windows 上。

MSYS2 的思路完全不同。它本质上是在 Windows 上跑了一层 POSIX 兼容层,然后通过自带的 pacman 包管理器维护一套完整的 GNU 工具链。你不需要 CMake 去生成 .sln 文件,也不需要关心 .vcxproj 里那些平台工具集版本号。写 CMakeLists.txt 声明目标,然后 cmake、make、g++ 三个命令搞定一切,这套流程和你在 Ubuntu 上敲的命令一模一样。

1.2 MSYS2、Cygwin、WSL 怎么选

这个问题几乎每个折腾过的人都会纠结。WSL 适合跑完整 Linux 发行版,但做 C++ 开发有个天然的痛点:跨文件系统 IO 慢。如果你把源码放在 /mnt/c/ 下,每次编译都要经过虚拟化层做文件转发,大项目编译起来那叫一个煎熬。想把项目放 WSL 内部文件系统,VSCode 远程连接又需要额外配置,而且 VSCode 的 C++ 插件 remote 模式下很多东西不稳定。

Cygwin 走的是另一条路,它兼容层做得更彻底,但也因此更重。Cygwin 动态库(cygwin1.dll)对 POSIX 的兼容程度近乎“整台机器伪装成 Linux”,对于想要“在 Windows 上原生运行程序”的场合并不合适,而且它的包管理器比 pacman 难用得多。

MSYS2 的正确打开方式是:把 MSYS2 当作包管理器和编译环境启动器,编译出来的程序默认依赖 Windows 原生 API(取决于你选的工具链),不会像 Cygwin 那样要求运行时环境。用 MSYS2 终端跑命令可以获得和 Linux shell 接近的体验,同时编译产物可以直接在 Windows 上运行,不需要额外的 DLL 环境(除非静态链接的是动态库版本)。

如果你还是纠结,我给你一个粗暴的建议:主力开发 Windows 原生 GUI 程序选 MSYS2;需要完整 Linux 生态(部署、跑 Python 包、跑 research 脚本)选 WSL;Cygwin 就别浪费生命了。

1.3 选择 GCC + MSYS2 而非 MSVC 的本质原因

MSVC 编译器和 GCC 在使用层面最大的区别不在于速度,而在于标准支持策略和编译选项体系的差异。GCC 对 C++17、C++20 的很多特性支持得更激进,它对“代码哪里写得有潜在问题”的警告体系也特别完善。比如-Wall -Wextra -Wpedantic -Wconversion这套组合,在 MSVC 下要开启/W4还不够,很多 warning 根本不提示。

更关键的是,CMake 与 GCC 的组合是开源社区事实上的标准构建流程。你想用的那些第三方库——Boost、fmt、spdlog、GoogleTest、Eigen——它们的官方文档给出的 CMake 集成范例,绝大多数是基于 GCC 测试验证过的。你在 Linux 上用这套,Windows 上也能无缝平移,这才是“Linux 式开发环境”的核心价值。

2. MSYS2 安装与环境准备,把坑填平

2.1 下载与安装:别用旧版本,别放 C:\msys64 之外

MSYS2 的下载源始终在官方 GitHub Releases 页面。注意千万别去各种第三方软件站下载“MSYS2 中文版”,那玩意要么被塞了广告,要么版本旧到连 pacman 都跑不动。

我之前就踩过一次坑,下了个老版本的 MSYS2,结果 pacman -Syu 每次都报错PGP signature verification failed,折腾半天才发现是官方改了密钥分发方式,旧版本连带新的 pacman 更新都做不了。所以直接去官方页面拉最新版,文件名一般是msys2-x86_64-2024xxxx.exe

安装路径保持默认C:\msys64就好。为什么?因为 MSYS2 的内部环境变量、pacman 脚本、shell 配置都硬编码了这个路径。如果你自作聪明装到D:\Tools\MSYS2,后面遇到的各种cannot find /usr/bin/bash.bashrc不生效问题,大概率都和路径异常有关。

安装过程中如果卡在“Installing 50%”这种阶段,别急着关窗口,先检查是不是杀毒软件在实时扫描。Windows Defender 对 MSYS2 的 bash.exe 和 gcc.exe 有时候会做全量扫描,导致解包极慢,甚至假死。处理方法是把C:\msys64整个目录加入 Defender 排除项,然后把安装包也加入排除项。如果你公司电脑装了第三方安全软件,务必确认它不会拦截newlibbinutils关键文件的写入。

2.2 第一次启动:pacman -Syu 的正确姿势与镜像加速

安装完成后启动 MSYS2 MSYS 终端,第一件事就是执行更新:

pacman -Syu

这个命令会同步仓库并更新所有核心包。但国内直连官方源速度非常感人,经常几 KB/s,而且容易在下载中途报failed retrieving file ... could not resolve host

解决办法是换成清华 TUNA 镜像或者中科大镜像。以清华为例,编辑/etc/pacman.d/mirrorlist.msys,把文件里的 Server 行替换或前置为:

Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch

注意,MSYS2 仓库分成三个:msys(shell 和基础工具)、mingw64(64 位 Windows 原生程序)、mingw32(32 位)。对应的配置文件有三个:

  • /etc/pacman.d/mirrorlist.msys
  • /etc/pacman.d/mirrorlist.mingw64
  • /etc/pacman.d/mirrorlist.mingw32

三个文件都要替换成同样的清华地址,只是最后的$repo/$arch部分会略有区别,通常清华镜像提供的是统一路径msys2/$repo/$arch,直接全部替换即可。

替换完再执行一次:

pacman -Syu

如果之前因为强制关闭导致部分包处于锁状态,先执行:

rm -rf /var/lib/pacman/db.lck

这个锁文件是 pacman 防止并发修改的机制。很多人碰到“卡住”“一直等待”都是因为这个残留锁。

2.3 pacman 核心命令速查

这套包管理器是 MSYS2 的灵魂,我用顺了之后觉得比 apt 还清晰。以下命令请当作肌肉记忆来背:

pacman -Ss 关键词 # 搜索包 pacman -S 包名 # 安装包 pacman -Sy # 刷新仓库但不升级(慎用,见到 Y 之后再说) pacman -Syu # 刷新仓库并全量升级 pacman -R 包名 # 卸载单个包 pacman -Rs 包名 # 卸载包及其不再需要的依赖 pacman -Ql 包名 # 列出某个包安装后释放的所有文件

搜索 GCC 相关包时可以这么做:

pacman -Ss mingw-w64-x86_64-gcc

会看到一堆类似mingw-w64-x86_64-gccmingw-w64-x86_64-gcc-adamingw-w64-x86_64-gcc-fortran的包。C++ 开发只需要带基础功能的mingw-w64-x86_64-gcc,Fortran/Ada 那些爱装不装,反正用不到。

注意 MSYS2 下的包名都带mingw-w64-x86_64-前缀,而 MSYS 环境自身的工具包(如gcc)是另一个体系,不要搞混了。前者是 Windows 原生程序,后者是依赖 MSYS2 运行时环境的版本。

3. 核心组件安装与 GCC 编译器配置避坑

3.1 用 pacman 一次装齐 GCC、CMake、Ninja 和调试工具

MSYS2 下安装这些真是一行命令的事,但要注意一下包选择。先给完整命令:

pacman -S --needed \ mingw-w64-x86_64-gcc \ mingw-w64-x86_64-gdb \ mingw-w64-x86_64-cmake \ mingw-w64-x86_64-ninja \ mingw-w64-x86_64-make \ mingw-w64-x86_64-toolchain \ base-devel \ git

这里有几个点需要解释。

第一,为什么选择mingw-w64-x86_64-cmake而不是 MSYS 环境下的cmake?因为前者编译时默认绑定的是MINGW64环境下的 Make 和 Ninja,能正确识别 GCC 工具链。后者虽然也能用,但它生成的 Makefile 可能默认调用 MSYS 子系统的工具链,稍不注意就会产生一堆奇怪的路径问题。

第二,mingw-w64-x86_64-toolchainmingw-w64-x86_64-gcc有依赖关系,直接装 toolchain 会把 gcc、g++、gfortran、objdump、ar、ld 等一整套都带出来。如果你追求最小集,装 gcc 和 gdb 就够,但说实话这套环境最不缺的就是磁盘空间,直接装 toolchain 省心。

第三,--needed参数表示“如果已安装则跳过”。用这个参数可以放心重复执行安装命令,不会报冲突,也可以当“补装漏掉依赖”的工具。

安装完成之后验证版本:

gcc --version g++ --version cmake --version gdb --version

正常会显示类似gcc (Rev5, Built by MSYS2 project) 13.2.0这样的输出。

3.2 环境变量配置的血泪教训:PATH 顺序决定了你的 gcc 是谁

这一步是整个 MSYS2 设置里最阴间的环节,没有之一。

问题场景是这样的:你明明刚执行pacman -S mingw-w64-x86_64-gcc装了新版 GCC 15,结果在任意终端一敲gcc --version,它显示的还是 8.1.0,再一看路径C:\Program Files\mingw-w64\...\gcc.exe,或者更离谱,直接跳出来 Visual Studio 的 cl.exe 提示“gcc 不是内部或外部命令”——这两种情况我都遇见过。

根源在于 Windows 的环境变量 PATH 中,其他目录的gcc.exe抢占了 MSYS2 的入口。比如某些版本控制软件、Anaconda、Git for Windows 自带的 MinGW 工具链,都会往 PATH 里塞自己的 gcc,而这些路径通常排在C:\msys64\mingw64\bin前面。

解决办法有两个方向。

方向一:设置 MSYS2 内部的 PATH。编辑/etc/profile或用户级~/.bashrc,把下面的行加到文件最前面:

export PATH="/mingw64/bin:/usr/local/bin:/usr/bin:$PATH"

这样每次启动 MSYS2 终端,内部 shell 的 PATH 就永远是 MSYS2 的目录优先。然后在 MSYS2 终端里验证which gcc,确认它是/mingw64/bin/gcc

方向二:如果你希望 Windows 的 CMD 或 PowerShell 也能直接用 gcc/cmake,那就需要在系统环境变量里把C:\msys64\mingw64\binC:\msys64\usr\bin放在最前面。但这么做有个副作用——usr\bin下有一堆 GNU 工具(ls、cat、find、sort、uniq),会和 Windows 自带的同名命令混淆,部分脚本会因此产生诡异的行为。我的建议是只在 MSYS2 环境里用 GCC 相关命令,Windows 系统 PATH 保持干净,最多把C:\msys64\mingw64\bin加上去。

另外要说一下“gcc -v 不回显版本号”的问题。有些人在 CMD 里执行:

gcc -v

回车之后发现没有输出,或者只打印几行然后异常退出。这通常是gcc.exe依赖的 DLL 在 PATH 中找不到。GCC 运行需要libwinpthread-1.dlllibgcc_s_seh-1.dll这些运行时库,它们都位于C:\msys64\mingw64\bin。如果你只把C:\msys64\mingw64\bin和系统的别的路径混合排列,顺序不对就会导致找错 DLL。保证mingw64\bin在 PATH 的最前面基本能解决。

还有一个小技巧:MSYS2 环境里查看gcc -v时尾部的Configured with配置信息很有价值。它能告诉你当前的 GCC 构建配置,确认目标平台是x86_64-w64-mingw32,这就是 Windows 原生的意思,而不是 msys 目标。

3.3 mingw64 和 ucrt64 工具链的区别,别选错环境

MSYS2 在 2021 年之后引入了多套工具链环境,除了经典的mingw64之外,还有ucrt64clang64clangarm64

  • mingw64:使用微软旧版 MSVCRT 运行时,兼容性最好,能跑的 Windows 版本最广(从 Win7 到 Win11 都可以)。
  • ucrt64:使用新版 UCRT(Universal C Runtime),VS2015 之后所有 Windows 都自带,安全性更好,能解决某些 C 标准库函数的坑。
  • clang64:基于 Clang 编译器 + UCRT,适合想用 Clang 写代码的人。

我的建议是:常规开发老老实实用mingw64。原因有几个。第一,绝大多数 MSYS2 预编译的第三方库(Boost、Qt、GTKmm、OpenSSL 等)都是优先在mingw64环境构建和测试的,你用ucrt64去链接它们的二进制库,大概率会因为是不同的 ABI 而失败。第二,网上查资料时,关于mingw64的踩坑记录最多也最完整,碰到问题能找到参考的概率大得多。第三,UCRT 在旧版 Windows 上是“可选更新”,万一你部署的目标机器是 Win7 或者老版本的 Win10 LTSB,少了这个运行时会直接崩溃。

所以,不要被名字唬住,工具链选mingw64就对了。在你启动 MSYS2 MSYS 终端后,也可以注意到它默认是从 MSYS 环境进入的,但安装的mingw-w64-x86_64-系列程序,其实本质上是独立于 MSYS 环境的 Windows 可执行文件,用C:\msys64\mingw64\bin\gcc.exe这个绝对路径也完全能跑。

3.4 升级 GCC 后还是旧版本怎么办

这个问题实在是高频到让人无语。很多人在 MSYS2 里执行完pacman -Syu把 GCC 升级到新版本,然后欣喜地运行:

gcc --version

结果版本号纹丝不动。第一反应是升级失败,但其实pacman -Syu默认升级的是gcc这个包(归属于 MSYS 环境),而不是mingw-w64-x86_64-gcc。两个包几乎是同名的,但安装到不同目录,前者更新 MSYS 运行时自带的编译器,后者才是你在mingw64环境里用的那个。

正确做法是:

pacman -Syu mingw-w64-x86_64-gcc

甚至可以直接:

pacman -S --needed mingw-w64-x86_64-gcc

强制让 pacman 检查并安装最新版。

第二个原因是版本缓存和命令哈希。bash 终端里有hash表,它会记忆命令对应的可执行文件路径。比如你之前执行的gcc映射到/mingw64/bin/gcc.exe,升级后哪怕这个路径没变,bash 也可能因为旧 hash 缓存不重新查找,显示的版本还是旧的。先用hash -r清一下缓存再试。

第三个原因就是前面说的 PATH 顺序问题。which gcc看一眼路径,如果指向/usr/bin/gcc而不是/mingw64/bin/gcc,说明当前 shell 环境是 MSYS 环境优先。仅靠调整 PATH 可能不够,还需要确认你启动的终端本身就是 “MSYS2 MinGW64” 快捷方式,而不是 “MSYS2 MSYS”。Windows 开始菜单里两个入口长得几乎一样,我至少三次在这上面吃亏。

4. 实操:用 CMake + GCC 编译一个完整工程

4.1 从 VSCode 开始的基础配置

既然要告别 Visual Studio,编辑器自然是 VSCode。用 VSCode 开发 C++ 最重要的一件事是安装微软官方的 C/C++ 扩展,它是调试和 IntelliSense 的提供者。

打开 VSCode,在扩展市场搜“C/C++”,安装由 Microsoft 发布的那个(注意不是 C/C++ Extension Pack,那个是合集,装单个就够了)。然后进入设置,搜索C_Cpp.default.compilerPath,把它设置为:

"C:\\msys64\\mingw64\\bin\\g++.exe"

配置这个的目的有两个:一是让 IntelliSense 正确解析 GCC 的头文件路径,不然满屏红色波浪线;二是让调试器(gdb)能正确地找到符号信息。

接着在项目根目录建.vscode/c_cpp_properties.json,给出更精细的配置:

{ "configurations": [ { "name": "Win64-MinGW", "includePath": [ "${workspaceFolder}/**", "C:/msys64/mingw64/include/c++/**", "C:/msys64/mingw64/include/**" ], "defines": [], "compilerPath": "C:/msys64/mingw64/bin/g++.exe", "cStandard": "c17", "cppStandard": "c++17", "intelliSenseMode": "windows-gcc-x64" } ], "version": 4 }

intelliSenseModewindows-gcc-x64很重要。默认是windows-msvc-x64,用 MSVC 的模式去解析 GCC 的语法,会出现一堆奇怪的误报和错误提示。

4.2 写第一个 CMake 工程

用一个最简单的“冒泡排序 + 二分查找”小项目来演示从零到编译运行的完整链路,这也是很多人新手期的第一个“C++ 小游戏”。

项目目录结构:

demo/ ├── CMakeLists.txt ├── src/ │ ├── main.cpp │ ├── sort.h │ └── sort.cpp └── include/ └── demo/ └── sort.h

CMakeLists.txt内容如下:

cmake_minimum_required(VERSION 3.22) project(Demo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_executable(demo src/main.cpp src/sort.cpp ) target_include_directories(demo PRIVATE include) if(MSVC) target_compile_options(demo PRIVATE /W4) else() target_compile_options(demo PRIVATE -Wall -Wextra -Wpedantic) endif()

这里有两个细节值得展开。

CMAKE_CXX_EXTENSIONS OFF是强制要求编译器只使用标准 C++,不要开启 GNU 扩展。不开这个选项,GCC 会默认以-std=gnu++17编译,虽然大多数情况下没区别,但某些代码的特性检测宏会变得不标准。作为想要“标准 C++”的人,我建议把这三个CXX相关选项写成固定模板。

target_compile_options里的差异化处理也很关键。MSVC 不认识-Wall,GCC 不认识/W4,所以同一个 CMakeLists 如果要在 Windows 和 Linux 上都跑,务必用if(MSVC)分支做隔离。我以前没注意这层,直接把 LLVM 仓库的 CMake 拉到 Windows 上编译,撞了满屏的 “unknown option”,还以为是 LLVM 代码问题。

src/main.cpp

#include <iostream> #include <vector> #include <algorithm> #include "demo/sort.h" int main() { std::vector<int> arr = {9, 3, 7, 1, 5, 6, 2, 8, 4}; demo::bubble_sort(arr.begin(), arr.end()); auto it = std::lower_bound(arr.begin(), arr.end(), 5); std::cout << "sorted: "; for (int x : arr) std::cout << x << ' '; std::cout << "\nfind 5 at index " << (it - arr.begin()) << '\n'; return 0; }

include/demo/sort.h

#pragma once #include <iterator> namespace demo { template <typename Iter> void bubble_sort(Iter first, Iter last) { if (first == last) return; for (auto i = first; i != last; ++i) { for (auto j = first; std::next(j) != last; ++j) { if (*std::next(j) < *j) { std::iter_swap(j, std::next(j)); } } } } }

这个例子特意用了模板,因为模板的编译行为很能反映工具链设置是否正确。如果 CMake 配置阶段有问题,很多 template 相关的 warning 会井喷式爆发,方便我们验证环境。

4.3 用命令行的方式走完 CMake 构建流程

在 MSYS2 MinGW64 终端里进入项目目录,依次执行下面这些命令就能完成配置和编译:

mkdir -p build cd build cmake .. -G "MinGW Makefiles" cmake --build .

如果非要指定编译器和生成器,也可以写成:

cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_CXX_COMPILER=g++ cmake --build build

这里面有个大坑:很多人在 Windows 上直接执行cmake ..不带-G,结果 CMake 默认找到的是 Visual Studio 生成器,接着抛出一堆错误或者生成一个你要用 VS 才能打开的 .sln 工程。原因是你系统装了 VS,CMake 检测到了 MSVC 但找不到对应的 cl.exe(因为没进 Developer Command Prompt),于是整个配置过程变成一场灾难。

-G "MinGW Makefiles"就是强制指定使用 MinGW 下的原生 makefile 生成器。CMake 会根据我们传入的CMAKE_CXX_COMPILER寻找 g++,然后生成一个适用于 mingw32-make 的 Makefile。这一步做完之后,cmake --build .就是编译+链接全过程。

还有一个选项是-G "Ninja"。Ninja 的构建速度快于 Make,尤其适合大项目增量编译。要在 MSYS2 安装 Ninja 的话执行:

pacman -S mingw-w64-x86_64-ninja

然后用:

cmake -S . -B build-ninja -G "Ninja" cmake --build build-ninja

Ninja 的日志输出和错误格式我个人认为比 Make 更清晰,遇到编译错误时文件名和行号看得更直接,强烈推荐给有选择困难症的人直接选 Ninja。

编译完成之后运行:

./demo.exe

如果正常输出:

sorted: 1 2 3 4 5 6 7 8 9 find 5 at index 4

说明整条工具链已经彻底打通。

4.4 CMake 选项和编译器检测的深入理解

CMake 配置阶段经常遇到“明明我没装 VS,为什么 CMake 还是去检测 MSVC”的疑问。原因是 CMake 默认的CMAKE_CXX_COMPILER为空时,它会按照平台惯例查找编译器。Windows 上第一选择是用 Visual Studio 也是历史遗留习惯,并不是说你机器上没装 VS 就不会去找。

如果你不想每次命令行都手写-DCMAKE_CXX_COMPILER=g++,可以在CMakeLists.txt开头强制指定:

if(WIN32 AND NOT MSVC) set(CMAKE_C_COMPILER "gcc") set(CMAKE_CXX_COMPILER "g++") endif()

不过更好的做法是用 CMake 的预设文件。在项目根目录建CMakePresets.json

{ "version": 3, "cmakeMinimumRequired": { "major": 3, "minor": 22, "patch": 0 }, "configurePresets": [ { "name": "mingw-debug", "displayName": "MinGW Debug", "generator": "Ninja", "binaryDir": "${sourceDir}/build/mingw-debug", "cacheVariables": { "CMAKE_CXX_COMPILER": "g++", "CMAKE_BUILD_TYPE": "Debug", "CMAKE_CXX_STANDARD": "17" } }, { "name": "mingw-release", "displayName": "MinGW Release", "generator": "Ninja", "binaryDir": "${sourceDir}/build/mingw-release", "cacheVariables": { "CMAKE_CXX_COMPILER": "g++", "CMAKE_BUILD_TYPE": "Release" } } ] }

之后编译只需要:

cmake --preset mingw-debug cmake --build build/mingw-debug

这样团队协作的时候,每个人只需要安装 MSYS2 并装好工具链,项目根目录的 preset 文件会自动把所有编译参数统一起来,再也不用在文档里写“请修改 CMakeLists.txt 第 10 行的编译器路径”。

5. 常见问题与排查技巧实录

5.1 那些让人崩溃的典型错误速查表

我整理了这段时间在网上看到和亲身踩过的高频问题,直接做成表格方便查阅。

现象根本原因解决方案
安装 MSYS2 卡在 50%杀毒软件实时扫描大量小文件C:\msys64加入 Defender 排除项后重装或等待
gcc --version显示旧版本升级了错误的包或 PATH 顺序问题确认安装mingw-w64-x86_64-gccwhich gcc看路径
cmake ..生成 VS 工程系统装了 VS,CMake 默认选了 MSVC 生成器-G "MinGW Makefiles"-G "Ninja"
cannot find -lstdc++工具链没装全执行pacman -S --needed mingw-w64-x86_64-gcc mingw-w64-x86_64-toolchain
fatal error: boost/...: No such file or directory缺少 Boost 开发包pacman -S mingw-w64-x86_64-boost
MSYS2 里lsrm不是内部命令打开了 CMD 而不是 MSYS2 终端用 MSYS2 MinGW64 终端执行命令
error: Microsoft Visual C++ 14.0 or greater is requiredPython 的 pip 包编译需要 MSVC 工具链这个不是本环境的锅,安装 VS Build Tools 或者改用预编译 wheel

第二条真的是重灾区,我再补充说一句:很多人装了mingw-w64-x86_64-gcc之后which gcc显示/mingw64/bin/gcc,但还是旧版本,这是很反常的。此时直接执行pacman -Syu mingw-w64-x86_64-gcc看看有没有正在更新的提示;如果它说“up to date”,就手动删除/mingw64/bin/gcc.exe和版本对应的g++.exe之后再重新pacman -Smingw-w64-x86_64-gcc。这不是乱操作,二进制包的解压过程有时会因为之前的意外中断导致旧文件残留,手动强制重新解包可以解决。

5.2 链接库时路径和依赖关系的坑

很多初学者把第三方库下载到项目目录,然后在 CMakeLists 里写:

target_link_libraries(demo PRIVATE "D:/libs/mylib/mylib.a")

这种做法不推荐。原因在于 MSYS2 的 GCC 是 Windows 原生目标,它的链接器处理绝对路径时的规则和 Linux 下有所不同。Windows 下的绝对路径带盘符冒号,冒号在链接器的参数解析里有时会被当成驱动器分隔符,于是出现skipping incompatible或者找不到文件的诡异报错。

正确的方式是把第三方库做成 CMake 的 IMPORTED 目标:

add_library(mylib STATIC IMPORTED) set_target_properties(mylib PROPERTIES IMPORTED_LOCATION "D:/libs/mylib/libmylib.a" ) target_include_directories(demo PRIVATE "D:/libs/mylib/include") target_link_libraries(demo PRIVATE mylib)

这样 CMake 会自动处理路径转义和依赖传递关系。

另外一个值得留意的是静态库的依赖顺序。GCC 的链接器符号解析默认是单遍扫描,从左到右的。如果 A 库依赖 B 库,那么命令里必须A B的顺序,反了就会undefined reference。这个问题在项目库比较多的时候非常折磨人,我后来干脆把所有库封装成 CMake 的INTERFACE IMPORTED或者直接使用target_link_libraries传参,让 CMake 帮我们排依赖顺序。

5.3 调试器 gdb 在 Windows 上的配置注意点

Windows 上 gdb 最烦的问题有两个:一是断点命中后调试器告诉你 “cannot find bounds of current function”,二是带动态库调试时符号加载不出来。

第一个问题通常是 GCC 优化选项导致的。如果你用-O2编译,行号和变量信息会被优化得乱七八糟,gdb 找不到当前函数的边界很正常。本地调试时用-O0 -g,只有发布版本才开-O2

第二个问题,确认你的 exe 旁边能否找到它依赖的 DLL 文件。gdb 启动时如果在可执行文件目录和 PATH 中找不到libstdc++-6.dlllibgcc_s_seh-1.dll,就会自动加载失败。一个应急办法是在 MSYS2 终端里运行windeployqt之类的部署工具来拷贝依赖,但注意 MSYS2 环境下其实可以直接在终端里执行:

gdb ./demo.exe

然后 gdb 加载 DLL 时,它读取的是 MSYS2 的环境变量,通常能自动定位到/mingw64/bin下的 DLL。只要保证在正确的终端里启动 gdb,就不会有太大问题。

5.4 与 Visual Studio Build Tools 冲突的处理

MSYS2 环境里编译 Python C 扩展时,最常见的一个报错是:

error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools"

这个错误和信息出现在 pip 安装包时。pip 默认调用的是系统里的 MSVC 编译器来编译扩展模块,和你是否配置好 MSYS2 无关。破解办法有两个:一是在 Windows 上安装微软官方的 Build Tools(会带一大堆东西,但能确保 pip install 不出岔子);二是设置环境变量让 pip 使用 MinGW 编译(不推荐,兼容性差)。

我的建议是遇到这种情况直接装 VS Build Tools。不要把 MinGW 挤进 Python 工具链,没必要。我们折腾 MSYS2 的目的只是纯 C++ 开发,Python 编译这种事情应该让 Python 最常见的配套编译器去处理。

6. 从骨架到顺手:让 MSYS2 融入日常工作流

6.1 预置 GCC 版code命令与 Shell 启动优化

工具链通了以后,为了提高日常使用效率,我做了两个小调整:

第一个是在~/.bashrc里加了几个别名:

alias cmakeconf='cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER=g++' alias cmakebuild='cmake --build build -j$(nproc)'

这样每次配置和编译都只需敲两个单词。nproc在 MSYS2 中是可用的,它会返回 CPU 核心数,-j参数会自动把并行度开到最大,比默认不加-j的编译速度快非常多,尤其是第一次全量编译第三方库时。

第二个调整是给 Windows Terminal 添加 MSYS2 的启动配置。如果你用 Windows Terminal,新增一个 profile,命令行指向:

C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64

这样打开 Windows Terminal 的新标签页就能直接进入 MinGW64 环境,不用专门去开始菜单点那个简陋的黑色窗口。配合 Windows Terminal 的标签管理、分屏和自定义字体,体验其实已经可以和 Linux 终端有得一拼。

6.2 可选配置:设置默认编译器为 clangd 或 LSP 的注意事项

如果你喜欢更现代的代码补全和静态分析,可以不用 VSCode 的微软 C/C++ 扩展,而改用 clangd,但 clangd 需要知道你的 compile_commands.json。CMake 正好可以生成这个文件,只需要在 CMakeLists 里加上:

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

然后在build目录下找到compile_commands.json,用脚本把它软链到项目根目录,VSCode 的 clangd 插件会自动读取。

但注意:clangd 默认按 Clang 的语义解析代码,它的头文件搜索路径和 GCC 并不完全一致。如果你在.clangd配置文件里没有正确指定 GCC 的 include 路径,一些只有 GCC 有的扩展头文件会报找不到。我的经验是尽量保持两个工具链用同一份头文件(MSYS2 的/mingw64/include/c++/),并且在.clangd里加上:

CompileFlags: Add: - "--gcc-toolchain=C:/msys64/mingw64"

这样 clangd 就能找到正确的标准库头文件目录。

7. 写在最后的一点体会

在我把主开发环境切到 MSYS2 之前,一直觉得“Windows 上写 C++ 就是 VS 一条路”。现在回头看,MSYS2 + GCC + CMake 的组合不仅给了我不亚于 Linux 原生的开发体验,还让我对编译器、链接器、CMake 生成器这些概念有了真正的底层认知。你在 Linux 上学的 Makefile、CMake、静态库、动态库知识,在 Windows 上完全无缝复用。

唯一要提醒的是:别轻易乱动C:\msys64里的系统目录,也别图省事把 MSYS2 的/usr/bin直接扫进 Windows 全局 PATH。这套环境最好的使用方法,就是把它当作“Windows 上的一个开发工具容器”,需要的时候打开终端用,不需要的时候就别让它干扰系统全局。坚持这个原则,踩坑率会低很多。如果你也打算从 VS 迁移过来或者纯粹想体验 Linux 式的 C++ 工作流,希望这篇记录能帮你省下我当初浪费的那两周时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询