学习从源代码编译东西(在 Unix/Linux/OSX 上)

Sim*_*son 49 linux unix installation mac-osx

当我尽可能从包 (MacPorts / apt-get) 安装软件时,我经常发现自己需要从源代码编译包。./configure && make && sudo make install通常就足够了,但有时它不起作用 - 当它不起作用时,我经常被卡住。这几乎总是以某种方式与其他库依赖项相关。

我想学习以下内容:

  • 我如何弄清楚要传递给什么参数./configure
  • 共享库如何在 OS X / Linux 下工作 - 它们在文件系统上的位置,如何./configure && make找到它们,当它们被链接时实际发生了什么
  • 共享库和静态链接库之间的实际区别是什么?为什么我不能只是静态链接所有内容(现在 RAM 和磁盘空间都很便宜),从而避免奇怪的库版本冲突?
  • 我如何知道我安装了哪些库以及哪些版本?
  • 如何在不破坏正常系统的情况下安装多个版本的库?
  • 如果我使用软件包管理的系统上从源代码安装东西,那么最干净的方法是什么?
  • 假设我设法从源代码中精心编译了一些东西,那么我如何将其打包,以便其他人不必跳过相同的圈套?特别是在 OS X 上......
  • 我需要掌握哪些命令行工具才能擅长这些东西?诸如 otool、pkg-config 之类的东西。

我愿意在这里投入相当多的时间和精力——我不一定想要对上述问题的直接答案,我更愿意得到关于我可以阅读的书籍/教程/常见问题解答的建议,这会给我我需要了解实际发生的事情,从而自己解决问题。

小智 42

我很抱歉直接回答所有问题,但我不知道任何有用的教程、常见问题解答等。基本上接下来是 8 年的桌面应用程序制作(我帮助分发)、沮丧和谷歌搜索:

1. 如何确定要传递给 ./configure 的参数?

实实在在地练习。Autotools 很容易,因为它是一致的。但是有很多东西可以使用 cmake 或自定义构建脚本。通常,您不必传递任何内容进行配置,它应该确定您的系统是否可以构建 foo-tool。

配置和 GNU 工具都在 /、/usr 和 /usr/local 中查找依赖项。如果您在其他任何地方安装任何东西(如果依赖项是由 MacPorts 或 Fink 安装的,这会让事情变得痛苦),您将必须传递一个标志来配置或修改 shell 的环境,以帮助 GNU 工具找到这些依赖项。

2. 共享库如何在 OS X/Linux 下工作——它们在文件系统上的位置,./configure && make 如何找到它们,当它们被链接时实际发生了什么

在 Linux 上,它们需要安装到动态链接器可以找到的路径,这是由LD_LIBRARY_PATH环境变量和 /etc/ld.conf 的内容定义的。在 Mac 上,大多数开源软件几乎总是相同的(除非它是 Xcode 项目)。除了 env 变量是DYLD_LIBRARY_PATH

链接器搜索库有一个默认路径。它是 /lib:/usr/lib:/usr/local/lib

您可以通过使用 CPATH 变量或 CFLAGS 或任何数量的其他环境变量来对此进行补充(非常复杂)。我建议 CFLAGS 像这样:

出口 CFLAGS="$CFLAGS -L/new/path"

-L 参数添加到链接路径。

现代的东西使用 pkg-config 工具。您安装的现代东西还会安装一个 .pc 文件,该文件描述了库及其位置以及如何链接到它。这可以使生活更轻松。但它没有随 OS X 10.5 一起提供,因此您也必须安装它。也有很多基本的 deps 不支持它。

链接的行为只是“在运行时解析这个函数”,实际上它是一个大字符串表。

3. 共享库和静态链接库之间的实际区别是什么?为什么我不能只是静态链接所有内容(现在 RAM 和磁盘空间都很便宜),从而避免奇怪的库版本冲突?

当您链接到静态库文件时,代码将成为您应用程序的一部分。就好像该库有一个巨大的 .c 文件,并且您将其编译到您的应用程序中。

动态库具有相同的代码,但是当应用程序运行时,代码会在运行时加载到应用程序中(简化解释)。

