Makefile实例
在c.c里面,包含一个头文件c.h,在c.h里面定义一个宏,把这个宏打印出来。
c.c:
#include <stdio.h>
#include <c.h>
void func_c()
{
printf("This is C = %d\n", C);
}
c.h
#define C 1
然后上传编译,执行./test
,打印出:
测试没有问题,然后修改c.h
重新编译,发现没有更新程序,运行结果不变,说明现在的Makefile存在问题。
为什么会出现这个问题呢?首先test依赖c.o,c.o依赖c.c,如果我们更新c.c,会重新更新整个程序。但c.o也依赖c.h,我们更新了c.h,并没有在Makefile上体现出来,导致c.h的更新,Makefile无法检测到。
因此需要添加:
c.o: c.c c.h
现在每次修改c.h,Makefile都能识别到更新操作,从而更新最后输出文件。
我们怎么为每个.c文件添加.h文件呢?对于内核,有几万个文件,不可能为每个文件依次写出其头文件。因此需要做出改进,让其自动生成依赖
gcc -M c.c // 打印出依赖
gcc -M -MF c.d c.c // 把依赖写入文件c.d
gcc -c -o c.o c.c -MD -MF c.d // 编译c.o, 把依赖写入文件c.d
修改Makefile
objs = a.o b.o c.o
dep_files := $(patsubst %,.%.d,$(objs))
dep_files := $(wildcard $(dep_files))
test: $(objs)
gcc -o test $^
ifneq($(dep_files),)
include $(dep_files)
endif
%.o:%.c
gcc -c -o $@ $< -MD -MF .$@.d
clean:
rm *.o test
disclean:
rm $(dep_files)
.PHONY:clean
首先用obj变量将.o文件放在一块。
利用前面讲到的函数,把obj里所有文件都变为.%.d格式,并且用变量dep_files表示。
利用前面介绍的wildcrad函数,判断dep_files是否存在。
如果dep_files变量不为空,就将其包含进来。
再添加CFLAGS,即编译参数。比如加上编译参数-Werror,把所有的警告当成错误。
CFLAGS = -Werror -Iinclude
...
%.o:%.c
gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d
CFLAGS是一个变量,用于存储传递给编译器的编译选项,具体来说,CFLAGS中包含了两个选项:
- -Werror:将编译警告(warnings)视为错误(errors)。通常,编译器会生成一些警告,但它们不会阻止程序的编译。使用’-Werror’选项会将这些警告当做错误,如果有任何警告,编译过程将被中断。
- -Iinclude:将指定目录’include’添加到头文件搜索路径中。这对于告诉编译器在哪里找到包含的头文件是很有用的。
总体来说,使用双引号“file.h”会首先在当前目录中查找头文件,然后再去系统目录中查找。而使用尖括号<file.h>只会在系统目录中查找。
选择使用哪一种形式通常取决于头文件是项目内的还是标准库的