三、Makefile的使用
1、为什么需要Makefile
在编写程序后,如果仅改动了一个源文件(比如.h文件),那么不可能通过一系列的命令来重新编译所有的源文件,甚至有时改动的源文件比较多,出现最后忘记编译某些源文件的情况。而make工具可以解决上述问题,它会在有必要时重新编译所有受改动影响的源文件。而Makefile文件则告诉make怎样编译和连接成一个程序。Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
Makefile文件一般和项目的其他源文件放在同一目录下。
2、Makefile的简单规则:
一个简单的Makefile文件包含一系列的“规则”,其样式如下:
目标(target):依赖(prerequiries) //目标程序或者文件 依赖于: 依赖文件
<tab>命令(command) //如果“依赖文件”比“目标文件”更加新(即发生更改),那么执行“命令”来重新生成“目标文件”
命令被执行的2个条件:依赖文件比目标文件新,或是 目标文件还没生成。
命令是Make执行的动作,一个规则可以含有几个命令,每个命令占一行。注意:每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。这是不小心容易出错的地方。
3、先介绍Makefile的2个函数
A. $(foreach var,list,text)
简单地说,就是 for each var in list, change it to text。
对list中的每一个元素,取出来赋给var,然后把var改为text所描述的形式。
例子:
objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d) // 最终 dep_files := .a.o.d .b.o.d
B. $(wildcard pattern)
pattern所列出的文件是否存在,把存在的文件都列出来。
例子:
src_files := $( wildcard *.c) // 最终 src_files中列出了当前目录下的所有.c文件
4、当使用make命令时,会先在当前目录下寻找名为Makefile的文件,然后执行Makefile中的命令,并在终端上显示执行的命令,如:
book@100ask:~/04_test_Makefile$ make
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o
如果目标程序/文件无需更改,则会提示目标程序已经是最新的了:
book@100ask:~/04_test_Makefile$ make
make:'test' is up to date.
5、创建Makefile文件时选择文件格式为Makefile
6、命令的执行是有顺序的:假如Makefile文件的命令如下:
test : main.o sub.o
gcc -o test main.o sub.omain.o : main.c
gcc -c -o main.o main.csub.o : sub.c
gcc -c -o sub.o sub.c
第一个目标文件test依赖于main.o 和sub.o,因为main.o不存在,跳到第二段命令,所以先执行gcc -c -o main.o main.c;然后回到第一行,发现sub.o也不存在,所以跳到第三段,执行gcc -c -o sub.o sub.c;再回到第一行,两个依赖文件都有了,满足条件生成目标文件test
7、make clean:只执行Makefile中的clean这个目标文件的相关命令。
8、$@ 表示全部的目标文件,$^ 表示全部的依赖文件,$< 表示第一个依赖文件
%.o :%.c
gcc -c -o $@ $^
9、当一个目标文件有两依赖文件时,其中一个有命令,另一个没命令,那么这两依赖文件会合并在一起,更改没有命令的依赖文件也会触发另一个依赖文件的命令。
但是没有命令的依赖文件下面需要空一行并且空行不能有Tab键。
10、想达到的效果:修改源文件或者头文件时,只要使用make命令重新编译牵涉到的文件,就可以重新生成APP:请看4-3-1节(听不懂)
①为什么make clean可以反复使用?clean不是个test那样的程序吗?如果clean存在了为什么还可以反复生成?
②为什么要改成.main.d的文件格式?
③objs := main.o sub.o是什么用法?
④为什么使用objs时要使用${objs}的形式?
11、通用Makefile的使用方法:
(1)把顶层Makefile, Makefile.build放入程序的顶层目录
在各自子目录创建一个空白的Makefile
(2)确定编译哪些源文件
步骤:
(一)按照需要修改和添加顶层目录中Makefile的obj-y
(二)在每个子目录中空白的Makefile中添加obj-y
对obj-y 的说明:
obj-y += xxx.o
obj-y += yyy/
①这表示要编译当前目录下的xxx.c(把.c文件编译得到.o文件)和yyy子目录(yyy目录中的Makefile文件又会指定该目录下需要编译的文件和子目录)
②表示yyy是子目录的斜杠 / 不可省略
(3)确定编译选项、链接选项
步骤:
(一)修改顶层目录Makefile的CFLAGS,这是编译所有.c文件时都要用的编译选项;
顶层的Makefile文件中有这样一段:
CFLAGS += -I $(shell pwd)/include
因为一个工程文件中所有的头文件都放在include目录中,所以这一段表示:编译所有.c文件时都会查找该文件下的include目录(中的头文件)
(二)修改顶层目录Makefile的LDFLAGS,这是链接最后的应用程序时的链接选项;
LDFLAGS可以在链接时指定库的位置和库的名称:
LDFLAGS := -L dir -labc
-L 是指定库的位置是当前位置下的dir目录,-l (小写L)指定库的名称为abc
(三)设置其他编译选项(可不设置)
修改各自子目录下的Makefile:
"EXTRA_CFLAGS", 它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置
"CFLAGS_xxx.o", 它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置
(4)使用哪个编译器?
修改顶层目录Makefile第一行的CROSS_COMPILE, 用来指定工具链的前缀(比如arm-linux-)
如果是在gcc下编译则不进行赋值
(5)确定应用程序的名字:
修改顶层目录Makefile的TARGET, 这是用来指定编译出来的程序的名字,如:
TARGET := test
(6)执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除
(7)编译成功后时会提示“xxx has been built !”
(8)通用Makefile的源码位置:【第4篇】嵌入式Linux应用开发基础知识/source/05_general_Makefile
12、通用Makefile的解析: 4-3-3节,跳过
分析了Makefile中的命令的含义和Makefile的设计思路
资料请参考第4章《嵌入式linux应用开发基础知识》的3.0.3节和3.1节