一个通用makefile文件
有没有一个比较通用的makefile呢?
写在前面:
这个通用mkaefile文档,来源于正电原子学习板中提到的一种,当然这不代表这个就是最合适的,这只是我在学习过程中遇到的文档中,觉得最厉害的一个。
重点还是以学习为主,学学一般的makefile文件是怎么写的,以及makefile的基础知识。
上代码:(上接我写那个bsp推文)
项目源:https://gitee.com/iron2222/linux-driver-development.git
可直接下载其中的5_ledc_bsp,都是整理好的。
CROSS_COMPILE ?= arm-linux-gnueabihf-#这一行针对不同的编译器是可以进行更改的
TARGET ?= bsp#这个目标名字也是,针对不同到历程也是要改的
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
#变量 INCDIRS 包含整个工程的.h 头文件目录,文件中的所有头文件目录都要添加到变量INCDIRS中
INCDIRS := imx6ul \
bsp/clk \
bsp/led \
bsp/delay
#SRCDIRS 包含的是整个工程的所有.c 和.S 文件目录
SRCDIRS := project \
:= bsp/clk \
:= bsp/led \
:= bsp/delay
#变量 INCLUDE 使用到了函数 patsubst,通过函数 patsubst 给变量 INCDIRS 添加一个“-I”,因为 Makefile 语法要求指明头文件目录的时候需要加上“-I”
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
#变量 SFILES 保存工程中所有的.s 汇编文件(包含绝对路径),变量 SRCDIRS 已经存放了工程中所有的.c 和.S 文件,所以我们只需要从里面挑出所有的.S 汇编文件即可
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
#变量 CFILES 和变量 SFILES 一样,只是 CFILES 保存工程中所有的.c 文件(包含绝对路径)
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
#使用函数 notdir 将 SFILES 和 CFILES 中的路径去掉
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
#默认所有的文件编译出来的.o 文件和源文件在同一个目录中
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
#变量 OBJS 是变量 SOBJS 和 COBJS 的集合
OBJS := $(SOBJS) $(COBJS)
#VPATH 是指定搜索目录的,这里指定的搜素目录就是变量 SRCDIRS 所保存的目录,这样当编译的时候所需的.S 和.c 文件就会在 SRCDIRS 中指定的目录中查找
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)
不管做什么事,最重要的就是框架和逻辑,要明白自己所要达成的目的是什么?
就像昨天听师兄们开题,第一个上台的师兄被院长问了一个问题:你知道开题意义是什么?
师兄说是:发现问题,解决问题。
没错,就是要发现问题,确定目标,选择方法,实践,达到目标,解决问题。(其实有时候是自己创造问题来解决)
写makefile文档的目的是什么?
简化我们的操作步骤,更高效的编译,来获得目标文件。
写makefile文档会面临哪些问题?
这可就多了,针对不同的项目框架,目录结构,是不一样的,这里只是针对本项目的:
- 不同的支持文件放在不同的文件夹里
- 生成的文件要放在指定文件夹里
这是比较难的两个点。咱们一步一步来,直至完成上面的目标!!
开始写了
1)编译准备工作
CROSS_COMPILE ?= arm-linux-gnueabihf-#这一行针对不同的编译器是可以进行更改的
TARGET ?= bsp#这个目标名字也是,针对不同到历程也是要改的
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
首先当然是确定交叉编译工具链了,这个要根据你的实际情况来写,还有目标文件不要忘记了。
gcc、ld、objcopy、objdump是一条龙服务。
- gcc编译
- ld连接
- objcopy把.elf文件转化成.bin文件(关于这两个的区别可以看这个连接)
- objdump反汇编生成.dis文件
2)然后是文件路径
这里的文件路径包括 头文件所在路径,以及.c和.s文件虽在路径,用于后面的查找。
#变量 INCDIRS 包含整个工程的.h 头文件目录,文件中的所有头文件目录都要添加到变量INCDIRS中
INCDIRS := imx6ul \
bsp/clk \
bsp/led \
bsp/delay
#SRCDIRS 包含的是整个工程的所有.c 和.S 文件目录
SRCDIRS := project \
:= bsp/clk \
:= bsp/led \
:= bsp/delay
3)对路径进行处理,置于不同的变量中
#变量 INCLUDE 使用到了函数 patsubst,通过函数 patsubst 给变量 INCDIRS 添加一个“-I”,因为 Makefile 语法要求指明头文件目录的时候需要加上“-I”
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
这里提到了一个函数patsubat,用于替换通配符。
就是,把$(INCDIRS)中所有符合 %的,全部替换成 -I %,也就是在所有的头文件前面加上了-I。
#变量 SFILES 保存工程中所有的.s 汇编文件(包含绝对路径),变量 SRCDIRS 已经存放了工程中所有的.c 和.S 文件,所以我们只需要从里面挑出所有的.S 汇编文件即可
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
#变量 CFILES 和变量 SFILES 一样,只是 CFILES 保存工程中所有的.c 文件(包含绝对路径)
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
先说foreach,这个函数就像是for循环,具体用法如下:
$(foreach ,
,
)
这个函数的意思是,把参数
;中的单词逐一取出放到参数;所指定的变量中,然后再执行< text>;所包含的表达式。每一次
;会返回一个字符串,循环过程中, ;的所返回的每个字符串会以空格分隔,最后当整个循环结束时, ;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。
再说wildcard这个函数,这个函数简单些,就是拓展通配符,把目标文件的绝对路径找到并进行拼接。
#使用函数 notdir 将 SFILES 和 CFILES 中的路径去掉
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
函数notdir,去除路径,会的所有的.s和.c文件。
经过上面一顿操作,看看咱们现在都有了哪些变量:
- 放.c于.s文件的CFILENDIR、SFILENDIR(无路径)
- 放头文件.h的INCLUDE
- 当然还有交叉编译用的各种工具变量
4)确定分目标文件
前面我们有了连接之后的总目标,现在我们要定义各个分目标。
#默认所有的文件编译出来的.o 文件和源文件在同一个目录中
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
这里有一个新的用法:
对于一个已经定义的变量,可以使用“替换引用”将其值中的后缀字符(串)使用指定的字符(字符串)替换。格式为“$(VAR:A=B)”(或者“${VAR:A=B}”),意思是,替换变量“VAR”中所有“A”字符结尾的字为“B”结尾的字。“结尾”的含义是空格之前(变量值多个字之间使用空格分开)。而对于变量其它部分的“A”字符不进行替换。
这样我们就得到C文件、S文件的目标文件格式。
最后再集合一下:
#变量 OBJS 是变量 SOBJS 和 COBJS 的集合
OBJS := $(SOBJS) $(COBJS)
5)开始编译、连接、反编译
$(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)
这些就属于基础操作了。
END!!!
写在最后:
如果在学习的过程中遇到更好的makefile,我还会继续进行更新,大家有问题,也可以私信我交流,毕竟我也是刚开始学,肯定会有很多错误的。
祝大家,早安,午安,晚安!