【发布时间】:2013-06-19 00:37:55
【问题描述】:
在构建库时如何实现 make installcheck 目标?也就是说,我有一个check 目标,它创建一个链接到创建的库的测试程序,由一些脚本调用以检查库代码是否正常运行,我希望执行相同的检查但链接到库安装后;我该如何实现?
【问题讨论】:
标签: makefile installation shared-libraries
在构建库时如何实现 make installcheck 目标?也就是说,我有一个check 目标,它创建一个链接到创建的库的测试程序,由一些脚本调用以检查库代码是否正常运行,我希望执行相同的检查但链接到库安装后;我该如何实现?
【问题讨论】:
标签: makefile installation shared-libraries
由于我找不到这方面的任何指南,我将介绍我想出的方法,该方法是通过阅读GNU automake manual 和一些一般性的试验和错误来修补的。它可能很难看,但它确实有效......
如果构建库的检查例程涉及将库链接到测试程序并查看该程序是否正常工作,那么要安装检查库,我们只需要做同样的事情,但将测试程序链接到已安装的库,而不是本地构建的库。
我们将库称为 alpha(因此我们将创建 libalpha.so 和/或 libalpha.a)。假设 alpha 的源代码在 src 目录下的文件 alpha.cpp 中,我们将照常创建 src/Makefile.am:
# src/Makefile.am
lib_LTLIBRARIES = libalpha.la
libalpha_la_SOURCES = alpha.cpp
include_HEADERS = alpha.h
检查例程涉及创建链接到 alpha 的二进制 beta。 beta 的源代码在 tests 目录下的 beta.cpp 文件中。自动生成文件 tests/Makefile.am 如下所示:
# tests/Makefile.am
check_PROGRAMS = beta
beta_SOURCES = beta.cpp
beta_CPPFLAGS = -I$(top_srcdir)/src
beta_LDADD = $(top_builddir)/src/libalpha.la
我们将通过在 tests/Makefile.am 中创建“本地”目标来创建我们的 check 和 installcheck 例程,如下所示:
# tests/Makefile.am
# ...
check-local:
# recipe to run when 'make check' is called.
installcheck-local:
# recipe to run when 'make installcheck' is called.
check 和 installcheck 目标冲突,因为使用任一目标会阻止另一个目标正确执行(一个目标“污染”另一个目标的构建树);为了使其他目标正确执行,我们需要删除 beta 及其目标文件,并根据其性质重新编译和重新链接目标(installcheck 已安装文件, check 到本地文件)。
我们可以通过在两个目标的配方中简单地运行make clean 来解决这个被污染的构建树的问题。这将清楚地删除受污染的构建,但过于热心,因为每当我们再次运行相同的目标时我们都不需要重新构建。只有在我们运行另一个目标时,构建树才会受到污染。
我们只能通过记住之前调用过的两个目标中的哪一个来解决这个复杂问题,我们可以通过创建/销毁一个中间文件(我们称之为 taint)来解决这个问题。只要文件 taint 存在,check 目标就会受到污染,它通过清理、重建和删除 taint 来解决;当文件 taint 不存在时,installcheck 目标被污染,它通过清理、重建和创建 taint 来解决此问题。
我们的目标将采取以下形式:
# tests/Makefile.am
# ...
check-local:
# First, check to see if the build tree is tainted and rebuild if so
test ! -f taint || $(MAKE) $(AM_MAKEFLAGS) check_rebuild
# Then, run our check tests. Substitute with your shell scripts or testsuite files as appropriate
./beta
installcheck-local:
# First, check to see if the build tree is tainted and rebuild if so
test -f taint || $(MAKE) $(AM_MAKEFLAGS) installcheck_rebuild
# Then, run our installcheck tests. Substitute with your shell scripts or testsuite files as appropriate
./beta
目标check_rebuild 需要根据check 的运行方式进行重建,看起来像这样:
# tests/Makefile.am
# ...
.PHONY: check_rebuild
check_rebuild:
$(MAKE) $(AM_MAKEFLAGS) clean # remove tainted build tree
$(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT) # rebuild beta
rm -f taint # mark build tree as untainted
目标installcheck_rebuild 同样看起来像这样:
# tests/Makefile.am
# ...
.PHONY: installcheck_rebuild
installcheck_rebuild:
$(MAKE) $(AM_MAKEFLAGS) clean # remove tainted build tree
$(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT) \
beta_CPPFLAGS="-I$(DESTDIR)$(includedir)" \
beta_LDADD="$(DESTDIR)$(libdir)/libalpha.la" \
beta_DEPENDENCIES="$(DESTDIR)$(libdir)/libalpha.la"
echo 1 > taint # mark build tree as untainted
请注意,在 installcheck_rebuild 中重建 beta 现在需要覆盖 automake 变量,以便它们指向已安装的库。
最后,我们需要将污点文件添加到DISTCLEANFILES,这样运行distcheck 就不会因“distclean 后遗留在构建目录中的文件”错误而失败。
应该就是这样。最终的 tests/Makefile.am 应该如下所示:
# tests/Makefile.am
check_PROGRAMS = beta
beta_SOURCES = beta.cpp
beta_CPPFLAGS = -I$(top_srcdir)/src
beta_LDADD = $(top_builddir)/src/libalpha.la
taint_file = .taint
check-local:
test ! -f $(taint_file) || $(MAKE) $(AM_MAKEFLAGS) check_rebuild
./beta # substitute with your actual test routines
installcheck-local:
test -f $(taint_file) || $(MAKE) $(AM_MAKEFLAGS) installcheck_rebuild
./beta # substitute with your actual test routines
.PHONY: check_rebuild
check_rebuild:
$(MAKE) $(AM_MAKEFLAGS) clean
$(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT)
rm -f $(taint_file)
.PHONY: installcheck_rebuild
installcheck_rebuild:
$(MAKE) $(AM_MAKEFLAGS) clean
$(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT) \
beta_CPPFLAGS="-I$(DESTDIR)$(includedir)" \
beta_LDADD="$(DESTDIR)$(libdir)/libalpha.la" \
beta_DEPENDENCIES="$(DESTDIR)$(libdir)/libalpha.la"
echo 1 > $(taint_file)
DISTCLEANFILES = $(taint_file)
这已在 Linux 上检查为“标准”构建,但它可能不适用于其他构建环境,或者如果您正在尝试做一些“异国情调”的事情。希望它应该,但这不是我费心去检查的东西。如果有错误,问题很可能是我的上述目标之一中缺少或误用的变量。
例如,check_rebuild 目标具有以下行:
check_rebuild:
# ...
$(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT)
# ...
变量 $(AM_MAKEFLAGS) 和 $(EXEEXT) 是 automake 自身创建例程以填充它创建的 Makefile 的一部分,如果我在上面的目标中忽略了它们,那么它可能会导致构建失败(或至少不能按预期运行)。
我已尝试确保我上面建议的目标同样是规范构建的,但我可能遗漏了一些东西。如果出现构建错误,最好的办法是在构建过程中打开 automake 自身生成的 Makefile,查看它是如何创建对象并在相应的 Makefile.am 文件中模仿这些结构的,就像我尝试做的那样。
另一个可能的问题可能是我在installcheck_rebuild 目标中构建beta 二进制文件的“hackish”方式。同样,诊断问题的最佳选择是查看 automake 在它生成的 Makefile 中是如何做事的,并尝试模仿它。否则,阅读 automake 手册是有必要的,如果做不到这一点,你可能需要比我更有知识的人的帮助。祝你好运。
【讨论】: