2017-10-24-autoconf的例子

Table of Contents

1 目录   toc_2_org

2 autoconf example

最开始的目录结构是这样的:

.
└── src
    └── main.c

1 directory, 1 file

其中的 main.c 就是一个普通的 hello,world 程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int args,char *argv[])
{
    printf("test for autoconf\n");
    return 0;
}

在主目录下执行 autoscan 生成两个新文件:

 ➜  temp tree
.
├── autoscan.log
├── configure.scan
└── src
    └── main.c

1 directory, 3 files 

configure.scan 改名为 configure.ac1mv configure.scan configure.ac 当前文件长这样:

#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([stdlib.h string.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT
  

在这个文件的基础上进行下简单的修改:

#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([peng-test], [1.0], [pengpengxppri@sina.com])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([stdlib.h string.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT
  

这个时候还 autoconf 只能生成 configure 文件,还不能生成 Makefile 文件,需要在根目录和src目录下都新建一个 Makefile.am 的 文件。根目录下的 Makefile.am 长这样:

AUTOMAKE_OPTIONS = foreign
SUBDIRS = src

第一句表示文件目录和gnu标准的格式是不同的。第二句表示子目录包括当前 目录下的 src 目录。

src 目录下的 Makefile.am 表这样:

# what flags you want to pass to the C compiler & linker
AM_CFLAGS =
AM_LDFLAGS =

# this lists the binaries to produce, the (non-PHONY, binary) targets in
# the previous manual Makefile
bin_PROGRAMS = main
main_SOURCES = main.c

这个文件是最关键的。它直接指定编译时需要的选项,文件等等。主要看下面 的 bin_PROGRAMSmain_SOURCES 。后面的大写我理解应该是已预定义 好的宏。 !bin_PROGRAMS= main! 表示最后生成的可执行文件名字为 main ,使用 make install 安装后是安装到 $PREFIX/bin 目录下面。 !main_SOURCES = main.c! 表示编译可执行文件需要的源文件只有 main.c

然后在修改根目录的 configure.ac ,在 AC_INIT() 的后面加入:

AM_INIT_AUTOMAKE(pengtest, 1.0)

还需要在 configure.ac 中加入下面一句来生成期望生成的 Makefile

AC_OUTPUT(Makefile src/Makefile)

最后总的 configure.ac 看起来是这样的:

