工程中编写自己的makefile---6 makefile学习

来源:互联网 发布:软件协会网站 编辑:程序博客网 时间:2024/05/01 18:52

1        Makefile

1.1             Makefile运行顺序

1、读入所有的 Makefile。

2、读入被 include 的其它 Makefile。

3、初始化文件中的变量。

4、推导隐晦规则,并分析所有规则。

5、为所有的目标文件创建依赖关系链。

6、根据依赖关系,决定哪些目标要重新生成。

7、执行生成命令。

 

1-5步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义

的变量被使用了,那么,make 会把其展开在使用的位置。但 make

并不会完全马上展开,make 使用的是拖延战术,如果变量出现在依

赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其

内部展开。

1.2             Makefile显式规则

target ... : prerequisites ...

command

...

...

例如

target:目标文件1 目标文件2  目标文件3

    <tab>   gcc –o 欲建立的执行文件目标文件1 目标文件2  目标文件3

 

target:        是一个目标文件;可以是 Object File,也可以是执行文件;还可以是一个标签(Label);

prerequisites: 要生成那个target 所需要的文件或是目标

command:       也就是 make 需要执行的命令(任意的Shell 命令)

1.3             引用其他的makefile

使用 include 关键字可以把别的 Makefile 包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。

include 的语法是:

include <filename>

filename 可以是当前操作系统 Shell 的文件模式(可以保含路径和通配符)

注意!!!在 include 前面可以有一些空字符, 但是绝不能是[Tab]键开始。 include和<filename>可以用一个或多个空格隔开

1.4             一些注意的符号

注意!!!

1,-    :命令前面加了一个小减号,标记为不管命令出不出错都认为是成功的

2,@       :用“@”字符在命令行前,这个命令将不被 make 显示出来

3,$<     :自动化变量,表示所有的依赖目标集

4,$@     :自动化变量,表示目标集

5,$$$$  :意为一个随机编号

6,$(CFLAGS) 编译参数。如果没有定义就是空的。CFLAGS主要是指makefile中的隐式规则里会用到的常见预定义变量,是C编译器的选项

1.5             Make的参数

1,-n:只显示命令,但不执行命令,利于调试Makefile,看看命令执行起来是什么顺序

2,-s:全面禁止命令的显示

3,-f:运行指定的 Makefile,例如:make -f make.linux

4,-i:Makefile 中所有命令都会忽略错误

5,-k:如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则

1.6             文件搜寻目录

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉 make,让 make 在自动去找。

         Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。

VPATH = src:../headers

上面的的定义指定两个目录,“src”“../headers”make会按照这个顺序进行搜索。目录由冒号分隔。(当然,当前目录永远是最高优先搜索的地方)

另一个设置文件搜索路径的方法是使用makevpath关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,它的使用方法有三种

         vpath <pattern> <directories>

为符合模式<pattern>的文件指定搜索目录<directories>

vpath <pattern>

清除符合模式<pattern>的文件的搜索目录。

vpath

清除所有已被设置好了的文件搜索目录。

vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录。例如:

vpath  %.h  ../headers                  该语句表示,要求make“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)

vpath  %.h  ../headers:work          该语句表示,要求make“../headers”work两个目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)

我们可以连续地使用 vpath 语句,以指定不同搜索策略。如果连续的vpath 语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make 会按照 vpath 语句的先后顺序来执行搜索

1.7             伪目标

         伪目标并不是一个文件,只是一个标签,由于伪目标不是文件,所以make无法生成它的依赖关系和决定它是否要执行;为了避免和文件重名的这种情况,我们可以使用一个特殊的标记.PHONY来显示地指明一个目标是“伪目标”

.PHONY:clean

clean :

rm edit $(OBJECTS)

 

       伪目标作为依赖;目的:不同的指令清除不同的目标;可以输入“make  cleanall”和

make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的

      .PHONY:cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff

rm program

cleanobj :

rm *.o

cleandiff :

rm *.diff

注意!!!

可以使用 依赖一个伪目标,这样每次编译时因为ALWAYSMAKE不存在,都会执行依赖后的命令

1.8             静态模式

需要重点看下!!!

         静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。

<targets ...>: <target-pattern>:<prereq-patterns ...>

<commands>

..

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合(目标集)

