在Linux或Unix环境下,对于只含有几个源代码文件的小程序(如hello.c)的编译,可以手工键入gcc命令对源代码文件逐个进行编译;然而在大型的项目开发中,可能涉及几十到几百个源文件,采用手工键入的方式进行编译,则非常不方便,而且一旦修改了源代码,尤其头文件发生了的修改,采用手工方式进行编译和维护的工作量相当大,而且容易出错。所以在Linux或Unix环境下,人们通常利用GNU make工具来自动完成应用程序的维护和编译工作。实际上,GNU make工具通过一个称为Makefile的文件来完成对应用程序的自动维护和编译工作。Makefile是按照某种脚本语法编写的文本文件,而GNU make能够对Makefile中指令进行解释并执行编译操作。Makefile文件定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。GNU make工作时的执行步骤如下:
- 读入所有的Makefile。
- 读入被include的其它Makefile。
- 初始化文件中的变量。
- 推导隐晦规则,并分析所有规则。
- 为所有的目标文件创建依赖关系链。
- 根据依赖关系,决定哪些目标要重新生成。
- 执行生成命令。
1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。下面对makefile的相关问题进行简单介绍。
Makefile的基本结构
Makefile的一般结构:
target……:dependency……
command……
结构中各部分的含义:
1. target(目标):一个目标文件,可以是Object文件,也可以是执行文件。还可以是一个标签(Label)。
2. dependency(依赖):要生成目标文件(target)所依赖哪些文件
3. command(命令):创建项目时需要运行的shell命令(注:命令(command)部分的每行的缩进必须要使用Tab而不能使用多个空格)。
Makefile实际上是一个文件的依赖关系,也就是说, target这一个或多个的目标文件依赖于dependency中的文件,其生成规则定义在命令command中。如果依赖文件(dependency)中有一个以上的文件比目标(target)文件要新的话,shell命令(command)所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
例如,假设有一个C源文件test.c,该源文件包含有自定义的头文件test.h,则目标文件test.o明确依赖于两个源文件:test.c和test.h。如果只希望利用gcc命令来生成test.o目标文件,这时,就可以利用如下的makefile来定义test.o的创建规则:
#This makefile just is a example.
test.o: test.c test.h
gcc –c test.c
从上面的例子注意到,第一个字符为#的行表示注释行。第一个非注释行指定test.o为目标,并且依赖于test.c和test.h文件。随后的行指定了如何从目标所依赖的文件建立目标。
当test.c或test.h文件在编译之后又被修改,则make工具可自动重新编译test.o,如果在前后两次编译之间,test.c和test.h均没有被修改,而且test.o还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义,make工具可避免许多不必要的编译工作。
一个makefile文件中可定义多个目标,利用make target命令可指定要编译的目标,如果不指定目标,则使用第一个目标。通常,makefile中定义有clean目标,可用来清除编译过程中的中间文件
# This makefile just is a example.
test.o: test.c test.h
gcc -c test.c
clean:
rm -f *.o
运行make clean时,执行rm –f *.o命令,删除编译过程中生成的所有中间文件。
Makefile的基本内容
Makefile一般包括包含:显式规则、隐晦规则、变量定义、文件指示和注释等五个内容。
1. 显式规则:显式规则说明如何生成一个或多个的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
2. 变量定义。在Makefile中可以定义一系列的变量,变量一般都是字符串,当Makefile被执行时,变量的值会被扩展到相应的引用位置上。
3. 隐含规则:由于GNU make具有自动推导功能,所以隐晦规则可以比较粗糙地简略地书写Makefile,然后由GNU make的自动推导功能完成隐晦规则的内容。
4. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。
5. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“\#”。
Makefile中的变量
- Makefile中定义的变量,与C/C++语言中的宏一样,代表一个文本字串,在Makefile被执行时候变量会自动地展开在所使用的地方。Makefile中的变量可以使用在“目标”,“依赖目标”,“命令”或Makefile的其它部分中。
- Makefile中变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回车等)。
- Makefile中变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。传统的Makefile的变量名是全大写的命名方式
- 变量在声明时需要给予初值,而在使用时,需要在变量名前加上“$”符号
# makefile test for hello program
#written by Emdoor
CC=gcc
CFLAGS=
OBJS=hello.o
all: hello
hello: $(OBJS)
$(CC) $(CFLAGS) $(OBJS) –o hello
hello.o: hello.c
$(CC) $(CFLAGS) –c hello.c –o $(OBJS)
clean:
rm –rf hello *.o
上面自定义变量OBJS表示hello.o,当makefile被执行时,变量会在使用它的地方精确地展开,就像C/C++中的宏一样。上述makfile变量展开后的形式为:
# makefile test for hello program
#written by Emdoor
CC=gcc
CFLAGS=
OBJS=hello.o
all: hello
hello: hello.o
gcc hello.o –o hello
hello.o: hello.c
gcc –c hello.c –o hello.o
clean:
rm –rf hello *.o
GNU make的主要预定义变量
GNU make 有许多预定义的变量,这些变量具有特殊的含义,可在规则中使用。以下给出了一些主要的预定义变量,除这些变量外,GNU make 还将所有的环境变量作为自己的预定义变量。
$@ ——表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$% ——仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
$< ——依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$? ——所有比目标新的依赖目标的集合。以空格分隔。
$^ ——所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+ ——这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
命令的变量
AR 函数库打包程序。默认命令是“ar”。
AS 汇编语言编译程序。默认命令是“as”。
CC C语言编译程序。默认命令是“cc”。
CXX C++语言编译程序。默认命令是“g++”。
CO 从 RCS文件中扩展文件程序。默认命令是“co”。
CPP C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
FC Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。
GET 从SCCS文件中扩展文件的程序。默认命令是“get”。
LEX Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。
PC Pascal语言编译程序。默认命令是“pc”。
YACC Yacc文法分析器(针对于C程序)。默认命令是“yacc”。
YACCR Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc –r”。
MAKEINFO 转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”。
TEX 从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。
TEXI2DVI 从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。
WEAVE 转换Web到TeX的程序。默认命令是“weave”。
CWEAVE 转换C Web 到 TeX的程序。默认命令是“cweave”。
TANGLE 转换Web到Pascal语言的程序。默认命令是“tangle”。
CTANGLE 转换C Web 到 C。默认命令是“ctangle”。
RM 删除文件命令。默认命令是“rm –f”。
命令参数变量
下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空。
ARFLAGS 函数库打包程序AR命令的参数。默认值是“rv”。
ASFLAGS 汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
CFLAGS C语言编译器参数。
CXXFLAGS C++语言编译器参数。
COFLAGS RCS命令参数。
CPPFLAGS C预处理器参数。( C 和 Fortran 编译器也会用到)。
FFLAGS Fortran语言编译器参数。
GFLAGS SCCS “get”程序参数。
LDFLAGS 链接器参数。(如:“ld”)
LFLAGS Lex文法分析器参数。
PFLAGS Pascal语言编译器参数。
RFLAGS Ratfor 程序的Fortran 编译器参数。
YFLAGS Yacc文法分析器参数。
隐含规则
GNU make 包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件建立特定类型的目标。
GNU make 支持两种类型的隐含规则:
1. 后缀规则(Suffix Rule)。后缀规则是定义隐含规则的老风格方法。后缀规则定义了将一个具有某个后缀的文件(例如,.c 文件)转换为具有另外一种后缀的文件(例如,.o 文件)的方法。每个后缀规则以两个成对出现的后缀名定义,例如,将 .c 文件转换为 .o 文件的后缀规则可定义为:
.c.o:
$(CC) $(CCFLAGS) $(CPPFLAGS) -c -o $@ $<
2. 模式规则(pattern rules)。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。模式规则看起来非常类似于正则规则,但在目标名称的前面多了一个 % 号,同时可用来定义目标和依赖文件之间的关系,例如下面的模式规则定义了如何将任意一个 X.c 文件转换为 X.o 文件:
%.c:%.o
$(CC) $(CCFLAGS) $(CPPFLAGS) -c -o $@ $<
运行 make
一般来说,最简单的就是直接在命令行下输入make命令,GNU make找寻默认的Makefile的规则是在当前目录下依次找三个文件——“GNUmakefile”、“makefile”和“Makefile”。其按顺序找这三个文件,一旦找到,就开始读取这个文件并执行,也可以给make命令指定一个特殊名字的Makefile。要达到这个功能,要求使用make的“-f”或是“- -file”参数,例如特殊名字为makefile1,则命令为:make –f makefile1
autotools原理
Makefile固然可以帮助make完成它的使命,但要编写Makefile确实不是一件轻松的事,尤其对于一个较大的项目而言更是如此。那么,有没有一种轻松的手段生成Makefile而同时又能让我们享受make的优越性呢?本实验要讲autotools系列工具正是为此而设的,它只需用户输入简单的目标文件、依赖文件、文件目录等就可以轻松地生成Makefile了,这无疑是广大用户的所希望的。另外,这些工具还可以完成系统配置信息的收集,从而可以方便地处理各种移植性的问题。也正是基于此,现在Linux上的软件开发一般都用autotools来制作Makefile。
一、什么是GNU autotools
GNU autotools主要包括下面三个工具:
Autoconf – 这个工具用来生成configure脚本。就像前面提到的,这个脚本主要用来分析你的系统以找到合适的工具和库。譬如:你的系统的C编译器是“cc”还是“gcc”?
Automake – 这个工具用来生成Makefiles。它需要使用到Autoconf提供的信息。譬如,如果Autoconf检测到你的系统使用“gcc”,那Makefile就使用gcc作为C编译器。反之,如果找到“cc”,那就使用“cc”。
Libtools – 这个工具创建共享库。它是平台无关的。
二、autotools使用流程
autotools是系列工具,首先要确认系统是否装了以下工具(可以用which命令进行查看)。
aclocal
autoscan
autoconf
autoheader
automake
使用autotools主要就是利用各个工具的脚本文件以生成最后的Makefile。其总体流程是这样的:
1)使用aclocal生成一个“aclocal.m4”文件,该文件主要处理本地的宏定义;
2)改写“configure.scan”文件,并将其重命名为“configure.in”,并使用autoconf文件生成configure文件。
使用autotools自动生成makefile文件
利用文本编辑器创建hello.c、hello.h文件
//hello.c
//written by Emdoor
int main()
{
printf("Welcome Emdoor!\n");
return 1;
}
//hello.h
#include <stdio.h>
使用autoscan生成configure.scan
编辑configure.scan,修改相关内容,并将其重命名为configure.in
使用aclocal生成aclocal.m4
使用autoconf生成configure
使用autoheader生成config.h.in
编辑makefile.am
这一步是创建Makefile很重要的一步,automake要用的脚本配置文件是makefile.am,用户需要自己创建这个文件。之后,automake工具将其转换成makefile.in
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=hello
hello_SOURCES=hello.c hello.h
使用automake生成makefile.in
automake
automake –a
使用configure生成makefile
./configure
在这一步中,通过运行自动配置设置文件configure,把makefile.in变成了最终的makefile
使用make生成hello可执行文件
./hello
使用make install将hello安装到系统目录下
make install
hello
使用make dist生成hello压缩包
解压hello压缩包
tar -xzf hello-1.0.tar.gz
进入解压目录,在该目录下安装hello软件
cd ./hello-1.0
./configure
make
./hello
make install
hello
差不多就这么多了,其实都是嵌入式实验的一部分内容,记下了仅作回忆。