前言
当我们需要编译一个比较大的项目时,编译命令会变得越来越复杂,需要编译的文件越来越多。其
次就是项目中并不是每一次编译都需要把所有文件都重新编译,比如没有被修改过的文件则不需要重
新编译。工程管理器就帮助我们来优化这两个问题。
MakeFile就类似于make工程管理的工作的脚本。用来告诉工程管理器如何正确的编译我们的程
序
依赖于目标的关系:
在MakeFile中依赖于目标是相互的,并不是绝对
比如 a.c 是生成a.o的一个依赖文件, 对于a.o 则是a.c的目标,a.o 又是image的依赖。
在我们使用make 进行编译的时候,工程管理器则会根据依赖于目标的关系来检查它们之间时间戳
关系,如果依赖有给你更新那么目标文件则需要执行。
安装make
sudo apt install make
语法
target : tgt_dependency1 tgt_dependency2 ...
command
注意:
目标必须存在
依赖可以没有
命令前面必须是一个制表符TAB
Makefile 文件的命名一般是 Makefile
没有后缀也没有前缀
如果规则中没有写依赖,则无论如何该规则该规则都会执行
如果目标已经存在,然后也没有写依赖则不执行该规则
示例1
Even:Jacy
@echo "Hello Makefile"
Jacy:ChuiHua
@echo "Hello Even"
ChuiHua:
@echo "Hello Jacy"
执行
make // 工程管理器把第一个目标当成最终目标 Even
make Jacy // 告诉工程管理器 Jacy是我们需要的最终目标
示例2
./bin/main:./src/*.c
gcc ./src/*.c -o ./bin/main -I./inc -L./lib -lmy_lib
示例3
animal_assembly : moose goose cat
command
moose : antlers hooves fur
command
goose : beak wings webbed_feet interest_in_bread
command
cat : whiskers evil_personality
command
当不带参数调用时,将尝试构建目标animal_assembly
。
假设依赖项moose
、goose
和cat
已经在目录中可用,它将完全忽略它们的规则,并animal_assembly从现有内容构建。
如果moose
和cat
可用,但goose
不可用,它会注意到moose
存在,看到goose
不存在,寻找要构建的规则goose,找到规则,构建goose,然后注意cat
存在和构建animal_assembly
。
如果 moose
, goose
, cat
都不存在,则必须使用可用规则构建所有这些。
一个好的经验法则是将最后和最重要的命令(对于我们的目的,最终将目标文件链接在一起成为可执行文件的命令)放在顶部。
变量:
- 变量和函数的展开(除规则的命令行以外),是在make读取Makefile文件时进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的变量。
- 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜
索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。 - 变量名不能包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。需要注意的是,
尽管在GNU make中没有对变量的命名有其它的限制,但定义一个包含除字母、数字和下
划线以外的变量的做法也是不可取的,因为除字母、数字和下划线以外的其它字符可能会在
以后的make版本中被赋予特殊含义,并且这样命名的变量对于一些Shell来说不能作为环
境变量使用。 - 变量名是大小写敏感的。变量“foo”、“Foo”和“FOO”指的是三个不同的变量。Makefile传统做法是变量名是全采用大写的方式。推荐的做法是在对于内部定义的一般变量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式,这并不是要求的。但需要强调一点:对于一个工程,所Makefile
中的变量命名应保持一种风格,否则会显得你是一个蹩脚的开发者(就像代码的变量命名风格一样),随时有被鄙视的危险。 - 另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自
动化变量。像“<”、“@”、“?”、“*”、“@D”、“%F”、“^D”等等,后面会详
述之。 - 变量的引用跟Shell脚本类似,使用美元符号和圆括号,比如有个变量叫A,那么对
他的引用则是$(A)
,有个自动化变量叫@
,则对他的引用是$(@)
,有个系统变量是CC
则
对其引用的格式是$(CC)
。对于前面两个变量而言,他们都是单字符变量,因此对他们引用
的括号可以省略,写成$A
和$@
。`
自定义变量
顾名思义就是用户自己定义的变量
A = apple # 定义并赋值变量
B = I love China
C = $(A) tree # $() 则是对某一个变量进行引用
Even:
@echo $(A)
@echo $(B)
@echo $(C)
通过自定义变量来修改的Makefile 第二版本
TAG=./bin/main
SRC=./src/*.c
CC=gcc
O=-o
CONFIG=-I./inc -L./lib -lmy_lib
$(TAG):$(SRC)
$(CC) $(SRC) $(O) $(TAG) $(CONFIG)
clean:
rm ./bin/*
系统变量
自动化变量
自动化变量的值会自动发生变化
Makefile 中定义的变量有以下几种不同的方式:
1,递归定义方式:
A = I love $(B) # 在第一行使用到变量B但是还没有定义,以此管理器进行全文搜索找到B并引用
B = China
2,直接定义方式:
B = China
A := I love $(B)
此处,定义 A 时用的是所谓的“直接”定义方式,说白了就是如果其定义里出现有对
其他变量的引用的话,只会其前面的语句进行搜寻(不包含自己所在的那一行),而不是搜
寻整个文件,因此,如果此处将变量 A 和变量 B 的定义交换一个位置:
A := I love $(B) # A在B之前引用B 则为空
B = China
则 A 的值将不包含 China,因此在定义 A 时 B 的值为空。
3,条件定义方式:
有时我们需要先判断一个变量是否已经定义了,如果已经定义了则不作操作,如果没有
定义再来定义它的值,这时最方便的方法就是采用所谓的条件定义方式:
A = apple
A ?= I love China
此处对 A 进行了两次定义,其中第二次是条件定义,其含义是:如果 A 在此之前没有
定义,则定义为“I love China”,否则维持原有的值。
4,多行命令定义方式:
define commands
echo “thank you!”
echo “you are welcome.”
endef
此处定义了一个包含多行命令的变量commands,我们利用它的这个特点实现一个完
整命令包的定义。注意其语法格式:以define开头,以endef结束,所要定义的变量名必须
在指示符“define”的同一行之后,指示符define所在行的下一行开始一直到“end”所在行的
上一行之间的若干行,是变量的值。这种方式定义的所谓命令包,可以理解为编程语言中的
函数。
Makefile中的变量还有以下几种操作方式:
1,追加变量的值,例如:
A = apple
A += tree
这样,变量A的值就是apple tree。
2,修改变量的值,例如:
A = srt.c string.c tcl.c
B = $(A:%.c=%.o)
输出为srt.o string.o tcl.o
第三个版本:
TAG=./bin/main
SRC=./src/Input.c ./src/main.c ./src/Oper.c ./src/Output.c
OBJ=$(SRC:%.c=%.o)
CC=gcc
O=-o
CONFIG=-I./inc
$(TAG):$(OBJ)
$(CC) $(^) $(O) $(@) $(CONFIG)
%.o:%.c
$(CC) $< -o $(@) $(CONFIG) -c
clean:
$(RM) ./bin/* ./src/*.o
函数
$(subst FROM,TO,TEXT)
功能:将字符串 TEXT 中的字符 FROM 替换为 TO。
返回:替换之后的新字符串。
范例:
A = $(subst pp,PP,apple tree)
替换之后变量 A 的值是”aPPle tree”
$(wildcard PATTERN)
功能:获取匹配模式为 PATTERN 的文件名。
返回:匹配模式为 PATTERN 的文件名。
范例:
A = $(wildcard *.c)
假设当前路径下有两个.c 文件 a.c 和 b.c,则处理后 A 的值为:”a.c b.c”。
override一个变量,例如:
override CFLAGS += -Wall
.PHONY 来明确地告诉 Makefile,不要对 clean 运用任何隐式规则,不能运用隐式规则的目标被称为
伪目标
.PHONY:clean
用来修饰 clean 清空的工作不会被误以为是一个目标来执行
第四版本(通用版本)
TAG=./bin/main
SRC= $(wildcard src/*.c)
OBJ=$(SRC:%.c=%.o)
CC=gcc
override CONFIG += -I./inc
$(TAG):$(OBJ)
$(CC) $(^) -o $(@) $(CONFIG)
%.o:%.c
$(CC) $< -o $(@) $(CONFIG) -c
clean:
$(RM) ./bin/* ./src/*.o
.PHONY:clean