Makefile整理

来源:互联网 发布:淘宝没有耐克旗舰店吗 编辑:程序博客网 时间:2024/05/27 02:33

1. 基本规则

target : 依赖    命令

make会由上往下的找target,也就是说,第一个target是最终目标。找到第一个target后,make寻找target文件,如果target不存在,则寻找target的依赖,一般最终目标的依赖都是“.o”的中间文件。如果有“.o”的文件不存在,make会继续往下寻找以该“.o”作为target的行,依次往下寻找。

Makefile会自动的推到“.o”“.c”文件是它的依赖,并且自动的有
CC xx.c命令。
所以可以简化Makefile

objs = x1.o x2.otarget0:  $(objs)    $(CC) -o target0 $(objs)x1.o : x11.h x12.hx2.o : x21.h x22.h.PHONY : clean        #表示clean是一个伪目标文件clean :    rm -rf $(objs)

拥有相同依赖.h的.o也可以一起作目标,而把依赖写于其后。这是一种另类的结构。

x1.o x2.o : x12.h x21.h

Makefile 里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

显式规则 : makefile的基本规则,
target : depends     command xx.c xx.c ...
隐晦规则 : 即makefile的自动推导等。变量定义 : 可以用 '=',':=','?=','export','overide'文件指示 : include注释    :  #注释

2. 搜索规则

make命令默认搜索名为Makefile,makefile的文件。
使用-f选项,可以指定make搜索的文件名。

Makefile支持三种通配符” * “,”?”,”[…]”。

makefile中的文件默认在,makefile文件所在目录下搜索。
可以通过两种方式更改makefile搜索文件的PATH。

1.特殊变量VPATHVPATH = src:.../inc#上面指明了,两个搜索路径,src和.../inc。2.关键字vpath    2.1 vpath <pattern> <dir>    #为符合模式<pattern>的文件指定搜索目录<dir>。    2.2 vpath <pattern>    #清除符合模式<pattern>的文件的搜索目录。    2.3 vpath    #清除所有已被设置好了的文件搜索目录。vapth 使用方法中的<pattern>需要包含“ %”字符。“ %”的意思是匹配零或若干字符,例如, “%.h”表示所有以“.h”结尾的文件。    vpath的书写顺序就是搜索顺序。

3.伪目标

伪目标可以用来一次生成多个可执行文件,也可用来选择生成哪个可执行文件。
例1

all : prog1 prog2 prog3.PHONY : allprog1 : prog1.o utils.o    cc -o prog1 prog1.o utils.oprog2 : prog2.o    cc -o prog2 prog2.oprog3 : prog3.o sort.o utils.o    cc -o prog3 prog3.o sort.o utils.o#可以使用make命令一次生成多个可执行文件。

例2

.PHONY: cleanall cleanobj cleandiffcleanall : cleanobj cleandiff    rm programcleanobj :    rm *.ocleandiff :    rm *.diff#可以选择性的执行make cleanall或cleanobj或cleandiff

4.多目标

Makefile 的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖
于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。当然,多个目标的
生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自
动化变量“ $@”,这个变量表示着目前规则中所有的目标的集合。

bigoutput littleoutput : text.g    generate text.g -$(subst output,,$@) > $@上述规则等价于:bigoutput : text.g    generate text.g -big > bigoutputlittleoutput : text.g    generate text.g -little > littleoutput其中, -$(subst output,,$@)中的"$"表示执行一个 Makefile 的函数,函数名为 subst,后面的为参数。这里的这个函数是截取字符串的思,"$@"表示目标的集合,就像一个数组."$@"依次取出目标,并执于命令。

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

<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.oall: $(objects)$(objects): %.o: %.c    $(CC) -c $(CFLAGS) $< -o $@等价foo.o : foo.c    $(CC) -c $(CFLAGS) foo.c -o foo.obar.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))表示调用 Makefile 的 filter 函数,过滤##"$filter"集, 只要其中模式为"%.o"的内容。

5. 自动生成依赖

如果是一个比较大型的工程,你必需清楚哪些 C 文件包含了哪些头文件,并且,你在 加入或删除头文件时,也需要小心地修改 Makefile,这是一个很没有维护性的工作。为了 避免这种繁重而又容易出错的事情,我们可以使用C/C++编译的一个功能。大多数的 C/C++ 编译器都支持一个“ -M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。

例如

main.c中有#include<stdio.h>#include<defs.h>命令    gcc -M main.c相当于    gcc main.c stdio.h defs.h

GNU 组织建议把编译器为每一个源 文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个 “ name.d”的 Makefile 文件, [.d]文件中就存放对应[.c]文件的依赖关系。

可以写出[.c]文件和[.d]文件的依赖关系,并让 make 自动更新或自成[.d]文 件,并把其包含在主 Makefile 中,这样,就可以自动化地生成每个文件的依赖 关系了。

这里,给出了一个模式规则来产生[.d]文件:

%.d: %.c@set -e; rm -f $@; \$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \rm -f $@.$$$$第一行的意思是,所有的[.d]文件依赖于[.c]文件,“ rm -f $@”的意思是删除所有的目标,也就是[.d]文件.第二行的意思是,为每个依赖文件“ $<”,也就是[.c]文件生成 依赖文件,“ $@”表示模式“ %.d”文件,如果有一个 C 文件是 name.c,那么“ %”就是“ name”, “ $$$$”意为一个随机编号,第二行生成的文件有可能是“ name.d.12345”,第三行使用 sed 命令做了一个替换,关于 sed 命令的用法请参看相关的使用文档。第四行就是删除临时文件。

6. 变量

