《跟我一起写Makefile》阅读笔记

第一章    概述

1.         首先区别make 与 Makefile

2.         make的整个过程包括编译和链接
输入源文件,经过编译,生成中间文件,即.o文件
输入中间文件,即.o文件,经过链接,生成exe文件或静态、动态库。

3.         Makefile文件是被解释执行的,可以调用操作系统的一些函数。

第二章    Makefile介绍

1.         最基本规则

target ... : prerequisites ...

command
其中target可以是.o(编译过程输入),exe(连接过程输出),标签即伪目标
prerequisits:所需文件
command:相关操作

2.         target为伪目标是非常常用的技术

clean :

rm edit main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o
clean不是一个文件,它只不过是一个动作名字
其冒号后什么也没有,那么,make就不会自动去找文件的依赖性

3.         make的默认工作流程,即只输入make的工作流程(区别输入make clean等)

1)        当前目录查找Makefile或makefile

2)        查找Makefile文件的第一个目标文件,即确定了该Makefile的唯一任务,只要完成该任务即可退出,其它都是浮云。

3)        反复生成目标文件、查找依赖文件、生成依赖文件这个类似栈的过程,直到第一个目标出现,即生成。

4)        最后,很显然,2中的clean并非第一个target,所以需要显示执行,如make clean

4.         变量的声明与使用
声明:Variant = value
使用:$( Variant)

5.         make的自动规则
例如:main.o:main.c main.h
make在遇到.o,会自动依赖同名的.c文件,所以上句可简写成
main.o:main.h

6.         另类风格
make支持a.o  b.o  c.o:abc.h,但不建议

7.         一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令

第三章    Makefie总述

1.         make默认寻找当前目录的makefile或Makefile,当然你也可以自己指定
make –f(--f) Make.Other

2.         引用其它的Makefile
语法:include <filename>  其中filename支持包含绝对路径或相对路径

3.         include 的搜索过程

1)        制定了相对路径或绝对路径,直接取

2)        否则

i.           当前目录

ii.         make 选项 –I或—include-dir所指的参数

4.         MAKEFILES参数,作用类似include,只是它包含进的文件中的target不起作用。

第四章    书写规则

1.         target : prerequisites
command

1)        多个target以空格隔开

2)        如果prerequisites与target不在同一行出现,需要以TAB为行首

3)        Command支持shell中的命令,毕竟是由/bin/sh来执行命令

2.         在文件名中使用通配符:*  ?  [0-9]
波浪号~使用:“~/test”,这就表示当前用户的$HOME目录。
            “~zhangsan/test”则表示用户zhangsan的宿主目录下的test目录

3.         文件搜索功能
就像C语言中的#include使用的搜索目录功能

1)        Makefile的特殊变量VPATH
VPATH = src:../header
冒号用于多个目录的分隔符。

2)        关键字vpath的使用

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

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

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

iv.       举例:vpath %.c foo:bar  冒号用于多个目录的分隔符
      vpath % blish

4.         伪目标

1)        clean:
rm –rf *.o
clean就是伪目标,但clean不能当前Makefile所在目录中的其它文件重名,为了避免这种顾忌,可以使用.PHONY声明伪目标
.PHONY clean
clean:
rm –rf *.o

2)        通常一个Makefile只能生成一个目标

i.           伪目标应用案例一:生成多个目标
all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o

prog2 : prog2.o
cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o

ii.    类似实现子函数的伪目标应用案例
.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
rm program

cleanobj :
rm *.o

cleandiff :
rm *.diff

5.       多目标
一个Makefile只能有一个目标,即第一个出现的目标,与即将要说的多目标有冲突吗?
可以理解为没有冲突,因为第一个出现的目标,可以于其它目标并列存在,即target中用空格隔开目标。
自动化变量“$@”通常是为多目标服务的。

6.       静态模式
它是相对接下来要讲到的自动生成依赖性而言的,它侧重于手动编写,多用于多目标。
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
....
targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

7.       <targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
....
targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。
objects = foo.o bar.o foo.c bar.c
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
%.o即过滤objects中后缀为.o的目标
%.c的%指的,上一级模式匹配(即%.o)中%符号所匹配到的字符串,即foo与bar

