现在的位置: 首页 > 综合 > 正文

Makefile基础

2018年04月03日 ⁄ 综合 ⁄ 共 5498字 ⁄ 字号 评论关闭

1.Makefile里的subst为字符串替换函数,用法是$(subst FROM,TO,TEXT),即将TEXT中的东西从FROM变为TO。

示例:

  $(subst a,the,There is a big tree) 

把“There is a big tree”中的“a”替换成“the”,返回结果是“There is the big tree”。

2.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))”,

    那么,“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一样的。

 

 

3.addprefix加前缀函数,格式为$(addprefix ;,;)

    功能:把前缀;加到;中的每个单词后面。

    返回:返回加过前缀的文件名序列。

示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。

4.addsuffix缀函数,格式为$(addsuffix ;,;)

    功能:把后缀;加到;中的每个单词后面。

    返回:返回加过后缀的文件名序列。

    示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。

5.在Makefile规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的用法是:$(wildcard PATTERN...)。一般我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。

6. filter-out反过滤函数,格式为$(filter-out ;,;)

   示例:

     objects=main1.o foo.o main2.o bar.o

     mains=main1.o main2.o

    

     $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。

7.$(strip <string> )

 

 

名称:去空格函数——strip。 

功能:去掉<string>字串中开头和结尾的空字符。 

返回:返回被去掉空格的字符串值。 

示例:

$(strip a b c )

把字串“abc”去到开头和结尾的空格,结果是“abc”。

 

8.Phony Targets

PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字。有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能。

如果编写一个规则,并不产生目标文件,则其命令在每次make 该目标时都执行。例如:

  clean:

  rm *.o temp

因为"rm"命令并不产生"clean"文件,则每次执行"make clean"的时候,该命令都会执行。如果目录中出现了"clean"文件,则规则失效了:没有依赖文件,文件"clean"始终是最新的,命令永远不会 执行;为避免这个问题,可使用".PHONY"指明该目标。如:

  .PHONY : clean

  这样执行"make clean"会无视"clean"文件存在与否。

已知phony 目标并非是由其它文件生成的实际文件,make 会跳过隐含规则搜索。这就是声明phony 目标会改善性能的原因,即使你并不担心实际文件存在与否。

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

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

subsystem:

cd subdir && $(MAKE)

其等价于:

subsystem:

$(MAKE) -C subdir

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

我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非除非指定了“-e”参数。

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

 export <variable ...>

如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:

 unexport <variable ...>

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

还有一个在“嵌套执行”中比较有用的参数,“-w”或是“--print-directory”会在make的过程中输出一些信息,让你看到目前的工作目录。比如,如果我们的下级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”总是失效的。

10.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>是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。

11.makefile中的条件判断语句,如下例  

ifeq ($(origin LW_GCC), undefined)

CROSS_COMPILE = arm-unknown-linux-uclibc-

CC = $(CROSS_COMPILE)gcc

LIB_PARA = -shared -fPIC -s

else

CC = $(LW_GCC)

LIB_PARA = $(LW_LIB_PARA)

endif

根据LW_GCC是否定义执行不同的语句

当然也可以使用关键字“ifdef”。语法是:

ifdef <variable-name>

如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假。当然,<variable-name>同样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子:

示例一:

    bar =

    foo = $(bar)

    ifdef foo

    frobozz = yes

    else

    frobozz = no

    endif

示例二:

    foo =

    ifdef foo

    frobozz = yes

    else

    frobozz = no

    endif

第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。

12.makefile中的if函数

if函数很像GNUmake所支持的条件语句——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>只会有一个被计算。

13.foreach:

foreach是用来做循环用的,类似于for 语句,语法是:$(foreach <var>,<list>,<text> )

       意思:把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。所以,<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用<var>这个参数来依次枚举<list>中的单词。

例:

names := a b c d

files := $(foreach n,$(names),$(n).o)

      $(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。

13.Makefile可以调用shell脚本,但是Makefileshell脚本是不同的。

1shell中所有引用以$打头的变量其后要加{},而在Makefile中的变量是以$打头的后加()。实例如下:

Makefile

PATH="/data/"

SUBPATH=$(PATH)

Shell

PATH="/data/"

SUBPATH=${PATH}

2Makefile中所有以$打头的单词都会被解释成Makefile中的变量。如果你需要调用shell中的变量(或者正则表达式中锚定句位$),都需要加两个$符号($$)。实例如下:

PATH="/data/"

all:

    echo ${PATH}

    echo $$PATH例子中的第一个${PATH}引用的是Makefile中的变量,而不是shell中的PATH环境变量,后者引用的事Shell中的PATH环境变量。

3)通配符区别

shell 中通配符*表示所有的字符

Makefile 中通配符%表示所有的字符

4)在Makefile中只能在target中调用Shell脚本,其他地方是不能输出的。比如如下代码就是没有任何输出:

VAR="Hello"

echo "$VAR"

all:

   .....以上代码任何时候都不会输出,没有在target内,如果上述代码改为如下:

VAR="Hello"

all:

    echo "$VAR"

    .....以上代码,在make all的时候将会执行echo命令。

5)在Makefile中执行shell命令,一行创建一个进程来执行。这也是为什么很多Makefile中有很多行的末尾都是“;  \”,以此来保证代码是一行而不是多行,这样Makefile可以在一个进程中执行,例如:

SUBDIR=src example

all:

    @for subdir in $(SUBDIR); \

    do\

        echo "building "; \

    done上述可以看出for循环中每行都是以”; \”结尾的。

抱歉!评论已关闭.