您的位置:首页 > 其它

GNUmake 初步介绍

2008-01-30 13:21 211 查看
大家刚开始学习编程的时候,比较典型的是c程序编程。相信很多人都只使用单文件,也就是所有的函数都放在一个文件里面。估计很多人都有过这种感觉(特别是 使用谭浩强的《c语言设计》入门的时候)好像头文件什么用处也没有,大可以把头文件的东西放到c文件的开头。这样像头文件的如下定义很多人就无法理解是做 什么的了。
#ifndef _xxx_h_
#define _xxx_h_
//do something
#endif
其实上面那个声明是c中为了防止在多模块(一个c文件对应一个模块)的工程中重复引用头文件。
好,那么单文件系统有什么问题呢。
程序如果太大了,你会发现只用单个文件不好管理,比如源文件大小超过1M的工程,如果只有一个文件的话,那对程序员来说简直就是噩梦。如果是多人合作编程,单文件要实现同步编程不方便.
假如你只修改了程序的某一个函数,那你要编译的话就只能全部编译,这比较耗时。
在介绍多文件的优点前有必要先说说一般工程的编译过程。
如果我有一个小工程,包含如下文件p1.c p1.h p2.c p2.h p3.c p3.h,用gcc将会如下编译
C:/>gcc -c p1.c -o p1.o
C:/>gcc -c p2.c -o p2.o
C:/>gcc -c p3.c -o p3.o
C:/>gcc -o out p1.o p2.o p3.o
前面三步生成各自的obj文件,最后一部把各个obj文件链接成为目标文件out
多文件有什么好处呢
1.可以多个文件分别编程,控制单个文件的大小,便于维护,便于分模块编程
2.可以方便多人合作,如上我们完全可以由三个人合作,一人负责一个文件的编码和维护。
3.只修改了一个文件的时候,就不需要全部重新编译,只要编译修改过的模块再重新链接一次就行了。节省时间。
既然我们现在已经用多文件的工程了,试想如果我们的工程有几十个几百个文件,那么我们编译的时候岂不是要敲几十个几百个命令。而且有些模块要依赖其他模块的头文件,头文件改了,则依赖其的模块都要重新编译,这可不是容易的工作。
有一个解决方案就是把编译命令做成一个批处理文件。每次就执行这个文件就可以了。但是这样子必然会每次都重新编译所有模块,不管是不是有必要,如果工程大的话可得花不少的时间。
说了那么多废话,终于到介绍make的时候了。
Make的作用就是通过读取一个描述文件makefile来自动编译工程的模块,它能够自动分辨出哪些模块需要重新编译,哪些不需要

说明:本文适合对make不了解的人入门,致力于把打菜鸟变成小菜鸟。因为本人也是刚入门不久的菜鸟,欢迎斧正,哈哈哈。

--Make的基本原理
那么,make是通过什么来判断哪个文件需要重新编译,那个文件不需要呢?其基本的依据是文件的修改时间,根据makefile中的规则,make会递归 的判断目标文件和所依赖的文件之间的修改时间,如果发现依赖文件比目标文件要新(或者目标文件不存在),就会执行相应的命令。

