目录

LibOpenCM3 项目模板

项目模板地址: https://github.com/libopencm3/libopencm3-template

如果仅仅用IDE开发, 并不需要了解 Makefile 结构. 对于需要实现自动化发布和测试的项目, Makefile 几乎是默认的选项, 了解 Makefile 工作机制对项目长期的开发和维护都是很有帮助的.

文件结构

├── libopencm3                # libopencm3 封装库
│ ├── COPYING.GPL3
│ ├── COPYING.LGPL3
│ ├── doc
│ ├── HACKING
│ ├── HACKING_COMMON_DOC
│ ├── include
│ ├── ld
│ ├── lib # 编译后, 链接库 .a 文件的存放目录
│ ├── locm3.sublime-project
│ ├── Makefile # libopencm3 项目 Makefile
│ ├── mk
│ │ ├── gcc-config.mk
│ │ ├── gcc-rules.mk
│ │ ├── genlink-config.mk # 用于生成ld文件
│ │ ├── genlink-rules.mk # 用于生成ld文件
│ │ └── README
│ ├── README.md
│ ├── scripts
│ └── tests
├── LICENSE
├── my-common-code # 用于演示的公用代码部分
│ ├── api-asm.h
│ ├── api-asm.S
│ ├── api.c
│ └── api.h
├── my-project # 用于演示的用户项目代码部分
│ ├── Makefile # 用户项目 Makefile
│ └── my-project.c
├── README.md
└── rules.mk # 全局规则

其中, libopencm3 的 Makefile 用于将 libopencm3 编译生成链接库, 用户项目的 Makefile 用于引用 libopencm3 以及包含公用代码生成最终的 elf 和 bin, 以及生成 ld 文件.

libopencm3 的 Makefile

libopencm3 的 Makefile 主要用于将 libopencm3 编译为链接库库

