makefile介绍
makefile定义整个工程的编译规则,带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译。
makefile一般有两个过程,编译和链接。编译一般获得所有的依赖和中间文件。链接主要是链接函数和全局变量,来最终生成目标文件。
makefile的基本规则
target : prerequisites
command1
command2
command3
- target就是目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。
- prerequisites是要生成那个target所需要的文件或是目标。
- command也就是需要执行的命令,命令要以tab键开头,否则会报错。
这是makefile的一个最小单元,包含了编译的目标和依赖关系。target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。当prerequisites中如果有文件比target文件要新即依赖文件改变,command所定义的命令就会被执行。
一个简单的demo
hello工程有2个头文件(common_def.h,hello.h)和3个c文件(main.c command.c display.c),最终输出hello.o的可执行文件。
all : main.o command.o display.o
cc main.o command.o display.o -o hello.o
main.o : main.c common_def.h hello.h
cc -c main.c
command.o : command.c common_def.h hello.h
cc -c command.c
display.o : display.c common_def.h hello.h
cc -c display.c
clean :
rm main.o command.o display.o
make的执行过程
在shell下执行make命令即可运行当前目录下的Makefile或makefile文件,然后将文件中的第一个目标(target),在上面的例子中是“all”,将其作为最终目标。首先获取执行“all”这个目标的依赖,如main.o则回去查找main.o对应的目标去执行,最终获取到所有的依赖项,然后执行“all”对应的命令。
最后的clean目标没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行。可以通过make clean这样指定命令执行。
变量的使用
将上面的demo修改为使用变量的形式
objs = main.o command.o display.o
includes = common_def.h hello.h
all : $(objs)
cc $(objs) -o hello.o
main.o : main.c $(includes)
cc -c main.c
command.o : command.c $(includes)
cc -c command.c
display.o : display.c $(includes)
cc -c display.c
clean :
rm $(objs)
make自动推导
make可以自动推导文件以及文件依赖关系后面的命令,make遇见xxx.o 文件目标时自动把 xxx.c 文件加在依赖关系中,并将 gcc -c xxx.c 作为xxx.o的命令。因此上述demo可修改如下:
objs = main.o command.o display.o
all : $(objs)
cc $(objs) -o hello.o
main.o : $(includes)
command.o : $(includes)
display.o : $(includes)
.PHONY : clean
clean :
rm $(objs)
其中.PHONY
表示clean
是个伪目标文件。
makefile书写规则
主要包含两个部分的规则:依赖关系和生成目标的规则。
makefile关键字与命令
判断语句
关键字 | 功能 |
---|---|
ifeq | 判断参数是否相等,相等为 true,不相等为 false。 |
ifneq | 判断参数是否不相等,不相等为 true,相等为 false。 |
ifdef | 判断是否有值,有值为 true,没有值为 false。 |
ifndef | 判断是否有值,没有值为 true,有值为 false。 |
其它
编译选项
关键字 | 功能 |
---|---|
obj-m: | 编译成模块,把文件作为"模块"进行编译,不会编译到内核,但是会生成一个独立的 “ko” 文件 |
obj-y: | 编译到内核 |
obj-n: | 不编译 |
KERNELDIR指的是内核库文件的路径,你的代码中使用的是内核提供的函数,而这些函数也是有具体实现的,在连接成一个内核模块时要说明这些库文件在哪里,方便链接程序把它们连接成一个完成的模块。
“?=”:如果这个KERNELDIR为空说明你没有指定内核库文件的路径,那么它就会给KERNELDIR赋值,因为顶层Makefile通过这个环境变量知道内核库文件在哪里。KERNELDIR=/lib/modules/$(shell uname -r)/build; 其中 shell uname -r 说的是调用shell里头的uname指令 你可以uname -r看看呢是什么,他表示的是内核版本号。一般来说我们构造内核树时,它把内核库统一保存在/lib/modules/内核版本号/build目下。
Makefile里面获取相对路径必须在pwd前面加shell,然后把shell pwd当一个变量来引用,书写形式是:$(shell pwd)
$(MAKE) -C $(KERNELDIR)
转到dir这个目录下面make