2021-10-23

有没有一个比较通用的makefile呢?

写在前面:

这个通用mkaefile文档,来源于正电原子学习板中提到的一种,当然这不代表这个就是最合适的,这只是我在学习过程中遇到的文档中,觉得最厉害的一个。

重点还是以学习为主,学学一般的makefile文件是怎么写的,以及makefile的基础知识。

上代码:(上接我写那个bsp推文)

项目源:Linux驱动开发: Makefile+driver 可直接下载其中的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是一条龙服务。

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 <var>,<list>,<text>)

这个函数的意思是,把参数<list>;中的单词逐一取出放到参数<var>;所指定的变量中,然后再执行< text>;所包含的表达式。每一次<text>;会返回一个字符串,循环过程中,<text>;的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。

再说wildcard这个函数,这个函数简单些,就是拓展通配符,把目标文件的绝对路径找到并进行拼接。


#使用函数 notdir 将 SFILES 和 CFILES 中的路径去掉
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))

函数notdir,去除路径,会的所有的.s和.c文件。

经过上面一顿操作,看看咱们现在都有了哪些变量:

  1. 放.c于.s文件的CFILENDIR、SFILENDIR(无路径)
  2. 放头文件.h的INCLUDE
  3. 当然还有交叉编译用的各种工具变量

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,我还会继续进行更新,大家有问题,也可以私信我交流,毕竟我也是刚开始学,肯定会有很多错误的。

祝大家,早安,午安,晚安!

上一篇:SwiftUI Metal 入门教程之02 基本任务和概念


下一篇:Hi3516开发笔记(九):在QtCreator开发环境中引入海思sdk的bsp包,运行显示Qt界面