|
make 和 Makefile
make 的几个常用参数:
-k : 产生错误时尽可能不停止 make 。当目标失败时,那些依赖于它的目标或文件不能被重新make;而这个目标所依赖的文件仍然能被处理。
-n : 打印出要被执行的命令,但实际上不会去执行它们。
-f <filename> : 指定 makefile 文件,如果不指定,默认就寻找 makefile 或 Makefile 。
-C dir : 读入指定目录下的 Makefile
-I dir : 指定被包含的 Makefile 所在的目录
-i : 忽略所有的命令执行错误
-s : 在执行命令时不显示命令
-w : 如果 make 在执行过程中改变目录,打印当前目录名
-p : 显式 make 变量数据库和隐含规则
如果要输出几个目标,那就使用 Makefile 里使用 all ,如 all: myapp1 myapp2 。如果想输出多个目标时而没有用 all ,那么只会输出第一个目标。
一个简单的实例:myapp: main.o 2.o 3.o
gcc -o myapp main.o 2.o 3.o
main.o: main.c a.h
gcc -c main.c
2.o: 2.c a.h b.h
gcc -c 2.c
3.o: 3.c b.h c.h
gcc -c 3.c 其中 a.h b.h c.h 的内容都为空; main.c 中的内容为:#include <stdio.h>
#include <stdlib.h>
#include "a.h"
extern void function_two();
extern void function_three();
int main()
{
function_two();
function_three();
exit(EXIT_SUCCESS);
} 其中 2.c 中的内容为:#include "a.h"
#include "b.h"
void function_two(){
} 其中 3.c 中的内容为:#include "b.h"
#include "c.h"
void function_three() {
} 说明:make 过之后会生成 myapp 可执行文件。如果把其中一个目标文件如 2.o 删除,那重新 make 一次,就如下所示:beyes@linux-beyes:~/MAKE/exp1> make
gcc -c 2.c
gcc -o myapp main.o 2.o 3.o 这里,因为之前删除了 2.o 这个目标文件,所以重新 make 时,也仅仅是再次生成 2.o 此文件,而不会把所有的内容全部 make 过一遍。
Makefile 中使用宏
为了方便,可以在 makefile 文件中使用宏。
宏的定义方法是:
MACRONAME=value # = 号两边允许有空格
在使用宏时写成如下形式:
$(MACRONAME) 或 ${MACRONAME} ,有些 make 版本也接受 $MACRONAME 这种形式。
使用示例:
# Where are include files kept # Options for development # CFLAGS = -O -Wall -ansi $(CC) -o myapp main.o 2.o 3.o $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
说明:上面,在 make ,宏就会按照事先定义好的值展开,原理和 C 语言中的 #define 一样。
另外,gcc 的 -I dir 选项可以在头文件的搜索列表中添加 dir 目录。这时,GCC 就会到相应的位置查找对应的目录。如上面的,就在当前目录寻找头文件。
上面的 make 假设同一目录下所有的 .c 和 .h 文件都已存在,那么运行输出:beyes@linux-beyes:~/MAKE/exp2> make
gcc -I. -g -Wall -ansi -c main.c
gcc -I. -g -Wall -ansi -c 2.c
gcc -I. -g -Wall -ansi -c 3.c
gcc -o myapp main.o 2.o 3.o 自动变量(内置宏)
自动变量通常可以代表语句中出现目标文件和依赖文件等,并且具有本地含义( 即下一语句中出现的相同变量代表的是下一语句目标文件和依赖文件),Makefile 中常见的自动变量:
变量名(宏名)
| 定义
| $*
| 不包括扩展名的目标文件名称
| $+
| 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件
| $<
| 第一个依赖文件的名称
| $@
| 当前目标文件的名称
| $^
| 所有不重复的依赖文件,以空格分开
| $%
| 如果目标是归档成员,则该变量表示目标的归档成员名称
| $?
| 所有时间戳比目标文件晚的依赖文件,并以空格分开
| 示例说明,先把上面的 makefile 文件里相关的内容用内置宏来替换: # Where are include files kept # Options for development # CFLAGS = -O -Wall -ansi $(CC) -o $@ main.o 2.o 3.o $(CC) -I$(INCLUDE) $(CFLAGS) -c $< $(CC) -I$(INCLUDE) $(CFLAGS) -c $< $(CC) -I$(INCLUDE) $(CFLAGS) -c $<
make 后输出:beyes@linux-beyes:~/MAKE/exp2> make -f Makefile1
gcc -I. -g -Wall -ansi -c main.c
gcc -I. -g -Wall -ansi -c 2.c
gcc -I. -g -Wall -ansi -c 3.c
gcc -o myapp main.o 2.o 3.o 上面,第 11 行中 $@ 符号展开后,其实就是 myapp;
第13,15,17行中的 $< 在每一行中分别对应第一个依赖文件的名称,这里分别对应着main.c, 2.c,3.c。
11行可以改写成:$(CC) $^ -o $@ 这样输出的结果也一样,因为 $^ 表示所有不重复的依赖文件,各文件名以空格分开。
在 13 行中,还可以改成: $(CC) -I$(INCLUDE) $(CFLAGS) -c $< -o $*.o
因为 $* 代表的是不包含扩展名的目标文件名称,所以为了输出 main.o ,所以在 $^ 后自行添加了后缀名 .o 。
多目标
有时在一个 makefile 里会有多个目标,下面的例子中就有 clean 和 install 两个,看代码:# Where are include files kept # Options for development # CFLAGS = -O -Wall -ansi $(CC) -o myapp main.o 2.o 3.o $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c chmod a+x $(INSTDIR)/myapp;\ chmod og-w $(INSTDIR)/myapp;\ echo "Installed in $(INSTDIR)";\ echo "Sorry, $(INSTDIR) does not exist";\
说明:
上面的 makefile 文件中,增加了两个目标,分别是 clean 和 install 。
clean 是一个目标,它的作用是清除掉所有的 .o 文件,在这里使用了 rm 命令。
在 21 行中,最前面的 @ 号是说明在执行 rm 命令时,不会把命令执行结果输出到终端;接着的 - 号告诉 make 忽略掉命令的输出结果。所以,尽管没有目标以及 rm 返回错误的结果,但 make clean 仍然会成功执行。这里注意,clean 不是一个文件,它只不过是一个动作名字,也可称其为标签,其冒号后什么都没有。这样,make 就不会自动去查找文件之间的依赖关系,因此也不会自动执行其后所定义的命令,除非指定 clean 为一个目标(make clean)时才会去执行其后的命令 。
install 目标依赖于 myapp 文件,所以 make 会先创建 myapp 后才来执行 install 后的命令。同样的,在 23 行中的 @ 号就是要在执行其后命令时不把执行结果输出到终端中。
需要说明的是,在 install 时,默认情况下,在执行下一条命令前是不会检查上一条命令是否正确的。假如说,前面的命令执行成功与否对后面的命令执行很重要,那么可以把上面的相关代码改成:
chmod a+x $(INSTDIR)/myapp; &&\ chmod og-w $(INSTDIR)/myapp; &&\
也就是说,在没一条的指令后面都加了 && 符号。
注意:在 make install 时,由于 make 会调用一个新 shell 来执行每条命令规则,所以必须增加一个反斜杠到每行语句之后。这样所有的脚本命令都会作为一个逻辑行并整个的传递到一个 shell 的单独调用中并获得执行。
内置默认规则
一段测试代码为:
#include <stdlib.h>
#include <stdio.h>
int main()
{
printf("Hello World\n");
exit(EXIT_SUCCESS);
} 用 make 来编译,默认情况下有:beyes@linux-beyes:~/MAKE/exp4> make foo
cc foo.c -o foo 由上可见,默认的编译器是 cc 而不是 gcc (在 linux 这是可以的,因为 cc 通常是 gcc 的一个链接 )。其实默认的规则里也是对宏进行展开后所得,故通过给相关宏指定新值就可以改变这些默认的属性:beyes@linux-beyes:~/MAKE/exp4> make CC=gcc CFLAGS="-Wall -g" foo
gcc -Wall -g foo.c -o foo 在 make 中使用参数 -p 可以看到所有的内置默认规则。 |
|