7 Directories


对于所有发布的文件都在一个目录下的简单工程来说,只有一个 Makefile.am已足够将所有东西编译到一起。
对于大型工程,通常将文件组织在不同目录中,形成目录层次结构树。例如,可能有一个目录用于程序源文件,一个目录用于测试组件,一个目录用于文档;或者是对于超大型工程,每个程序、库、模块都有一个目录。
构建多目录工程的传统方式是通过递归构建子目录,使用 makerecursion功能:每个目录下都有各自的 Makefile,当在工程根目录执行 make时,会依次进入各子目录,并将当前子目录作为一个新的 make实例来构建该子目录的内容。
由于这种递归构建方式非常普遍,所以Automake内建支持它。

7.1 Recursing subdirectories

在packcages中使用递归make时,工程根目录中的Makefile.am需要告诉Automake哪些子目录需要构建。这通过SUBDIRS变量实现。
SUBDIRS变量的值是一个由空格分隔的子目录列表,构建过程中会在每个子目录中执行各种构建动作(如构建可执行文件或构建库)。在生成的Makefile中的大部分用于构建目标文件的标的(例如all标的)会在当前目录和指定的子目录中执行构建命令。注意:在SUBDIRS变量指定的子目录中不要求包含Makefile.am脚本,只需要有Makefile脚本(configure后生成的)。这允许从其他packages中引用一个库,而不需要使用Automake。
在多目录工程中,根目录Makefile.am通常很简短。例如下面是GNU Hello发行样例的Makefile.am

EXTRA_DIST = BUGS ChangeLog.O README-alpha
SUBDIRS = doc intl po src tests

当Automake在子目录中需要引用make时,会使用MAKE变量来引用它。并且Automake会将AM_MAKEFLAGS变量的值传递给make,这允许在Makefile.am中将必须传递给make的flags赋给AM_MAKEFLAGS变量。
SUBDIRS变量中指定的目录通常是该Makefile.am所属目录的直接子目录(如果引用其他packages的库,则可能指定其他目录),每个子目录都有各自的Makefile.am脚本,并且各自的Makefile.am脚本中也可能包含SUBDIRS变量来指向再下一层子目录。Automake可通过这种方式来构建任意深度目录结构的packages。
默认情况下,Automake生成的Makefiles脚本在各目录中的执行顺序是深度优先的:即子目录先于当前目录进行构建。但这种默认规则可通过在SUBDIRS变量的值中插入 . (指当前目录)的方式改变。例如:

SUBDIRS = lib src . test

则各目录的构建顺序依次为:lib、src、当前目录、test。通常将test目录的构建放在最后,用于测试在这之前都构建了哪些内容。
除了Automake内建的一些递归标的(allcheck等),开发者也可以自定义递归标的。这通过在configure.ac中将要定义的标的作为参数传给m4宏AM_EXTRA_RECURSIVE_TARGETS来定义。Automake会生成规则来处理这些标的的递归动作。开发者可以通过在相应目录的Makefile.am中定义 -local 标的,来定义当在根目录make自定义的标的,递归到当前目录下时要具体执行的动作。