8.       自动生成依赖性

1)       如果我们的main.c中有一句“#include "defs.h"”,那么我们的依赖关系应该是:
main.o : main.c defs.h
再如果在main.c中又需要引入”consts.h”,那么我们又需要修改Makefile
main.o : main.c defs.h consts.h

2)       大部分C/C++编译器,支持-M选项,自动搜索.c文件中依赖的头文件
GNU的C/C++编译器,你得用“-MM”参数,因为“-M”参数会把一些标准库的头文件也包含进来

3)       问题来了,编译器是编译器,Makefile是Makefile,Makefile如何利用编译器生成的依赖关系呢?
编译器为每一个源文件生成相应的依赖关系临时文件,该文件属于Makefile文件,然后主Makefile包含这个Makefile即可获得依赖关系。

第五章    书写命令

1.       命令显示

1)       命令的开头必须以[Tab]键开头

2)       make通常会在屏幕上显示当前执行的命令行。
但在命令前使用“@”字符,那么这个命令将不被显示出来。
@echo 正在编译XXX模块......

3)       make参数“-n”或“--just-print”,只显示命令,但不执行命令,常用于调试Makefile,查看命令执行过程与顺序。

4)       make参数“-s”或“--slient”则是全面禁止命令的显示

2.       命令执行

1)       如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。

2)       exec:
cd /home/hchen
pwd
假设当前目录是根目录:/
结束输出根目录:/

3)       exec:
cd /home/ zhangsan; pwd
假设当前目录是根目录:/
结束输出根目录:/home/zhangsan

3.       命令出错

1)       命令运行完后,make会检测每个命令的返回码
如果成功,继续执行
如果失败,可能终止执行

2)       可在Makefile的命令前加一个减号“-”(在Tab键之后),忽略返回码的判断

3)       make加上“-i”或是“--ignore-errors”参数
如果一个规则是以“.IGNORE”作为目标的
make的参数的是“-k”或是“--keep-going”
以上具体请参阅Page 27

4.       嵌套执行make

1)       在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile

2)       例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:
subsystem:
cd subdir && $(MAKE)
其等价于:
subsystem:
$(MAKE) -C subdir
这两个例子的意思都是先进入“subdir”目录,然后执行make命令。
这里为什么使用$(MAKE),而不是make命令呢?因为make通常需要一些参数,所以定义成一个变量比较利于维护。

3)       嵌套make中最关心的问题通常是参数的传递
与C语言类似,内层可以访问外层变量,如果变量名相同,则隐藏或者说替换。

4)       上层Makefile如何向下层Makefile传递参数呢?
export <variable ...>
export variable = value
export variable := value
export variable += value
取消参数传递使用:unexport <variable ...>

5)       需要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,其总是要传递到下层Makefile中,特别是MAKEFILES变量,其中包含了make的参数信息,如果我们执行“总控Makefile”时有make参数或是在上层Makefile中定义了这个变量,那么MAKEFILES变量将会是这些参数,并会传递到下层Makefile中,这是一个系统级的环境变量。

6)       如果你不想往下层传递参数,你可以:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=

7)       “嵌套执行”中比较有用的make参数,“-w”或是“--print-directory”
使用“make -w”来执行,那么当进入该目录时,我们会看到:
make: Entering directory `/home/hchen/gnu/make'.
而在完成下层make后离开目录时,我们会看到:
make: Leaving directory `/home/hchen/gnu/make
当你使用“-C”参数来指定make下层Makefile时,“-w”会被自动打开的

5.       定义命令包

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

2)       “run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名

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

第六章    使用变量
在Makefile中的定义的变量,就像是C/C++语言中的宏一样, 与C/C++所不同的是,你可以在Makefile中改变其值。

1.       变量在声明时需要给予初值,使用时加“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来,给变量加上括号完全是为了更加安全地使用这个变量。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。

2.       变量中的变量

1)       使用“=”的变量
foo = $(bar)
bar = Hello
特点:变量不一定非要是已定义好的值
      在宏替换过程中,遇到变量依次展开,这样的话就支持递归展开。
