• LibOpenCM3(二) 项目模板 Makefile分析


    目录

    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
    

    参考

  • 相关阅读:
    jenkins持续集成
    对pm2对研究
    模板⽅法模式
    python configparser模块
    Python正则表达式
    Python读写文件之换行符
    Python字符串
    Python字典
    Python列表
    爬虫 urllib,requests,lxml,bs4 小结
  • 原文地址:https://www.cnblogs.com/milton/p/15921252.html
Copyright © 2020-2023  润新知