前言:编译大的工程会花费很长的时间,这个章节需要实际练习,多多练习即可,复杂但不难。
1、工程管理器,顾名思义,是指管理较多的文件。
2、Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量。
3、作用
Make将只编译改动的代码文件,而不用完全编译。
根据文件时间戳自动发现更新过的文件。
读入Makefile 文件编辑内容来执行编译工作。
Makefile是Make读入的唯一配置文件
target:dependency_files
command
例如:
hello.o:hello.c hello.h
gcc –c hello.c –o hello.o
创建f1.c f2.c main.c head.h文件,创建makefile文件,分别在文件中添加内容:
f1.c
#include
void print1(){
printf("this is print1\n");
}
f2.c
#include
void print2(){
printf("this is print2\n");
}
head.h
void print1();
void print2();
makefile
test:f1.o f2.o main.o
gcc f1.o f2.o main.o -o test
f1.o:f1.c
gcc -c f1.c -o f1.o
f2.o:f2.c
gcc -c f2.c -o f2.o
main.o:main.c
gcc -c main.c -o main.o
.PHONY:clean
clean:
rm *.o test
伪目标的作用:.PHONY是一个伪目标,可以防止在Makefile中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突,另一种是提交执行makefile时的效率。
执行编译命令:make(后面会讲具体make使用)
删除.o文件命令:make clean
最后看运行结果
注意:关于gcc的命令,点击gcc基本用法跳转查看相关章节gcc基本用法
在使用makefile文件编写命令时,不少命令不够直观,不能一下读懂语义,使用变量的目的就是使makefile编写更流畅,更易懂,可复制到其他项目中去。包括:
a.递归展开方式VAR=var
b.简单方式VAR:=var
变量使用$(VAR),用”$”则用”$$”来表示、类似于编程语言中的宏。
使用变量,修改上面案例的makefile
缺点:不能对该变量进行循环扩展,例如 CFLAGS = $(CFLAGS) -O 会造成死循环
例:请问foo的值为?
foo = $(bar)
bar = $(ugh)
ugh = Huh?
test2:
@echo $(foo)
答:代码复制到makefile进行make编译。foo去找bar,bar去找ugh,ugh=Huh?,所以结果:
用这种方式定义的变量,会在变量的定义点,按照被引用的变量的当前值进行展开。这种定义变量的方式更适合在大的编程项目中使用,因为它更像我们一般的编程语言。
例:请问x、y的值为?
m:= mm
x:= $(m)
y:= $(x) bar
x:= later
test2:
@echo $(x)$(y)
答:第一个x是mm,第二个x是later。结果:
例:FOO的值是?
dir:= /foo/bar
FOO?= bar
test:
@echo $(FOO)
答:空。含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做。结果:
如果上述代码改成如下,输出结果为abc。
dir:= /foo/bar
FOO:= abc
FOO?= bar
test:
@echo $(FOO)
你可以通过+=为已定义的变量添加新的值
main=hello.o hello-1.o
main+=hello-2.o
test:
@echo $(main)
输出结果为:hello.o hello-1.o hello-2.o
AR 库文件维护程序的名称,默认值为ar。
AS 汇编程序的名称,默认值为as。
CC C编译器的名称,默认值为cc。
CPP C预编译器的名称,默认值为$(CC) –E。
CXX C++编译器的名称,默认值为g++。
FC FORTRAN编译器的名称,默认值为f77
RM 文件删除程序的名称,默认值为rm -f
ARFLAGS 库文件维护程序的选项,无默认值。
ASFLAGS 汇编程序的选项,无默认值。
CFLAGS C编译器的选项,无默认值。
CPPFLAGS C预编译的选项,无默认值。
CXXFLAGS C++编译器的选项,无默认值。
FFLAGS FORTRAN编译器的选项,无默认值。
通过echo命令可以进行查看打印,上述命令已在前面的例子中演示过。
test:
@echo $(AR)
$* 不包含扩展名的目标文件名称 //hello.o 可以用$*.c 代替目标文件
$+= 所有的依赖文件,以空格分开,并以出现的先后为序,可能 包含重复的依赖文件
$< 第一个依赖文件的名称 //test:f1.o f2.o main.o $<代表f1.o
$? 所有时间戳比目标文件晚的的依赖文件,并以空格分开
$@ 目标文件的完整名称 //相当于test
$^ 所有不重复的目标依赖文件,以空格分开 //相当于f1.o f2.o main.o
$% 如果目标是归档成员,则该变量表示目标的归档成员名称
根据上文案例利用自动变量修改makefile:
OBJS = f1.o f2.o main.o
CC = gcc
CFLAGS = -c
test:$(OBJS)
$(CC) $^ -o $@
f1.o:f1.c
$(CC) $(CFLAGS) $< -o $@
f2.o:f2.c
$(CC) $(CFLAGS) $< -o $@
main.o:main.c
$(CC) $(CFLAGS) $< -o $@
.PHONY:clean
clean:
rm *.o test
隐含规则1:编译C程序的隐含规则
“
$(CC) -c $(CPPFLAGS) $(CFLAGS)
隐含规则2:链接Object恩建的隐含规则
“
$(CC) $(LDFLAGS) .o
隐含规则3:$(LOADLIBES) $(LDLIBS)
这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。
如下规则:
x:x.o y.o z.o并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。
规则1举例,还是上面的例子:
OBJS = f1.o f2.o main.o
CC = gcc
CFLAGS = -c
test:$(OBJS)
$(CC) $^ -o $@
f1.o:f1.c
# $(CC) $(CFLAGS) $< -o $@ //#相当于注释掉了,去掉3天命令, make也不会报错
f2.o:f2.c
# $(CC) $(CFLAGS) $< -o $@
main.o:main.c
# $(CC) $(CFLAGS) $< -o $@
.PHONY:clean
clean:
rm *.o test
规则3举例,还是上面的例子:
OBJS = f1.o f2.o main.o
CC = gcc
CFLAGS = -c
f1:f1.o f2.o main.o
# .o的目标依赖目标会自动推导为“.c”并且其生成命令是$(CC) -c $(CPPFLAGS) $(CFLAGS)
# 相当于cc x.o y.o z.o -o x
.PHONY:clean
clean:
rm *.o test
VPATH:虚路径
举例,还是上面的例子,我们建立文件路径,把之前的.c和.h放入路径内
老方法执行
CC = gcc
CFLAGS = -c -I include #-I include包含head.h的路径
f1:src1/f1.o src2/f2.o main/main.o #这是写路径的方式
.PHONY:clean
clean:
find ./ -name "*.o" -exec rm {} \;;rm f1
VPATH执行(效率更高)
CC = gcc
CFLAGS = -c -I include
VPATH=src1:src2:main
f1:f1.o f2.o main.o
.PHONY:clean
clean:
find ./ -name "*.o" -exec rm {} \;;rm f1
# 正则表达式:找一下当前目录下哪里有.o,然后执行一下rm,然后把.o删除,然后把f1删除
案例:我们沿用之前的例子,这次在f1、f2、include、main、obj文件夹下,多了一个makefile文件
#f1 makefile
../$(OBJS_DIR)/f1.o:f1.c
$(CC) -c $^ -o $@
#f2 makefile
../$(OBJS_DIR)/f2.o:f2.c
$(CC) -c $^ -o $@
#main makefile
../$(OBJS_DIR)/main.o:main.c
$(CC) -c $^ -o $@
#obj makefile
../$(BIN_DIR)/$(BIN):$(OBJS)
$(CC) -o $@ $^
#根目录下的makefile
CC = gcc
SUBDIRS=f1 \
f2 \
main \
obj
OBJS = f1.o f2.o main.o
BIN=myapp
OBJS_DIR=obj
BIN_DIR=bin
export CC OBJS BIN OBJS_DIR BIN_DIR
#export是把上面用到的变量,传给下面子目录makefile使用
all:CHECK_DIR $(SUBDIRS) #
CHECK_DIR:
mkdir -p $(BIN_DIR) #生成bin目录
$(SUBDIRS):ECHO
make -C $@ #执行指定目录下的makefile
ECHO:
@echo $(SUBDIRS) #打印
@echo begin compile
clean:
@$(RM) $(OBJS_DIR)/*.o #把OBJS_DIR下的.o删除
@rm -rf $(BIN_DIR) #把mayapp删除
最终效果: