从 autoreconf -i 看懂 Autoconf:以 Lely CANopen 交叉编译为例
本文面向第一次接触 Autotools / Autoconf 的读者。目标不是背命令,而是看懂:
autoreconf -i为什么要执行、从哪个文件开始、会生成什么文件、这些文件又怎样参与 Lely CANopen 的交叉编译。
@[toc]
先给结论
autoreconf -i 的作用是:根据源码中的 Autotools 描述文件,生成后续构建需要的 configure 脚本、Makefile.in 模板、config.h.in 模板和一批辅助脚本。
它不是编译命令,也不会生成 ARM/aarch64 目标程序。真正编译 Lely 的动作发生在后面的 make 阶段。
完整链路如下:
1 | flowchart TD |
一句话记忆:
1 | configure.ac + Makefile.am |
为什么会有 Autoconf
C/C++ 项目在不同平台上构建时,不能只靠一份固定 Makefile。原因包括:
- 编译器名字可能不同,例如
gcc、clang、aarch64-poky-linux-gcc。 - 系统头文件可能不同,例如 Linux 下才有 SocketCAN 相关头文件。
- 库的名字和链接参数可能不同,例如
pthread、rt。 - 目标平台可能不是当前主机,例如 x86 Ubuntu 上交叉编译到 ARM64 Linux。
- 项目可能要按配置开启或关闭功能,例如 Python、Cython、测试、共享库、静态库。
GNU Autoconf 官方说明:Autoconf 是一套 M4 宏,用来生成可以自动配置源码包的 shell 脚本;这些脚本会根据不同类 Unix 系统的特性调整构建配置。参考来源:GNU Autoconf introduction。
所以 Autoconf 的核心工作不是“编译”,而是生成并运行一个配置脚本:
1 | Autoconf 输入:configure.ac |
autoreconf -i 不是一个工具在单干
autoreconf 可以理解成 Autotools 的总调度器。它会按照项目需要调用多个工具。
GNU Automake 手册说明,autoreconf 会以正确顺序调用 autoconf、automake 和相关命令;核心分工是:Autoconf 根据 configure.ac 生成 configure,Automake 根据 Makefile.am 和 configure.ac 生成 Makefile.in。参考来源:GNU Automake: Creating amhello。
在 Lely 这类项目中,可以把 autoreconf -i 简化理解成:
1 | flowchart LR |
各工具职责如下:
| 工具 | 输入 | 输出 | 作用 |
|---|---|---|---|
aclocal |
configure.ac、m4/*.m4、系统宏 |
aclocal.m4 |
收集宏定义,供 Autoconf 使用 |
libtoolize |
configure.ac 中的 Libtool 宏 |
ltmain.sh、m4/libtool*.m4 |
准备动态库/静态库构建支持 |
autoheader |
configure.ac |
config.h.in |
生成配置头文件模板 |
autoconf |
configure.ac、aclocal.m4 |
configure |
生成配置脚本 |
automake |
Makefile.am、configure.ac |
Makefile.in |
生成 Makefile 模板 |
-i 是什么作用
-i 是 --install 的短写。
它的作用是:发现项目缺少标准辅助文件时,自动复制这些文件到源码树中。
GNU Autoconf 手册说明,autoreconf --install 会安装缺失的标准辅助文件;默认是复制文件,也可以配合 --symlink 改成符号链接。它还可能触发 automake --add-missing、libtoolize 等动作。参考来源:GNU Autoconf: autoreconf invocation。
典型辅助文件包括:
1 | compile |
这些文件大多不是项目业务源码,而是构建系统为了跨平台、依赖跟踪、测试和库构建而使用的脚本或宏文件。
什么时候需要执行 autoreconf -i
通常在这些情况下需要执行:
| 场景 | 是否需要 | 原因 |
|---|---|---|
| 从 Git 仓库克隆 Lely 源码后首次构建 | 需要 | 源码树可能没有现成 configure,需要先生成 |
修改了 configure.ac |
需要 | configure 由它生成,入口变了要重新生成 |
修改了某个 Makefile.am |
需要 | Makefile.in 由它生成,模板变了要重新生成 |
| 更新了 Autoconf / Automake / Libtool 版本 | 建议 | 生成文件可能需要按新工具刷新 |
只是重新执行 make |
通常不需要 | 已有 Makefile 时,直接编译即可 |
只是换一个 --host 重新配置 |
不一定需要 | 一般重新跑 ../configure --host=... 即可 |
Lely 仓库说明:项目使用 GNU Build System,即 configure、make、make install;初次 clone 或下载源码后,需要在项目根目录运行 autoreconf -i 生成 configure 脚本,这一步在 configure.ac 或 Makefile.am 变化时才需要重复。参考来源:Lely core README。
补充:M4 宏到底是什么
前面多次提到 m4/*.m4,这里单独解释。先给结论:M4 是一个文本宏处理器;.m4 文件就是给 Autoconf 使用的宏定义文件。
GNU M4 手册对 M4 的定义是:M4 会把输入复制到输出,同时展开遇到的宏;宏可以是内置宏,也可以是用户自定义宏,并且可以带参数。参考来源:GNU M4 manual。
可以先把它理解成“比 C 预处理器更通用的文本展开器”:
1 | 输入文本里写:某个宏名(参数1, 参数2) |
在 Autoconf 体系里,M4 的输出通常不是 C 代码,而是 shell 脚本片段。例如:
1 | AC_CHECK_HEADER([linux/can.h]) |
这不是 shell 命令本身,而是一个 Autoconf 宏调用。Autoconf 会借助 M4 把它展开成一段 shell 检测逻辑,用来判断目标构建环境里是否有 linux/can.h 这个头文件。
所以关系是:
1 | flowchart LR |
M4 宏和 C 宏有什么区别
| 对比项 | C 预处理宏 | M4 / Autoconf 宏 |
|---|---|---|
| 常见文件 | .h、.c、.cpp |
.m4、configure.ac |
| 处理工具 | C/C++ 预处理器 | m4 / autoconf / aclocal |
| 输入 | C/C++ 源码 | Autoconf 配置描述文本 |
| 输出 | 预处理后的 C/C++ 源码 | configure shell 脚本、aclocal.m4 等 |
| 用途 | 条件编译、常量、代码替换 | 生成平台检测逻辑、生成构建脚本 |
一句话:C 宏服务于编译源码;M4 宏服务于生成构建脚本。
为什么 Lely 的 m4/ 目录里有这些 .m4 文件
你看到的目录内容类似:
1 | -rw-rw-r-- 1 <USER> <GROUP> 1415 <DATE> ax_check_python.m4 |
这些文件大体分两类:
| 文件类型 | 示例 | 来源 | 作用 |
|---|---|---|---|
| 项目自带 / 第三方 Autoconf 宏 | ax_check_python.m4、ax_pthread.m4、ax_cxx_compile_stdcxx.m4 |
Lely 仓库中已有,或来自 Autoconf Archive / 项目维护者 | 给 configure.ac 提供额外检测能力 |
| Libtool 宏 | libtool.m4、ltoptions.m4、ltsugar.m4、ltversion.m4、lt~obsolete.m4 |
libtoolize 从系统 Libtool 安装目录复制 |
支持生成跨平台库构建逻辑 |
Lely 的 configure.ac 中指定了:
1 | AC_CONFIG_MACRO_DIR([m4]) |
这表示:项目本地的 Autoconf 宏目录是 m4/。 Autoconf/Automake/Libtool 在处理这个项目时,会把这个目录作为项目宏目录使用。Lely 仓库的 m4/ 目录中列出了 ax_check_python.m4、ax_code_coverage.m4、ax_cxx_compile_stdcxx.m4、ax_pthread.m4 和 ax_valgrind_check.m4 等文件。参考来源:lely-core/m4。
这些 .m4 文件是怎么“生成”或“复制”出来的
这里要区分三件事:
1 | 项目已有的 .m4 文件 |
1. ax_*.m4:通常不是本次 autoreconf -i 临时生成的
像这些文件:
1 | ax_check_python.m4 |
一般属于 Autoconf 扩展宏。
它们的名字以 AX_ 开头,常见来源有两种:
- 项目维护者自己写的宏。
- 从 GNU Autoconf Archive 复制进项目的宏。
GNU Autoconf Archive 是一个收集大量可复用 Autoconf 宏的项目;其仓库说明它收集了 500 多个由社区贡献的 GNU Autoconf 宏。参考来源:GNU Autoconf Archive mirror。
在 Lely 里,ax_check_python.m4 是 Lely 自己维护的宏文件,文件说明中写明它定义 AX_CHECK_PYTHON(MAJOR-VERSION, ...),用于检查指定 Python 主版本是否可用,并设置 PYTHON2 或 PYTHON3 这类输出变量。参考来源:lely-core/m4/ax_check_python.m4。
ax_pthread.m4 这类宏的典型作用是检测 POSIX threads。GNU Autoconf Archive 对 AX_PTHREAD 的说明是:它会判断如何构建使用 POSIX threads 的 C 程序,并设置 PTHREAD_LIBS、PTHREAD_CFLAGS 等输出变量。参考来源:Autoconf Archive: AX_PTHREAD。
2. libtool.m4 / lt*.m4:通常是 libtoolize 复制进来的
你看到的这些文件:
1 | libtool.m4 |
来自 GNU Libtool。
Lely 的 configure.ac 里有:
1 | LT_INIT([win32-dll]) |
这个宏表示项目启用 Libtool 支持。autoreconf -i 看到项目使用 Libtool 后,会调用 libtoolize。libtoolize 会把 Libtool 所需文件复制到项目中,例如:
1 | ltmain.sh |
GNU Libtool 手册说明,Libtool 用一个通用接口隐藏不同平台构建共享库和静态库时的差异。参考来源:GNU Libtool manual。
所以这几个文件不是 Lely 业务代码,也不是编译产物,而是 Libtool 的宏和脚本支持文件。
3. aclocal.m4:这是 aclocal 汇总生成的
m4/*.m4 是单个宏文件,而:
1 | aclocal.m4 |
是 aclocal 生成的宏汇总文件。
处理关系如下:
1 | flowchart TD |
因此可以这样记:
1 | m4/*.m4 是宏来源文件 |
libtoolize 又是哪来的
libtoolize 是 GNU Libtool 软件包提供的命令,不是 Lely 自己写的命令。
可以用下面命令确认它来自哪里:
1 | which libtoolize |
常见位置类似:
1 | /usr/bin/libtoolize |
它通常由系统包管理器安装,例如 Ubuntu/Debian 系统中常由 libtool 包提供。
它被调用的条件通常是:
- 项目
configure.ac中使用了LT_INIT等 Libtool 宏。 - 执行
autoreconf -i。 autoreconf判断需要初始化/刷新 Libtool 支持文件。
Lely 中正是因为 configure.ac 里有 LT_INIT([win32-dll]),并且设置了 AC_CONFIG_MACRO_DIR([m4]),所以 libtoolize 会把 Libtool 的宏复制到 m4/,把 ltmain.sh 复制到源码根目录。
这些文件逐个是干嘛的
| 文件 | 类别 | 主要作用 | 在 Lely 构建中的意义 |
|---|---|---|---|
ax_check_python.m4 |
Lely 自定义 AX 宏 | 检查指定 Python 主版本,设置 Python 解释器变量 | 支持 Lely 配置 Python 相关工具;交叉编译时可用 --disable-python 降低复杂度 |
ax_code_coverage.m4 |
AX 扩展宏 | 增加代码覆盖率相关配置和 make 规则 | 主要服务测试/覆盖率,不是目标板运行 coctl 的核心依赖 |
ax_cxx_compile_stdcxx.m4 |
AX 扩展宏 | 检查 C++ 标准支持,必要时添加 -std=... |
Lely 有 C++ 接口,例如 liblely-coapp,所以需要检测 C++ 编译能力 |
ax_pthread.m4 |
AX 扩展宏 | 检查 pthread 编译/链接参数 | Lely 在 Linux 下通常需要线程支持 |
ax_valgrind_check.m4 |
AX 扩展宏 | 增加 Valgrind 测试相关规则 | 主要用于开发测试,不是部署到目标板的核心依赖 |
libtool.m4 |
Libtool 宏 | 定义 Libtool 主宏,例如 LT_INIT 相关展开逻辑 |
支持生成跨平台库构建逻辑 |
ltoptions.m4 |
Libtool 宏 | 处理 Libtool 配置选项 | 支持 --enable-shared、--enable-static 等库构建选项 |
ltsugar.m4 |
Libtool 宏 | Libtool 内部辅助宏 | 给 Libtool 宏展开使用,通常不手工改 |
ltversion.m4 |
Libtool 宏 | 记录 Libtool 宏版本信息 | 用于版本一致性检查 |
lt~obsolete.m4 |
Libtool 宏 | 兼容旧 Libtool 宏 | 避免旧宏调用导致配置失败 |
这些 .m4 文件在 Lely 中怎么被用起来
结合 Lely,完整链路是:
1 | flowchart TD |
分阶段看:
| 阶段 | 读哪些文件 | 生成什么 | 说明 |
|---|---|---|---|
libtoolize |
configure.ac |
ltmain.sh、m4/libtool.m4、m4/lt*.m4 |
因为 Lely 使用 LT_INIT |
aclocal |
configure.ac、m4/*.m4 |
aclocal.m4 |
把宏定义汇总起来 |
autoconf |
configure.ac、aclocal.m4 |
configure |
把 M4 宏展开成 shell 脚本 |
automake |
Makefile.am、configure.ac |
Makefile.in |
生成 Makefile 模板 |
../configure |
configure、Makefile.in、config.h.in |
Makefile、config.h、libtool、.pc |
按目标平台生成最终构建文件 |
为什么有些 .m4 看起来像“生成的”
判断一个 .m4 是项目自带还是工具复制,不能只看时间戳。你执行 autoreconf -i 后,很多文件的修改时间会变成同一时间,所以容易误以为都是“生成”的。
更准确的判断方法是:
1 | # 如果是 Git 仓库,先看文件是否被版本管理 |
常见结论:
| 现象 | 更可能的原因 |
|---|---|
ax_*.m4 本来就在仓库里 |
项目维护者把宏随源码一起提交,保证别人构建时不依赖系统额外宏包 |
libtool.m4、lt*.m4 执行后出现或刷新 |
libtoolize 从系统 Libtool 安装目录复制 |
aclocal.m4 执行后生成或刷新 |
aclocal 汇总所有需要的宏 |
configure 执行后生成或刷新 |
autoconf 根据 configure.ac 和宏定义生成 |
对你这批文件的直接结论
你列出来的文件可以这样理解:
1 | m4/ax_*.m4 |
这些文件最终只服务于一件事:让 autoconf 能够生成正确的 configure,再让 configure 生成适合当前目标平台的构建文件。
Lely 中的入口文件是 configure.ac
在 Lely 源码根目录中,Autotools 的主入口是:
1 | <LELY_SRC>/configure.ac |
Lely 的 configure.ac 中包含这些关键宏:
1 | AC_PREREQ([2.69]) |
这些宏决定了 Lely 的配置系统要做什么。Lely 当前公开仓库中的 configure.ac 确实包含 AC_INIT、AC_CONFIG_MACRO_DIR([m4])、AC_CANONICAL_TARGET、AM_INIT_AUTOMAKE、AC_PROG_CXX 和 LT_INIT([win32-dll]) 等宏。参考来源:lely-core/configure.ac。
关键宏解释
| 宏 | 在 Lely 中的作用 |
|---|---|
AC_INIT |
定义包名、版本等基础信息 |
AC_CONFIG_MACRO_DIR([m4]) |
指定额外宏目录是 m4/ |
AC_CANONICAL_TARGET |
启用平台三元组识别,例如 <TARGET_TRIPLET> |
AM_INIT_AUTOMAKE |
启用 Automake,根据 Makefile.am 生成 Makefile.in |
AC_PROG_CC_STDC |
检查 C 编译器能力 |
AC_PROG_CXX |
检查 C++ 编译器能力 |
LT_INIT |
启用 Libtool,用于构建库文件 |
AC_OUTPUT |
触发输出文件生成,例如 Makefile、config.status 等 |
GNU Autoconf 手册说明,每个 configure 脚本必须调用 AC_INIT,另一个必需宏是 AC_OUTPUT;AC_OUTPUT 会生成并运行 config.status,由它创建 Makefile 和其他配置输出文件。参考来源:GNU Autoconf: Initializing configure、GNU Autoconf: Outputting files。
Lely 中还有哪些输入文件
除了 configure.ac,Lely 还会使用多个 Makefile.am。这些文件是 Automake 的输入。
GNU Automake 手册说明,Automake 会从 Makefile.am 自动生成 Makefile.in;Makefile.am 本质上是一系列 make 变量定义,偶尔带有规则。参考来源:GNU Automake introduction。
在 Lely 中,常见输入关系如下:
1 | <LELY_SRC>/configure.ac |
这些输入文件经过 autoreconf -i 后,会产生对应的模板文件:
1 | <LELY_SRC>/configure |
Lely 中执行 autoreconf -i 的位置
在 Lely 源码根目录执行:
1 | cd <LELY_SRC> |
注意:必须在有 configure.ac 的目录执行。不要在 build 目录里执行。
正确顺序:
1 | # 1. 进入源码目录 |
Lely 官方交叉编译文档给出的流程也是:clone 源码后进入 lely-core,执行 autoreconf -i,创建 build 目录,然后执行 ../configure --host=HOST --disable-python,最后 make 和 make install。参考来源:Lely CANopen cross-compilation。
autoreconf -i 之后文件怎样流动
下面这张图从“文件输入输出”的角度看整个流程:
1 | flowchart TD |
你看到的输出是什么意思
示例输出:
1 | libtoolize: putting auxiliary files in '.'. |
这些不是错误,是正常的“补齐构建系统文件”日志。
| 输出 | 含义 |
|---|---|
copying file './ltmain.sh' |
Lely 使用 Libtool,需要复制 Libtool 主脚本 |
copying file 'm4/libtool.m4' |
把 Libtool 的 M4 宏放到 m4/ 目录 |
installing './compile' |
安装通用编译包装脚本 |
installing './config.guess' |
安装平台识别脚本,用于猜测系统类型 |
installing './config.sub' |
安装平台三元组规范化脚本 |
installing './install-sh' |
安装通用安装脚本 |
installing './missing' |
安装缺失工具提示脚本 |
installing './tap-driver.sh' |
安装 TAP 测试驱动脚本 |
installing './depcomp' |
安装依赖跟踪脚本,用于头文件依赖分析 |
installing './test-driver' |
安装 Automake 测试驱动脚本 |
如果这一步失败,通常后面不会有可执行的 configure。如果这一步成功,下一步才是执行 ../configure。
../configure --host=... 又做什么
autoreconf -i 生成的是通用模板,尚未绑定你的交叉编译目标。真正指定目标架构的是:
1 | ../configure --host=<TARGET_TRIPLET> ... |
它会做几类事情:
- 检查交叉编译器是否存在。
- 判断目标平台类型。
- 根据
--enable-*/--disable-*参数决定功能开关。 - 根据
Makefile.in生成当前构建目录下的Makefile。 - 根据
config.h.in生成config.h。 - 生成
libtool,用于后续构建库文件。 - 生成
pkgconfig/*.pc,供后续应用通过pkg-config链接 Lely。
输出关系如下:
1 | flowchart LR |
这里容易混淆的是:
| 文件 | 谁生成 | 什么时候生成 | 作用 |
|---|---|---|---|
Makefile.am |
开发者写 | 源码中已有 | Automake 输入 |
Makefile.in |
automake 生成 |
autoreconf -i 阶段 |
Makefile 模板 |
Makefile |
configure 生成 |
../configure 阶段 |
make 真正读取的构建文件 |
config.h.in |
autoheader 生成 |
autoreconf -i 阶段 |
配置头模板 |
config.h |
configure 生成 |
../configure 阶段 |
C/C++ 源码实际包含的配置头 |
make 阶段才是真正编译
执行:
1 | make -j"$(nproc)" |
这一步会读取 <BUILD_DIR>/Makefile,然后调用交叉编译器,例如:
1 | <TARGET_TRIPLET>-gcc |
生成 Lely 的库和工具。典型产物包括:
1 | liblely-*.so |
所以如果你要判断是否真的编译到了目标架构,应检查 make 之后的产物,而不是检查 autoreconf -i 的输出。
例如:
1 | file <BUILD_DIR>/stage/<INSTALL_PREFIX>/bin/coctl |
期望看到类似:
1 | ELF 64-bit LSB executable, ARM aarch64 |
make install DESTDIR=... 是收集产物
执行:
1 | make install DESTDIR="${PWD}/stage" |
它会把编译出的文件安装到一个临时目录,而不是直接写入系统目录。
可以理解成:
1 | --prefix=<INSTALL_PREFIX> |
典型结构:
1 | <BUILD_DIR>/stage/<INSTALL_PREFIX>/ |
这样做的好处是不会污染构建主机的 /usr 或 /usr/local,也便于后续打包部署到目标板。
在 Lely 交叉编译中,推荐按这个顺序执行
下面是脱敏后的完整命令模板:
1 | # 1. 准备环境变量 |
如果 <TARGET_TRIPLET> 是 ARM64 Linux 工具链,可替换为类似:
1 | aarch64-poky-linux |
但本文不写入真实 SDK 路径和真实部署目录。
常见误解
误解 1:autoreconf -i 是交叉编译
不是。autoreconf -i 只是生成构建系统。它不会调用目标交叉编译器,也不会产生 ARM ELF。
真正和目标平台有关的是:
1 | ../configure --host=<TARGET_TRIPLET> |
误解 2:configure.ac 会直接被 make 使用
不会。make 读的是 Makefile。
关系是:
1 | configure.ac → configure → Makefile → make |
误解 3:Makefile.am 和 Makefile 是一回事
不是。
1 | Makefile.am 人写的 Automake 输入 |
误解 4:执行一次 autoreconf -i 后永远不用再执行
也不是。修改 configure.ac 或 Makefile.am 后,应该重新执行。更换交叉编译目标时,通常只需要重新建构建目录并执行 ../configure --host=...,不一定要重新 autoreconf -i。
误解 5:看到 installing 就是安装到了系统
不是。autoreconf -i 输出里的 installing './config.sub' 这类信息,是把构建辅助脚本复制到源码树,不是把 Lely 安装到系统。
真正安装 Lely 产物的是:
1 | make install DESTDIR="${PWD}/stage" |
排查时优先看这些文件
当 Lely 构建失败时,不同阶段看不同文件。
| 阶段 | 关注文件 | 常见问题 |
|---|---|---|
autoreconf -i |
configure.ac、Makefile.am、m4/*.m4 |
宏缺失、Autotools 版本不足、Libtool 文件缺失 |
../configure |
config.log |
编译器找不到、头文件检测失败、库检测失败 |
make |
编译日志、对应 Makefile |
C/C++ 编译错误、链接错误、目标库缺失 |
make install |
stage/ 目录 |
安装路径不符合预期、.pc 文件缺失、工具未安装 |
| 运行目标程序 | file、ldd、LD_LIBRARY_PATH |
架构错误、动态库找不到、运行环境缺失 |
建议检查顺序:
1 | # 检查 configure 是否生成 |
最小心智模型
如果只记一张图,记这张:
1 | flowchart TD |
对应到 Lely:
1 | Lely 源码目录 |










