wine + mingw 在macOS上生成exe可执行文件

背景

由于数据结构实验课需要将C++代码编译成exe文件提交,自己的Win本又过于笨重不想带到实验室里面去用,之前都是把cpp文件发给室友帮忙编译的,但是由于强迫症,我还是想在自己的电脑上编译((

前前后后用过很多种方法,试过用xmake这个构建工具来进行平台交叉编译,但是失败了;尝试过通过wine来运行Dev C来傻瓜式编译生成exe,虽然效果不错,但是总感觉不够优雅,而且Dev C这个软件也一百年都没更新过了,好多C++新特性都无法使用。于是我就萌生了一个想法:让Windows平台上的编译工具链通过wine运行在macOS上不就行了吗。于是就有了本文,由于捣鼓了好多天才弄出来,所以写一篇博客记录一下过程

PS:只适用于平时练习一下cpp题,对于有众多第三方库的项目未尝试过。

操作环境说明

由于Apple的M系列芯片采用了ARM架构,而wine又是为x86_64架构设计的,所以需要Rosetta转译运行

本文的运行环境是:MacBook Pro M3 Pro

如果你的运行环境差异很大,那么你就要根据自己实际的环境来进行配置,下面的内容仅供参考

安装wine

在macOS下使用homebrew可以很方便地安装wine

1
2
brew install --cask wine-stable
brew install winetricks

第一个命令来自homebrew官方

除了安装wine之外,我们还需要安装一个名为winetricks的包,这个包可以让我们自定义Windows层的一些功能,例如添加新的dll文件、更改系统字体、更改渲染方式等等。

在安装过程中homebrew可能会提示:

1
2
3
4
wine-stable is built for Intel macOS and so requires Rosetta 2 to be installed.
You can install Rosetta 2 with:
softwareupdate --install-rosetta --agree-to-license
Note that it is very difficult to remove Rosetta 2 once it is installed.

现在的日期时2024/10/23,在此时间及之后购买的MacBook Pro M3 芯片系列应该都已经预装了Rosetta 2,如果你不确定是否已经安装了Rosetta 2,你可以打开另一个终端,运行:

1
arch -x86_64 zsh

如果没有报错则说明已经安装了Rosetta 2

上面的命令实际上是打开了一个x86_64架构下的zsh终端

在homebrew的上述提示后会卡住一会,这个时候不要以为它执行失败了,实际上后台还在下载东西,静静等待即可

如果等了很久都没反应你可以先设置终端代理

安装完成后,运行以下命令检查是否安装成功(你可能重启终端):

1
wine --version

如果输出了版本号则说明安装成功

然后运行:

1
winecfg

这会打开一个图形化配置界面,实际上我们什么都不需要配置,直接点击OK(或者确定),即可完成wine的初始化

还有一些额外的针对字体的配置:

1
2
3
winetricks wenquanyi
winetricks fakechinese
winetricks fontsmooth=rgb

前两行安装中文字体,防止显示中文时乱码,最后一行设置字体平滑,能让你的wine使用体验更加舒适

这时候你可以在网上去找几个小型的exe看看能不能运行,比如扫雷之类的,运行命令如下:

1
wine your.exe

如果能成功运行,那么你可以进行下一步啦!

安装MinGW

建议直接去 MinGW的Github官网 进行下载,我下载的是:

x86_64-14.2.0-release-win32-seh-msvcrt-rt_v12-rev0.7z

大家根据自己的需要进行下载

下载完毕后找一个位置解压它,随便什么位置都可以 (只要你能找到

配置运行命令

想要通过wine正确运行MinGW的GCC/G++编译器,需要一大堆环境变量,我建议将这一整个命令以别名的形式写在你的Shell配置文件中,macOS Sonoma 默认使用Zsh终端,所以我在 ~/.zshrc 中加入了以下代码:

1
2
3
# 编译 exe 文件
alias mingw-exe="export COLLECT_LTO_WRAPPER=/Users/graftcopolymer/mingw64/libexec/gcc/x86_64-w64-mingw32/14.2.0/lto-wrapper.exe;export COLLECT_GCC=/Users/graftcopolymer/mingw64/bin/g++.exe;export C_INCLUDE_PATH=/Users/graftcopolymer/mingw64/lib/gcc/x86_64-w64-mingw32/14.2.0/include/;export CPLUS_INCLUDE_PATH=/Users/graftcopolymer/mingw64/lib/gcc/x86_64-w64-mingw32/14.2.0/include/c++/;wine /Users/graftcopolymer/mingw64/bin/g++.exe -B /Users/graftcopolymer/mingw64/bin/ -static"

可以看到确实很长(((

我们来解释一下里面的环境变量都应该怎么填

  • COLLECT_LTO_WRAPPER: 这个变量要填写你的MinGW安装目录中的lto-wrapper.exe的路径
  • COLLECT_GCC: 填写MinGW安装目录中的bin文件夹中的g++.exe的路径
  • C_INCLUDE_PATH: 这个变量指定C程序编译时的include目录,注意这个地方一定要指定,否则将会采用Xcode中的include目录,这样你在编译时就会看到一堆找不到对象的错误!该目录的位置仍然在MinGW的安装位置中,请自行寻找
  • CPLUS_INCLUDE_PATH: 指定C++程序编译时的include目录,和C_INCLUDE_PATH同理

配置了这一堆环境变量后,就是命令的主角 wine 了:

1
wine /Users/graftcopolymer/mingw64/bin/g++.exe -B /Users/graftcopolymer/mingw64/bin/ -static

首先就是编译器的可执行文件,我们使用wine来执行编译器进行编译

然后我们需要指定 -B 参数,这个参数指定了编译器的进行编译时所用的汇编器 as.exe 和链接器 ld.exe 的目录,这两个exe一般是放在同一个文件夹的,所以我们只需要指定一个目录就行了,如果你不指定就会报链接器错误,因为会使用Xcode提供的链接器(( 可恶的Xcode

注意在指定目录时一定要加上一个尾随斜杠,告诉g++.exe这是一个目录

然后就是 -static 参数,这个参数让编译器使用静态链接,即将所需要的所有依赖库都打包在同一个exe文件中,否则你就必须要将一大堆dll文件复制到你编译出来的exe文件的同目录下才能运行,但是这样也有缺点,就是编译出来的exe文件很大,大概会多出12MB左右,如果引用的库比较多会更大,这个由大家自己取舍

测试

1
2
3
mkdir -p ~/cpp_code
cd ~/cpp_code
vim hello.cpp

用上述命令创建一个cpp文件,编辑后保存退出vim

然后使用:

1
mingw-exe -o hello.exe hello.cpp

这一步如果提示mingw-exe不是命令或者不存在,重启一次终端应该就没问题了

即可编译出一个exe文件,运行exe文件:

1
wine hello.exe

如果成功输出就成功了!

把这个exe拿到Windows上去也是可以正常运行的