目录

Makefile基本概念

下面这些是在项目的Makefile中会用到的, 主要就说一下赋值和$方法

赋值=, :=, ?=, +=

Makefile中的赋值, 可以赋值一个列表, 例如

VAR = dir1 dir2 dir3

= 赋值

make会将整个Makefile展开后, 再决定变量的值, 也就是说变量的值会是整个 Makefile 中最后被指定的值

x = foo
y = $(x) bar
x = xyz

y的值会是 xyz bar ,而不是 foo bar

:= 赋值

表示变量的值等于 Makefile 执行到此处时的值, 而不是整个 Makefile 展开后的最终值

x := foo
y := $(x) bar
x := xyz

y的值将会是 foo bar ,而不是 xyz bar

:=赋值可以用来避免递归问题, 例如下面的赋值

CC = gcc
CC = ${CC} all:
@echo ${CC}

会报错

$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually). Stop.

如果改成:=赋值就没问题了

CC := gcc
CC := ${CC} all:
@echo ${CC}

?= 赋值

  • 如果已经赋值, 就不变
  • 如果没有被赋值过, 就赋予等号后面的值

+= 赋值

添加等号后面的值

Makefile 中$的用法

$在Makefile中是一种重要的符号

$(...) 或 ${...}的方法

$()${}是一样的, 执行括号内的内容, 类似于eval, 将整个$(...)替换为执行的结果字符串

直接执行

例如$(VAR = 4)表示执行shell命令VAR = 4, 因为无返回, 所以不会有任何操作

shell 执行shell命令

$(shell cc $(SDK_TOOLS)/wm_getver.c -Wall -O2 -o $(VER_TOOL)) 这个命令表示, 在shell下执行后面的命令

abspath 返回绝对路径

PATH = $(abspath $(TOP_DIR)) 获取绝对路径

dir 返回目录字符串

SUBDIRS ?= $(patsubst %/,%,$(dir $(wildcard */Makefile))), 获取所有Makefile的目录, 并去除结尾的斜杆

substr 字符串替换

TARGET ?= $(subst FROM, TO, TEXT), 这个命令会将字符串TEXT中的子串FROM变为TO后返回, 然后返回的值赋值给TARGET

patsubst 匹配替代

patsubst是patten substitude的缩写,匹配替代的意思

OBJ = $(patsubst %.c, %.o, $(SRC)) , 在SRC中找到所有.c 结尾的文件,然后把所有的.c换成.o。

与使用OBJ = $(SRC:%.c=%.o)效果一样.

wildcard 列出文件

