如果您的问题是:
我可以在某台机器 A 上使用自动工具来创建一个可以在所有其他机器上运行的通用 makefile 吗?
那么答案是“否”。自动工具甚至不会假装尝试这样做。它们旨在包含可移植代码,这些代码将决定如何在目标机器上创建一个可行的 makefile。
如果您的问题是:
我可以使用自动工具来配置需要在不同机器上运行的软件,使用我的插件使用的不同版本的主要软件,以及各种第 3 方库,更不用说 32 位与 64 位的问题了吗?
那么答案是“是”。自动工具旨在能够做到这一点。此外,它们适用于 Unix、Linux、MacOS X、BSD。
我有一个程序 SQLCMD(它比 Microsoft 的同名程序早了十年甚至更多),它与 IBM Informix 数据库一起工作。它检测安装的客户端软件(称为 IBM Informix ESQL/C,IBM Informix ClientSDK 或 CSDK 的一部分)的版本,以及它是 32 位还是 64 位。它还检测安装了哪个版本的软件,并根据支持产品中的可用功能调整其功能。它支持已经发布了大约 17 年的版本。它是自动配置的——我必须为 Informix 功能和其他一些小玩意(高分辨率计时、/dev/stdin 的存在等)编写一些 autoconf 宏。但这是可行的。
另一方面,我不会尝试发布适合所有客户机器和环境的单个 makefile;有太多的可能性是明智的。但是 autotools 会为我(和我的用户)处理细节。他们所做的只是:
./configure
这比弄清楚如何编辑 makefile 更容易。 (哦,在最初的 10 年里,程序是手动配置的。尽管我设置了很好的默认设置,但人们很难做到。这就是我转向自动配置的原因:它使人来安装。)
福兹先生评论:
我想要介于两者之间的东西。在我的情况下,客户将在同一台机器上使用相同基础应用程序的多个版本和位数。我不担心交叉编译,例如在 Linux 上构建 Windows 二进制文件。
您需要为 32 位和 64 位版本单独构建插件吗? (我认为是的 - 但你可能会让我感到惊讶。)所以你需要提供一种机制让用户说
./configure --use-tppkg=/opt/tp/pkg32-1.0.3
(其中 tppkg 是您的第三方包的代码,位置由用户指定。)但是,请记住可用性:用户必须提供的此类选项越少,更好;对此,不要硬编码应该是可选的东西,例如安装位置。一定要查看默认位置 - 这很好。并且默认为您找到的东西的琐碎。也许如果您同时找到 32 位和 64 位版本,那么您应该同时构建这两个版本——不过,这需要仔细构建。您可以随时回显“正在检查 TP-Package ...”并指出您找到的内容和找到的位置。然后安装程序可以更改选项。确保在“./configure --help”中记录选项是什么;这是标准的 autotools 做法。
不要做任何交互的事情;配置脚本应该运行,报告它做了什么。 Perl Configure 脚本(注意大写字母 - 它是一个完全独立的自动配置系统)是剩下的少数密集交互配置系统之一(这可能主要是因为它的传统;如果重新开始,它很可能是非交互式的)。这样的系统配置起来比非交互式系统更麻烦。
交叉编译很难。谢天谢地,我从来不需要这样做。
福兹先生也评论道:
感谢额外的 cmets。我正在寻找类似的东西:
./configure --use-tppkg=/opt/tp/pkg32-1.0.3 --use-tppkg=/opt/tp/pkg64-1.1.2
它将在一个 makefile 中为当前平台创建 32 位和 64 位目标。
嗯,我确信可以做到;与两个单独的配置运行并在两者之间进行完全重建相比,我不太确定这样做是否值得。您可能想要使用:
./configure --use-tppkg32=/opt/tp/pkg32-1.0.3 --use-tppkg64=/opt/tp/pkg64-1.1.2
这表示两个单独的目录。您必须决定如何进行构建,但可能会有两个子目录,例如“obj-32”和“obj-64”,用于存储单独的目标文件集。您还可以按照以下方式排列您的 makefile:
FLAGS_32 = ...32-bit compiler options...
FLAGS_64 = ...64-bit compiler options...
TPPKG32DIR = @TPPKG32DIR@
TPPKG64DIR = @TPPKG64DIR@
OBJ32DIR = obj-32
OBJ64DIR = obj-64
BUILD_32 = @BUILD_32@
BUILD_64 = @BUILD_64@
TPPKGDIR =
OBJDIR =
FLAGS =
all: ${BUILD_32} ${BUILD_64}
build_32:
${MAKE} TPPKGDIR=${TPPKG32DIR} OBJDIR=${OBJ32DIR} FLAGS=${FLAGS_32} build
build_64:
${MAKE} TPPKGDIR=${TPPKG64DIR} OBJDIR=${OBJ64DIR} FLAGS=${FLAGS_64} build
build: ${OBJDIR}/plugin.so
这假设插件是一个共享对象。这里的想法是自动工具将检测第三方软件包的 32 位或 64 位安装,然后进行替换。如果需要 32 位包,则 BUILD_32 宏将设置为 build_32,否则留空; BUILD_64 宏的处理方式类似。
当用户运行'make all'时,它将首先构建build_32目标,然后是build_64目标。要构建 build_32 目标,它将重新运行 make 并为 32 位构建配置标志。同样,要构建 build_64 目标,它将重新运行 make 并为 64 位构建配置标志。重要的是,所有受 32 位和 64 位构建影响的标志都设置在 make 的递归调用上,并且构建对象和库的规则要仔细编写 - 例如,将源代码编译为object 必须小心地将对象文件放在正确的对象目录中 - 例如,使用 GCC,您可以指定(在 .c.o 规则中):
${CC} ${CFLAGS} -o ${OBJDIR}/$*.o -c $*.c
宏 CFLAGS 将包含处理位的 ${FLAGS} 值(例如,FLAGS_32 = -m32 和 FLAGS_64 = -m64, and so when building the 32-bit version,FLAGS = -m32would be included in theCFLAGS` 宏。
自动工具中的剩余问题是如何确定 32 位和 64 位标志。如果最坏的情况发生在最坏的情况下,您将不得不自己为此编写宏。但是,我希望(无需研究)您可以使用 autotools 套件中的标准工具来完成它。
除非您为自己创建一个仔细(甚至是无情地)对称的 makefile,否则它不会可靠地工作。