三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Windows平台Makefile构建指南:MSYS2、NMake与WSL三种方案详解

Windows平台Makefile构建指南:MSYS2、NMake与WSL三种方案详解

1. 为什么在Windows上折腾Makefile是个技术活

如果你是从Linux或macOS转战Windows的开发者,第一次在Windows上尝试运行一个开源项目的make命令时,大概率会收获一个冰冷的错误提示:“‘make’不是内部或外部命令,也不是可运行的程序或批处理文件。” 这个瞬间,你可能会深刻体会到什么叫“平台差异”。Makefile,这个在Unix-like系统上如同空气和水一样自然存在的构建工具,在Windows上却需要你额外费一番功夫才能让它运转起来。

这背后的核心原因在于,Makefile及其配套的make工具,本质上是Unix哲学和工具链的产物。它重度依赖shell环境(如bash)、路径分隔符(正斜杠/)、以及一系列标准的Unix命令行工具(如rm,cp,echo等)。而Windows的CMD或PowerShell有着完全不同的生态。因此,在Windows上使用Makefile,本质上是在搭建一个能让Unix风格构建脚本运行的“兼容层”或“替代环境”。

网络上相关的搜索热词非常集中:mingwgccvscodemsvc和mingw区别。这恰恰反映了开发者们最常走的几条路:要么安装一个完整的Unix-like环境(如MSYS2/MinGW),要么寻求原生Windows工具链(如MSVC)的替代方案,再要么就是在强大的编辑器(如VSCode)中寻找集成支持。今天,我就结合自己多年的跨平台开发经验,为你彻底梳理清楚在Windows上玩转Makefile的三种主流方法,并深入分析每种方法的原理、选型理由、具体操作以及那些官方文档里不会写的“坑”。

2. 方法一:拥抱MSYS2与MinGW-w64——最接近Linux的体验

这是我最推荐,也是绝大多数从Linux迁移过来的开发者的首选方案。它的核心思想是:既然Windows原生不支持,那我就在Windows上“模拟”出一个轻量级的Unix-like环境。MSYS2和MinGW-w64就是干这个的黄金组合。

2.1 核心组件拆解:MSYS2、MinGW-w64与GCC

很多人容易混淆这几个概念,我们先来理清它们的关系和各自扮演的角色。

MSYS2:你可以把它理解为一个在Windows上运行的、专门为开发打造的最小化Unix环境。它提供了一个Bash shell、一个包管理器(pacman,源自Arch Linux)、以及一整套Unix核心工具(如coreutils,findutils,grep等)。当你在这个环境里打开终端,输入ls -larm -rf./configure这些命令时,它们都能正常工作。MSYS2的关键在于它提供了一个POSIX兼容的运行时层和API转换层(通过msys-2.0.dll),让为Unix编写的软件以为自己在Unix上运行。

MinGW-w64:它的全称是“Minimalist GNU for Windows 64-bit”。顾名思义,它是一个编译器工具链项目,提供了GCC、GDB、Binutils等工具的Windows原生端口。这里“原生”的意思是,它编译出来的程序是纯粹的Windows PE格式可执行文件(.exe,.dll),不依赖任何额外的运行时库(比如Cygwin的cygwin1.dll),可以直接在任意Windows电脑上运行。MinGW-w64是原MinGW项目的现代化分支,支持32位和64位。

GCC:GNU编译器集合,是MinGW-w64工具链的核心。我们常说的“安装GCC”,在Windows语境下,通常就是指安装包含GCC的MinGW-w64发行版。

那么,它们和Makefile有什么关系?MSYS2的包管理器里,提供了一个名为make的软件包。当你安装它之后,就能在MSYS2的终端里直接使用make命令了。这个make是GNU Make的Windows移植版,它运行在MSYS2提供的兼容层上,能够完美理解Makefile中的Unix语法(如$(RM) -f)。

2.2 详细安装与配置指南

明白了原理,安装就清晰了。我们不推荐去各种第三方网站下载零散的安装包,MSYS2官方提供了最清晰、最易维护的路径。

