2.26-GNU Make Tutorial

  • 以下为《跟我一起学写Makefile》的笔记
    • 学习目标:能够写小型工程的Makefile,能够看懂CMake自动生成的Makefile
  • Make 工具是一个自动化编译工具,写一个脚本文件便可决定整个工程的编译规则,比如哪一个文件先编译,哪些文件后编译,编译好的文件放在什么目录中,等等。
  • 书上的版本为3.80,本机版本为3.81,可见make工具这么多年来都没有更新
  • 关于编译知识,还得系统学习龙书《编译原理》
  • 示例工程里有3个头文件和8个C文件,由于没有下载,只好推测之
    • command.c    display.c    insert.c    search.c    files.c    utils.c  main.c  kbd.c
    • defs.h  command.h  buffer.h
  • 第一行为依赖关系,第二行为命令,第二行需要以Tab键开头
  • 我们还可以定义“clean”等与编译无关的动作
  • GNU Make 可以自动推倒文件以及文件依赖关系后面的命令
  • “.PHONY”显式地表示此目标是一个伪文件,用以避免与文件重名
  • 命令前面添加“-”的作用是:如果此命令执行错误,则忽略此错误,继续执行其他命令
  • 不要设置${MAKEFILES}环境变量
  • 分号“;”用来分隔多个命令
  • 如果命令过长,使用反斜杠“\”分行
  • 伪目标的特点是,永远是最新的,后面如果有依赖文件则永远要更新
  • “$?” 表示:
  • "$@" 表示:一个数组存放目标集合,其中顺次存放着所有的目标
  • “$<”表示:所有的依赖目标集
  • gcc命令:gcc -MM main.c    自动生成文件依赖关系,则不需要在Makefile中写明依赖关系(-M 将把标准库的头文件也包含进来)
  • 如果你希望上一条命令的结果应用到下一条命令时,用分号分隔这两条命令
  • 传统的Makefile变量名是大写的,但是现在推荐大小写搭配的变量名,这样可以避免和系统的变量冲突,发生意外。
  • 一般来讲,使用变量名应该这样:${},加上括号完全是为了更加安全。
  • 变量名的本质是宏,在运行时会先扩展,本质就是“扩展”
  • “=”和“:=”的不同在于:前者可以引用后面的变量,后者只能引用前面的变量
  • 目标型变量:为某个目标设置局部变量,它的作用范围只在这条规则以及连带规则中,所以其值只在作用范围内有效,而不会影响规则链之外的全局变量的值
  • 模式变量:我们可以把变量定义在符合一种模式的所有目标上,这样的变量同样是局部变量
  • 条件表达式:Make是在读取Makefile时就计算条件表达式的值,相当于C语言的预编译,所以在条件表达式中最好不要放置自动化变量(如“$@”),因为自动化变量是在运行时才有的,而且不允许把整个条件语句分成两部分放在不同的文件中。
  • 函数包括:字符串操作函数;文件名操作函数;foreach 函数;if, call, origin, shell函数,控制Make 的 error, warning函数
  • Make的运行:制定Makefile,制定目标,等
  • Make的参数:路径参数若有多个,则后面的路径以前面的路径作为相对路径,并以最后的目录作为被制定目录。
  • -j --jobs:同时运行命令的个数
  • Make隐含规则:如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则
  • 隐含规则是有顺序的,越靠前的规则越是被经常使用的,如果你显式制定了规则,但是这个规则前面还有“隐含规则”,那么隐含规则一样会生效。
  • 隐含规则有两种,“模式规则”和“后缀规则”
  • 后缀规则:只要有文件的后缀包含在“后缀列表”中,那么这些隐含规则就会生效。
  • 隐含规则的变量,分关于命令的变量和关于参数的变量。
  • CPP为C的预处理器
  • C++源程序后缀为*.cc
  • 函数库文件其实是对*.obj或*.o中间文件的打包文件,在UNIX中,静态库为*.a,动态库为*.so。
  • 重点——模式规则
    • 使用模式规则来定义一个隐含规则
    • “%”的意思是表示一个或多个任意字符。在依赖项目中同样可以使用“%”,只是依赖项目的取值,取决于其目标。这就是说,目标中的“%”值决定了依赖目标中的值
    • %.o : %.c ; <command ......>
    • 一旦依赖目标中的"%"模式被确定,那么,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 所不能识别的,那么"$*"就是空值。
    • 当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用,例如,假设有
    • 一个函数库文件叫"lib",其由其它几个 object 文件更新。那么把 object 文件打包的比较
    • 有效率的 Makefile 规则是:
    • lib : foo.o bar.o lose.o win.o
    • ar r lib $?
    • 在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文
    • 件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前
    • 目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。这是 GNU make 中老版本的特性,
    • 在新版本中,我们使用函数"dir"或"notdir"就可以做到了。"D"的含义就是 Directory,就
    • 是目录,"F"的含义就是 File,就是文件。
    • 下面是对于上面的七个变量分别加上"D"或是"F"的含义:
    • $(@D)
    • 表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"
    • 就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。
    • $(@F)
    • 表示"$@"的文件部分,如果"$@"值是"dir/foo.o", 那么"$(@F)"就是"foo.o","$(@F)"
    • 相当于函数"$(notdir $@)"。
    • "$(*D)"
    • "$(*F)"
    • 和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子, "$(*D)"
    • 返回"dir",而"$(*F)"返回"foo"
    • "$(%D)"
    • "$(%F)"
    • 分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形
    • 式的目标中的"member"中包含了不同的目录很有用。
    • "$(<D)"
    • "$(<F)"
    • 分别表示依赖文件的目录部分和文件部分。
    • "$(^D)"
    • "$(^F)"
    • 分别表示所有依赖文件的目录部分和文件部分。(无相同的)
    • "$(+D)"
    • "$(+F)"
    • 分别表示所有依赖文件的目录部分和文件部分。(可以有相同的)
    • "$(?D)"
    • "$(?F)"
    • 分别表示被更新的依赖文件的目录部分和文件部分。
    • 最后想提醒一下的是,对于"$<",为了避免产生不必要的麻烦,我们最好给$后面的那
    • 个特定字符都加上圆括号,比如,"$(<)"就要比"$<"要好一些。
    • 还得要注意的是,这些变量只使用在规则的命令中,而且一般都是"显式规则"和"静态
    • 模式规则"(参见前面"书写规则"一章)。其在隐含规则中并没有意义。

猜你喜欢

转载自www.cnblogs.com/lizhensheng/p/11117200.html