GNU构建系统和Autotool

news/2024/11/13 20:14:06/

1、前言

经常使用Linux的开发人员或者运维人员,可能对configure->make->make install相当熟悉。事实上,这叫GNU构建系统,利用脚本和make程序在特定平台上构建软件。这种方式成为一种习惯,被广泛使用。本文从用户视角和开发者视角详细说明,这种构建方式的细节,以及开发者如何利用autoconf和automake等工具(autotools)创建兼容GNU构建系统的项目。

为了简化可移植构建的难度,在早期有一套autotools工具帮助程序员构建软件。我们熟知的configure->make->make install三部曲,大多都是基于autotools来构建的。autotools是GNU程序的标准构建系统,所以其实我们经常在使用三部曲。有些程序虽然也是这三部曲,但却不是用autotools实现的,比如nginx的源码就是作者自己编写的构建程序

2、用户视角

用户通过configure->make->make install基于源码安装软件。然而大部分用户可能并不知道这个过程究竟做了些什么。

configure脚本是由软件开发者维护并发布给用户使用的shell脚本。这个脚本的作用是检测系统环境,最终目的是生成Makefile和config.h

make通过读取Makefile文件,开始构建软件。而make install可以将软件安装到需要安装的位置。
在这里插入图片描述
如上图,开发者在分发源码包时,除了源代码(.c .h…),还有许多用以支撑软件构建的文件和工具,其中最重要的文件就是Makefile.in和config.h.in。configure脚本执行成功后,将为每一个*.in文件处理成对应的非*.in文件。

大部分情况只生成Makefile和config.h,因为Makefile用于make程序识别并构建软件,而config.h中定义的宏,有助于软件通过预编译来改变自身的代码,以适应目标平台某些特殊性。有些软件在configure阶段,还可以生成其他文件,这完全取决于如软件本身。

configure
当运行configure时,将看到类似如下的系统检查,这些检查的多少取决于软件本身的需要,也就是由软件开发者来定义和编写的。

checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking for C compiler default output file name... a.out
...

一般来说,configure主要检查当前目标平台的程序、库、头文件、函数等的兼容性。这些检查结果将作用于config.h和Makefile文件的生成。从而影响最终的编译。

用户也可以通过给configure配置参数来定制软件需要包含或不需要包含的组件、安装路径等行为。这些参数分为5组,可以通过执行./configure --help来查看,软件提供哪些配置参数:

  • *安装路径相关配置。最常见的是--prefix
  • *程序名配置。例如--program-suffix可用于为生成的程序添加后缀。
  • *跨平台编译。不太常用。
  • *动态库静态库选项。用于控制是否生成某种类型的库文件。
  • 程序组件选项。用于配置程序是否将某种功能编译到程序中,一般形如--with-xxx。这可能是最常用的配置,而且由软件开发者来定义。

(*表示几乎所有软件都支持的配置,因为这些配置是autotool生成的configure脚本默认支持的。)

configure在执行过程中,除了生成Makefile外,还会生成的文件包括但不限于:

  • config.log 日志文件
  • config.cache 缓存,以提高下一次configure的速度,需通过-C来指定才会生成
  • config.status 实际调用编译工具构建软件的shell脚本
    在这里插入图片描述
    如果软件通过libtool构建,还会生成libtool脚本。关于libtool脚本如何生成,请看开发者视角。

configure经常会中途出错,这一般是由于当前平台不具有构建该软件所必需的依赖(库、函数、头文件、程序…)。此时,不要慌张,仔细查看输出,解决这些依赖。

3、开发者视角

开发者除了编写软件本身的代码外,还需要负责生成构建软件所需要文件和工具。当我接触到autotools后,我发现,虽然有工具的帮助,但这件事情依旧十分复杂。

对于开发者而言,要么自己编写构建用的脚本,这往往需要极其扎实的shell能力和平台熟悉度。另一个选择就是部分依赖工具。autoconf和automake就是这样的工具。

autoreconf