您可以静态链接到所有内容,但是遗憾的是,几乎没有任何构建系统可以轻松实现这一点。您必须手动编辑构建系统文件(例如 Makefile.am 或 CMakeLists.txt)。但是,如果您经常安装需要不同版本库的东西,并且发现并行安装依赖项很困难,那么这可能值得学习。

诀窍是将链接行从 -lfoo 更改为 -l/path/to/static/foo.a

您可能可以找到并替换。然后使用 ldd foo 或 otool -L foo 检查工具没有链接到 .so 或 dylib

另一个问题是并非所有库都编译为静态库。许多人这样做。但后来 MacPorts 或 Debian 可能决定不发布它。

4. 我如何知道我安装了哪些库,以及哪些版本?

如果您有这些库的 pkg-config 文件,则很容易:

pkg-config --list-all

否则你往往不能轻易。dylib 可能具有与库版本相同的 soname(即 foo.0.1.dylib,soname 为 0.1)。然而,这不是必需的。soname 是一个二进制可计算特性,如果你改变库中函数的格式,你必须撞到 soname 的主要部分。所以你可以得到例如。2.0 库的 14.0.5 版 soname。虽然这并不常见。

我对这种事情感到沮丧,并在 Mac 上为此开发了一个解决方案,接下来我将讨论它。

5. 如何在不破坏正常系统的情况下安装多个版本的库?

我的解决方案在这里:http : //github.com/mxcl/homebrew/

我喜欢从源代码安装,并且想要一个可以简化安装的工具,但具有一些包管理功能。因此,我使用 Homebrew 构建,例如。wget自己从源代码,但一定要安装到一个特殊的前缀:

/usr/local/Cellar/wget/1.1.4

然后我使用自制工具将所有这些符号链接到 /usr/local,所以我仍然有 /usr/local/bin/wget 和 /usr/local/lib/libwget.dylib

稍后,如果我需要不同版本的 wget,我可以并行安装它,只需更改链接到 /usr/local 树的版本即可。

6. 如果我在使用包管理的系统上从源代码安装东西,那么最干净的方法是什么?

我相信 Homebrew 方式是最干净的,所以使用它或做同样的事情。安装到 /usr/local/pkgs/name/version 并符号链接或硬链接其余部分。

请使用 /usr/local。存在的每个构建工具都会在那里搜索依赖项和标头。你的生活会轻松很多

7. 假设我设法从源代码中精心编译了一些东西,然后我如何将其打包,以便其他人不必跳过相同的圈套?特别是在 OS X 上......

如果它没有依赖项,您可以将构建目录压缩并提供给其他可以执行“make install”的人。但是,您只能为完全相同版本的 OS X 可靠地执行此操作。在 Linux 上,它可能适用于具有相同内核版本和 libc 次要版本的类似 Linux(例如 Ubuntu)。

在 Unix 上分发二进制文件不容易的原因是二进制兼容性。GNU 人和其他所有人经常更改他们的二进制接口。

基本上不分发二进制文件。事情可能会以非常奇怪的方式破裂。

在 Mac 上,最好的选择是制作一个 macports 包。每个人都使用macports。在 Linux 上有这么多不同的构建系统和组合,我认为没有比写一篇关于如何在 y 奇怪的配置中成功构建 x 工具的博客条目更好的建议了。

如果你做一个包描述(对于 macports 或 homebrew),那么任何人都可以安装该包,它也解决了依赖问题。然而,这通常并不容易,而且将您的 macports 配方包含在主 macports 树中也不容易。此外,macports 不支持特殊安装类型,它们为所有软件包提供一种选择。

我对 Homebrew 的未来目标之一是让点击网站上的链接成为可能(例如 homebrew://blah,它将下载该 Ruby 脚本,安装该软件包的 deps,然后构建应用程序。但是,是的,尚未完成,但考虑到我选择的设计并不太棘手。

8. 我需要掌握哪些命令行工具才能擅长这些东西?诸如 otool、pkg-config 之类的东西。

otool 真的只有在之后才有用。它告诉你构建的二进制链接到什么。当您弄清楚必须构建的工具的依赖关系时,它是无用的。pkg-config 也是如此,因为您在使用它之前已经安装了依赖项。

我的工具链是,阅读自述文件和安装文件,然后执行配置 --help。观察构建输出以检查它是否正常。解析任何构建错误。也许在将来,询问 serverfault :)

  • 有趣的是,Homebrew 看起来很像 Portage(来自 Gentoo Linux 的包管理器)。听起来是件不错的作品。 (2认同)

