Make和Makefile

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/daaikuaichuan/article/details/82313449

  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)



  

  
【Note】:

猜你喜欢

转载自blog.csdn.net/daaikuaichuan/article/details/82313449
今日推荐