目录
一:Makefile基本规则
1.1示例
1.2 隐式规则
1.3 伪目标
1.4 搜索源文件
二:变量
2.1使用变量定义变量值
2.2追加变量
三:条件判断
四:函数
Linux下Makefile总结
——一步
MakeFile可以看做是一种简单的编程语言,其诞生的本质目的是实现自动化编译。
以Linux下gcc-c编译器为例,编译一个c语言程序需要经过以下几个步骤:
1.将c语言源程序预处理,生成.i文件;
2.预处理后的.i语言编译成汇编语言,生成.s文件;
3.汇编语言经过汇编,生成目标文件.o文件;
4.将各个模块的.o文件链接起来,生成一个可执行程序文件。
我们知道,在Visual C++6.0中,可以新建一个工程,在一个工程当中能够包含若干个c语言文件,则编译的时候直接编译整个工程便可。Linux下无法为多个c语言文件新建工程,但可以通过MakeFile实现它们的整合编译。
如上gcc-c编译步骤,如果使用Makefile则过程为:
.C文件——>.o文件——>可执行文件
当然,Makefile中也加入了自己的设置变量方法与集成了一些函数,能够更有效地方便用户使用。
/**************************分隔符********************************/
一:Makefile基本规则
1.1示例
target ... : prerequisites ...
command
...
...
target也就是一个目标文件,可以是Object File,也可以是执行文件。prerequisites就是,要生成那个target所需要的文件或是目标。command也就是make需要执行的命令。(任意的Shell命令)
为了方便理解,我们来看一个示例:
/*Makefile示例*/
edit : main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
gcc -o edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
main.o : main.c defs.h #生成main.o
gcc -c main.c
kbd.o : kbd.c defs.h command.h #生成kdb.o
gcc -c kbd.c
command.o : command.c defs.h command.h #生成command.o
gcc -c command.c
display.o : display.c defs.h buffer.h #生成display.o
gcc -c display.c
insert.o : insert.c defs.h buffer.h #生成insert.o
gcc -c insert.c
search.o : search.c defs.h buffer.h #生成search.o
gcc -c search.c
files.o : files.c defs.h buffer.h command.h #生成files.o
gcc -c files.c
utils.o : utils.c defs.h #生成utils.o
gcc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
/*END*/
执行输入:make -s edit
反斜杠(/)是换行符的意思。我们来回忆一下一个c语言编译的过程:
c语言文件:main.c
1.gcc main.c -c main.o
2. gcc main.o -o main
经过这两步便可以编译出可执行文件:main。
现在我们再看上面的Makefile:
main.o : main.c defs.h #生成main.o
gcc -c main.c
这两句便生成main.o文件(main.c与defs.h为用到的源文件)。之后
edit:main.o ...
gcc -o edit main.o ...
便会生成main可执行文件;同理,其他几个文件亦是。“edit”便是最终编译出的可执行文件名。
可以看到Makefile只是整合了编译过程,也可以同时编译多个文件,其诞生的本质目的确实是为了实现自动化编译。
1.2隐式规则
make工具会自动使用gcc -c命令,将一个扩展名为.c的c语言程序源文件编译成一个同名的.o目标文件。
因此,在上例中,将:
main.o : main.c defs.h #生成main.o
gcc -c main.c
改为:
main.o :defs.h
(即去掉源同名c语言文件名,其他几个文件亦是如此。)这样便使用了隐式规则,隐式规则只是节省了敲代码的数量。
1.3 伪目标
可以注意到上述示例中末尾有几行代码:
clean :
rm edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
这几行代码并不会编译生成文件,可以把它们看成是Makefile里附加的实现小功能的程序(Makefile里藏了shell小程序),
这个名称为clean的目标便叫做伪目标。我们可以直接输入:make clean来运行这个伪目标。
显然,此伪目标可以清除之前生成的.o目标文件以及edit执行文件。
为了防止命名的重复,可以使用.PHONY来显式地指明一个伪目标:
.PHONY clean
clean :
rm edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
这样就不会对clean这个目标的性质定义产生混乱了。
/*********看完上面应该基本知道Makefile用途了******************/
1.4搜索源文件
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻这些相关源文件时,需要在文件前加上路径,但最好的方法是把一个路径告诉make,让make自动去找。
Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:../headers
上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。
(当然,当前目录永远是最高优先搜索的地方)。
另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH 变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:
1.vpath
为符合模式
2.vpath
清除符合模式
3.vpath
清除所有已被设置好了的文件搜索目录。
vpath使用方法中的
vpath %.h ../headers
该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的
vpath %.c foo
vpath %.c blish
vpath %.c bar
其表示“.c”结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。
vpath %.c foo:bar
vpath %.c blish
而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录。
/**************************分隔符*******************************/
二:变量
为了增强Makefile的功能与增加程序的可移植性,Makefile中也使用了变量。
看个例子:
/*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 #生成main.o
CC FLAGS main.c
kbd.o : kbd.c defs.h command.h #生成kdb.o
CC FLAGS kbd.c
command.o : command.c defs.h command.h #生成command.o CC FLAGS command.c
display.o : display.c defs.h buffer.h #生成display.o
CC FLAGS display.c
insert.o : insert.c defs.h buffer.h #生成insert.o
CC FLAGS insert.c
search.o : search.c defs.h buffer.h #生成search.o CC FLAGS search.c
files.o : files.c defs.h buffer.h command.h #生成files.o CC FLAGS files.c
utils.o : utils.c defs.h #生成utils.o
CC FLAGS utils.c
.PHONY clean
clean :
rm edit $(objects)
/*END*/
此时,程序便有了很好的可修改性与移植性。
2.1使用变量定义变量值
可以用变量定义变量的值,Makefile中有三种方式:
1.使用'='操作符
/*Makefile程序*/
you = $(me)
me = $(he)
he = she
all:
echo $(you)
/*END*/
执行:make -s all
输出:she
程序将变量me的值赋给you,而变量me的值为变量he的值,he的值为she,因此变量you的值为she。
Makefile中的变量值是可以使用后面的变量来定义的。但这样有危险,例如:
a = $(b)
b = $(a)
将陷入无限的变量赋值过程。
2.使用’:=‘操作符
这样定义时,前面的变量不能使用后面的变量,只能使用前面已经定义好的变量。如果使用前面未定义的变量,
则该值为空,例如:
y := $(x) me
x := you
all:
echo $(y)
执行:make -s all
输出:me (注:如果提示“遗漏分隔符”提示则是代码少敲了
由于变量x未定义,则为空值,只输出me。后面x已经赋值为you,但是y已经赋值过了。
3.使用‘?='操作符
如果变量之前没有被定义过,则变量的值此时被定义,如果变量的值已经被定义过,则此时赋值语句什么也不做。
例如:
a := hello
b ?= world
a ?= HELLO
all:
echo $(a)
echo $(b)
执行:make -s all
输出:hello
world
由于a值之前被赋为hello,之后:a ?= HELLO并不会再给变量a赋值,以此a 保持hello值。
2.2 追加变量值
由于Makefile当中的变量本质上是一个字符串,因此可以追加变量的值,用‘+=’操作符。例如:
objects := main.o foo.o
则objects += another.o 结果为:
objects := main.o foo.o another.o
/**************************分隔符********************************/
三:条件判断
条件表达式的语法为:
endif
以及:
else
endif
其中
1.“ifeq”
ifeq (
ifeq '
ifeq "
ifeq "
ifeq '
比较参数“arg1”和“arg2”的值是否相同,相同则为真。
2.第二个条件关键字是“ifneq”。语法是:
ifneq (
ifneq '
ifneq "
ifneq "
ifneq '
其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。
3.第三个条件关键字是“ifdef”。语法是:
ifdef
如果变量
4.第四个条件关键字是“ifndef”。其语法是:
ifndef
如果
/**************************分隔符********************************/
四:函数
函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:
$(
或是
${
这里,
例如:
$(subst
名称:字符串替换函数——subst。
功能:把字串
返回:函数返回被替换过后的字符串。
示例:
$(subst ee,EE,feet on the street),
把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。
/**************************分隔符********************************/
Makefile支持的函数并不多,例举如下:(摘自:陈皓《跟我一起写Makefile》)★字符串处理函数
名称:字符串替换函数——subst。
$(subst
功能:把字串
返回:函数返回被替换过后的字符串。
示例:
$(subst ee,EE,feet on the street),
把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。
名称:模式字符串替换函数——patsubst。
$(patsubst
功能:查找
返回:函数返回被替换过后的字符串。
示例:
$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”备注:
这和我们前面“变量章节”说过的相关知识有点相似。如:
“$(var:
相当于
“$(patsubst
而“$(var:
则相当于
“$(patsubst %
例如有:objects = foo.o bar.o baz.o,
那么,“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一样的。
名称:去空格函数——strip。
$(strip
功能:去掉
返回:返回被去掉空格的字符串值。
示例:
$(strip a b c )
把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。
名称:查找字符串函数——findstring。
$(findstring
功能:在字串
返回:如果找到,那么返回
示例:
$(findstring a,a b c)
$(findstring a,b c)
第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)
名称:过滤函数——filter。
$(filter
功能:以
返回:返回符合模式
示例:
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”。