-DMACRO选项
-DOS_LINUX将宏定义OS_LINUX
-Idir
添加头文件搜索目录dir
-Ldir
添加链接库搜索目录dir。gcc优先使用共享程序库
-static
仅选用静态程序库进行链接,如果一个目录中静态库和动态库都存在,则仅选用静态库
-g
包括调试信息
-On
优化程序,n代表优化级别,n常为2
-Wall
打开警告信息
TARGET...: DEPENDEDS...
COMMAND
...
...
TARGET:规则定义的目标
DEPENDEDS:执行规则锁必须依赖的条件(DEPENDEDS也可以是某个TARGET,形成嵌套)
COMMAND:规则所执行的命令
变量名 | 含义 | 默认值 |
---|---|---|
AR | 生成静态库文件的程序名称 | ar |
AS | 汇编编译器的名称 | as |
CC | C语言编译器的名称 | cc |
CPP | C语言预编译器的名称 | $(CC) -E |
CXX | C++语言编译器的名称 | g++ |
FC | FORTRAN语言编译器的名称 | f77 |
RM | 删除文件程序的名称 | rm -f |
ARFLAGS | 生成静态库库文件程序的选项 | \ |
ASFLAGS | 汇编语言编译器的编译选项 | \ |
CFLAGS | C语言编译器的编译选项 | \ |
CPPFLAGS | C语言预编译的编译选项 | \ |
CXXFLAGS | C++语言编译器的编译选项 | \ |
FFLAGS | FORTRAN语言编译器的编译选项 | \ |
变量 | 含义 |
---|---|
$* | 表示目标文件的名称,不包含目标文件的扩展名(GUN make特有,其他make不一定支持) |
$+ | 所有依赖项的集合,不会去除重复的依赖项 |
$< | 表示依赖项中第一个依赖文件的名称 |
$? | 依赖项中,所有目标文件时间戳晚的依赖文件,以来稳健之间以空格分开 |
$@ | 目标项中目标文件的名称 |
$^ | 依赖项中,所有不重复的依赖文件,这些文件之间以空格分开 |
使用方法:
VPATH=add:sub(加入add和sub搜索路径)
VPATH 只控制.c/.cpp的查找路径(将.c/.cpp文件所在文件夹添加到VPATH后只用写文件名gcc即可查找到文件),对于.h文件,需要使用-I./header。
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
1“=”
make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:
x = foo
y = $(x) bar
x = xyz
在上例中,y的值将会是 xyz bar ,而不是 foo bar 。
2“:=”
“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。
x := foo
y := $(x) bar
x := xyz
在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。
.PHONY:romfs # .PHONY修饰romfs只有规则,没有依赖
romfs
all:main1 main2 clean
main1: main1.c
@gcc main1.c -o main1
main2: main2.o
@gcc main2.o -o main2
main2.o: main2.c
@gcc -c main2.c
clean:
@rm -f main2.o
但是当我们make之后main2.o仍然存在,怎么回事呢makefile中的all和.PHONY的作用
原来这里的目标clean没有任何依赖,make执行时认为这已经到“根上”了(就是认为磁盘上有clean,就像main2.c),将其忽略(尽管它有规则)。
关键字.PHONY可以解决这问题,告诉make该目标是“假的”(磁盘上其实没有clean),这时make为生成这个目标就会将其规则执行一次。.PHONY修饰的目标就是只有规则没有依赖。
加上一句.PHONY:clean即可:
all:main1 main2 clean
main1: main1.c
@gcc main1.c -o main1
main2: main2.o
@gcc main2.o -o main2
main2.o: main2.c
@gcc -c main2.c
.PHONY:clean
clean:
@rm -f main2.o
makefile中@是不打印命令本身的意思 ,以下Makefile执行make clean时终端不会打印rm -f main2.o的信息。
clean:
@rm -f main2.o
递归调用的方式
make可以递归调用每个子目录中的Makefile。我们可以用如下方式编译add中的文件:
add:
cd add && $(MAKE)
等价于:
add:
$(MAKE) -C add
上面两个例子都是先进入子目录下add中,然后执行make命令。
总控Makefile
调用“$(MAKE) -C”的Makefile叫做总控Makefile。如果总控Makefile中的一些变量需要传递给下层的Makefile,可以使用export命令,如:
export OBJSDIR=./objs
获取匹配模式的文件名wildcard
这个函数功能是查找当前目录下所有符合模式PATTERN的文件名,其返回值是以空格分割的、当前目录下的所有符合模式PATTERN的文件名列表。原型如下:
$(wildcard PATTERN)
例如,如下模式返回当前目录下所有扩展名为.c的文件列表
$(wildcard *.c)
模式替换函数patsubst
这个函数的功能是查找字符串text中按照空格分开的单词,将符合模式pattern的字符串替换成replacement。pattern中的模式可以使用通配符,%代表0到n个字符,当pattern和replacement中都有%时,符合条件的字符将被replacement中的替换。函数的返回值是替换后的新字符串。原型如下:
$(patsubst pattern,replacement,text)
返回值:例如需要将.c文件替换成.o文件可以使用如下模式
$(patsubst %.c,%.o,$(wildcard *.c))
输出的字符串将当前扩展名为.c的文件替换成扩展名为.o的文件列表
循环函数foreach
函数原型:
$(foreach VAR,LIST,TEXT)
foreach将LIST字符串中的一个人空格分隔符的单词,先传给临时变量VAR,然后执行TEXT表达式,TEXT表达式处理结束后输出。其返回值是空格分割表达式TEXT的计算结果。
例如,对于存在add和sub的两个目录,设置DIRS为“add sub ./”包含目录add、sub和当前目录。表达式$(wildcard$(dir)/*.c)
,可以去除目录add和sub及当前目录中的所有扩展名为.c的文件。
DIRS = sub add ./
#查找所有目录下的扩展名为.c的文件,复制给变量FILES
FILES = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))
【make中命令行前面加上减号】
就是,忽略当前此行命令执行时候所遇到的错误。
而如果不忽略,make在执行命令的时候,如果遇到error,会退出执行的,加上减号的目的,是即便此行命令执行中出错,比如删除一个不存在的文件等,那么也不要管,继续执行make。
【make中命令行前面加上at符号@】
就是,在make执行时候,输出的信息中,不要显示此行命令。
而正常情况下,make执行过程中,都是会显示其所执行的任何的命令的。如果你不想要显示某行的命令,那么就在其前面加上@符号即可。
【make中命令行前面加上加号+】
+号修饰符,要求make执行命令,即使使用了-n选项。
make –help
帮助中-n选项的含义
-n, –just-print, –dry-run, –recon
Don’t actually run any commands; just print them.
使用加号修饰符让命令始终执行。
简单的示例Makefile
all:
@echo aaaa
@+echo bbbb
使用make执行的结果为
$ make
aaaa
bbbb
使用make -n执行的结果为
echo aaaa
echo bbbb
bbbb
在内核的 Makefile 中会在多处地方看到 FORCE ,比如:
# vmlinux image - including updated kernel symbols
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
实际上它是一个伪目标:
PHONY +=FORCE
FORCE:
# Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)
从上面看到,FORCE 既没有依赖的规则,其底下也没有可执行的命令。
如果一个规则没有命令或者依赖,而且它的目标不是一个存在的文件名,在执行此规则时,目标总会被认为是最新的。也就是说,这个规则一旦被执行,make 就认为它所表示的目标已经被更新过。当将这样的目标(FORCE)作为一个规则的依赖时(如上的 vmlinux: ),由于依赖总被认为是被更新过的,所以作为依赖所在的规则定义的命令总会被执行。
比如上面的 vmlinux: 在每次 make 时,它下面的这些命令总会被执行:
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE)-f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES
$(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
$(call vmlinux-modpost)
$(call if_changed_rule,vmlinux__)
$(Q)rm -f .old_version
用一个直观的例子可以清楚看到这一点,比如有 1 Makefile 文件:
helloworld:file1.o file2.o
gcc file1.o file2.o -o helloworld
file1.o:file1.c file2.h
gcc -c file1.c -o file1.o
file2.o:file2.c file2.h
gcc -c file2.c -o file2.o
clean:
rm -rf *.o helloworld
PHONY +=FORCE
FORCE:
.PHONY: $(PHONY)
在执行 make 后,观察文件的生成时间:
[beyes@SLinux Makefile]$ ll
total 32
-rw-rw-r--. 1 beyes beyes 129 Apr 16 19:00 file1.c -rw-rw-r--. 1 beyes beyes 924 Apr 16 20:20 file1.o -rw-rw-r--. 1 beyes beyes 108 Apr 16 19:01 file2.c -rw-rw-r--. 1 beyes beyes 139 Apr 16 18:49 file2.h -rw-rw-r--. 1 beyes beyes 880 Apr 16 20:20 file2.o -rwxrwxr-x. 1 beyes beyes 4786 Apr 16 20:20 helloworld -rw-rw-r--. 1 beyes beyes 246 Apr 16 20:20 Makefile
helloworld 文件的生成时间是 20:20
如果将上面的 Makefile 文件的 helloworld:file1.o file2.o 这一句后面加个 FORCE,那么再过几分钟后再 make 时,再观察一下 helloworld 的生成时间,可以看到是重新生成的了,当然在 make 执行时命令的输出也能知道该命令被再次执行:
[beyes@SLinux Makefile]$ ll
total 32
-rw-rw-r--. 1 beyes beyes 129 Apr 16 19:00 file1.c -rw-rw-r--. 1 beyes beyes 924 Apr 16 20:20 file1.o -rw-rw-r--. 1 beyes beyes 108 Apr 16 19:01 file2.c -rw-rw-r--. 1 beyes beyes 139 Apr 16 18:49 file2.h -rw-rw-r--. 1 beyes beyes 880 Apr 16 20:20 file2.o -rwxrwxr-x. 1 beyes beyes 4786 Apr 16 20:26 helloworld -rw-rw-r--. 1 beyes beyes 246 Apr 16 20:20 Makefile
其他资料:
Makefile 使用总结:http://www.cnblogs.com/wang_yb/p/3990952.html