Cygwin实用指南:在Windows上编译Linux项目与make使用详解 第一次在Windows上尝试编译一个开源项目时很多人都会遇到同一个尴尬下载下来的是.tar.gz源码包里面装着满满的configure、Makefile、.c文件一看就是为Linux世界准备的可你手里只有一台Windows机器。装虚拟机太重双系统太折腾WSL又不是所有时候都顺手。这时候Cygwin就是一个非常经典、也非常务实的选择。Cygwin不是一个虚拟机也不是一个模拟器它做的事情很纯粹在Windows上提供一整套GNU和开源工具的运行时环境让你能跑bash、grep、sed、awk这些Linux命令行工具也能把大量POSIX程序在Windows上编译出来运行。对我这类需要在Windows上处理Linux风格项目的人来说Cygwin几乎是“补全工具链”的最直接手段。这篇东西我会从Cygwin到底怎么工作讲起给出完整的安装教程包括镜像源、软件包选择、静默安装再重点聊聊很多人搜过的“指定cygwin make”——也就是GNU make在Cygwin环境里的正确使用方式最后做一个和MinGW、MSYS2、WSL的选型对比以及我这些年实际踩过的一些坑。初学的可以照着装老手看避坑部分应该也有点共鸣。1. Cygwin是什么一个“Windows里的Linux翻译官”1.1 不是虚拟机也不是模拟器先说一个最常见的误解。有人觉得Cygwin是个“Windows里的Linux虚拟机”有人说它是模拟器其实都不准确。虚拟机是在Windows上跑一个完整的Linux内核模拟器是把ARM等架构的指令翻译成x86去跑。Cygwin的做法完全不同——它有一个运行在Windows进程里的动态链接库叫cygwin1.dll这个库负责把程序里用到的POSIX系统调用比如fork、socket、open、pthread翻译成Windows API调用。打个比方一个Linux程序原本是在用“中文”和操作系统对话到了Cygwin里cygwin1.dll在现场当翻译程序说的每一句“中文”都被实时翻译成Windows听得懂的“英文”。程序本身还是原生编译出来的机器码只是系统调用这一层被“换了个频道”。所以Cygwin程序跑起来是Windows进程看进程管理器能看到它但它内部的很多行为习惯却沿袭了Linux。1.2 cygwin1.dll具体做了什么cygwin1.dll是Cygwin环境的基石。几乎所有Cygwin程序都动态链接到这个库这也是为什么你拿Cygwin里编译出的exe放到一台没有装Cygwin的Windows机器上会报“缺少cygwin1.dll”的原因。它需要翻译的内容包括路径格式把Windows的C:\Users\xxx映射成/cygdrive/c/Users/xxx系统调用fork、exec、mmap、signal这些Linux风格的调用映射到CreateProcess、VirtualAlloc等Windows API软链接与权限Windows没有完整的POSIX权限模型Cygwin用特殊方式和文件属性来模拟终端行为用cygwin1.dll配合mintty来实现pty、颜色、控制键等终端语义也就是说Cygwin不是简单地“把命令翻译一下”它是一个比较完整的POSIX兼容层。所有基于POSIX API写的程序在Cygwin环境下重新编译一遍通常就能在Windows上运行。这就是为什么很多老牌开源项目提供Windows版本时会直接提供Cygwin编译版本。1.3 性能到底行不行有人担心翻译层会有明显性能损耗。客观说进程创建、信号处理、终端I/O这些场景确实比原生Linux慢一些尤其是fork在Windows上实现代价很高。但对于日常命令行操作、gcc编译、make打包这些场景体感并不明显。我带过不少小中型C项目在Cygwin里跑make、gcc速度完全在可接受范围内。1.4 Cygwin提供的东西分三块Shell环境bash、zsh、mintty终端、ssh、tmux等工具集grep、sed、awk、find、wget、curl、git、openssl等编译工具链gcc、g、make、gdb、binutils、autoconf、automake等这三块加在一起基本覆盖了在Windows上复刻一个Linux命令行工作台的需求。第2节我就按这个思路从零开始讲安装。2. 安装全流程你大概会卡住的几个点2.1 先下载setup-x86_64.exeCygwin的安装器是一个很小的exe叫setup-x86_64.exe32位系统的老版本是setup-x86.exe现在基本不需要管它。去官网下载拿到文件后双击运行。这个安装器有个特点它既可以安装新环境也可以后续用来添加、删除、更新软件包。也就是说它不是一次性工具得留着。运行时会问你三件事安装模式、安装目录、软件包镜像源。安装模式一般选“Install from Internet”——这是最常见的。还有“Download Without Installing”和“Install from Local Directory”分别对应离线下载包和从本地包安装这个对局域网内多台机器部署很有用。2.2 安装目录建议直接放C盘根目录我记得Cygwin历史上比较忌讳安装到带空格的路径里比如C:\Program Files\Cygwin。虽然新版本对空格兼容性好了一些但还是会有一些工具的脚本在解析路径时偷懒一旦路径里出现空格就炸。所以我的习惯是装到C:\cygwin64或者D:\cygwin64这类纯净路径一劳永逸。还有一点安装目录的“系统根目录”概念和Windows不完全一致。Cygwin会把安装目录当作自己的/根目录所以安装目录下的bin、etc、home这些子目录在Cygwin里就是/bin、/etc、/home。如果你把Cygwin装在D:\tools\cygwin64那么在Cygwin里看D:\tools\cygwin64\home\用户名就是你登录后的主目录/home/用户名。理解这个映射关系后面很多路径问题就不难办了。2.3 镜像源是关键别用默认源硬扛这一步非常影响安装体验。默认源是国外服务器下载速度经常让人崩溃尤其是需要装gcc、make、git这一堆依赖包的时候几十上百个包拖下来慢能慢到怀疑人生。我的建议是直接选国内镜像。比较稳的选项包括中科大https://mirrors.ustc.edu.cn/cygwin/阿里云https://mirrors.aliyun.com/cygwin/在安装器里选择“Choose A Download Site”的时候把默认的源勾掉只勾你要用的国内镜像。实测中科大镜像的包完整性很好下载速度也可以。选好之后点Next它会开始拉取软件包列表这步可能要等一会儿。2.4 软件包选择搜名字不要一个一个翻分类接下来是Cygwin安装的重点软件包选择界面。这个界面看起来像Windows资源管理器左边是分类右边是软件包每个包有“Skip/版本号”的下拉选择框。默认状态下所有包都是Skip也就是什么都不装只装一个最小核心环境。但你既然装了Cygwin肯定不是只想看个bash至少要把编译工具链装齐。我的建议是直接用界面上方的搜索框逐个搜索并点选下面这些包软件包作用所属分类makeGNU make构建工具核心Develgcc-coreC语言编译器Develgcc-gC编译器Develbinutilsld、as等汇编链接工具Develgdb调试器Develgit版本控制Developensshssh客户端/服务端Netwget / curl下载工具Netvim编辑器Editorsdos2unix转换换行符很实用Utils选包的时候注意只要点下拉框选一个版本依赖它的子包通常会自动选中。Cygwin的依赖解析做得还可以但偶尔也会出现你选了gcc-core它没把关键的依赖包自动带上的情况。所以装完最好用gcc --version验证一下缺了什么再通过setup.exe补。另外如果你不想手动点setup-x86_64.exe支持命令行静默安装。用管理员身份打开cmd执行setup-x86_64.exe -q -P make,gcc-core,gcc-g,binutils,gdb,git,openssh,vim,wget,curl,dos2unix-q表示静默模式-P后面跟要安装的包名用逗号隔开。它会在当前目录下创建工作目录来缓存下载的包首次安装时会自动安装最小环境加这些软件包。这条命令适合批量部署也适合懒人。2.5 第一次启动进入bash前的心理建设安装完成后桌面上会出现“Cygwin64 Terminal”的快捷方式双击它默认会启动mintty终端并进入bash。第一次进到这个黑底白字的界面你会看到类似userhostname ~的提示符这说明你已经进入Cygwin环境了。注意这里的bash是运行在Windows上的bash不是Linux内核。刚进来时默认当前目录是/home/username这就是你的Cygwin用户主目录对应Windows文件系统里的C:\cygwin64\home\username。我见过不少人第一次进Cygwin后发现找不到C盘D盘其实用cd /cygdrive/c就能进入C盘。/cygdrive是Cygwin挂载Windows盘符的固定路径前缀。比如C:\Users\你的名字\Desktop在Cygwin里写成/cygdrive/c/Users/你的名字/Desktop。3. 让工具链顺手make、gcc和“指定cygwin make”这件事3.1 为什么要在Windows环境里找make这是很多刚从Windows原生编译环境比如Visual Studio转过来的人会困惑的地方。Windows上编译程序大项目可以用Visual Studio的MSBuild小项目可以直接用IDE点按钮但如果你面对的是一个跨平台的开源项目它通常不带.sln文件只带Linux世界通用的Makefile。在Windows上想要执行这个Makefile要么用WSL进真实的Linux环境要么用Cygwin/MSYS2这类POSIX兼容层。Cygwin里的make是GNU make它能读取和Linux上一样的Makefile语法并且默认shell是bash所以在Cygwin里你不需要修改任何Makefile直接make就能构建绝大多数Linux风格的项目。这也是很多人把Cygwin当作“Windows下快速跑Linux老工程”的首选原因。3.2 “指定cygwin make”到底指的是什么网上搜“指定cygwin make”最多的场景大概是这几个你在Windows上装了多个工具链有Cygwin、有MSYS2/MinGW、还有Git Bash命令行里敲make不确定实际调用的是哪一个。你用CMake生成构建系统时CMake默认选择了其他make程序你想让它显式用Cygwin的make。你下载了一个Makefile里面依赖make命令但Windows的PATH里没有Cygwin的bin目录导致执行时提示找不到make。这三种情况本质都是同一个问题——make不在你预期的PATH位置或者构建脚本没有被明确告诉“去Cygwin找make”。解决办法也很直接确认Cygwin的/usr/bin也就是安装目录下的bin在PATH里然后在Cygwin终端里执行which make看看输出的是不是/usr/bin/make。$ which make /usr/bin/make $ make -v GNU Make 4.4.1 Built for x86_64-pc-cygwin看到“Built for x86_64-pc-cygwin”这一行就说明你用的确实是Cygwin的make。如果你在Cygwin终端里执行which make输出的是/mingw64/bin/make或者/c/Program Files/Git/usr/bin/make那说明Cygwin的/usr/bin没有排到PATH前面或者根本不是从Cygwin终端启动的。3.3 显式指定Cygwin make的四种实操方式方式一在CMake里指定make程序用CMake的时候最稳的做法是显式声明生成器和make路径cmake -G Unix Makefiles -DCMAKE_MAKE_PROGRAM/usr/bin/make ..-G Unix Makefiles是告诉CMake生成的是Unix风格的Makefile而不是Visual Studio项目或者NMake Makefile-DCMAKE_MAKE_PROGRAM则直接把make程序指定到/usr/bin/make。两个参数一起用基本不会跑偏。这里多说一句很多人只在Windows上用CMake默认生成器通常是Visual Studio它会生成.sln而不是Makefile。所以如果源码包明确支持CMake并且你想在Cygwin里用make编译就必须手动指定-G Unix Makefiles。方式二在Makefile里覆盖MAKE变量GNU make会读取环境变量MAKE。如果你在某个CI脚本或者构建脚本里想强制所有子make调用都使用Cygwin的make可以这样export MAKE/usr/bin/make make -f build.mk这个技巧在递归makerecursive make项目里特别实用。有些老项目内部的sub-make直接写make如果你希望它统一走Cygwin的make就可以用这个环境变量把它“按住”。方式三用绝对路径调用如果只是临时执行某个Makefile不需要搞复杂配置直接用绝对路径最暴力也最明确/usr/bin/make clean /usr/bin/make -j4这种方式虽然不优雅但排除PATH干扰时非常好使。当你怀疑“当前这个make不是Cygwin的”时敲一次绝对路径就知道了。方式四调整PATH顺序如果你打开Cygwin终端时PATH里前面混入了MSYS2或者Git Bash的目录那make确实可能指向别人的make。比如Git Bash目录下的/usr/bin/make往往和Cygwin不兼容它依赖MSYS2的DLL执行时可能直接报“cygwin1.dll not found”或者反过来。解决办法是在~/.bashrc里把Cygwin的路径放到最前面export PATH/usr/bin:$PATH或者干脆在/usr/bin/make想“单独出道”时把其他工具链的路径从Cygwin的PATH里去掉。这个看个人使用习惯但路径优先级确实是make“指错”的最常见原因。3.4 用一套最小示例验证工具链为了确认“指定cygwin make”成功我建议装完后跑一个最简单的C项目测试。创建一个目录写一个hello.c和一个Makefile#include stdio.h int main(void) { printf(Hello from Cygwin make\n); return 0; }CCgcc CFLAGS-Wall all: hello hello: hello.o $(CC) -o hello hello.o hello.o: hello.c $(CC) $(CFLAGS) -c hello.c clean: rm -f hello hello.o在Cygwin终端里执行make clean make ./hello.exe如果你看到输出Hello from Cygwin make说明make、gcc、编译链接这一整套链路都通了。注意在Cygwin里编译生成的程序默认后缀是.exe但你不写后缀也能运行bash会自动补上。3.5 如果make命令不存在怎么办很多人安装时没选make包进入Cygwin后敲make结果是bash: make: command not found。这时候不要重新装整个Cygwin只要再次运行setup-x86_64.exe在软件包界面搜make选中后一路Next就会自动补装。命令行也可以setup-x86_64.exe -q -P make补装完之后make -v能正常输出版本问题就解决了。4. 和MinGW、MSYS2、WSL排排坐谁更适合你4.1 这四者的本质区别被问得最多的问题就是Cygwin和MinGW有什么区别Cygwin和MSYS2有什么区别现在有了WSL还要用Cygwin吗我直接用一张表说清楚工具原理生成程序的依赖典型场景CygwinPOSIX翻译层cygwin1.dll运行需要cygwin1.dll在Windows上用Linux工具链、编译老开源项目MinGW-w64直接用Windows API编译原生exe无额外DLL生成原生Windows程序、JNI库MSYS2Cygwin改良版带pacman包管理同时提供cygwin环境和mingw64原生编译链现代Windows上编译开源库、用makepkg式包管理WSL完整Linux环境WSL2是轻量虚拟机Linux原生程序需要真实Linux内核兼容性的场景从“贴近Linux”的程度排序WSL最接近真实Linux其次是Cygwin/MSYS2最后是MinGW。但越接近Linux和Windows原生生态的隔离感也越强。4.2 不同需求选谁如果你是要处理纯粹的Linux服务器运维脚本、需要在Windows上跑Docker一样的Linux用户态环境那么WSL最合适因为它本质上就是Linux。如果你是要把一个依赖大量POSIX API的C/C项目比如用到了fork、pty、socket的高级特性从Linux搬到Windows上编译运行Cygwin是最省事的选择。因为MSYS2也可以但它把重点放在提供mingw64交叉编译链上纯POSIX环境的体验反而不如Cygwin历史悠久。如果你最终目标是要生成一个能在其他Windows机器上直接运行、不需要安装额外运行时的exe比如一个GUI工具、一个JNI DLL那应该用MinGW-w64别用Cygwin。因为你用Cygwin编出来的exe拿到别人机器上对方会缺cygwin1.dll体验很差。如果只是想在Windows下有个漂亮的bash、能用pacman装软件、并且能原生编译很多Linux库那MSYS2的体验比Cygwin更现代化。MSYS2的包管理工具是pacman安装软件比Cygwin的setup.exe高效很多。4.3 我自己的一个选型经验说句实话现在新项目我一般默认用WSL或者MSYS2但老项目、旧服务器脚本、某些嵌入式工具链文档里Cygwin仍然是出场率极高的名字。尤其是工业界的老系统很多厂商提供的Windows版工具链就是基于Cygwin打包的。所以Cygwin不是“过时”了而是它解决的那类问题——在Windows上提供一个尽量完整的POSIX环境——至今还有不可替代的场景。如果你只是想快速跑通一个开源项目而且公司电脑不能随便开WSL那Cygwin是个没有额外系统要求、装个exe就能用的方案。这也是它这么多年没被淘汰的原因。5. 日常使用避坑清单路径、编码、工具混用5.1 路径的“假象”与cygpathCygwin里路径长这样/cygdrive/c/Users/xxx。但很多Windows程序只认C:\Users\xxx在脚本里混用两种路径很容易出错。这时候用cygpath做转换。cygpath -w /cygdrive/c/Users/xxx # 输出 C:\Users\xxx cygpath -u C:\Users\xxx # 输出 /cygdrive/c/Users/xxx我在写构建脚本时经常用cygpath -w $(pwd)把当前目录转换成Windows路径传给某个Windows原生的exe。这个工具是Cygwin环境下“跨世界”的胶水建议早点学会。5.2 换行符CRLF和LF是一个大坑Windows文本文件默认换行是CRLFLinux是LF。Cygwin里bash脚本、Makefile、configure脚本如果带着CRLF执行时会莫名其妙报错。最常见的是$\r: command not found。解决办法很简单dos2unix script.sh Makefile configure或者用sed -i s/\r$// file批处理。我更喜欢后者因为不用额外装dos2unix虽然装了更方便。另外如果你用git管理代码建议在Cygwin里设置core.autocrlf input让提交到仓库的文本统一用LFgit config core.autocrlf input这样至少从源头减少CRLF流入Cygwin文件的概率。5.3 路径里有空格老式Makefile的杀手Windows很多路径带空格比如C:\Program Files\某软件。在Cygwin的Makefile里直接写这个路径$(CC)之类的变量很容易被空格截断。我踩过最典型的坑是调用某个Windows SDK里的工具时路径带空格导致make每次都报No such file or directory。后来我的做法是统一用cygpath转换并转义或者干脆把依赖工具放到无空格的路径下。能不放Program Files就不放省心。5.4 PATH混入多个工具链的噩梦Cygwin最怕的不是不会用而是跟其他工具链混在一起的时候PATH里出现多个互相冲突的make、gcc、awk。典型的例子是Git Bash安装后自带一套/usr/bin里面有MSYS2的make和grep如果它出现在Cygwin的PATH前面你在Cygwin里执行make可能实际调用的是Git Bash的版本进而因为DLL冲突直接闪退或者报错。排查手段很简单先执行which -a make看看PATH里到底有几个make分别在哪个目录。如果发现异常就把不需要的目录从PATH里摘掉或者调整优先级。5.5 setup.exe不只是安装工具还是卸载和更新工具很多人装完Cygwin就把setup-x86_64.exe删了等到想加包、升级包、卸包的时候又得重新下载。其实setup.exe本来就是Cygwin的包管理入口。想更新所有包运行setup.exe选镜像源在软件包界面把所有包的版本号都点成“Keep”然后一路Next它就会自动升级可更新的包。想卸载某个包也是在setup.exe里把这个包的下拉选成“Uninstall”。这个操作比手动去bin目录删文件靠谱得多因为Cygwin的包之间依赖关系很复杂手动删容易把环境搞坏。5.6 在Cygwin里调用Windows原生程序注意参数传递Cygwin里可以直接调用Windows的notepad.exe、cmd.exe甚至Visual Studio的cl.exe但参数传递有一些隐藏问题。Windows程序接收的是Windows命令行字符串而Cygwin传给它的是POSIX风格路径经常需要先在bash里转换成Windows格式再传。比如我想用notepad打开当前目录下的某个文件notepad $(cygpath -w /cygdrive/c/work/note.txt)在构建脚本里如果某个make目标要调用Windows下原生编译器务必记住这个原则路径转换交给cygpath环境变量同理。Windows原生程序读不到/usr/bin这种路径它会直接当作不存在的目录。我在实际使用中最后形成了一套自己的“三板斧”所有要在Cygwin里编译的代码先把换行符统一成LF所有要跨工具链调用的命令用绝对路径或者which确认来源所有路径转换一律交给cygpath。这三条做到位Cygwin基本不会再给你使绊子。这些年它在“Windows上临时顶替Linux环境”这件事上救过我好几次熟悉它的脾气之后其实是个非常好用的老伙计。