小智 12

这是一个很大的话题,所以让我们从 Linux 上的共享库开始(Linux 上的 ELF 和 OS X 上的 Mach-O),Ulrich Drepper对编写 DSO(动态共享对象)有很好的介绍,其中涵盖了 Linux 上可用共享库的一些历史这里包括为什么它们很重要

Ulrich 还描述了为什么静态链接被认为是有害的,这里的关键点之一是安全更新。广泛静态链接的公共库(例如 zlib)中的缓冲区溢出会导致分发的巨大开销 - 这发生在 zlib 1.1.3(红帽咨询

精灵

链接器 ld.so 手册页

man ld.so 
Run Code Online (Sandbox Code Playgroud)

解释了运行时动态链接中涉及的基本路径和文件。在现代 Linux 系统上,您会看到通过 /etc/ld.so.conf.d/ 添加的其他路径通常通过 /etc/ld.so.conf 中的 glob include 添加。

如果您想通过 ld.so 配置查看动态可用的内容,您可以运行

ldconfig -v -N -X
Run Code Online (Sandbox Code Playgroud)

阅读 DSO howto 应该会给你一个很好的基础知识水平,以便继续了解这些原则如何应用于 OS X 上的 Mach-O。

马赫-O

在 OS X 上,二进制格式是 Mach-O。链接器的本地系统文档是

man dyld
Run Code Online (Sandbox Code Playgroud)

马赫格式的文档可从Apple

UNIX 构建工具

通用configure, make,make install过程通常由 GNU autotools 提供,它有一本在线书籍,涵盖了配置/构建拆分和 GNU 工具链的一些历史。Autoconf使用测试来确定目标构建系统上的功能可用性,它使用M4 宏语言来驱动它。Automake基本上是 Makefile 的一种模板方法,该模板通常称为 Makefile.am,它输出一个 Makefile.in,autoconf(配置脚本)的输出转换为 Makefile。

GNU你好程序作为一个很好的例子为理解GNU工具链-和手动包括自动工具的文档。


小智 10

西蒙!我知道你的感受; 我在学习 Linux 的这部分也很挣扎。根据我自己的经验,我写了一个关于你提到的一些项目的教程(主要是作为我自己的参考!):http : //easyaspy.blogspot.com/2008/12/buildinginstalling-application-from.html。我想你会喜欢我关于构建/安装 Python 应用程序是多么简单的说明。:)

希望这会有所帮助!并且编译愉快。

蒂姆·琼斯


在 Ubuntu Linux 中从源代码构建/安装应用程序

虽然 Ubuntu 存储库充满了很棒的应用程序,但有时您一定会遇到存储库中没有的“必备”工具(或没有 Debian 软件包),或者您需要一个比存储库中的版本新。你做什么工作?好吧,您必须从源代码构建应用程序!别担心,它真的没有听起来那么复杂。以下是一些提示,基于我从业余爱好者的经历!(虽然我在这个例子中使用 Ubuntu,但一般概念应该适用于大多数 Unix/Linux 发行版,例如 Fedora,甚至 Windows 上的 Cygwin 平台。)

从源代码构建(编译)大多数应用程序的基本过程遵循以下顺序:配置 --> 编译 --> 安装。执行这些操作的典型 Unix/Linux 命令是:config--> make--> make install。在某些情况下,您甚至会发现网页显示所有这些都可以组合成一个命令:

$ config && make && make install
Run Code Online (Sandbox Code Playgroud)

当然,此命令假定这些步骤中的任何一步都没有问题。这就是乐趣所在!

入门

如果您之前没有在您的系统上从源代码编译应用程序,您可能需要使用一些通用开发工具对其进行设置,例如gcc编译器套件、一些常见的头文件(将其视为已经编写好的代码)由您正在安装的程序使用的其他人)和 make 工具。幸运的是,在 Ubuntu 中,有一个名为的元包build-essential可以安装它。要安装它(或确保您已经拥有它!),请在终端中运行以下命令:

$ sudo apt-get install build-essential
Run Code Online (Sandbox Code Playgroud)