注意:递归通常发生在取值的时候,即使用$符号时才会发生
      所以区别y = z与y = $(z),然后x = y。

2)       使用“:=”的变量
x := foo
y := $(x) bar
x := later
最终x值为later,y值为foo bar
y := $(x) bar
x := foo
最终x值为foo,y值为bar,因为x未定,即为空
特点:前面的变量不能使用后面的变量

3)       定义空格变量技术
nullstring :=
space := $(nullstring) # end of the line
nullstring是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。

4)       使用“?=”的变量
FOO ?= bar
如果定义了FOO,FOO的值就是“bar”,否则什么也不做

3.       变量的高级用法

1)       第一种是变量值的替换
例如:y := a.o b.o,变量y的值由a.o b.o变成了a.c b.c。
      注意:y的值通常是不变的,通常是将变化后的值放入(临时)变量中
foo := a.o b.o c.o
bar := $(foo:.o=.c)
意思是: 以“o”字串“结尾替换成以“c”字串结尾的
最终bar值为a.c b.c c.c
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
使用通配符的方式,可参考静态模式

2)       第二种高级用法是——“把变量的值再当成变量”
first_second = Hello
a = first
b = second
all = $($a_$b)
最终all的值就是“Hello”。

4.       追加变量值   --   “+=”操作符

1)       举例:objects = main.o foo.o bar.o utils.o
      objects += another.o

2)       如果变量之前没有定义过,那么,“+=”会自动变成“=”

3)       如果前面有变量定义,那么“+=”会继承于前次操作的赋值符  ***

5.         override指示符
如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。
如果你想在Makefile中设置这类参数的值,那么,你可以使用“override”指示符。其语法是:
override <variable> = <value>
override <variable> := <value>
当然,你还可以追加:
override <variable> += <more text>
还有多行变量
override define foo
bar
endef

6.         多行变量
define two-lines
echo foo
echo $(bar)
endef
其工作方式和“=”操作符一样
因为命令需要以[Tab]键开头,所以如果你用define定义的命令变量中没有以[Tab]键开头,那么make就不会把其认为是命令

7.         环境变量
make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是 如果Makefile中已定义了这个变量,或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(如果make指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量)

8.         目标变量
之前定义过通常的全局变量,它的作用域通常是整个文件。
目标变量,即它的作用域是生成该目标的整个过程中。
语法:<target ...> : <variable-assignment>
      <target ...> : overide <variable-assignment>
<variable-assignment>可以是各种赋值表达式,如“=”、“:=”、“+=”或是“?=”

9.         模式变量
模式变量时基于目标变量之上的功能。
即它为目标变量应用于多个目标成为了可能。
<pattern ...> : <variable-assignment>
<pattern ...> : override <variable-assignment>

第七章    使用条件判断

1.         语法:
#ifeq、#ifneq、#ifdef、#ifndef
#else
#endif

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

第八章    使用函数

1.         函数调用,很像变量的使用,也是以“$”来标识的,其语法如下
$(<function> <arguments>) 或是 ${<function> <arguments>}
<function>就是函数名,make支持的函数不多。<arguments>是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。

2.         字符串处理函数

1)        $(subst <from>,<to>,<text>)
名称:字符串替换函数——subst。
功能:把字串<text>中的<from>字符串替换成<to>。
返回:函数返回被替换过后的字符串。

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

3)        $(strip <string>)
名称:去空格函数——strip。
功能:去掉<string>字串中开头和结尾的空字符。 中间空格不处理。
返回:返回被去掉空格的字符串值。

4)        $(findstring <find>,<in>)
名称:查找字符串函数——findstring。
功能:在字串<in>中查找<find>字串。
返回:如果找到,那么返回<find>,否则返回空字符串。

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

6)        $(filter-out <pattern...>,<text>)
与5)功能相反

7)        $(sort <list>)
名称:排序函数——sort。
功能:给字符串<list>中的单词排序(升序)。
返回:返回排序后的字符串。

8)        $(word <n>,<text>)
名称:取单词函数——word。
功能:取字符串<text>中第<n>个单词。(从一开始)
返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。

