make 要点简记

1.隐式推导

make可以自动推导文件及其文件依赖关系后面的命令,所以我们没有必要在每一个.o文件后面都写上类似的命令,因为make 会自动识别并且自动推导命令.

objects = main.o
main.o:main.cc main:$(objects)
c++ $(objects) -o main
# 伪目标,防止目录下有同名为clean的文件造成make clean执行失败
.PHONY:clean
clean:
 -rm $(objects) main

2.在规则中使用通配符

objects = $(wildcard *.o)表示将object变量定义为所有.o文件组成的集合。在定义变量的时候,不能使用通配符,否则上述变量objects的值就为.o,除非使用wildcard*函数.

3.引用其他的makefile文件

可以通过include 命令引用其他的makefile文件,如下所示:

	include foo.make *.mk

上面表示引用当前目录下的foo.make文件以及所有后缀为mk的文件。

4.make 的工作方式

1. 读入所有的makefile

2. 读入被include 的其他makefile

3. 初始化文件中的变量

4. 推导隐晦规则,分析所有规则

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

6. 根据依赖关系,确定那些目标需要重新生成

7. 执行生成命令

5.文件搜寻

为了指明更多的文件搜索路径,可以定义vpath变量。vpath的定义方式有下面3种:

  1. vpath 为符合模式pattern的文件指定搜索路径为directories
  2. vpath 清除符合模式为pattern的文件搜索路径
  3. vpath 清除所有已经设置的文件搜索路径
vpath %.h ../header #表示要求make在"../headers"目录下搜索所有以".h"结尾的文件

6.静态模式

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

语法:

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

在上面的命令中:

  1. targets定义了一系列的目标文件,是目标的一个集合,可以有通配符。
  2. target-pattern指明了target的模式,也就是目标集模式。
  3. prereq-patterns是目标的依赖模式,它是对target-pattern形成的模式再进行一次依赖目标的定义。

例子:

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

可以使用filter函数去除指定模式的文件,如下所示:

files = foo.elc bar.o
$(filter %.o,$(files)):%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@

7.嵌套执行make

在一些大型的工程中,我们会把不同的模块或者不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录下的makefile文件,这有利于维护我们的makefile.

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

subsystem:
cd subdir $$(MAKE)

其等价于:

subsystem:
$(MAKE) -C subdir

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

export <variable ...>

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

unexport <variable ...>

8.变量中的变量

在定义变量的值时,可以使用其它变量来构造变量的值。有下述两种方式:

1. 采用=定义变量

使用=定义变量可以使用未定义的值,也可以是后面定义的值,如下所示:

foo = $(bar)
$bar=huh?

上面例子中$(foo)最终的值为huh?

2.采用:=定义变量

使用:=定义变量,必须使用当前已经定义好的变量,不能使用当前未定义的变量。

9.追加变量值

我们可以使用"+="操作符给变量追加值。例如:

objects: main.o foo.o bar.o utils.o
objects+=another.o

这样,上面例子中的$(objects)最终的值为main.o foo.o bar.o utils.o another.o

10.使用条件判断

用两个例子来介绍这一部分的内容:

例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

与ifeq相对应的有ifneq,即如果不等于

例2:

bar =
foo = $(bar)
ifdef foo
frobozz=yes
els
frobozz=no
endif

类似于C++中宏定义中的#ifdef

11.函数

1.字符串处理函数
1.subst
$(subst <from>,<to>,<text>)
名称:字符串替换函数-subst函数
功能:将字符串<test>中的<from>字符串替换为<to>
返回:函数返回被替换过后的字符串 
2.patsubst
$(patsubst <pattern>,<replacement>,<text>)
名称:模式字符串替换函数-patsubst
功能:查找<test>中的单词是否符合模式pattern,如果匹配,就以<replacement>进行替换,这里的<pattern>可以包括通配符"%"表示任意长度的字符串。

例子:

patsubst(%.c,%.o,x.c.c bar.c)
将字符串"x.c.c bar.c"中符合模式[%.c]的单词替换为[%.o],故返回的结果为x.c.o bar.o
3.strip
$(strip <string>)
名称:去空格函数
功能:去掉<string>字符串中开头和结尾的空字符串
返回:返回去掉空格的字符串值
4.findstring

