阿里云手机网站建设,济南正规网站建设公司,北京网下载,男科医院网站建设策略前文中#xff0c;我们有多少个.c文件#xff0c;就需要build 出来多少个.o文件
假设我们的项目很大#xff0c;怎么管理这些 .c文件呢#xff1f;
这里就要学习一个make文件的编写了。
makefile 本质上是一个脚本语言
脚本语言实际上就是将一系列命令放在一起执行 mak…前文中我们有多少个.c文件就需要build 出来多少个.o文件
假设我们的项目很大怎么管理这些 .c文件呢
这里就要学习一个make文件的编写了。
makefile 本质上是一个脚本语言
脚本语言实际上就是将一系列命令放在一起执行 makefile 的用途
项目代码编译管理
节省编译项目时间
一次编译终身受益 Makefile 工作内容
makefile 会把遇见的第一条指令作为他的终极指令
但是如果开发者在前面加了关键字 ALL
就会把ALL:后面的 做为 makefile 的终极目标
因此一般我们都会写ALL
ALL:a.out make file的写法
命名只有两种一种叫做makefile 一种叫做Makefile
一个规则
目标依赖条件 一个tab缩进命令
hello:hello.cgcc hello.c -o hello 解释
目标是生成hello 这个可执行文件
依赖条件的意思是要生成hello这个目标文件需要依赖于 hello.c这个文件
然后缩进这个是语法格式要求
命令的意思是怎么使用 hello.c生成hello这个目标文件 基本原则的原文 若想生成目标 检查规则中的额依赖条件是否存在如不存在则寻找是否有规则用来生成该依赖文件。
好我们把这段话从自己刚才写的例子理解一下
上述我们写的时候 是目标是 hello 依赖条件是hello.c
我们改写一下 先从hello.c生成 hello.o
然后从hello.o生成目标文件hello hello:hello.ogcc hello.o -o hellohello.o:hello.cgcc -c hello.c -o hello.ohunandedehunandede-virtual-machine:~/day03/makefilestudy$ ls
hello.c makefile
hunandedehunandede-virtual-machine:~/day03/makefilestudy$ make
gcc -c hello.c -o hello.o
gcc hello.o -o hello
hunandedehunandede-virtual-machine:~/day03/makefilestudy$ 1.学习这个之前我们将昨天的部分整理过来并 按照g的方法手动build 一下
文件内容如下head 中存放.h文件
src 文件夹下放着所有的.cpp文件 g src/test.cpp src/addfunc.cpp src/devfunc.cpp src/mulfunc.cpp src/subfunc.cpp -o a.out -I ./head如上我们build 出来了一个 a.out文件如下是执行 a.out文件 hunandedehunandede-virtual-machine:~/day03/makefilestudy2$ ./a.out
ab 15
a-b 5
a*b 50
a/b 22. 改造成makefile 的写法 a.out:addfunc.o subfunc.o mulfunc.o devfunc.o test.og addfunc.o subfunc.o mulfunc.o devfunc.o test.o -o a.out
addfunc.o:addfunc.cppg -c addfunc.cpp -o addfunc.o -I../headsubfunc.o:subfunc.cppg -c subfunc.cpp -o subfunc.o -I../headmulfunc.o:mulfunc.cppg -c mulfunc.cpp -o mulfunc.o -I../headdevfunc.o:devfunc.cppg -c devfunc.cpp -o devfunc.o -I../headtest.o:test.cppg -c test.cpp -o test.o -I../head执行make 就会生成a.out文件然后我们运行 a.out文件
hunandedehunandede-virtual-machine:~/day03/makefilestudy2/src$ ./a.out
ab 15
a-b 5
a*b 50
a/b 2这里有一个问题我们上面为什么要将所有的.c分开写转成.o然后再使用使用所有的.o生成 a.out呢 直接 g addfunc.cpp subfunc.cpp mulfunc.cpp divfunc.cpp -o a.out 写起来不是更方便吗 原因是 当addfunc.cpp的代码有改动后重新build的时候这么一行的写法会让所有的.cpp文件编译成.o,然后将所有的.o文件再链接成 a.out文件。
实际上只有 addfunc.cpp改动了其他的都没有变化让其他的跟着重新build出来自己的.o文件是很没有必要的。因此要分开写。 回顾 g编译的四个阶段预处理头文件展开宏替换注释去掉编译检查语法错误将c语言变成汇编语言消耗时间和系统资源最多汇编将汇编语言变成 二进制指令链接数据段合并以及地址回填将函数库中相应的代码组合到目标文件中 引申出的另一个问题g是如何知道addfunc.cpp有变化了那就要看file 属性中的ctime了changed time当一旦发现ctime有变化就会重新编译了。实际上应该是说 当addfunc.cpp的ctime比addfunc.o的时间晚则会重新编译 addfunc.cpp 1.目标的时间必须晚于依赖条件的时间否则更新目录。
2.依赖条件如果不存在找寻新的规则去产生依赖。 新的问题 看起来是可以完全fix此类问题好又有一个新问题我们的需求变化了增加了一个 模余.cpp文件用来求余数的。
那么我们就要改动 makefile文件以支持,类似如下的写法
虽然也可以实现功能但是很笨拙 a.out:addfunc.o subfunc.o mulfunc.o devfunc.o moyufunc.o test.og addfunc.o subfunc.o mulfunc.o devfunc.o test.o -o a.out
addfunc.o:addfunc.cppg -c addfunc.cpp -o addfunc.o -I../headsubfunc.o:subfunc.cppg -c subfunc.cpp -o subfunc.o -I../headmulfunc.o:mulfunc.cppg -c mulfunc.cpp -o mulfunc.o -I../headdevfunc.o:devfunc.cppg -c devfunc.cpp -o devfunc.o -I../headmoyufunc.o:moyufunc.cppg -c moyufunc.cpp -o moyufunc.o -I../headtest.o:test.cppg -c test.cpp -o test.o -I../head 解决这样笨拙写法的方案是 使用两个函数类似通配符
二个函数
wildcard 和 patsubst
wildcard 通配符白搭牌的意思
patsubst 是用来匹配和替换的。 src $(wildcard *.cpp) src $(wildcard *.cpp) 匹配当前工作目录下的所有.cpp文件 将文件名组成列表赋值给变量src 也就是说在这一句执行完毕之后src addfunc.cpp subfunc.cpp mulfunc.cpp divfunc.cpp 解释 $(wildcard *.c) 整个是函数调用 wildcard 是函数名*.c 表示参数 整个函数调用完成后会交给 src 这个变量 脚本变量只有一种类型字符类型 注意的是 wildcard 和 *.cpp 是有空格的。 obj $(patsubst %.cpp,%.o, $(src)) obj $(patsubst %.cpp,%.o, $(src)) 将参数3中 包含参数1的部分替换为参数2 $(src)表示将 src 中变量依次 取出来src 中是 addfunc.cpp subfunc.cpp mulfunc.cpp divfunc.cpp 然后把src变量里所有后缀为.cpp的文件替换成.o 将addfunc.cpp替换成addfunc.o 将subfunc.cpp替换成subfunc.o 将mulfunc.cpp替换成mulfunc.o 将divfunc.cpp替换成divfunc.o 因此当 $(patsubst %.cpp,%.o, $(src))执行完毕后 obj中的值是addfunc.o subfunc.o mulfunc.o divfunc.o 有了这两个函数makefile就能简化 但是这个简化程度 只能将 集体的.cpp 或者.c 或者.o 替换还不能完全达到我们想要的结果因此还要往后面学习三个自动变量的使用 src $(wildcard *.cpp) #src addfunc.cpp, subfunc.cpp mulfunc.cpp devfunc.cpp test.cpp
obj $(patsubst %.cpp, %.o, $(src)) #obj addfunc.o subfunc.o mulfunc.o devfunc.o test.oALL:a.outa.out:$(obj)g $(obj) -o a.out
addfunc.o:addfunc.cppg -c addfunc.cpp -o addfunc.o -I../headsubfunc.o:subfunc.cppg -c subfunc.cpp -o subfunc.o -I../headmulfunc.o:mulfunc.cppg -c mulfunc.cpp -o mulfunc.o -I../headdevfunc.o:devfunc.cppg -c devfunc.cpp -o devfunc.o -I../headtest.o:test.cppg -c test.cpp -o test.o -I../headclean:-rm -rf $(obj) a.out#a.out:addfunc.o subfunc.o mulfunc.o devfunc.o test.o
# g addfunc.o subfunc.o mulfunc.o devfunc.o test.o -o a.out
#addfunc.o:addfunc.cpp
# g -c addfunc.cpp -o addfunc.o -I../head#subfunc.o:subfunc.cpp
# g -c subfunc.cpp -o subfunc.o -I../head#mulfunc.o:mulfunc.cpp
# g -c mulfunc.cpp -o mulfunc.o -I../head#devfunc.o:devfunc.cpp
# g -c devfunc.cpp -o devfunc.o -I../head#test.o:test.cpp
# g -c test.cpp -o test.o -I../headclean 清除不想要的文件 还有一个问题我们每次build 出来的都有.o文件实际上这个.o文件是中间文件没啥用 可以使用 clean指令删除这些不想要的.o文件,也可以将 a.out clean:-rm -rf $(obj) a.out 使用时先用 make clean -n看一下要删除哪些东西确认没有问题后 再使用 make clean 真正的删除。 -rm 这个 -的意思是 即使这个文件不存在也不会报错 三个自动变量
$ 在规则的命令中表示规则中的目标也就是说只能出现在如下的“命令”中
目标依赖条件一个tab缩进命令 $^ 在规则的命令中表示所有的依赖条件 $ 在规则的命令中表示第一个依赖条件
如果将该变量应用在模式规则中它可将依赖条件列表中的依赖条件依次取出套用模式规则。 那就在改一版 src $(wildcard *.cpp) #src addfunc.cpp, subfunc.cpp mulfunc.cpp devfunc.cpp test.cpp
obj $(patsubst %.cpp, %.o, $(src)) #obj addfunc.o subfunc.o mulfunc.o devfunc.o test.oALL:a.outa.out:$(obj)g $^ -o $ #changed the 规则的命令中只能是在命令行改动
addfunc.o:addfunc.cppg -c $ -o $ -I../headsubfunc.o:subfunc.cppg -c $ -o $ -I../headmulfunc.o:mulfunc.cppg -c $ -o $ -I../headdevfunc.o:devfunc.cppg -c $ -o $ -I../headtest.o:test.cppg -c $ -o $ -I../headclean:-rm -rf $(obj) a.out#a.out:$(obj)
# g $(obj) -o a.out
#addfunc.o:addfunc.cpp
# g -c addfunc.cpp -o addfunc.o -I../head#subfunc.o:subfunc.cpp
# g -c subfunc.cpp -o subfunc.o -I../head#mulfunc.o:mulfunc.cpp
# g -c mulfunc.cpp -o mulfunc.o -I../head#devfunc.o:devfunc.cpp
# g -c devfunc.cpp -o devfunc.o -I../head#test.o:test.cpp
# g -c test.cpp -o test.o -I../head#clean:
# -rm -rf $(obj) a.out#a.out:addfunc.o subfunc.o mulfunc.o devfunc.o test.o
# g addfunc.o subfunc.o mulfunc.o devfunc.o test.o -o a.out
#addfunc.o:addfunc.cpp
# g -c addfunc.cpp -o addfunc.o -I../head#subfunc.o:subfunc.cpp
# g -c subfunc.cpp -o subfunc.o -I../head#mulfunc.o:mulfunc.cpp
# g -c mulfunc.cpp -o mulfunc.o -I../head#devfunc.o:devfunc.cpp
# g -c devfunc.cpp -o devfunc.o -I../head#test.o:test.cpp
# g -c test.cpp -o test.o -I../head4.模式规则
我们发现上面的代码是有些是很相似的
#都是 %o:%.cpp g -c $ -o $ -I../head
addfunc.o:addfunc.cppg -c $ -o $ -I../headsubfunc.o:subfunc.cppg -c $ -o $ -I../headmulfunc.o:mulfunc.cppg -c $ -o $ -I../headdevfunc.o:devfunc.cppg -c $ -o $ -I../headtest.o:test.cppg -c $ -o $ -I../head#都是
%o:%.cppg -c $ -o $ -I../head#因此可以直接替换这个就是规则模式5.还有问题模式规则的疑问fix-使用静态模式规则
我们前面通过模式规则将.cpp变成.o
那么如果我们有些.cpp不想变成.o文件呢
或者说我们还有一些c文件也要变成.o文件呢 静态模式规则就是用来fix上述问题的。 obj2 main.c func1.c func2.c obj1 aa.cpp bb.cpp cc.cpp $(obj1):%o:%.cpp g -c $ -o $ -I../head
$(obj2):%o:%.c g -c $ -o $ -I../head 因此可以再改动一版
src $(wildcard *.cpp) #src addfunc.cpp, subfunc.cpp mulfunc.cpp devfunc.cpp test.cpp
obj $(patsubst %.cpp, %.o, $(src)) #obj addfunc.o subfunc.o mulfunc.o devfunc.o test.oALL:a.outa.out:$(obj)g $^ -o $ #changed the 规则的命令中只能命令行%.o:%.cppg -c $ -o $ -I../head#addfunc.o:addfunc.cpp
# g -c $ -o $ -I../head#subfunc.o:subfunc.cpp
# g -c $ -o $ -I../head#mulfunc.o:mulfunc.cpp
# g -c $ -o $ -I../head#devfunc.o:devfunc.cpp
# g -c $ -o $ -I../head#test.o:test.cpp
# g -c $ -o $ -I../headclean:-rm -rf $(obj) a.out#a.out:$(obj)
# g $(obj) -o a.out
#addfunc.o:addfunc.cpp
# g -c addfunc.cpp -o addfunc.o -I../head#subfunc.o:subfunc.cpp
# g -c subfunc.cpp -o subfunc.o -I../head#mulfunc.o:mulfunc.cpp
# g -c mulfunc.cpp -o mulfunc.o -I../head#devfunc.o:devfunc.cpp
# g -c devfunc.cpp -o devfunc.o -I../head#test.o:test.cpp
# g -c test.cpp -o test.o -I../head#clean:
# -rm -rf $(obj) a.out#a.out:addfunc.o subfunc.o mulfunc.o devfunc.o test.o
# g addfunc.o subfunc.o mulfunc.o devfunc.o test.o -o a.out
#addfunc.o:addfunc.cpp
# g -c addfunc.cpp -o addfunc.o -I../head#subfunc.o:subfunc.cpp
# g -c subfunc.cpp -o subfunc.o -I../head#mulfunc.o:mulfunc.cpp
# g -c mulfunc.cpp -o mulfunc.o -I../head#devfunc.o:devfunc.cpp
# g -c devfunc.cpp -o devfunc.o -I../head#test.o:test.cpp
# g -c test.cpp -o test.o -I../head删除各个版本演化注释的就变成下面的样子了
src $(wildcard *.cpp) #src addfunc.cpp, subfunc.cpp mulfunc.cpp devfunc.cpp test.cpp
obj $(patsubst %.cpp, %.o, $(src)) #obj addfunc.o subfunc.o mulfunc.o devfunc.o test.oALL:a.outa.out:$(obj)g $^ -o $ #changed the 规则的命令中只能命令行%.o:%.cppg -c $ -o $ -I../headclean:-rm -rf $(obj) a.out 6. makefile脚本的运行
1.直接make 就会执行makefile 中的所有指令
2.使用make -n 不会真的执行指令会将指令显示出来
2.使用make clean -n 查看要清除的文件
3.使用make clean 真正清除文件 7.make执行时候的问题
从上述的学习知道我们会在makefile 中写一个
ALL:a.outclean:-rm -rf $(obj) a.out
如果这时候我们当前文件下还有一个 clean的文件
再使用make clean的时候就会有问题。
因此我们还有改动 makefile文件写一个伪目标
.PHONY: clean ALL 这句话的意思是即使当前文件夹下有 clean的文件或者有ALL的文件会认为clean文件和ALL文件是伪目标。不会执行真正的文件而是执行make 命令参数。例如make clean src $(wildcard *.cpp) #src addfunc.cpp, subfunc.cpp mulfunc.cpp devfunc.cpp test.cpp
obj $(patsubst %.cpp, %.o, $(src)) #obj addfunc.o subfunc.o mulfunc.o devfunc.o test.oALL:a.outa.out:$(obj)g $^ -o $ #changed the 规则的命令中只能命令行%.o:%.cppg -c $ -o $ -I../headclean:-rm -rf $(obj) a.out.PHONY: clean ALL 8.优化可以将-g -Wall -I 作为参数加进去 src $(wildcard *.cpp)
obj $(patsubst %.cpp, %.o, $(src))ALL:a.outmyargs -g -Wall -I../heada.out:$(obj)g $^ -o $ ${myargs} %.o:%.cppg -c $ -o $ ${myargs}clean:-rm -rf $(obj) a.out.PHONY: clean ALL9.练习 1
要求 inc 目录下放置.h文件
obj目录下放置生成的.obj文件
src目录下放置.cpp 文件
最终生成的 a.out可执行文件和makefile文件在同一级目录
写出makefile文件 src1 $(wildcard ./src/*.cpp) #这时候 src1 ./src/addfunc.cpp ./src/subfunc.cpp ......
obj $(patsubst ./src/%.cpp, ./obj/%.o, $(src1)) #这里要注意 将参数3中 包含参数1的部分替换为参数2 ,这时候参数3取出来的值是 ./src/addfunc.cpp
#因此需要将 patsubst %.cpp 换成 .src/%.cpp, 将第二个参数%.o改成 ./obj/%o,
#这样 % 才能代表同样的部分也就是文件名的部分 obj的值就是 ./obj/addfunc.o ./obj/subfunc.o ......ALL:a.outmyargs -g -Wall -I./inca.out:$(obj)g $^ -o $ ${myargs}./obj/%.o : ./src/%.cppg -c $ -o $ ${myargs}clean:-rm -rf $(obj) a.out.PHONY: clean ALL10.练习 2 本地有多个 main程序一键生成
假设有a.cpp, b.cpp, c.cpp 每个.cpp文件中都有main函数
我们的目标是实现
g a.cpp -o a
g b.cpp -o b
g c.cpp -o c src $(wildcard *.cpp)target $(patsubst %.cpp, %, $(src))ALL: $(target)%:%cppgcc -o $clean:-rm -rf $(target).PHONY: clean ALL