为了生成configure脚本和Makefile.in等文件,开发者需要创建并维护一个configure.ac文件(在早期,通常叫configure.in文件,虽然没有区别,但强烈建议使用.ac,因为.in文件往往意味着被configure脚本识别为模板文件并生成直接参与最终构建的文件,configure.in在命名上有歧义),以及一系列的Makefile.amautoreconf程序能够自动按照合理的顺序调用autoconfautomake aclocal等程序。
在这里插入图片描述
configure.ac
configure.ac用于生成configure脚本。autoconf工具用来完成这一步。下面是一个configure.ac的例子:

AC_PREREQ([2.63])
AC_INIT([st], [1.0], [zhoupingtkbjb@163.com])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([src/config.h])AM_INIT_AUTOMAKE([foreign])# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL# Checks for libraries.# Checks for header files.# Checks for typedefs, structures, and compiler characteristics.# Checks for library functions.AC_CONFIG_FILES([Makefilesrc/Makefilesrc/a/Makefilesrc/b/Makefile])
AC_OUTPUT

其中以AC_开头的类似函数调用一样的代码,实际是一些被称为“”的调用。这里的宏与C中的宏概念类似,会被替换展开。m4是一个经典的宏工具,autoconf正是构建在m4之上,可以理解为autoconf预先实现了大量的,用于检测系统可移植性的宏,这些宏在展开后就是大量的shell脚本。所以编写configure.ac需要对这些宏熟练掌握,并且合理调用。有时,甚至可以自己实现自己的宏。

autoscan和configure.scan
可以通过调用autoscan命令得到一个初始化的configure.scan文件,然后重命名为configure.ac后,在此基础上编辑configure.acautoscan会扫描源码,并生成一些通用的宏调用、输入的声明以及输出的声明。尽管autoscan十分方便,但是没人能够在构建之前,就把代码完全写好,因此autoscan通常用于初始化configure.ac

autoheader和config.h
autoheader命令扫描configure.ac中的内容,并确定需要如何生成config.h.in。每当configure.ac有所变化,都可以通过再次执行autoheader更新config.h.in。在configure.ac通过AC_CONFIG_HEADERS([config.h])告诉autoheader应当生成config.h.in的路径。在实际的编译阶段,生成的编译命令会加上-DHAVE_CONFIG_H定义宏,于是在代码中,我们可以通过下面代码安全的引用config.h

/bin/sh ../../libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H ...
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

config.h包含了大量的宏定义,其中包括软件包的名字等信息,程序可以直接使用这些宏;更重要的是,程序可以根据其中的对目标平台的可移植性相关的宏,通过条件编译,动态的调整编译行为。

automake和Makfile.am

手工编写Makefile是一件相当烦琐的事情,而且,如果项目复杂的话,编写难度将越来越大。因而,automake工具应运而生。我们可以编写像下面这样的Makefile.am文件,并依靠automake来生成Makefile.in:

SUBDIRS = a b
bin_PROGRAMS    = st
st_SOURCES        = main.c
st_LDADD        = $(top_builddir)/src/a/liba.la $(top_builddir)/src/b/libb.la 

这里通过SUBDIRS声明了两个子目录,子目录的中的构建需要靠a/Makefile.am和b/Makefile.am来进行,这样多目录组织起来就方便多了。

bin_PROGRAMS声明一个可执行文件目标,
st_SOURCES指定这个目标所依赖的源代码文件。
st_LDADD声明了可执行文件在连接时,需要依赖的Libtool库文件。

automake的出现晚于autoconf,所以automake是作为autoconf的扩展来实现的。通过在configure.ac中声明AM_INIT_AUTOMAKE告诉autoconf需要配置和调用automake

aclocal
上面提到,configure.ac实际是依靠宏展开来得到configure的。因此,能否成功生成取决于,宏定义能否找到。autoconf会从自身安装路径下来寻找事先定义好了宏。然而对于像automake、libtool和gettext等第三方扩展宏,甚至是开发者自行编写的宏。于是,存在这个工具aclocal,将在configure.ac同一目录下生成aclocal.m4,在扫描configure.ac的过程中,将第三方扩展和开发者自己编写的宏定义复制进去。这样,autoconf在遇到不认识的宏时,就会从aclocal.m4中查找。

下面这张图更为详细的展现了整个工具链是如何互相配合的。
在这里插入图片描述
libtool
libtool试图解决不同平台下,库文件的差异。libtool实际是一个shell脚本,实际工作过程中,调用了目标平台的cc编译器和链接器,以及给予合适的命令行参数。libtool可以单独使用,这里只介绍与autotools集成使用相关的内容。

