Linux(程序设计):02---make与Makefile的设计与应用

幽映每白日,清辉照衣裳。这篇文章主要讲述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-ffilename==> 执行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)??


②预定义变量

  • ?概念:?系统自带的变量,有的有默认值(默认值也可以更改),有的没有默认值
  • 定义以及使用与用户自定义变量是一样的

Linux(程序设计):02---make与Makefile的设计与应用

文章图片

??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?
  • 特点:这种变量在第一次定义之后就不可以改变(就是我们上面所使用的方式)

【Linux(程序设计):02---make与Makefile的设计与应用】



?格式二?

  • ?变量 := value?
  • 特点:这种变量在第一次定义之后,还可以通过 “+=” 追加变量内容

??OBJS := test1.o test2.otest:$(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:allprog1: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 = https://www.songbingjia.com/android/$(wildcard *.c)??


patsubst函数
  • 功能:字符串转换函数,将一些字符串转换为另一种格式的字符串
??#OBJS是一系列.o文件名的字符串SRC = https://www.songbingjia.com/android/$(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



    推荐阅读