作者:Liigo,日期:20230812。
因工作需要在Windows服务器上部署运行rsync,可rsync官方并不提供Windows版本。所以我想着能不能自己编译它的源代码。首先想到的是WSL2环境,结果在里面编译出来的是ELF文件,不是EXE,Windows里根本无法运行。后面又想到了MSYS2和Cygwin。
MSYS2是运行在Windows系统上的一个Linux运行环境和编译环境,其编译结果是Windows程序。有些Linux开源项目官网不提供Windows版可执行文件,只能自己下载源码编译,这种情况下MSYS2就有用武之地了(注:前提是项目源码是跨平台的,否则MSYS2也无能为力)。MSYS2有终端有Shell有各种工具应用,MSYS2内置了包管理工具Pacman,通过它可以安装GCC,安装第三方项目开发库。Pacman使得安装开源项目的依赖项变得容易。
MSYS2与WSL的区别:后者gcc编译出的是Linux应用(ELF/SO),而MSYS2的gcc编译出的是Windows应用(PE/EXE/DLL)。
sed -i "s#https\?://mirror.msys2.org/#https://mirrors.tuna.tsinghua.edu.cn/msys2/#g" /etc/pacman.d/mirrorlist*
pacman -Suy
pacman -S mingw-w64-ucrt-x86_64-gcc
pacman -S mingw-w64-ucrt-x86_64-XXX
(其中XXX为库名称,例如openssl)注意安装包都要加上前缀mingw-w64-ucrt-x86_64-
(我被坑过,它为啥不自动加上前缀)。终于明白了,它有多个启动入口( 不对,它推荐使用且默认启动的就是UCRT64环境入口msys2.exe
/ucrt64.exe
/mingw64.exe
/…),不同入口有不同的默认系统前缀。我们应该选择ucrt64.exe
入口,其默认前缀应该是mingw-w64-ucrt-x86_64-
。ucrt64.exe
(参考)。
我用了两天时间尝试在MSYS2内编译rsync,折腾出很多问题,不得不放弃,原因最终归结为:rsync源码本身不跨平台,不支持在Windows系统运行,因而无法直接编译Windows版本。说实话这事不能怨MSYS2,巧妇难为无米之炊嘛,MSYS2是提供了Windows的头文件和库,rsync源码不去调用谁也没招。网上倒是有人提供rsync的Windows版本下载,估计它们大幅修改了rsync源码。(备注:此处理解可能有误。)
pacman -S rsync
直接安装,目标文件在/usr/bin/rsync.exe
,大小535KB,运行时依赖 msys-2.0.dll, msys-crypto-3.dll, msys-lz4-1.dll, msys-iconv-2.dll, msys-xxhash-0.dll, msys-zstd-1.dll 等运行库,exe和dll加起来共计10MB。这样看来MSYS应该也是可以编译rsync源码呀。我前面的理解可能有误。
前面试过MSYS2编译rsync失败了,再尝试一下Cygwin,体验很好。先下载一个安装包只有1.3MB,安装界面可选国内源(163/aliyun/huawei等),软件包里有rsync 3.2.7可选。一开始我只选了rsync,其他没选,但它仍默认下载了一些常用包(约75个),最后弹出一个终端界面,里面就可以直接使用rsync.exe了,真赞。最后看一下安装目录,总计136MB,bin子目录内有 bash.exe, cat.exe, cd, chmod.exe, cp.exe, dir.exe, echo.exe, vi.exe 等Linux常见命令的Windows/EXE版本,另有cygwin1.dll等运行库,当然还有我刚才选择下载的rsync.exe(536KB)。rsync.exe在bin目录下可直接运行,复制到别的目录运行的话,提示缺少如下文件:cygwin1.dll, cyglz4-1.dll, cygiconv-2.dll, cygcrypto-1.1.dll, cygxxhash-0.dll, cygz.dll, cygzstd-1.dll,这些文件都可以在bin目录内找到。rsync.exe和所有依赖的dll加起来一共7.7MB,可以复制到别的Windows电脑上运行。
Cygwin里可以选装GCC 11.4(gcc-core
)和Make 4.4,再选装ssl,lz4,zstd,xxhash开发包,然后就可以编译rsync 3.3pre1源码了,./configure, make, 一切顺利,编译得到的rsync.exe大小为2.1MB,同样依赖cygwin1.dll等一堆运行库,全加起来共计9.2MB,经测试功能正常。Cygwin在此处的表现堪称完美,远超MSYS2。要知道rsync源码本身不支持Windows,MSYS2对编译rsync束手无策,而Cygwin居然可以不动声色地编译出Windows版EXE,堪称神奇。现在反思MSYS2的应用场景,似乎挺尴尬,在源码不支持Windows的情况下它不起作用,而源码支持Windows的情况下却又没必要用MSYS2了。我看MSYS2官方展示的CI场景,在Windows容器里安装MSYS2编译项目运行测试,其实完全可以抛开MSYS直接在Windows容器里做同样的工作嘛。(备注:此处对MSYS2的理解可能有误,有待验证)。
用如下代码做一个实验:
#include
#include // to use pthread
#include // to use sleep
static void* mythreadproc(void *arg) {
printf("thread begin, tid=%d\n", (int)pthread_self());
sleep(*(int*)arg);
printf("thread end, tid=%d\n", (int)pthread_self());
return NULL;
}
int main() {
pthread_t thread1, thread2;
int secs1 = 1, secs2 = 3;
printf("main start, tid=%d\n", (int)pthread_self());
pthread_create(&thread1, NULL, mythreadproc, &secs1);
pthread_create(&thread2, NULL, mythreadproc, &secs2);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("main end, tid=%d\n", (int)pthread_self());
return 0;
}
以上代码明显是针对Linux编写的,根本没考虑要编译成Window的EXE。编译方法: gcc main.c -pthread
。分别在WSL/MSYS2/Cygwin编译运行,三者编译运行都正常。除WSL生成Linux的ELF可执行文件外,其余二者均生成了Windows的EXE。这样看来MSYS2也不要求源码跨平台呀,奇怪了。
曾经有人通俗的说:Msys2是轻量级的Cygwin,是重量级的Mingw64。对,也不对。
从概念上来说。Mingw64是GCC的Windows版,它是编译器;Msys2和Cygwin都是Windows系统下的Linux系统运行环境,它们是类Linux系统。历史上先有的Cygwin,确实也很臃肿,以至于后来诞生了更轻量级的Msys2,可是后来Cygwin自己演进也越来越轻量级,以至于跟Msys2不相上下(个人感受)。
Mingw64编译环境,Msys2和Cygwin是Linux运行环境,从概念上论差距很大呀,为什么经常有人把这三者一起谈论一起比较呢?是这样的,Mingw64是编译环境不假,可编译时也少不了用cd/ls/make这些命令吧,于是它为了提升用户体验,内嵌了一个精简版的Msys2。而Msys2和Cygwin呢,它们是Linux运行环境不假,可Linux环境怎能少了gcc呢,于是它们为了提升用户体验,也允许选装Mingw64。你看,Mingw64里有了Msys2,Msys2里有了Mingw64,可不就越来越像了嘛。
三者之间怎么选择呢?我个人以为首先可以排除单独使用Mingw64,它仅仅是编译环境,不能很方便的第三方开发库,编译源码时总要依赖第三方库的,手动下载和配置多费劲呀。所以我们会更倾向于使用Msys2和Cygin,二者即有Linux运行环境,又有选装的Mingw64,还有库管理器(如Pacman)用于快速安装第三方库,而且还可以不编译源代码就直接下载可执行文件,非常方便,而且也算不算多重量级,三五百兆而已。Msys2和Cygwin又该选谁呢,见仁见智,我的意见参见下文。
在MSYS2里安装rsync:pacman -S rsync
,在MSYS2环境内启动后台daemon: rsync --daemon --config=rsyncd.conf
,配置文件rsyncd.conf内容:
port = 9191
use chroot = false
log file = rsync.log
[test]
path = /d/tmp/rsync-test
read only = false
在MSYS2外面启动执行居然也可以(方便加到计划任务里),runrsync.bat:
cd D:\msys64\home\Administrator
D:\msys64\usr\bin\rsync --daemon --config=rsyncd.conf
pause
服务端到本地同步:./rsync -a rsync://121.196.13.93:9191/test/ local/ --delete
本地到服务端同步:./rsync -a local/ rsync://121.196.13.93:9191/test/ --delete
备注:Git自带的MSYS里用不了rsync --daemon
,莫名其妙报 chdir failed(难道是因为它没用home没有user?),折腾一天浪费了很多时间。后来切换到官方的MSYS就很顺利搞定。
备注:要是按前面的使用感受,综合下来应该是Cygwin优于Msys2,那为什么我最后还是选用了Msys2呢?我想其中关键的一点是Msys访问磁盘路径更贴心:/c
,而Cygwin就糟心一些:/cygdrive/c
——没想到在一个小细节上决定了胜负。Msys2还有一个优势,是可以编译出不依赖任何msys-***.dll
的纯正的EXE,而Cygwin编译出来的EXE是没办法不依赖cyg***.dll
的。当然Cygwin也有自己的重大优势,兼容性更强,可以直接编译在Msys2不能直接编译的源代码(例如上文提及的rsync)。