target-parrtern是指明了targets的模式,也就是的目标集模式(目标模式)

prereq-parrterns是目标的依赖模式(依赖模式),它对target-parrtern形成的模式再进行一次依赖目标的定义。

上面的模式为生成一个新集合(依赖目标集或者依赖集)

 

还是举个例子来说明一下吧。如果我们的<target-parrtern>定义成“%.o”,意思是我们的<target>集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>定义成“%.c”,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合(依赖目标集)

 

objects = foo.o bar.o

all: $(objects)

 

$(objects): %.o: %.c

$(CC) -c $(CFLAGS) $< -o $@

等价于

      foo.o:foo.c

           $(CC) –c  $(CFLAGS) foo.c  –o  foo.o

      bar.o:bar.c

           $(CC) –c  $(CFLAGS) bar.c  –o  bar.o

 

      files= foo.elc bar.o lose.o

$(filter %.o,$(files)): %.o: %.c

$(CC) -c $(CFLAGS) $< -o $@

$(filter %.elc,$(files)): %.elc: %.el

emacs -f batch-byte-compile $<

 

$(filter %.o,$(files))表示调用Makefilefilter 函数,过滤“$filter”集,

上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。

1.8.1.1   嵌套执行 make

      在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。

      我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:

subsystem:

cd subdir && $(MAKE)

其等价于:

subsystem:

$(MAKE) -C subdir

定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。

 

如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:

export <variable ...>

否则

unexport <variable ...>

 

export variable = value

其等价于:

variable = value

export variable

其等价于:

export variable := value

其等价于:

variable := value

export variable

如果你要传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量。

 

“-w”          输出当前的工作目录;

如果我们的下级make目录是“/home/hchen/gnu/make”,如果我们使用“make -w”来执行,那么当进入该目录时,我们会看到:

make: Entering directory `/home/hchen/gnu/make'.

而在完成下层make后离开目录时,我们会看到:

make: Leaving directory `/home/hchen/gnu/make'

当你使用-C参数来指定make下层Makefile时,“-w”会被自动打开的。如果参数中有“-s”“--slient”)或是“--no-print-directory”,那么,“-w”总是失效的。

1.9             Makefile变量

1.9.1        定义变量

变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”“#”“=”或是空字符(空格、回车等)。使用写法(MAKEFLAGS)

变量使用:

$ MAKEFLAGS

$(MAKEFLAGS)同一使用此种模式,包括在shell

${ MAKEFLAGS }

如果要使用真实的“$”字符,那么需要用“$$”来表示。

1.9.2        定义命令包

这种命令序列的语法以“define”开始,以“endef”结束

1.9.3        变量中的变量

1,”=”

MyName=$(Name)

Name=wzt

变量是可以使用后面的变量来定义的(递归调用容易出问题)

2,”:=”

    Name:=wzt

MyName:=$(Name)

前面的变量不能使用后面的变量,只能使用前面已定义好了的变量

推荐使用此种方法

3,”?=”

如果 FOO 没有被定义过,那么变量 FOO 的值就是“bar”

如果 FOO 先前被定义过,那么这条语将什么也不做

4,”+=”

追加变量值

objects =main.o foo.o bar.o utils.o

objects +=another.o

于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o another.o

another.o被追加进去了)

如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,

1.9.4        多行变量

define 指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以 endef关键字结束。其工作方式和“=”操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以[Tab]键开头,所以如果你用 define 定义的命令变量中没有以[Tab]键开头,那么 make就不会把其认为是命令

definetwo-lines

echo foo

echo $(bar)

endef

1.9.5        变量高级用法

1,变量值的替换(类似静态模式)

$(var:a=b)

把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”

 

2,把变量的值再当成变量

x = y

y = z

a := $($(x))

    有$(a)=z

1.9.6        目标变量

    可以为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。

其语法是:

<target...> : <variable-assignment>

<target...> : overide <variable-assignment>

 

<variable-assignment>可以是前面讲过的各种赋值表达式,如“=”、

“:=”、 “+=”或是“?=”。第二个语法是针对于 make 命令行带入的变量,

或是系统环境变量。

这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用

到由这个目标所引发的所有的规则中去。如:

prog: CFLAGS = -g

prog: prog.o foo.o bar.o

