基本知识
首先明白编译的目标文件是啥,搞单片机一般要用到的就是.hex,.bin,.elf. 而有了.elf就能转换成*.hex和*.bin.
所以目标是生成*.elf文件,而elf文件是由*.o文件、.a库文件链接而成,.o文件是由.c,*.cpp, .s等文件生成。而这些工具都需要用到编译器,makefile作用就是让编译器怎么工作*
.o文件也可以单独打包成.a(静态库文件),再被链接
入门语法
再了解一下makefile的简单语法
如
AXX = BXX,就是声明AXX并赋值为BXX
AXX = BXX CXX DXX 就是声明AXX并赋值为BXX CXX DXX
EXX = $(AXX) 就是声明EXX并引用AXX所包含的内容,沿用上面的话EXX就是BXX CXX DXX,而不是AXX
进阶功能 := ,?=,+=,-=可以自己百度一下
工作流程
找到所有需要的源文件
要生成*.o文件的话就要找到所有的头文件路径
ROOT = 啥啥… 后面跟的是linux的指令
# Working directories
# 根目录
ROOT := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
# 工程目录
TARGET_INCLUDE_DIR = $(ROOT)/cores
# 库目录
SYSTEM_INC = $(ROOT)/system/inc
# 全部目录
INCLUDES = $(SYSTEM_INC) $(TARGET_INCLUDE_DIR) $(TARGET_STARTUP_DIR)
以及所有源文件
源文件可以一个个的添加
src += xx/xx/xx.c
也可以用linux指令一次性查找所有的文件
#查找所有c文件
SYSTEM_SRC = $(wildcard $(SYSTEM_DIR)/*.c)
TARGET_SRC = $(wildcard $(TARGET_SRC_DIR)/*.c)
#还有启动文件
TARGET_STARTUP_SRC = $(ROOT)/variants/$(TARGET)/$(TARGET)_STARTUP.s
然后定义编译工具
编译工具是拿来处理c文件,makefile的主要工作都是围绕它给展开。不同的平台有不同的编译工具。如clang, vc, clang, gcc等等,而针对arm芯片的有keil,arm-none-eabi-gcc等等,这里用的是arm-none-eabi-gcc, 可以根据自己的需要去找,提前安装到电脑上,并且保证能被终端调用。
# Tool names
ARM_SDK_PREFIX ?= arm-none-eabi-
CROSS_CC := $(ARM_SDK_PREFIX)gcc
CROSS_CXX := $(ARM_SDK_PREFIX)g++
OBJCOPY := $(ARM_SDK_PREFIX)objcopy
SIZE := $(ARM_SDK_PREFIX)size
AR := $(ARM_SDK_PREFIX)ar
定义编译时的选项
主要是.c,.cpp,.s文件生成时的选项,要结合编译器的指令和芯片的特征来,这些都在编译的器使用说明上有。
这里必须用到的指令有:
芯片类型: -mcpu=cortex-m4 -mthumb
头文件路径:
(
a
d
d
p
r
e
f
i
x
−
I
,
(addprefix -I,
(addprefix−I,(INCLUDES))
语言优化等级:-02
预编译选项(#ifdef xx用到的): -Dxx
剩下的可根据需要自行理解添加,
(‘\’的意思是内容没有结束,接下一行)
#build flags
ARCH_FLAGS = -mthumb -mcpu=cortex-m4
CFLAGS = \
$(ARCH_FLAGS) \
$(addprefix -I,$(INCLUDES)) \
-c \
-O2 \
-g \
-ffast-math \
-std=gnu11 \
-Wall \
-ffunction-sections \
-fdata-sections \
-fsingle-precision-constant \
-Wdouble-promotion \
-Wunsafe-loop-optimizations \
-save-temps=obj \
-MMD \
-MP \
ASFLAGS = $(ARCH_FLAGS) \
-x assembler-with-cpp \
$(addprefix -I,$(INCLUDES)) \
-MMD -MP
生成文件
准备开始生成了,定义好要生成的目标
makefile的第一个xxxx : yyyy格式的内容会自动成为makefile的最终目标,xxxx为目标,yyyy为生成目标所需的依赖,makefile会自动寻找目标所需的全部依赖,包含依赖的依赖
#获取当前系统时间
DATE_TIME = $(shell date +"%Y-%M-%d-%H:%M:%S")
#要输出的目标,不列出来可能不输出
OUTPUT: $(TARGET_BIN) $(TARGET_HEX)
下面是各个依赖的生成
格式就是各个工具的使用方式
加@会隐藏编译过程中的指令
$<是指依赖的第一个
$@是指目标
echo 是终端的输出指令,类似printf
“#”是指注释
#command tips visible
V1 = @
#command visible
V0 = @
#生成bin,用elf转换成bin. arm-none-eabi-objcpy.exe - O binary /路径/文件名.elf /路径/文件名.bin
$(TARGET_BIN): $(TARGET_ELF)
$(V0) echo "$(DATE_TIME) Generating binFile ... $@"
$(V1) $(OBJCOPY) -O binary $< $@
#生成hex,与bin同理
$(TARGET_HEX): $(TARGET_ELF)
$(V0) echo "$(DATE_TIME) Generating hexFile ... $@"
$(V1) $(OBJCOPY) -O ihex $< $@
#拼接成elf arm-none-eabi-gcc.exe -o /路径/目标.elf 所有o文件 链接规则
$(TARGET_ELF): $(TARGET_OBJS)
$(V0) echo "$(DATE_TIME) Linking everyting together... $@"
$(V1)$(CROSS_CC) -o $@ $^ $(LDFLAGS)
$(V0) $(SIZE) $(TARGET_ELF)
#生成静态库
$(TARGET_LIB): $(TARGET_LIB_OBJS)
$(V0) echo "$(DATE_TIME) Linking static library... $@"
$(V1) $(AR) -rc $@ $^
#编译所有c文件
$(OBJECT_DIR)/$(TARGET)/%.o: %.c
$(V0) echo "$(DATE_TIME) compiling... $<"
$(V1) mkdir -p $(dir $@)
$(V1) $(CROSS_CC) -c -o $@ $(CFLAGS)$<
#编译所有s文件
$(OBJECT_DIR)/$(TARGET)/%.o: %.s
$(V0) echo "$(DATE_TIME) Assembling... $<"
$(V1) mkdir -p $(dir $@)
$(V1) $(CROSS_CC) -c -o $@ $(ASFLAGS) $<
#有些s文件是大写的S
$(OBJECT_DIR)/$(TARGET)/%.o: %.S
$(V0) echo "$(DATE_TIME) Assembling... $<"
$(V1) mkdir -p $(dir $@)
$(V1) $(CROSS_CC) -c -o $@ $(ASFLAGS) $<
#清除编译过程中的文件
clean:
$(V0) echo "Cleaning $(TARGET)"
$(V0) rm -f $(CLEAN_ARTIFACTS)
$(V0) rm -rf $(OBJECT_DIR)
$(V0) rm -rf $(OBJECT_DIR)/$(TARGET)
$(V0) echo "Cleaning $(TARGET) succeeded."