Makefile

文章目录

  • Makefile
    • Makefile的规则
      • 语法
      • 通配符
      • 文件搜寻
      • 伪目标
      • 多目标
      • 静态变量
      • 自动生成依赖性
    • 书写命令
      • 嵌套执行make
      • 定义命令包
    • 变量使用
      • 变量基础
      • 变量中的变量
      • 变量的高级用法
      • overide指示符
      • 目标变量
    • 条件判断
    • 函数使用
      • 函数调用语法
      • 字符串处理函数
        • subst
        • patsubst
        • strip
        • findstring
        • filter
        • filter-out
        • sort
        • word
        • wordlist
        • words
        • firstword
      • 文件名操作函数
        • dir
        • notdir
        • suffix
        • basename
        • addsuffix
        • addprefix
        • join
      • foreach 函数
      • if 函数
      • shell 函数
      • 控制make的函数
    • 自动化变量

Makefile的规则

语法

target ... : prerequisites ... command......

target

可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。

prerequisites
生成该target所依赖的文件和/或target

command
该target要执行的命令(任意的shell命令)

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

使用反斜杠\作为换行符

通配符

make支持三个通配符:*?~

通配符代替了一系列文件,*.c表示所有后缀为c的文件。如果文件名中有通配符,如*,可以使用转义字符\,如\*来表示真实的*字符,而不是任意长度的字符串。

clean:cat main.crm -f *.o

这是shell命令中的通配符

print: *.clpr -p $?touch print

上面这个例子说明了通配符也可以在我们的规则中,目标print依赖于所有的 .c 文件。其中的 $? 是一个自动化变量

object = *.o

上面这个例子,表示了通配符同样可以用在变量中,并不是说 *.o 会展开,objects的值就是*.o。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开,也就是让objects的值是所有 .o 的文件名的集合,那么,你可以这样:

objects := $(wildcard *.o)
  1. 列出确定文件夹中的所有.c文件

objects := $(wildcard *.c)

  1. 列出所有的.o文件

