本篇博客主要试介绍基础的Makefile使用,常用的变量和函数。看完后能理解makefile书写原理,自己写一些简单的makefile。
本专栏知识点是通过<零声教育>的音视频流媒体高级开发课程进行系统学习,梳理总结后写下文章,对音视频相关内容感兴趣的读者,可以点击观看课程网址:零声教育
⼀个规则是由⽬标(targets)、先决条件(prerequisites)以及命令(commands)所组成。
规则的功能就是指明 make 什么时候以及如何来为我们重新创建⽬标。
规则的语法:
targets : prerequisites
command
当没有指明具体的⽬标是什么时,那么 make 以 Makefile ⽂件中定义的第⼀个⽬标作为这次运⾏的⽬标。
对于make ⼯具,⼀个⽂件是否改动不是看⽂件⼤⼩,⽽是其时间戳。
如果先决条件中相关的⽂件的时间戳⼤于⽬标的时间戳,即先决条件中的⽂件⽐⽬标更新,则知道有变化,那么需要运⾏规则当中的命令重新构建⽬标。
在现实中也难免存在所定义的⽬标与所存在的⽂件是同名的,Makefile 利用假⽬标来处理。
采用==.PHONY== 关键字来定义。
一、变量
1.1 自动变量
$@⽤于表示⼀个规则中的⽬标。当我们的⼀个规则中有多个⽬标时, $@所指的是其中任何造成命令被运⾏的⽬标。
$^则表示的是规则中的所有先择条件。
$<表示的是规则中的第⼀个先决条件。
Makefile
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
$(CC) -o $@ $^
main.o: main.c
$(CC) -o $@ -c $^
foo.o: foo.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
1.2 特殊变量
$(MAKE) :make
$(MAKECMDGOALS):⽤户输⼊的⽬标,当我们只运⾏ make 命令时,第⼀个⽬标将成为缺省⽬标,即 all ⽬标,但 MAKECMDGOALS 仍然是空,⽽不是all
1.3 变量类别
递归扩展变量:只⽤⼀个“=”符号定义
简单扩展变量:⽤“:=”操作符定义,make 只对其进⾏⼀次扫描和替换
条件赋值变量:条件赋值符“?=”定义,当变量以前没有定义时,就定义它并且将左边的值赋值给它,如果已经定义了那么就不再改变其值。
斌值的同时完成后缀替换操作:
Makefile
.PHONY: all
foo = a.o b.o c.o
bar := $(foo:.o=.c)
all:
@echo "bar = $(bar)"
执⾏
$make
bar = a.c b.c c.c
1.4 函数
addprefix 函数:
⽤来在给字符串中的每个⼦串前加上⼀个前缀,其形式是:
$(addprefix prefix, names...)
示例:
Makefile
.PHONY: all
without_dir = foo.c bar.c main.o
with_dir := $(addprefix objs/, $(without_dir))
all:
@echo $(with_dir)
执⾏
$make
objs/foo.c objs/bar.c objs/main.o
filter 函数:
⽤于从⼀个字符串中,根据模式得到满⾜模式的字符串,其形式是:
$(filter pattern..., text)
Makefile
.PHONY: all
sources = foo.c bar.c baz.s ugh.h
sources := $(filter %.c %.s, $(sources))
all:
@echo $(sources)
执⾏
$make
foo.c bar.c baz.s
filter-out 函数:
⽤于从⼀个字符串中根据模式滤除⼀部分字符串,其形式是:
$(filter-out pattern..., text)
result = $(filter-out main%.o, main1.o foo.o main2.o bar.o)
patsubst 函数:
是⽤来进⾏字符串替换的,其形式是:
$(patsubst pattern, replacement, text)
objects := $(patsubst %.c, %.o, $(mixed))
strip 函数:
⽤于去除变量中的多余的空格,其形式是:
$(strip string)
stripped := $(strip $(original))
wildcard (通配符)函数:
用于得到我们所需的⽂件,其形式是:
$(wildcard pattern)
SRCS = $(wildcard *.c)
二、复杂项目Makefile
2.1 创建目录
Makefile
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
DIRS = objs exes
all: $(DIRS)
$(DIRS):
$(MKDIR) $@
clean:
$(RM) $(RMFLAGS) $(DIRS)
make 之后,会生成 exes 和objs两个文件
make clean 之后,会删除
2.2 增加头文件
在complicated
项⽬的Makefile
中加⼊对于源程序进⾏编译的部分。
假设makefile
当前目录已有以下3个文件:foo.h
,foo.c
和main.c
。现在需要利用这些文件编译出可执行文件complicated
。
foo.h
#ifndef __FOO_H
#define __FOO_H
void foo ();
#endif
foo.c
#include <stdio.h>
#include “foo.h”
void foo ()
{
printf (“This is foo ()!\n”);
}
main.c
#include “foo.h”
int main ()
{
foo ();
return 0;
}
Makefile文件如下:
Makefile
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
EXE = complicated
DIRS = objs exes
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
all: $(DIRS) $(EXE)
$(DIRS):
$(MKDIR) $@
$(EXE): $(OBJS)
$(CC) -o $@ $^
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)
先利用.c文件编译为.o文件,之后生成complicated。
执⾏
$make
mkdir objs
mkdir exes
cc -o foo.o -c foo.c
cc -o main.o -c main.c
cc -o complicated foo.o main.o
$ls
Makefile complicated exes foo.c foo.h foo.o main.c main.o objs
$./complicated
This is foo ()!
$make clean
rm -fr objs exes complicated foo.o main.o
$ls
Makefile foo.c foo.h main.c
2.3 将文件放到目录里
利用addprefix()函数,将⽬标⽂件或是可执⾏程序分别放⼊所创建的 objs 和 exes ⽬录中。
Makefile
DIR_OBJS = objs
DIR_EXES = exes
DIRS = $(DIR_OBJS) $(DIR_EXES)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
利用 addprefix
函数的运⽤为每⼀个⽬标⽂件加上“objs/”前缀外