$(findstring ,)

名称:查找字符串函数-findstring

功能:在字符串中查找字符串

返回:如果找到,返回,否则返回空字符串

5.filter
$(filter <pattern...>,<text>)
名称:过滤函数
功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern> 的单词。可以有多个模式

例子:

sources = foo.c bar.c baz.s ugh.h
foo:$(source)
cc $(filter %.c %.s,$(sources)) -o foo

其中$(filter %.c %.s,$(sources))返回的值为"foo.c bar.c baz.s"

6.filter-out
$(filter-out <pattern ...>,<text>)
名称:反过滤函数-filter-out
功能:以<pattern>模式过滤<text>字符串中的单词,去掉符合模式<pattern>的单词,可以有多个模式
返回:返回不符合模式<pattern>的字符串。

例子:

objects = main1.o main2.o foo.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects))
上述经过filter-out的返回值为bar.o以及foo.o
7.sort
$(sort <list>)
名称:排序函数-sort
功能:给字符串<list>中的单词排序
返回:返回排序后的字符串
示例:$(sort foo bar lose)返回"bar foo lose"
备注:sort函数会去除list之中重复的单词
8.word
$(word <n>,<text>)
名称:去单词函数-word
功能:取出字符串text中的第<n>个单词(从1开始)
返回:返回字符串<text> 中的第<n>个单词,若<n>比 <text>中的单词数要大,那么返回空字符串
示例:$(word 2,foo bar baz) 返回值为bar
2.文件名操作函数
1.dir
$(dir <names ...>)
名称:取目录函数 --dir
功能:从文件名序列<names>中取出目录部分,目录部分指的是最后一个反斜杠 "/"之前的部分,如果没有反斜杠,则返回“./”
返回:返回文件名序列<names>中的目录部分
示例:$(dir src/foo.c hacks)返回值为 "src/ ./"
2.notdir
$(notdir <names...>)
名称:取文件函数
功能:从文件名序列<names>中取出非目录的部分。菲目录部分是最后一个/之后的部分
返回:返回文件名序列<names>中的非目录部分
示例:$(notdir src/foo.c hacks)返回值为"foo.c hacks"
3.suffix
$(suffix <names...>)
名称:取后缀函数--suffix
功能:从文件名序列<names> 中取出各个文件名的后缀
返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字符串。
示例:$(suffix src/foo.c src-1.0/bar.c hacks) 返回值为".c .c"
4.basename
名称:取前缀函数--basename
功能:从文件名序列中取出各个文件名的前缀部分
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字符串。
示例:$(basename src/foo.c src-1.0/bar.c hacks)的返回值为"src/foo src-1.0/bar hacks"
5.addsufix
$(addsuffix <suffix>,<names...>)
名称:加后缀函数-addsuffix
功能:把后缀<suffix>加到 <names>中的每个单词后面
返回:返回加过后缀的文件名序列
示例:$(addsuffix .c,foo bar)返回值为"foo.c bar.c"
6.addprefix
$(addprefix <suffix>,<names...>)
名称:加后缀函数--addsuffix
功能:把后缀<suffix>加到<names>中的每个单词后面。
返回:返回加过后缀的文件名序列
示例:$(addprefix .c,foo bar)返回值为"foo.c bar.c"
7.join
$(join <list1>,<list2>)
名称:连接函数--join
功能:把<list2>中的单词相应地添加到<list1>的单词后面。如果<list1>中的单词个数要比<list2>多,那么<list1>中多出来的单词将保持原样。如果<list2>中的单词数量要比<list1>多,那么<list2>中多出来的单词要被复制到<list1>中。 
返回:返回连接过后的字符串 
示例:$(join aaa bbb,111 222 333) 返回值为"aaa111,bbb222,3333"
3.foreach函数

foreach函数用于循环。语法如下:

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

例子:

names:=a b c d
files := $(foreach n,$(names),$(n).o)
执行完毕后,变量$(files)的值为"a.o b.o c.o d.o"
4.if函数
1.$(if <condition>, <then-part>)
2.$(if <condition>,<then-part>,<else-part>) if函数可以包含else或者不包含。即if函数的参数可以是两个,也可以是3个。<condition>参数是if的表达式,如果器返回为非空字符串,那么这个表达式就为真,于是<then-part>会被计算,否则<else-part>会被计算。
如果<condition>为真,那么<then-part>会为整个函数的返回值,否则<else-part>会作为函数的返回值。如果<else-part>没有定义,那么函数会返回空字符串。
5.shell函数

shell函数将执行shell命令的返回值作为函数返回值。

例子:

contents:=$(shell cat foo)
files:=$(shell echo *.c)

12.构建多个目标

通过构建伪目标all的方式来同时指定多个目标为终极目标。

例子:

.PHONY:all
all:exec1 exec2
这个makefile的终极目标由exec1和exec2构成

13.隐含规则

foo:foo.o bar.o
cc- o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

我们注意到,上述makefile没有写下如何生成foo.o和bar.o这两个目标的规则和命令,因为make的隐含规则功能为我们自动推导这两个目标的依赖目标。

隐含规则一览
1.C程序

<n>.o的依赖目标会自动推导为<n>.c,并且其生成命令为 $(CC) -c $(CPPFLAGS) $(CFLAGS)

2.C++程序

<n>.o的依赖目标会自动推导为<n>.c或者<n>.cc,并且其生成命令为 $(CC) -c $(CPPFLAGS) $(CXXFLAGS)

因此建议使用.cc而不是.c或者.cpp作为C++程序的后缀。

链接object文件的隐含规则

对于下述makefile

x:y.o z.o

如果y.c 与 z.c文件都存在,隐含规则会执行下述命令:

cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o

14.隐含规则使用的变量

命令
  1. AR 函数库打包的程序,默认为ar
  2. AS 汇编语言编译程序,默认为as
  3. CC C语言编译程序,默认为"cc"
  4. CXX C++语言编译程序,默认为"g++"
  5. RM 删除文件命令,默认为rm
参数
  1. ARFLAGS 函数库打包程序AR命令的参数,默认为rv
  2. ASFLAGS 汇编语言编译器参数
  3. CFLAGS C语言编译器参数
  4. CXXFLAGS C++语言编译器参数
  5. CPPFLAGS C预处理器参数
  6. LDFLAGS 链接器参数

15.自定义模式规则

举例说明,下述makefile文件将所有的目录下所有的.c文件都编译成为.o文件.

%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@

上述makefile自定义了一个针对.o文件的隐含规则

16.自动化变量

  1. $@ 表示规则中的目标文件集,在模式规则中,如果有多个目标,那么 $@ 就是匹配于目标中模式定义的集合。
  2. $% 仅当目标是函数库文件时,表示规则中的目标成员名。例如一个目标是foo.a(bar.o),那么$%表示bar.o, $@ 表示foo.a。
  3. $< 依赖目标中的第一个目标文字。如果依赖目标是以模式%定义的,那么$<将是符合模式的一系列文件集,请注意是一个一个取出来的。
  4. $? 所有比目标新的依赖目标的几个,以空格分隔。
  5. $^ 所有依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的。那么这个变量会去除重复的依赖目标,只保留一份。
  6. $+ 是所有依赖目标的集合,但是不去除重复的依赖目标。

17.使用make更新函数库文件

可以使用下述格式来指定函数库文件及其组成。

archive(member)

例子:

foolib(hack.o foo.o):hack.o foo.o
ar cr foolib hack.o foo.o

上述命令在文件foo.o和hack.o文件的基础之上构建了foolib.a文件。

在进行函数库打包的过程中,若多个ar命令同时运行在同一个函数库打包文件时,可能损坏这个函数库文件,所以对于这种情况要谨慎使用make -j 命令。

函数库在生成过程中可能也会触发.o文件的隐含规则

18. 上述学习过程及形成的代码保存在我的代码仓库了,欢迎访问!

makefile