automake支持libtool构建声明。在Makefile.am中,普通的库文件目标写作xxx_LIBRARIES:

noinst_LIBRARIES = liba.a
liba_SOURCES = ao1.c ao2.c ao3.c

而对于一个libtool目标,写作xxx_LTLIBRARIES,并以.la作为后缀声明库文件。

noinst_LTLIBRARIES = liba.la
liba_la_SOURCES = ao1.c ao2.c ao3.c

在configure.ac中需要声明LT_INIT:

...
AM_INIT_AUTOMAKE([foreign])LT_INIT
...

有时,如果需要用到libtool中的某些宏,则推荐将这些宏copy到项目中。首先,通过AC_CONFIG_MACRO_DIR([m4])指定使用m4目录存放第三方宏;然后在最外层的Makefile.am中加入ACLOCAL_AMFLAGS = -I m4

all-in-one
上面讨论了很多关于autoreconf的细节。实际上,如今我们可以直接调用autoreconf --install来自动调用上面提到的所有子命令。这里–install参数试图将辅助的脚本和宏copy到当前项目目录中,下面是执行时的输出:

autoreconf: Entering directory `.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal 
autoreconf: configure.ac: tracing
autoreconf: running: libtoolize --copy
libtoolize: putting auxiliary files in `.'.
libtoolize: copying file `./ltmain.sh'
libtoolize: Consider adding `AC_CONFIG_MACRO_DIR([m4])' to configure.ac and
libtoolize: rerunning libtoolize, to keep the correct libtool macros in-tree.
libtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am.
autoreconf: running: /usr/bin/autoconf
autoreconf: running: /usr/bin/autoheader
autoreconf: running: automake --add-missing --copy --no-force
configure.ac:10: installing `./config.guess'
configure.ac:10: installing `./config.sub'
configure.ac:9: installing `./install-sh'
configure.ac:9: installing `./missing'
src/Makefile.am: installing `./depcomp'
autoreconf: Leaving directory `.'

当我们以--install参数运行时,libtoolize --copy被调用,这将使得ltmain.sh被copy进来;接下来分别执行autoconf和autoheader;automake的参数为--add-missing --copy --no-force,这将使得几个辅助脚本和文件被安装到目录下。

这些辅助文件默认安装在configure.ac同一个目录下,如果你希望用另一个目录来存放他们,可以配置AC_CONFIG_AUX_DIR,例如AC_CONFIG_AUX_DIR([build-aux])将使用build-aux目录来存放辅助文件。

如果不使用–install参数,辅助文件要么不copy,要么以软链的形式创建。推荐使用–install,因为这样,其他软件维护可以避免由于构建工具版本不一致造成问题。

4、辅助文件

一个依靠GNU构建系统开发的软件除了源码之外,还有很多辅助的文件,有些是脚本,有些是文本文件。下面将逐一解释这些文件:

  • aclocal.m4:上面提到了,这个宏定义文件里面包含了第三方的宏定义,用于autoconf展开configure.ac
  • NEWS、README、AUTHORS、ChangeLog:这些文件是GNU软件的标配,不过在项目中不一定需要加入。如果项目中没有这些文件,每次autoreconf会提示缺少文件,不过这并不影响。如果不想看到这些错误提示,可以用AM_INIT_AUTOMAKE([foreign])来配置automake。foreign参数就是告诉automake不要这么较真:)
  • config.guess、config.sub:由automake产生,两个用于目标平台检测的脚本
  • depcomp、install-sh:由automake产生,用于完成编译和安装的脚本
  • missing:由automake产生
  • ltmain.sh:由libtoolize产生,该脚本用于在configure阶段配置生成可运行于目标平台的libtool脚本
  • ylwrap:由automake产生,如果检测构建需要使用lex和yacc,那么会产生这个包装脚本
  • autogen.sh:在早期,autoreconf并不存在,软件开发者往往需要自己编写脚本,按照顺序调用autoconf、autoheader、automake等工具程序。这个文件就是这样的脚本。起这么个名字可能是习惯性的

5、总结