第一步:安装MSYS2

  1. 访问MSYS2官网(https://www.msys2.org/),下载安装程序。
  2. 运行安装程序,建议安装路径不要有中文和空格,例如C:\msys64
  3. 安装完成后,在开始菜单中找到“MSYS2 UCRT64”并启动。这是目前最推荐使用的环境,它使用较新的UCRT运行时库,与Visual Studio兼容性更好。

注意:MSYS2提供了多个启动快捷方式,如MSYS2 MSYSMSYS2 MINGW64MSYS2 UCRT64。简单来说:

  • MSYS2 MSYS: 最“纯净”的MSYS2环境,主要用于维护MSYS2自身。编译软件时,生成的文件可能依赖MSYS2的DLL。
  • MSYS2 MINGW64/UCRT64: 这些是“MinGW-w64”环境。在这里,工具链会认为目标是纯Windows,编译出的程序是原生的。我们日常开发就使用这个

第二步:安装必要的工具链在打开的UCRT64终端中,首先更新软件包数据库(这步很重要,能避免后续安装失败):

pacman -Syu

如果提示关闭终端,请关闭后重新打开UCRT64终端,再次运行更新命令直至完成。

然后,安装开发基础工具包:

pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain

这个base-devel包含makeautoconfautomake等构建工具。mingw-w64-ucrt-x86_64-toolchain是一个元包,它会安装GCC、G++、GDB、Binutils等一整套编译调试工具。

第三步:配置系统环境变量(关键步骤)为了让Windows的CMD或PowerShell也能直接使用这些工具,我们需要将MinGW-w64的bin目录添加到系统的PATH环境变量中。

  1. 找到你的MSYS2安装目录,例如C:\msys64
  2. 进入ucrt64\bin目录(完整路径如C:\msys64\ucrt64\bin)。这个目录下就有make.exe,gcc.exe,g++.exe,gdb.exe等。
  3. 复制此路径。
  4. 在Windows搜索栏输入“环境变量”,打开“编辑系统环境变量”。
  5. 点击“环境变量”,在“系统变量”中找到Path变量,双击编辑。
  6. 点击“新建”,将刚才复制的ucrt64\bin路径粘贴进去。建议将其上移到靠前的位置,以避免与系统其他工具冲突。
  7. 一路点击“确定”保存。

验证安装:重新打开一个新的CMD或PowerShell窗口(重要,让环境变量生效),输入以下命令:

gcc --version make --version

如果都能正确输出版本信息,说明配置成功。现在,你可以在任意目录的终端中,像在Linux上一样使用make了。

2.3 实操心得与避坑指南

  • 路径问题的“天坑”:这是最大的陷阱。在MSYS2的Bash终端里,路径/c/Users/YourName/project指向的是C:\Users\YourName\project。但是,如果你在Windows的CMD中使用make,Makefile里写的Unix路径(如../src/file.c)仍然能被make理解,因为GNU Make本身处理的是字符串。然而,如果Makefile中调用了shell命令,并且命令中混用了Windows路径和Unix路径,就可能出错。最佳实践是:在Makefile中,对于文件路径,坚持使用Unix风格的正斜杠/。GCC和大多数工具在Windows上都能正确处理它。避免使用反斜杠\,除非你确定只在CMD上下文里运行。

  • 选择哪个终端:我个人的习惯是,复杂的、交互式的构建工作在MSYS2 UCRT64终端中进行,因为那里的环境最纯净、最一致。而简单的、已经验证过的make命令,可以在配置好PATH的VSCode集成终端或Windows Terminal中直接运行,这样更方便。

  • 关于mingw-w64ucrt:搜索热词里有msvc和mingw区别。简单说,MSVC是微软的亲儿子,集成在Visual Studio里;MinGW-w64是GCC的Windows移植。选择UCRT版本是为了更好地与Windows 10及以后系统的C运行时库兼容,减少潜在的运行时库冲突问题。

  • 安装失败或更新问题:如果pacman -Syu失败,通常是网络或镜像源问题。可以尝试修改/etc/pacman.d/mirrorlist.mingw等文件,将服务器地址替换为国内的镜像源(如清华、中科大的源),能极大提升速度和稳定性。

3. 方法二:利用Visual Studio自带的NMake——微软的原生方案

如果你主要进行Windows原生开发,并且已经安装了Visual Studio(尤其是较新的版本),那么你其实已经拥有了一个微软官方的make替代品——NMake

3.1 NMake是什么?它与GNU Make的异同

NMake是Microsoft Program Maintenance Utility(程序维护工具)的简称,可以看作是微软版本的make。它同样可以读取Makefile(通常命名为Makefile或带有.mk扩展名)并根据依赖关系执行构建任务。

核心区别

  1. 语法差异:这是最大的障碍。NMake的语法与GNU Make有诸多不兼容之处。例如:
    • 变量引用:GNU Make用$(VAR)${VAR},NMake用$(VAR)(括号是必须的)或%VAR%(环境变量风格)。
    • 自动化变量:GNU Make的$@(目标)、$<(第一个依赖)在NMake中对应的是$@$**(所有依赖)或$?(更新的依赖),但语义不完全相同。
    • 函数:GNU Make有丰富的内置函数($(wildcard),$(patsubst)),NMake的内置功能较弱,更多依赖批处理脚本。
    • 条件判断:语法完全不同。
  2. 默认规则:GNU Make有一整套内置的隐式规则(例如知道如何用gcc编译.c文件),而NMake的默认规则集是针对微软工具链(如cl.exe,rc.exe)的。
  3. 环境依赖:NMake通常需要在“Visual Studio开发者命令提示符”或“x64 Native Tools Command Prompt”这样的特殊环境中运行,因为该环境已经配置好了cl(MSVC编译器)、link(链接器)等工具的环境变量。

3.2 如何启用并使用NMake

你不需要单独安装NMake,它随Visual Studio一起提供。

第一步:启动正确的命令行环境不要在普通CMD里直接运行nmake。从开始菜单找到并启动与你Visual Studio版本和目标架构对应的命令提示符,例如:

  • “Developer Command Prompt for VS 2022”
  • “x64 Native Tools Command Prompt for VS 2022”

这个快捷方式实际上只是运行了一个叫vcvarsall.bat的批处理脚本,它设置了INCLUDELIBPATH等一大堆环境变量,让cllinknmake等工具能被找到和使用。

第二步:编写或适配Makefile如果你有一个为GNU Make编写的Makefile,想用NMake运行,通常需要修改。一个最简单的、可能兼容的Makefile例子(编译一个C程序):

# 适用于NMake的简单Makefile CC = cl CFLAGS = /nologo /W4 /O2 LINKER = link LFLAGS = /nologo APP = hello.exe OBJS = hello.obj all: $(APP) $(APP): $(OBJS) $(LINKER) $(LFLAGS) /out:$@ $** hello.obj: hello.c $(CC) $(CFLAGS) /c hello.c clean: del $(OBJS) $(APP)

注意:命令前缀是制表符(Tab),这点和GNU Make一样。但命令是Windows命令(del)。

第三步:运行NMake在配置好的命令提示符中,切换到你的项目目录,直接运行:

nmake nmake clean

3.3 适用场景与局限性分析

什么时候该用NMake?

  • 项目本身就是为Windows/MSVC设计的:很多Windows平台的古老代码库或驱动开发项目,其构建脚本就是为NMake写的。
  • 深度依赖Windows SDK/驱动开发包(WDK):这些SDK提供的构建示例通常都是NMake格式的。
  • 团队纯Windows/Visual Studio开发:为了统一工具链,避免引入额外的依赖。

为什么大多数开源项目不推荐用它?

  • 语法锁死:你需要为NMake专门维护一套构建脚本,无法与主流的、为GNU Make编写的开源项目共享。
  • 功能较弱:缺少GNU Make那些强大的函数和自动化功能,编写复杂的、跨平台的构建逻辑非常吃力。
  • 工具链绑定:它天然绑定MSVC,如果你想在同一个Makefile里支持GCC(MinGW)和MSVC,会变得异常复杂。

个人经验:除非你的项目强绑定Windows平台和微软工具链,否则我建议将NMake视为一个“遗产兼容工具”或“特定场景工具”。对于新项目,尤其是希望跨平台的项目,坚持使用GNU Make(通过MSYS2)是更可持续的选择。搜索热词中makefile生成工具cmake的流行,也侧面反映了大家更倾向于使用CMake这种生成器,来为不同平台(包括NMake)生成对应的构建文件,而不是手写多套。

4. 方法三:在WSL中无缝使用——终极的Linux环境

如果你使用的是Windows 10版本2004及以上或Windows 11,那么Windows Subsystem for Linux无疑是体验最完美、最彻底的解决方案。WSL不是一个虚拟机,而是一个在Windows内核上实现的、能够直接运行原生Linux二进制文件的兼容层。

4.1 WSL的原理与优势:为什么它是“终极方案”

WSL(特别是WSL 2)本质上是一个轻量级的虚拟机,运行着一个完整的Linux内核。这意味着:

  1. 100%的Linux兼容性:你安装的是真正的Ubuntu、Debian、Fedora等发行版。系统里的makegccbash就是Linux原生版本,行为与在物理Linux机器上完全一致。
  2. 完美的文件系统互操作:你既可以在Linux环境中直接访问Windows文件(/mnt/c/),也可以在Windows资源管理器中通过\\wsl$网络路径访问Linux文件。这使得跨环境编辑和构建变得非常方便。
  3. 极低的性能开销:与完整虚拟机相比,WSL 2的启动速度和运行时性能损耗极小,几乎感觉不到。
  4. 与Windows工具链无缝集成:你可以在VSCode中直接打开WSL中的项目文件夹,使用Remote - WSL扩展进行开发,享受完整的智能感知、调试等功能,而构建命令则在背后的Linux子系统中执行。

对于Makefile而言,在WSL中运行就是“回家”的感觉,所有Unix的路径、命令、环境变量都正常工作,零适配成本。

4.2 从零开始搭建WSL开发环境

第一步:启用WSL功能以管理员身份打开PowerShell或Windows终端,运行:

wsl --install

这个命令默认会安装WSL 2和Ubuntu发行版。如果你需要其他发行版,可以先运行wsl --install -d <DistributionName>,或者去Microsoft Store搜索安装。

安装完成后,重启电脑。首次启动安装的Linux发行版(如Ubuntu),会要求你创建Unix用户名和密码。

第二步:在WSL中安装开发工具打开你的Linux发行版(可以从开始菜单直接启动“Ubuntu”),这相当于进入了一个Linux终端。 更新软件包列表并安装构建工具链:

sudo apt update sudo apt upgrade sudo apt install build-essential

build-essential是一个元包,它会安装gcc,g++,make,libc-dev等一整套编译所需的基础工具。

第三步:在VSCode中连接WSL(强烈推荐)

  1. 在Windows上安装VSCode。
  2. 在VSCode扩展商店搜索并安装“Remote - WSL”扩展。
  3. 在WSL终端中,进入你的项目目录,然后输入code .。VSCode会自动启动,并在左下角显示“WSL: Ubuntu”的绿色标识。这意味着VSCode的扩展和终端都运行在WSL环境中。
  4. 在这个环境下打开集成终端(Ctrl+),你看到的就是Linux的Bash,可以直接运行make`。

4.3 跨文件系统工作的注意事项与性能优化

虽然WSL提供了完美的兼容性,但跨Windows和Linux文件系统工作仍有一些细节需要注意,这也是搜索热词中windows 识别btrfs这类问题背后的关切——人们希望获得更好的性能。

  • 性能关键:将项目文件放在WSL文件系统内!这是最重要的经验法则。如果你在VSCode中通过\\wsl$打开Windows盘符(如/mnt/c/)下的项目,然后在该目录下执行make,由于所有文件I/O都需要经过一层转换,构建速度会慢一个数量级,尤其是涉及大量小文件时。正确做法:在WSL的家目录(如~/projects/)下克隆或创建项目。然后通过VSCode的Remote-WSL打开这个Linux路径下的项目。这样所有操作都在原生的Linux文件系统(WSL 2默认是ext4)上进行,速度极快。

  • 如何在Windows中方便地访问WSL文件: 除了\\wsl$,你还可以在Windows资源管理器的地址栏直接输入\\wsl.localhost\Ubuntu\home\<yourname>\projects来访问。更方便的是,在VSCode的WSL环境中,右键文件夹,选择“Reveal in File Explorer”,Windows资源管理器会自动在对应位置打开。

  • 处理行尾符(CRLF vs LF): 如果你在Windows上编辑了文件然后放到WSL中编译,可能会遇到行尾符问题。Git可以在提交时自动转换。在VSCode的WSL项目中,右下角可以确认行尾序列是“LF”(Unix)还是“CRLF”(Windows),建议统一为LF。

  • 图形界面应用: WSL 2支持GUI应用(需要额外配置并安装Windows端的X Server,如VcXsrv)。但对于纯开发构建,命令行已经足够。像make menuconfig这类基于ncurses的文本界面也能正常显示。

5. 三种方法对比与选型决策指南

为了让你能一目了然地做出选择,我将这三种方法的核心特性、优缺点和适用场景总结如下表:

特性维度方法一:MSYS2/MinGW-w64方法二:Visual Studio NMake方法三:WSL
核心原理在Windows上模拟Unix环境与工具链使用微软原生的构建工具在Windows上运行完整的Linux子系统
Make工具GNU Make (原生移植版)Microsoft NMakeGNU Make (Linux原生版)
编译器MinGW-w64 GCC/G++Microsoft CL (MSVC)发行版自带GCC/G++ (Linux原生)
环境一致性高(专为跨平台设计)高(纯Windows原生)极高(与Linux发行版100%一致)
与Windows集成好(工具是原生exe,PATH配置后随处可用)完美(深度集成于VS生态)极好(文件系统互通,VSCode深度集成)
性能好(原生exe,无虚拟化开销)好(原生工具)极好(WSL2接近原生,但需注意文件位置)
语法兼容性完美兼容GNU Make语法需适配,与GNU Make语法有显著差异完美兼容GNU Make语法
适用项目跨平台C/C++项目、需要GCC特性的项目、开源项目构建纯Windows原生项目、驱动开发、遗留项目维护任何Linux优先的项目、服务器端开发、想获得纯粹Linux体验
入门复杂度中(需安装配置MSYS2和PATH)低(VS用户开箱即用)中(需启用WSL并安装发行版)
主要缺点路径问题需小心处理语法不通用,生态局限于Windows需要一定的Linux基础知识

如何选择?我的个人建议是:

  1. 首选WSL(方法三):如果你的开发不重度依赖特定的Windows GUI库或SDK,并且你追求最原汁原味的Linux开发体验,或者你的项目本身就是为Linux部署的,那么WSL是目前的最佳选择。它与VSCode的集成堪称完美,解决了“环境差异”这个根本痛点。

  2. 次选MSYS2/MinGW(方法一):如果你需要编译生成原生Windows可执行文件,但又离不开GNU Make和GCC工具链的丰富生态和跨平台便利性,或者你需要与现有的、为GNU Make编写的跨平台构建系统对接,那么MSYS2/MinGW-w64是你的不二之选。它是在Windows上获得“类Unix”开发体验的标杆。

  3. 特定场景用NMake(方法二):如果你的工作完全围绕微软生态展开,例如使用DirectX、Win32 API、.NET Native或进行Windows驱动开发,并且团队统一使用Visual Studio,那么学习和使用NMake是合理的选择。对于新项目,更现代的方案是使用CMake生成VS项目文件(.sln),而非直接手写NMakefile。

最后,无论选择哪种方法,一个重要的趋势是使用CMakeMeson这样的高级构建系统生成器。它们可以为你自动生成对应平台的构建文件(在Windows上可以是MinGW Makefiles、NMake Makefiles或Visual Studio项目)。这样,你只需要维护一份高级别的构建描述(CMakeLists.txt),而无需为makenmakemsbuild分别编写和维护构建脚本,这极大地提升了项目的可维护性和跨平台能力。这也是为什么makefile生成工具cmake会成为高频搜索词的原因——大家正在从手写Makefile转向更现代、更高效的构建管理方式。

← 返回列表