在学习了童永清编著《linux C编程实战》中的make的使用和Makefile的编写一节之后自己做一下相关的总结.
本段摘自 《linux C编程实战》
在Linux中,有一个用来维护模块关系和生成可执行程序的工具.它可以根据程序模块的修改情况重新编译链接生成中间代码或者最终可执行程序.而需要一个名为makefile或者Makefile的文本文件.这个文件定义了整个项目的编译规则.它定义了模块间的依赖关系.制定了文件的编译,以及编译所使用的命令.有了make命令和makefile文件,整个项目的源程序可以自动编译,极大的提高软件开发的效率.
现在终端下(本机是以ubuntu14.04为例,其他系统所以安装命令可能会有所不同,而对于make使用和makefile的编写则规则是完全相同)
执行make
如果提示没有安装make可以安装 使用命令 sudo apt-get install make
makefile基本构成.
一条规则指定一个或者多个目标文件,目标文件后面根的是编译生成目标文件所依赖的文件或模块,最后是生成或者更新目标文件所使用的命令。
目标文件列表 分隔符(默认是:)依赖文件列表 [;命令;命令;命令......]
[命令]
[命令]
...
现在想要将他们分别编译链接成为可执行文件且名称为1.out 2.out 3.out
举个列子如果终端下面有1.c 2.c 3.c 4.c
vim Makefile
解释一下这个Makefile
蓝色字体makeall
为目标文件(但是不会出现在文件之中,它可以是一个虚拟目标)
:是分隔符后面是生成该目标所依赖的文件.(如果没有依赖就不执行命令生成这个目标文件,且退出makefile下面的目标文件均不被生成)
依赖文件后面 ; 之后的是生成该目标文件所执行的命令,以及执行命令的方式.(可以不执行任何命令也可以执行若干名令不过每个命令都已 ; 为结尾)
命令还可写在所生成的目标下的任意行(命令要以Tab间隔,就是在树命令前先打一个Tab键)直到下一目标文件的上一行(命令间也可以插入多个空行,但是空行也必须以Tab间隔)
也可以没有任何命令.
好了写完了Makefile之后可以保存退出.在中端之中执行
make
然后查看文件
ls
你会发现所有文件都编译链接成了对应的可执行文件.是不是很给力。
说明一下,make执行顺序
他会会在当前目录找对应的m/Makefile 文件
然后找到我们所编写的Makefile
开始执行 首先要生成第一个目标文件.他依赖的文件是1.c 2.c 3.c 4.c 然后系统在当前目录下找这些文件发现都存在.然后开始逐条执行命令。并且打印到终端.
如果没有找全所有的依赖文件make执行过程自动退出.
好了现在已经有了我们所需要的文件.可是如果我们将1.c的源码改变现在想要生成新的1.out 如何处理
其实只要我们在终端现在执行一次make即可.
Makefile 在判断目标文件是否需要重新生的条件是,他的依赖文件之中至少有一个比目标文件生成的晚.若有那麽就重新生成目标文件,执行对应的命令
以此处的目标文件makeall 来说明的话.
第一次生成的makeall这个目标比2.c 3.c 4.c的最后修改时间晚,但是却比1.c最后更改时间早,以此来推断上次的makeall这个目标不是最新的,那麽make自动执行
gcc 1.c -o 1.out
gcc 2.c -o 2.out
gcc 3.c -o 3.out
gcc 4.c -o 4.out
来生成新的makeall这个目标文件,此时1.c被更新了.
但如果讲Makefile 改成如下
| 之前的文件依然作于makeall比较跟新时间来确定makeall是否过时需要重新生成.
而之后的2.c 3.c 4.c 却不参与其中就算他们的修改时间晚于makeall然而重新生成makeall因为make不把他们与makeall进行比较
命令执行的属性(这些属性加道明令之前)
@ 执行该命令时部将命令输出道屏幕上
- 在执行命令时如果返回了错误(即正确的命令返回错误,如删除不存在的文件.)不退出makefile执行过程而是继续向下执行
+ 本名令始终被执行即使make命令是用了-n , -q , -t选项.
举个列子说明
现在想把刚生成的1.out 2.out 3.out 4.out 删除掉并且不显示删除 rm *.out 密令我们可以这样修改Makefile
我们给rm -r *.out 前面添加了@
在中端外面执行make rmall.out 时就不会在打印rm -r *.out了.但是删除后在执行一遍make rmall.out就会出现错误因为删除的 *.out不存在
现在我们再给rm -r *.out 出过加上@再加上-那麽就算在没有*.out(以.out结尾的文件)也不会出错病退出执行makefile了
细心的你也许已经发现了在执行生成rmall.out 是我们使用了make rmall.out来执行(这是强制执行生成某个目标文件的方法,不把目标文件于依赖文件进行比较而直接
执行生成它,执行生成它所需的命令).
你也可以试试,直接在终端下使用make命令,会发现根本不会执行rmall.out的命令,因为他没有依赖文件系统默认rmall.out是最新的不用生成,就不会执行生成它的命令
因此rm -r *.out不会执行.
此时当前目录下如果存在一个名为rmall.out的文件,现在想要使用make rmall.out都不行了,他会告诉你目标文件rmall.out是最新的.不用执行命令生成它.
如果我们想要确保他能正常的被执行,那麽我们可以将它作为 .PHONY的依赖文件.
这样我们又可以使用make rmall.out 来删除以.out结尾的文件.
这是因为作为.PHONY 的依赖文件,它会成为一个伪目标之后无论当前目录下是否存在该名称的文件使用make 伪目标名称 都可以执行命令
(使用make 强制执行的原因是make all没有依赖默认为最新的,必须强制执行)
.PHONE 是特殊的目标文件它的功能已经说过,下面就来说明下同为特殊文件,的依赖文件他们所受到的"特殊待遇"及特殊文件的作用
格式为特殊目标名:aimfile (意味名为aimfile的依赖文件)
.IGNORE : aimfile 生成aimfile的时候执行发生错误时进行忽略而不退出.类似于给aimfile的执行命令前面全部加上-
.SUFFIXES: aimfile 作为后缀列表,检查后追规则时是使用.
.SILENT: aimfile 执行生成aimfile时不打印命令类似全部aimfile命令加上@属性
.PRECIOUS: aimfile 如果make被kill终止或者遇到意外而被终止.这些一类并不会被删除.
.INTERMEDIATE: aimfile aimfile被当作中间文件来对待.
下面来看一下搜索目录.若果目标文件放在不同的目录里,这种情况我们需要让make到不同的目录里去寻找依赖文件.当文件所在目录发生变化,可以不用该makefile中的规则
而只改变依赖的搜索目录.
举个列子说明下.
vapath = /usr/src : ../
make 将在除过当前目录下还会在/user/src ../(当前目录的上级目录查找)
vapath = %.c ../
vapath = %.a ~/
vapath = %.o /user/lib
此例说明同一个makefile文件之中可以存在多个指定路径的vpath说明,他得知行顺序是
在上级目录中找所有.c文件
用户的家目录下面找 .a(静态态链接库文件)
在user/lib下查找所有.o(动态链接库文件)
make 中的变量.
定义了变量后就可以直接使用该变量了.
通过 = 张开的变量是递归展开的变量
如 a=$(b)
b=${c}
此时在确定a的的值得时候,会先确定b的值,在确定b的时候会预先确定c的值.
然而变量c只是在定义时使用了,它里面默认为空值,确定c为空的时候,把c的空值赋给b,再把b的空值赋值给a
$(var) ,${var}都是取变量var的值.
如定义var=${var}+a
此时将会发生死循环要注意.
优点是在没有一个变量定义之前可以引用改变量的值.
另一种赋值方法是:= 讲等号右边的变量展开赋值给左边
如:
a ;= I
b :=${a} am
c :=${b} fun
结果是
a 为 I
b 为 I am
c 为 I am fun
+=
是追加操作
预置变量
变量名 初始值 说明
CC cc 默认使用的编译器
CFLAGS -O 编译器使用的选项
MAKE make make命令
MAKEFLAGS 空 make命令选项
SHELL 默认是用的Shell类型
PWD 运行mae命令时候的当前目录
AR ar 库管黎明令
ARFLADS -ruv 管理命令选项
LIBSUFFIXE .a 库的后缀
A a 哭的扩展名
可以通过调整他们实现堆make的调整.
使用条件语句
条件语句用到了三个关键字. ifeq,else ,endif
ifeq (${a},${b})
...
else
....
endif
还有ifdef,ifndef,分表判断变量是否定义,是否没有定义
......
由于还没有全部解读,所有使用库和隐含规则并未说明。
关于make 的参数可以在终端下输入 man 命令来进行查看。。。