Jerry 和 Maaza:浪漫的 Autotools





5.00/5 (1投票)
互联网上关于 Autotools 的文章缺失的是,它们是如何以一种简单简洁的方式整合在一起的。如果您像 Jerry 一样,对 Makefiles 以及编译器、链接器和预处理器选项有所了解,那么您在理解这个故事时会更容易一些。
工作的一天
于是,Jerry 发布了他的心血之作 C++ 开源项目。可以想象,当他得知被派往遥远星系的宇航员对他的应用感兴趣时,他的脸上露出了怎样的笑容。他的应用 TimeTravel 几乎不需要宣传;它很棒。没有其他应用能做到 TimeTravel 所做的事情。其中一位宇航员用尽了他们飞船上有限的带宽,花了两个半小时下载了 TimeTravel。但是,他无法在自己的 Linux PC 上安装它。他在编译过程中遇到了一个缺少文件的错误。多么令人失望!其他一些努力安装程序却未能成功的人,追踪到了 Jerry 的私人电子邮件,并向他发去了愤怒的邮件。他已经不记得在 TimeTravel 的任何文档中包含过他的电子邮件地址了;然而,愤怒的客户还是设法找到了他。一场愤怒的群众开始在他家门外聚集。
“咚咚!”
是 Maaza,她用柔和、天使般的声音宣布她的到来。Jerry 很快从他的胡思乱想中回过神来。Maaza 的到来总是受欢迎的。她总是那么整洁的仪容让 Jerry 纳闷她早上要花多少时间打理自己。令人惊讶的是,她是第一个到办公室的人。一两次,他曾在健身房见过她;她看起来不像会在这方面敷衍了事。俗话说,第一印象最深刻。Jerry 第一次见到 Maaza 是通过她的简历,当时老板让他评估软件工程师职位的候选人。Maaza 作为学生的辉煌成就当时并没有让她获得这份工作,因为缺乏经验,但她坚持申请其他职位。
“你说你带来了 U 盘?你在 IM 上是这么说的吗?”
“是的,Maaza。我带来了,”Jerry 回答道。“我很感激你的帮助。我最后不想做的是将一个未经测试的产品公之于众。”
和同龄的大多数男人一样,Jerry 发现自己会开玩笑来逗乐异性。“你知道,我可不想让愤怒的村民拿着火把和叉子来追我,”他说。Maaza 微笑起来,Jerry 心满意足。
“我今晚会看看的。不过,我确信你已经涵盖了所有的基本要素,”Maaza 回答道。她转过身,开始走回她的办公桌。
“回头再聊,那。”
晚上的时光
夏夜还很年轻,Jerry 拿起手机给 Maaza 打电话。“嗨 Maaza,怎么了?TimeTravel 已经运行起来了吗?”
“我花了一个小时在上面,但无法安装。我想是因为我缺少几个共享库。”
Maaza 是一个值得尊敬的 Linux 用户;Jerry 知道问她是否忽略了一些小事是没有意义的。如果她无法轻松安装,他知道大多数人会觉得更难。“来吧,我们 IM 上继续聊,”他说。“我还会给你发一个补丁。我做了一些修改。”
“你有补丁?”Maaza 问道,但她甚至没有等答案。“那么,你今天早上给我的那个版本号是多少?”
“我没想过需要版本号,我打算一旦发布就不维护了,你知道的,”Jerry 解释道。
“嗯,你现在确实在维护它,”Maaza 说道。“我将发送关于如何使用 Autotools 来完成这些事情的说明给你。”
闲聊
几分钟后,Maaza 的 IM 状态亮了起来。
- Jerry
- 又见面了!
- Maaza
- 好的,我们将一步一步地进行。首先,在你的项目根目录下创建一个名为 configure.ac 的文件。我看到你将你的根目录命名为 timetravel_src,所以将 configure.ac 放在 timetravel_src 中。
- Jerry
- configure.ac 是什么?它的用途是什么?
- Maaza
- 在我告诉你如何使用 configure.ac 之前,我想我应该先告诉你 Autotools。这只是三个程序的统称:Autoconf、Automake 和 Libtool。Autoconf 是一系列 M4 宏,最终生成一个名为 configure 的 shell 脚本。该脚本在目标机器上运行,以测试本地操作系统和设置。Automake 是一个程序,它接收 Makefile.am 文件并生成 Makefile.in 文件。编译器和链接器选项以及头文件和目标源文件都在 Makefile.ac 中指定。Libtool 有助于在不同操作系统之间统一共享库的创建。所有这些程序都由 configure 脚本协调,该脚本是用 configure.ac 中的模板创建的,请记住。
- Jerry
- 我相信我以前见过 Automake 的用法,但我的问题是,普通的、老式的 Makefile 在这个过程中扮演什么角色?看看这篇文章……
https://codeproject.org.cn/Articles/31488/Makefiles-in-Linux-An-Overview - Maaza
- 我知道你的源代码包里有很多 Makefile,但你想要的是可配置的 Makefile。我确信你的 Makefile 在你的机器上能正常工作,因为你确保它们能与你机器上的环境、设置和库一起工作;但不能保证我的机器会有那些设置和库。所以你需要使你的 Makefile 能够适应我的设置。你至少应该让你的包检查我的机器上的设置是否符合成功构建和安装的要求。还记得我之前说的 Makefile.in 吗?当 configure 脚本运行时,它会替换 Makefile.in 文件中的某些元素,以生成最终的、配置好的 Makefile。
- Jerry
- 这越来越有趣了。我已经创建了 configure.ac。接下来我该做什么?
- Maaza
- 你知道吗?我应该给你看一个我用来处理我 C++ 项目的 configure.ac 文件。这样讨论会更容易,而不是让你输入 configure.ac 中的所有内容。
- Jerry
- Maaza,你总是那么体贴。谢谢你。
Maaza 拿起一个她用于一个 C++ 项目的 configure.ac 文件,做了一些快速修改,然后发给了 Jerry。
## timetravel_src/configure.ac
############# initializing project with version number
##package_name = timetravel
##version_number = 1.0.0
##bug_report_email_address = username@domain.com
##AC_INIT(package_name, version_number, bug_report_email_address)
AC_INIT([timetravel], [1.0.0], [username@domain.com])
############## initializing Automake
AM_INIT_AUTOMAKE
############## initializing libtool
LT_INIT
############## checking programs
##checking C++ compiler
AC_PROG_CXX
##checking C Preprocessor
AC_PROG_CPP
##checking C compiler
AC_PROG_CC
############## checking libraries
AC_CHECK_LIB([pthread], [main])
############## checking header files
AC_CHECK_HEADERS([stdlib.h stringstrings.h sys/socket.h sys/time.h])
############## generating output makefiles from input
AC_CONFIG_FILES(Makefile ttravel/Makefile)
AC_OUTPUT
- Maaza
- 我添加了注释来解释步骤,但总的来说,这些是您可能需要用来适应目标系统的宏。您可以看到,我从
AC_INIT
开始,为项目提供包名和版本号,然后检查了一些程序、库和头文件。最后,我指定了需要创建的 Makefile。当然,它们之间还可以包含许多其他的宏。有关完整的列表,请查阅 gnu.org 上的 Automake:General Index。 - Jerry
- 等等——就这些吗?那 Makefile 呢?
- Maaza
- 好的,我也会解释 Makefile.am。我之前说过 configure.ac 接受 M4 宏。嗯,你可以在 configure.ac 和 Makefile.am 文件中放入任何东西,从 M4 宏到 Linux shell 命令,但在 Makefile.am 中,我们主要使用 Automake 正确解释的变量来实现所需的效果。Automake 变量被赋值来创建 Libtool 库、存档库、可执行文件等等。它们也用于指示二进制文件将被安装的目标目录。每个变量都有两个用下划线分隔的组件:主变量和目录变量。例如,在
bin_PROGRAMS
中,PROGRAMS
是主要的,告诉我们它将用于生成可执行文件,而bin
是目标目录——更具体地说,是 $(prefix)/bin。默认情况下,$(prefix) 解析为 /usr/local,但就像 Autotools 的许多东西一样,这也可以配置,并可以更改为其他位置,如 /usr/ttravel。您可能还想了解的其他变量是:lib_LIBRARIES
和lib_LTLIBRARIES
。正如您所料,LIBRARIES
用于静态 .a 库,而LTLIBRARIES
用于 Libtool 生成的 .la 共享库。
## timetravel_src/ttravel/Makefile.am
AM_CXXFLAGS = -g -std=c++11 -Wall -pedantic
############# two static libraries
noinst_LIBRARIES = libairports.a libtrainstations.a
##the two share mi_distance.cpp in common
libairports_a_SOURCES = mi_distance.cpp mi_flightduration.cpp
libairports_a_CPPFLAGS = -I../ttravel_headers
libtrainstations_a_SOURCES = mi_distance.cpp mi_trainstops.cpp
libtrainstations_a_CPPFLAGS = -I../ttravel_headers
############# an lt library
lib_LTLIBRARIES = ttravel.la
libttravel_la_SOURCES = ttravel.cpp
libttravel_la_CPPFLAGS = -I../ttravel_headers
libttravel_la_LIBADD = libairports.a libtrainstations.a
############# an executable
bin_PROGRAMS = ttravel
ttravel_SOURCES = ttravel_main.cpp
ttravel_CPPFLAGS = -I../ttravel_headers
ttravel_LDADD = ttravel.la
ttravel_LDFLADS = -lpthread
- Jerry
- 我知道这里是怎么回事。你正在为整个 Makefile 设置
AM_CXXFLAGS
,而ttravel_CPPFLAGS
、libttravel_la_CPPFLAGS
、libairports_a_CPPFLAGS
和libtrainstations_a_CPPFLAGS
是为各个目标设置的,对吗? - Maaza
- 完全正确!抱歉这么突然。嗯,正如你所见,
target_SOURCES
是接受该特定目标源文件的变量;其余的对我来说似乎不言自明。但是,请注意_LIBADD
、_LDADD
和_LDFLAGS
的用法。_LIBADD
和_LDADD
之间的区别在于,_LIBADD
连接库的组件,而_LDADD
被传递给链接器以生成可执行程序。链接器应该在_LDFLAGS
中设置,如果你注意到的话,我们在 configure.ac 中检查了pthread
,现在链接了它。 - Jerry
- 你真棒。谢谢你!
- Maaza
- 还有最后一件事——你需要在 timetravel_src 中创建另一个 Makefile.am。这将是 configure.ac 找到的第一个 Makefile.am。这个 Makefile.am 应该包含一个用于递归构建的宏。你只需要一行:
SUBDIRS = ttravel
。你知道,同一个目录中不能有两个 Makefile,原因很简单,你无法为它们命名。所以,我们可以通过目录来引用一个 Makefile。现在,我可以说,当你运行 timetravel_src Makefile 时,timetravel_src/ttravel 中的那个将首先被执行。 - Maaza
- 解决了所有这些问题后,剩下要做的就是在正确的顺序运行 Autoconf、Automake 和 Libtool。我们有一个命令,在终端中,导航到你放置了 configure.ac 和 Makefile.am 的 timetravel_src 目录。输入以下命令
autoreconf
这个命令将为你生成所有 Makefile.in 和 configure 脚本。- 之后,客户只需要运行
./configure
make
make install- Jerry
- 我不知道如何感谢你,除非请你吃顿饭。这会不会让你那位“男友”客户经理吃醋?哈哈
- Maaza
- 这个笑话有点老了。那天晚上我们一起吃了晚饭,因为我们都加班到很晚才完成工作。
- Jerry
- 但是,之后你们还去看电影了。
- Maaza
- 纯粹的柏拉图式友谊!