1,makefile文件的组成内容
- 显式规则
- 隐式规则
- 变量的意义
- 文件指示
- 注释
2,make工具的退出码
- 0——表示makefile文件成功执行
- 1——表示makefile文件执行时出现了错误
- 2——如果用户使用了-q选项,并且make使一些目标不需要更新,则返回2
3,规则的使用
3.1,基本规则(显式规则)
- a, targets : prerequisites
- command
- /…
- b, targets : prerequisites ; command
- command
- …
edit : main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.omain.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
3.2,隐式规则
- make工具有自己的推导规则,例如:make工具会自动使用gcc -c命令,将一个扩展名为.c的C语言源程序编译成一个同名的.o的目标文件。因此当编译一个单独的c文件到o文件时,可以使用隐含规则,让make工具自己推导规则。
edit : main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.omain.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
clean :
rm edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o3.3,伪目标
- 上面的makefile中,多次提到一个称为clean的目标,那就是一个伪目标。
- 使用clean目标,执行后面的命令,清除所生成的目标文件,为下次编译工作做准备
- 因此,make并不生成clean这个目标文件,这个文件称为伪目标文件。
- 为了避免和已存在的文件重名,必须使用一个特殊的标记.PHONY来显示地指明一个目标
- 是伪目标。如:.PHONY : clean.表示不管makefile文件是否有clean文件,都将其声明为
- 一个伪目标。
- 伪目标也可以作为默认目标,其位置必须是第一个目标
all : main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o.PHONY all
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c这个makefile,默认目标为伪目标即不生成目标文件,所以这个makefile可以用来每次更新多个目标文件而生成目标文件。
3.4,使用通配符
- make工具支持3种通配符*,?,[…]. ~表示用户的根目录,该目录通常都是用$HOME环境变量所保存。
- *表示匹配所有字符,如*.c表示所有扩展名为c的文件。?表示匹配一个字符,[]表示一个模式。
3.5,搜索源文件
1,设置vpath变量
-
makefile文件中使用特殊特殊变量vpath就是实现搜索源文件的功能,若没有指明该变量,make就默认在当前目录中去寻找依赖文件和目标文件。如vpath=src:../include。vpath变量被定义为2个目录,分别是src和../include。表示make工具会顺序搜索当前目录、当前目录下的src目录和父目录下include目录。
2,使用vpath关键字
-
使用vpath的方法有三种:a,指定特定模式的文件的搜索目录vapth <pattern> <dirs>;b,指定文件的模式 vpath<pattern>表示清楚符合模式pattern的文件的搜索目录;c,清除所有的文件的搜索目录vpath.eg: vpath %.h ../include表示在当前目录和父目录下的include搜索所有的以.h结尾的文件
4,使用命令
4.1,显示命令
- make工具会把要执行的命令行在命令执行之前输出到屏幕上,当使用@字符在命令行前,这个命令将不被make工具显示出来,如:echo compling命令会先显示echo compling然后显示compling,而@echo compling不会显示echo compling而显示compling。
- 另外,make执行时,使用参数-n或者--just-print时,只是显示命令,不会执行命令。使用-s或者——slient,禁止所有命令的显示,不论命令前面是否有@。
4.2,执行命令
- makefile:
- all:
- ls –l
- ./hello
- .PHONY: all
- make –s all
- hello hello.c makefile
- hello world
- makefile:
- exec:
- cd /home/admin
- pwd
- .PHONY: exec
- make –s exec
- /home/admin/make ####注意这里不是/home/admin
- makefile:
- exec:
- cd /home/admin;pwd ####如果需要让上一条命令的结果应用在下一条命令时,应该用
- #### ;分割这两条命令
- .PHONY: exec
- make –s exec
- /home/admin/
4.3,命令出错
- 一般一个命令运行完成后,make工具就会检测每个命令的返回码。如果命令返回成功,make会执行下一条命令。当规则中所有的命令返回成功后,这个规则就运行完成。如果某个出现错误,make工具就会终止。但是在命令前加“-”可以忽略对该民联执行结果的判断。如:
- clean:
- -rm –f *.c
- 或者make使用-i(--ignore-errors)是makefile所有命令都忽略错误。又或者规则以.IGNORE声明为目标。如:
- clean:
- rm –f *.c
- .IGNORE
5,使用变量
5.1,使用普通变量
- 可以在makefile中使用变量,makefile中的变量代表一个字符串。makefile变量在声明的时候需要对其赋值,需要在变量前加上$符号(需要使用$符号,则用$$表示),对3.1中makefile使用变量后:
- objects=main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o edit : $(objects)
gcc -o edit $(objects) main.o : main.c defs.h
gcc -c main.c
kbd.o : kbd.c defs.h command.h
gcc -c kbd.c
command.o : command.c defs.h command.h
gcc -c command.c
display.o : display.c defs.h buffer.h
gcc -c display.c
insert.o : insert.c defs.h buffer.h
gcc -c insert.c
search.o : search.c defs.h buffer.h
gcc -c search.c
files.o : files.c defs.h buffer.h command.h
gcc -c files.c
utils.o : utils.c defs.h
gcc -c utils.c
clean :
rm edit $(objects)在很多情况下,用户将编译器以及编译器选项定义为变量,能够增强makefile的灵活性,便于移植。
CC=gcc
FLAGS=-c
objects= main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
edit: $(objects)
$(CC) -o edit $(objects)
main.o: main.c defs.h
$(CC) $(FLAGS) main.c
kbd.o: kbd.c defs.h command.h
$(CC) $(FLAGS) kbd.c
command.o: command.c defs.h command.h
$(CC) $(FLAGS) command.c
display.o: display.c defs.h buffer.h
$(CC) $(FLAGS) display.c
insert.o: insert.c defs.h buffer.h
$(CC) $(FLAGS) insert.c
search.o:search.c defs.h buffer.h
$(CC) $(FLAGS) search.c
files.o:files.c defs.h buffer.h command.h
$(CC) $(FLAGS) files.c
utils.o: utils.c defs.h
$(CC) $(FLAGS) utils.c
clean :
rm edit $(objects)
.PHONY: clean当移植到其他的平台时,只需要更改变量CC和变量FLAGS即可,其他内容都不需要更改。
5.2,变量中的变量
- 在makefile中使用变量定义变量值的方式有3种
- 1,使用“=”操作符
- foo=$(bar)
- bar=$(ugh)
- ugh=Hub?
- all:
- echo $(foo)
- 执行后显示,Hub? PS:防止递归定义
- 2,使用:=操作符
- 使用:=操作符可以避免递归定义,使用:=定义时,只能使用前面已经定义好的变量。
- 3,使用?=操作符
- ?=表示如果变量之前未被定义,那么变量的值就被定义。如变量之前的值已经定义则赋值语句不做任何操作
5.3,追加变量的值
- makefile允许给变量追加一个值,操作符是+=
- objects=main.o foo.o bar.o utils.o
- objects+= $(objects) another.o
- 例子:
- CC =gcc
FLAGS=-c
FLAGS+= –O2- objects =main.o kbd.o command.o display.o
insert.o search.o fles.o utils.o
objects+=new.o
edit: $(objects)
$(CC) -o -O2 edit $(objects)- main.o: main.c defs.h
$(CC) $(FLAGS) main.c
- kbd.o: kbd.c defs.h command.h
$(CC) $(FLAGS) kbd.c
command.o: command.c defs.h command.h
$(CC) $(FLAGS) command.c
display.o: display.c defs.h buffer.h
$(CC) $(FLAGS) display.c
insert.o: insert.c defs.h buffer.h
$(CC) $(FLAGS) insert.c
search.o:search.c defs.h buffer.h
$(CC) $(FLAGS) search.c
files.o:files.c defs.h buffer.h command.h
$(CC) $(FLAGS) files.c
utils.o: utils.c defs.h
$(CC) $(FLAGS) utils.c
new.o: new.c
$(CC) $(FLAGS) new.c
- clean :
rm edit $(objects)- .PHONY clean
5.4,自动化变量
- makefile一共有7个自动化变量$@,$%,$<,$?,$^,$+,$*。
$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
所有比目标新的依赖目标的集合。以空格分隔。
$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+
这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$*
这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。
6,使用条件判断
6.1,条件表达式
- 条件关键字有4个:ifeq,ifneq,ifdef,ifndef
- 1,ifeq:表示相等则执行,其格式如下:
- ifeq(<arg1>,<arg2>)
- ifeq ‘<arg1>’’<arg2>’
- ifeq “<arg1>”“<arg2>”
- ifeq “<arg1>”’<arg2>’
- ifeq ‘<arg1>’”<arg2>”
- 2,ifneq:表示不相等则执行
- ifeq(<arg1>,<arg2>)
- ifeq ‘<arg1>’’<arg2>’
- ifeq “<arg1>”“<arg2>”
- ifeq “<arg1>”’<arg2>’
- ifeq ‘<arg1>’”<arg2>”
- 3,ifdef:表示值非空则执行
- ifdef <variable-name>
- 4,ifndef:表示值为空则执行
- ifndef<variable-name>
- 例子:
- var1 =
var2 = hello- all:
ifdef $var1
$var1=hello
endif- ifeq($(var1),$(var2))
echo "they are equal!"
else- echo "they are not equal"
endif
.PHONY:all
6.2,表达式实例
CC=gcc
libs_for_gpu=-lgpu
normal_libs=
objects= main.o list.o my_lib.o
app: $(objects)
ifeq ($(CC),gcc)
$(CC) -o app $(objects) $(libs_for_gpu)
else
$(CC) -o app $(objects) $(normal_libs)
endifmain.o : main.c
$(CC) -c main.c
list.o : list.c
$(CC) -c list.c
my_lib.o : my_lib.c
$(CC) -c my_lib.c
7,使用函数
7.1,函数调用的语法
- makefile文件中的函数以$标识,其语法如下:
- $(<function> <arguments>)
- or
- ${<function> <arguments>}
- <function>表示函数名,<arguments>表示函数的参数列表。参数间以逗号”,”分隔,函数名与参数间用空格分隔。例子:
- comma :=,
empty :=
space := $(empty) $(empty)
var :=a b c
bar :=
all:
$bar:=$(subst $(space),$(var),$(comma))
.PHONY: all
7.2,字符串处理函数
- $(subst <from>;,<to>;,<text>;)
- $(subst ee,EE,feet on the street) 把”feet on the street”中的“ee“替换成“EE“
- $(patsubst <pattern>;,<replacement>;,<text>;)
- $(patsubst %.c,%.o,x.c.c bar.c) 把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
- $(strip <string>;)去空格函数
- 去掉<string>字串中开头和结尾的空字符。
- $(strip a b c ) 把字串“a b c ”去到开头和结尾的空格,结果是“a b c”
- $(findstring <find>,<in>;)查找字符串函数
- 在字串<in>中查找<find>;字串。
- $(findstring a,a b c)
$(findstring a,b c) 第一个函数返回“a”字符串,第二个返回“”字符串(空字符串) - $(filter <pattern...>,<text>)过滤函数
- 以<pattern>;模式过滤<text>;字符串中的单词,保留符合模式<pattern>;的单词。可以有多个模式。
- sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo- $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。
$(filter-out <pattern...>,<text>;) 反过滤函数- 以<pattern>模式过滤<text>;字符串中的单词,去除符合模式<pattern>;的单词。可以有多个模式。
- objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”- $(sort <list>) 排序函数
- 给字符串<list>;中的单词排序(升序)。$(sort foo bar lose)返回“bar foo lose” 。
- $(word <n>,<text>;)取单词函数
- 取字符串<text>中第<n>个单词。(从一开始) $(word 2, foo bar baz)返回值是“bar”。
- $(wordlist <s>,<e>,<text>;)取单词串函数
- 从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
- $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。
- $(words <text>) 单词个数统计函数
- 统计<text>;中字符串中的单词个数,$(words, foo bar baz)返回值是“3”
- $(firstword <text>)首单词函数
- 取字符串<text>中的第一个单词。$(firstword foo bar)返回值是“foo”。
7.3,文件名操作函数
- $(dir <names…>),从文件名序列<names>中取出目录部分
- result:=
all:
$(result)=$(dir test.c /home/admin/test.c)
echo -n "the result is :"
echo $(result)
.PHONY: all- 结果是./ /home/admin
- $(notdir <names…>)从文件名序列<names…>中取出非目录部分。非目录部分是指最后一个“/”之后的部分
- $(result)=$(notdir test.c /home/admin/test.c)
- result为test.c test.c
- $(suffix <name…>)取后缀函数
- $(result)=$(suffix test.txt test.c)得到.txt .c
- $(basename <names…>)从文件名序列取各个文件名的前缀部分
- $(addsuffix <suffix>,<names…>) 把后缀<suffix>加到序列中每个单词的后面
- $(addprefix <prefix>,<names…>)把前缀<prefix>加到序列中每个单词的前面
- $(join <list1>,<list2>)把<list2>中的单词对应地加到<list1>的单词的后面
7.4,foreach函数
- foreach函数是用来控制循环。$(foreach <var> ,<list> ,<text>) <var>最好是一个变量名,<list>可以是一个表达式,而<text>一般会使用<var>参数来一次枚举<list>中的单词。
- files :=
names := a b c d- all:
$(files) :=$(foreach n, $(names),$(n).c)
echo -n "the file is : "
echo $(files)
.PHONY: all7.5,if 函数
- if函数很像makefile文件中所支持的条件语句——ifeq。$(if <condition>,<then-part>) or $(if <condition> ,<then-part> ,<else-part>)
- a := 1
b := 1
result :=
all:
$(result) :=$(if ifeq($a,$b), $a=10,$b=10)
echo -n "the result is :"
echo $(result)
.PHONY: all7.6,call 函数
- call函数为用户创建一个自己定义的函数。用户可以写一个非常复杂的表达式,每次用到这个表达式的时候,使用call函数跳转到该表达式出执行即可。$(call <expression> ,<parm1> ,<parm2> ,<parm3> …),当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等会被参数<parm1>, <parm2>,<parm3>依次取代。
- add = $(1)+$(2)
result :=
all:
$(result) := $(call add,1,2)
echo -n "the result is : "
echo $(result)
.PHONY: all7.7,origin函数
- origin函数操作变量的值,该函数可以将变量的定义返回给用户,其语法如下所示:$(origin <variable>) <variable>表示变量的名字。
7.8,shell函数
- shell函数用来执行操作系统shell的命令。该函数把执行操作系统命令后的输出作为函数返回。用户可以通过调用shell函数,使用操作系统命令来生成一个变量。格式如下:
- $(shell <command> ,<parm1> ,<parm2> ,<parm3>…) <command>表示需要执行的shell的命令,所以该函数的返回值为执行的shell命令的输出结果。
- files :=
all:
$(files) := $(shell ls)
echo -n "the files is : "
echo $(files)
.PHONY: all
8,makefile实例
8.1 makefile实例——项目中的总makefile
- 若某个项目的源代码按模块分类分别存储在多个目录下,每个模块的目录内都有各自的的makefile文件。其源程序存储的目录结构如下所示:
- hello(目录) include(目录) list(目录) Makefile(makefile文件)
- project/hello目录下。
- hello.c(源代码文件) Makefile(makefile文件)
- project/include目录下。
- hello.h(头文件) list.h(头文件)
- project/list目录下。
- list.c(源代码文件) Makefile(makefile文件)
- hello目录下的makefile如下:
- hello: hello.o
gcc hello.o -o hello
hello: hello.c
hcc -c hello.c
clean:
rm -rf hello *.c
.PHONY: clean- list目录下的makefile文件如下:
- list: list.o
gcc list.o -o list
list.o: list.c
gcc -c list.o
clean:
rm -rf list *.o
.PHONY: clean- project目录中的makefile文件如下:
- SUBDIRS := list hello
all: modules
.PHONY all
modules:
for n in $(SUBDIRS)
do
exit= make --directory=$$nif [$exit="1"];then
exit 1
fi
done
.PHONY modules
clean:
for n in $(SUBDIRS)
do
make --directory=$$n clean
done
.PHONY: clean8.2 makefile实例——makefile模板
- 一个比较规范的makefile文件模板:
- CC = XXX-gcc
CFLAGS+= xxx
LDFLAGS+= xxx
EXEC = xxx- all: $(EXEC)
.PHONY all- xxx: xxx.c
$(CC) $(CFLAGS) $(LDFLAGS) xxx.c -o $@- clean:
-rm -f $(EXEC) *.elf *.gdb *.o *.a
.PHONY: clean- 使用该模板创建hello程序的makefile文件:
- CC = gcc
CFLAGS+=
LDFLAGS+=
EXEC =all: $(EXEC)
.PHONY allhello: hello.c
$(CC) $(CFLAGS) $(LDFLAGS) hello.c -o $@clean:
-rm -f $(EXEC) *.elf *.gdb *.o *.a
.PHONY: clean如果此时该程序需要使用pthread线程库,并且使用做高的优化编译选项O3,则修改后的makefile文件如下:
CC = gcc
CFLAGS+= -O3
LDFLAGS+= -pthread
EXEC = helloall: $(EXEC)
.PHONY allhello: hello.c
$(CC) $(CFLAGS) $(LDFLAGS) hello.c -o $@clean:
-rm -f $(EXEC) *.elf *.gdb *.o *.a
.PHONY: clean