整个软件系统被划分为几个小的子系统,而每个小的子系统又划分为几个独立工作的由一组文件组成的模块时,就涉及到模块之间的协调问题。
make工具的作用就是当一个模块被修改后,保证其他模块与之相关的部分也随之改变,进而不会影响模块之间的协调关系。
make本身是一个单独工作的程序,可以根据程序模块的修改情况重新编译链接目标代码,以保证目标代码总是由它的最新模块组成。
要使用make,必须编写一个称为Makefile的文件。它描述了软件包中各个文件之间的关系,提供了更新每个文件的命令。
在一个软件包里,软件更新顺序:
Makefile的每个相关行说明一个目标依赖于哪几个文件,以及生成或更新目标时所需要的命令。
#这里写注释
目标:依赖项列表
[命令]
目标:依赖项列表
[命令]
...
注:
make命令:
当一个适当的Makefile存在时,每次改变某些源文件,用简单的shell命令:
make [选项][宏][目标]
将足以完成所有必须的重新编译。
如果只输入
make
而未指定其他任何参数,make将对Makefile的第一行目标进行维护(意思就是只对第一行的目标所层层依赖的关系进行搜索更新)。
make程序也可以指定要进行维护的目标:
make module.o
就只把module1.o当作目标,而只考虑它所依赖的文件的更新。
如果想使用自己指定的Makefile文件,可以使用如下命令:
make -f filename
这样,make就在当前目录下寻找文件名为filename的Makefile文件,并读入该文件的相关行。
也可以指定执行Makefile文件中的某一行指令:
例如Makefile文件内容如下:
exe:exe.o f.o
gcc exe.o f.o -o exe
exe.o: exe.c f.h
gcc -c exe.c
f.o:f.c f.h
gcc -c f.c
clean:
rm -f exe *.o
使用以下指令:
make -f exe clean
则执行的是 rm -f exe *.o
命令,即清除该目录下exe文件和 .o格式的文件。
例子:
应用程序exe1,它由两个目标代码模块组成,分别为 module1.o 和 module2.o ;并且 module1.o 依赖于 module1.c 和 module1.h , module2.o 依赖于 module2.c 和 module2.h 。
则Makefile应该这样写:
exe1:module1.o module2.o
gcc module1.o module2.o -o exe1
module1.o:module1.c module1.h
gcc -c module1.c
module2.o:module2.c module2.h
gcc -c module2.c
Makefile文件有两种方式产生:
自动生成
手动编写,当文件很多时,这种方法是很繁琐的,也不能保证其正确性;我们倾向于使用自动生成。
编译器可以自动的从源码文件中产生文件的相互依赖关系。当编译器编译每一个源码文件时,它知道应该包括什么样的头文件。当使用gcc时,用-M开关,它可以为每一个输入的C语言源文件输出一个依赖规则,把gcc将要生成的目标文件作为Makefile规则的目标文件,而把生成这个目标文件的C语言源文件和所有应该被引用的头文件作为依赖文件。
上述方法gcc并不区分系统的头文件和程序自带的头文件。由于在一般情况下不会修改系统头文件,为了避免输出的依赖关系中包含系统头文件,可以用-MM参数来代替-M传递给gcc。
gcc只输出规则的依赖关系,不含有命令部分。用户可以自己写入需要的命令,或者什么也不写,make会使用隐含规则。
Makefile里的变量就像一个环境变量。这种变量对大小写敏感,一般使用大写字母。好处就是,如果变量的值发生变化,就只需要在一个地方修改,从而简化了Makefile的维护。
作用:
使用:
Makefile中的变量使用一个字符串在Makefile中定义的,这个文本串就是变量的值。只要在一行的开始写下这个变量的名字,后面跟一个“=”号,然后跟要设定的这个变量的值,即可定义变量。
变量名=字符串
使用时,把变量用括号括起来,并在前面加上“$”符号,就可以引用变量的值。
$(变量名)
Makefile中的默认变量(略)
例子:
将上面的Makefile用变量重新编写后得:
#原文件
exe:exe.o f.o
gcc exe.o f.o -o exe
exe.o: exe.c f.h
gcc -c exe.c
f.o:f.c f.h
gcc -c f.c
clean:
rm -f exe *.o
#新文件
OBJS=exe.o f.o
C=-c
exe:$(OBJS)
gcc $(OBJS) -o exe
exe.o: exe.c f.h
gcc $(C) exe.c
f.o:f.c f.h
gcc $(C) f.c
clean:
rm -f exe *.o
在Makefile中,并不是所有的目标都对应于磁盘上的文件,有的目标的存在只是为了形成一条规则,从而使make完成特定的工作,并不生成新的目标文件,这样的目标成为伪目标。例如上面例子中的clean就是。常见的伪目标有all、clean等。
例如:
all:exe1 exe2 exe3
exe1:exe1.c exe1.h
gcc exe1.c -o exe1
exe2:exe2.c exe2.h
gcc exe2.c -o exe2
exe3:exe3.c exe3.h
gcc exe3.c -o exe3
clean:
rm -f exe*
其中,all、clean即为伪目标,一个伪目标和一个正常的目标几乎是一样的,只是伪目标的目标文件不存在。以上Makefile中的第一条规则吓得命令行为空,make不会执行任何动作,只是检查依赖文件的更新情况,所以会扫描剩下的几条规则并执行响应的编译命令生成可执行文件。
同样,由于没有任何其它规则依赖的clean,在命令行执行make时,这条规则将不会被执行,但是,如果明确使用命令make clean,make会把命令行上的参数clean作为它的目标,并执行对应的删除命令。
条件语句包含三条指令:ifeq、else和endif。
例如:
ifeq($(VAR),1)
gcc -o exe1 module
else
gcc -o exe2 moudle
endif
#后跟Makefile文件中的正常内容
上述条件语句说明在变量VAR=1时,把moudle模块编译输出文件为exe1,不等于时,把moudle模块编译输出文件为exe2。
通过-d选项使make在执行命令时打印调试信息,这些信息包括以下的内容: