Linux(程序设计):02---make与Makefile的设计与应用
Posted 董哥的黑板报
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux(程序设计):02---make与Makefile的设计与应用相关的知识,希望对你有一定的参考价值。
一、概念
- 执行make命令时,需要一个Makefile文件,以告诉make命令需要怎么样的去编译和链接程序
- Makefile是一个文本形式的数据库文件,其中包含一些规则来告诉make处理哪些文件以及如何处理这些文件
Make是GNU的开源工具
- 概念与Make的帮助文档见:http://www.gnu.org/software/make/
二、特点
- 因为Makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令
- 自动化编译:一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率
make与makefile是如何工作的
- 当我们输入make命令后,make会在当前工作目录下寻找Makefile文件
- 如果找到Makefile文件,它会找到Makefile文件中的第一个目标文件,并把这个目标文件作为最终的目标文件
- 如果这个目标文件不存在或者目标文件依赖于后面的.o文件/其他文件时,那么make就会执行目标文件后面的.o文件/其他文件来生成前面这个目标文件,类似于递归
Makefile的注释
- #:Makefile中只有行注释
因为#是Makefile的注释,所以如下你想要在Makefile中使用#符号,必须使用转义字符进行转义:\\#
Makefile的换行
- 执行命令过长时,可以使用\\换行,但是\\后面不可以再跟其他字符,空白符也不行
Make的能力
- Make使最终用户能够构建和安装包,而无需知道如何完成的详细信息 - 因为这些详细信息记录在您提供的makefile中
- 根据更改的源文件,自动生成需要更新的文件。如果一个非源文件依赖于另一个非源文件,它还会自动确定更新文件的正确顺序 因此,如果您更改了一些源文件然后运行Make,则无需重新编译所有程序。它仅更新直接或间接依赖于您更改的源文件的非源文件
- Make不限于任何特定语言。对于程序中的每个非源文件,makefile指定用于计算它的shell命令。这些shell命令可以运行编译器来生成目标文件,生成可执行文件的链接器,
ar
更新库,或者TeX或Makeinfo来格式化文档- Make不仅限于构建包。您还可以使用Make来控制安装或卸载软件包,为其生成标签表,或者您想要经常做的任何其他事情,以便在写下如何操作时使其值得
三、Makefile的基本格式
目标文件:依赖文件
执行命令
- 目标文件:就是我们想要生成的文件
- 依赖文件:生成目标文件所依赖的文件
- 执行命令:利用依赖文件生成目标文件所执行的命令
我们通过依赖文件,执行命令来生成我们想要的目标文件,这就是Makefile最基本的格式
备注:
- 目标文件的时间戳比任何一个依赖文件的时间戳晚,就生成目标文件
- 目标文件的时间戳比依赖文件的时间戳都早,就更新目标文件
- 缩进:“执行命令”前面有一个Tab键空格
四、make的执行
test:prog.o code.o
gcc –o test prog.o code.o
prog.o:prog.c prog.h code.h
gcc –c prog.c –o prog.o
code.o:code.c code.h
gcc –c code.c –o code.o
①没有指定执行目标
- 概念:只输入make,会在当前目录下寻找makefile或Makefile或GNUmakefile脚本文件,并执行脚本文件中的第一个目标。通常我们建议使用“Makefile”
- 例如:make ==>那么就执行生成test的语句块
②指定执行目标
- 概念:make + 目标 ==>执行Makefile文件中的指定目标
- 例如:make prog ==>执行生成prog目标文件的语句块
③指定执行其他名称脚本
- 如果有特殊要求,不想make命令执行的是makefile或Makefile文件,可以使用-f或者-file参数
- 例如:make -f filename ==>执行filename文件,而不去执行makefile/Makefile文件
五、Makefile的显示规则、隐式规则
显示规则:
- 当我们执行一个目标时,如果这个目标的依赖文件没有,那么make就回去向下检查是否有生成这个依赖文件的命令,如果有就去执行(这是自动执行的)
注意:
- 依赖性是从文件从上向下的,不能从下向上
- 显示规则下的依赖文件时手动给出的
案例:当我们执行make时,make会去生成test这个目标文件,但是prog.o、code.o这两个依赖文件在目录中不存在,那么:
- 通过依赖性,make向下寻找是否有生成prog.o、code.o这两个文件的命令
- 通过向下寻找,可以寻找到生成这两个目标文件的命令,那么就隐式的执行下面两个生成prog.o、code.o的命令
- 隐式生成prog.o、code.o之后,就去执行生成test目标的命令,最终生成test文件
test:prog.o code.o gcc –o test prog.o code.o prog.o:prog.c prog.h code.h gcc –c prog.c –o prog.o code.o:code.c code.h gcc –c code.c –o code.o
隐式规则:
- 概念:通过显示规则我们知道,我们目标文件的依赖文件没有生成时,就会向下寻找生成依赖文件的生成命令,但是这些生成命令都是手动给出的。通过隐式规则,我们可以省略生成这些依赖文件的命令
注意:
- 隐式规则下,在目录下必须有能通过命令生成依赖文件的同名文件
案例:
- 我们将上面的Makefile进行改写,省去了生成prof.o、code.o的命令,因为make会隐式的生成.o文件,再用这些.o文件生成test(但是目录下必须有prog.c、code.c这样的同名文件,隐式规则才会生效)
test:prog.o code.o gcc –o test prog.o code.o
六、Makefile变量的分类
Makefile的变量分为以下3类:
- 用户自定义变量
- 预定义变量
- 自动变量及环境变量
①用户自定义变量
- 概念:用户自己定义的(通常建议大写)
定义以及使用:
- 定义之后通过 “$(变量名) ”或 “$变量名” 定义
hello:hello.c gcc -g -o hello hello.c
- 将上面内容进行更改,使用变量,就是下面的格式
TARGET = hello //定义变量 OBJ = hello.c //定义变量 FLAGS = -g -o //定义变量 $(TARGET):$(OBJ) gcc $(FLAGS) $(TARGET) $(OBJ)
②预定义变量
- 概念:系统自带的变量,有的有默认值(默认值也可以更改),有的没有默认值
- 定义以及使用与用户自定义变量是一样的
TARGET = hello //定义变量 OBJ = hello.c //定义变量 FLAGS = -g -o //定义变量 CC = gcc //定义变量 $(TARGET):$(OBJ) $(CC) $(FLAGS) $(TARGET) $(OBJ)
③自动变量及环境变量
系统自带的一些变量,不能修改,直接使用,有特殊意义:
- $@:代表目标文件
- $^:代表依赖文件
- $<:代表第一个依赖文件
- $?:所有时间戳比目标文件晚的依赖文件
- $*:不包含扩展名的目标文件名称
例如:
- 我们将上面的Makefile改为下面的Makefile
TARGET = hello OBJ = hello.c FLAGS = -g -o $(TARGET):$(OBJ) $(CC) $(FLAGS) $(TARGET) $(OBJ)
TARGET = hello OBJ = hello.c FLAGS = -g -o $(TARGET):$(OBJ) $(CC) $(FLAGS) $@ $^
七、Makefile变量的赋值
Makefile的变量赋值也分为以下3类:
- 格式一:变量 = value
- 格式二:变量 := value
- 格式三:变量 += value
格式一
- 变量 = value
- 特点:这种变量在第一次定义之后就不可以改变(就是我们上面所使用的方式)
格式二
- 变量 := value
- 特点:这种变量在第一次定义之后,还可以通过 “+=” 追加变量内容
OBJS := test1.o test2.o test:$(OBJS) gcc -o test $(OBJS) OBJS += test3.o //现在OBJS = test1.o test2.o test3.o
格式三:
- 变量 += value
- 特点:就是对用 “:=”方式定义的变量进行追加内容,上面有案例
八、伪目标(.PHONY)
- 概念:伪目标是执行时没有依赖文件,并且在磁盘上不会生成文件,只是用来执行命令
- 加不加.PHONY的区别:如果没有使用.PHONY,那么当在当前目录下有一个clean文件时就会报错;加了就不会报错
案例:
- 例如下面有一个伪目标clean,当我们执行make clean时,就去执行rm -r *命令(两种方式在于有无.PHONY)
#方式一
.PHONY:clean
clean:
rm -r *
#方式二
clean:
rm -r *
用伪目标生成多个文件
- 当我们执行make all时,就回去执行生成prog1 prog2 prog3的命令
all:prog1 prog2 prog3 .PHONY:all prog1:prog1.o utils.o gcc -o prog1 prog1.o utils.o prog2:prog2.o gcc -o prog2 prog2.o prog3:prog3.o gcc -o prog3 prog3.o
九、Makefile的隐藏输出
- 我们知道makefile中的命令执行之后会在控制台中输出出来,如果不想要命令输出在控制台上,可以在命令之前加一个“@”符号
例如下面的第2、第3句gcc命令不会在控制台中输出
test:prog.o code.o
gcc –o test prog.o code.o
prog.o:prog.c
@gcc –c prog.c –o prog.o
code.o:code.c
@gcc –c code.c –o code.o
十、通配符
常用的通配符(这些通配符在所有语言中通用的,都是统一标准的):
- %:任意一个
- ?:匹配
- *:所有
*通配符
- 代表所有的字符
#make clean命令删除目录下所有的.c文件 clean: rm *.c
%通配符
- 只指代一个字符
- he%l ===>heel、hell、heal都是
?通配符
- 匹配1-n个字符
- h?o ===>heo、heeo、hello都算
十一、函数
Makefile定义了一系列函数
wildcard函数
- 功能:提取当前目录下的所有.c文件的名称,返回值是这些.c文件名称的字符串
#SRC这个变量是目录下所有.c文件名的字符串列表 SRC = $(wildcard *.c)
patsubst函数
- 功能:字符串转换函数,将一些字符串转换为另一种格式的字符串
#OBJS是一系列.o文件名的字符串 SRC = $(wildcard *.c) OBJS=$(patsubst %.c,%.o,$(SRC)) #将SRC中的所有.c字符串变为.o字符串
十二、条件判断
- 待续
十三、Makefile的引用与嵌套
- 待续
十四、make的管理命令
- -C dir:读取指定目录下的Makefile文件
- -f file:将当前目录下指定的file文件作为Makefile文件执行
- -i:忽略所有命令执行错误
- -I dir:指定被包含的Makefile所在目录
十五、makefile的文件指示
1.格式:
- #include <filename>
2.案例
- include foo.make *.mk $(bar) 等价于: include foo.make a.mk b.mk c.mk e.mk f.mk
以上是关于Linux(程序设计):02---make与Makefile的设计与应用的主要内容,如果未能解决你的问题,请参考以下文章
Makefile项目管理-----在Linux下编译c/c++程序