GNU AUTOTOOLS 简介
1.介绍
大部分时候,你从网上下载了一个开放源代码的软件,在解压后,你一般会敲入以下三个命令来编译和安装你的程序:
1)./configure
2)make
3)make install
其中,configure脚本分析你的系统以到合适的工具和库,make是真正用来编译构建软件的工具,make install来安装软件。在开放源代码世界,configure脚本获得了广泛的使用,它使从源代码安装软件变得非常容易,本文主要就是介绍如何使用GNU autotools,以便使你能够以上面这种方式来编译和安装你的程序。
2.什么是GNU autotools
GNU autotools主要包括下面三个工具:
Autoconf – 这个工具用来生成configure脚本。就像前面提到的,这个脚本主要用来分析你的系统以到合适的工具和库。譬如:你的系统的C编译器是“cc”还是“gcc”?
Automake – 这个工具用来生成Makefiles。它需要使用到Autoconf提供的信息。譬如,如果Autoconf检测到你的系统使用“gcc”,那Makefile就使用gcc作为C编译器。反之,如果到“cc”,那就使用“cc”。
Libtools – 这个工具创建共享库。它是平台无关的。
3.为“Hello,world”添加Autoconf
1) 创建一个“Hello,world!”
写一个简单的“Hello,world!”程序
hello.c:
#include <stdio.h>
int main(void)
{
printf ("Hello World.\n");
Automake – 这个工具用来生成Makefiles。它需要使用到Autoconf提供的信息。譬如,如果Autoconf检测到你的系统使用“gcc”,那Makefile就使用gcc作为C编译器。反之,如果到“cc”,那就使用“cc”。
Libtools – 这个工具创建共享库。它是平台无关的。
3.为“Hello,world”添加Autoconf
1) 创建一个“Hello,world!”
写一个简单的“Hello,world!”程序
hello.c:
#include <stdio.h>
int main(void)
{
printf ("Hello World.\n");
}
写一个简单的Makefile
Makefile
EXEC:=hello
all: $(EXEC)
.PHONY:
clean
clean:
rm -f hello *.o
2) 添加Autoconf
现在,我们将添加autoconf到这个程序。首先,我们需要创建一个叫做“configure.ac”的文件。这个文件将指导Autoconf如何生成“configure”脚本。因为用手工创建这个文件非常乏味,所以Autoconf提供了一个程序来帮助你创建这个文件。这个程序就是“autoscan”.“autoscan”扫描你的源程序并创建一个相应的“configure.ac”文件。但是注意它并不是直接创建“configure.ac”,相反,它创建了一个叫做“configure.scan”的文件。所以,
写一个简单的Makefile
Makefile
EXEC:=hello
all: $(EXEC)
.PHONY:
clean
clean:
rm -f hello *.o
2) 添加Autoconf
现在,我们将添加autoconf到这个程序。首先,我们需要创建一个叫做“configure.ac”的文件。这个文件将指导Autoconf如何生成“configure”脚本。因为用手工创建这个文件非常乏味,所以Autoconf提供了一个程序来帮助你创建这个文件。这个程序就是“autoscan”.“autoscan”扫描你的源程序并创建一个相应的“configure.ac”文件。但是注意它并不是直接创建“configure.ac”,相反,它创建了一个叫做“configure.scan”的文件。所以,
我们需要把它重命名为“configure.ac”.为什么“autoscan”不直接创建“configure.ac”呢?当然,现在你也许不需要自己来写“configure.ac”,但是,以后你可能会自己写。你当然不希望一不小心把“configure.ac”覆盖掉了。所以“autoscan
”不会直接写到“configure.ac”。
现在,我们可以运行“autoconf”来生成“configure”了.
也许,你现在就想输入“./configure”,但是,别急。“configure”脚本是被用来生成一个新的Makefile。但是“configure”需要一个叫做“Makefile.in”的文件来生成新的Makefile。所以,现在,我们需要一个“Makefile.in”。“Makefile.in”就是一个“Makefile”的模板,它被用来生成一个新的Makefile。所以,现在,我们只是简单的把“Makefile”改为“Makefile.in”。我们将在后面对其进行介绍:
现在,我们已经可以运行“./configure”和“make”.
虽然上面有个error,说不到config.h.in,但是对于整个脚本的目标—生成Makefile,并没有太大影响。config.h.in,将在下面讲到。
3)总结
”不会直接写到“configure.ac”。
现在,我们可以运行“autoconf”来生成“configure”了.
也许,你现在就想输入“./configure”,但是,别急。“configure”脚本是被用来生成一个新的Makefile。但是“configure”需要一个叫做“Makefile.in”的文件来生成新的Makefile。所以,现在,我们需要一个“Makefile.in”。“Makefile.in”就是一个“Makefile”的模板,它被用来生成一个新的Makefile。所以,现在,我们只是简单的把“Makefile”改为“Makefile.in”。我们将在后面对其进行介绍:
现在,我们已经可以运行“./configure”和“make”.
虽然上面有个error,说不到config.h.in,但是对于整个脚本的目标—生成Makefile,并没有太大影响。config.h.in,将在下面讲到。
3)总结
写代码、Makefile等
运行“autoscan”(“autoscan”创建“configure.scan”)
把“configure.scan”改为“configure.ac”
运行“autoconf”(“autoconf”使用“configure.ac”生成“configure”脚本)
把“Makefile”改为“Makefile.in”(“configure”会用“Makefile.in”来生成“Makefile”)
运行“./configure”和“make”
$ls
$hello.c Makefile
$autoscan
$mv configure.scan configure.ar
$mv Makefile Makefile.in
$autoconf
$./configure
运行“autoscan”(“autoscan”创建“configure.scan”)
把“configure.scan”改为“configure.ac”
运行“autoconf”(“autoconf”使用“configure.ac”生成“configure”脚本)
把“Makefile”改为“Makefile.in”(“configure”会用“Makefile.in”来生成“Makefile”)
运行“./configure”和“make”
$ls
$hello.c Makefile
$autoscan
$mv configure.scan configure.ar
$mv Makefile Makefile.in
$autoconf
$./configure
4.让“Hello,world!”可移植
1)用“config.h”使程序可移植
要使程序可移植,我们必须修改“hello.c”,使用大量的“#ifdef”或“#ifndef”等等。但是,更普遍的做法是使用config.h.in,它里面是一些与平台或库相关的常量信息。GNU autotools同样提供了一个工具“autoheader”来帮助我们生成config.h,使用它非常简单,只要在运行“./configure”之前,运行“autoheader”,然后运行
“./configure”,就会生成“config.h”,让我们看一下“config.h”的内容,当然,这儿有些常量定义,但是没有一个是和移植有关的。这是由于我们的程序太简单了,下面,我们将让“config.h”包含一些与移植有关的常量。
2)让“hello.c”变得复杂一些
为了说明如何使用“configu.h”来使程序可移植,我们添加了“gettimeof”函数,“gettimeof”函数获得自1970.1.1(unix纪元)以来的秒数,整个程序如下所示:
首先,确认目录下只有hello.c和Makefile.in这两个文件,其余的文件用rm –fr 命令删除:
1)用“config.h”使程序可移植
要使程序可移植,我们必须修改“hello.c”,使用大量的“#ifdef”或“#ifndef”等等。但是,更普遍的做法是使用config.h.in,它里面是一些与平台或库相关的常量信息。GNU autotools同样提供了一个工具“autoheader”来帮助我们生成config.h,使用它非常简单,只要在运行“./configure”之前,运行“autoheader”,然后运行
“./configure”,就会生成“config.h”,让我们看一下“config.h”的内容,当然,这儿有些常量定义,但是没有一个是和移植有关的。这是由于我们的程序太简单了,下面,我们将让“config.h”包含一些与移植有关的常量。
2)让“hello.c”变得复杂一些
为了说明如何使用“configu.h”来使程序可移植,我们添加了“gettimeof”函数,“gettimeof”函数获得自1970.1.1(unix纪元)以来的秒数,整个程序如下所示:
首先,确认目录下只有hello.c和Makefile.in这两个文件,其余的文件用rm –fr 命令删除:
然后参照前面的方法来生成“configure”脚本和“Makefile”:
下面看一下“config.h”的部分内容:
由于有了这样一个“config.h”,我们就有了不少可用于检测移植性的宏,譬如可以检测gettimeofday()是否存在(使用Have_GETTIMEOFDAY),或检测是否存在头文件
“sys/time.h”(使用HAVE_SYS_TIME_H)等。
hello.c
#include <stdio.h>
#include <sys/time.h>
int main(void)
{
double sec;
struct timeval tv;
gettimeofday(&tv, NULL);
下面看一下“config.h”的部分内容:
由于有了这样一个“config.h”,我们就有了不少可用于检测移植性的宏,譬如可以检测gettimeofday()是否存在(使用Have_GETTIMEOFDAY),或检测是否存在头文件
“sys/time.h”(使用HAVE_SYS_TIME_H)等。
hello.c
#include <stdio.h>
#include <sys/time.h>
int main(void)
{
double sec;
struct timeval tv;
gettimeofday(&tv, NULL);
sec = tv.tv_sec;
sec += tv.tv_usec/1000000.0;
printf ("sec = %e", sec);
return 0;
}
3)让我们的代码变得可移植
现在,我们可以修改我们代码,使它易于移植.当然,你可以重新从“autoscan”开始再走一遍生成“configure”脚本的过程,以便发现新的移植性问题。
现在,你的程序不仅使用了“autoconf”而且是可移植的。
4)总结
在写你的代码的时候,要时刻考虑到移植性。创建“Makefile.in”
运行“autoscan”
把“configure.scan”改为“configure.ac”
运行“autoheader”
sec += tv.tv_usec/1000000.0;
printf ("sec = %e", sec);
return 0;
}
3)让我们的代码变得可移植
现在,我们可以修改我们代码,使它易于移植.当然,你可以重新从“autoscan”开始再走一遍生成“configure”脚本的过程,以便发现新的移植性问题。
现在,你的程序不仅使用了“autoconf”而且是可移植的。
4)总结
在写你的代码的时候,要时刻考虑到移植性。创建“Makefile.in”
运行“autoscan”
把“configure.scan”改为“configure.ac”
运行“autoheader”
运行“autoconf”
“./configure”
查看“config.h”,如果有必要,改写你的源代码,并从第二步重新开始。
编译,“make”
5.使用Automake
1)Automaking
Automaking包含下面三步,但是你应该对第三步很熟悉了:
创建一个文件“Makefile.am”
“Automake”使用“Makefile.am”创建一个新的“Makefile.in”
“Makefile.in”被“configure”用来创建“Makefile”
下面是一个“Makefile.am”的简单的示例:
Makefile.am
bin_PROGRAMS=hello
hello_SOURCES=hello.c
“./configure”
查看“config.h”,如果有必要,改写你的源代码,并从第二步重新开始。
编译,“make”
5.使用Automake
1)Automaking
Automaking包含下面三步,但是你应该对第三步很熟悉了:
创建一个文件“Makefile.am”
“Automake”使用“Makefile.am”创建一个新的“Makefile.in”
“Makefile.in”被“configure”用来创建“Makefile”
下面是一个“Makefile.am”的简单的示例:
Makefile.am
bin_PROGRAMS=hello
hello_SOURCES=hello.c
这非常简单。当然,在写“Makefile.am”,还有许多命令可以使用,如果你有兴趣,可以参看GNU的“Automake”手册。
现在,我们Automake这个文件。这需要下面两步:
automake – 执行这个命令肯定会输出很多的错误。我们需要检查并修复它
aclocal – 在处理第一步的输出错误时,我们需要增加一些“Automake”的宏到“configure.ac”里,但是,“Autoconf”肯定不会认出这些新宏。我们需要一个
“aclocal.m4”文件,里面定义了这些新的宏,以便使“Autoconf”认出它们。“alocal”会为我们自动化生成这个文件。
2)修复错误
运行“automake”,会有如下输出:
首先,我们需要修改“configure.ac”,在里面加入AM_INIT_AUTOMAKE:
然后,在修改了“configure.ac”过后,我们需要运行一下aclocal,以生成“aclocal.m4”文件。
现在,我们Automake这个文件。这需要下面两步:
automake – 执行这个命令肯定会输出很多的错误。我们需要检查并修复它
aclocal – 在处理第一步的输出错误时,我们需要增加一些“Automake”的宏到“configure.ac”里,但是,“Autoconf”肯定不会认出这些新宏。我们需要一个
“aclocal.m4”文件,里面定义了这些新的宏,以便使“Autoconf”认出它们。“alocal”会为我们自动化生成这个文件。
2)修复错误
运行“automake”,会有如下输出:
首先,我们需要修改“configure.ac”,在里面加入AM_INIT_AUTOMAKE:
然后,在修改了“configure.ac”过后,我们需要运行一下aclocal,以生成“aclocal.m4”文件。
最后,在我们运行Automake的时候,它需要下面的文件:
install-sh
missing
INSTALL
NEWS
README
AUTHORS
ChangeLog
COPYING
depcomp
当“automake”使用“-a”选项运行时,它会自动生成install-sh,missing,INSTALL,COPYING和depcomp。其余的需要手工创建:
3)运行automake的结果
运行“automake”后,生成“Makefile.in”,如下所示:
然后,如前所述,运行“autoconfig”生成“configure”脚本,运行“configure”脚本生成“Mak
install-sh
missing
INSTALL
NEWS
README
AUTHORS
ChangeLog
COPYING
depcomp
当“automake”使用“-a”选项运行时,它会自动生成install-sh,missing,INSTALL,COPYING和depcomp。其余的需要手工创建:
3)运行automake的结果
运行“automake”后,生成“Makefile.in”,如下所示:
然后,如前所述,运行“autoconfig”生成“configure”脚本,运行“configure”脚本生成“Mak
efile”。如果,你打开“Makefile”看一下,你会发现它比你自己写的要大了很多,大概有500行,功能丰富了很多,如果有兴趣,并对Makefile的编写比较了解的话,可以看一下,这对写Makefile还是很有好处的。而我们只是添加了不到3句话。从这儿也可以看出GNU autotools的强大。
4)总结
写代码,创建“Makefile.am”
运行“autoscan”
把“configure.scan”改为“configure.ac”
修改“configure.ac”,添加Automake的宏
运行“aclocal”
创建运行“automake”需要的文件
运行“automake”
运行“autoheader”
运行“autoconf”
“./configure”
4)总结
写代码,创建“Makefile.am”
运行“autoscan”
把“configure.scan”改为“configure.ac”
修改“configure.ac”,添加Automake的宏
运行“aclocal”
创建运行“automake”需要的文件
运行“automake”
运行“autoheader”
运行“autoconf”
“./configure”
查看“config.h”,如果有必要,改写你的源代码,并从第二步重新开始。
编译,“make”
编译,“make”
Makefile.am 详解
Makefile.am是一种比Makefile更高层次的规则。只需指定要生成什么目标,它由什么源文件生成,要安装到什么目录等构成。
表一列出了可执行文件、静态库、头文件和数据文件,四种书写Makefile.am文件个一般格式。
表 1Makefile.am一般格式
对于可执行文件和静态库类型,如果只想编译,不想安装到系统中,可以用noinst_PROGRAMS代替bin_PROGRAMS,noinst_LIBRARIES代替lib_LIBRARIES。
Makefile.am还提供了一些全局变量供所有的目标体使用:
表 2 Makefile.am中可用的全局变量
在Makefile.am中尽量使用相对路径,系统预定义了两个基本路径:
表 3Makefile.am中可用的路径变量
在上文中我们提到过安装路径,automake设置了默认的安装路径:
1)标准安装路径
默认安装路径为:$(prefix) = /usr/local,可以通过./configure --prefix=<new_path>的方法来覆盖。
其它的预定义目录还包括:bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc等等。
2) 定义一个新的安装路径
比如test, 可定义testdir = $(prefix)/test, 然后test_DATA =test1 test2,则test1,test2会作为数据文件安装到$(prefix)/ /test目录下。
我们首先需要在工程顶层目录下(即project/)创建一个Makefile.am来指明包含的子目录:
SUBDIRS=src/lib src/ModuleA/apple/shell src/ModuleA/apple/core
CURRENTPATH=$(shell /bin/pwd)
INCLUDES=-I$(CURRENTPATH)/src/include -I$(CURRENTPATH)/src/ModuleA/apple/include
export INCLUDES
由于每个源文件都会用到相同的头文件,所以我们在最顶层的Makefile.am中包含了编译源文件时所用到的头文件,并导出,见蓝部分代码。
我们将lib目录下的swap.c文件编译成libswap.a文件,被apple/shell/apple.c文件调用,那么lib目录下的Makefile.am如下所示:
noinst_LIBRARIES=libswap.a
makefile phonylibswap_a_SOURCES=swap.c
INCLUDES=-I$(top_srcdir)/src/includ
细心的读者可能就会问:怎么表1中给出的是bin_LIBRARIES,而这里是noinst_LIBRARIES?这是因为如果只想编译,而不想安装到系统中,就用noinst_LIBRARIES代替bin_LIBRARIES,对于可执行文件就用noinst_PROGRAMS代替bin_PROGRAMS。对于安装的情况,库将会安装到$(prefix)/lib目录下,可执行文件将会安装到${prefix}/bin。如果想安装该库,则Makefile.am示例如下:
bin_LIBRARIES=libswap.a
libswap_a_SOURCES=swap.c
INCLUDES=-I$(top_srcdir)/src/include
swapincludedir=$(includedir)/swap
swapinclude_HEADERS=$(top_srcdir)/src/include/swap.h
最后两行的意思是将swap.h安装到${prefix}/include /swap目录下。
接下来,对于可执行文件类型的情况,我们将讨论如何写Makefile.am?对于编译apple/core目录下的文件,我们写成的Makefile.am如下所示:
noinst_PROGRAMS=test
test_SOURCES=test.c
test_LDADD=$(top_srcdir)/src/ModuleA/apple/shell/apple.o $(top_srcdir)/src/lib/libswap.a
test_LDFLAGS=-D_GNU_SOURCE
DEFS+=-D_GNU_SOURCE
#LIBS=-lpthread
由于我们的test.c文件在链接时,需要apple.o和libswap.a文件,所以我们需要在test_LDADD中包含这两个文件。对于Linux下的信号量/读写锁文件进行编译,需要在编译选项中指明-D_GNU_SOURCE。所以在test_LDFLAGS中指明。而test_LDFLAGS只是链接时的选项,编译时同样需要指明该选项,所以需要DEFS来指明编译选项,由于DEFS已经有初始值,所以这里用+=的形式指明。从这里可以看出,Makefile.am中的语法与Makefile的语法一致,也可以采用条件表达式。如果你的程序还包含其他的库,除了用AC_CHECK_LIB宏来指明外,还可以用LIBS来指明。
如果你只想编译某一个文件,那么Makefile.am如何写呢?这个文件也很简单,写法跟可执行文件的差不多,如下例所示:
noinst_PROGRAMS=apple
apple_SOURCES=apple.c
DEFS+=-D_GNU_SOURCE
我们这里只是欺骗automake,假装要生成apple文件,让它为我们生成依赖关系和执行命令。所以当你运行完automake命令后,然后修改apple/shell/下的Makefile.in文件,直接将LINK语句删除,即:
…….
clean-noinstPROGRAMS:
-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
apple$(EXEEXT): $(apple_OBJECTS) $(apple_DEPENDENCIES)
@rm -f apple$(EXEEXT)
#$(LINK) $(apple_LDFLAGS) $(apple_OBJECTS) $(apple_LDADD) $(LIBS)
…….
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论