文章目录
- 一、demo的目录结构
- 二、使用Makefile生成可执行文件
- 1、简单版本的Makefile
- 2、使用变量改进Makefile
- 3、使用自动变量继续改进Makefile
- 4、使用 %.o:%.c 样式继续改进Makefile
- 5、使用模块化继续改进Makefile
- 三、Makefile中一些关键语法
- 1、wildcard
- 2、notdir
- 3、patsubst
- 4、`@、$@、$^、$<`
- 四、一个实例详解
Makefile是一个神奇的东西,有了它只需一个make命令就可以让源文件按你的规则编译成你所想要的程序。非常简单,方便。对于Keil,VS等IDE,一般只需点一下绿色的三角按钮,就可以完成编译。但具体内部是怎么实现编译的?改动文件后如何只编译改动的文件?学完makefile就可以掌握这些东西,从而对系统编程会有更深层次的理解。
一、demo的目录结构
二、使用Makefile生成可执行文件
1、简单版本的Makefile
//例行公式,表示在bin目录下生成可执行文件
all : ../bin/make_test
//可执行文件(make_test)的生成依赖于多个目标文件(.o),'\'表示转义字符
../bin/make_test : ./main.o ./sub1/dummy1.o \
./sub1/dummy2.o ./sub2/dummy3.o \
./sub2/dummy4.o
//生成目标文件(.o)
g++ -o ../bin/make_test ./main.o ./sub1/dummy1.o \
./sub1/dummy2.o ./sub2/dummy3.o \
./sub2/dummy4.o
//目标文件(.o)的生成依赖于源文件(.cpp)
./main.o : ../src/main.cpp
g++ -o ./main.o -c ../src/main.cpp -I ../inc
./sub1/dummy1.o : ../src/sub1/dummy1.cpp
g++ -o ./sub1/dummy1.o -c ../src/sub1/dummy1.cpp -I ../inc
./sub1/dummy2.o : ../src/sub1/dummy2.cpp
g++ -o ./sub1/dummy2.o -c ../src/sub1/dummy2.cpp -I ../inc
./sub2/dummy3.o : ../src/sub2/dummy3.cpp
g++ -o ./sub2/dummy3.o -c ../src/sub2/dummy3.cpp -I ../inc
./sub2/dummy4.o : ../src/sub2/dummy4.cpp
g++ -o ./sub2/dummy4.o -c ../src/sub2/dummy4.cpp -I ../inc
//清理目标文件(.o)和可执行文件
clean:
rm -rf ./*.o ./sub1/* ./sub2/* ../bin/*
2、使用变量改进Makefile
all : ../bin/make_test
//':='表示不跟踪变量,一般用于固定格式的命令或变量
RM := rm -rf
//'='表示跟踪变量,可以追加和修改,类似于C++中的引用
OBJS = ./main.o ./sub1/dummy1.o \
./sub1/dummy2.o ./sub2/dummy3.o \
./sub2/dummy4.o
//$(val)表示使用变量
../bin/make_test : $(OBJS)
g++ -o ../bin/make_test $(OBJS)
./main.o : ../src/main.cpp
g++ -o ./main.o -c ../src/main.cpp -I ../inc
./sub1/dummy1.o : ../src/sub1/dummy1.cpp
g++ -o ./sub1/dummy1.o -c ../src/sub1/dummy1.cpp -I ../inc
./sub1/dummy2.o : ../src/sub1/dummy2.cpp
g++ -o ./sub1/dummy2.o -c ../src/sub1/dummy2.cpp -I ../inc
./sub2/dummy3.o : ../src/sub2/dummy3.cpp
g++ -o ./sub2/dummy3.o -c ../src/sub2/dummy3.cpp -I ../inc
./sub2/dummy4.o : ../src/sub2/dummy4.cpp
g++ -o ./sub2/dummy4.o -c ../src/sub2/dummy4.cpp -I ../inc
clean:
$(RM) $(OBJS) ../bin/*
3、使用自动变量继续改进Makefile
all : ../bin/make_test
RM := rm -rf
OBJS = ./main.o ./sub1/dummy1.o \
./sub1/dummy2.o ./sub2/dummy3.o \
./sub2/dummy4.o
../bin/make_test : $(OBJS)
g++ -o $(@) $(^)
./main.o : ../src/main.cpp
g++ -o $(@) -c $(<) -I ../inc
./sub1/dummy1.o : ../src/sub1/dummy1.cpp
g++ -o $(@) -c $(<) -I ../inc
./sub1/dummy2.o : ../src/sub1/dummy2.cpp
g++ -o $(@) -c $(<) -I ../inc
./sub2/dummy3.o : ../src/sub2/dummy3.cpp
g++ -o $(@) -c $(<) -I ../inc
./sub2/dummy4.o : ../src/sub2/dummy4.cpp
g++ -o $(@) -c $(<) -I ../inc
clean:
$(RM) $(OBJS) ../bin/*
4、使用 %.o:%.c 样式继续改进Makefile
all : ../bin/make_test
RM := rm -rf
OBJS = ./main.o ./sub1/dummy1.o \
./sub1/dummy2.o ./sub2/dummy3.o \
./sub2/dummy4.o
../bin/make_test : $(OBJS)
g++ -o $(@) $(^)
./%.o : ../src/%.cpp
g++ -o $(@) -c $(<) -I ../inc
./sub1/%.o : ../src/sub1/%.cpp
g++ -o $(@) -c $(<) -I ../inc
./sub2/%.o : ../src/sub2/%.cpp
g++ -o $(@) -c $(<) -I ../inc
clean:
$(RM) $(OBJS) ../bin/*
5、使用模块化继续改进Makefile
// ./sub1/sub.mk
OBJS += ./sub1/dummy1.o ./sub1/dummy2.o
./sub1/%.o : ../src/sub1/%.cpp
g++ -o $(@) -c $(<) -I ../inc
// ./sub2/sub.mk
OBJS += ./sub2/dummy3.o ./sub2/dummy4.o
./sub2/%.o : ../src/sub2/%.cpp
g++ -o $(@) -c $(<) -I ../inc
// ./sub.mk
OBJS += ./main.o \
./%.o : ../src/%.cpp
g++ -o $(@) -c $(<) -I ../inc
// Makefile终极版本
RM := rm -rf
-include ./sub.mk
-include ./sub1/sub.mk
-include ./sub2/sub.mk
all : ../bin/make_test
../bin/make_test : $(OBJS)
@echo 'Building target : $@'
@echo 'Invoking : GCC C++ Linker'
g++ -o $(@) $(^)
@echo 'Finishing building target: $@'
@echo ' '
//显式指定某个目标为伪目标 ,表明不论是否有 clean 这个文件, make clean 中 make 都会将 clean 当作伪目标。
.PHONY : clean
clean:
-$(RM) $(OBJS) ../bin/*
三、Makefile中一些关键语法
Makefile里的函数使用,和取变量的值类似,是以一个‘$’开始,然后是一个括号里面是函数名和需要的参数列表,多个变量用逗号隔开,像这样:
return = $(functionname arg1,arg2,arg3...)。
1、wildcard
SRC = $(wildcard *.c ./foo/*.c)
搜索当前目录及./foo/下所有以.c结尾的文件,生成一个以空格间隔的文件名列表,并赋值给SRC.当前目录文件只有文件名,子目录下的文件名包含路径信息,比如./foor/bar.c。
2、notdir
SRC = $(notdir wildcard)
去除所有的目录信息,SRC里的文件名列表将只有文件名。
3、patsubst
OBJ = $(patsubst %.c %.o $(SRC))
patsubst是patten substitude的缩写,匹配替代的意思。这句是在SRC中找到所有.c 结尾的文件,然后把所有的.c换成.o。
4、@、$@、$^、$<
// target:表示我们的目标,components:表示依赖对象,rule:表示规则
target:components
rule
@:表示不显示命令本身
$@:代表目标文件(target)
$^:代表所有的依赖文件(components)
$<:代表第一个依赖文件(components中最左边的那个)。
四、一个实例详解
# 定义一些变量
DIR_BIN = .
DIR_OBJ = ./obj
DIR_SRC = ./src
# 把DIR_SRC目录下所有的.CPP文件展开
SRC = $(wildcard $(DIR_SRC)/*.cpp)
# 把DIR_SRC目录下所有的.CPP替换为.o文件
OBJ = $(patsubst %.cpp,$(DIR_OBJ)/%.o,$(notdir $(SRC)))
# -g表示调试,-Wall表示让所有编译警告都显示出来,
# -std=c++11表示使用C++11版本,-pthread表示使用多线程库,
# -O3表示产生更高级别的优化
CXX_FLAG = -g -Wall -std=c++11 -pthread -O3
CC = g++
TARGET = exec
# 生成可执行文件
$(DIR_BIN)/$(TARGET) : $(OBJ)
$(CC) $(CXX_FLAG) -o $@ $^
# 表示把DIR_SRC目录下所有的.cpp文件编译成.o文件。
$(DIR_OBJ)/%.o : $(DIR_SRC)/%.cpp
if [ ! -d $(DIR_OBJ) ]; then mkdir -p $(DIR_OBJ); fi;
$(CC) $(CXX_FLAG) -c $< -o $@
# 清空所有目标文件
.PHONY : clean
clean :
-rm -rf $(DIR_OBJ)