现在您已完成基本设置,下载应用程序源文件并将它们保存到您具有读/写权限的目录中,例如您的“主”目录。通常,这些将位于文件扩展名为.tar.gz或的存档文件中.tar.bz2。该.tar只是意味着它是一个“磁带归档”,这是一个组,保留它们的相对目录结构的文件。的.gz代表的gzip(GNU拉链),这是一个受欢迎的Unix / Linux的压缩格式。类似地,.bz2bzip2 是一种较新的压缩格式,提供比 gzip 更高的压缩率(更小的压缩文件大小)。

下载源文件后,打开终端窗口(Ubuntu 菜单中的系统终端)并切换到保存文件的目录。(我将~/download在本例中使用。这里,“~”是您“主”目录的快捷方式。)使用 tar 命令从下载的存档文件中提取文件:

如果您的文件是 gzip 存档(例如,以 结尾.tar.gz),请使用以下命令:

            $ tar -zxvf filename.tar.gz
Run Code Online (Sandbox Code Playgroud)

如果您的文件是 bzip2 存档(例如,以 结尾.tar.bz2),请使用以下命令:

            $ tar -jxvf filename.tar.gz
Run Code Online (Sandbox Code Playgroud)

提示:如果您不想记住用于提取档案的所有命令行开关,我建议您使用以下实用程序中的一个(或两个):dtrx(我最喜欢的!)或 deco(更受欢迎)。使用这些实用程序中的任何一个,您只需输入实用程序的名称(dtrx 或 deco)和文件名,它会完成其余所有工作。这两个“知道”如何处理您可能遇到的大多数存档格式,并且它们具有很好的错误处理能力。

从源代码构建时,您可能会遇到两种常见的错误类型:

  1. 当您运行配置脚本(通常命名为 config 或 configure)以创建特定于您的设置的 makefile 时,会发生配置错误。
  2. 运行 make 命令时(在生成 makefile 之后)会发生编译器错误,并且编译器无法找到它需要的某些代码。

我们将逐一查看并讨论如何解决它们。

配置和配置错误

提取源代码存档文件后,在终端中,您应该切换到包含提取文件的目录。通常,此目录名称将与文件名称相同(不带.tar.gz.tar.bz2扩展名)。但是,有时目录名只是应用程序的名称,没有任何版本信息。

在源目录中查找README文件和/或INSTALL文件(或具有相似名称的内容)。这些文件通常包含有关如何构建/编译应用程序和安装它的有用信息,包括有关依赖项的信息。“依赖项”只是成功编译所需的其他组件或库的一个花哨名称。

在您阅读README和/或INSTALL文件(并希望查看该应用程序的任何相关在线文档)后,查找名为configor的可执行文件(在文件上设置了“x”权限)文件configure。有时文件可能有一个扩展名,例如.sh(例如,config.sh)。这通常是一个 shell 脚本,它运行一些其他实用程序以确认您有一个“健全的”编译环境。换句话说,它将检查以确保您安装了所需的一切。

提示:如果这是一个基于 Python 的应用程序,而不是一个配置文件,你应该找到一个名为setup.py. Python 应用程序通常非常易于安装。要安装此应用程序,请以 root 身份(例如,在 Ubuntu 下将 sudo 放在以下命令的前面),运行以下命令:

    $ python setup.py install
Run Code Online (Sandbox Code Playgroud)

这应该是您需要做的全部。您可以跳过本教程的其余部分,直接继续使用和享受您的应用程序。

在终端中运行配置脚本。通常,您可以(并且应该!)使用常规用户帐户运行配置脚本。

$ ./config
Run Code Online (Sandbox Code Playgroud)

该脚本将显示一些消息,让您了解它在做什么。通常,脚本会提示您它是成功还是失败,如果失败,还会提供有关失败原因的一些信息。如果您没有收到任何错误消息,那么您通常可以假设一切正常。

如果您没有找到任何看起来像配置脚本的脚本,那么这通常意味着该应用程序是一个非常简单的应用程序并且它与平台无关。这意味着您可以简单地跳到下面的构建/编译步骤,因为所提供的Makefile应该适用于任何系统。

一个例子

