源文件->编译(compile)->形成中间代码文件(.o/.obj)
中间代码文件->链接(link)->可执行文件
在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出
一个警告,但可以生成 Object File。而在链接程序时,链接器会在所有的 Object File 中
找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)
target ... : prerequisites ...
command
->
target 是目标文件,可以是 .o文件,也可以是执行文件。还可以是一个标签(Label)
->
prerequisites 是,要生成那个 target 所需要的文件或是目标(也可以叫成是"依赖")。
->
command 也就是 make 需要执行的命令。(可用任意的 Shell 命令)
在对应目录下执行make命令,就会追踪到当前目录下的Makefile文件。
查看“prerequisites”的修改时间是否“新”于“target”
如果是,就执行下面的command
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
$@ 是当前目标文件(target)
$^ 是所有的依赖文件
$< 是第一个依赖文件
$(xxx) 相当于宏替换。
$ 是通佩符
* 是通佩符
patsubst
$(patsubst 参数1, 参数2, 参数3)
功能:查找参数3中的字符串(字符串需要以“空格”、“TAB”、“回车” 或 “换行”分隔)
如果当前字符串符合参数1的格式,那么将会替换为参数2的格式。(可以使用通佩符)
我为什么要使用这个函数?
因为我们在编译源码的时候需要指定"头文件的路径"。而Makefile在制定头文件路径的时候需要 “-i”
这个函数就是能将:我们写入的路径上面 批量的加入 “-i”
wildcard
$(wildcard 参数1)
$(wildcard 参数1)可以在函数的参数/变量中使用,而参数1中可以带通佩符。调用函数的时候会将通佩符所代表的所有值都带入函数。
功能:在变量中使用通佩符,将其展开使用。
$(foreach 参数1,参数2,参数3)、
注:参数3是一个含参数1的表达式
功能:将参数2中的字串取出,放入参数1中,再带入到参数3这个表达式中遍历并逐一返回。
$(notdir 参数1)
注:参数1是一串路径
功能:将参数1字串中非目录部分取出。非目录部分是指最后一个反斜杠“/”后的字串
例:obj/bsp_led.o -> bsp_led.o
VPATH:
Makefile 文件中的特殊变量“VPATH”就是完成不同目录下文件搜寻功能的,如果没有指明这个变量,
make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make
就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
无依赖文件,make xxx直接执行命令
clean:
rm *.o temp
–我们并不生成“clean”这个文件。“伪目标”并不是一个文件,只是一个标签,
由于“伪目标”不是文件,所以 make 无法生成它的依赖关系和决定它是否要执行。我们只
有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,
不然其就失去了“伪目标”的意义了。
–当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显
示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪
目标”。
.PHONY : clean
Makefile的格式是这样的:
目标 : 依赖
命令
静态模式则:
: :
我也没看懂,暂时用过下面这种
据个例子:
OBJS = obj/start.o obj/main.o obj/bsp_clk.o obj/bsp_led.o obj/bsp_delay.o
$(SOBJS) : obj/%.o : %.S
...
将所有的.S文件编译成.o,并存放到obj目录下
用程序模块化的思想建立了bsp工程
# 工程文件夹路径分配:
# bsp:imx6ul :包含寄存器定义结构体
# :bsp :包含自定义的.c.h文件
# :obj :编译后生成的中间文件.o
# :project:main.c和 start.s
CROSS_COMPILE ?= arm-linux-gnueabihf-
#交叉编译工具 注 ?= 是如果没有被赋值过就赋予等号后面的值
TARGET ?= bsp
#bin文件(可执行文件)的文件名
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump #交叉编译器的四个功能,下一篇文章中会有详细叙述! 注 := 是覆盖之前的值
INCDIRS := imx6ul \
bsp/clk \
bsp/led \
bsp/delay #头文件目录路径
SRCDIRS := project \
bsp/clk \
bsp/led \
bsp/delay #源文件目录路径
#抓取工程中的.h文件的路径
INCLUDE := $(patsubst %, -I %, $(INCDIRS)) #patsubst:批量格式替换,将$(INCDIRS)中的字符串都加入"-I"为前缀。
#为什么要加“-I”,这是makefile对指定头文件路径的要求
#抓取工程中的.s文件 和 .c文件路径
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) #wildcard关键字:在变量中可以使用通佩符$*
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c)) #foreach函数:将参数2中的字串,放入参数1中,再带入到参数3这个表达式中遍历并逐一返回。
#一个.S/c 对应1个.o文件,把文件名抓出来
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES)) #notdir:将.s文件 和 .c文件的路径去掉只留文件名:xxx.S xxx.C
#抓取工程中的.o文件的路径
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o)) #patsubst:批量格式替换,将$(SFILENDIR:.S=.o)中的字符串都加入"obj/"为前缀。
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o)) #$(SFILENDIR:.S=.o),将SFILENDIR中的.替换为.o
OBJS := $(SOBJS) $(COBJS)
VPATH := $(SRCDIRS)
#VPATH特殊变量的作用:指定搜索依赖的路径。如果不定义,就只会再mikefile文件的同一目录下寻找
.PHONY: clean #声明伪目标,为防止与文件重名。
#链接
$(TARGET).bin : $(OBJS)
$(LD) -T imx6ul.lds -o $(TARGET).elf $^
$(OBJCOPY) -O binary -S $(TARGET).elf $@
$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis
#编译
$(SOBJS) : obj/%.o : %.S #这个写法叫静态模式:将所有的.S文件编译成.o,并存放到obj目录下
$(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)
print1:#测试1-抓取.h路径
@echo INCLUDE = $(INCLUDE)
print2:#测试2-抓取.s路径
@echo SFILES = $(SFILES)
print3:#测试3-抓取.c路径
@echo CFILES = $(CFILES)
print4:#测试3-抓取.o路径
@echo OBJS = $(OBJS)