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

Posted 董哥的黑板报

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux(程序设计):02---make与Makefile的设计与应用相关的知识,希望对你有一定的参考价值。


一、概念


  • 执行make命令时,需要一个Makefile文件,以告诉make命令需要怎么样的去编译和链接程序
  • Makefile是一个文本形式的数据库文件,其中包含一些规则来告诉make处理哪些文件以及如何处理这些文件


Make是GNU的开源工具


二、特点


  • 因为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)​


②预定义变量


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

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



格式二


  • 变量 := 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++程序

mak iso

nmake - 模拟 eval 函数

MCSA / Windows Server 2016 授权许可和MAK / KMS / ADBA激活

uC/OS-II实现TEST.MAK块

Linux系统安装minuit包