$(wildcard pattern) 是用于用于匹配列出文件名的方法

  • SRCS := $(wildcard *.c)这个表达式中, 所有以.c结尾的文件名都会被赋值给变量SRCS.
  • CCFILES += $(wildcard src/*.cpp), 将匹配后面通配符的文件都列出, 并追加赋值给CCFILES

$(变量名:% = %) 替代

BINS := $(SRCS:%.c=%) 这种方式属于替代赋值, 在这个表达式中, 如果变量 SRCS 的值为 'foo.c bar.c', 变量 BINS 将被赋值 'foo bar'.

notdir 去除目录信息

SRC = $(notdir wildcard), 去除所有的目录信息,SRC里的文件名列表将只有文件名

foreach 循环处理

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

所以: var是一个变量, list可以是一个表达式或者一个列表变量, 而express一般是一个命令表达式, 用于处理var.

例子一

在递归编译中常用的 $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);), 将SUBDIRS中收集到的目录, 值依次执行$(MAKE) -C $(d).

例子二

names := a b c d
files := $(foreach n,$(names),$(n).o)

将names中的值依次加上.o, 再赋值给files, $(files)的值变为a.o b.o c.o d.o

WM-SDK-W806 的 Makefile 分析

Makefile 文件结构

代码地址

下面列出了 WM-SDK-W806 中, 与make相关的文件, 可以看到这是一个递归make的结构. 主要的文件都已经在结构中标出

│  Makefile                  # 主Makefile文件, make 执行入口
├─app
│ │ Makefile
│ ├─inc
│ └─src
│ Makefile
├─bin
│ └─build
│ └─W806
│ ├─lib
│ └─obj
├─demo
│ Makefile

├─platform
│ ├─arch
│ │ │ Makefile
│ │ └─xt804
│ │ │ Makefile
│ │ ├─bsp
│ │ │ Makefile
│ │ └─libc
│ │ Makefile
│ ├─component
│ │ │ Makefile
│ │ ├─auto_dl
│ │ │ Makefile
│ │ └─FreeRTOS
│ │ │ Makefile
│ │ ├─include
│ │ └─portable
│ │ │ Makefile
│ │ ├─MemMang
│ │ │ Makefile
│ │ └─xt804
│ │ Makefile
│ └─drivers
│ Makefile
└─tools
└─W806
│ .config # 由menuconfig生成的,被conf.mk包含的配置信息
│ conf.mk # 全局make配置文件, 会被每一个Makefile头部包含
│ inc.mk # 全局的include配置, 在conf.mk底部包含, 所以实际上也会被每一个Makefile包含
│ mconfig.sh # make menuconfig时调起的shell脚本
│ rules.mk # 全局的make目标文件, 会被每一个Makefile底部包含
│ wconfig # menuconfig的字段配置文件
├─projects
└─utilities

Make 的执行顺序

执行make时,

  1. 先执行主Makefile,
  2. 主Makefile中依次包含conf.mk, inc.mk, rule.mk
  3. 行进到rule.mk, 在其中中执行指定的目标, 如果未指定, 则执行默认的all目标.

Make的规则流程 - RULE.MK

这里重点分析rule.mk文件, 因为这里定义了所有的规则, 以及对应的目标处理关系. 当执行默认的all目标时, 其规则定义为

all: .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS)

目标 .subdir

这一步实现了对子目录的递归访问

  1. 在all目标中, 第一个前置目标为.subdirs
  2. .subdirs目标的执行是@set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);),
    • set -e使得执行的命令返回值非0时, make立即退出
    • $(foreach ...)的功能前面写了, 在这里就是遍历SUBDIRS中收集到的子目录列表, 对每一项依次执行make -C 子目录
    • SUBDIRS的定义SUBDIRS ?= $(patsubst %/,%,$(dir $(wildcard */Makefile))) , 注意, 这里只是列出当前目录的下一级目录中包含的Makefile, 如果是跨一级目录的Makefile是不会包含进来的
    • SUBDIRS除了收集的子目录, 还有一处从主Makefile添加的platform目录下的三个路径
  3. 到这一步, make进行到了下一层, 对下一层同样进行前面的处理
  4. 当进行到最底层时, 已经没有可执行的.subdirs目标, make会开始执行后面的目标$(OBJS)

目标: $(OBJS)

这一步实现了从C, CPP, S文件到对象文件的编译. OBJS的定义如下, 收集了当前目录下的.c, .cpp, .S文件, 产生.o的对象文件列表, 因此这里会处理一系列的.o文件前置目标

OBJS := $(CSRCS:%.c=$(OBJODIR)/$(subdir_path)/%.o) \
$(CPPSRCS:%.cpp=$(OBJODIR)/$(subdir_path)/%.o) \
$(ASRCS:%.S=$(OBJODIR)/$(subdir_path)/%.o)

对于.o文件的规则是如下的三条, 这是编译时实际执行的命令

$(OBJODIR)/$(subdir_path)/%.o: %.c
@mkdir -p $(OBJODIR)/$(subdir_path)
$(CC) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) $(INCLUDES) $(CMACRO) -c "$<" -o "$@" -MMD -MD -MF "$(@:$(OBJODIR)/$(subdir_path)/%.o=$(OBJODIR)/$(subdir_path)/%.o.d)" -MT "$(@)" $(OBJODIR)/$(subdir_path)/%.o: %.cpp
@mkdir -p $(OBJODIR)/$(subdir_path)
$(CXX) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CXXFLAGS)) $(COPTS_$(*F)) $(INCLUDES) $(CMACRO) -c "$<" -o "$@" -MMD -MD -MF "$(@:$(OBJODIR)/$(subdir_path)/%.o=$(OBJODIR)/$(subdir_path)/%.o.d)" -MT "$(@)" $(OBJODIR)/$(subdir_path)/%.o: %.S
@mkdir -p $(OBJODIR)/$(subdir_path)
$(ASM) $(ASMFLAGS) $(INCLUDES) $(CMACRO) -c "$<" -o "$@"