% cat configure.ac
AC_INIT([pkg-name], [1.0]
AM_INIT_AUTOMAKE
AM_EXTRA_RECURSIVE_TARGETS([foo])
AC_CONFIG_FILES([Makefile sub/Makefile sub/src/Makefile])
AC_OUTPUT

% cat Makefile.am
SUBDIRS = sub
foo-local:
@echo This will be run by "make foo".

% cat sub/Makefile.am
SUBDIRS = src

% cat sub/src/Makefile.am
foo-local:
@echo This too will be run by a "make foo" issued either in
@echo the ’sub/src/’ directory, the ’sub/’ directory, or the
@echo top-level directory.
@echo 在'sub/src/'目录或'sub/'目录或根目录执行'make foo'时,都会执行
@echo 'sub/src/Makefile.am'中foo-local标的下定义的动作

7.2 Conditional Subdirectories

如果只是想构建整个package的一个子集时,你可能需要条件性的定义SUBDIRS变量,就像GNU Inetutils的情况一样。
为了说明这种方式怎么执行,假设有两个目录src/opt/src/ 目录总是需要构建,但我们需要在configure中确定opt/ 目录是否需要构建。(例如当 $want_opt变量值为yes时,opt/ 目录需要构建)。
执行make时应该总是递归进src/ 目录,而有可能递归进opt/ 目录。
但是 make dist必须始终能递归到src/ 目录和opt/ 目录中。因为opt/ 目录也是需要发布的,即使在当前配置中不需要它,这意味着opt/Makefile需要被无条件(unconditionally)的创建。
有两种方式可在一个工程中实现这种功能。可以使用Automake的条件特性(第20章 [Conditionals]),或使用Autoconf的AC_SUBST变量(Autoconf手册的’Setting Output Variables’章节)。推荐使用Automake的条件特性。在说明这两种方式之前,先介绍一下DIST_SUBDIRS

7.2.1 SUBDIRS vs. DIST_SUBDIRS

Automake会识别分别由SUBDIRS变量和DIST_SUBDIRS变量定义的两组目录。
SUBDIRS变量包含当前目录一定会被构建的子目录,其必须被手动定义,因为Automake不会去猜测某个目录是否要被构建。在后面两节中我们可以看到,可能会以条件定义的方式定义SUBDIRS变量,所以某些条件下有些目录在构建时可能会被忽略。
DIST_SUBDIRS变量用于那些需要递归进所有目录的规则,即使某些目录已经从构建目录列表中条件省略了。在前面的例子中我们可能不想构建opt/ 目录,但如果想要发布它,就需要DIST_SUBDIRS变量来指定:SUBDIRS可能不包含opt/,但DIST_SUBDIRS一定包含opt/
准确的说,DIST_SUBDIRS变量用于make maintainer-cleanmake distcleanmake dist。其他的递归构建规则使用SUBDIRS变量。
如果SUBDIRS变量是通过Automake的条件特性来条件定义,Automake会自动定义DIST_SUBDIRS变量,该变量的值就是所有可能的SUBDIRS变量的值。
如果SUBDIRS变量包含AC_SUBST变量,即通过AC_SUBST条件定义,DIST_SUBDIRS变量将无法正确定义,因为Automake不知道SUBDIRS的所有可能值。这种情况下DIST_SUBDIRS需要手动定义。

7.2.2 Subdirectories with AM_CONDITIONAL

configure应该在每个目录下都生成Makefile,并且定义一个条件来确定opt/ 是否需要编译。

...
AM_CONDITIONAL([COND_OPT], [test "$want_opt" = yes])
AC_CONFIG_FILES([Makefile src/Makefile opt/Makefile])
...

在根目录Makefile.am中定义SUBDIRS变量如下:

if COND_OPT
MAYBE_OPT = opt
endif
SUBDIRS = src $(MAYBE_OPT)

可以直观看到的是,执行make后就会必定进入src/ 目录而有可能进入opt/
无法直观看到的是,执行make dist会递归进入src/ 目录和opt/ 目录,因为make dist 不像make all一样根据SUBDIRS变量递归构建,而是根据DIST_SUBDIRS变量递归发布。
这种情况下,Automake会自动定义DIST_SUBDIRS = src opt,因为它知道某些情况下可能会包含opt

7.2.3 Subdirectories with AC_SUBST

另一种可能的方式是通过 ./configure 使用AC_SUBST定义MAYBE_OPT

...
if test "$want_opt" = yes; then
MAYBE_OPT=opt
else
MAYBE_OPT=
fi
AC_SUBST([MAYBE_OPT])
AC_CONFIG_FILES([Makefile src/Makefile opt/Makefile])
...

这种情况下,根目录的Makefile.am应该包含以下内容:

SUBDIRS = src $(MAYBE_OPT)
DIST_SUBDIRS = src opt

缺点是由于Automake无法确定MAYBE_OPT的可能值,它需要手动定义DIST_SUBDIRS

7.2.4 Unconfigured Subdirectories

DIST_SUBDIRS的语义经常被某些用户误解为条件性的配置并构建(config and build) 子目录。目前,对于配置(configuring)认为是生成Makefile(it might also involve running a nested configure script: this is a costly operation that
explains why people want to do it conditionally, but only the Makefile is relevant to the
discussion)。
前几节中的例子都假设会在所有目录下生成Makefile,即使该目录不会被构建。因为我们想make dist能够发布那些不会被构建的目录(例如平台依赖性代码),因此make dist必须递归进子目录中,因此这些子目录必须被配置并且在DIST_SUBDIRS变量值的目录列表中。
不配置任何子目录的构建packages是一件棘手的事,不推荐新手这么做,因为会因为错误导致生成不完整的tar包。这里不深入讨论这个问题,只需要记住以下规则:

  • SUBDIRS必须始终是DIST_SUBDIRS的子集。
    将一个目录放入SUBDIRS而不放入DIST_SUBDIRS是没有意义的。
  • SUBDIRSDIST_SUBDIRS中列出的所有目录必须被配置。
    即必须生成Makefile,否则make将无法递归到该目录进行构建。
  • 所有被配置的目录被包含在DIST_SUBDIRS中。
    这样清除规则(make distclean等)就可以删除生成的Makefile
    为了防止递归到一些未配置(没有Makefile)的目录中,必须保证未配置目录没有在DIST_SUBDIRS(和SUBDIRS)目录中。当然,未在DIST_SUBDIRS中列出的目录也不会被发布。

7.3 An Alternative Approach to Subdirectories

7.4 Nesting Packages

在GNU Build System中,packages可以嵌套任意深度。这意味着一个自带configureMakefiles等构建脚本的package可以嵌套到其他packages中。
如果一些子packages嵌套到了父package中,这些子packages应该作为父package的子目录,并且被列入父package对应子packages所在目录的SUBDIRS变量的目录列表中。但是这些子packages的Makefiles是由它们自己的configure脚本生成的,不是由父package的configure。这是通过在父目录的configure.ac脚本中使用Autoconf宏AC_CONFIG_SUBDIRS实现的。
下面是一个例子,arm工程链接了一个hand库,hand库作为arm工程的hand子目录嵌套到arm package中。
arm package的configure.ac

AC_INIT([arm], [1.0])
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
# Call hand’s ./configure script recursively.
AC_CONFIG_SUBDIRS([hand])
AC_OUTPUT

arm package的Makefile.am

# Build the library in the hand subdirectory first.
SUBDIRS = hand
# Include hand’s header when compiling this directory.
AM_CPPFLAGS = -I$(srcdir)/hand
bin_PROGRAMS = arm
arm_SOURCES = arm.c
# link with the hand library.
arm_LDADD = hand/libhand.a

hand 子package的hand/configure.ac

AC_INIT([hand], [1.2])
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE
AC_PROG_CC
AM_PROG_AR
AC_PROG_RANLIB
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

hand 子package的hand/Makefile.am

lib_LIBRARIES = libhand.a
libhand_a_SOURCES = hand.c

当在父package arm的根目录下运行make dist时,会创建一个arm-1.0.tar.gz,该发布包包含arm工程和hand子目录。这个父package可以像普通package那样使用 ‘./configure && make && make install’ 命令序列来构建和安装(在这个过程中会同时构建和安装hand)。
当在hand package的根目录中执行make dist时,将只生成一个包含hand自身的发布包hand-1.2.tar.gz。所以hand可以嵌入到其他package中,也可以单独使用。
AC_CONFIG_AUX_DIR[.] 宏的作用是强制Automake和Autoconf在当前目录下查找辅助脚本。例如上例,这意味着有两个install-sh:一个在arm package的根目录,另一个在hand/ 子目录用于hand package。
默认情况下,在父目录或父父目录中查找辅助脚本。所以如果把hand/configure.ac中删除AC_CONFIG_AUX_DIR[.] 这行,这个子package将与arm package共享辅助脚本。这看起来像是对package大小的改进(因为少了一个辅助脚本,可能减少了几千字节),但失去了模块化的灵活性,因为这种方式导致hand子package无法发布自己(在hand子目录中执行make dist命令什么都不会做)。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值