Building Wine - WineHQ Wiki

本文翻译自Building Wine - WineHQ Wiki

对于从源代码编译Wine感兴趣吗?想要提交一些补丁,运行一次二分查找,或者只是安装一个补丁来使你喜爱的游戏正常运行?你来对地方了。

1 准备工作

在编译任何一行 ANSI C 代码之前,为了成功构建 Wine,你需要做一些准备工作。

1.1 获取Wine源代码

你可以从多个地方(包括Wine下载服务器)下载Wine源代码的tarball,但是如果你计划进行任何实际的测试或开发工作,你会想要使用git。有关更多信息和一些有用的提示,请参阅Git Wine教程和Source Code维基页面。

要获取wine源代码,只需输入以下命令,并将最后的目录路径替换为你想要下载源代码树的任何文件夹:

git clone https://gitlab.winehq.org/wine/wine.git ~/wine-dirs/wine-source

如果由于某种原因你的网络阻止了git协议,请尝试使用http URL代替(http://gitlab.winehq.org/wine/wine.git)。

1.2 满足构建依赖

一旦你获取了源代码,就该开始获取所有必要的构建工具和库了。如果你使用的是具有包管理功能的 Unix 系统,可能最快的方法是使用你的包管理器提供的“构建依赖”命令来找到大部分你需要的软件。

不幸的是,单凭这一点可能还不够。你想要的功能可能有一些依赖被标记为可选的,或者甚至在你发行版的软件仓库中都没有打包。Wine 作为一个项目仍在不断发展,可能在不同版本之间切换到不同的库,如果你使用的是稳定版本的发行版,这一点尤其重要。

查看类别:发行版,看看你的发行版是否有自己的页面,其中可能包括更多关于依赖项和编译的提示。

尽管 Wine 有许多外部库的依赖关系,但并不是所有的都是构建 Wine 所必需的,或者确切地说,不是运行你感兴趣的程序所必需的。以下表格详细说明了 Wine 的构建依赖项及其用途。

表格请查看原网页

在使用包管理器之后,确定你仍然需要哪些依赖项的最可靠方法是从 Wine 的源代码目录开始构建过程,并运行配置脚本:

./configure --options

(或者在一个独立的构建目录中,但关于这一点在下一节会详细介绍):

relative-path/to-wine-source/configure --options

这个命令会运行 Wine 的配置脚本,它会检查你系统中缺少的依赖项,并提供相应的错误或警告信息,以便你安装或处理它们。

Glib2 和 gstreamer 在 32 位和 64 位系统上有不同的头文件,因此在 64 位系统上编译支持 gstreamer 的 32 位 Wine 时,需要使用 PKG_CONFIG_PATH=/path/to/pkgconfig 来指定运行 ./configure 时指向 32 位文件的位置。如果没有这样做,./configure 将会找到 64 位的开发文件而不是 32 位的文件,并且会禁用 gstreamer 的支持。(参见 https://www.winehq.org/pipermail/wine-devel/2016-January/111245.html)

如果你缺少 Wine 构建所需的库或程序,脚本应该会输出一条特别告诉你仍然需要安装什么的消息。理想情况下,一次通过 configure 脚本会列出所有缺少的软件,但至少目前为止,脚本在第一个检查失败时就会终止。如果你坚持通过循环(我知道很繁琐)运行 configure、安装缺失的软件、重复此过程,脚本应该会完成并显示一个成功消息。

顺便说一句,一些 Wine 的功能(遗留的、实验性的等)默认情况下是禁用的,但仍然可以通过正确的库构建到 Wine 中。在成功运行后,configure 脚本应该会列出所有禁用的功能以及启用它们的库。要包含这些功能,只需安装相关的库,然后再次运行 configure 脚本即可。

1.3 运行时依赖

如果你实际上想在构建后运行 Wine,你还需要安装运行时依赖。对于大多数系统来说,最简单的方法是安装由你的发行版提供的 Wine 发行版,以及所有的依赖项。当然,如果你使用的是一个非常旧的发行版,这可能需要强制升级许多依赖项才能正常工作。

一旦你安装了所有的依赖项,只要为你定制的 Wine 构建提供一个独立的 Wine 前缀,你就应该可以在官方仓库版本的 Wine 旁边(从构建目录内)运行它,而不会发生冲突。

如果你宁愿卸载你的发行版上的 Wine 版本,那么在卸载后你有几种选择:

2 普通编译

如果你计划在与构建相同的 32 位架构上运行 Wine,那么你很幸运;事情应该会非常简单。

如果你只是想在常见硬件上与 64 位发行版一起构建 Wine,那么很可能你想要一个 WoW64 构建。

在安装了所有构建要求之后:

在命令行上:

cd ~/wine-dirs/wine-build/
../wine-source/configure
make
make install

最后一步完全由你自己决定;你可以完全从构建目录中运行 Wine 而不安装。如果你通过 make 安装了 Wine,请确保你没有已安装的 Wine 版本。重叠的安装不应该破坏你的系统,但它们可能会严重混乱你的库和软件包管理。

2.1 配置选项

如果你在一个 64 位系统上,要构建一个 64 位的 Wine,你只需要在运行上面的命令时,在 configure 脚本中传递 –enable-win64:

../wine-source/configure --enable-win64

问题在于,单独使用这个选项构建的 Wine 只能运行为 64 位 Windows 编译的应用程序。由于绝大多数的 Windows 应用程序都是 32 位的,你很可能想要按照下面的 WoW64 说明进行操作。

如果你想明确地不包含一个可选功能,即使你已经安装了它的依赖项,configure 脚本也有许多选项 (–without-PACKAGE) 来禁用某些库。还有一些选项可以强制包含通常不默认包含的库 (–with-PACKAGE)。第二类选项可以用来禁用或启用 Wine 中的一般功能 (–disable-FEATURE 和 –enable-FEATURE)。

如果你计划进行跨平台编译到完全不同的架构(不仅仅是在 64 位系统上构建 32 位的 Wine),你肯定会想要 –with-wine-tools= 选项。当你键入它时,只需添加相对路径到一个为你的主机系统编译的 Wine 构建的 tools 子目录。除非包含这个选项(或工具的副本被缓存到构建脚本可以看到的地方),否则如果你同时传递了一个交叉编译选项,configure 脚本将会中止并显示错误。

configure 脚本有一个帮助标志 (-h),列出了所有相关的选项和环境变量,以及一个简短的描述。该脚本还可以识别制表符自动补全,所以对于支持的选项的简单列表,只需在 configure 命令后面键入两个连字符,然后按下 tab 几次即可。

如果由于任何原因你需要在构建 Wine 时传递标志给编译器,你可以在配置期间使用 CFLAGS 环境变量来设置它们。

2.2 一般编译提示

你完全可以从源代码目录中构建和安装 Wine,但使用一个单独的构建目录有很多优点。它保持了源代码的整洁,使得测试不同的构建更容易,允许更清晰地进行交叉编译,简化了 chroots 或 Linux 容器等。

在 Unix 上开始构建软件?你可能听说过 make clean。wine 的 makefile 也包含了这个规则,它会自动清除构建目录中的所有中间对象文件。然而,这些相同的对象文件是 make 知道在你更改源代码并重新编译时可以跳过哪些步骤的方式。因此,如果你不想在每次补丁后都重新从头编译整个 Wine,就不要干扰这些对象文件。

也许你有一款那种(极具优势的)固态硬盘,你想知道在你的固态硬盘上编译是否有所帮助。在互联网上搜索会得到不同的意见。如果你确实看到了改善,它们可能会微不足道,主要来自于对源文件的更快读取时间(处理器性能是编译器的主要瓶颈)。同时,虽然不像以前那样成为问题,但固态硬盘在耗尽之前只能进行固定数量的写操作。如果你真的想要,有办法让你的编译器从 SSD 中读取源代码,将临时构建文件写入硬盘上的一个目录(甚至是 RAM),甚至将完成的程序移回 SSD(看看单独构建目录是多么酷?)

如果你会反复从源代码构建,而不是寻找固态硬盘来加速编译时间,请考虑安装 ccache。即使使用 make,它也提供了更复杂的缓存功能,可以在不同的目录之间共享。对于第一次构建时的一点小时间损失,它可以显著缩短编译时间。除非你使用了另一个编译器的包装器(这会使事情变得更加复杂),你只需要将 CC 环境变量设置为从 ccache 调用你的编译器,这也可以在调用 configure 脚本时针对你的 Wine 构建进行特殊设置。例如,如果你使用的是 GCC:

../wine-source/configure CC="ccache gcc" CROSSCC="ccache i686-w64-mingw32-gcc" #your usual configure options

请确保对于 64 位构建,用 x86_64 替换 i686。如果你有一个多核处理器,make 命令有一些你可能喜欢的标志。

make -j5
../wine-source/configure -C ... #你的常规配置选项

3 跨平台编译

在目标 32 位系统上编译很容易,但如果你在一个 64 位系统上,或者你的目标架构与主机系统不同怎么办?嗯…事情会变得有点混乱。总的想法是你需要为目标系统交叉编译 Wine,核心问题是避免多个版本的相同库相互冲突。每个架构的库版本都需要与其他架构分开。幸运的是,有几种技术可以做到这一点。

3.1 多架构和多库

如果你第一次听到跨平台编译的想法,觉得像是,“我为什么不能通过包管理器安装另一个架构的库,然后用这些库构建?”那么你并不孤单。许多 Unix 发行版已经开始调整包的安装方式,以允许精确地做到这一点,但有几种不同的方法。

多架构兼容的发行版可以在同一系统上安装几个版本的库,按照架构进行排序。发行版提供的所有工具和软件也应该能够根据上下文或用户明确提供的参数正确确定使用哪个版本。

还有一种更有限的形式称为“多库”,它仅区分单个架构的ABI(例如,在 amd64 上的 i686 库可能是最突出的)。多库的常见方法是为不同版本设置单独的库目录(例如,在 amd64 系统上的 i686 库是 /usr/lib32),然后在构建时向具有多库功能的编译器(如 GCC 或 Clang)传递参数。

虽然大多数更新的发行版都具有多库功能,但多架构仍在进行中,并且除了 Debian Linux 家族之外的大多数发行版似乎并不认为它是一项优先任务。但如果你的发行版可以处理你的特定情况,那么多库/多架构可能是跨平台编译最快捷、最干净的方式。在“发行版”类别中查找你的发行版以获取更多详细信息。

如果你已经安装了必要的库,wine 的配置脚本应该会为你处理任何多库编译器标志。

对于基于 Debian 的发行版的开发人员,请查看 Multiarch 页面,了解一些仍然不兼容多架构的 wine 依赖项列表。你可以使每个可以多架构兼容的软件包帮助 Wine、Debian 及其后代(可能还有其他发行版)。

3.2 容器

如果主机系统无法自行跟踪不同的库版本,下一个选择是将系统的一部分隔离开来,并将冲突的文件和进程放在那里。在过去的十年左右,基于内核和文件系统的容器已经被证明是一个非常有效的方法。

这种技术(也称为操作系统级虚拟化)通过在文件系统的不同部分之间进行强烈隔离,同时通过共享内核空间的文件和进程来节省资源。虽然它们不能包含完全不同的平台,但你甚至可以在单独的容器中理论上运行不同的发行版,只要它们都与相同的内核兼容(尽管通常需要进行一些调整)。容器的另一个主要优点是它们相对简单易用,比完整的虚拟机要简单得多。

例如,要在基于 Debian 的系统上安装 LXC(Linux 容器)并创建一个 32 位容器:

在命令行中,操作看起来可能是这样的:

sudo apt-get install lxc
sudo lxc-create -t ubuntu -n my32bitbox -- --bindhome $LOGNAME -a i386
sudo lxc-start -n my32bitbox

如果你的容器与主机具有相同的用户空间,你还可以在启动容器之前从主机复制 apt 配置。否则,你可能需要在容器内部手动编辑这些文件:

sudo cp -R /etc/apt /var/lib/lxc/my32bitbox/rootfs/etc

另外,如果启动容器时没有自动出现登录提示符,只需启动第二个终端,然后附加到容器并使用你的普通凭据登录:

sudo lxc-attach -n my32bitbox
login

从那里,你只需要像在主机系统上构建一样,获取开发软件(例如 git)和构建依赖项。一旦设置好了一切,就在容器内部编译 Wine。

也许容器的主要缺点是它们通常需要为每个容器创建一个全新的用户空间镜像。只要你有一台典型的计算机和稳定的宽带互联网访问,获取系统镜像不应该成为问题。容器可能会出现的另一个问题,特别是如果你只是在一台 PC 或笔记本电脑上,是低级进程是如何由主机处理的。如果你需要安装软件包,设置诸如 Wi-Fi 等东西可能需要一些工作。

流行的容器软件:

3.3 chroot

chroot(来自“change root”)是处理这个问题的经典方式。事实上,上一节描述的虚拟容器本质上只是 chroot 的进化形式。chroot 使用一个最小但完整的环境,被隔离在文件系统的子树中。当激活 chroot 时,会启动一个会话,该会话被沙盒化在文件系统的 chroot 部分内。除了挂载 chroot 外的目录或拥有 root 权限的进程之外,该会话既不能看到也不能访问更广泛的主机系统。

chroot 有点低级,并且像大多数低级的东西一样,它可能是一把双刃剑……非常灵活,但有时不太容易使用。这就是一个帮助管理和配置系统上所有 chroot 的程序(比如 schroot)真正方便的地方。除了允许普通用户进行 chroot 访问和简单的配置文件外,schroot 还会自动为您绑定挂载主机环境中的某些目录(默认包括 $HOME……非常方便)。

在 Ubuntu 系统上,你可以使用 schroot 和 debootstrap 来安装一个用于构建 32 位 wine 的 chroot,步骤如下:

  1. 安装 schroot 和 debootstrap 软件包
  2. 创建一个 schroot 配置文件
  3. 创建一个 chroot 文件夹,并在其中安装一个精简版的 Ubuntu
  4. 设置 chroot 的 APT 仓库并进入 chroot
  5. 安装一些基本的包,debootstrap 跳过了这些

在命令行上,步骤 1 看起来像这样:

sudo apt-get install schroot debootstrap
sudo nano (或其他编辑器) /etc/schroot/chroot.d/ubuntu_i386.conf

步骤 2 中的 schroot 配置文件应该如下所示(替换你在主机系统上的用户名、首选 chroot 目录等):

[ubuntu_i386]
description=Ubuntu Release 32-Bit
personality=linux32
directory=/srv/chroot/ubuntu_i386
root-users=your_username
type=directory
users=your_username

步骤 3 - 5 继续在命令行上进行:

sudo mkdir -p /srv/chroot/ubuntu_i386
sudo debootstrap --variant=buildd --arch=i386 vivid (或其他发行版) /srv/chroot/ubuntu_i386 http://archive.ubuntu.com/ubuntu/
sudo cp /etc/apt/sources.list /srv/chroot/ubuntu_i386/etc/apt/
schroot -c ubuntu_i386 -u root
apt-get update
apt-get install ubuntu-minimal
sudo apt-get install software-properties-common

之后,就像在容器中一样,下载必要的库和工具,然后按照在主机系统上构建 wine 的相同指令进行操作。

虽然学习曲线有点陡峭,但 chroot 是一个强大的技术要学习(不仅仅是用于跨平台编译)。它们比虚拟机或容器更轻量,因为只有文件系统的部分被隔离;chroot 会话可以与主机会话共享其他所有内容,包括内核实例。

然而,chroot 还有一些缺点。在许多情况下,需要将一个显著的系统镜像加载到 chroot 目录中,因此你需要获取一个发行版镜像,就像在使用容器时一样。虽然对于跨平台编译受信任的代码来说这不是什么大问题,但 chroot 并不像其他沙盒化方法那样安全。

3.4 虚拟机

只要你有目标平台的镜像,包含所有构建工具和库,你应该能够在你喜爱的虚拟机内编译 Wine。你可以在更高级别上控制许多虚拟机,而不是容器或 chroot,而且这种方法还会留下一个目标环境的实例供你测试,即使它与你的主机系统完全不同。

然而,请记住,虚拟化可能不完美,并引入新的错误。如果在构建过程中没有任何干扰,那么虚拟机的主要缺点将是在编译过程中的性能损失(以及如果在虚拟机内测试 Wine 的话,在执行时也会有性能损失)。你还需要考虑到获取你想要的发行版镜像的不便之处。

即使你可以在虚拟机中构建和运行 Wine,请不要报告你发现的错误,除非你在本机平台上也见到了它们。如果没有来自本机系统的数据,我们就只能默认假设该错误是由虚拟机引入的。不过,在构建和运行 Wine 可以是调试虚拟机软件本身的非常有效的方法。

流行的平台虚拟化软件:

4 共享的 WoW64

当 Windows 开始针对 64 位架构时,微软决定包括一个兼容层来支持他们庞大的 32 位应用程序世界。这种名为 WoW64(Windows on Windows 64 位)的子组件也在 Wine 中实现,以解决完全相同的问题。

没有 32 位支持的 64 位 Wine 将无法运行任何 32 位应用程序,而大多数 Windows 二进制文件都是 32 位的。甚至许多 64 位程序仍包含 32 位组件!

64 位 Wine 已经可以在一些操作系统上运行,但如果你想帮助将 Wine 移植到另一个 AMD64 平台,我们将非常乐意得到你的帮助。

好消息是,一旦你已经准备好编译 32 位和 64 位 Wine 的依赖关系,你已经完成了艰难的部分。如果你通过多库或者(在未来的某个幸福的日子)通过多架构共同安装了所有依赖关系,你只需要遵循两个简单的步骤:

在命令行上执行:

cd ~/wine-dirs/wine64-build/
../wine-source/configure --enable-win64
make
cd ~/wine-dirs/wine32-build/
PKG_CONFIG_PATH=/path/to/pkgconfig ../wine-source/configure --with-wine64=../wine64-build
make

PKG_CONFIG_PATH 应该指向 32 位 pkgconfig 文件的位置,可能是 /usr/lib 或 /usr/lib32。如果没有设置,configure 脚本将会使用 64 位文件,从而禁用 32 位构建中的 gstreamer 支持。

当你制作 32 位版本的 Wine 时,构建过程应该会注入 64 位版本需要处理 32 位程序所需的任何库。之后,只需从 64 位构建中运行 Wine,就可以使用 WoW64 功能。

如果你使用的是 GCC,你至少需要 v4.4 版本来编译 64 位 Wine,因为较早版本缺少 __builtin_ms_va_list 的支持。出于同样的原因,你至少需要 Clang 的 v3.7.1 版本。

如果你需要使用容器或 chroot,你需要注意一些复杂性。最终,它们只是需要你花费更多时间编译。主要区别在于,在编译了 64 位 Wine 之后,你需要两次编译 32 位版本,每次都使用不同的配置:

在命令行上执行:

schroot -c chroot_i386 -u 你的用户名 ... 或者
sudo lxc-start -n my32bitbox
cd ~/wine-dirs/wine32-tools
../wine-source/configure
make
cd ~/wine-dirs/wine32-combo
../wine-source/configure --with-wine64=../wine64-build --with-wine-tools=../wine32-tools
make

理论上,–with-wine64 选项应该能够单独工作,告诉 make 使用来自 64 位构建的预编译工具。然而,其中一个必要的工具是一个名为 makedep 的脚本,当用 64 位 Wine 构建时,它需要访问系统上的 amd64 库。但是为了在 i386 构建的 chroot 中拥有 32 位库,这些库必须在 chroot 外部。第一次构建的 32 位 Wine 允许第二次构建仍然引用 64 位版本,同时覆盖了(失败的)对 64 位工具的使用。

此外,请尽量保持你的 64 位和 32 位构建目录靠近,放在一个你可以轻松从你的 chroot 绑定挂载的目录中。如果你尝试在不包含 64 位构建目录的容器或 chroot 中构建 32 位版本,则 –with-wine64= 选项将无法看到必要的文件。

最后一点需要注意的是,如果你选择安装你的 WoW64 构建,你应该先在 32 位构建树中运行 make install,然后在 64 位构建树中运行。

如果你运行你的双位 WoW64 构建的 32 位部分(未安装),理论上它应该能够像独立的 32 位 Wine 一样工作。但实际上情况并非如此,所以欢迎任何帮助…。

5 打包 Wine

有关在构建后组装包的详细信息,请参阅打包。

如果你正在构建 Wine 以便打包并在存储库中共享,那么构建过程在大部分情况下应该遵循相同的步骤。你的主要考虑因素应该是确保你的构建环境具有良好的库卫生和完整的依赖关系,以确保不会为最终用户引入新的错误。

如果你有兴趣帮助打包 Wine,你所用的发行版的当前打包者可能有很多有用的信息,超过了你在上游可以找到的信息。许多主要的发行版也开发了自己的打包工具,以简化流程并确保代码质量。请查看分类: 发行版,以查看维基是否有更多关于你的发行版的信息。

5.1 硬依赖与软依赖

除了 Wine 运行所需的所有关键头文件之外,还有一些可选的子组件具有软依赖关系。由于许多最终用户可能希望安装这些功能,因此应在编译时安装适当的库,即使你自己不经常使用 Wine 的这些功能。

在最大程度上,你的 Wine 包应配置为在用户系统上“推荐”(默认安装但不要求)这些软依赖包。

5.2 64位软件包

如果你计划为 64 位操作系统打包支持 WoW64 的 Wine(纯 64 位 Wine 构建对于大多数用户来说太受限制,无法正常打包),主要考虑因素是你的 64 位软件包需要包含来自 32 位构建的一些文件。

最直接的方法是按照上述共享 WoW64 构建的说明操作;这应该会将所需的 32 位文件加载到 64 位构建中。如果由于某种原因你无法进行共享构建,你仍应该能够分别编译 32 位和 64 位 Wine,然后从 32 位构建目录复制所需的文件。查看打包页面以获取准确的列表。

5.3 其他软件包

当你考虑打包 Wine 本身时,为什么不考虑一下来自 Wine 项目的其他一些程序呢?它们并不是 Windows API 的主要部分,但它们提供的功能对许多 Windows 程序来说至关重要,因此项目已将它们列为同等重要的优先事项。

其他软件包:

6 调试器

Wine 默认在从源代码构建时包含了自己独特的调试器(winedbg)和调试符号。如果你更愿意使用 wine 内置的调试器之外的其他调试器,你也可以这样做(Wine 开发人员指南中有更多信息)。

6.1 编译器优化和调用堆栈

要使用更强大的工具,你实际构建 wine 的主要变化在于你需要禁用一些编译器优化,主要是消除调用堆栈中的操作。这会在运行时造成性能损失,但为内存检查器提供了更全面的实际发生情况。

如果你使用的是 GCC >= v4.8 或 Clang >= v4.0,则可以通过传递 -Og 标志指示编译器优化调试数据,而不是速度或大小:

../wine-source/configure CFLAGS="-Og" CROSSCFLAGS="-Og"

如果你使用的是较旧的编译器,或者出于其他原因想要选择特定标志,以下是你更有效地进行调试所需的主要标志:

6.2 内存和地址检查器

另一类可以在构建时使用的工具是用于 C/C++ 的内存检查器。其中一些(例如 AddressSanitizer)是作为编译器的特性实现的,而另一些(例如 Valgrind)是单独的套件。大多数这些工具不应需要任何配置,只需要在编译之前安装在你的系统上。

如果系统上安装了 Valgrind,则构建过程应该会自动在 wine 中包含 Valgrind 注释。如果你想双重检查,请在配置构建目录后使用 grep 查找 include/config.h 中的 Valgrind 变量:

grep VALGRIND include/config.h

这应该会返回两行,其中包含 Valgrind 标志:

#define HAVE_VALGRIND_MEMCHECK_H 1
#define HAVE_VALGRIND_VALGRIND_H 1

如果 grep 没有给出这些行,则 configure 脚本因某种原因未能找到 Valgrind。

Valgrind 可以使用这些注释来确定运行在 Wine 上的 Windows 应用程序尝试访问已释放的堆块时。即使 wine 没有使用 Valgrind 符号构建,你仍然可以使用 Valgrind 运行 wine 来查找其他内存错误,例如对未初始化内存的访问。

有关实际使用 Valgrind 和对 wine 源代码的有用补丁的更多信息,请参阅 Wine and Valgrind。

要使用 AddressSanitizer,它自从 Clang v3.1 和 GCC v4.8 起就已经成为了一部分,你只需要将 -fsanitize=address 标志传递给编译器和链接器。要通过 wine 的 configure 脚本执行此操作,使用 CFLAGS 和 LDFLAGS 变量:

../wine-source/configure CFLAGS="-Og -fsanitize=address -other-flags" LDFLAGS="-fsanitize=address -lasan -lpthread"

你可能需要一个补丁来解决 bug 40330。

7 请参阅