本文总概念上阐述了autotool系列工具是如何工作的。相比如今现成的IDE,GNU构建系统其实是非常难用的,学习成本比较高。笔者认为最为效率的途径是学习开源的程序,从中慢慢体会,慢慢吸收。另外,推荐几个资料:

《GNU Autoconf Automake and Libtool》

Autotools Tutorial (https://www.lrde.epita.fr/~adl/autotools.html)

GNU网站上关于autoconf automake 和 libtool的手册

autotool构建工程的主要步骤:

  1. autoscan 命令生成 configure.scan
  2. 将configure.scan 重命名为 configure.ac 并手动编辑
  3. aclocal 命令生成aclocal.m4
  4. autoconf命令生成configure文件
  5. autoheader命令生成config.h文件
  6. 手动创建Makefile.am文件
  7. automake命令生成Makefile.in
    8/7.2. automake --add-missing 命令生成辅助文件
  8. configure脚本生成Makefile
    构建实例:
    GNU Autotools的使用方法

http://www.ppmy.cn/news/1545888.html

相关文章

ORB_SLAM3安装

ORB_SLAM3安装 一.前期准备1.1ubuntu查看当前版本的命令1.2 根据ubuntu版本&#xff0c;更新下载软件源1.3 先下载git1.4 vim语法高亮1.5 常见的linux命令 二.ORB-SLAM3下载2.1 ORB_SLAM3源码下载2.2 安装依赖库2.2.1 依赖库2.2.2 安装pangolin2.2.3 安装opencv2.2.4 Eigen3安装…

Spring Boot技术在导师双选系统中的应用

第一章 绪论 1.1 选题背景 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;尽管身边每时每刻都在产生大量信息&#xff0c;这些信息也都会在短时间内得到处理&#xff0c;并迅速传播。因为很多时候&#xff0c;管理层决策需要大量信…

朴素贝叶斯分类器基于iris及Python手写实现

数据来源为sklean.datasets中的load_iris&#xff0c;代码如下&#xff1a; # -*- coding:utf-8 -*- import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.metrics import ac…

Yocto 项目下通过网络更新内核、设备树及模块

Yocto 项目下通过网络更新内核、设备树及模块 前言 在 Yocto 项目的开发过程中&#xff0c;特别是在进行 BSP&#xff08;Board Support Package&#xff09;开发时&#xff0c;经常需要调整特定软件包的版本&#xff0c;修改内核、设备树以及内核模块。然而&#xff0c;每次…

江苏博才众创科技产业园集团拟投资10亿元在泰兴打造汽车零部件产业园

2024年11月7日&#xff0c;泰兴市高新技术产业开发区与江苏博才众创科技产业园集团举行新能源汽车零部件智能制造产业园项目签约仪式。 泰兴市高新区党工委委员、管理办副主任王峰表示&#xff1a;高新区是全市项目建设的主阵地&#xff0c;近年来聚焦高端化、智能化、绿色化&a…

【青牛科技】应用方案 | RTC实时时钟芯片D8563和D1302

一、应用领域 工控主板、安卓主板、TV板卡、智能三表&#xff08;电表、水表、气表&#xff09;、便携式仪器仪表等需要计时或有日历要求的产品。 二、基本特性 D8563和D1302是芯谷科技推出的RTC实时时钟芯片&#xff0c;具有功耗低、走时精准、外围简单等特点&#xff0c;二者…

使用Docker快速部署FastAPI Web应用

Docker是基于 Linux 内核的cgroup、namespace以及 AUFS 类的Union FS 等技术&#xff0c;对进程进行封装隔离&#xff0c;一种操作系统层面的虚拟化技术。Docker中每个容器都基于镜像Image运行&#xff0c;镜像是容器的只读模板&#xff0c;容器是模板的一个实例。镜像是分层结…

100种算法【Python版】第50篇——Tim Sort

本文目录 1 基本原理2 主要步骤3 算法示例4 Python 实现4.1 代码说明4.2 复杂度分析Tim Sort 是一种混合排序算法,由 Tim Peters 于 2002 年为 Python 编程语言设计。它结合了插入排序和归并排序的优点,专门针对实际数据中的某些模式进行优化。Tim Sort 的核心思想是将数组分…