objects := ( p a t s u b s t (patsubst %.c,%.o, (patsubst(wildcard *.c))

Makefile的关键字:wildcardpatsubst

文件搜寻

makefile中有特殊的变量VPATH指定文件路径,可使make自动去寻找文件间的依赖关系。如果没有指定这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,make就会在当前目录找不到的情况下,到指定的目录中去找寻文件了。

例如:

VPATH = src : …/headers

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

另一个设置文件搜索路径的方法是使用make的vpath关键字,这是全小写的,这不是变量,是一个make的关键字。可以指定不同的文件在不同的搜索目录中。这个灵活的功能的使用方法有三种:

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

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

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

vpath %.h ../headers

该语句表示,要求make在“…/headers”目录下搜索所有以 .h 结尾的文件

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

vpath %.c foo
vpath %   blish
vpath %.c bar

其表示.c结尾的文件,现在foo目录,然后是blish,最后是bar目录

vpath %.c foo:bar
vpath % blish

.c结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录

Makefile中的%标记和系统通配符 * 的区别在于, * 是应用在系统中的,%是应用在这个Makefile文件中的

伪目标

clean:rm *.o tmp

我们并不生成“clean”这个文件。“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显式地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。

当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

.PHONY : clean
clean:rm *.o temp

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性:

all : prog1 prog2 prog3
.PHONY : allprog1 : prog1.o utils.occ -o prog1 prog1.o utils.oprog2 : prog2.occ -o prog2 prog2.oprog3 : prog3.o sort.o utils.occ -o prog3 prog3.o sort.o utils.o

我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于默认目标的特性是,总是被执行的,但由于“all”又是一个伪目标,伪目标只是一个标签不会生成文件,所以不会有“all”文件产生。于是,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。 .PHONY : all 声明了“all”这个目标为“伪目标”。(注:这里的显式“.PHONY : all” 不写的话一般情况也可以正确的执行,这样make可通过隐式规则推导出, “all” 是一个伪目标,执行make不会生成“all”文件,而执行后面的多个目标。建议:显式写出是一个好习惯。)

.PHONY : cleanall cleanobj cleandiffcleanall : cleanobj cleandiffrm programcleanobj :rm *.ocleandiff :rm *.diff

“make cleanall”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。

多目标

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

bigoutput littleoutput : text.ggenerate text.g -$(subst output,,$@) > $@

上述规则等价于

bigoutput : text.ggenerate text.g -big > bigoutput
littleoutput : text.ggenerate text.g -little > littleoutput

-$(subst output,,$@) 中的 $表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是替换字符串的意思,$@表示目标的集合,就像一个数组, $@ 依次取出目标,并执于命令。

静态变量

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

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

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

target-pattern是指明了targets的模式,也就是的目标集模式。

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

如果我们的定义成 %.o ,意思是我们的;集合中都是以 .o 结尾的,而如果我们的定义成 %.c ,意思是对所形成的目标集进行二次定义,其计算方法是,取模式中的 % (也就是去掉了 .o 这个结尾),并为其加上 .c 这个结尾,形成的新集合。

objects = foo.o bar.oall: $(objects)$(objects): %.o: %.c$(CC) -c $(CFLAGS) $< -o $@

上面的例子中,指明了我们的目标从$object中获取, %.o 表明要所有以 .o 结尾的目标,也就是 foo.o bar.o ,也就是变量 $object 集合的模式,而依赖模式 %.c 则取模式 %.o 的 % ,也就是 foo bar ,并为其加下 .c 的后缀,于是,我们的依赖目标就是 foo.c bar.c 。而命令中的 $<$@ 则是自动化变量, $< 表示第一个依赖文件, $@ 表示目标集(也就是“foo.o bar.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: %.elemacs -f batch-byte-compile $<

$(filter %.o,$(files))表示调用Makefile的filter函数,过滤“$files”集,只要其中模式为“%.o”的内容。

自动生成依赖性

在大型的工程中,需要知道一些c文件中包含了那些头文件,并且在加入或者修改头文件时,也需要小心的修改makefile,这样很没有维护性。为了避免这个情况,可以使用C/C++编译器的功能,使用-M的选项,自动找到源文件中包含的头文件,并形成依赖关系。GUN使用-M参数会把一些标准库的头文件包含进来,使用-MM则不会。

编译器为每一个源文件自动生成的依赖关系放到一个文件中,为每一个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文件依赖于.crm -f $@的意思是删除所有的目标,也就是.d文件。第二行的意思,为每个依赖文件$<,也就是.c文件生成依赖文件,$@表示模式%.d文件,如果有一个C文件是name.c,那么%就是name,$$$$是一个随机编号,第二行生成的文件有可能是name.d.12345,第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。

将这些规则放到主Makefile中,可以使用include来引用别的Makefile文件

sources = foo.c bar.c
include $(sources: .c=.d)

上述语句中的 $(sources:.c=.d) 中的 .c=.d 的意思是做一个替换,把变量 $(sources) 所有 .c 的字串都替换成 .d ,关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次序来载入文件,最先载入的 .d文件中的目标会成为默认目标。

书写命令

### 显示命令

makefile中#是注释符

make会把其要执行的命令行在命令执行前输出到屏幕上。当使用@字符在命令行前,那么这个命令将不被make显示出来。

@echo 正在编译XXX模块......

当make执行时,会输出“正在编译xxx模块”,但是不会输出命令,如果没有@,make将会输出

echo 正在编译XXX模块......
正在编译XXX模块......

如果make执行时,带入make参数-n或者--just-print,那么只是显示命令,但是不会执行命令,这个命令有利于调试makefile,make参数-s或者--silent--quiet则是全面禁止命令的显示。

### 命令执行

如果想让上一条命令的结果应用在下一条命令时,应该使用分号分隔这两条命令。,并且这两条命令需要放在一行上。

exec:cd /home/leo; pwd

### 命令出错

每当命令运行完成后,make会检查每个命令的返回码,如果命令返回成功,make会执行下一条命令,当规则中所有的命令都返回成功后,这个规则算是成功完成了。如果某个命令出错了,make会终止当前的规则,这有可能或终止所有规则的执行。

为了忽略命令出错,可以在Makefile的命令行前加一个-(TAB之后),标记为不管命令是否出错都认为是成功的。

在make后加上-i或者是-ignore-errors,这是一种全局的方法,Makefile中所有命令都会忽略错误。

-k或者-keep-going表示如果某个命令出错了,那么就终止该命令的执行,但继续执行其他规则。

clean:-rm -f *.o

嵌套执行make

在大的工程中,把不同模块或者是不同功能的源文件放在不同的目录中,可以在每个目录中都书写一个该目录的Makefile,有利于Makefile更加简洁,不至于把所有东西都写在一个Makefile中,对模块编译和分段编译非常有好处。

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

subsystem:cd subdir && $(MAKE)

等价于

subsystem:$(MAKE) -C subdir

总控的Makefile可以传递到下级的Makefile中,但是不会覆盖下层Makefile中所定义的变量,除非指定了-e参数

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

export <variable ...>;

如果不想要某些变量传递到下级的Makefile,可以这样声明

unexport <variable ...>;

如果需要传递所有的变量,只需要使用export就行,后面什么也不用跟,表示传递所有的变量。

一个是SHELL一个是MAKEFLAGS,这俩个变量不管是否export,其总是要传递到下层的Makefile中,特别是MAKEFLAGS变量。

如果不想传,可以这样:

subsystem:cd subdir && $(MAKE) MAKEFLAGS=

在嵌套执行的过程中,有一些比较有用的参数,-w或者--print-directory可以在make的过程中输出当前工作的目录。

进入目录:

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

离开目录:

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

定义命令包

如果Makefile中出现一些相同的命令序列,可以为这些命令序列定义一个变量。定义命令序列的语法以define开始,以endef结束

define run-yacc
yacc $(firstword &^)
mv y.tab.c &@
endef

run-yacc是命令包的名称,不能和Makefile中的变量重名。
第一个命令是运行yacc程序,因为yacc程序总是生成y.tab.c的文件,所以第二行的命令就是把这个文件改名字。

foo.c : foo.y$(run-yacc)

命令包“run-yacc”中的 $^ 就是 foo.y , $@ 就是 foo.c (有关这种以 $ 开头的特殊变量,我们会在后面介绍),make在执行命令包时,命令包中的每个命令会被依次独立执行。

变量使用

  • 变量可以包含字符、数字、下划线,但是不应该包含:#=或者空字符串。

  • 变量大小写敏感

  • $<$@是自动化变量

变量基础

  • 使用时,需要在变量名前加$符号,最好使用小括号()或者是{}把变量包括起来。

  • 使用真实的$字符,可以使用$$来表示。

  • 变量可以使用在文件中的目标依赖命令和新的变量中。

objects = program.o foo.o utils.o
program : $(objects)cc -o program $(objects)$(objects) : defs.h

变量中的变量

  • 在定义变量的值时,可以使用其他的变量来构造变量的值,在Makefile中有两种方式来用变量来定义变量的值。
foo = $(bar)
bar = $(ugh)
ugh = Huhall:echo $(foo)
  • Makefile中的变量是可以把真实性推到后面来定义

  • = : 延时赋值,该变量只用在调用的时候才被赋值

  • := : 直接赋值,与延时赋值相反,使用直接赋值的话,变量的值定义时就已经确定

  • ?= : 若变量的值为空,则进行赋值,通常用于设置默认值

  • += : 追加赋值,可以往变量后增加新的内容

VAR_A = FILEA
VAR_B = $(VAR_A)
VAR_C := $(VAR_A)
VAR_A += FILEB
VAR_D ?= FILED
.PHONY:check
check:@echo "VAR_A:"$(VAR_A)@echo "VAR_B:"$(VAR_B)@echo "VAR_C:"$(VAR_C)@echo "VAR_D:"$(VAR_D)

变量的高级用法

  • 变量值替换

    • 可以替换变量中共有的部分:$(var:a=b) 或者 ${var:a=b}。把var变量中以a字串结尾的a换成b。
foo := a.o b.o c.o
bar := $(foo:.o=.c)

overide指示符

通常在执行make时,如果通过命令行定义了一个变量,那么它将替代在Makefile中出现的同名变量的定义。就是说,对于一个在Makefile中使用常规方式(使用“=”、“:=”或者“define”)定义的变量,我们可以在执行make时通过命令行方式重新指定这个变量的值,命令行指定的值将替代出现在Makefile中此变量的值。如果不希望命令行指定的变量值替代在Makefile中的变量定义,那么我们需要在Makefile中使用指示符“override”来对这个变量进行声明

目标变量

  • 设置局部变量:在Makefile中定义的变量都是“全局变量”,在整个文件,我们都可以访问这些变量;同样可以为某个目标设置局部变量,可以和"全局变量"同名。但是作用范围只在这条规则和其连带规则中,所以其值只在作用范围内有效,而不会影响规则链外的全局变量。
<target ...> : <variable-assignment>;
<target ...> : overide <variable-assignment>

事例:

prog : CFLAGS = -g
prog : prog.o foo.o bar.o$(CC) $(CFLAGS) prog.o foo.o bar.oprog.o : prog.c$(CC) $(CFLAGS) prog.cfoo.o : foo.c$(CC) $(CFLAGS) foo.cbar.o : bar.c$(CC) $(CFLAGS) bar.c

$(CFLAGS) 的值是什么,在prog目标,以及其所引发的所有规则中(prog.o foo.o bar.o的规则), $(CFLAGS) 的值都是 -g

条件判断

ifeqelseendif 三个关键字

  • ifeq: 表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起

  • endif: 表示一个条件语句的结束

事例1

libs_for_gcc = -lgnu
normal_libs =foo: $(objects)
ifeq ($(CC),gcc)$(CC) -o foo $(objects) $(libs_for_gcc)
else$(CC) -o foo $(objects) $(normal_libs)
endif

事例2

libs_for_gcc = -lgnu
normal_libs =ifeq ($(CC),gcc)libs=$(libs_for_gcc)
elselibs=$(normal_libs)
endiffoo: $(objects)$(CC) -o foo $(objects) $(libs)

关键字

  1. ifeq
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
  1. ifneq
ifneq (<arg1>, <arg2>)
ifneq '<arg1>' '<arg2>'
ifneq "<arg1>" "<arg2>"
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"
  1. ifdef

事例一:

bar =
foo = $(bar)
ifdef foofrobozz = yes
elsefrobozz = no
endif

事例二:

foo =
ifdef foofrobozz = yes
elsefrobozz = no
endif

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

  1. ifndef

函数使用

  • 函数的返回值可以当作变量来使用

函数调用语法

函数调用像变量的使用,是以$来标识的

$(<function> <arguments>) 或者 ${<function> <arguments>}

<function>是函数名,make支持的函数不多,是函数的参数,参数间以逗号来分隔,而函数名和参数之间以空格分隔。函数调用以$开头,以圆括号或者花括号把函数名和参数括起。例如:$(subst a,b,$(x))

实例:

comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))

在这个示例中, $(comma) 的值是一个逗号。 $(space) 使用了 $(empty) 定义了一个空格, $(foo) 的值是 a b c$(bar) 的定义用,调用了函数 subst ,这是一个替换函数,这个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数是替换操作作用的字串。这个函数也就是把 $(foo)中的空格替换成逗号,所以 $(bar) 的值是 a,b,c

字符串处理函数

subst

  • $(subst <from>, <to>, <text>)
  • 名称:字符串替换函数
  • 功能:把字符串text中的from替换成to
  • 返回:函数返回被替换过后的字符串
  • 示例:$(subst ee,EE,feet on the street)
  • 结果:fEEt on the strEEt

patsubst

  • $(patsubst <pattern>,<replacement>,<text>)
  • 名称:模式字符串替换函数
  • 功能:查找 <text> 中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 <pattern> ,如果匹配的话,则以 <replacement> 替换。这里, <pattern> 可以包括通配符 % ,表示任意长度的字串。如果 <replacement> 中也包含 % ,那么, <replacement> 中的这个 % 将是 <pattern> 中的那个 % 所代表的字串。(可以用 \来转义,以 \%来表示真实含义的 % 字符)
  • 返回:函数返回被替换过后的字符串
  • 示例:$(patsubst %.c,%.o,x.c.c bar.c)
  • 类似于:$(var:<pattern>=<replacement>;) 相当于 $(patsubst <pattern>,<replacement>,$(var))

strip

  • $(strip <string>)
  • 名称:去空格函数
  • 功能:去掉<string>字串中开头和结尾的空字符

findstring

  • $(findstring <find>,<in>)
  • 查找字符串函数
  • 功能:在字串<in>中查找<find>字串
  • 返回:若找到,返回<find>,否则返回空字符串
  • 示例:$(findstring a,a b c)

filter

  • $(filter <pattern...>,<text>)
  • 名称:过滤函数
  • 功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多种模式。
  • 返回:返回符合模式<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

  • filter相反

sort

  • $(sort <list>)
  • 名称:排序函数
  • 功能:给字符串<list>中的单词排序(升序),sort会去掉list中相同的单词
  • 返回:返回排序后的字符串
  • 示例:$(sort foo bar lose)返回bar foo lose

word

  • $(word <n>, <text>)
  • 名称:取单词函数
  • 功能:取字符串<text>中第<n>个单词(从1开始)
  • 示例:$(word 2, foo bar baz) 返回值是bar

wordlist

  • $(wordlist <ss>,<e>,<text>)
  • 名称:取单词串
  • 功能:从字符串 <text> 中取从 <ss> 开始到 <e> 的单词串
  • 示例:$(wordlist 2, 3, foo bar baz) 返回值是 bar baz

words

  • $(words <text>)
  • 名称:单词个数统计函数
  • 功能:统计<text>中字符串中的单词个数
  • 返回:返回<text>中的单词数
  • 示例:$(words, foo bar baz)返回值是3

firstword

  • $(firstword <text>)
  • 返回出字符串中第一个单词

文件名操作函数

dir

  • $(dir <names ...>)
  • 名称:取目录名称–dir
  • 功能:从文件名序列 <names> 中取出目录部分。目录部分是指最后一个反斜杠( / )之前的部分。如果没有反斜杠,那么返回 ./
  • 示例:$(dir src/foo.c hacks) 返回值是 src/ ./

notdir

  • 与函数dir相反,返回文件名序列<names>的非目录部分
  • 示例:$(notdir src/foo.c hacks) 返回值是 foo.c hacks

suffix

  • $(suffix <names...>)
  • 取后缀函数,取出各个文件名的后缀

basename

  • $(basename <names...)
  • 取前缀函数,从文件名序列 <names> 中取出各个文件名的前缀部分
  • 示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是src/foo src-1.0/bar hacks

addsuffix

  • $(addsuffix <suffix>,<names...>)
  • 加后缀函数
  • 功能:把后缀 <suffix> 加到 <names> 中的每个单词后面
  • 示例:$(addsuffix .c,foo bar) 返回值是 foo.c bar.c

addprefix

  • $(addprefix <prefix>,<names...>)
  • 名称:加前缀函数
  • 功能:把前缀 加到 中的每个单词后面
  • 示例:$(addprefix src/,foo bar) 返回值是 src/foo src/bar

join

  • $(join <list1>,<list2>)
  • 名称:连接函数
  • 功能:把 <list2> 中的单词对应地加到 <list1> 的单词后面。如果 <list1> 的单词个数要比 <list2> 的多,那么, <list1> 中的多出来的单词将保持原样。如果 <list2> 的单词个数要比 <list1> 多,那么, <list2> 多出来的单词将被复制到 <list1>
  • 示例:$(join aaa bbb , 111 222 333) 返回值是 aaa111 bbb222 333

foreach 函数

  • $(foreach <var>,<list>,<text>)
  • 把参数 <list> 中的单词逐一取出放到参数 <var> 所指定的变量中,然后再执行 <text> 所包含的表达式。每一次 <text> 会返回一个字符串,循环过程中, <text> 的所返回的每个字符串会以空格分隔,最后当整个循环结束时, <text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。
  • 示例:
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

if 函数

  • $(if <condition>,<then-part>) 或者 $(if <condition>,<then-part>,<else-part>)

shell 函数

  • shell函数把执行操作系统命令后的输出作为函数返回,可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量
contents := $(shell cat foo)
files := $(shell echo *.c)

控制make的函数

  • $(error <text ...>)
  • 产生一个致命的错误, <text ...> 是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。
ERR = $(error found an error!).PHONY: errerr: $(ERR)
  • $(warning <text ...>) 这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。

自动化变量

自动化变量:这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。

  • $@ : 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么, $@ 就是匹配于目标中模式定义的集合。
  • $% : 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是 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所不能识别的,那么 $* 就是空值。

【整理】Makefile编写规则相关推荐

  1. 一篇文章看懂makefile编写规则

    该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客:(最原始版本) http://blog.csdn.net/haoel/article/details/2886 我转自 ...

  2. Makefile 编写入门

    Makefile编写规则: 刚开始跟这些Makefile的时候发现会报错误Makefile:2: missing separator. Stop. 在make命令后出现这种错误提示,是提示第2行没有分 ...

  3. Makefile文件的编写规则

    欢迎大家关注笔者,你的关注是我持续更博的最大动力 Makefile文件编写规则 文章目录: 1 makefile文件介绍 2 makefile文件编写 1 makefile文件介绍 makefile是 ...

  4. c语言编译流程简单整理以及简单makefile编写

    c语言编译流程简单整理以及简单makefile编写 前言: 最近接触了makefile.为了学习makefile,就去了解了部分需要的知识,目前先简单的整理在这里. c语言编译过程 C语言在编译过程中 ...

  5. C语言:gcc编译过程及make命令、makefile语法规则

    gcc编译过程 编译过程: make && makefile make概述 makefile语法规则 make命令格式 Makefile案例 之所以写成多个文件,是出于模拟多模块编程的 ...

  6. linux环境cpp/c文件的makefile编写(caffe举例)

    编译单个cpp文件 方法一.g++ 文件名.cpp,生成一个名为 "文件名.out" 的可执行文件 方法二.g++ -c 文件名.cpp -o 新文件名.o:生成一个被命名成 &q ...

  7. Linux C编程Makefile编写初步-转

    Linux C编程Makefile编写初步 假设我们有下面这样的一个程序,源代码如下:  /* main.c */  #include "mytool1.h"  #include  ...

  8. Ubuntu下使用gcc和makefile编写c语言程序

    文章目录 前言 一.gcc编写c语言程序 1.hello world的输出 2.简单程序的编译与运行 3.windows环境下的编译运行结果对比 二.makefile编写c语言程序 总结 前言 本文通 ...

  9. linux下MaKefile编写

    Linux下编写 makefile 详细教程 近期在学习Linux下的C编程,买了一本叫<Linux环境下的C编程指南>读到makefile就越看越迷糊,可能是我的理解能不行. 于是goo ...

最新文章

  1. DeeCamp2021启动,李开复张亚勤吴恩达等大咖喊你报名啦
  2. matlab中给图像加几个矩形框_在图像中画矩形框(matlab)
  3. 清理c盘垃圾的cmd命令_用命令删除系统垃圾,这波操作深藏功与名
  4. android studio 搭建环境,Android studio搭建xposed环境
  5. 使用 redmind 进行项目任务管理
  6. 学习clojure(2)
  7. java文档注释加减乘除,java精确的加减乘除
  8. 论文笔记_S2D.20_2017-ICCV-从单张RGB图像到精确尺度深度图评估的一种双支网络
  9. Python中的lamda表达式
  10. php二维数组以某个键进行排序
  11. 【动态规划】最大子段和
  12. Edge浏览器 安装 插件Adblock Plus失败
  13. Python大数据分析(三):大数据统计分析技术
  14. PostgreSQL/pgsql生成随机的姓名的函数
  15. Unreal Engine 4学习资料整理
  16. JavaScript案例:页面自动跳转到首页
  17. 2012第35周国内Android应用下载动态
  18. 微信公众号短链生成服务器,微信公众号短链接生成源码
  19. SpringCloud相关jar maven管理工具不能下载(Finchley.M8)
  20. 计算机视觉——SIFT特征提取与检索

热门文章

  1. 估值指标二把手——市净率
  2. 成本优化之使用P2P的方案的需要了解的本地SDK的背后的原理
  3. halocn标定找旋转中心_HALOCN运算功能函数快查 | 学步园
  4. springboot的商品设计热销排行实现
  5. 入行大数据,需要学习哪些基础知识?
  6. Redis部署启动多个端口实例
  7. Oracle登录错误12560,登录错误-----ORA-12560: TNS: 协议适配器错误
  8. 如何理解柯里化|函数式编程
  9. 《大话处理器》简要学习笔记
  10. 基于视觉导航的自主机器人简介(一)