#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([pengtest], [1.0], [pengpengxppub@sina.com])
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([stdlib.h string.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile src/Makefile)

此时在根目录下执行 aclocal 命令,生成 aclocal.m4 文件。它包括了 automake 执行时需要用到的一些宏,比如 AM_INIT_AUTOMAKE

最后在根目录下执行 automake --add-missing 。automake会读取 configure.ac 中的内容和顶层 Makefile.am 的内容,再后递归地读取到 每个子目录中的 Makefile.am ,为每个 Makefile.am 生成一个对应的 Makefile.in 文件。 --add-missing 选项告诉automake如果出错则使用 默认脚本来替代(忽略错误。)。此时的目录看起来是这样的了:

  ➜  temp tree
.
├── aclocal.m4
├── autom4te.cache
│   ├── output.0
│   ├── output.1
│   ├── output.2
│   ├── output.3
│   ├── requests
│   ├── traces.0
│   ├── traces.1
│   ├── traces.2
│   └── traces.3
├── autoscan.log
├── compile -> /usr/share/automake-1.15/compile
├── config.log
├── config.status
├── configure
├── configure.ac
├── depcomp -> /usr/share/automake-1.15/depcomp
├── install-sh -> /usr/share/automake-1.15/install-sh
├── Makefile.am
├── Makefile.in
├── missing -> /usr/share/automake-1.15/missing
└── src
    ├── main.c
    ├── Makefile.am
    └── Makefile.in

2 directories, 24 files

最后,执行一次 autoconf ,重新生成一下 configure 文件。

这样该软件的编译安装就直接 ./configure && make && make install 就 可以了。

最后再写一个脚本从头搞一次:

aclocal
automake --add-missing
autoconf

# all the command above can be replaced by one line:
# `autoreconf -i'
run command to build from source:
,#+BEGIN_SRC sh
sh build.sh
./configure
make
,#+END_SRC

3 libtool example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fun.h"

int main(int args,char *argv[])
{
    printf("test for autoconf\n");
    fun();
    return 0;
}
#include "fun.h"

int fun()
{
    printf("test for libtools\n");
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int fun();
AUTOMAKE_OPTIONS = foreign
SUBDIRS = src

ACLOCAL_AMFLAGS = -I m4
# what flags you want to pass to the C compiler & linker
AM_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src
AM_LDFLAGS =

# this lists the binaries to produce, the (non-PHONY, binary) targets in
# the previous manual Makefile

lib_LTLIBRARIES = libpeng.la
libpeng_la_SOURCES = fun.c fun.h
libpeng_la_LDFLAGS = -version-info 0:0:0

bin_PROGRAMS = main
main_SOURCES = main.c
main_LDADD = libpeng.la
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([pengtest], [1.0], [pengpengxppub@sina.com])
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([stdlib.h string.h])

LT_INIT

AC_CONFIG_MACRO_DIRS([m4])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile src/Makefile)
libtoolize
aclocal
automake --add-missing
autoconf

# all the command above can be replaced by one line:
# `autoreconf -i'
run command to build from source:
#+BEGIN_SRC sh
sh build.sh
./configure
make
#+END_SRC

3.1 tips

在git仓库中, libtoolize 默认会在git的root目录创建一个 ltmain.sh 的文件,但是有我们把第三方库代码拿回来,不想以git submodule的方式使用,直接copy源码到我们的仓库时,使用 libtoolize 生成的 ltmain.sh 就会出现在我们仓库的根目录而不是在对应第三方库的 当前目录。

解决这个问题,可以在 configure.ac 中加入下面这句这ok了,强制在当 前目录生成:

AC_CONFIG_AUX_DIR([.])

4 后续补充

4.1 TODO 根目录中的 config.h 文件

  • State "TODO" from [2017-10-24 Tue 11:37]

默认使用 autoscan 扫描的时候生成的 configure.scan 中有一条:

AC_CONFIG_HEADERS([config.h])

上面的例子中为了简单我把其中的 config.h 删掉了。实际使用时,如果使 用 autoreconf -i 来自动进行上述过程,会报下面的错误:

  ➜  temp autoreconf -i          
autoheader: error: AC_CONFIG_HEADERS not found in configure.ac
autoreconf: /usr/bin/autoheader failed with exit status: 1

于是试着加回去,然后到 automake 的时候会报错,提示文件找不到:

configure.ac:8: error: required file 'config.h.in' not found

5 工具链

5.1 autoscan

帮忙生成 configure.scan 。可以在其上改出 configure.ac

5.2 ifnames

可以扫出文件中所有使用的宏。 它的功能用洋文叫 List Conditionals

➜  src cat main.c  
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"

int main(int args, char *argv[])
{
#ifdef PENG
        printf("yestest for autoconf\n");
#else
        printf("no\n");
#endif
        return 0;
}
➜  src ifnames main.c
PENG main.c

5.3 autoconf

用于生成 configure 的工具。不带参数执行时,它会结合M4的宏处理器来 处理 configure.ac ,然后生成 configure 文件。如果后面指定某文件, 它会根据指定的文件来处理,并输出到标准输出。

autoconf的宏没可能分布在不同的地方。 acsite.m4aclocal.m4 等。 如果同一个宏在不同的地方定义,以最后一次定义为准。

5.4 autoreconf

autoconf, autoheader, aclocal, automake, libtoolize, autopoint这些工 具多次调用很烦, autoreconf 可以自动地来调用它们,来更新编译系统。

5.5 libtool

它解决库的问题。不同的环境上支持的不同,有的只支持共享库,有的只支 持静态库,有的两者都支持。它就是来解决这些问题的。

详细使用上可以参考官网 Libtool

我觉得就是在平时编译链接的gcc命令前面加入 libtool 就可以了:

libtool --mode=compile gcc -g -O -c foo.c

libtool --mode=link gcc -g -O -o libhello.la foo.o hello.o

libtool --mode=link gcc -g -O -o hell main.o libhello.la
   

6 参考资料

7 MISC note

7.1 automake变量

7.1.1 BUILT_SOURCES

假设一种场景, foo.c 中include了 foo.h 。正常情况下 foo.c,foo.h 都存在的情况下。从 foo.c 中编出 foo.o 是很容易的。 因为依赖关系是明确的且文件都是静态存在的。但是如果 foo.h 可能需 要的编译时通过脚本或者其它方式生成时,编译 foo.o 时可能因为缺少 foo.h 而失败。

该变量就是为这种情形存在的。它包括的文件可以在 make allmake check 中被生成。

把下面两个文件保存到一个目录,然后执行 autoreconf -i 。然后 ./configure && make 就会发现 echo 语句会在最开始运行。

AUTOMAKE_OPTIONS = foreign

ACLOCAL_AMFLAGS = -I m4

BUILT_SOURCES = foo.h

foo.h:
        echo "xiepeng"
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([pengtest], [1.0], [pengpengxppub@sina.com])
AC_CONFIG_SRCDIR([])
AM_INIT_AUTOMAKE

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([stdlib.h string.h])

LT_INIT

AC_CONFIG_MACRO_DIRS([m4])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile)

