一、Makefile的功能
高效地编译程序
在我们使用KEIL、Visual Studio编译程序时,会发现修改源文件或头文件,只需要重新编译牵涉到的文件,就可以重新生成可执行程序。
Makefile就是一个可以帮你自动进行高效编译的脚本。
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
二、Makefile的基本规则
一个简单的Makefile样式:
目标(target)…: 依赖(prerequiries)…
命令(command)
例如:
hello: hello.c
gcc -o hello hello.c
命令被执行的 2 个条件:
1、依赖文件比目标文件新。
2、目标文件还没生成。
三、Makefile的使用步骤
(1)在程序目标文件新建一个名为Makefile
的文件,文件内写入:
hello: hello.c
gcc -o hello hello.c
(2)在程序目标文件新建hello.c
,文件内写入:
#include <stdio.h>
int main()
{
printf("hello world!\n");
return 0;
}
新建完成后目标文件夹如下:
(3)命令行输入make
make
得结果
四、Makefile的常用知识
(1)make命令的使用
执行 make 命令时,它会去当前目录下查找名为“Makefile”的文件,并根据它的指示去执行操作,生成第一个目标。
1、我们可以使用“-f”选项指定文件,不再使用名为“Makefile”的文件,比如:
make -f Makefile4
2、我们可以使用“-C”选项指定目录,切换到其他目录里去,指定目录为Makefile存在目录,比如:
make -C /nfs_rootfs/makefile_code/TEXT -f Makefile
3、我们可以指定目标,不再默认生成第一个目标:
make -C a/ -f Makefile.build hello2
(2)即时变量、延时变量:
在 GNU make 中对变量的赋值有两种方式:延时变量、立即变量。
**延时变量:**在这个变量使用时才扩展, 即当真正使用时这个变量的值才确定。
**立即变量:**在定义这个变量时,它的值就确定了。
延时变量的定义:
延时变量用 =, ?=, 定义, 或用define 定义。
如: GCC = arm-linux- GCC ?= arm-linux-
注意 ?= 用来定义第一次出现的延时变量。
立即变量的定义:
:= 用来定义立即变量,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
如: GCC := arm-linux-
特殊的赋值:
有一种特殊的赋值特殊的赋值+=
GCC += arm-linux-
如果 GCC 在前面是延时变量,那么现在它还是延时变量
如果 GCC 在前面是立即变量,那么现在它还是立即变量
makefile中的内部变量:
$@ 扩展为当前规则的目标文件名;
$< 扩展为当前规则依赖文件列表中的第一个依赖文件;
$? 扩展为所有的修改日期比当前规则的目标文件的创建日期更晚的依赖文件,该值只有在使用显式规则时才会被使用;
$* 扩展成当前规则中目标文件和依赖文件共享的文件名,不含扩展名;
$^ 扩展为整个依赖文件的列表 ( 除掉了所有重复的文件名 ) 。
(3)变量的导出(export):
在编译程序时,我们会不断地使用“make -C dir”切换到其他目录,执行其他目录里的 Makefile。如果想让某个变量的值在所有目录中都可见,要把它 export 出来。
比如CC = $(CROSS_COMPILE)gcc
,这个 CC 变量表示编译器,在整个过程中都是一样的。定
义它之后,要使用export CC
把它导出来。
(4)Makefile 中可以使用 shell 命令:
比如:
TOPDIR := $(shell pwd)
这是个立即变量,TOPDIR 等于 shell 命令 pwd 的结果。
(5)在 Makefile 中怎么放置第 1 个目标:
执行 make 命令时如果不指定目标,那么它默认是去生成第 1 个目标。所以“第 1 个目标”,位置很重要。有时候不太方便把第 1 个目标完整地放在文件前面,这时可以在文件的前面直接放置目标,在后面再完善它的依赖与命令。比如:
First_target: // 这句话放在前面
.... // 其他代码,比如 include 其他文件得到后面的 xxx 变量
First_target : $(xxx) $(yyy) // 在文件的后面再来完善
command
(6)假想目标:
我们的 Makefile 中有这样的目标:
clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
如果当前目录下恰好有名为“clean”的文件,那么执行“make clean”时它就不会执行那些删除命令。这时我们需要把“clean”这个目标,设置为“假想目标”,这样可以确保执行“make clean”时那些删除命令肯定可以得到执行。
使用下面的语句把“clean”设置为假想目标:
.PHONY : clean
(7)常用的函数:
1、$(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,
(
o
b
j
s
)
,
.
(objs), .
(objs),.(f).d) // 最终 dep_files := .a.o.d .b.o.d
2、 $(wildcard pattern)
pattern 所列出的文件是否存在,把存在的文件都列出来。
例子:
src_files := $( wildcard *.c) // 最终 src_files 中列出了当前目录下的所有.c 文件
3、 $(filter pattern…,text)
把 text 中符合 pattern 格式的内容,filter(过滤)出来、留下来。
例子:
obj-y := a.o b.o c/ d/
DIR := $(filter %/, $(obj-y)) //结果为:c/ d/
4、$(filter-out pattern…,text)
把 text 中符合 pattern 格式的内容,filter-out(过滤)出来、扔掉。
例子:
obj-y := a.o b.o c/ d/
DIR := $(filter-out %/, $(obj-y)) //结果为:a.o b.o
5、 $(patsubst pattern,replacement,text)
寻找text’中符合格式
pattern’的字,用replacement’替换它们。
pattern’和`replacement’
中可以使用通配符。
比如:
subdir-y := c/ d/
subdir-y := $(patsubst %/, %, $(subdir-y)) // 结果为:c d
(8)通用 Makefile 的设计思想
1、在 Makefile 文件中确定要编译的文件、目录,比如:
obj-y += main.o
obj-y += a/
“Makefile”文件总是被“Makefile.build”包含的。
2、在 Makefile.build 中设置编译规则,有 3 条编译规则:
i. 怎么编译子目录? 进入子目录编译:
$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build
ii. 怎么编译当前目录中的文件?
%.o : %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
iii. 当前目录下的.o 和子目录下的 built-in.o 要打包起来:
built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^
3、顶层 Makefile 中把顶层目录的 built-in.o 链接成 APP:
$(TARGET) : built-in.o
$(CC) $(LDFLAGS) -o $(TARGET) built-in.o