文件编译过程
源文件首先经过compile,生成.o文件(linux下),object file, 再把大量object file经过link合成执行文件。
compile,告诉编译器位置,只要语法正确就会生成目标文件,一个源文件对应一个目标文件(.o)
编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
link, 链接函数和全局变量
链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File.
makefile rules
/换行符,命令太长时候用
target ... : prerequisites ...
command(必须tab开头)
...
...
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的Shell命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。
说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
这就是Makefile的规则。也就是Makefile中最核心的内容。
举个例子:
make看到.o文件就会自动把.c文件加在依赖关系中,所以不用写了, cc -c whatever.c 也会被推导出来,只写依赖的头文件(.h)就可以了
所以新的makefile
“.PHONY”表示,clean是个伪目标文件。写clean是个好习惯
引用makefile
在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:include <filename>
在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和<filename>可以用一个或多个空格隔开。
举个例子:
你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,
那么,下面的语句:include foo.make *.mk $(bar)等价于:include foo.make a.mk b.mk c.mk e.mk f.mk
指定makefile:
GNU make 找寻默认的 Makefile 的规则是在当前目录下依次找三个文件—— “GNUmakefile”、“makefile”和“Makefile”。其按顺序找这三个文件,一旦找到,就开始读取这个 文件并执行
而使用 make 的 -f 或是 --file 参数(--makefile 参数也行),可以给 make 命令指定一个特殊名字的 Makefile,例如,我们有个 makefile 的名字是“hchen.mk”, 那么,我们可以这样来让 make 来执行这个文件:make -f hchen.mk
指定终极目标
.phone:all
all:prog1 prog2 prog3 prog4
也算是伪目标(clean什么的也是伪目标,可以make clean 显式执行)
这个makefile中有四个需要编译的程序——“prog1”,“prog2”, “prog3”和“prog4”,我们可以使用“make all”命令来编译所有的目标(如果把all置成第一个目标,那么只需执行“make”),我们也可以使用“make prog2”来单独编译目标“prog2”
GNU的make工作时的执行步骤
1、读入所有的Makefile。
2、读入被include的其它Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。
1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。
VPATH
大量源文件分类,存放在不同的目录中。当make需要去找寻文件的依赖关系时,可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。
Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:../headers
上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)
另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,有三种使用方法:
- vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>。
例如:vpath %.h ../headers该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话) - vpath <pattern>
清除符合模式<pattern>的文件的搜索目录。 - vpath
清除所有已被设置好了的文件搜索目录。
vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录。如果连续的vpath语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make会按照vpath语句的先后顺序来执行搜索
多目标
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
上述规则等价于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
其中,-$(subst output,,$@)中的“$”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“$@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。
make的参数(老王笔记):
-b, -m 这两个参数的作用是忽略和其它版本 make 的兼容性。 -B, --always-make 认为所有的目标都需要更新(重编译)。
-C <dir>, --directory=<dir> 指定读取 makefile 的目录。如果有多个“-C”参数,make 的解释是 后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make -C ~hchen/test -C prog”等价于“make -C ~hchen/test/prog”。
-debug[=<options>] 输出 make 的调试信息。如果没有参数,那就是 输出最简单的调试信息。下面是 <options> 的取值:
• a: 也就是 all,输出所有的调试信息。(会非常的多)
• b: 也就是 basic,只输出简单的调试信息。即输出不需要重编译的目标。
• v: 也就是 verbose,在 b 选项的级别之上。输出的信息包括哪个 makefile 被解析,不需 要被重编译的依赖文件(或是依赖目标)等
• i: 也就是 implicit,输出所以的隐含规则。
• j: 也就是 jobs,输出执行规则中命令的详细信息,如命令的 PID、返回码等。
• m: 也就是 makefile,输出 make 读取 makefile,更新 makefile,执行 makefile 的信 息。
-d 相当于“--debug=a”。
-e, --environment-overrides 指明环境变量的值覆盖 makefile 中定义的变量的值。
-f=<file>, --file=<file>, --makefile=<file> 指定需要执行的 makefile。
-h, --help 显示帮助信息。
-i , --ignore-errors 在执行时忽略所有的错误。
-I <dir>, --include-dir=<dir> 指定一个被包含 makefile 的搜索目标。可以使用多个“-I”参数 来指定多个目录。
-j [<jobsnum>], --jobs[=<jobsnum>] 指同时运行命令的个数。如果没有这个参数,make 运行命令 时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注 意这个参数在 MS-DOS 中是无用的)
-k, --keep-going 出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执 行了。
-l <load>, --load-average[=<load>], -max-load[=<load>] 指定 make 运行命令的负载。
-n, --just-print, --dry-run, --recon 仅输出执行过程中的命令序列,但并不执行。
-o <file>, --old-file=<file>, --assume-old=<file> 不重新生成的指定的 <file>,即使这个目 标的依赖文件新于它。
-p, --print-data-base 输出 makefile 中的所有数据,包括所有的规则和变量。这个参数会让一个 简单的 makefile 都会输出一堆信息。如果你只是想输出信息而不想执行 makefile,你可以使用 “make -qp”命令。如果你想查看执行 makefile 前的预设变量和规则,你可以使用“make –p – f /dev/null”。这个参数输出的信息会包含着你的 makefile 文件的文件名和行号,所以,用这
个参数来调试你的 makefile 会是很有用的,特别是当你的环境变量很复杂的时候。-q, --question 不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是 0 则说明要
更新,如果是 2 则说明有错误发生。-r, --no-builtin-rules 禁止 make 使用任何隐含规则。-R, --no-builtin-variabes 禁止 make 使用任何作用于变量上的隐含规则。 -s, --silent, --quiet 在命令运行时不输出命令的输出。
-S, --no-keep-going, --stop 取消“-k”选项的作用。因为有些时候,make 的选项是从环境变量 “MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项
失效。
-t, --touch 相当于 UNIX 的 touch 命令,只是把目标的修改日期变成最新的,也就是阻止生成目标 的命令运行。
-v, --version 输出 make 程序的版本、版权等关于 make 的信息。
-w, --print-directory 输出运行 makefile 之前和之后的信息。这个参数对于跟踪嵌套式调用 make
时很有用。
--no-print-directory 禁止“-w”选项。
-W <file>, --what-if=<file>, --new-file=<file>, --assume-file=<file> 假定目标 <file>; 需要更新,如果和“-n”选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有“-n” 那么就像运行 UNIX 的“touch”命令一样,使得 <file>; 的修改时间为当前时间。
--warn-undefined-variables 只要 make 发现有未定义的变量,那么就输出警告信息。