一:基本概念
-
目标(target):
位于‘:’的前面,其名字可以是由字母和下划线‘_’组成。 -
假目标(phony target)
:用于解决所定义的目标与所存在的文件是同名的的问题。实例:.PHONY: clean -
先决条件(prerequisites)
:生成某个目标时,所依赖的目标。通常位于’:’后面。 -
命令(commands)
:通常位于目标下面。每条规则中的命令和操作系统 Shell 的命令行是一致的。make 会按顺序一条一条地执行命令,
每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的
。在命令行之间中的空格或是空行会被忽略,但是,如果该空格或空行是以 Tab 键开头的,那么 make 会认为其是一个空命令。 -
规则
:- 由目标(targets)、先决条件(prerequisites)以及命令(commands)所组成的。
- Makefile的基本单元。
注
:
目标和先决条件之间表达的就是依赖关系(dependency),这种依赖关系指明在构建目标之前,必须保证先决条件先满足(或构建)。而先决条件可以是其它的目标,当先决条件是目标时,其必须先被构建出来。 还有就是一个规则中目标可以有多个,当存在多个目标,且这一规则是 Makefile 中的第一个规则时,如果我们运行 make 命令不带任何目标,那么规则中的第一个目标将被视为是缺省目标。
- 实例
源文件:
//foo.c
#include <stdio.h>
void foo ()
{
printf (“This is foo ()!\n”);
}
//main.c
extern void foo ();
int main ()
{
foo ();
return 0;
}
makefile初版1
#-c:表示只编译不链接。
all: main.o foo.o
gcc -o simple main.o foo.o
main.o: main.c
gcc -o main.o -c main.c
foo.o: foo.c
gcc -o foo.o -c foo.c
clean:
rm simple.exe main.o foo.o
二:变量
-
概念
:一个变量的定义很简单,就是一个名字(变量名)后面跟上一个等号,然后在等号的后面放这个变量所期望的值。对 于变量的引用,则需要采用
$(变量名)
或者
${变量名}
这种模式。 -
变量的分类
:
1. 自动变量
:用于解决目标和先决条件的名字会在规则的命令中多次出现的问题。
$@:用于表示一个规则中的目标。当我们的一个规则中有多个目标时, $@所指的是其中任何造成命令被运行的目标。
$^:则表示的是规则中的所有先择条件。
$<:表示的是规则中的第一个先决条件。
2. 特殊变量
:make程序定义好的变量。
CC = cc #c语言编译器的名称
CPP = $(cc) -E #c文件预处理器的名称
CFLAGS #C文件的编译选项
CPPFLAGS #C文件预处理的编译选项
CXXFLAGS #CPP文件的编译选项
LDFLAGS #连接的动态库
CURDIR := /home/zxy/... #当前路径
MAKEFLAGS = p #make命令选项
RM = rm -f
VPATH #文件的搜索路径
MAKE:指make命令名是什么。当我们需要在 Makefile 中调用另一个 Makefile 时需要用到这个变量, 采用这种方式,有利于写一个容易移植的 Makefile。
MAKECMDGOALS: 指的是用户输入的目标。
-
变量的类别
(赋值)
1. 递归扩展变量(recursively expanded variable)
:使用“=”操作符进行变量定义和赋值,递归扩展变量的引用是递归的。 其值为推导到最后一次的赋值值。
2. 简单扩展变量(simply expanded variables)
:使用“:=”操作符来定义的。 对于这种变量, make 只对其进行一次扫描和 替换。
3.
条件赋值符
:使用“?=”操作符来定义的。条件赋值的意思是当变量以前没有定义时,就定义它并且将左边的值赋值给
它,如果已经定义了那么就不再改变其值。条件赋值类似于提供了给变量赋缺省值的功能。
4.
追加赋值
:使用“+=”操作符来定义的。原变量用追加一个新值。
5.
多行变量
define two-lines
echo foo
echo $(bar)
endef
-
变量及其值的来源
:
1 . 对于前面所说到的自动变量,其值是在每一个规则中根据规则的上下文自动获得变量值的。
2 .可以在运行 make 时,在 make 命令行上定义一个或多个变量。如果采用 make bar=x 运行 Makefile,则得到的结果将完 全不同。在 make 命令行中定义的变量及其值同样在 Makefile 中是可见的。其实,我们可以通过在 make 命令行中定义 变量的方式从而覆盖 Makefile 中所定义的变量的值。
3 .变量还可以来自于 Shell 环境, 如,采用 Shell 中的 export 命令定义了一个 bar变量。
-
高级变量引用功能
:
在斌值的同时完成后缀替换操作。如:
PHONY: all
foo = a.o b.o c.o
bar := $(foo:.o=.c)
all:
@echo "bar = $(bar)"
-
override 指令
:
我们可以采用在 make 命令行上定义变量的方式, 使得 Makefile 中定义的变量被覆盖掉,从而不起作用。 可能,在设计 Makefile 时,我们并不希望用户将我们在 Makefile 中定义的某个变量覆盖掉,那就得用 override 指令了。
#更新makefile2:
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
$(CC) -o $@ $^
main.o: main.c
$(CC) -o $@ -c $^
foo.o: foo.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
三:模式
- 解决问题:
如果对于每一个目标文件都得写一个不同的规则来描述,那会是一种“体力活”,太繁了!对于一个大型项目, 就更不用说了。 Makefile 中的模式就是用来解决我们的这种烦恼的。
# 更新makefile3:
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
$(CC) -o $@ $^
#模式应用
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
四:条件判断
使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。
注意:
make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。
条件表达式的语法为:
# 111
<conditional-directive>
<text-if-true>
endif
# 222
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
# 333
ifeq (<arg1>, <arg2>)
<text-if-true>
else ifeq (<arg3>, <arg4>)
<text-if-true>
else
<text-if-true>
endif
其中<conditional-directive>表示条件关键字,如“ifeq”。这个关键字有四个。
(1)第一个是“ifeq”:
注:ifeq 后面参数要加$(), 因为是值引用, 值可以为数值或字符串
ifeq (<arg1>, <arg2> )
比较参数“arg1”和“arg2”的值是否相同。当然,参数中还可以使用make的函数。如:
ifeq ($(strip $(foo)),)
<text-if-empty>
endif
这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么<text-if-empty>就生效。
(2)第二个条件关键字是“ifneq”。语法是:
ifneq (<arg1>, <arg2> )
其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。
(3)第三个条件关键字是“ifdef”。语法是:
ifdef <variable-name>
如果变量<variable-name>是否有值,那表达式为真。否则,表达式为假。当然,<variable-name>同
样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子:
注:ifdef后的变量名不能加 $()。
示例一:
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
示例二:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif
第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。
(4)第四个条件关键字是“ifndef”。其语法是:
ifndef <variable-name>
和“ifdef”是相反的意思。
在<conditional-directive>这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然
就被认为是命令)。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。
feq ($(TARGET_ARCH), arm)
LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), x86)
LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), mips)
LOCAL_SRC_FILES := ...
else
LOCAL_SRC_FILES := ...
endif
五:函数
-
addprefix 函数
addprefix 函数是用来在给字符串中的每个子串前加上一个前缀,其形式是:
$(addprefix prefix, names…)
实例代码:
.PHONY: all
without_dir = foo.c bar.c main.o
with_dir := $( addprefix objs/, $(without_dir))
all:
@echo $(with_dir)
-
filter函数
filter 函数用于从一个字符串中,根据模式得到满足模式的字符串,其形式是:
$(filter pattern…, text)
实例代码:
.PHONY: all
sources = foo.c bar.c baz.s ugh.h
sources := $(filter %.c %.s, $(sources))
all:
@echo $(sources)
-
filter-out函数
filter-out 函数用于从一个字符串中根据模式滤除一部分字符串,filter 与 filter-out 是互补的。其形式是:
$(filter-out pattern…, text)
实例代码:
.PHONY: all
objects = main1.o foo.o main2.o bar.o
result = $(filter-out main%.o, $(objects))
all:
@echo $(result)
-
patsubst函数
patsubst 函数是用来进行字符串替换的,其形式是:
$(patsubst pattern, replacement, text)
实例代码:
.PHONY: all
mixed = foo.c bar.c main.o
objects := $(patsubst %.c, %.o, $(mixed))
all:
@echo $(objects)
-
strip函数
strip 函数用于去除变量中的多余的空格,其形式是:
$(strip string)
实例代码:
.PHONY: all
original = foo.c bar.c
stripped := $(strip $(original))
all:
@echo "original = $(original)"
@echo "stripped = $(stripped)"
-
wildcard 函数
wildcard 是通配符函数,通过它可以得到我们所需的文件,这个函数如果我们在 Windows 或是Linux 命令行中的“*”。 其形式是:
$(wildcard pattern)
实例代码:
.PHONY: all
SRCS = $(wildcard *.c)
all:
@echo $(SRCS)
# 更新makefile4
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(EXE): $(OBJS)
$(CC) -o $@ $^
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)