Makefile 简单工程篇
前面介绍了Makefile的基本语法以及简单使用。这次我们考虑怎么编写一个简单工程的Makefile。
假设我们的工程目录结构为:
|–src
| — main.cpp
| – foo1.cpp
| – foo2.cpp
|–include
| — foo1.h
| — foo2.h
|–obj
|–bin
我们希望编译生成的.o目标文件放在obj目录下,生成的最终可执行文件放在bin目录下。
在不使用Makefile的情况下我们需要使用以下几条指令
g++ -c src/foo2.cpp -o obj/foo2.o -I ./include
g++ -c src/foo1.cpp -o obj/foo1.o -I ./include
g++ -c src/main.cpp -o obj/main.o -I ./include
g++ ./obj/foo2.o ./obj/foo1.o ./obj/main.o -o ./bin/main
清除生成的文件需要以下几条指令:
rm ./obj/*
rm ./bin/*
这是只有三个源文件的情况下,如果源文件更多,我们将需要大量指令来编译,你能想象一个工程几百个源文件的情况吗。因此,Makefile的作用就体现出来了
这里先给出我写的一个简单Makefile,稍后在一步一步解释。
#!Makefile
DIR_INC = ./include
DIR_SRC = ./src
DIR_OBJ = ./obj
DIR_BIN = ./bin
SRC = $(wildcard $(DIR_SRC)/*.cpp)
OBJ = $(patsubst $(DIR_SRC)/%.cpp, $(DIR_OBJ)/%.o, $(SRC))
TARGET = main
CC = g++
main: $(OBJ)
$(CC) $(OBJ) -o $(DIR_BIN)/$(TARGET)
$(DIR_OBJ)/%.o: $(DIR_SRC)/%.cpp
$(CC) -c $< -o $@ -I $(DIR_INC)
.PHONY:clean
clean:
rm $(DIR_OBJ)/*
rm $(DIR_BIN)/*
前面5行,定义了四个变量,分别对应四个目录
第7定义了变量SRC,这里使用了一个Makefile内置函数wildcard,作用为展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。
这里展开结果为: SRC = ./src/main.cpp ./src/foo1.cpp ./src/foo2.cpp
第8行定义了变量OBJ,使用了字符串替换函数patsubst,替换后的结果为:OBJ = ./obj/main.o ./obj/foo1.o ./obj/foo2.o
此时我们已经准备好了g++所需的所有参数,怎么使用他们呢?
我们先关注第18,19两行,直接将对应的变量替换进去,应该能看懂这两行在干什么。
obj/%.o: src/%.cpp
g++ -c $< -o $@ -I include #其中$<变量代表src/%.cpp,$@变量代表obj/%.o
obj/%.o: src/%.cpp
g++ -c src/%.cpp -o obj/%.o -I include
在将%替换为main, foo1, foo2, 发现就是我们前面将对应源文件生成相应目标文件的前3条指令。
g++ -c src/foo2.cpp -o obj/foo2.o -I ./include
g++ -c src/foo1.cpp -o obj/foo1.o -I ./include
g++ -c src/main.cpp -o obj/main.o -I ./include
因此,这两行的内容不难理解,就是说当我们需要obj/%.o 文件的时候,将使用下面这条指令来生成。
再来关注第15,16行,将所有变量替换后发现就是上面的最后一条指令。
g++ ./obj/foo2.o ./obj/foo1.o ./obj/main.o -o ./bin/main
最后总结一下整个Makefile做的工作,我们告诉Makefile我们的最终目标是main,它需要的文件为$(OBJ),使用的命令为
$(CC) $(OBJ) -o $(DIR_BIN)/$(TARGET)
而$(OBJ)文件我们去哪找呢?
$(DIR_OBJ)/%.o: $(DIR_SRC)/%.cpp
$(CC) -c $< -o $@ -I $(DIR_INC)
这两行告诉Makefile对应的.o文件怎么生成。