makefile学习笔记

makefile学习笔记

在使用make命令时, 如果工程只有部分c文件修改,那么我们只编译被修改的c文件,并链接目标程序. 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的 c 文件,并链接目标程序。

make的工作原理, 即更新当前目录下(M)makefile中的第一个目标文件(该更新的则找相应规则). 其他的非目标文件用作动作指令.

VPATH变量用来质定’依赖文件’的搜索路径

符号 ‘-‘ 一般用来不管是否出错继续执行的操作,可用在 include和编译命令cc等前. 全局的办法是make 加上 -i ,那么,Makefile 中所有命令都会忽略错误. 而 -k 参数意味着终止出错规则的执行,但是继续执行其他规则.

静态模式 ‘%’符号的运用常见有

1
$(object): %.o :%.c

prereq-parrterns 是目标的依赖模式,它对 target-parrtern 形成的模式再进行一次依赖目标的定义。结合懒人写法 filter过滤器:

1
$(filter %.o,$(files)): %.o: %.c

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

awk,sed命令, 还不是很理解的生成.d文件的一个模式规则:

1
2
3
4
5
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$

希望两条命令顺序或者说后者在前者的基础上执行,则应该写一行上并用 ‘;’ 分割.

传递变量到下级makefile用export, 但是SHELL和MAKEFLAGS是系统级的环境变量.自动传递.

在变量中使用函数会使make 运行时非常慢,会使用得两个 make 的函数“wildcard”和“shell”发生不可预知的错误。’:=’ 使左侧变量只能使用右侧’ $()’ 内已经定义的变量和其他字符. 定义变量时注意’#’ 会表示变量定义终止的特性. ‘?=’ 用于’检验型定义’

makefile中定义的系统环境变量和命令行带入的系统环境变量或覆盖系统原有的环境变量值. 但’-e’参数可以使原有系统环境变量覆盖Makefile中定义的变量. 可以为某个目标设置局部变量,它的作用范围就在这条规则以及连带规则中. 模式变量用法类似目标变量

常用的字符串处理函数有

1
2
3
4
5
6
7
8
9
10
11
$(subst <from>,<to>,<text>)
$(patsubst <pattern>,<replacement>,<text>)
$(strip <string>)
$(findstring <find>,<in>)
$(filter <pattern...>,<text>)
$(filter-out <pattern...>,<text>)
$(sort <list>)
$(word <n>,<text>)
$(wordlist <ss>,<e>,<text>)
$(words <text>)
$(firstword <text>)

文件名操作函数

1
2
3
4
5
6
7
$(dir <names...>)
$(notdir <names...>)
$(suffix <names...>)
$(basename <names...>)
$(addsuffix <suffix>,<names...>)
$(addprefix <prefix>,<names...>)
$(join <list1>,<list2>)

其他的常见函数

循环函数

1
$(foreach <var>,<list>,<text>)

一句话判断

1
$(if <condition>,<then-part>,<else-part>)

创建新函数

1
$(call <expression>,<parm1>,<parm2>,...,<parmn>)

查询性质

1
$(origin <variable>)

shell函数

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

控制make执行流, error报错可让make退出

1
2
$(error <text ...>)
$(warning <text ...>)

MAKECMDGOALS环境变量存放指定的终极目标的列表.

CFLAGS可以控制编译时的编译器参数

贴些常用的隐含规则

  1. 编译 C++ 程序的隐含规则。

    < n >.o 的目标的依赖目标会自动推导为 < n >.cc 或是 < n >.C ,并且其生成命令是

    1
    $(CXX) –c $(CPPFLAGS) $(CFLAGS) 。(建议使用 .cc 作为 C++ 源文件的后缀,而不是 .C )
  2. 编译 C 程序的隐含规则。

    < n >.o 的目标的依赖目标会自动推导为 < n >.c ,并且其生成命令

    1
    $(CC) –c $(CPPFLAGS) $(CFLAGS)
  1. 汇编和汇编预处理的隐含规则。
    .o 的目标的依赖目标会自动推导为 .s ,默认使用编译品 as ,并且其生成命令是:

    1
    $ (AS) $(ASFLAGS) 。

    .s 的目标的依赖目标会自动推导为 .S ,默认使用 C 预编译器 cpp ,并且其生成命令是

    1
    $(AS) $(ASFLAGS)
  1. 链接 Object 文件的隐含规则。
    目标依赖于 .o ,通过运行 C 的编译器来运行链接程序生成(一般是 ld ),其生成命令
    是:

    1
    $(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)
  1. 这个规则对于只有一个源文件的工程有效,同时也对多个 Object 文件(由不同的源文件生成)的也有效。例如如下规则:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    x : y.o z.o
    并且 x.c 、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
关于命令的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
• AR : 函数库打包程序。默认命令是 ar
• AS : 汇编语言编译程序。默认命令是 as
• CC : C 语言编译程序。默认命令是 cc
• CXX : C++ 语言编译程序。默认命令是 g++
• CO : 从 RCS 文件中扩展文件程序。默认命令是 co
• CPP : C 程序的预处理器(输出是标准输出设备)。默认命令是 $(CC) –E
• FC : Fortran 和 Ratfor 的编译器和预处理程序。默认命令是 f77
• GET : 从 SCCS 文件中扩展文件的程序。默认命令是 get
• LEX : Lex 方法分析器程序(针对于 C 或 Ratfor)。默认命令是 lex
• PC : Pascal 语言编译程序。默认命令是 pc
• YACC : Yacc 文法分析器(针对于 C 程序)。默认命令是 yacc
• YACCR : Yacc 文法分析器(针对于 Ratfor 程序)。默认命令是 yacc –r
• MAKEINFO : 转换 Texinfo 源文件(.texi)到 Info 文件程序。默认命令是 makeinfo
• TEX : 从 TeX 源文件创建 TeX DVI 文件的程序。默认命令是 tex
• TEXI2DVI : 从 Texinfo 源文件创建军 TeX DVI 文件的程序。默认命令是 texi2dvi
• WEAVE : 转换 Web 到 TeX 的程序。默认命令是 weave
• CWEAVE : 转换 C Web 到 TeX 的程序。默认命令是 cweave
• TANGLE : 转换 Web 到 Pascal 语言的程序。默认命令是 tangle
• CTANGLE : 转换 C Web 到 C。默认命令是 ctangle
• RM : 删除文件命令。默认命令是 rm –f

关于命令参数的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空。
• ARFLAGS : 函数库打包程序 AR 命令的参数。默认值是 rv
• ASFLAGS : 汇编语言编译器参数。(当明显地调用 .s 或 .S 文件时)
• CFLAGS : C 语言编译器参数。
• CXXFLAGS : C++ 语言编译器参数。
• COFLAGS : RCS 命令参数。
• CPPFLAGS : C 预处理器参数。(C 和 Fortran 编译器也会用到)。
• FFLAGS : Fortran 语言编译器参数。
• GFLAGS : SCCS “get”程序参数。
• LDFLAGS : 链接器参数。(如:ld )
• LFLAGS : Lex 文法分析器参数。
• PFLAGS : Pascal 语言编译器参数。
• RFLAGS : Ratfor 程序的 Fortran 编译器参数。
• YFLAGS : Yacc 文法分析器参数。

模式规则:

1
2
3
4
%.o : %.c ; <command ......>;
例子:
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

指出了怎么从所有的 .c 文件生成相应的 .o 文件的规则。

自动化变量

$@ : 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,$@ 就是匹配于目标中模式定义的集合。

• $% : 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是 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 被载入内存时,会被转换成模式规则