文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
一、Makefile简介
makefile主要使用在没有集成开发环境时,对工程文件进行自动化编译的工具。
makefile 的本质是一个文件,需要配合make指令进行自动化编译。
make是一个命令工具,用来解释makefile文件中的代码,从而实现自动化编译。编译使用的编译器本质上还是gcc。
makefile文件中定义了一系列的规则来指定, 哪些文件需要先编译, 哪些文件需要后编译, 哪些文件需要重新编译, 甚至于进行更复杂的功能操作。
makefile文件的命名:makefile或者Makefile。(也可通过其他方式,不使用这两个名字,通常我们使用这两个即可)
二、makefile 原理
基本原理:若想生成目标, 检查规则中的所有的依赖文件是否都存在。
1、当有依赖文件不存在
- 如果有的依赖文件不存在, 则向下搜索规则, 看是否有生成该依赖文件的规则:
- 如果有规则用来生成该依赖文件,则执行规则中的命令生成依赖文件;
- 如果没有规则用来生成该依赖文件, 则报错
也就是如下图所示:
2、当所有依赖文件存在
如果所有依赖都存在, 检查规则中的目标是否需要更新, 必须先检查它的所有依赖,依赖中有任何一个被更新, 则目标必须更新.(检查的规则是哪个时间大哪个最新)
如下图所示:
三、makefile基本规则
1.makefile规则三要素
- 目标: 要生成的目标文件
- 依赖: 目标文件由哪些文件生成
- 命令: 通过执行该命令由依赖文件生成目标
2.基本规则
规则如下:
目标:依赖
命令 #注意命令需要以tab键开始
main:main.c test.c
gcc main.c test.c -o main
3、makfile中的变量
makefile中使用变量有点类似于C语言中的宏定义, 使用该变量相当于内容替换, 使用变量可以使makefile易于维护, 修改起来变得简单。
makefile中有三种变量:
- 普通变量
- 自动变量
- 自带变量(不太了解)
3.1普通变量
变量定义是用 = 号即可,例如:
OBJS = main //定义并赋值(通常不这样使用),会配合: 进行使用,后面会详解
$(OBJS) //使用变量
3.2自动变量
自动变量主要有,如下所示:
- $@: 规则中的目标集合,在模式规则中,如果有多个目标的话,“ $@”表示匹配模式中定义的目标集合。
- $<:依赖文件集合中的第一个文件,如果依赖文件是以模式(即“ %” )定义的,那么“ $<”就是符合模式的一系列的文件集合
- $^:所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,会去除重复的依赖文件,只保留一份。
- $%:当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。
- $?:所有比目标新的依赖目标集合,以空格分开。
- $+:和“ $^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。
3.3例程
CC := gcc #arm-linux-gnueabihf-gcc
TARGET := main
object := main.o test.o
.PHONY : clean
$(TARGET):$(object)
$(CC) -o $@ $^
%.o:%.c
$(CC) -o -c $@ $<
clean:
rm main main.o test.o #rm main %.o
上面就是一个简单的makefile文件
4、makefile中的伪目标
Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。
注意:使用伪目标的主要是为了避免 Makefile 中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突
上面例程中使用的伪目标clean,是用来清理编译产生的文件。
四、makefile其他常用的规则
- := :一个变量在定义并赋值后,不会使用后面给该变量赋值的值,只能使用前面已经定义好的
- ?= :如果该变量在前面没有被赋值,就给该变量赋值。如果赋值过了,就使用之前赋值的值
- += :需要给前面已经定义好的变量添加一些字符串进去,类似x +=1;
- %:类似通配符,例:%.c,也就是以 .c 结尾的所有文件
- \:是makefile中行的分隔符。
五、makefile中的函数
注意:此处只说下面综合例程中使用的函数和常用的函数
5.1函数 subst
函数作用:完成字符串的替换操作。
调用形式如下:
$(subst <from>,<to>,<text>)
#from:字符串中被替换的字符串
#to:替换的字符串
#text:字符串
#举例
$(subst aaa,AAA,aaabbb)
#也就是将字符串aaa替换成AAA,替换后字符串为AAAbbb
5.2函数 patsubst
函数作用:用来完成模式字符串替换
调用形式如下:
$(patsubst <pattern>,<replacement>,<text>)
$(patsubst %.c,%.o,a.c b.c c.c)
#将字符串“ a.c b.c c.c”中的所有符合“ %.c”的字符串,替换为“ %.o”,替换完成以后的字符串为“ a.o b.o c.o”
5.3函数 dir
函数作用: 用来获取目录
调用形式如下:
$(dir <names…>)
$(dir </src/a.c>)
#提取文件“ /src/a.c”的目录部分,也就是“ /src”
5.4函数 notdir
函数作用:除文件中的目录部分,提取文件名
调用形式如下:
$(notdir <names…>)
$(notdir </src/a.c>)
#提取文件“ /src/a.c”中的非目录部分,也就是文件名“ a.c”。
5.5函数 foreach
函数作用:函数用来完成循环
调用形式如下:
$(foreach <var>, <list>,<text>)
#把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中,
# <text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时, <text>所返回的每个字符串所组成的整个字符串将会是函数 foreach
# 函数的返回值。
5.6函数 wildcard
函数作用:通配符“ %”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard
调用形式如下:
$(wildcard PATTERN…)
$(wildcard *.c)
#上面的代码是用来获取当前目录下所有的.c 文件,类似“%”
六、综合例程
CROSS_COMPILE := arm-linux-gnueabihf-
TARGET := bsp
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
INCDIRS := imx6ul \
bsp/clk \
bsp/delay \
bsp/LED
SRCDIRS := project \
bsp/clk \
bsp/delay \
bsp/LED
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
#SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS := $(SOBJS) $(COBJS)
VPATH := $(SRCDIRS)
.PHONY: clean
$(TARGET).bin : $(OBJS)
$(LD) -Timx6ul.lds -o $(TARGET).elf $^
$(OBJCOPY) -O binary -S $(TARGET).elf $@
$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis
$(SOBJS) : obj/%.o : %.S
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
$(COBJS) : obj/%.o : %.c
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
clean:
rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)
print :
@echo INCLUDE = $(INCLUDE)
@echo SFILES = $(SFILES)
@echo CFILES = $(CFILES)
@echo SFILENDIR = $(SFILENDIR)
@echo CFILENDIR = $(CFILENDIR)
@echo SOBJS = $(SOBJS)
@echo COBJS = $(COBJS)
@echo OBJS = $(OBJS)
例程中的makefile适用于大型工程管理,只需要添加源文件路径和头文件路径,不需要改动太多的makefile代码,即可实现工程代码的自动化编译。