# 注: 如果工具链未加入PATH, 可以在这里加上路径
PREFIX ?= arm-none-eabi- # 注: 这个perl脚本貌似是用来检查代码注释的格式的
STYLECHECK := scripts/checkpatch.pl
STYLECHECKFLAGS := --no-tree -f --terse --mailback # 注: 这里设置的编译的范围, 默认是编译全部型号, 这会消耗很长的时间, 如果只需要编译某一个型号, 可以在make时直接指定
# 例如 TARGETS=stm32/f1 make
# 这里的名称要和 lib 下的目录名严格一致, 因为下面会用这个拼接完整目录
#
TARGETS ?= stm32/f0 stm32/f1 stm32/f2 stm32/f3 stm32/f4 stm32/f7 \
stm32/l0 stm32/l1 stm32/l4 \
stm32/g0 \
stm32/h7 \
gd32/f1x0 \
lpc13xx lpc17xx lpc43xx/m4 lpc43xx/m0 \
lm3s lm4f msp432/e4 \
efm32/tg efm32/g efm32/lg efm32/gg efm32/hg efm32/wg \
efm32/ezr32wg \
sam/3a sam/3n sam/3s sam/3u sam/3x sam/4l \
sam/d \
vf6xx \
swm050 \
pac55xx # Be silent per default, but 'make V=1' will show all compiler calls.
# 注: 使用V=1输出编译时的完整命令, 例如 TARGETS=stm32/f1 make V=1
ifneq ($(V),1)
Q := @
# Do not print "Entering directory ...".
MAKEFLAGS += --no-print-directory
endif # Avoid the use of shell find, for windows compatibility
IRQ_DEFN_FILES := $(foreach TARGET,$(TARGETS),$(wildcard include/libopencm3/$(TARGET)/irq.json))
STYLECHECKFILES := $(wildcard include/*/*.h include/*/*/*.h include/*/*/*/*.h)
STYLECHECKFILES += $(wildcard lib/*/*.h lib/*/*/*.h lib/*/*/*/*.h)
STYLECHECKFILES += $(wildcard lib/*/*.c lib/*/*/*.c lib/*/*/*/*.c) # 注: 默认的目标是 all -> build -> lib
all: build build: lib %.genhdr:
@printf " GENHDR $*\n";
# 注: irq2nvic_h 是个 python 脚本, 用JSON定义的中断编号生成 nvic.h 头文件
$(Q)./scripts/irq2nvic_h ./$*; %.cleanhdr:
@printf " CLNHDR $*\n";
$(Q)./scripts/irq2nvic_h --remove ./$* # 注: 根据 TARGETS 拼接目录
LIB_DIRS:=$(wildcard $(addprefix lib/,$(TARGETS)))
# 注: $(IRQ_DEFN_FILES:=.genhdr) 这个表达式, 实际上是批量替换, 替换的左边扩展名为空,
# 所以其作用是将 IRQ_DEFN_FILES 中每一个值, 加上了.genhdr, 去执行 %.genhdr 的规则
$(LIB_DIRS): $(IRQ_DEFN_FILES:=.genhdr)
$(Q)$(RM) .stamp_failure_$(subst /,_,$@)
@printf " BUILD $@\n";
$(Q)$(MAKE) --directory=$@ PREFIX="$(PREFIX)" || \
echo "Failure building: $@: code: $$?" > .stamp_failure_$(subst /,_,$@) lib: $(LIB_DIRS)
$(Q)$(RM) .stamp_failure_tld
$(Q)for failure in .stamp_failure_*; do \
[ -f $$failure ] && cat $$failure >> .stamp_failure_tld || true; \
done;
$(Q)[ -f .stamp_failure_tld ] && cat .stamp_failure_tld && exit 1 || true; # 注: 执行 doc 目录下的 make
html doc:
$(Q)$(MAKE) -C doc html TARGETS="$(TARGETS)" # 注: 清理, 这些$(XXX:=.xxx)也是批量替换用法
clean: $(IRQ_DEFN_FILES:=.cleanhdr) $(LIB_DIRS:=.clean) $(EXAMPLE_DIRS:=.clean) doc.clean styleclean genlinktests.clean %.clean:
$(Q)if [ -d $* ]; then \
printf " CLEAN $*\n"; \
$(MAKE) -C $* clean || exit $?; \
fi;
$(Q)$(RM) .stamp_failure_*; stylecheck: $(STYLECHECKFILES:=.stylecheck)
styleclean: $(STYLECHECKFILES:=.styleclean) # the cat is due to multithreaded nature - we like to have consistent chunks of text on the output
%.stylecheck: %
$(Q)if ! grep -q "* It was generated by the irq2nvic_h script." $* ; then \
$(STYLECHECK) $(STYLECHECKFLAGS) $* > $*.stylecheck; \
if [ -s $*.stylecheck ]; then \
cat $*.stylecheck; \
else \
rm -f $*.stylecheck; \
fi; \
fi; %.styleclean:
$(Q)rm -f $*.stylecheck; LDTESTS :=$(wildcard ld/tests/*.data) genlinktests: $(LDTESTS:.data=.ldtest)
genlinktests.clean:
$(Q)rm -f $(LDTESTS:.data=.out) %.ldtest:
@if ./scripts/genlinktest.sh $* >/dev/null; then\
printf " TEST OK : $*\n"; \
else \
printf " TEST FAIL : $*\n"; \
fi; .PHONY: build lib $(LIB_DIRS) doc clean generatedheaders cleanheaders stylecheck genlinktests genlinktests.clean

rule.mk 分析

多匹配的优先级选择, 参考 How Patterns Match 模式匹配的工作机制

# This version of rules.mk expects the following to be defined before
# inclusion..
### REQUIRED ###
# OPENCM3_DIR - duh
# PROJECT - will be the basename of the output elf, eg usb-gadget0-stm32f4disco
# CFILES - basenames only, eg main.c blah.c
# CXXFILES - same for C++ files. Must have cxx suffix!
# DEVICE - the full device name, eg stm32f405ret6
# _or_
# LDSCRIPT - full path, eg ../../examples/stm32/f4/stm32f4-discovery/stm32f4-discovery.ld
# OPENCM3_LIB - the basename, eg: opencm3_stm32f4
# OPENCM3_DEFS - the target define eg: -DSTM32F4
# ARCH_FLAGS - eg, -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
# (ie, the full set of cpu arch flags, _none_ are defined in this file)
#
### OPTIONAL ###
# INCLUDES - fully formed -I paths, if you want extra, eg -I../shared
# BUILD_DIR - defaults to bin, should set this if you are building multiarch
# OPT - full -O flag, defaults to -Os
# CSTD - defaults -std=c99
# CXXSTD - no default.
# OOCD_INTERFACE - eg stlink-v2
# OOCD_TARGET - eg stm32f4x
# both only used if you use the "make flash" target.
# OOCD_FILE - eg my.openocd.cfg
# This overrides interface/target above, and is used as just -f FILE
### TODO/FIXME/notes ###
# No support for stylecheck.
# No support for BMP/texane/random flash methods, no plans either
# No support for magically finding the library.
# C++ hasn't been actually tested with this..... sorry bout that. ;)
# Second expansion/secondary not set, add this if you need them. # 注: 指定默认的中间文件目录
BUILD_DIR ?= bin
OPT ?= -Os
CSTD ?= -std=c99 # Be silent per default, but 'make V=1' will show all compiler calls.
# If you're insane, V=99 will print out all sorts of things.
V?=0
ifeq ($(V),0)
Q := @
NULL := 2>/dev/null
endif # Tool paths.
PREFIX ?= arm-none-eabi-
CC = $(PREFIX)gcc
CXX = $(PREFIX)g++
LD = $(PREFIX)gcc
OBJCOPY = $(PREFIX)objcopy
OBJDUMP = $(PREFIX)objdump
OOCD ?= openocd OPENCM3_INC = $(OPENCM3_DIR)/include # Inclusion of library header files
INCLUDES += $(patsubst %,-I%, . $(OPENCM3_INC) ) # 注: 格式 $(SRC:%.c=%.o) 批量替换, 在SRC中找到所有.c 结尾的文件,然后把所有的.c换成.o
# 等价于 $(patsubst %.c, %.o, $(SRC))
OBJS = $(CFILES:%.c=$(BUILD_DIR)/%.o)
OBJS += $(CXXFILES:%.cxx=$(BUILD_DIR)/%.o)
OBJS += $(AFILES:%.S=$(BUILD_DIR)/%.o)
GENERATED_BINS = $(PROJECT).elf $(PROJECT).bin $(PROJECT).map $(PROJECT).list $(PROJECT).lss TGT_CPPFLAGS += -MD
TGT_CPPFLAGS += -Wall -Wundef $(INCLUDES)
TGT_CPPFLAGS += $(INCLUDES) $(OPENCM3_DEFS) TGT_CFLAGS += $(OPT) $(CSTD) -ggdb3
TGT_CFLAGS += $(ARCH_FLAGS)
TGT_CFLAGS += -fno-common
TGT_CFLAGS += -ffunction-sections -fdata-sections
TGT_CFLAGS += -Wextra -Wshadow -Wno-unused-variable -Wimplicit-function-declaration
TGT_CFLAGS += -Wredundant-decls -Wstrict-prototypes -Wmissing-prototypes TGT_CXXFLAGS += $(OPT) $(CXXSTD) -ggdb3
TGT_CXXFLAGS += $(ARCH_FLAGS)
TGT_CXXFLAGS += -fno-common
TGT_CXXFLAGS += -ffunction-sections -fdata-sections
TGT_CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ TGT_ASFLAGS += $(OPT) $(ARCH_FLAGS) -ggdb3 TGT_LDFLAGS += -T$(LDSCRIPT) -L$(OPENCM3_DIR)/lib -nostartfiles
TGT_LDFLAGS += $(ARCH_FLAGS)
TGT_LDFLAGS += -specs=nano.specs
TGT_LDFLAGS += -Wl,--gc-sections
# OPTIONAL
#TGT_LDFLAGS += -Wl,-Map=$(PROJECT).map
ifeq ($(V),99)
TGT_LDFLAGS += -Wl,--print-gc-sections
endif # Linker script generator fills this in for us.
# 注: 判断变量是否为空的格式 ifeq ($(TEST),) TEST := $(something else) endif
ifeq (,$(DEVICE))
LDLIBS += -l$(OPENCM3_LIB)
endif
# nosys is only in newer gcc-arm-embedded...
#LDLIBS += -specs=nosys.specs
LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group # Burn in legacy hell fortran modula pascal yacc idontevenwat
.SUFFIXES:
.SUFFIXES: .c .S .h .o .cxx .elf .bin .list .lss # Bad make, never *ever* try to get a file out of source control by yourself.
%: %,v
%: RCS/%,v
%: RCS/%
%: s.%
%: SCCS/s.% # 注: 默认目标, 需要产生项目elf和bin文件
all: $(PROJECT).elf $(PROJECT).bin
# 注: flash目标, 需要 项目.flash, 会定向到 %.flash 任务
flash: $(PROJECT).flash # error if not using linker script generator
ifeq (,$(DEVICE))
$(LDSCRIPT):
ifeq (,$(wildcard $(LDSCRIPT)))
$(error Unable to find specified linker script: $(LDSCRIPT))
endif
else
# if linker script generator was used, make sure it's cleaned.
# 将 ld 文件加入二进制文件列表, 用于 clean 的时候清除
GENERATED_BINS += $(LDSCRIPT)
endif # Need a special rule to have a bin dir
# 注: 匹配 BUILD_DIR 目录下的 .o 文件目标, 检查 .c 文件, 并对应的调用 CC 进行编译
# 匹配规则和优先级顺序参考 GNU Make 手册的 10.5.4 How Patterns Match
$(BUILD_DIR)/%.o: %.c
@printf " CC\t$<\n"
@mkdir -p $(dir $@)
$(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $@ -c $< $(BUILD_DIR)/%.o: %.cxx
@printf " CXX\t$<\n"
@mkdir -p $(dir $@)
$(Q)$(CXX) $(TGT_CXXFLAGS) $(CXXFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $@ -c $< $(BUILD_DIR)/%.o: %.S
@printf " AS\t$<\n"
@mkdir -p $(dir $@)
$(Q)$(CC) $(TGT_ASFLAGS) $(ASFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $@ -c $< # 注: LIBDEPS 定义在 genlink-config.mk, 指向 libopencm3/lib/libopencm3_xxxx.a 文件,
# 根据设置的 DEVICE 不同, 名称不同
$(PROJECT).elf: $(OBJS) $(LDSCRIPT) $(LIBDEPS)
@printf " LD\t$@\n"
$(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ # 注: 用 .elf 文件生成 .bin 文件
%.bin: %.elf
@printf " OBJCOPY\t$@\n"
$(Q)$(OBJCOPY) -O binary $< $@ %.lss: %.elf
$(OBJDUMP) -h -S $< > $@ %.list: %.elf
$(OBJDUMP) -S $< > $@ # 注: 烧录
%.flash: %.elf
@printf " FLASH\t$<\n"
ifeq (,$(OOCD_FILE))
$(Q)(echo "halt; program $(realpath $(*).elf) verify reset" | nc -4 localhost 4444 2>/dev/null) || \
$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \
-f target/$(OOCD_TARGET).cfg \
-c "program $(realpath $(*).elf) verify reset exit" \
$(NULL)
else
$(Q)(echo "halt; program $(realpath $(*).elf) verify reset" | nc -4 localhost 4444 2>/dev/null) || \
$(OOCD) -f $(OOCD_FILE) \
-c "program $(realpath $(*).elf) verify reset exit" \
$(NULL)
endif clean:
rm -rf $(BUILD_DIR) $(GENERATED_BINS) .PHONY: all clean flash
-include $(OBJS:.o=.d)

my-project 的 Makefile

用户项目的 Makefile 主要用于定义一些路径和名称变量, 最后是引用 genlink-config.mk, rules.mk, genlink-rules.mk 这三个文件进行编译

# 注: 设置项目输出的固件文件名
PROJECT = awesomesauce
# 注: 编译中间产物的存放目录
BUILD_DIR = bin # 注: 下面会将这个目录设置为 VPATH, 用于放置共用代码或其它的库
SHARED_DIR = ../my-common-code
# 注: 编译项目时需要添加的C文件
CFILES = my-project.c
CFILES += api.c
# 注: 汇编文件
AFILES += api-asm.S # TODO - you will need to edit these two lines!
# 注: MCU型号, 用于生成ld文件
DEVICE=stm32f103c6t6
# 注: 用于 openocd 下载和调试的配置文件, 如果不用 openocd, 可以不管
OOCD_FILE = board/stm32f4discovery.cfg
# 注: 如果不设置PATH, 这里要把工具链的路径加上
PREFIX ?= /opt/gcc-arm/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi- # You shouldn't have to edit anything below here.
VPATH += $(SHARED_DIR)
# 注: 头文件路径, libopencm3在rules.mk中会包含, 这里不需要写
INCLUDES += $(patsubst %,-I%, . $(SHARED_DIR))
OPENCM3_DIR=../libopencm3 include $(OPENCM3_DIR)/mk/genlink-config.mk
include ../rules.mk
include $(OPENCM3_DIR)/mk/genlink-rules.mk

LibOpenCM3(二) 项目模板 Makefile分析的更多相关文章

  1. 二、主目录 Makefile 分析(1)

    2.1 uboot 版本号 uboot 的版本号分三个级别: VERSION:主版本号 PATCHLEVEL:次版本号,为补丁级别 SUBLEVEL:再次版本号 EXTRAVERSION:附加版本信息 ...

  2. WindowsPhone自定义控件详解(二) - 模板类库分析

    转自:http://blog.csdn.net/mr_raptor/article/details/7251948 WindowsPhone自定义控件详解(一) - 控件类库分析 上一节主要分析了控件 ...

  3. 二、主目录 Makefile 分析(3)

    2.8 编译选项 重新回到 主 makefile 中执行 2.8.1 u-boot  的编译顺序 代码166 到 189 行,这些是 u-boot 文件编译的顺序,由代码可以看到,首先是从cpu/$( ...

  4. 二、主目录 Makefile 分析(2)

    2.7 编译选项---config.mk 代码 163 164 行 # load other configuration include $(TOPDIR)/config.mk 此段就是包含顶层目录下 ...

  5. 从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板

    标题:从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11155 ...

  6. Visual Studio 项目模板制作(二)

    上一篇,我们制作了项目模板,本篇我制作项模板 首先,从我们需要导出模板的项目中,文件->导出模板,弹出 导出模板向导 对话框 选择项模板,点击下一步 选择要导出的项,点击下一步 选择要Refer ...

  7. AIR32F103(三) Linux环境基于标准外设库的项目模板

    目录 AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告 AIR32F103(二) Linux环境和LibOpenCM3项目模板 AIR32F103(三) Linux环境基于标准外 ...

  8. Android发展的一个重要方面Makefile分析

    Android发展的一个重要方面Makefile分析 随着移动互联网的发展,移动开发也越来越吃香了.眼下最火的莫过于android.android是什么就不用说了,android自从开源以来,就受到非 ...

  9. Cookiecutter: 更好的项目模板工具:(1)简介及可用资源汇总

    原文档地址:https://cookiecutter.readthedocs.io/en/latest/ 本系列只介绍cookiecutter的基础使用,而且会删除与功能使用无关的部分.深度使用及了解 ...

随机推荐

  1. SSTI(以Twig模板引擎为例)

    一.模板注入与常见Web注入 就注入类型的漏洞来说,常见 Web 注入有:SQL 注入,XSS 注入,XPATH 注入,XML 注入,代码注入,命令注入等等.注入漏洞的实质是服务端接受了用户的输入,未 ...

  2. session反序列化

    先来了解一下关于session的一些基础知识 什么是session?在计算机中,尤其是在网络应用中,称为"会话控制".Session 对象存储特定用户会话所需的属性及配置信息.这样 ...

  3. 解析HetuEngine实现On Yarn原理

    摘要:本文介绍HetuEngine实现On Yarn的原理,通过阅读本文,读者可以了解HetuEngine如何在资源使用方面融入Hadoop生态体系. 本文分享自华为云社区<MRS HetuEn ...

  4. 利用quake捡洞

    quake一开漏洞全靠捡 定位资产 通过主域名定位子域名资产 domain:"target.com" 通过C段定位资产 ip: "1.1.1.1/24" 通过证 ...

  5. Solon 开发,六、提取Bean的函数进行定制开发

    Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...

  6. 使用 SourceGenerator 简化 Options 绑定

    目录 摘要 Options 绑定 使用 SourceGenerator 简化 如何 Debug SourceGenerator 如何 Format 生成的代码 使用方法 SourceCode & ...

  7. 返回值String是文本数据

    MyController类中: index.jsp中 修改text前: 改为text后: 还是有乱码是因为使用这个ISO-8859-1编码处理的 MyController中修改注解中属性

  8. Springboot +redis+⾕歌开源Kaptcha实现图片验证码功能

    Springboot +redis+⾕歌开源Kaptcha实现图片验证码功能 背景 注册-登录-修改密码⼀般需要发送验证码,但是容易被 攻击恶意调⽤ 什么是短信-邮箱轰炸机 手机短信轰炸机是批.循环给 ...

  9. 学习JAVAWEB第十天

    今天内容: 运行servlet到崩溃,一直是404 明天继续运行

  10. Java使用DOM方式读写XML

    一.DOM读取 import ***; public class ReadXML { public static void main(String[] args) { try { //DOM Docu ...