makefile的作用
用来进行条件编译来实现对工程编译的优化
基本规则
TARGET...:DEPENDENCE ...
COMMAND
...
- 目标:target程序产生的文件,如可执行那个文件和目标文件;目标也可以是要执行的动作,如:“clean”
-
依赖:dependence是用来产生目标的输入文件,一个目标通常依赖于多个文件。
-
命令:command是make执行的动作,一个target可以有多个命令。
Attention:每个命令必须只占一行,而且必须要以TAB键开头(固定格式)
makefile的核心内容
如果dependence中有一个或多个文件更新的话,command就要执行
make的工作方式
- 找到文件“Makefile”或者“makefile”
- 最终目标为第一个目标(也就是生成main)
- 如果main文件不存在或者是有.o文件有更新,那么它会执行后面的指令来生产main文件
- 看看哪个.o更新了,找到相应的target的command执行得到.o文件,最后执行终极目标生成main。
伪目标
".PHONY"它的作用主要是防止文件名和自己的指令名重合,如果重合只执行为目标的命令。
.PHONY:clean
由例子来说明Makefile
#这是一个最简陋的makefile的表达
main:main.o add.o minus.o
#首先就是查看下面的目标有没有生成,生成之后最后将所有生成main工程的.o文件找到(main.o add.o subtract.o)生成main工程。
gcc main.o add.o minus.o -o main
main.o:main.c add.h minus.h #首先将头文件和c文件列出来
gcc -c main.c -o main.o #每一个.o文件都需要编译生成
add.o:add.c add.h
gcc -c add.c -o add.o
minus.o: minus.c minus.h
gcc -c minus.c -o minus.o
.PHONY:clean #伪指令防止重名
clean:
rm -f main main.o add.o minus.o #对工程进行清理
上面的makefile是最复杂的那种,此处说的复杂是需要每次手动加入相应的新文件和它们的依赖。不可取。
三个自动化变量
-
$@ 规则的目标文件名
-
$< 规则的第一个依赖文件名
-
$^ 规则的所有依赖文件列表
makefile使用变量
#定义变量
object=main.o add.o minus.o #脚本语言不需要变量类型
main:$(object) #在使用变量之前需要加$(变量名)
Makefile自动推导
GNU的make命令很厉害,可以根据要生成的.o文件的名字找到相应的c文件,并且也会自己推倒命令。所以我们可以将上面的一些命令去掉,剩下就是需要包含的一些头文件。
#这个是自动推导指令的makefile公式
object=main.o add.o minus.o
main:$(object)
gcc $(object) -o main
main.o: add.h minus.h #只要头文件
add.o: add.h
minus.o:minus.h
.PHONY:clean #伪指令防止重名
clean:
rm -f main main.o add.o minus.o
但是每一次创建一个新的文件之后还是要将新的文件加到object里面,最后我们可以使用一些好用的Makefile函数
Makefile函数
- wildcard函数 :当前目录下匹配模式的文件,因为makefile会将通配符当成一个宏定义,也就是想要获取文件的时候就不能使用通配符,但是我们可以通过wildcard函数,使用通配符 例如:src=$(wildcard *.c) 就是将所有的文件夹内部的c文件获取给src
- notdir函数 :去除路径 例如:$(notdir $src)
- patsubst函数 :模式匹配替换 可以将搜索到的.c文件替换成.o文件 例如:$(patsubst%.c,%.o,$src)
- 等价于$(src:.c=.o)
- shell函数 : 执行shell命令 例如:$(shell ls –d */)
ELF=main
CC=gcc
src=$(wildcard *.c) #通过wildcard找到所有的
objects=$(src:.c=.o) #找到src里面所有的c文件所对应的.o文件
$(ELF):$(objects)
$(CC) $^ -o $@
$(objects):
clean:
rm -f $(objects) $(ELF)
最后一个完整版的makefile,有许多的IDE都有专门帮你生成makefile的文件的功能,像是Qt的Qmake,你打开它们内部生成的一些makefile或者是.mk文件,你就会发现,人家写的很清晰(变量用的规范,好看)
为了好看,也为了学习,我就自己也模仿了一个
CXX = g++
CXXFLAG = -o2 -g -Wall
LDFLAGS = #这里一般写一些库的路径,可以配合pkg-config
SRC += $(wildcard *.cpp)
#按道理讲人家会都把你添加到工程的.o文件罗列在这里,但是我比较懒就用函数了
OBJS = $(patsubst %.cpp,%.o,$(SRC)) #之前这里的SRC写成小写的了,导致错误
LIBS += -lpthread \
-lboost_system
HEADER_DIR = -I.
TARGET = myserver
$(TARGET):$(OBJS)
$(CXX) $(OBJS) $(HEADER_DIR) -o $(TARGET) $(LIBS)
%.o:%.cpp
$(CXX) -c $< -o $@ $(LIBS)
$(OBJS): #关键不要忘记写了 , 不然会出现问题生成不了OBJ
.PHONY:clean
clean:
rm -f $(OBJ) $(TARGET)
遇到一个bug就是居然找不到依赖也就是编译的时候使用$^找不到,后来发现是伪指令声明上面没有加$(OBJS):,其实只是会写也不太懂具体是为什么会导致这个错误,总之要记一下。