9)        $(wordlist <s>,<e>,<text>)
名称:取单词串函数——wordlist。
功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
返回:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束的单词串。

10)     $(words <text>)
名称:单词个数统计函数——words。
功能:统计<text>中字符串中的单词个数。
返回:返回<text>中的单词数。

11)     $(firstword <text>)
名称:首单词函数——firstword。
功能:取字符串<text>中的第一个单词。
返回:返回字符串<text>的第一个单词。

3.         文件名操作函数

1)        $(dir <names...>)
名称:取目录函数——dir。
功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列<names>的目录部分

2)        $(notdir <names...>)
名称:取文件函数——notdir。
功能:从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“ /”)之后的部分。
返回:返回文件名序列<names>的非目录部分。

3)        $(suffix <names...>)
名称:取后缀函数——suffix。
功能:从文件名序列<names>中取出各个文件名的后缀。
返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串

4)        $(basename <names...>)
名称:取前缀函数——basename。
功能:从文件名序列<names>中取出各个文件名的前缀部分。
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。

5)        $(addsuffix <suffix>,<names...>)
名称:加后缀函数——addsuffix。
功能:把后缀<suffix>加到<names>中的每个单词后面。
返回:返回加过后缀的文件名序列。

6)        $(addprefix <prefix>,<names...>)
名称:加前缀函数——addprefix。
功能:把前缀<prefix>加到<names>中的每个单词后面。
返回:返回加过前缀的文件名序列。

7)        $(join <list1>,<list2>)
名称:连接函数——join。
功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list1>中。
返回:返回连接过后的字符串。

4.         foreach
$(foreach <var>,<list>,<text>)
把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。
names := a b c d
files := $(foreach n,$(names),$(n).o)
$(files)的值是“a.o b.o c.o d.o”

5.         if
$(if <condition>,<then-part>)
$(if <condition>,<then-part>,<else-part>)
<condition>参数是if的表达式,如果其返回的为非空字符串,才返回真

6.         call
$(call <expression>,<parm1>,<parm2>,<parm3>...)
当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。

7.         origin
$(origin <variable>)

1)        返回undefined

2)        返回default
可能指环境变量、CC、MAKEFLAGS等

3)        返回command line
执行make命令时输入的变量

4)        返回override
重定义的变量

5)        返回automatic
命令行运行中的自动化变量