需要注意的是 :该变量只是遵循 make all, make check, make install 这三个。也就是说如果最开始的时候直接指定编 make foo 可 能是通不过的。这时候可能需要先使用 make all 然后才能编过 make foo

7.1.2 AM_CPPFLAGS和AM_CFLAGS

AM_CPPFLAGS中的flag会传给调用预处理的每一次编译。

AM_CFLAGS传递一些额外的编译参数。

autoconf example 中的例子来说,如果我们把src中的 Makefile.am 改一下:

# what flags you want to pass to the C compiler & linker
AM_CPPFLAGS = -DXIEPENG1
AM_CFLAGS = -D XIEPENG2
AM_LDFLAGS =

# this lists the binaries to produce, the (non-PHONY, binary) targets in
# the previous manual Makefile
bin_PROGRAMS = main
main_SOURCES = main.c

编译输出如下:

/tmp/autoconf_example $ make
Making all in src
make[1]: Entering directory '/tmp/autoconf_example/src'
gcc -DPACKAGE_NAME=\"pengtest\" -DPACKAGE_TARNAME=\"pengtest\" -DPACKAGE_VERSION=\"1.0\" -DPACKAGE_STRING=\"pengtest\ 1.0\" -DPACKAGE_BUGREPORT=\"pengpengxppub@sina.com\" -DPACKAGE_URL=\"\" -DPACKAGE=\"pengtest\" -DVERSION=\"1.0\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -I.  -DXIEPENG1  -DXIEPENG2 -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
gcc -DXIEPENG2 -g -O2   -o main main.o  
make[1]: Leaving directory '/tmp/autoconf_example/src'
make[1]: Entering directory '/tmp/autoconf_example'
make[1]: Nothing to be done for 'all-am'.
make[1]: Leaving directory '/tmp/autoconf_example'
/tmp/autoconf_example $ 

上述两个参数都会被每个单独编译中的 xxx_CPPFLAGSxxx_CFLAGS 覆盖。 还是以 autoconf example 中的例子来说,如果我们再把src中的 Makefile.am 改一下:

# what flags you want to pass to the C compiler & linker
AM_CPPFLAGS = -DXIEPENG1
AM_CFLAGS = -DXIEPENG2
AM_LDFLAGS =

# this lists the binaries to produce, the (non-PHONY, binary) targets in
# the previous manual Makefile
bin_PROGRAMS = main
main_SOURCES = main.c
main_CFLAGS = -DXIEPENG3
main_CPPFLAGS = -DXIEPENG4

看下编译输出:

/tmp/autoconf_example $ make
Making all in src
make[1]: Entering directory '/tmp/autoconf_example/src'
gcc -DPACKAGE_NAME=\"pengtest\" -DPACKAGE_TARNAME=\"pengtest\" -DPACKAGE_VERSION=\"1.0\" -DPACKAGE_STRING=\"pengtest\ 1.0\" -DPACKAGE_BUGREPORT=\"pengpengxppub@sina.com\" -DPACKAGE_URL=\"\" -DPACKAGE=\"pengtest\" -DVERSION=\"1.0\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -I.  -DXIEPENG4  -DXIEPENG3 -g -O2 -MT main-main.o -MD -MP -MF .deps/main-main.Tpo -c -o main-main.o `test -f 'main.c' || echo './'`main.c
mv -f .deps/main-main.Tpo .deps/main-main.Po
gcc -DXIEPENG3 -g -O2   -o main main-main.o  
make[1]: Leaving directory '/tmp/autoconf_example/src'
make[1]: Entering directory '/tmp/autoconf_example'
make[1]: Nothing to be done for 'all-am'.
make[1]: Leaving directory '/tmp/autoconf_example'
/tmp/autoconf_example $ 

Footnotes:

1
之前叫 configure.in

Author: Peng Xie

Created: 2018-10-01 Mon 21:36