在目标 \((OBJS)完成后, 开始执行目标\)(OLIBS)

目标:$(OLIBS)

这一步是将对象文件打包成库, 来源定义为

OLIBS := $(GEN_LIBS:%=$(LIBODIR)/%)

其中GEN_LIBS定义在每一层的Makefile中, 是一个层层打包的过程, 为生成库文件, 设计了两套规则

  1. 在rule.mk中定义了名为 ShortcutRule 的规则宏用于展开
define ShortcutRule
$(1): .subdirs $(2)/$(1)
endef

通过以下语句展开为每个库的目标规则

$(foreach lib,$(GEN_LIBS),$(eval $(call ShortcutRule,$(lib),$(LIBODIR))))
  1. 在rule.mk中定义了名为 MakeLibrary 的规则宏用于展开
define MakeLibrary
DEP_LIBS_$(1) = $$(foreach lib,$$(filter %$(LIB_EXT),$$(COMPONENTS_$(1))),$$(LIBODIR)/$$(notdir $$(lib)))
DEP_OBJS_$(1) = $$(foreach obj,$$(filter %.o,$$(COMPONENTS_$(1))),$$(OBJODIR)/$$(notdir $$(obj)))
$$(LIBODIR)/$(1)$(LIB_EXT): $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1))
@mkdir -p $$(LIBODIR)
$$(if $$(filter %$(LIB_EXT),$$?),@mkdir -p $$(OBJODIR)/_$(1))
$$(if $$(filter %$(LIB_EXT),$$?),@cd $$(OBJODIR)/_$(1); $$(foreach lib,$$(filter %$(LIB_EXT),$$?),$$(AR) $(ARFLAGS_2) $$(UP_EXTRACT_DIR)/$$(notdir $$(lib));))
$$(AR) $(ARFLAGS) $$@ $$(filter %.o,$$?) $$(if $$(filter %$(LIB_EXT),$$?),$$(OBJODIR)/_$(1)/*.o)
$$(if $$(filter %$(LIB_EXT),$$?),@$$(RM) -r $$(OBJODIR)/_$(1))
endef

通过以下语句展开为各个库的目标规则, 这些规则将负责处理 ShortcutRule 的前置目标

$(foreach lib,$(GEN_LIBS),$(eval $(call MakeLibrary,$(basename $(lib)))))

这就是$(OLIBS)目标的机制

目标:$(OBINS)

这一步会打包为二进制image, 来源定义为

OBINS := $(GEN_BINS:%=$(BINODIR)/%)

其中GEN_BINS在主Makefile中定义, 就是W806.bin

GEN_BINS = $(TARGET).bin

首先也是通过 ShortcutRule 展开的规则集, 用于处理目标 $(OBINS)

$(foreach bin,$(GEN_BINS),$(eval $(call ShortcutRule,$(bin),$(BINODIR))))

然后通过下面的规则, 去处理 \((ODIR)/\)(TARGET)/bin/W806.bin目标

$(BINODIR)/%.bin: $(IMAGEODIR)/%.elf
@mkdir -p $(FIRMWAREDIR)
@mkdir -p $(FIRMWAREDIR)/$(TARGET)
$(OBJCOPY) -O binary $(IMAGEODIR)/$(TARGET).elf $(FIRMWAREDIR)/$(TARGET)/$(TARGET).bin

再通过 ShortcutRule 展开的规则集, 处理目标 $(IMAGEODIR)/%.elf

$(foreach image,$(GEN_IMAGES),$(eval $(call ShortcutRule,$(image),$(IMAGEODIR))))
```makefile
再通过 MakeImage 展开的规则集, 处理目标 $$(IMAGEODIR)/W806.elf
```makefile
define MakeImage
DEP_LIBS_$(1) = $$(foreach lib,$$(filter %$(LIB_EXT),$$(COMPONENTS_$(1))),$$(LIBODIR)/$$(notdir $$(lib)))
DEP_OBJS_$(1) = $$(foreach obj,$$(filter %.o,$$(COMPONENTS_$(1))),$$(OBJODIR)/$$(notdir $$(obj)))
$$(IMAGEODIR)/$(1).elf: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1))
@mkdir -p $$(IMAGEODIR)
$(LINK) -Wl,--gc-sections -Wl,-zmax-page-size=1024 -Wl,--whole-archive $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(if $$(LINKFLAGS_$(1)),$$(LINKFLAGS_$(1))) -Wl,--no-whole-archive $(LINKFLAGS) $(MAP) -o $$@
endef

以上就是WM-SDK-W806 的 Makefile 分析. 这里只说明了编译的主体流程, 在配置项上也有不少内容, 如果有人感兴趣, 可以另外开篇介绍.

联盛德 HLK-W806 (十二): Makefile组织结构和编译流程说明的更多相关文章

  1. 联盛德 HLK-W806 (十三): 运行FatFs读写FAT和exFat格式的SD卡/TF卡

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  2. 联盛德 HLK-W806 (十): 在 CDK IDE开发环境中使用WM-SDK-W806

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  3. 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  4. 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  5. 联盛德 HLK-W806 (九): 软件SPI和硬件SPI驱动ST7789V液晶LCD

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  6. 联盛德 HLK-W806 (五): W801开发板上手报告

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  7. 联盛德 HLK-W806 (三): 免按键自动下载和复位

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  8. 联盛德 HLK-W806 (四): 软件SPI和硬件SPI驱动ST7735液晶LCD

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  9. 联盛德 HLK-W806 (六): I2C驱动SSD1306 128x64 OLED液晶屏

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

随机推荐

  1. 浅讲.Net 6之ConfigurationManager

    介绍 本节为大家带来.NET 6新增的ConfigurationManager,很多人好奇为啥要讲这个,读取加载配置信息都随手就来了,我们往下看一下. 翻译:这添加了 ASP.NET Core 的新 ...

  2. plink 进行PCA分析

    当我们进行群体遗传分析时,得到vcf后,可利用plink进行主成分(PCA)分析: 一.软件安装 1 conda install plink 二.使用流程 第一步:将vcf转换为plink格式 1 p ...

  3. Ansi,UTF8,Unicode,ASCII编码的区别

    Ansi,UTF8,Unicode,ASCII编码的区别 近日需要不同的编码,关于上述编码,一直迷迷糊糊,查了些资料,总算大致了解了, 下面全是从网上搜来的: 1.  ASCII和Ansi编码     ...

  4. git放弃修改,强制覆盖本地代码

    1.git fetch --all  //从远程拉取最新的代码 不merge 2.git reset --hard origin/develop  //使用指定分支的代码(此处develop)强制覆盖 ...

  5. Linux——基础命令用法(下)

    一.linux用户 1.什么是用户 用户是用来运行某一些进程.拥有某一些文件或目录. 在Linux里面,用户分成三大类:root用户.系统用户.普通用户. 用户是用UID来唯一标识身份的,且root用 ...

  6. java运行报错 has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

    解决方法: 解决办法: 在项目的属性里设置jdk版本,方法是右击项目-->properties-->java compiler --> Enable project specific ...

  7. Hadoop入门 集群常用知识与常用脚本总结

    目录 集群常用知识与常用脚本总结 集群启动/停止方式 1 各个模块分开启动/停止(常用) 2 各个服务组件逐一启动/停止 编写Hadoop集群常用脚本 1 Hadoop集群启停脚本myhadoop.s ...

  8. 数仓day04----日志预处理2

    1.详细描述idmap的整个计算方案 (1)使用SparkSession对象读取用户不同类别的埋点日志,解析并抽取出相应的标识id,使用union进行合并,得到装有汇总标识id的rdd(ids) (2 ...

  9. 初学js正则表达式之密码强度验证

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. CSS基础语法(一)

    目录 CSS基础语法(一) 一.CSS简介 1.CSS语法规范 2.CSS代码风格 二.CSS基础选择器 1.标签选择器 2.类选择器 3.id选择器 4.通配符选择器 5.总结 三.CSS字体属性 ...