在本教程中,我将使用名为 Newsbeuter 的基于文本的 RSS 阅读器作为构建应用程序时可能遇到的错误类型的示例。对于 Newsbeuter,配置脚本的名称是config.sh. 在我的系统上,当我运行时config.sh,出现以下错误:

tester@sitlabcpu22:~/download/newsbeuter-1.3$ ./config.sh
Checking for package sqlite3... not found

You need package sqlite3 in order to compile this program.
Please make sure it is installed.
Run Code Online (Sandbox Code Playgroud)

在做了一些研究后,我发现,事实上,sqlite3应用程序已安装。但是,由于我正在尝试从源代码构建,这是一个提示,config.sh即实际寻找的是sqlite3. 在 Ubuntu 中,大多数包都有一个相关的开发对应包,以-dev. (其他平台,例如 Fedora,通常使用包后缀 of-devel作为开发包。)

要为sqlite3开发包找到合适的包,我们可以使用apt-cacheUbuntu 中的实用程序(类似地,yumFedora 中的实用程序):

tester@sitlabcpu22:~/download/newsbeuter-1.3$ sudo apt-cache search sqlite
Run Code Online (Sandbox Code Playgroud)

这个命令返回相当大的结果列表,所以我们必须做一些侦探工作来确定哪个是合适的包。在这种情况下,合适的包是libsqlite3-dev. 请注意,有时我们要查找的包会带有lib前缀,而不仅仅是相同的包名加上-dev。这是因为有时我们只是在寻找可能被许多不同应用程序使用的共享库。要安装libsqlite3-dev,请在终端中运行典型的 apt-get install 命令:

tester@sitlabcpu22:~/download/newsbeuter-1.3$ sudo apt-get install libsqlite3-dev
Run Code Online (Sandbox Code Playgroud)

现在,我们必须config.sh再次运行以确保我们已经解决了这个依赖问题并且我们没有更多的依赖问题。(虽然我不会在这里展示它,但在 Newsbeuter 的情况下,我也必须安装该libcurl4-openssl-dev包。)此外,如果您安装了一个开发包(如libsqlite3-dev),而关联的应用程序包(如sqlite3)不是已经安装,大部分系统会同时自动安装相关的应用包。

当配置成功运行时,结果将是它会创建一个或多个 make 文件。这些文件通常被命名Makefile(请记住,文件名大小写在 Unix/Linux 中很重要!)。如果构建包包含子目录,例如src等,则这些子目录中的每一个都将包含一个Makefile,以及。

构建和编译错误

现在,我们已准备好实际编译应用程序。这通常被称为构建,这个名字是从现实世界中构建某物的过程中借来的。应用程序的各个“部分”(通常是多个源代码文件)组合在一起以形成整个应用程序。make 实用程序管理构建过程并调用其他应用程序(例如编译器和链接器)来实际完成工作。在大多数情况下,您只需从运行配置的目录运行 make(使用您的常规用户帐户)。(在少数情况下,例如编译用 Qt 库编写的应用程序,您将需要运行另一个“包装器”应用程序,如 qmake。同样,请始终检查README和/或INSTALL文档以获取详细信息。)

与上面的配置脚本一样,当您在终端中运行 make(或类似的实用程序)时,它会显示一些有关正在执行的内容以及任何警告和错误的消息。您通常可以忽略警告,因为它们主要针对应用程序的开发人员,并告诉他们有一些违反了标准的做法。通常,这些警告不会影响应用程序功能。另一方面,必须处理编译器错误。使用 Newsbeuter,当我运行 make 时,一切正常,但随后出现错误:

tester@sitlabcpu22:~/download/newsbeuter-1.3$ make
...
c++ -ggdb -I/sw/include -I./include -I./stfl -I./filter -I. -I./xmlrss -Wall -Wextra -DLOCALEDIR=\"/usr/local/share/locale\" -o src/configparser.o -c src/configparser.cpp
c++ -ggdb -I/sw/include -I./include -I./stfl -I./filter -I. -I./xmlrss -Wall -Wextra -DLOCALEDIR=\"/usr/local/share/locale\" -o src/colormanager.o -c src/colormanager.cpp
In file included from ./include/pb_view.h:5,
from src/colormanager.cpp:4:
./include/stflpp.h:5:18: error: stfl.h: No such file or directory
In file included from ./include/pb_view.h:5,
from src/colormanager.cpp:4:
./include/stflpp.h:33: error: ISO C++ forbids declaration of \u2018stfl_form\u2019 with no type
./include/stflpp.h:33: error: expected \u2018;\u2019 before \u2018*\u2019 token
./include/stflpp.h:34: error: ISO C++ forbids declaration of \u2018stfl_ipool\u2019 with no type
./include/stflpp.h:34: error: expected \u2018;\u2019 before \u2018*\u2019 token
make: *** [src/colormanager.o] Error 1
Run Code Online (Sandbox Code Playgroud)

