源文件首先要编译成中间目标文件(Object File),windows下生成.obj文件,linux环境下生成.o文件;再把大量的Object File链接成执行文件(exe)。当中间目标文件太多时,可以将其打包,windows下称之为库文件(Library File).lib文件,lunix下称Archive File,.a文件。
makefile定义一系列规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译等。使用makefile自动化编译极大提高软件开发的效率。
编译和链接某个工程中多个文件的规则是:
- 如果工程没有编译过,那么所有源文件(.c/.cpp等)都要编译并被链接;
- 如果工程的某几个源文件被修改,那么只编译被修改的源文件并链接目标程序;
- 如果工程的头文件被修改,那么需要编译引用了这几个头文件的源文件,并链接目标程序。
编写makefile文件语法格式:
目标:依赖
命令
- 目标:通常是要生成的文件名称,也可以是要执行的动作名称,如“clean”。
- 依赖:生成目标所需要的文件或中间过程生成的目标。
- 命令:通过依赖,执行命令生成目标。命令前必须是一个Tab,不能是空格。
makefile工作原理
- 检查依赖:在生成目标之前,make会检查规则中的依赖是否存在。如果不存在则寻找是否有规则用来生成该依赖文件。
- 检查更新:如果依赖存在,make会检查依赖的时间是否比目标的时间新。如果依赖的时间更新,或无目标文件,则执行命令更新/生成目标。否则不需执行命令。
- 执行命令:如果目标需要更新/生成,则按规则中的命令执行操作。
编写makefile文件步骤
1、在bash下创建makefile文件
vi makefile
2、根据语法格式编写相应文件
all:
gcc hello.c -o hello
目标:all
依赖:空
命令:gcc hello.c -o hello
C++代码示例:
hello:hello.cc
g++ hello.cc -o hello
目标:hello
依赖:hello.cc
命令:g++ hello.cc -o hello
注:在linux中.cpp等价于.cc等价于.cxx;g++编译C++,gcc编译C语言。
若想保存中间文件,则可以替换成
all:hello.o
gcc hello.o -o hello
hello.o:hello.c
gcc -c hello.c
或者
all:hello.o
gcc hello.c -o hello
hello.o:hello.c
gcc -c hello.c -o hello.o
执行顺序是从下而上,先生成hello.o,再生成hello.exe
3、执行makefile脚本
命令:make 目标文件/make all。若不指定目标,默认执行makefile/Makefile文件中第一个没有通配符的目标所对应的命令。例如:
all:hello.o
gcc hello.o -o hello
hello.o:hello.c
gcc -c hello.c -o hello.o
clear:
rm -rf *.o hello
这里,make与make all功能一致。
此时执行make clear,就会执行 rm -rf *.o hello命令。
此时若目录下存在与clear同名的文件,则在执行make clear时会报错。
make -f filename:指定makefile文件名称,可以指定多个文件名称。
make -C dir:执行指定路径的makefile文件。
make -n:打印makefile文件中的命令但不执行。-f指定文件名。
make -s:执行makefile文件中的命令但不打印。-f指定文件名。
伪目标
解决方法:使用伪目标
伪目标格式:
.PHONY:目标
all:hello.o
gcc hello.o -o hello
hello.o:hello.c
gcc -c hello.c -o hello.o
.PYONY:claer
clear:
rm -rf *.o hello
执行make clear可以成功。伪目标写在clear规则下面也可以!
Makefile变量
变量可以用在目标、依赖、命令中;
变量赋值方式有:“:= , = , ?= , += ”,分别代表立即赋值,延迟赋值,判断赋值,追加赋值。
变量的引用是通过 $(变量名) 来实现的。
1、:=立即赋值示例:
var1:=hello
var2:=$(var1) world
var1:=hi
all:
echo $(var2)
运行结果:
echo hello world
hello world
2、=延迟赋值示例:
var1=hello
var2=$(var1) world
var1=hi
all:
echo $(var2)
运行结果:
echo hi world
hi world
3、?=判断赋值示例:
var1:=hello
var2=$(var1) world
var1?=hi
all:
echo $(var2)
判断赋值首先判断变量是否已经赋值,若有则使用之前赋值结果;若无则使用本次赋值结果。示例运行结果:
echo hello world
hello world
将示例中的立即赋值注释掉,判断赋值就会生效
#var1:=hello
var2:=$(var1) world
var1?=hi
all:
echo $(var2)
运行结果:
echo hi world
hi world
4、 +=追加赋值示例:
var1:=hello
var2=$(var1) world
var1+=hi
var1+=hello
all:
echo $(var2)
+=追加赋值时在追加处用空格隔开,运行结果:
echo hello hi hello world
hello hi hello world
若赋值很长,可以用换行符‘\’
var1:=hello\
hi\
hello
var2=$(var1) world
all:
echo $(var2)
运行结果与上面相同,在换行符处用空格隔开:
echo hello hi hello world
hello hi hello world
makefile自动化变量
为了简化代码,使用一组特殊变量来表示文件名,在执行规则时自动替换成相应的值。
$@:当前规则的目标文件名
$<:当前规则的第一个依赖文件名
$^:当前规则的所有依赖文件名(去重)
$+:当前规则的所有依赖文件名
$*:当前规则的目标文件名去除后缀
例如:
all:hello.o main.o
gcc hello.o main.o -o hello
hello.o:hello.c
gcc -c hello.c -o hello.o
main.o:main.c
gcc -c main.c -o main.o
.PYONY:claer
clear:
rm -rf *.o hello
使用变量var替换hello.o和main.o,添加依赖文件时可通过仅修改变量var即可实现。
使用$^替换所有依赖文件名;
使用替换符%和特殊变量$<、$@替换目标和依赖,简化代码;
var:=hello.o main.o
all:$(var)
gcc $^ -o hello
%.o:%.c
gcc -c $< -o $@
.PYONY:claer
clear:
rm -rf *.o hello
注意:%是带着寻找hello.o和main.o的目的去执行的。
makefile函数
假设当前路径下存在一个a.c文件和一个test文件夹,test文件夹中存在一个b.c文件。
1、wildcard函数
格式:$(wildcard 文件路径)
显示指定文件路径
var:=$(wildcard ./*.c ./test/*.c)
all:
@echo $(var)
运行结果:
./a.c ./test/b.c
2、notdir函数
格式:$(notdir 文件路径)
显示指定文件名(去掉路径)
var:=$(wildcard ./*.c ./test/*.c)
var1=$(notdir &(var))
all:
@echo $(var1)
运行结果:
a.c b.c
3、dir函数
格式:$(dir 文件路径)
显示指定文件路径
var:=$(wildcard ./*.c ./test/*.c)
var2=$(dir $(var))
all:
@echo $(var2)
运行结果:
./ ./test/
4、patsubst函数
格式:$(patsubst 源文件,目标文件,文件列表)
替换文件名后缀
var:=$(wildcard ./*.c ./test/*.c)
var1=$(notdir &(var))
var3=$(patsubst %.c,%.s,$(var1))
all:
@echo $(var3)
运行结果:
a.s b.s
5、foreach函数
格式:$(foreach 变量,参数列表,表达式)
将参数列表中的参数作为变量,放到表达式中执行。
var:=$(wildcard ./*.c ./test/*.c)
var1=$(notdir &(var))
var2=$(dir $(var))
var4=$(foreach n,$(var2),$(wildcard $(n)*.c))
all:
@echo $(var4)
运行结果:
./a.c ./test/b.c