make 要点简记的更多相关文章

  1. Android图形合成和显示系统---基于高通MSM8k MDP4平台

    介绍了Android SurfaceFlinger层次以下的图形合成和显示系统,主要基于高通MSM8k MDP4x平台. 做为Android Display专题.SurfaceFlinger的详细介绍 ...

  2. 【简记】HTML + CSS 的一些要点(不定时更新)

    1.td占据多行 / 列时,其挤开的 td 不写(但是包裹 td 的 tr 要写) 2. display:td 的元素中的文本默认垂直不居中(table中的td中的文本是垂直居中的) 3.th虽然定义 ...

  3. C++常见笔试面试要点以及常见问题

    1. C++常见笔试面试要点: C++语言相关: (1) 虚函数(多态)的内部实现 (2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现 ...

  4. 《高性能javascript》一书要点和延伸(上)

    前些天收到了HTML5中国送来的<高性能javascript>一书,便打算将其做为假期消遣,顺便也写篇文章记录下书中一些要点. 个人觉得本书很值得中低级别的前端朋友阅读,会有很多意想不到的 ...

  5. RangePartitioner 实现简记

    摘要: 1.背景 2.rangeBounds 上边界数组源码走读 3.RangePartitioner的sketch 源码走读 4.determineBounds 源码走读 5.关于RangePart ...

  6. [php]laravel框架容器管理的一些要点

    本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...

  7. 基础笔记(一):C#编程要点

    前言 来源于手中日常摘录的资料和书籍,算是对看过的东西的总结,部分注有阅读心得,也有部分只提出大纲或结论.(备注:本篇文章中大部分要点需要有实际的开发经验,有助于阅读理解.)     目录 const ...

  8. CORS基础要点:关于dataType、contentType、withCredentials

    事实上,面试时我喜欢问跨域,因为多数开发者都知道它并且常用,而我希望能从面试者的回答中知道他在这个问题的深入程度,进一步看看面试者研究问题的思维方式及钻研精神,然而确实难到了很多人,当然这也不是面试通 ...

  9. 漫谈C++:良好的编程习惯与编程要点

    以良好的方式编写C++ class 假设现在我们要实现一个复数类complex,在类的实现过程中探索良好的编程习惯. ① Header(头文件)中的防卫式声明 complex.h: # ifndef ...

随机推荐

  1. Git学习 -- 自定义Git

    忽略特殊文件 在工作区创建.gitignore文件,编写内容 # Windows: Thumbs.db ehthumbs.db Desktop.ini # Python: 忽略Python编译产生的. ...

  2. HDU - 1702 ACboy needs your help again!(栈和队列)

    Description ACboy was kidnapped!! he miss his mother very much and is very scare now.You can't image ...

  3. PAT (Advanced Level) 1108. Finding Average (20)

    简单模拟. #include<cstdio> #include<cstring> #include<cmath> #include<vector> #i ...

  4. loadrunner常用计数器分析

    内存是第一个监视对象,确定系统瓶颈的第一个步骤就是排除内存问题.内存短缺的问题可能会引起各种各样的问题. Object(对象) Counters Description(描述) 参考值 Memory ...

  5. Chapter 1 First Sight——13

    It was just drizzling still, not enough to soak me through immediately as I reached for the house ke ...

  6. 迄今最安全的MySQL?细数5.7那些惊艳与鸡肋的新特性(上)【转载】

    转自: DBAplus社群 http://www.toutiao.com/m5762164771/ 迄今最安全的MySQL?细数5.7那些惊艳与鸡肋的新特性(上) - 今日头条(TouTiao.com ...

  7. VLAN及Trunk实验

    VLAN及Trunk实验 一 基本VLAN试验 实验拓扑: 实验目的:掌握VLAN基本配置 掌握静态接口VLAN划分 实验需求:在交换机switch上面创建两个VLAN,vlan2和vlan3,vla ...

  8. 【嵌入式linux】(第三步):安装串口终端 (ubuntu安装minicom串口终端)

    1.前言 我使用的是USB转串口,芯片是PL2303,貌似ubuntu自带了PL2303的USB驱动,可以直接使用,其它的USB转串口的没试过. 2.minicom安装 在终端中输入 : sudo a ...

  9. textbox文本键盘全选

    private void textBox1_KeyDown(object sender, KeyEventArgs e)        {            if (e.Modifiers == ...

  10. 把一个 int 数字 n 格式化成16进制的字符串(前面补零成0位)

    例如,输入n=10,要求输出 0x0000000A; C++:  sprintf( buffer, "0x%08X", n); C#:    string s = string.F ...