8.         shell函数
它和反引号“`”是相同的功能
contents := $(shell cat foo)
files := $(shell echo *.c)

9.         控制make的函数

1)        error
$(error <text ...>) 产生一个致命的错误,<text ...>是错误信息。
将中止make命令

2)        $(warning <text ...>)同error函数,只是它并不会让make退出

第九章    make的运行

1.         make的退出码
返回0表示成功,返回1表示失败,还有返回2的与-q选项有关

2.         指定Makefile
执行make时,make会依次搜索“GNUmakefile”、“makefile”和“Makefile”
如何指定makefile的文件呢?
make –f hchen.mk

3.         指定目标

1)        例如常见的make clean

2)        任何在makefile中的目标都可以被指定成终极目标,但是除了以“-”打头,或是包含了“=”的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。

3)        make的环境变量叫“MAKECMDGOALS”,这个变量中会存放你所指定的终极目标的列表,如果在命令行上,你没有指定目标,那么,这个变量是空值。

4.         常用目标名

1)        all
编译所有目标

2)        clean
删除make创建的所有文件

3)        install
编译所有目标后,将可执行文件拷贝到xxx/bin下

4)        print
列出改变过的源文件

5)        tar
将源文件打包

6)        dist
将tar打包后的文件压缩成gz文件

7)        TAGS
更新所有目标,以备完整编译。

8)        check与test
一般用来测试makefile的流程

5.         检查、调试Makefile

1)        ”n” 、“--just-print” 、“--dry-run” 、“--recon”
不执行参数,这些参数只是打印命令,不管目标是否更新

2)        “-t” 、“--touch”
这个参数的意思就是把目标文件的时间更新,但不更改目标文件。

3)        “-q” 、“--question”
找目标,如果目标存在,什么也不输出,也不会执行编译,如果目标不存在,则打印一条出错信息。

4)        “-W <file>” 、“--what-if=<file>” 、“--assume-new=<file>” 、“--new-file=<file>”
指定一个文件(源文件或依赖文件),执行与该文件相关的命令,通常与“-n”参数一同使用。

6.         make参数

1)        “-b” 、“-m”
这两个参数的作用是忽略和其它版本make的兼容性。

2)        “-B” 、“--always-make”
更新目标(重编译)

3)        “-C <dir>” 、“--directory=<dir>”
指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录

4)        “—debug[=<options>]”
输出make的调试信息。

i.           a —— all,输出所有的调试信息。(会非常的多)

ii.         b —— basic,简单的调试信息。即输出不需要重编译的目标。

iii.        v —— verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。

iv.       i —— implicit,输出所以的隐含规则。

v.         j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。

vi.       m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。

5)        “-d”
相当于“--debug=a”。

6)        “-e” /“--environment-overrides”
指明环境变量的值覆盖makefile中定义的变量的值。

7)        “-f=<file>” /“--file=<file>” /“--makefile=<file>”
指定需要执行的makefile。

8)        “-h” 、“--help”
显示帮助信息。

9)        “-i” 、“--ignore-errors”
在执行时忽略所有的错误。

10)     “-I <dir>” / “--include-dir=<dir>”
指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。

11)     “-j [<jobsnum>]” 、“--jobs[=<jobsnum>]”
指同时并行运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。

12)     “-k” 、“--keep-going”
出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了

13)     “-l <load>” 、“--load-average[=<load]” 、“—max-load[=<load>]”
指定make运行命令的负载。

14)     “-n” 、“--just-print” 、“--dry-run” 、“--recon”
仅输出执行过程中的命令序列,但并不执行

15)     “-o <file>” 、“--old-file=<file>” 、“--assume-old=<file>”
不重新生成指定的<file>,即使这个目标的依赖文件新于它。

16)     “-p” 、“--print-data-base”
输出makefile中的所有数据,包括所有的规则和变量
例:想输出信息而不想执行makefile,你可以使用“make -qp”

17)     “-q” 、“--question”
仅检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生。

18)     “-r” 、“--no-builtin-rules”
禁止make使用任何隐含规则。

19)     “-R” 、“--no-builtin-variabes”
禁止make使用任何作用于变量上的隐含规则。

20)     “-s” 、“--silent” 、“--quiet”
在命令运行时不输出命令的输出。

21)     “-S” 、“--no-keep-going” 、“--stop”
取消“-k”选项的作用。

22)     “-t” 、“--touch”
只是把目标的修改日期变成最新的,阻止生成目标的命令运行。

23)     “-v” 、“--version”
输出make程序的版本、版权等关于make的信息。

24)     “-w” 、“--print-directory”
踪嵌套式调用make,输出进入、退出目录信息。

25)     “--no-print-directory”
禁止“-w”选项。

26)     “-W <file>” 、“--what-if=<file>” 、“--new-file=<file>” 、“--assume-file=<file>”
指定一个文件(源文件或依赖文件),执行与该文件相关的命令,通常与“-n”参数一同使用。

27)     --warn-undefined-variables”
只要make发现有未定义的变量,那么就输出警告信息

第十章    隐含规则

1.         隐藏规则是什么呢?
Makefile由目标、依赖关系和命令组成,那么隐藏规则主要指的是依赖关系的自动生成,命令的自动生成。
--warn-undefined-variables”
只要make发现有未定义的变量,那么就输出警告信息

2.         即使是我们指定了“-r”参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了“后缀规则”来定义的

3.         隐含规则一览

1)        编译C的隐含规则

i.           自动生成依赖关系
“<n>.o”的目标的依赖目标会自动推导为“<n>.c”

ii.         自动生成命令      
“$(CC) –c $(CPPFLAGS) $(CFLAGS)”

2)        编译C++的隐含规则

i.           自动生成依赖关系
“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”,建议.cc

ii.         自动生成命令
“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”

3)        汇编与汇编预处理的隐含规则

i.           自动生成依赖关系
“<n>.o” 的目标的依赖目标会自动推导为“<n>.s”, 默认使用编译品“as”
“<n>.s” 的目标的依赖目标会自动推导为“<n>.S”,默认使用”cpp”

ii.         自动生成命令
“$(AS) $(ASFLAGS)”,

4)        连接object的隐含规则

i.           自动生成依赖关系
“<n>”目标依赖于“<n>.o”

ii.         “$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”
默认使用C编译的连接程序

iii.        补充
x依赖于x.o y.o
x : y.o  可以省略x.o,但不能省略y.o

5)        从C程序创建Lint库的隐含规则

i.           自动生成的依赖关系
“<n>.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”

ii.         自动生成的命令
“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”

6)        除C、C++外的语言,请参考书籍。

4.         隐含规则使用的变量

1)        make的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的作用。

2)        首先变量分两种:命令相关变量和参数相关变量

3)        命令相关变量

i.           C语言编译程序,默认命令是“cc

ii.         C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”

iii.        C++语言编译程序,默认命令是“g++”

iv.       汇编语言编译程序,默认命令是“as”

v.         函数库打包程序,默认命令是“ar”

vi.       删除文件命令。默认命令是“rm –f”。

4)        参数相关的变量

i.           C语言编译器参CFLAGS

ii.         C预处理器参数CPPFLAGS。

iii.        C++语言编译器参数CXXFLAGS

iv.       汇编语言编译器参数ASFLAGS (当明显地调用“.s”或“.S”文件时)

v.         函数库打包程序AR命令的参数。默认值是“rv”,ARFLAGS

vi.       链接器参数LDFLAGS (如:“ld”)

5.         隐含规则链

1)        说明一个文件或是目标是中介目标,你可以使用伪目标“.INTERMEDIATE”来强制声明。

2)        阻止make自动删除中间目标,可以使用伪目标“.SECONDARY”来强制声明

6.         定义模式规则

1)        在Makefile中一提到模式,就少不了%。
"%"的展开是有讲究的,变量和函数的展开发生在make载入Makefile时,而模式规则中的"%"则发生在运行时。

2)        模式可应用于目标、依赖文件。
那么命令呢?命令通常需要利用自动化变量,后面提到。

3)        如果"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值。

7.         自动化变量

1)        自动化变量的出现就是为Makefile的模式服务的

2)        $@:  目标集

3)        $%:  目标的成员集,目标为函数库时有效
     目标是"foo.a (bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。

4)        $<:  依赖集的第一个成员。如果依赖集是模式定义的(即%),则依次代表每
     一个成员

5)        $?:  比目标新的依赖集,以空格分隔。注:是所有,不是依次。

6)        $^:  去重复成员的依赖集,以空格分隔。注:是所有,不是依次。

7)        $+:  不去重复成员的依赖集,以空格分隔。注:是所有,不是依次。

8)        $*:  表示模式中"%"及其之前的部分的目标集。
     目标是"dir/a.foo.b",目标的模式是"a.%.b","$*"的值就是
     "dir/a.foo",构造有关联的文件名是比较有用。

9)        以上搭配上"D"或"F"字样使用,可实现是否显示目录的功能。
例如:$(@D) 、$(@F),即dir/foo.o,是否显示dir/的功能

8.         Makefile的一个非常强大的功能就是模式,那模式具体如何匹配呢?

1)        我们把"%"所匹配的内容叫做"茎"

2)        通常先去掉目录部分,最后再加回去。

9.         重载内建隐含规则
例如:main.o 自动依赖main.c时使用的C语言隐含规则
可通过如下重载
%.o : %.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -D$(date)

10.     过时的后缀规则
将会被模式规则取代,这不做介绍

第十一章              使用make更新函数库

1.         在Unix下,一般是由命令"ar"来完成打包工作。

2.         目标:archive(member)
例如:abclib(a.o b.o c.o): a.o b.o c.o
      ar cr abclib a.o b.o c.o

3.         "$%",这是专属函数库文件的自动化变量

4.         在进行函数库打包文件生成时,请小心使用make的并行机制("-j"参数)。该参数是并行参数。
目前在更新函数库时不建议使用-j参数,可能有问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值