Makefile中的变量相当于#define的常量。放在文件开头,一般用OBJS变量代替最终target的依赖,CC代替编译命令。

声明变量:

1.变量名 = 值12 ... 值n 用/换行继续写。引用变量:$(变量名)2.变量也可以用:=赋值。这样可以检测有么有递归调用。变量名 := $(shell pwd)获得当前位置。3.?=相当于ifndefa ?=b相当于a如果没定义,则a=b
高级变量使用方法一.替换示例:foo := a.o b.o c.obar := $(foo:.o=.c)这个示例中,先定义了一个“ $(foo)”变量,而第二行的意思是把" $(foo)"中所有 以".o"字串"结尾"全部替换成" .c",所以 $(bar)的值就是" a.c b.c c.c"。二.把变量的值再当成变量x = yy = za := $($(x))三.追加变量值+=四. override 指示符如果有变量是通常 make 的命令行参数设置的,那么 Makefile 中对这个变量的赋值会被忽略。如果你想在 Makefile 中设置这类参数的值,那么,可以使用"override"指示符。其语法是:override <variable> = <value>override <variable> := <value>当然,你还可以追加:override <variable> += <more text>对于多行的变量定义,我们用 define 指示符,在 define 指示符前,也同样可以使用ovveride 指示符,如:override define foobarendef五.多行变量define 关键字。使用 define 关键字设置变量的值可以有换行,这有利于定义一系列的命令(“命令包”的技术就是利用这个关键字)。define 指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以 endef 关键字结束。其工作方式和“ =”操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以[Tab]键开头,所以如果你用 define 定义的命令变量中没有以[Tab]键开头,那么 make 就不会把其认为是命令。下面的这个示例展示了 define 的用法:define two-lines    echo foo    echo $(bar)endef六.环境变量export环境变量是make运行时带入的,可在makefile中定义,然后带入嵌套的makefile。七.局部变量前面都是全局变量局部变量就像 target : 依赖八.模式变量模式变量名以%开头语法与局部变量一样。

7. 函数及其高级函数

函数使用方法$(functionname    arguments)1.替换函数subst   from,to,inin中from换位to2.模式替换patsubst 3.去掉开头的空格strip4.查找find  find,in5.过滤filter  string1 ...,in返回符合模式的6.反向过滤filter-out去掉符合模式的7.升序排序sort  list8.取第n个单词word n,text9.取m-n个单词word-list n,m,text10.统计词数words text11.取第一个单词firstword text12.取文件的目录dir  file...13.取非目录notdir file...取出file...中非目录名的14.取后缀suffix file...返回值带点15.取前缀basename 16.加后缀addsuffix  .x,file....17.加前缀addprefix  pre,file...18.链接join  aa bb,11 22 33结果aa11 bb22 33高级函数;1. foreach函数语法是:$(foreach <var>,<list>,<text>)把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中, <text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时, <text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返回值。所以, <var>最好是一个变量名, <list>可以是一个表达式,而<text>中一般会使用<var>这个参数来依次枚举<list>中的单词。例子:names := a b c dfiles := $(foreach n,$(names),$(n).o)上面的例子中, $(name)中的单词会被挨个取出,并存到变量“ n”中,“ $(n).o”每次根据“ $(n)”计算出一个值,这些值以空格分隔,最后作为 foreach 函数的返回,所以,$(files)的值是“ a.o b.o c.o d.o”。注意, foreach 中的<var>参数是一个临时的局部变量, foreach 函数执行完后,参数<var>的变量将不在作用,其作用域只在 foreach 函数当中。2.if 函数很像 GNU 的 make 所支持的条件语句——ifeq, if 函数的语法是:$(if <condition>,<then-part>)或是$(if <condition>,<then-part>,<else-part>)可见, if 函数可以包含“ else”部分,或是不含。即 if 函数的参数可以是两个,也可以是三个。 <condition>参数是 if 的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是, <then-part>会被计算,否则<else-part>会被计算。而 if 函数的返回值是,如果<condition>为真(非空字符串),那个<then-part>会是整个函数的返回值,如果<condition>为假(空字符串),那么<else-part>会是整个函数的返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。所以, <then-part>和<else-part>只会有一个被计算。3.call函数call 函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式,这个表达式中,可以定义许多参数,然后可以用 call 函数来向这个表达式传递参数。其语法是:$(call <expression>,<parm1>,<parm2>,<parm3>...)当 make 执行这个函数时, <expression>参数中的变量,如$(1), $(2), $(3)等,会被参数<parm1>, <parm2>, <parm3>依次取代。而<expression>的返回值就是 call 函数的返回值。例如:reverse = $(1) $(2)foo = $(call reverse,a,b)那么, foo 的值就是“ a b”4.origin函数origin 函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的.其语法是:$(origin <variable>)注意, <variable>是变量的名字,不应该是引用。所以你最好不要在<variable>中使用“ $”字符。 Origin 函数会以其返回值来告诉你这个变量的“出生情况”,下面,是 origin函数的返回值:“ undefined”如果<variable>从来没有定义过, origin 函数返回这个值“ undefined”。“ default”如果<variable>是一个默认的定义,比如“ CC”这个变量,这种变量我们将在后面讲述。“ environment”如果<variable>是一个环境变量,并且当 Makefile 被执行时,“ -e”参数没有被打开。“ file”如果<variable>这个变量被定义在 Makefile 中。“ command line”如果<variable>这个变量是被命令行定义的。“ override”如果<variable>是被 override 指示符重新定义的。“ automatic”如果<variable>是一个命令运行中的自动化变量。5.shell函数shell 命令在make中执行shell命令6.errorwarningerror text...报错并退出makewarning报错不退出
0 0
原创粉丝点击