automake 命令 #
automake
是 GNU 构建系统的一部分,用于从Makefile.am
文件自动生成符合 GNU 编码标准的Makefile.in
文件。它与autoconf
配合使用,简化了跨平台软件包的构建系统创建过程。
语法 #
automake [选项]... [Makefile]...
常用选项 #
选项 | 描述 |
---|---|
--help |
显示帮助信息 |
--version |
显示版本信息 |
-a, --add-missing |
添加缺少的标准文件到包中 |
-c, --copy |
复制而不是创建符号链接 |
-f, --force-missing |
强制更新标准文件 |
-i, --ignore-deps |
禁用依赖项跟踪 |
-v, --verbose |
启用详细输出 |
-W 类别 |
报告指定类别的警告 |
--foreign |
放宽 GNU 标准要求 |
--gnits |
严格遵守 GNU 标准 |
--gnu |
遵守 GNU 标准(默认) |
--include-deps |
包含自动生成的依赖项 |
--no-force |
不强制更新生成的文件 |
--output-dir=目录 |
将文件放在指定目录中 |
--strictness=类型 |
设置严格程度(gnu, gnits, foreign) |
基本用法 #
1. 生成 Makefile.in 文件 #
automake
这将处理当前目录中的Makefile.am
文件,并生成Makefile.in
文件。
2. 添加缺少的标准文件 #
automake --add-missing
这将添加缺少的标准文件(如install-sh
、missing
等)到项目中。
3. 使用外国模式(较少限制) #
automake --foreign
这将放宽 GNU 标准要求,适用于非 GNU 项目。
4. 强制更新所有文件 #
automake --force-missing --add-missing
5. 为特定 Makefile.am 生成 Makefile.in #
automake src/Makefile
Makefile.am 基本结构 #
Makefile.am
是 automake 的主要输入文件,它使用一种增强的 Makefile 语法来描述项目的构建规则。以下是一个基本的Makefile.am
文件结构:
# 定义要构建的程序
bin_PROGRAMS = myprogram
# 定义程序的源文件
myprogram_SOURCES = main.c utils.c
# 定义编译标志
myprogram_CFLAGS = -Wall -O2
# 定义链接标志
myprogram_LDFLAGS = -lm
# 定义要安装的头文件
include_HEADERS = myprogram.h
# 定义要安装的数据文件
data_DATA = config.data
# 定义要安装的文档
doc_DATA = README.md
# 定义子目录
SUBDIRS = src lib doc
常用变量和前缀 #
1. 主要安装目录变量 #
变量前缀 | 安装目录 | 描述 |
---|---|---|
bin_ |
$(bindir) |
可执行程序 |
sbin_ |
$(sbindir) |
系统管理员可执行程序 |
lib_ |
$(libdir) |
库文件 |
include_ |
$(includedir) |
头文件 |
data_ |
$(datadir) |
架构无关的数据文件 |
pkgdata_ |
$(pkgdatadir) |
特定于包的数据文件 |
sysconf_ |
$(sysconfdir) |
配置文件 |
sharedstate_ |
$(sharedstatedir) |
修改的架构无关数据 |
localstate_ |
$(localstatedir) |
修改的架构无关数据(本地) |
pkglib_ |
$(pkglibdir) |
特定于包的库文件 |
pkginclude_ |
$(pkgincludedir) |
特定于包的头文件 |
pkgsysconf_ |
$(pkgsysconfdir) |
特定于包的配置文件 |
doc_ |
$(docdir) |
文档文件 |
info_ |
$(infodir) |
Info 文档 |
html_ |
$(htmldir) |
HTML 文档 |
dvi_ |
$(dvidir) |
DVI 文档 |
pdf_ |
$(pdfdir) |
PDF 文档 |
ps_ |
$(psdir) |
PostScript 文档 |
man_ |
$(mandir) |
Man 页面 |
noinst_ |
不安装 | 不安装但会构建 |
check_ |
不安装 | 仅在运行测试时构建 |
2. 主要基本类型 #
类型 | 描述 |
---|---|
PROGRAMS |
可执行程序 |
LIBRARIES |
静态库 |
LTLIBRARIES |
Libtool 库(静态或共享) |
HEADERS |
头文件 |
SCRIPTS |
脚本 |
DATA |
数据文件 |
MANS |
Man 页面 |
TEXINFOS |
Texinfo 文档 |
3. 目标特定变量 #
变量后缀 | 描述 |
---|---|
_SOURCES |
源文件列表 |
_CFLAGS |
C 编译标志 |
_CXXFLAGS |
C++编译标志 |
_CPPFLAGS |
预处理器标志 |
_LDFLAGS |
链接器标志 |
_LDADD |
链接时添加的对象文件或库 |
_LIBADD |
添加到库的对象文件或库 |
_DEPENDENCIES |
额外的依赖项 |
4. 全局变量 #
变量 | 描述 |
---|---|
SUBDIRS |
要递归处理的子目录 |
DIST_SUBDIRS |
分发时包含的子目录 |
EXTRA_DIST |
额外的分发文件 |
CLEANFILES |
make clean 时要删除的文件 |
DISTCLEANFILES |
make distclean 时要删除的文件 |
MAINTAINERCLEANFILES |
make maintainer-clean 时要删除的文件 |
AM_CFLAGS |
全局 C 编译标志 |
AM_CXXFLAGS |
全局 C++编译标志 |
AM_CPPFLAGS |
全局预处理器标志 |
AM_LDFLAGS |
全局链接器标志 |
ACLOCAL_AMFLAGS |
aclocal 的标志 |
AUTOMAKE_OPTIONS |
automake 选项 |
高级用法 #
1. 构建和安装程序 #
# 定义要构建的程序
bin_PROGRAMS = program1 program2
# 定义每个程序的源文件
program1_SOURCES = main1.c utils.c
program2_SOURCES = main2.c utils.c
# 定义编译标志
program1_CFLAGS = -DPROGRAM1 -Wall
program2_CFLAGS = -DPROGRAM2 -Wall
# 定义链接标志
program1_LDADD = -lm
program2_LDADD = -lm -lpthread
2. 构建和安装库 #
# 静态库
lib_LIBRARIES = libstatic.a
libstatic_a_SOURCES = lib1.c lib2.c
# Libtool库(可以是静态或共享的)
lib_LTLIBRARIES = libshared.la
libshared_la_SOURCES = lib1.c lib2.c
libshared_la_LDFLAGS = -version-info 1:0:0
3. 条件构建 #
在configure.ac
中:
AM_CONDITIONAL([ENABLE_FEATURE], [test "x$enable_feature" = xyes])
在Makefile.am
中:
if ENABLE_FEATURE
bin_PROGRAMS = feature_program
feature_program_SOURCES = feature.c
else
bin_PROGRAMS = basic_program
basic_program_SOURCES = basic.c
endif
4. 构建多个目录 #
顶层Makefile.am
:
SUBDIRS = lib src doc tests
lib/Makefile.am
:
lib_LTLIBRARIES = libmylib.la
libmylib_la_SOURCES = lib1.c lib2.c
src/Makefile.am
:
bin_PROGRAMS = myprogram
myprogram_SOURCES = main.c
myprogram_LDADD = ../lib/libmylib.la
5. 安装数据文件 #
# 安装配置文件
sysconf_DATA = config.conf
# 安装特定于包的数据文件
pkgdata_DATA = data1.dat data2.dat
# 安装图标
icondir = $(datadir)/icons
icon_DATA = app.png
6. 自定义安装目录 #
# 定义自定义目录
myappdir = $(datadir)/myapp
myapp_DATA = data.xml
# 定义自定义脚本目录
myappscriptdir = $(libexecdir)/myapp
myappscript_SCRIPTS = helper.sh
7. 使用构建钩子 #
# 在所有目标之前运行
all-local:
@echo "Building all targets"
# 在安装之前运行
install-data-local:
$(MKDIR_P) $(DESTDIR)$(datadir)/myapp/extra
$(INSTALL_DATA) extra.dat $(DESTDIR)$(datadir)/myapp/extra/
# 在卸载之前运行
uninstall-local:
rm -f $(DESTDIR)$(datadir)/myapp/extra/extra.dat
-rmdir $(DESTDIR)$(datadir)/myapp/extra
8. 分发额外文件 #
# 添加额外文件到分发包
EXTRA_DIST = README.md LICENSE contrib scripts
# 不分发某些文件
dist-hook:
rm -rf $(distdir)/contrib/private
实用示例 #
1. 简单的可执行程序 #
bin_PROGRAMS = hello
hello_SOURCES = hello.c
hello_CFLAGS = -Wall -O2
2. 多个可执行程序 #
bin_PROGRAMS = prog1 prog2
prog1_SOURCES = main1.c common.c common.h
prog2_SOURCES = main2.c common.c common.h
3. 构建库和使用它的程序 #
# 库
lib_LTLIBRARIES = libmylib.la
libmylib_la_SOURCES = lib.c lib.h
libmylib_la_LDFLAGS = -version-info 1:0:0
# 程序
bin_PROGRAMS = myprog
myprog_SOURCES = main.c
myprog_LDADD = libmylib.la
4. 安装头文件 #
# 公共头文件
include_HEADERS = public.h
# 特定于包的头文件
pkginclude_HEADERS = mylib.h mylib_utils.h
5. 安装数据和配置文件 #
# 配置文件
sysconf_DATA = myapp.conf
# 数据文件
pkgdata_DATA = data/*.xml
# 自定义目录中的数据
resourcesdir = $(datadir)/myapp/resources
resources_DATA = resources/*.png resources/*.svg
6. 构建和运行测试 #
# 测试程序
check_PROGRAMS = test1 test2
test1_SOURCES = test1.c
test2_SOURCES = test2.c
# 测试脚本
TESTS = test1 test2 test3.sh
7. 使用条件编译 #
if ENABLE_GUI
bin_PROGRAMS = myapp-gui
myapp_gui_SOURCES = gui.c common.c
myapp_gui_CFLAGS = $(GTK_CFLAGS)
myapp_gui_LDADD = $(GTK_LIBS)
endif
if ENABLE_CLI
bin_PROGRAMS += myapp-cli
myapp_cli_SOURCES = cli.c common.c
endif
8. 生成源文件 #
BUILT_SOURCES = generated.h
generated.h: generator.sh data.txt
./generator.sh data.txt > $@
CLEANFILES = generated.h
9. 安装 man 页面 #
# Man页面
man1_MANS = myapp.1
man5_MANS = myapp.conf.5
10. 安装多语言支持 #
# 国际化支持
SUBDIRS = po
EXTRA_DIST = intl
# 设置翻译域
PACKAGE = myapp
localedir = $(datadir)/locale
DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
常见问题排查 #
1. 缺少标准文件 #
错误:
Makefile.am:1: error: required file './NEWS' not found
解决方案:
# 创建缺少的文件
touch NEWS README AUTHORS ChangeLog
# 或者使用--foreign选项
automake --foreign
2. 未定义的宏 #
错误:
Makefile.am:5: error: variable 'noinst_LIBRARIES' is defined but no program or library has that name
解决方案:
# 确保定义了库的源文件
noinst_LIBRARIES = libtest.a
libtest_a_SOURCES = test.c
3. 源文件找不到 #
错误:
make: *** No rule to make target 'missing_file.c', needed by 'myprogram-missing_file.o'. Stop.
解决方案:
# 确保所有源文件都存在并正确列出
myprogram_SOURCES = existing_file.c
4. 子目录问题 #
错误:
automake: error: cannot open < src/Makefile.am: No such file or directory
解决方案:
# 确保每个列在SUBDIRS中的子目录都有Makefile.am文件
touch src/Makefile.am
5. 版本不匹配 #
错误:
warning: The 'AM_INIT_AUTOMAKE' macro requires version 1.14, but have 1.15.1.
解决方案:
# 更新AM_INIT_AUTOMAKE调用中的版本号
AM_INIT_AUTOMAKE([1.15])
与其他构建系统的比较 #
构建系统 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Automake | 成熟、广泛支持、标准化 | 语法复杂、学习曲线陡峭 | GNU 项目、需要广泛兼容性的 Unix/Linux 项目 |
CMake | 跨平台、现代语法、IDE 支持 | 不如 Automake 在 Unix 系统上灵活 | 跨平台项目、需要 IDE 集成 |
Meson | 快速、简单、现代 | 相对较新、不如 Automake 成熟 | 新项目、注重构建速度 |
SCons | Python 语法、灵活 | 构建速度较慢 | Python 项目、需要高度定制的构建系统 |
提示和技巧 #
1. 使用 autoreconf 简化流程 #
# 一步完成所有autotools步骤
autoreconf -i
2. 使用并行构建 #
在configure.ac
中:
# 启用并行构建支持
AM_INIT_AUTOMAKE([parallel-tests])
3. 使用静默规则 #
在configure.ac
中:
# 使用静默规则
AM_SILENT_RULES([yes])
这将使构建输出更简洁,只显示正在执行的操作,而不是完整的命令行。
4. 使用通配符 #
# 使用通配符匹配源文件
myprogram_SOURCES = $(wildcard *.c)
5. 使用变量替换 #
# 从C文件生成对象文件列表
SOURCES = file1.c file2.c file3.c
OBJECTS = $(SOURCES:.c=.o)
6. 使用自定义规则 #
# 自定义规则生成文件
%.c: %.y
$(YACC) $(YFLAGS) -o $@ $<
# 确保自动生成的文件被清理
CLEANFILES = $(wildcard *.tab.c)
7. 使用递归变量和即时变量 #
# 递归变量(在使用时展开)
RECURSIVE = $(SOURCES:.c=.o)
# 即时变量(在定义时展开)
IMMEDIATE := $(SOURCES:.c=.o)