https://www.gnu.org/software/automake/manual/automake.html

Makefile.am中通常只包含变量定义,但也可以包含额外的Makefile规则,Automake在处理时,会将Makefile规则原样保留至输出文件中。

automake根据简单的Makefile.am文件生成复杂的Makefile.in文件。不需要关心Makefile.in文件的内容,可以将其视作automake的实现细节。

每个目录中都可以存在一个Makefile.am文件,这些文件都必须在configure.ac中通过AC_CONFIG_FILES宏声明。此外,Makefile.am中需要使用SUBDIRS变量标记其所在目录下的存在其他Makefile.am文件的子目录,SUBDIRS的顺序就是例如:
AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile
src/dira/Makefile src/dirb/Makefile])
在Makefile中需定义:SUBDIRS=lib src
在src/Makefile中需定义:SUBDIRS=dira dirb
因为通常父目录会依赖子目录的构建结果,子目录会被首先处理,然后才是父目录。如果希望修改这个行为,可以在SUBDIRS定义中明确定义当前目录:.,如SUBDIRS=dira . dirb。

where_PRIMARY = targets…

PRIMARY: 表示targets的构建结果,可以是以下值之一:

  • PROGRAMS
  • LIBRARIES
  • LTLIBRARIES (LIBTOOLS library)
  • HEADERS
  • SCRIPTS
  • DATA

where: 表示构建结果要被安装到的位置,通常是bin或者lib,也可以取custom、nodist或者check,其中custom会被安装到$(customdir)目录,nodist不会被安装,check会在make check时被构建。下图是和安装位置有关的常用变量:
image.png

除了where和PRIMARY,有时会在前面加一个option,形成option_where_PRIMARY = targets这样的结构。option可取dist或者nodist。

编译可执行文件

bin_PROGRAMS = foo run-me foo_SOURCES = foo.c foo.h print.c print.h run_me_SOURCES = run.c run.h print.c

binPROGRAMS表示foo和run-me都会被安装到$(bindir)目录中。
foo_SOURCES和run_me_SOURCES都是形如target_SOURCES这样命名的变量,用于指定构建target的源文件,注意run-me中的符号会被转为下划线

源文件中列出的.h头文件并不参与编译,这里列举出来是为了让它们被识别并被正确打包。
根据源文件的扩展名,如.c,.cpp,automake能够自动选择合适的编译器。

编译静态库文件

首先,需要在configure.ac中添加宏:AC_PROG_RANLIB。

lib_LIBRARIES = libfoo.a libbar.a libfoo_a_SOURCES = foo.c privfoo.h libbar_a_SOURCES = bar.c privbar.h include_HEADERS = foo.h bar.h

lib_LIBRARIES表示要构建静态库文件,并且要被安装到$(libdir)目录中。注意linux系统的静态库文件必须符合lib*.a的命名标准。
include_HEADERS定义了公开头文件,这些文件会被安装到$(includedir)目录中。
在target_SOURCES中定义的头文件不会被安装。

VPATH

由Autotools产生的configure支持VPATH构建,即使当前目录不是源码目录,我们也可以在当前目录进行构建,构建产物会在当前目录产生而不是源码目录。
在使用VPATH构建时,有两个目录概念:源码目录和构建目录。

  • 源码目录是configure、Makefile.in以及源码文件所在目录;
  • 构建目录是Makefile及构建产生的目标文件所在目录。

要使用VPATH构建,在非源码目录执行configure即可,当前目录会被当作构建目录;
VPATH的工作原理是:在每个Makefile中,config.status都会定义$(srcdir)变量,该变量定义源码所在目录。当make需要寻找某个文件时,它会现在源码目录中找,然后在构建目录中找,因此在autotools脚本中引用源码或目标文件时直接引用即可,不用操心该文件会在哪个目录中。
只有当指定非源码或目标文件时,比如指定-I选项时,需要特别指定源码目录:-I$(srcdir)/include,否则在执行VPATH构建时,默认会处于构建目录中,导致指定的是构建目录中的路径。

编译不最终安装的辅助库

lib/Makefile.am

noinst_LIBRARIES = libcompat.a libcompat_a_SOURCES = xalloc.c xalloc.h

src/Makefile.am

LDADD = ../lib/libcompat.a AM_CPPFLAGS = -I$(srcdir)/../lib bin_PROGRAMS = foo run-me foo_SOURCES = foo.c foo.h print.c print.h run_me_SOURCES = run.c run.h print.c

以上lib/Makefile.am文件定义了一个只有在构建时有用的工具库,src/Makefile.am使用LDADD定义引用了该工具库。注意LDADD指定库文件时没有使用$(srcdir),因为库文件是编译产物,只有当指定选项时,如AM_CPPFLAGS时才需要指定$(srcdir)。
Makefile.am中常用的选项有:

  • LDADD指定的库文件会参与所有编译,如果针对target指定,需要定义target_LDADD;
  • AM_CPPFLAGS指定了预处理选项,AM_XXXFLAGS这样的定义都可以针对target指定,只需写成target_XXXFLAGS即可,注意AM被省去了;
  • AM_CFLAGS指定C编译选项;
  • AM_LIBADD指定链接选项,只有在编译library时生效;
  • AM_LDADD指定链接选项,只有在编译program时生效;
  • AM_LDFLAGS指定链接选项,和LIBADD、LDADD不同,它总是生效,没有生效条件。

检查库文件

configure.ac中的AC_CHECK_LIB宏用于检查库文件及函数是否存在。通常使用如下方式检查库并根据检查结果定义Makefile.am中可用的变量:
AC_CHECK_LIB([efence], [malloc], [EFENCELIB=-lefence])
AC_SUBST([EFENCELIB])
这样,在Makefile.am中就可以引用$(EFENCELIB)了,如LDADD=$(EFENCELIB)。

发布工程

make dist和make distcheck创建一个tarball,这个tarball包含:
所有通过target_SOURCES声明的源文件;
所有通过target_HEADERS声明的头文件;
所有通过dist_target_SCRIPTS声明的脚本文件;
所有通过dist_target_DATA声明的数据文件;

其他诸如NEWS、ChangeLog等标准GNU文件;
EXTRA_DIST声明的其他文件。

条件控制

在进行编译时,可以使用条件控制决定哪些文件参与编译、哪些目标需要编译。
在configure.ac中使用以下宏来声明条件定义:
AM_CONDITIONAL(NAME, CONDITION) # NAME是条件名称,CONDITION是shell语句,若条件成立该shell语句应当返回真,常用例子如下:

AC_CHECK_HEADER([bar.h], [use_bar=yes]) AM_CONDITIONAL([WANT_BAR], [test “$use_bar” = yes])

然后,在Makefile.am中,可以对条件进行判断,如:

bin_PROGRAMS = foo if WANT_BAR bin_PROGRAMS += bar endif foo_SOURCES = foo.c bar_SOURCES = bar.c

建议

尽量使用-Wall -Werror
Keep Your Setup Simple
Do not lie to Automake

autoreconf

如果修改项目后,make失败了,重新生成一遍配置文件:
autoreconf —install
如果上面的命令也失败了,试试加—force:
autoreconf —install —force
如果还不行,再试试:
make -k maintainer-clean
autoreconf —install —force

没有必要的话不要运行以上命令,每个命令都会导致项目下次编译花费更长时间。