一旦遇到第一个错误,make 进程就会停止。处理编译器错误有时可能是一件棘手的事情。您必须查看错误以获取有关问题的一些线索。通常,问题是缺少某些通常具有.h或扩展名的头文件.hpp。在上述错误的情况下,很明显(或应该!)问题是stfl.h找不到头文件。如本示例所示,您希望查看错误消息的第一行并逐步找出问题的根本原因。

在查看了 Newsbeuter 文档后(我应该在开始之前完成,但是教程的这一部分不会很有意义!),我发现它需要一个名为 STFL 的第三方库。那么在这种情况下我们该怎么做呢?好吧,我们基本上对所需的库重复这个完全相同的过程:获取库并为其执行配置-构建-安装过程,然后继续构建所需的应用程序。例如,在 STFL 的情况下,我必须安装libncursesw5-dev软件包才能正确构建。(通常,在安装另一个所需的应用程序后,没有必要在我们的原始应用程序上重做配置步骤,但它也不会受到伤害。)

成功安装 STFL 工具包后,Newsbeuter 的 make 过程成功运行。make 过程通常会从它离开的地方(在错误点)开始。因此,任何已经成功编译的文件都不会被重新编译。如果要重新编译所有内容,可以运行 make clean all 以删除所有已编译对象,然后再次运行 make。

安装

构建过程成功完成后,您就可以安装应用程序了。在大多数情况下,要将应用程序安装到文件系统的公共区域(例如,/usr/bin/usr/share/bin等),您需要以 root 身份运行安装。安装确实是整个过程中最简单的一步。要安装,请在终端中运行:

$ make install
Run Code Online (Sandbox Code Playgroud)

检查此过程的输出是否有任何错误。如果一切顺利,您应该能够在终端中运行命令名称,它将启动。(将 & 附加到命令行的末尾,如果它是 GUI 应用程序,否则在应用程序完成运行之前您将无法使用终端会话。)

当您从源代码构建应用程序时,它通常不会向 Ubuntu 中的 GUI 菜单添加图标或快捷方式。您需要手动添加它。

这基本上就是在 Ubuntu 中从源代码构建和安装应用程序的过程,尽管可能是迭代的。在你这样做了几次之后,它就会成为你的第二天性!


小智 6

好吧, ./configure --help 将为您提供很多信息,用于 GNU autotools 生成的配置文件。大部分都归结为 --with/--without 启用功能(这些可能需要一个额外的参数,比如“共享”来说明在哪里可以找到库)。

其他重要的是 --prefix (大多数情况下默认为 /usr/local/ )来说明安装到哪里(如果您正在构建软件包,您通常希望将其设置为 --prefix=/usr 或 --prefix =/opt/YourPackage)。

在 Linux 上,/lib、/usr/lib 和 /usr/local/lib 通常在我的 gcc 中搜索,并包含在 ldconfig 的默认配置中。除非你有充分的理由,否则这就是你想要你的库的地方。然而,/etc/ld.so.conf 可能会列出额外的条目。

通过尝试运行“gcc -l”并查看它是否出错来配置和制作它们。您可以在 CFLAGS 参数中添加“-L”以添加额外的搜索路径。

您可以安装多个版本,并且与旧版本链接的软件将与其保持链接(运行 ldd 以找出 Linux 上的绑定),但新编译通常针对您系统上动态库的最新版本。

大多数软件都假定动态库,特别是如果它使用 libtool,所以您可能会发现非平凡的应用程序不能静态正确构建。

ls -l 是查找已安装库的最佳选择。

这就是我没有信息的地方;如何玩好包:不知道。如果可能,我会尝试将所有内容打包成一个包以避免出现问题。