目录
想指定具体的一个makefile文件:"make -f makefilename1"
想执行makefile文件中的某个具体的目标:"make 具体目标"
makefile的优势:一次编写,终身受益!
本质:解决生成命令的问题!
当写的指令特别多的时候,比如说,如果要重新制作一下库,需要把所以指令再重新敲一遍,有一种是思路是写一个脚本,把相关的命令都放一起,然后,用的时候,就光执行这个脚本就行了,就不需要再一条一条的敲指令了,还有一种方式就是写成一个makefile文件,然后,用makeflie文件去执行就可以了!
makefile文件的命名规则
makefile or Makefile
makefile的三要素
目标(想要生成什么)
依赖(通过那些文件去生成)
规则命令(怎么生成的)
执行makefile文件的命令:"make"
默认执行第一个目标:"make"
想指定具体的一个makefile文件:"make -f makefilename1"
想执行makefile文件中的某个具体的目标:"make 具体目标"
makefile规则
1. 依赖文件如果比目标新,则重新生成目标的文件。
2. 如果没有找到所写的依赖文件,就递推的去找能生成依赖文件的文件,并执行相应的命令,(即如果找不到".o",就会自动的去找".c"文件,先生成'.o"文件,再进行下面的操作)。
3.:默认处理第一个目标(隐含规则)
4. 三要素中,目标不能不写,其他的可以不写。
举例说明
下面举个例子:
还是通过五个文件(add.c、div.c、sub.c、mul.c、main.c),最后得到一个可执行的文件。那么接下来借用这个需求,来写一个最简单的makefile文件,下面一句一句分析,写成一个makefile文件需要包含三要素:目标,依赖和规则命令,他们直接的语法格式为:
目标:依赖
tab键 规则命令
注意:此处必须是tab键,不能是空格!
写成:
app:main.c add.c sub.c div.c mul.c
gcc -o app -I./include main.c add.c sub.c div.c mul.c
发现,如果以后缀".c"的文件开始执行,五个".c",那么,每次执行都需要先把".c"文件都转成".o"文件,然后,再执行;这样很慢影响效率,针对makeflie自身的特点:
那么如果所建立的".o"文件都比目标文件旧的话,就不需要重新生成了!进行操作如下:
app:main.o add.o sub.o div.o mul.o
gcc -o app -I./include main.o add.o sub.o div.o mul.o
main.o:main.c
gcc -c main.c -I ./include
sub.o:sub.c
gcc -c sub.c -I ./include
div.o:div.c
gcc -c div.c -I ./include
add.o:add.c
gcc -c add.c -I ./include
mul.o:mul.c
gcc -c mul.c -I ./include
同时,发现这样写,也有点繁琐,添加一个变量来进行简化:
#ObjFiles 定义目标文件
ObjFiles=main.o add.o sub.o div.o mul.o
#目标文件用法 $(Var)
app:$(ObjFiles)
gcc -o app -I./include main.o add.o sub.o div.o mul.o
main.o:main.c
gcc -c main.c -I ./include
sub.o:sub.c
gcc -c sub.c -I ./include
div.o:div.c
gcc -c div.c -I ./include
add.o:add.c
gcc -c add.c -I ./include
mul.o:mul.c
gcc -c mul.c -I ./include
其中,"ObjFiles"是一个变量,等同于"main.o add.o sub.o div.o mul.o "这五个文件。其次,使用变量的语法为:
目标:$(变量名)
tab键 规则命令
函数
下面进行常用函数的介绍
进行文本匹配:"wildcard"
内容的替换:"patsubst"
下面的代码,通过"wildcard"函数来获取所有的".c"文件,然后通过"patsubst"函数来实现将所有".c"文件转换为".o"文件。
#get all .c files
SrcFiles=$(wildcard *.c)
#all .c files --> .o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
#目标文件用法 $(Var)
app:$(ObjFiles)
gcc -o app -I./include $(ObjFiles)
#模式匹配规则,$@,$< 这样的变量,只能在规则中出现
%.o:%.c
gcc -c $< -I./include -o $@
test:
echo $(SrcFiles)
echo $(ObjFiles1)
clean:
-@rm *.o
rm -f app
其中:
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
"%.c,%.o"规定替换那些部分是将"%"的对应位置上的内容都进行替换,"$(SrcFiles)"规定了选择那些文件进行替换。
#模式匹配规则,$@,$< 这样的变量,只能在规则中出现,不能用于命令的输入
%.o:%.c
gcc -c $< -I./include -o $@
其中,"%"代表通配,相当于替换了原来的,将五个".c"转换为".o"文件的过程。这个可以用模式推导来进行替换,这就涉及到了变量:
- $@ 代表目标
- $^ 代表全部依赖
- $< 第一个依赖
- $? 第一个变化的依赖
当程序运行到第一个目标对应的位置的时候,发现依赖是".o"文件,可是没有找到,于是,就去看下面的命令看看,有没有和".o"相关的(递归规则),发现下面有个目标很相关:"%.o:%.c",于是,就开始执行这条命令,按照顺序首先开始找的是"main.o"文件,发现没有,就找到了下面这条,发现"gcc -c $< -I ./include -o $@"(意思是将第一个依赖文件转换为".o"文件),通过执行这条文件,的确可以生成"main.o"文件了就!于是,就继续执行第一个目标的命令中去,一次类推,逐个的将五个".c"文件就都生成了一个".o"文件了就。
可以对比一个原始的看:
gcc -c div.c -I ./include/ -o div.o
注:模式匹配规则,$@,$< 这样的变量,只能在规则中出现,不能用于命令的输入。
这里需要提醒的是,makefile默认都是执行的第一个目标对应的命令,如果想执行下面的命令,就需要在执行的时候指定一下才可以 :
make test
makefile存在的意义在于,工程管理的需要,把所有的需要执行的命令都放进来,方便管理,比如,我想执行一清理的操作,也可以将其放都makefile中来:
clean:
-@rm *.o
rm -f app
"@"在规则前代表不输出该条规则的命令
"-"在规则前代表当这条指令报错的时候,仍然继续执行
只不过,执行的时候,指定执行那个目标得:
make clean
防止有歧义
".PHONY:"的使用
还是上面的makefile文件,里面有个目标是"clean",执行的是清除操作,可是如果我在当前的目录下,创建一个名为"clean"的文件后,然后,再执行"makefile clean"会发现报"make: 'clean' is up to date"的错误,这是因为makefile执行clean命令的时候,发生了歧义。
应该在"clean"命令的上面加一个".PHONY:clean all",这句话的意思是声明"clean"是一个伪目标,并不是我真正要得到的目标,这样的话,即使目录下有个同名的clean文件,也不是我要生成的目标,说明此处的"clean"只是我要清理的目标。只在makefile里面有的,而跟目录里的内容无关。
#定义伪目标,防止有歧义
.PHONY:clean
clean:
-@rm -f *.o
-@rm -f app app1
"all"的使用
因为三要素里面目标是必须有的,设定一个"all"(也是一个伪目标),然后关联一个依赖,然后,根据递推规则,自动去执行下面的命令了就,最好清理的时候,清理下"all"。
#get all .c files
SrcFiles=$(wildcard *.c)
#all .c files --> .o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
all:app app1
#目标文件用法 $(Var)
app:$(ObjFiles)
gcc -o $@ -I./include $(ObjFiles)
app1:$(ObjFiles)
gcc -o $@ -I./include $(ObjFiles)
#模式匹配规则,$@,$< 这样的变量,只能在规则中出现
%.o:%.c
gcc -c $< -I./include -o $@
test:
@echo $(SrcFiles)
@echo $(ObjFiles1)
#定义伪目标,防止有歧义
.PHONY:clean all
clean:
-@rm -f *.o
-@rm -f app app1
最后说一句,在我刚开始的时候,就不明白如果只执行"make"指令的话,会不会都会连带地 执行一次清理的命令,那每次新生成一次".o"文件之后,就把这些文件就又都删除了?后来仔细思考后发现,这样想是错的,因为执行"make"指令,默认只执行第一个目标的命令,如果在执行这个目标的命令中,发现没有相关的内容,才会去下翻调用第一目标下面的下面的相关的目标来解决这个时候欠缺的目标或者命令(递归规则),而这其中是不可能查到clean这个功能目标的,因为这个目标与第一个目标是不相干的,如果想调用clean这个目标,就应该输入指令"make clean",否则默认的"make"指令,是不会调用的。
经验之谈:有时候会发现什么都对,但是就是报错,这可能是因为你头文件修改了,但".o"文件没更新所致的!
恋蛩音 发布了178 篇原创文章 · 获赞 6 · 访问量 6259 私信 关注