开发快智能硬件开发
400万+工程师在用
华为|鸿蒙开发者日
直播报名
400万+工程师在用
华为|鸿蒙开发者日
直播报名

城东

8年用户 357经验值
擅长:嵌入式技术 模拟技术 控制/MCU
私信 关注
[讨论]

【小e1开发板试用体验】重新深究编译架构一接着~/Share/SDK/makefile分析

2016-12-17 13:06

    这里延续之前的分析:https://bbs.elecfans.com/forum.p ... =1098846&extra=
    在上一篇文章中我们分析到了定义ShortcutRule函数,那么现在我们继续:
define MakeLibrary
DEP_LIBS_$(1) = $$(foreach lib,$$(filter %.a,$$(COMPONENTS_$(1))),$$(dir $$(lib))$$(LIBODIR)/$$(notdir $$(lib)))
DEP_OBJS_$(1) = $$(foreach obj,$$(filter %.o,$$(COMPONENTS_$(1))),$$(dir $$(obj))$$(OBJODIR)/$$(notdir $$(obj)))
$$(LIBODIR)/$(1).a: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1))
    @MKDIR -p $$(LIBODIR)
    $$(IF $$(filter %.a,$$?),mkdir -p $$(EXTRACT_DIR)_$(1))
    $$(if $$(filter %.a,$$?),cd $$(EXTRACT_DIR)_$(1); $$(foreach lib,$$(filter %.a,$$?),$$(AR) xo $$(UP_EXTRACT_DIR)/$$(lib);))
    $$(AR) ru $$@ $$(filter %.o,$$?) $$(if $$(filter %.a,$$?),$$(EXTRACT_DIR)_$(1)/*.o)
    $$(if $$(filter %.a,$$?),$$(RM) -r $$(EXTRACT_DIR)_$(1))
endef
    这里定义MakeLibrary这个函数,这个函数中定义了DEP_LIBS_$(1)、DEP_OBJS_$(1)这些定义都太复杂了,我们慢慢的分析,要分析DEP_LIBS_$(1),首先就要分析他的调用语句:$(foreach lib,$(GEN_LIBS),$(eval $(call MakeLibrary,$(basename $(lib))))),这SDK根目录下并没有定义GEN_BINS,这个是在第三方软件上定义的,比如D:\Oracle\share\SDK\third_party\lwip目录下的makefile中定义了UP_EXTRACT_DIR = ..
GEN_LIBS = liblwip.a
COMPONENTS_liblwip = api/liblwipapi.a    \
             arch/liblwiparch.a    \
                        core/liblwipcore.a    \
                        core/ipv4/liblwipipv4.a    \
                        core/ipv6/liblwipipv6.a    \
                        netif/liblwipnetif.a,那么这里传给MakeLibrary的变量就是liblwip,
这里的$(1)代表的是第一个参数,这是首先$$(filter %.a,$$(COMPONENTS_$(1)))然后赋值给lib,那么这里的lib就是api/liblwipapi.a arch/liblwiparch.a core/liblwipcore.a core/ipv4/liblwipipv4.a core/ipv6/liblwipipv6.a netif/liblwipnetif.a其实这里只是滤除动作,没有进行其他的功能,最后调用$$(dir $$(lib))$$(LIBODIR)/$$(notdir $$(lib))对lib变量进行扩展,在之前我们知道LIBODIR := .output\eagle\debug\lib,那么最后得到api/.output\eagle\debug\lib/liblwipcore.a,这个就是自生成的目录,也就是最终库的组件库目录。那么这个foreach的意思就是根据liblwip指定每一个具体的组件库,接下来的DEP_OBJS_$(1)也是如此,下来就是用上面生成的两个变量在LIBODIR目录下生成相应的.a库文件。
@mkdir -p $$(LIBODIR)
        其中的@放在行首,表示不打印此行。默认在编译的过程中,会把此行的展开效果字符串打印出来。这里先先创建.output\eagle\debug\lib,注意这里是在当前的makefile目录下创建,然后:
$$(if $$(filter %.a,$$?),mkdir -p $$(EXTRACT_DIR)_$(1))其中$$?代表依赖文件列表中被改变过的所有文件,如果处在就会创建EXTRACT_DIR_xx.a,下来$$(if $$(filter %.a,$$?),cd $$(EXTRACT_DIR)_$(1); $$(foreach lib,$$(filter %.a,$$?),$$(AR) xo $$(UP_EXTRACT_DIR)/$$(lib);))这里进入刚才那个目录调用AR工具来创建静态库,这里生成到(UP_EXTRACT_DIR)/$$(lib)目录下,下来
$$(AR) ru $$@ $$(filter %.o,$$?) $$(if $$(filter %.a,$$?),$$(EXTRACT_DIR)_$(1)/*.o)
虽然他们都是运行AR但是路径应该是不一样的。最后:
$$(if $$(filter %.a,$$?),$$(RM) -r $$(EXTRACT_DIR)_$(1))
调用RM工具删除掉(EXTRACT_DIR)_$(1)目录,接下来的define MakeImage的定义和上面的相似,只不过用的是CC工具来编译:
$$(CC) $$(LDFLAGS) $$(if $$(LINKFLAGS_$(1)),$$(LINKFLAGS_$(1)),$$(LINKFLAGS_DEFAULT) $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1))) -o $$@
  因为我们不需要最终文件,所以这里也不看他的具体意思了。接下来:
$(BINODIR)/%.bin: $(IMAGEODIR)/%.out
    @mkdir -p $(BIN_PATH)
    @mkdir -p $(BINODIR)
     这里创建BIN_PATH和BINODIR这两个目录$(BINODIR)/%.bin,也就是~share\SDK\et_app\.output\eagle\debug\bin下的所有点bin,接下来:
ifeq ($(APP), 0)
    @$(RM) -r $(BIN_PATH)/eagle.S $(BIN_PATH)/eagle.dump
    @$(OBJDUMP) -x -s $< > $(BIN_PATH)/eagle.dump
    @$(OBJDUMP) -S $< > $(BIN_PATH)/eagle.S
else
    @mkdir -p $(BIN_PATH)/upgrade
    @$(RM) -r $(BIN_PATH)/upgrade/$(BIN_NAME).S $(BIN_PATH)/upgrade/$(BIN_NAME).dump
    @$(OBJDUMP) -x -s $< > $(BIN_PATH)/upgrade/$(BIN_NAME).dump
    @$(OBJDUMP) -S $< > $(BIN_PATH)/upgrade/$(BIN_NAME).S
endif
这里根据APP这边变量进行反汇编我们这里传入的不是0,所以这里运行else分支,接下来:
    @$(OBJCOPY) --only-section .text -O binary $< eagle.app.v6.text.bin
    @$(OBJCOPY) --only-section .data -O binary $< eagle.app.v6.data.bin
    @$(OBJCOPY) --only-section .rodata -O binary $< eagle.app.v6.rodata.bin
    @$(OBJCOPY) --only-section .irom0.text -O binary $< eagle.app.v6.irom0text.bin
这里把相应的bin文件拷贝到最终程序的相应分支中,这样好像就不需要连接脚本了,接下来输出:
    @echo ""
    @echo "!!!"
    @echo "SDK_PATH: $(SDK_PATH)"
    然后:
ifeq ($(app), 0)
    @python $(SDK_PATH)/tools/gen_appbin.py $< 0 $(mode) $(freqdiv) $(size_map)
    @mv eagle.app.flash.bin $(BIN_PATH)/eagle.flash.bin
    @mv eagle.app.v6.irom0text.bin $(BIN_PATH)/eagle.irom0text.bin
    @rm eagle.app.v6.*
    @echo "BIN_PATH: $(BIN_PATH)"
    @echo ""
    @echo "No boot needed."
    @echo "Generate eagle.flash.bin and eagle.irom0text.bin successully in BIN_PATH"
    @echo "eagle.flash.bin-------->0x00000"
    @echo "eagle.irom0text.bin---->0x20000"
else
    @echo "BIN_PATH: $(BIN_PATH)/upgrade"
    @echo ""

    ifneq ($(boot), new)
        @python $(SDK_PATH)/tools/gen_appbin.py $< 1 $(mode) $(freqdiv) $(size_map)
        @echo "Support boot_v1.1 and +"
    else
        @python $(SDK_PATH)/tools/gen_appbin.py $< 2 $(mode) $(freqdiv) $(size_map)

        ifeq ($(size_map), 6)
        @echo "Support boot_v1.4 and +"
        else
            ifeq ($(size_map), 5)
        @echo "Support boot_v1.4 and +"
            else
        @echo "Support boot_v1.2 and +"
            endif
        endif
    endif

    @mv eagle.app.flash.bin $(BIN_PATH)/upgrade/$(BIN_NAME).bin
    @rm eagle.app.v6.*
    @echo "Generate $(BIN_NAME).bin successully in BIN_PATH"
    @echo "boot.bin------------>0x00000"
    @echo "$(BIN_NAME).bin--->$(addr)"
endif

    @echo "!!!"
    这里依旧是执行else分支,这里最主要的工作就是输出关于这个bin地址的详细信息,注意上面的内筒都是在make all之后运行的,现在来看看make 第一个目标,make all:
all:    .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
    看看第一个目标:
.subdirs:
    @set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);这里进入SUBDIRS定义的子目录中编译所有的程序,调用子目录的makefile进行编译工作,下来的$(OBJS)是编译所有的.o文件,根据所有的.c .CPP .S  .s进行编译,OBJS在前边的定义如下:
OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \
        $(CPPSRCS:%.cpp=$(OBJODIR)/%.o) \
        $(ASRCs:%.s=$(OBJODIR)/%.o) \
        $(ASRCS:%.S=$(OBJODIR)/%.o)
    下来$(OLIBS)生成所有的库文件,接下来$(OIMAGES)生成所有的镜像文件,接下来OBINS生成所有的.bin文件,这里就会调用上面的$(BINODIR)/%.bin: $(IMAGEODIR)/%.out分支打印出bin的相关信息。最后$(SPECIAL_MKTARGETS)这里好像没有定义SPECIAL_MKTARGETS,所以这里不会运行。
    最后这个makefile文件声明的变量有些不一样:
$(foreach lib,$(GEN_LIBS),$(eval $(call ShortcutRule,$(lib),$(LIBODIR))))

$(foreach image,$(GEN_IMAGES),$(eval $(call ShortcutRule,$(image),$(IMAGEODIR))))

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

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

$(foreach image,$(GEN_IMAGES),$(eval $(call MakeImage,$(basename $(image)))))
其实这里就是从各个子目录中抽取变量了,这才是最关键的地方。整体来看就是通过上面的这四个for循环找出所有要进行编译的库,然后进行编译。

上面这篇文章说得还是不够清楚,等我沉淀一段时间我再来总结一下!

更多回帖

打开APP