--Make的基本格式
下面是一条最简单的make单元语句
foo.o : foo.c defs.h
gcc -c foo.c
上面的语句可以分成三个部分;
Part1:Part2
part3
第一部分就是foo.o,这个部分是目标(注意这不一定要是文件,你可以随便指定猫阿狗阿什么的,后面解释),可以有多个目标,以空格分开
第二部分foo.c defs.h,这个部分是依赖文件(注意,这个文件可以不存在),同样可有多个,也可以为空,以空格分开
第三部分就是你要执行的命令了,命令可以有多个,每个命令占一行,以Tab符号(制表符)开头。(注意,如果你的makefile文件有问题,最好检查一下每个命令是不是用tab符号开头了

--Makefile的注释
Makefile的注释很简单,就是在需要注释的行开头加个#号,#号之后直到一行结束的语句都是注释。下面是个例子
foo.o:foo.c defs.h # here is the comment
gcc –-c foo.c

--Make的变量
Make中允许定义变量,定义变量有好多种形式,因为这里只是简单介绍,就只介绍最简单的一种,详细介绍可参考gnumake的文档。下面是一个例子
objs=foo1.o foo2.o foo3.o foo4.o foo5.o
program:$(objs)
gcc –-o program $(objs)
上面当make碰到后面两行的时候就会把$(objs)换成foo1.o foo2.o foo3.o foo4.o foo5.o

Make还包含了一些环境变量,系统的环境变量自动成为make的变量,可以用 $(变量名) 的格式之间引用。
Make还有一些自动变量,比较常用的有$@ $^ 和$< 这三个(注意不包含括号)
$@的值自动设成目标名,如果有多个目标名,则是当前起作用的目标
$<是第一个依赖文件的名称
$^自动设为整个依赖文件名称
如下用法
program:foo1.o foo2.o foo3.o foo4.o
gcc –-o $@ $^
上面语句第二行等效于
gcc –-o program foo1.o foo2.o foo3.o foo4.o

--Make的隐含规则
Make有一些隐含的规则,当你写的一个语句只有前两个部分而没有第三个部分,即没有一条命令时,就会触发隐含规则,隐含命令就会起作用。隐含规矩会使用 一些变量,你可以按照自己的需要重新设定。对于c程序,每一个foo.o文件都会用如下规则由相应的foo.c文件生成。
$(CC) -c $(CPPFLAGS) $(CFLAGS) foo.c
我们可以如下定义变量
CC=gcc
CPPFLAGS=-g
CFLAGS=
这样子,如下语句
CC=gcc
foo.o:foo.c foo.h
就等价于
foo.o:foo.c foo.h
gcc –c foo.c
更神奇的是如下语句
CC=gcc
program:foo1.o, foo2.o, foo3.o
$(CC) –o $@ $^
等价于如下语句
program:foo1.o, foo2.o, foo3.o
$(CC) –o $@ $^
foo1.o: foo.c
$(CC) –c foo.c
foo2.o: foo.c
$(CC) –c foo.c
foo3.o: foo.c
$(CC) –c foo.c

--.phony目标
如果我的makefile是如下语句将会如何呢?
clean:
delete *.o
分两种情况
如果存在clean这个文件,则因为没有依赖文件,clean被判断为已经是最新的,没有动作
如果不存在clean这个文件则只要你不通过其他途径建立clean这个文件,则每次执行的时候都会删除所有的.o文件。这个可以用来重新编译整个工程。
怎样使得即使存在clean文件,其命令都执行呢。.phony目标就有用了
.phony:clean
clean:
delete *.o
这样因为clean被定义成一个phony目标,则不管clean是否存在,都执行delete *.o这条命令。

--Make的运行
你可以在命令行打如下命令
gnumake
这样make就会自动寻找当前目录下名为makefile或者是MakeFile的文件并解释里面的规则。你也可以指定文件名如下
gnumake -f yourmakefile
还可以在命令行设定变量如下
gnumake mylove=xxmm
这样你的makefile就相当于多了一个值为xxmm的变量mylove
一个文件有好多条规则,注意,gnumake只执行一条规则,其他规则的执行是由依赖关系递归执行的,举个例子,makfile内容如下,文件t1,t2,t3,t4,t5都不存在
t1:t2
command1
t2:t3
command2
t3:t4
command3
t4:
command4
t5:
command5

如果你在命令行敲入
gnumake
make会执行第一条规则,执行顺序如下
Make现在的目标是t1,t1依赖t2则在此之前先要检查t2,然后又发现t2依赖t3,类似的再检查t3,t4.最后发现t4不依赖任何文件,于是就按如下顺序执行命令
command4
command3
command2
command1
可以看到,command5完全没有执行
怎么样使command5执行呢,如下命令。
gnumake t5
这时候make执行的是command5

一个makefile例子的分析

接下来分析一个简单的makefile文件,以便对以上的概念加深理解,例子是从Bruce Eckel书的代码中剽窃的,我做了一些修改。中文是我的注解
#下面两行定义两个变量,在隐含规则中使用
CC= gcc
OFLAG = -o
#下面六行是主规则,注意了该规则没有相应的command。前面忘
#了说了反斜杠/代表跟后面一行连接。下面六行等价于
#all: CLibTest LibTestScoperes
all: /
CLibTest /
LibTest/
Sizeof /
StackTest /
Scoperes

#下面6行的作用是编译并测试,test只用一个依
#赖all,执行的命令有五个
test: all
CLibTest
LibTest
Sizeof
StackTest StackTest.c
Scoperes

#以下就是生成每个可执行文件的规则
CLibTest: CLibTest.o CLib.o
$(CC) $(OFLAG)$@ $^
LibTest: LibTest.o Lib.o
$(CC) $(OFLAG)$@ $^
Sizeof: Sizeof.o
$(CC) $(OFLAG)$@ $^
StackTest: StackTest.o Stack.o
$(CC) $(OFLAG)$@ $^
Scoperes: Scoperes.o
$(CC) $(OFLAG)$@ $^

#下面运用隐含规则生成相应的.o文件
CLib.o: CLib.c CLib.h
CLibTest.o: CLibTest.c CLib.h
Lib.o: Lib.c Lib.h
LibTest.o: LibTest.c Lib.h ../require.h
Sizeof.o: Sizeof.c CLib.h Lib.h
Stack.o: Stack.c Stack.h ../require.h
StackTest.o: StackTest.c Stack.h ../require.h
Scoperes.o: Scoperes.c

#下面运用PHONY目标来重新编译全部文件。
.PHONY:clear
clean:
delete *.o
rebuild:clean all

分析如果要编译工程,只要执行
gnumake
就可以生成所有5个可执行文件
执行
gnumake rebuild
就会删除生成的所有.o文件重新编译
gnumake test
则会生成所有可执行文件只有再分别运行各个可
执行文件

--the end

本文只是对make的粗略介绍,有很多概念如函数,自动生成依赖文件,递归生成makefile
以及makefile中包含另一个makefile等等都没有涉及到。
想要了解更多的GNUmake方面的知识,可以参考http://www.gnu.org/software/make/manual/make.html 上面的文档
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: