Cygwin实用指南:在Windows上编译Linux项目与make使用详解
2026/9/10 20:16:59 网站建设 项目流程

第一次在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.exe

Cygwin的安装器是一个很小的exe,叫setup-x86_64.exe(32位系统的老版本是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会把安装目录当作自己的/(根目录),所以安装目录下的binetchome这些子目录,在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,构建工具核心Devel
gcc-coreC语言编译器Devel
gcc-g++C++编译器Devel
binutilsld、as等汇编链接工具Devel
gdb调试器Devel
git版本控制Devel
opensshssh客户端/服务端Net
wget / curl下载工具Net
vim编辑器Editors
dos2unix转换换行符,很实用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。第一次进到这个黑底白字的界面,你会看到类似user@hostname ~的提示符,这说明你已经进入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”,最多的场景大概是这几个:

  1. 你在Windows上装了多个工具链,有Cygwin、有MSYS2/MinGW、还有Git Bash,命令行里敲make,不确定实际调用的是哪一个。
  2. 你用CMake生成构建系统时,CMake默认选择了其他make程序,你想让它显式用Cygwin的make。
  3. 你下载了一个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

这个技巧在递归make(recursive 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; }
CC=gcc 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 路径的“假象”与cygpath

Cygwin里路径长这样:/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文本文件默认换行是CRLF,Linux是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,让提交到仓库的文本统一用LF:

git 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.execmd.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环境”这件事上救过我好几次,熟悉它的脾气之后,其实是个非常好用的老伙计。

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

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

立即咨询