黑马学习资源:
makefile语法:
本质是一个脚本文件,一系列命令的集合写在一个文件中,批量处理--->把写的代码(之间是有依赖的)组织起来,通过makefile工具管理和生成可执行文件
1 基本语法:
目标:依赖条件
命令(注意:前面是一个Tab缩进!!!)
示例先行: 一个makefile最简单的示例:
1、在maketest目录下 写一个hello.c
2、在maketest目录下 touch makefile (这个不能改,才能使用make命令)
3、vi makefile
# 一个最简单的makefile hello:hello.c gcc hello.c -o hello
4、在maketest目录下执行 make
2 一个规则:
1.若想生成目标,检查规则中的依赖条件是否存在,如不存在,则寻找是否有规则用来生成该依赖文件。
2.检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有人一个被更新,则目标必须更新
针对上面示例,makefile稍微扩展一下,解释一个规则中第一点
hello:hello.o gcc hello.o -o hello hello.o:hello.c gcc -c hello.c -o hello.o
也就是说,如果项目目录中没有 hello.c,但是是有hello.o,项目也可以编译成功。
下面针对项目多文件情况,解释下 一个规则中第一点 的好处:
假如有一个项目,每人负责项目中一个 .c,makefile这样写,如下:
hello:hello.c gcc hello.c add.c sub.c div1.c -o hello
项目开发完之后,一块编出hello。
假如后期,项目调试过程中,add.c要改;那么按照这个makefile规则,整个项目要重新编译一遍!!!如果项目过大,每次编译会花费巨长的时间。
“一个规则”把规则中间文件做细,后期没动的c文件就不用重新编译,就可以直接用第一次生成的 .o文件了
因此,下面只需要把一个makefile文件做细,就可以解决上面问题。
hello:hello.o add.o sub.o div1.o gcc hello.o add.o sub.o div1.o -o hello hello.o:hello.c gcc -c hello.c -o hello.o add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o div1.o:div1.c gcc -c div1.c -o div1.o
验证:
编译工程只做了下面这两行
3 两个函数:
makefile的2个函数
#wildcard 函数 函数Wildcard语法如下: $(wildcard pattern) # pattern定义了文件名的格式, wildcard取出其中存在的文件,并把名字赋给左边的变量。这个函数wildcard会以pattern这个格式,去寻找存在的文件,返回存在文件的名字。 示例: src=$(wildcard ./*.c) //匹配当前目录下的所有.c源文件,赋值给变量src(与shell类似,变量只有字符串类型) # patsubst 函数 obj=$(patsubst %.c,%.o,$(src)) //将参数3中包含参数1的部分替换为参数2 //在例子中,就是将src中的c文件名列表替换为 .o后缀
应用这两个函数将原来的makefile优化
优化前:
hello:hello.o add.o sub.o div1.o gcc hello.o add.o sub.o div1.o -o hello hello.o:hello.c gcc -c hello.c -o hello.o add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o div1.o:div1.c gcc -c div1.c -o div1.o
优化后:
src=$(wildcard ./*.c) obj=$(patsubst %.c,%.o,$(src)) ALL:hello hello:$(obj) gcc $(obj) -o hello hello.o:hello.c gcc -c hello.c -o hello.o add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o div1.o:div1.c gcc -c div1.c -o div1.o clean: -rm -rf $(obj) hello
ps:ALL和clean
ALL:指定makefile要生成的终极目标
make只会认为第一行是自己的最终目标, 如果最终目标没有写在第一行, 通过ALL来指定。
观察下面程序,如果不写ALL:hello,makefile生成hello.o就结束。因为hello:,,,没在第一行
ALL:hello hello.o:hello.c gcc -c hello.c -o hello.o add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o div1.o:div1.c gcc -c div1.c -o div1.o hello:hello.o add.o sub.o div1.o gcc hello.o add.o sub.o div1.o -o hello
clean:执行完make后,把 -rm -rf后面的文件删除(一般是删除中间文件,留下最终可执行文件)
clean: -rm -rf $(obj) hello
用法:make clean -n
(上面的clean可以认为是目标名),也可以不带,如果不带目标名的话它就想生成第一个规则里面的第一个目标。我们直接输入make的时候,会在makefile里面找到第一个目标然后执行下面的指令生成第一个目标。而当我们输入make clean的时候,就会在Makefile里面找到clean这个目标,然后执行里面的命令。
小注意:
-n 表示预执行,将将要执行的删除命令显示在命令行,先执行下这个是,为了防止删除原码。
rm前面的横杠表示出错(文件不存在)仍然执行
4 三个自动变量:
目标:依赖条件 命令(注意:前面是一个Tab缩进!!!)
$@
:用于在规则的命令部分中,表示规则中的目标$^
:用于在规则的命令部分中,表示所有依赖条件$<
:用于在规则的命令部分中,表示第一个依赖条件那进一步优化上面“两个函数部分”的makefile
应用优化前:
src=$(wildcard ./*.c) obj=$(patsubst %.c,%.o,$(src)) ALL:hello hello:$(obj) gcc $(obj) -o hello hello.o:hello.c gcc -c hello.c -o hello.o add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o div1.o:div1.c gcc -c div1.c -o div1.o clean: -rm -rf $(obj) hello
应用优化后:
src=$(wildcard ./*.c) obj=$(patsubst %.c,%.o,$(src)) ALL:hello hello:$(obj) gcc $^ -o $@ #目标依赖于所有依赖条件 hello.o:hello.c gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件 add.o:add.c gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件 sub.o:sub.c gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件 div1.o:div1.c gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件 clean: -rm -rf $(obj) hello
模式规则
鉴于上面的都是某个.o
文件依赖于某个.c
文件的形式,可以将其总结为一个模式规则:
%.o:%.c
gcc -c $< -o $@
应用模式规则后,makefile优化如下:
src=$(wildcard ./*.c)
obj=$(patsubst %.c,%.o,$(src))
ALL:hello
hello:$(obj)
gcc $^ -o $@
%.o:%.c
gcc -c $< -o $@
clean:
-rm -rf $(obj) hello
模式规则扩展:
我们这个例子中的模式规则知识针对当前例子。更复杂的项目或许有不止一个模式规则。这个时候应该指定模式规则的使用范围。
(1)静态模式规则(制定了模式规则给谁用):
$(obj): %.o: %.c gcc -c $< -o $@
其他
(1)加入伪目标(为了防止目录下可能有clean和ALL的同名文件,导致make认为makefile中的clean已经生成出来了,而不执行clean中的命令。给一个伪目标,让其不受干扰的执行):
我们在该目录下创建一个名为“clean”的文件,然后重新执行:make然后make clean,结果(会有下面的提示:):
make: `clean' is up to date.(MD,clean文件已经是最新啦!!!)
它根本没有执行我们的删除操作,这是为什么呢?
我们之前说,一个规则能够执行的条件:
1).目标文件直接就不存在
2).依赖文件比目标新。
现在我们的目录里面有名为“clean”的文件,目标文件是有的,并且没有依赖文件,没有办法判断依赖文件的时间。这种写法会导致:有同名的"clean"文件时,就不会执行make clean操作。
解决办法:我们需要把clean定义为假想目标,用关键字PHONY。
.PHONY: clean //把clean定义为假想目标。既然是假想的目标,意思就是想说,目标clean还不存在,因此他就不用判断名为“clean”的文件是否存在,而直接执行下面的命令
然后在Makfile结尾添加.PHONY: clean语句,重新执行:make clean,就会执行删除操作。而且如果makefile中有名为clean的目标文件的话,写法不一样,也不会造成冲突。
ALL也同理
.PHONY:clean ALL
(2)加入常用参数(-Wall, -I, -l, -L, -g),形成了最终版本:
我们之前在学习使用gcc编译的时候,有一些参数。这些参数如何在makefile中使用?
src=$(wildcard ./*.c)
obj=$(patsubst %.c,%.o,$(src))
myArgs=-Wall -g
ALL:hello
hello:$(obj)
gcc $^ -o $@ $(myArgs)
%.o:%.c
gcc -c $< -o $@ $(myArgs)
clean:
-rm -rf $(obj) hello
.PHONY:clean ALL
makefile学习【实操】
源码结构:
gyw@gyw-virtual-machine:~/桌面/动态库制作练习/LD_TEST$ tree
.
├── include
│ └── ld_test.h
├── makefile
├── object
└── source
├── add.c
├── ld_test.c
└── sub.c
3 directories, 5 files
编写makefile
1 src=$(wildcard ./source/*.c)
2 obj=$(patsubst ./source/%.c,./object/%.o,$(src))
3
4 FALGS=-Wall -g
INLUCDE_PATH=include
5
6 ALL:comput
7 comput:$(obj)
8 gcc $^ -o $@ ${FALGS}
9
10 $(obj):./object/%.o:./source/%.c
11 gcc -c $< -o $@ ${FALGS} -I ${INLUCDE_PATH}
12 clean:
13 #-rm -rf $(obj) comput
14
15 .PHONY:clean ALL
注意:line 2:对 函数使用,%通配符方面的理解,前面带目录
make
查看工程
gyw@gyw-virtual-machine:~/桌面/动态库制作练习/LD_TEST$ tree
.
├── comput
├── include
│ └── ld_test.h
├── makefile
├── object
│ ├── add.o
│ ├── ld_test.o
│ └── sub.o
└── source
├── add.c
├── ld_test.c
└── sub.c
3 directories, 9 files
总结:make和makefile的关系
在开发一个系统时,一般是将一个系统分成几个模块,这样做提高了系统的可维护性,但由于各个模块间不可避免存在关联,所以当一个模块改动后,其他模块也许会有所更新,这种开发方法对小系统来说,手工编译连接是没问题,但是如果是一个大系统,存在很多个模块,那么手工编译的方法就不适用了,过于繁琐。
为此,在Linux系统中,专门提供了一个make命令来自动维护目标文件。
与手工编译和连接相比,make命令的优点在于他只重新编译修改过的源文件(在Linux中,一个文件被创建或更新后有一个最后修改时间,make命令就是通过这个最后修改时间来判断此文件是否被修改),而对没修改的文件则置之不理,并且make命令不会漏掉一个需要更新的文件。
makefile中主要写:文件间依赖关系,目标文件,编译命令。
make和makefile的关系是:make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令。
当你需要在一些源文件改变后运行或更新一个任务时,通常会用到 make 工具。make 工具需要读取一个 Makefile(或 makefile)文件,在该文件中定义了一系列需要执行的任务。
makefile就像一首歌的乐谱,make工具就像指挥家,指挥家根据乐谱指挥整个乐团怎么样演奏,make工具就根据makefile中的命令进行编译和链接的。