1、Makefile规则
HelloWorld基本上所有程序员的所有编程语言一个最简单的例子,虽然makefile不是一门编程语言,但是我们同样可以在一个命令中终端上输出一个helloworld
all: echo"Hello World"
那上述例子中就有我们学习的第一个语法,echo前面必须只有TAB,且至少有一个TAB,不能用空格代替。(初学者易犯)
1.1目标 ,Makefile中第一个很重要的概念就是目标,上述例子中all就是我们的目标。
特点:1.放在”:“的前面
2.由字母和下划线组成
1.2 echo这句是一个生成目标的命令,echo是BASH shell中的一个命令,其功能是打印字符到终端上。
回归主题:all目标的定义,其实是定义了如何生成all目标我们也称之为规则。即定义了一个生成all目标的规则
all: echo "Hello world" test: echo "test game"
由此可见,⼀个 Makefile 中可以定义多个⽬标。调⽤ make 命令时,我们得告诉它我们的⽬标是什么,即要它⼲什么。当没有指明具体的⽬标是什么 时,那么 make 以 Makefile ⽂件中定义的第⼀个⽬标作为这次运⾏的⽬标。这“第⼀个”⽬标也称之 为默认⽬标(和是不是all没有关系)。当 make 得到⽬标后,先找到定义⽬标的规则,然后运⾏规则中的命令来达到构建⽬标的⽬的。
makefile中取消多余的命令行显示
在上面的指令中,多了很多的echo "......"
的内容,这部分不是我们所期望的,如果要去掉,需要对上面的makefile进行一个改动,也就是在命令前加上一个@,这个符号就是告诉make,在运行的时候这一行命令不显示出来。
all: @echo "Hello world" test: @echo "test game"
紧接着对makefile进行如下的改动,在all的后面加上test
all: test @echo "Hello world" test: @echo "test game"
⼀个规则是由⽬标(targets)、先决条件(prerequisites)以及命令(commands)所组成的。
需要指出的是,⽬标和先决条件之间表达的就是依赖关系(dependency),这种依赖关系指明在构建⽬标之前,必须保证先决条件先满⾜(或构建)。⽽先决条件可以是其它的⽬标,当先决条件是⽬标时,其必须先被构建出来。还有就是⼀个规则中⽬标可以有多个,当存在多个⽬标,且这⼀规则是 Makefile 中的第⼀个规则时,如果我们运⾏ make 命令不带任何⽬标,那么规则中的第⼀个⽬标将被视为是缺省⽬ 标。
规则的功能就是指明 make 什么时候以及如何来为我们重新创建⽬标,在 Hello World 例⼦中,不论我们 在什么时候运⾏ make 命令(带⽬标或是不带⽬标),其都会在终端上打印出信息来,和我们采⽤ make 进⾏代码编译时的表现好象有些不同。当采⽤ Makefile 来编译程序时,如果两次编译之间没有任何代码 的改动,理论上说来,我们是不希望看到 make 会有什么动作的,只需说“⽬标是最新的”,⽽我们的最终 ⽬标也是希望构建出⼀个“聪明的” Makefile 的。与 Hello World 相⽐不同的是,采⽤ Makefile 来进⾏ 代码编译时,Makefile 中所存在的先决条件都是具体的程序⽂件,后⾯我们会看到。
简单举例:makefile原理
all: main.o foo.o gcc -o simple main.o foo.o main.o: main.c gcc -o main.o -c main.c foo.o: foo.c gcc -o foo.o -c foo.c clean: rm simple main.o foo.o
编译:
上面的展示了测试结果,注意到了第⼆次编译并没有构建⽬标⽂件的动作吗?但为什么有构建simple可执⾏程序的动作呢?为了明⽩为什么,我们需要了解 make 是如何决定哪些⽬标(这⾥是⽂件)是需要重新编译的。为什么 make会知道我们并没有改变 main.c 和 foo.c 呢?答案很简单,通过⽂件的时间戳!当 make 在运⾏⼀个规则时,我们前⾯已经提到 了⽬标和先决条件之间的依赖关系,make 在检查⼀个规则时,采⽤的⽅法是:如果先决条件中相关的⽂件的时间戳⼤于⽬标的时间戳,即先决条件中的⽂件⽐⽬标更新,则知道有变化,那么需要运⾏规则当中 的命令重新构建⽬标。这条规则会运⽤到所有与我们在 make时指定的⽬标的依赖树中的每⼀个规则。⽐如,对于 simple 项⽬,其依赖树中包括三个规则,make 会检查所有三个规则当中的⽬标(⽂件)与先决条件(⽂件)之间的时间先后关系,从⽽来决定是否要重新创建规则中的⽬标。
问题一:第二次构建的时候为什么simple会被重新构建?
是因为simple文件不存在,我们在这次构建的目标是all,而all在我们编译的过成中并不生成,所以第二次make的时候找不到,所以又重新编译了一遍。
修改makefile
simple: main.o foo.o gcc -o simple main.o foo.o main.o: main.c gcc -o main.o -c main.c foo.o: foo.c gcc -o foo.o -c foo.c clean: rm simple main.o foo.o
结果
一个文件是否改变不是看这个文件的大小是否改变,而是看这个文件的时间戳是否发生了变化。可以直接使用touch指令对文件的时间戳进行修改。
假目标
如上图,如果我们的创建了一个clean文件之后,继续去运行make clean,这时候不是按照我们前面运行的make clean进行清理文件。
为什么出现上面的原因?
因为这个时候make 将clean单程是一个文件,并且在当前的目录下找到了这个文件,再加上clean目标没有任何的先决条件,这时候进行make clean时,系统会认为clean是最新的
使用假目标,假目标最从常用清净就是避免所定义的目标和的已经存在文件是从重名的情况,假⽬标可以采⽤.PHONY 关键字来定义,需要注意的是其必须是⼤写字⺟。
采⽤.PHONY 关键字声明⼀个⽬标后,make 并不会将其当作⼀个⽂件来处理,⽽只是当作⼀个概念上的⽬标。对于假⽬标,我们可以想像的是由于并不与⽂件关联,所以每⼀次 make 这个假⽬标时,其所在的规则中的命令都会被执⾏。
.PHONY: clean simple: main.o foo.o gcc -o simple main.o foo.o main.o: main.c gcc -o main.o -c main.c foo.o: foo.c gcc -o foo.o -c foo.c clean: rm simple main.o foo.o
采⽤.PHONY 关键字声明⼀个⽬标后,make 并不会将其当作⼀个⽂件来处理,⽽只是当作⼀个概念上的⽬标。对于假⽬标,我们可以想像的是由于并不与⽂件关联,所以每⼀次 make 这个假⽬标时,其所在的规则中的命令都会被执⾏。
变量
.PHONY: clean CC = gcc RM = rm EXE = simple OBJS = main.o foo.o $(EXE): $(OBJS) $(CC) -o $(EXE) $(OBJS) main.o: main.c $(CC) -o main.o -c main.c foo.o: foo.c $(CC) -o foo.o -c foo.c clean: $(RM) $(EXE) $(OBJS)
变量的使用可以提高makefile的可维护性。⼀个变量的定义很简单,就是⼀个名字(变量名)后⾯跟上⼀个等号,然后在等号的后⾯放这个变量所期望的值。对于变量的引⽤,则需要采⽤$(变量名)或者${变量名}这种模式。在这个 Makefile 中,我们引⼊了 CC 和 RM 两个变量,⼀个⽤于保存编译器名,⽽另⼀个⽤于指示删除⽂件的命令是什么。还有就是引⼊了 EXE 和 OBJS 两个变量,⼀个⽤于存放可执⾏⽂件名,可另⼀个则⽤于放置所有的⽬标⽂件名。采⽤变量的话,当我们需要更改编译器时,只需更改变量赋值的地⽅,⾮常⽅便,如果不采⽤变量,那我们得更改每⼀个使⽤编译器的地⽅,很是麻烦。
1.自动变量(☆☆☆☆☆)
对于每⼀个规则,⽬标和先决条件的名字会在规则的命令中多次出现,每⼀次出现都是⼀种麻烦,更为麻烦的是,如果改变了⽬标或是依赖的名,那得在命令中全部跟着改。有没有简化这种更改的⽅法呢?这我们需要⽤到 Makefile 中的⾃动变量,最常用包括:
- $@⽤于表示⼀个规则中的⽬标。当我们的⼀个规则中有多个⽬标时,$@所指的是其中任何造成命令被运⾏的⽬标。
- $^则表示的是规则中的所有先择条件。
- $<表示的是规则中的第⼀个先决条件。
在 Makefile 中‘$’具有特殊的意思,因此,如果想采⽤ echo 输出‘$’,则必需⽤两个连着的‘$’。还有就是,$@对于 Shell 也有特殊的意思,我们需要在“$$@”之前再加⼀个脱字符‘\’。
2.特殊变量
(1)MAKE变量
它表示的是make 命令名是什么。当我们需要在 Makefile 中调⽤另⼀个 Makefile 时需要⽤到这个变量,采⽤这种⽅式,有利于写⼀个容易移植的 Makefile。
(2)MAKECMDGOALS变量
它表示的是当前⽤户所输⼊的 make ⽬标是什么。MAKECMDGOALS 指的是⽤户输⼊的⽬标,当我们只运⾏ make 命令时,虽然根据
Makefile 的语法,第⼀个⽬标将成为缺省⽬标,即 all ⽬标,但 MAKECMDGOALS 仍然是空,⽽不是
all,这⼀点我们需要注意。
3.递归扩展变量
示例了使⽤等号进⾏变量定义和赋值,对于这种只⽤⼀个“=”符号定义的变量,我们称之为递归扩展变量(recursively expanded variable)。
.PHONY: all foo = $(bar) bar = $(ugh) ugh = Huh? all: @echo $(foo)
除了递归扩展变量还有⼀种变量称之为简单扩展变量(simply expanded variables),是⽤“:=”操作符
来定义的。对于这种变量,make 只对其进⾏⼀次扫描和替换。
另外还有一种条件赋值符“?=”,条件赋值的意思是当变量以前没有定义时,就定义它并且将左边的值赋值给它,如果已经定义了那么就不再改变其值。条件赋值类似于提供了给变量赋缺省值的功能。
此外,还有"+="操作符,对变量进⾏赋值的⽅法
4.override指令
在设计 Makefile 时,我们并不希望⽤户将我们在 Makefile 中定义的某个变量覆盖掉,那就得⽤ override 指令了。
.PHONY: all override foo = x all: @echo "foo = $(foo)"
5.模式
如果对于每⼀个⽬标⽂件都得写⼀个不同的规则来描述,那会是⼀种“体⼒活”,太繁了!对于⼀个⼤型项⽬,就更不⽤说了。Makefile 中的模式就是⽤来解决我们的这种烦恼的。
.PHONY: clean CC = gcc RM = rm EXE = simple OBJS = main.o foo.o $(EXE): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) -o $@ -c $^ clean: $(RM) $(EXE) $(OBJS)
与 simple 项⽬前⼀版本的 Makefile 相⽐,最为直观的改变就是从⼆条构建⽬标⽂件的规则变成了⼀条。
模式类似于我们在 Windows 操作系统中所使⽤的通配符,当然是⽤“%”⽽不是“*”。采⽤了模式以后,
不论有多少个源⽂件要编译,我们都是应⽤同⼀个模式规则的,很显然,这⼤⼤的简化了我们的⼯作。使
⽤了模式规则以后,你同样可以⽤这个 Makefile 来编译或是清除 simple 项⽬,这与前⼀版本在功能上是
完全⼀样的。
6.函数
函数是 Makefile 中的另⼀个利器,现在我们看⼀看采⽤函数如何来简化 simple 项⽬的 Makefile。对于
simple 项⽬的 Makefile,尽管我们使⽤了模式规则,但还有⼀件⽐较恼⼈的事,我们得在这个Makefile
中指明每⼀个需要被编译的源程序。对于⼀个源程序⽂件⽐较多的项⽬,如果每增加或是删除⼀个⽂件都
得更新 Makefile,其⼯作量也不可⼩视!
采⽤了 wildcard 和 patsubst 两个函数后 simple 项⽬的 Makefile。可以先⽤它来编译⼀下 simple 项⽬代码以验证其功能性。需要注意的是函数的语法形式很是特别,对于我们来说只要记住其形式就⾏了。
.PHONY: clean CC = gcc RM = rm EXE = simple SRCS = $(wildcard *.c) OBJS = $(patsubst %.c,%.o,$(SRCS)) $(EXE): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) -o $@ -c $^ clean: $(RM) $(EXE) $(OBJS)
(1)addprefix函数
addprefix 函数是⽤来在给字符串中的每个⼦串前加上⼀个前缀,其形式是:$(addprefix prefix, names...)
.PHONY:all without_dir= foo.c bar.c main.c with_dir:=$(addprefix objs/, $(without_dir)) all: @echo $(with_dir)
(2)filter函数
filter 函数⽤于从⼀个字符串中,根据模式得到满⾜模式的字符串,其形式是:$(filter pattern..., text)
.PHONY: all sources = foo.c bar.c baz.s ugh.h sources := $(filter %.c %.s, $(sources)) all: @echo $(sources)
运行结果
(3)filter-out函数
filter-out 函数⽤于从⼀个字符串中根据模式滤除⼀部分字符串,其形式是:$(filter-out pattern..., text)
(4)patsubst函数(☆☆☆)
patsubst 函数是⽤来进⾏字符串替换的,其形式是:$(patsubst pattern, replacement, text)
.PHONY:all mixed=foo.c bar.c main.o objects:=$(patsubst %.c, %.o, $(mixed)) all: @echo $(objects)
(5)strip
strip 函数⽤于去除变量中的多余的空格,其形式是:$(strip string)
(6)wildcard函数(☆☆☆)
wildcard 是通配符函数,通过它可以得到我们所需的⽂件,这个函数类似我们在 Windows 或Linux 命
令⾏中的“*”。其形式是:$(wildcard pattern)
.PHONY:all SRC=$(wildcard *.c) all: @echo "SRC = $(SRC)"
标签:foo,simple,Makefile,make,makefile,echo,学习,main From: https://www.cnblogs.com/gunancheng/p/18344244