程序的编译与链接:
编译时,编译器需要的是语法的正确,函数与变量声明的正确。
链接时,主要是链接函数与全局变量。
Makefile介绍:
1、显示规则:说明如何生成一个或多个目标文件(要生成的文件,文件的依赖 文件、生成的命令);
2、隐晦规则:make具有自动推导功能,可以让我们比较粗糙的写makefile;
3、变量定义:需要定义一系列的变量,变量一般都是字符串;
4、文件指示:主要包括三个部分:
1)在一个Makefile中调用另一个Makefile;
2)根据需要指定Makefile的有效部分,相当于C语言中的预编译#if;
3)定义多行命令。
5、注释:Makefile中只有行注释,使用#进行注释。
Makefile编写规则:
Makefile有多干条规则构成:
规则:
target(目标):prerequisites(依赖)
command(命令) #一定要以一个Tab键作为开头
#依赖关系的实质是说明目标文件是由哪些文件生成的
例程1:
#creat first rule
MObj = main.o func1.o func2.o
hello:$(MObj)
gcc $(MObj) -o hello
main.o:main.c
gcc -c main.c
func1.o:func1.c
gcc -c func1.c
func2.o:func2.c
gcc -c func2.c
.PHONY:clean
clean:
rm $(MObj) hello
.PHONY:install
install :
cp hello /usr/local/hello
.PHONY:uninstall #.PHONY:uninstall 伪目标
uninstall:
rm /usr/local/hello
makefile中的参数:
$0 这个程式的执行名字 $n 这个程式的第n个参数值,n=1..9 $* 这个程式的所有参数,此选项参数可超过9个。 $# 这个程式的参数个数 $$ 这个程式的PID(脚本运行的当前进程ID号) $! 执行上一个背景指令的PID(后台运行的最后一个进程的进程ID号) $? 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误) $- 显示shell使用的当前选项,与set命令功能相同 $@ 跟$*类似,但是可以当作数组用
make是如何工作的:
在默认的情况下,输入make命令,会按照以下步骤执行:
1、make会在当前目录下查找文件名为“Makefile”或“makefile”的文件;
2、找到之后,找到文件中的第一个目标,并把它作为最终目标;
3、如果最终目标不存在或其依赖文件比目标文件新,则执行后面的命令生成目标;
4、如果目标依赖的.o文件存在,make在当前文件夹中查找.o文件的依赖性;
5、完成执行命令,输出结果;
PS:没有被第一目标直接或间接关联的伪目标,其后的命令不会自动执行。
make的工作方式:
GNU的make工作的执行步骤如下:
1、读入所有的 Makefile;
2、读入被include的其他Makefile;
3、初始化文件中的变量;
4、推导隐晦规则,并分析所有规则;
5、为所有的目标文件创建依赖关系链;
6、根据依赖关系,决定哪些目标要重新生成;
7、执行生成命令。
Makefile中的隐晦规则:
make可以自动推导文件以及文件依赖关系后面的命令。make只要看到.o文件,就会自动的把.c文件添加到依赖关系中。如果make找到一个whatever.o文件,那么whatever.c以及cc -c whatever.c等会被自动推倒出来。
清除目标文件规则:
rm命令前面加"-"的作用是:也许某些文件会出问题,不用管继续执行后面的命令。
Makefile中使用别的文件名书写Makefile:
如果需要指定特定的Makefile,可以使用make的"-f"和"--file"参数,如make -f Make.linux或make --file Make.AIX.
引用其他的Makefile:
在Makefile中使用include命令引用其他的Makefile。include
如果文件都没有指定绝对路径和相对路径的话,make会在当前目录下首先查找,如果在当前目录下没有找到,那么make还会在下面几个目录中去查找:
1、如果make执行时,有"-I"或"--include dir"参数,那么make就会在这个参数指定的目录下去寻找;
2、如果目录
文件搜寻:
Makefile文件中的特殊变量'VPATH',就是完成找寻文件的功能。
VPATH=src:../headers,目录有冒号分隔“:”。
另一个设置文件搜索路径的方法是使用make的"vpath"关键字。它的使用方式有三种:
1、vpath
为符合模式
2、vpath
清除符合模式
3、vpath
清除所有已被设置好的文件搜索目录
可以连续使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现相同的pattern,或是被重复了的pattern,那么make会按照vpath语句的先后顺序来执行搜索。
例如:
vpath %.c foo
vpath % blish
vpath %.c bar
表示以.c结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录
vpath %.c foo:bar
vpath % blish
表示以.c结尾的文件,先在"foo"目录,然后是"bar"目录,最后是"blish"目录
伪目标:
伪目标不能与文件名重名;
伪目标一般没依赖的文件,但我们可以给伪目标指定所依赖的文件;
伪目标也可以为默认目标。
多目标:
Makefile中的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖一个文件,并且其生成文件命令大概相似。使用自动化变量$@,表示目前规则中所有的目标的集合。
静态模式:
静态模式可以更加容易的定义多目标的规则,其语法为;
< targets> :
targets:定义了一系列的目标文件,可以有通配符。是目标的集合
target-pattern:指明了target的模式,也就是目标集的模式。
prereq-pattern: 目标的依赖模式,对target-pattern形成的模式在进行一次依赖目标的定义。
$<:表示所有的依赖目标集(即foo.c bar.c)
$@:表示目标集(foo.o bar.o)
自动生成依赖性:
大多数C/C++编译器都支持"-M"选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。当使用GNU的C/C++编译器时,需要“-MM”选项,,使用-M的话会把一些标准库的头文件包含进来。
GNU组织建议把编译器为每一个源文件的自动生成的依赖关系都放在一个文件中,为每一个"name.c"的文件都生成一个"name.d"的Makefile文件,[.d]文件中就存放对应[.c]的依赖关系。需要使用include命令来将此makefile文件引入到主makefile中。
将变量$(source)所有的.c替换成.d。需要注意次序,最先被载入的[.d文件中的目标会成为默认目标。
Makefile命令:
显示命令:
通常,make会把其要执行的命令行在命令执行前输出到屏幕,当我们使用"@"字符在命令行前,那么这个命令将不被make显示出来。
如果在make执行时,带入make参数 "-n"或"--just-print",那么将只显示命令,不会执行命令。
make参数"-s"或"--slient"则是全面禁止命令的意思。
命令执行:
当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条执行其后的命令。如果想让第一条命令执行的结果应用在下一条命令时,应该是用分号分隔这两条命令。(两条命令需要在同一行中)
命令出错:
每次命令运行完,make会检测每个命令的返回码。如果一个规则中的某些命令出错了,返回值为非0,make将终止该条规则,甚至有可能终止所有规则的运行。可以用过一下手段忽略命令的错误:
1、在Makefile的命令行前加一个减号“-”(TAB键之后),标记为不管命令是否成功执行都是成功的;
2、给make加上"-i"或者"--ignore-errors"参数,那么所有命令都会忽略错误;
3、如果make的参数为"-k"或"--keep-going",表示如果某个规则中的命令出错了,就终止该规则,但是其他规则继续执行。
嵌套执行make:
在每个目录下书写该目录的makefile,有利于将makefile变得更加简洁。例如有一个子目录为subdir,这个目录下有一个makefile,来指明目录下文件的编译规则,那总控的makefile可以这样写:
执行的时候是先进入subdir目录,然后再执行make命令。
总控makefile中的变量可以传递到下级的makefile中,但是不会覆盖下层makefile中的变量,除非使用-e参数。
要传递变量到下级makefile中,可以使用export进行申明,即export
例如: export variable=value
等价于 variable=value
export variable
有两个变量,一个是 SHELL,一个是 MAKEFLAGS,这两个变量不管是否 export,其总是要传递到下层 Makefile 中.
相应的,不想将变量传递给下级的makefile,可以使用unexport。
嵌套执行中比较有用的参数还有"-w"或" --print-directory",会在执行过程中输出一些信息,可以看到当前的工作目录。
定义命令包:
如果 Makefile 中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束,如:
Makefile变量:
变量基础:
变量声明时需要给予初值,而在使用时,需要在变量名前加$符号,还需要使用"()"或"{}"将变量括起来。
变量中的变量:
make中使用变量来定义变量的方法就是使用":="操作符。前面的变量不能使用后面的变量。
变量高级用法:
1、变量值的替换:
$(var:a=b) 意思是把变量var中的所有以"a"结尾字符串的“a”替换成"b"字符串。
2、以静态模式定义
3、变量的值再做变量
4、追加变量值
使用"+="操作符给标量追加值:
5、override操作符
如果有参数是通过命令行参数设置的,那么makefile对这个变量的赋值就会被忽略。如果想在makefile中设置这类参数的值,可以使用"override",指示符。
6、环境变量
make运行时的系统环境变量可以在make开始运行时被载入到makefile中。
7、模式变量
通过给定一种“模式”,可以把变量定义在符合这种模式的所有目标上。
Makefile使用函数:
1、函数调用的语法
在makefile中可以使用函数来处理变量,函数调用之后,函数的返回值可以当做变量来使用。
函数的调用使用"$"来标识。$(
2、字符串处理函数
$(subst
名称:字符串替换函数——subst。
功能:把字串
返回:函数返回被替换过后的字符串。
例如:$(subst ee,EE,feet on the street),
把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。
$(patsubst
名称:模式字符串替换函数——patsubst。
功能:查找
“%”,表示任意长度的字串。如果
返回:函数返回被替换过后的字符串。
示例:$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
$(strip
名称:去空格函数——strip。
功能:去掉
返回:返回被去掉空格的字符串值。
示例:
$(strip a b c )
把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。
$(findstring
名称:查找字符串函数——findstring。
功能:在字串
返回:如果找到,那么返回
示例:
$(findstring a,a b c)
$(findstring a,b c)
第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)
$(filter
名称:过滤函数——filter。
功能:以
返回:返回符合模式
示例:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。
$(filter-out
名称:反过滤函数——filter-out。
功能:以
返回:返回不符合模式
示例:
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
$(sort )
名称:排序函数——sort。
功能:给字符串中的单词排序(升序)。
返回:返回排序后的字符串。
示例: $(sort foo bar lose)返回“bar foo lose” 。
备注: sort 函数会去掉中相同的单词。
$(word
名称:取单词函数——word。
功能:取字符串
返回:返回字符串
示例: $(word 2, foo bar baz)返回值是“bar”。
$(wordlist ,
名称:取单词串函数——wordlist。
功能:从字符串开始到和
返回:返回字符串到比开始,到
示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。
$(words
名称:单词个数统计函数——words。
功能:统计
返回:返回
示例: $(words, foo bar baz)返回值是“3”。
$(firstword
名称:首单词函数——firstword。
功能:取字符串
返回:返回字符串
示例: $(firstword foo bar)返回值是“foo”。
3、文件名操作函数
$(dir
名称:取目录函数——dir。
功能:从文件名序列
返回:返回文件名序列
示例: $(dir src/foo.c hacks)返回值是“src/ ./”。
$(notdir
名称:取文件函数——notdir。
功能:从文件名序列
返回:返回文件名序列
示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。
$(suffix
名称:取后缀函数——suffix。
功能:从文件名序列
返回:返回文件名序列
示例: $(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。
$(basename
名称:取前缀函数——basename。
功能:从文件名序列
返回:返回文件名序列
示例: $(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。
$(addsuffix
名称:加后缀函数——addsuffix。
功能:把后缀
返回:返回加过后缀的文件名序列。
示例: $(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
$(addprefix
名称:加前缀函数——addprefix。
功能:把前缀
返回:返回加过前缀的文件名序列。
示例: $(addprefix src/,foo bar)返回值是“src/foo src/bar”。
$(join
名称:连接函数——join。
功能:把
返回:返回连接过后的字符串。
示例: $(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。
4、foreach函数—用作循环
$(foreach ,,
名称:循环函数——foreach
功能:把参数中的单词逐一取出放到参数所指定的变量中,然后再执行
所以, 最好是一个变量名, 可以是一个表达式,而
中的单词。举个例子:
names := a b c d files := $(foreach n,$(names),$(n).o)返回值为a.o b.o c.o d.o
5、if函数——条件判断
$(if
6、call函数
$(call
名称:创建参数化函数——call
功能:唯一一个可以用来创建新的参数化的函数,当 make 执行这个函数时,
例如:
reverse = $(1) $(2)
foo = $(call reverse,a,b)
那么, foo 的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如: reverse = $(2) $(1)
foo = $(call reverse,a,b)
此时的 foo 的值就是“b a”。
7、origin函数——查看变量的来源
$(origin
下面,是 origin 函数的返回值:
“undefined”
如果
“default”
如果
述。
“environment”
如果
“file”
如果
“command line”
如果
“override”
如果
“automatic”
如果
8、shell函数
shell函数的参数就是操作系统shell的命令,和反引号"`"具有相同的功能。shell函数把执行操作系统命令的输出作为函数返回。如:
注意,这个函数会新生成一个 Shell 程序来执行命令,所以你要注意其运行性能,如果你的Makefile 中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统性能是有害的。特别是 Makefile 的隐晦的规则可能会让你的 shell 函数执行的次数比你想像的多得多。不能过多使用
9、控制make的参数
make 提供了一些函数来控制 make 的运行。通常,需要检测一些运行 Makefile 时的运行时信息,并且根据这些信息来决定, make 下一步的执行状态。
$(error
产生一个致命的错误,
示例一:
ifdef ERROR_001
$(error error is $(ERROR_001))
endif
示例一会在变量 ERROR_001 定义了后执行时产生 error 调用
$(warning
这个函数很像 error 函数,只是它并不会让 make 退出,只是输出一段警告信息,而make 继续执行。
make的运行:
1、make的退出码
make 命令执行后有三个退出码:
0 —— 表示成功执行。
1 —— 如果 make 运行时出现任何错误,其返回 1。
2 —— 如果你使用了 make 的“-q”选项,并且 make 使得一些目标不需要更新,那么返回 2。
2、指定目标
任何在 makefile 中的目标都可以被指定成终极目标,但是除了以“-”打头,或是包含了“=”的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。
有一个 make 的环境变量叫“MAKECMDGOALS”,这个变量中会存放你所指定的终极目标的列表,如果在命令行上,你没有指定目标,那么,这个变量是空值。这个变量可以让你使用在一些比较特殊的情形下。比如下面的例子:
sources = foo.c bar.c
ifneq ( $(MAKECMDGOALS),clean)
include $(sources:.c=.d)
endif
常用的伪目标的功能:
“all”
这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
“clean”
这个伪目标功能是删除所有被 make 创建的文件。
“install”
这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
“print”
这个伪目标的功能是例出改变过的源文件。
“tar”
这个伪目标功能是把源程序打包备份。也就是一个 tar 文件。
“dist”
这个伪目标功能是创建一个压缩文件,一般是把 tar 文件压成 Z 文件。或是 gz 文件。
“TAGS”
这个伪目标功能是更新所有的目标,以备完整地重编译使用。
“check”和“test”
这两个伪目标一般用来测试 makefile 的流程。
3、检查规则
通过使用 make 命令的下述参数来检查命令或者是执行的序列。
“-n”
“--just-print”
“--dry-run”
“--recon”
不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试 makefile 很有用处。
“-t”
“--touch”
这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说, make 假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
“-q”
“--question”
这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。
“-W
“--what-if=
“--assume-new=
“--new-file=
这个参数需要指定一个文件。一般是是源文件(或依赖文件), Make 会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。
另外一个很有意思的用法是结合“-p”和“-v”来输出 makefile 被执行时的信息
make的参数:
“-b”
“-m”
这两个参数的作用是忽略和其它版本 make 的兼容性。
“-B”
“--always-make”
认为所有的目标都需要更新(重编译)。
“-C
“--directory=
指定读取 makefile 的目录。如果有多个“-C”参数, make 的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”等价于“make –C ~hchen/test/prog”。
“—debug[=
输出 make 的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是
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=
“--makefile=
指定需要执行的 makefile。
“-h”
“--help”
显示帮助信息。
“-i”
“--ignore-errors”
在执行时忽略所有的错误。
“-I
“--include-dir=
指定一个被包含 makefile 的搜索目标。可以使用多个“-I”参数来指定多个目录。
“-j [
“--jobs[=
指同时运行命令的个数。如果没有这个参数, make 运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。
“-k”
“--keep-going”
出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。
“-l
“--load-average[= “—max-load[= 指定 make 运行命令的负载。 “-n” “--just-print” “--dry-run” “--recon” 仅输出执行过程中的命令序列,但并不执行。 “-o “--old-file= “--assume-old= 不重新生成的指定的 “-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 “--what-if= “--new-file= “--assume-file= 假定目标 为当前时间。 “--warn-undefined-variables” 只要 make 发现有未定义的变量,那么就输出警告信息。 隐含规则: “隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。 1、使用隐含规则: 如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么,make 会试图去自动推导产生这个目标的规则和命令,如果 make 可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。 2、隐含规则一览: 可以使用 make的参数“-r”或“--no-builtin-rules”选项来取消所有的预设置的隐含规则。 常用的隐含规则: 1)编译 C 程序的隐含规则。“ 2)汇编和汇编预处理的隐含规则。“ 3)链接 Object 文件的隐含规则。“ 隐含规则中的变量: 在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。可以利用 make 的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的作用。 隐含规则中对于命令的变量:CC ——C 语言编译程序。默认命令是“cc” 隐含规则中对于参数的变量: CFLAGS——C 语言编译器参数。 CPPFLAGS——C 预处理器参数。 自动化变量及环境变量: $*: 不包括扩展名的目标文件名 $<:第一个依赖文件名称 $?:所有时间戳比目标文件晚的依赖文件 $@:目标文件完整名称 $^:所有不重复的依赖文件