$(CC)$(CFLAGS) prog.o foo.o bar.o

prog.o: prog.c

$(CC)$(CFLAGS) prog.c

foo.o: foo.c

$(CC)$(CFLAGS) foo.c

bar.o: bar.c

$(CC)$(CFLAGS) bar.c

在这个示例中,不管全局的$(CFLAGS)的值是什么,在 prog 目标,以及其所引发的所有规则中(prog.o foo.o bar.o 的规则) ,$(CFLAGS) 的值都是“-g”

1.9.7        模式变量

我们知道,make 的“模式”一般是至少含有一个“%”的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义目标变量:

%.o: CFLAGS = -O

同样,模式变量的语法和“目标变量”一样:

<pattern...> : <variable-assignment>

<pattern...> : override <variable-assignment>

override同样是针对于系统环境传入的变量,或是make 命令行指定的变量

1.10       条件判断

<conditional-directive>

<text-if-true>

else

<text-if-false>

endif

 

ifeq(<arg1>, <arg2> )

ifeq'<arg1>' '<arg2>'

ifeq"<arg1>" "<arg2>"

ifeq"<arg1>" '<arg2>'

ifeq'<arg1>' "<arg2>"

比较参数“arg1”和“arg2”的值是否相同

 

<conditional-directive>表示条件关键字,如“ifeq”。这个关键字有四个。

1,ifeq  “arg1”和“arg2”的值相同为真

2,ifneq “arg1”和“arg2”的值不同为真

3,ifdef

    语法:

ifdef <variable-name>

如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假,

ifdef 只是测试一个变量是否有值,其并不会把变量扩展到当前位置。

4,ifndef

 

在<conditional-directive>这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令) 。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。

特别注意的是,make 是在读取 Makefile 时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。

 

 

1.11       常用函数

函数调用,很像变量的使用,也是以“$”来标识的,其语法为:$( )或${ }。

1.11.1    wildcard

原型:$(wildcard PATTERN)

功能:获取匹配模式的文件名

说明:这个函数的功能是查找当前目录下所有符合模式 PATTERN 的文件名,其返回值是以空格分割的,当前目录下的所有符合模式 PATTERN 的文件名列表。

 

例如:

如下模式返回当前目录下所有扩展名位 .c 的文件列表。

$(wildcard *.c)

 

1.11.2    patsubst

原型:$(patsubst pattern, replacement, text)

功能:模式替换函数

说明:函数功能是查找字符串 text 中按照空格分开的单词,将符合模式 pattern 的字符串替换成 replacement。 Pattern 中的模式可以使用通配符,当 pattern 和 replacement 中都有 % 时,符合条件的字符将被 replacement 中的替换。函数的返回值是替换后的新字符串。

例如

需要将 C 文件替换为 .o 的目标文件可以使用如下模式:

$(patsubst%.c, %.o, add.c)

上面的模式将 add.c 字符串作为输入,当扩展名为 .c 时符合模式 %.c ,其中 % 在这里代表 add,替换为 add.o,并作为输出字符串。

$(patsubst%.c, %.o, $(wildcard *.c))

输出的字符串将当前扩展名为 .c 的文件替换成 .o 的文件列表。

1.11.3    foreach

原型:$(foreach VAR, LIST, TEXT)

功能:循环函数

说明: foreach 将 LIST 字符串中一个空格分割的单词,先传给临时变量 VAR ,然后执行 TEXT 表达式,TEXT 表达式处理结束后输出。其返回值是空格分割表达式 TEXT 的计算结果。

例如:

对于存在 add 和 sub 的两个目录,设置 DIRS 为 "add sub ./" 包含目录 add、sub 和当前目录。表达式$(wildcard $(dir)/*.c) ,可以取出目录 add 和 sub 及当前目录中的所有扩展名为 .c 的C语言源文件:

DIRS = subadd ./

FILES =$(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))

1.11.4    filter

原型:$(filter PATTERN…,TEXT)

功能:过滤函数

说明:过滤掉字串“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))”的返回值给 cc 来编译生成目标“foo”,函数返回

值为“foo.c bar.c baz.s”

1.11.5    subst

原型:$(subst FROM, TO, TEXT),

功能:替换函数

说明:即将字符串TEXT中的子串FROM变为TO

 

 

 

原创粉丝点击