ESP8266_RTOS_SDK-2.0.0工程编译makefile 流程分析

ESP8266_RTOS_SDK-2.0.0

1.分析模板目录结构

│ Project
	│  application				#应用层代码
    	|include 				#库文件
    	|user					#主函数及.c文件
    		|makefile
    	|makefile	
    │  bin						#生成的烧录程序
    	|upgrade				#支持OTA升级会生成该文件,反之用接下来的两个文件
    	|eagle.flash.bin
    	|eagle.irom0text.bin
	|  ESP8266_RTOS_SDK-2.0.0	#ESP8266 SDK
    	|bin
    	|document
    	|drive_lib
    	|examples
    	|extra_include
    	|include
    	|ld
    	|lib
    	|third_party
    	|tools
    	|LICENSE
    	|makefile

从目录结构中,可以看出,编译一份程序中有三个makefile参与其中,因此接下来我们要分析三个makefile之间的关系。

2.工程构建

​ 本人是下载了ESP8266-RTOS-SDK-2.0.0,新建了一个Project文件夹,将ESP8266-RTOS-SDK-2.0.0放置到该文件下,拷贝ESP8266_RTOS_SDK-2.0.0/examples目录下的project_template文件夹到工程文件夹Device_Project下,重命名为application,作为应用层代码的目录。删除application目录(原project_template目录)下的多余的目录sample_lib。

3. 工程中makefile之间的关系

(1)user目录下makefile文件

ifndef PDIR
GEN_LIBS = libuser.a
endif


INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile

​ 在该文件中,我们可以看到语句 sinclude $(PDIR) Makefile ,这句语句的意思是将变量 PDIR 目录下的makefile文件包含进来(类似于C语言中的 #include “头文件名”),执行时会将包含文件在此处展开。同时变量$(PDIR)表示user目录的上一级目录PDIR := …/$(PDIR)即,application目录下的makefile文件。

(2)application目录下的文件

TARGET = eagle
#FLAVOR = release
FLAVOR = debug

#EXTRA_CCFLAGS += -u

ifndef PDIR # {
GEN_IMAGES= eagle.app.v6.out
GEN_BINS= eagle.app.v6.bin
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
SUBDIRS=    \
	user    \
	hilinksdk

endif # } PDIR

LDDIR = $(SDK_PATH)/ld

CCFLAGS += -Os

TARGET_LDFLAGS =		\
	-nostdlib		\
	-Wl,-EL \
	--longcalls \
	--text-section-literals

ifeq ($(FLAVOR),debug)
    TARGET_LDFLAGS += -g -O2
endif

ifeq ($(FLAVOR),release)
    TARGET_LDFLAGS += -g -O0
endif

COMPONENTS_eagle.app.v6 = \
	user/libuser.a  \
	hilinksdk/libhilinksdk.a

LINKFLAGS_eagle.app.v6 = \
	-L$(SDK_PATH)/lib        \
	-Wl,--gc-sections   \
	-nostdlib	\
    -T$(LD_FILE)   \
	-Wl,--no-check-sections	\
    -u call_user_start	\
	-Wl,-static						\
	-Wl,--start-group					\
	-lcirom \
	-lcrypto	\
	-lespconn	\
	-lespnow	\
	-lfreertos	\
	-lgcc					\
	-lhal					\
	-ljson	\
	-llwip	\
	-lmain	\
	-lmirom	\
	-lnet80211	\
	-lnopoll	\
	-lphy	\
	-lpp	\
	-lpwm	\
	-lsmartconfig	\
	-lspiffs	\
	-lssl	\
	-lwpa	\
	-lwps		\
	$(DEP_LIBS_eagle.app.v6)					\
	-Wl,--end-group

DEPENDS_eagle.app.v6 = \
                $(LD_FILE) \
                $(LDDIR)/eagle.rom.addr.v6.ld

CONFIGURATION_DEFINES =	-DICACHE_FLASH

DEFINES +=				\
	$(UNIVERSAL_TARGET_DEFINES)	\
	$(CONFIGURATION_DEFINES)

DDEFINES +=				\
	$(UNIVERSAL_TARGET_DEFINES)	\
	$(CONFIGURATION_DEFINES)


INCLUDES := $(INCLUDES) -I $(PDIR)include
sinclude $(SDK_PATH)/Makefile

.PHONY: FORCE
FORCE:


​ 同样,在application目录中的makefile文件中同样包含了目录$(SDK_PATH)下的makefile文件(这里变量$(SDK_PATH)是定义在application下的脚本文件gen_misc_sh中,需要在执行脚本之前设置),因此会将ESP8266_RTOS_SDK-2.0.0目录下的makefile文件在该处展开,以下是本人工程中的设置。

export SDK_PATH=$(pwd)/../ESP8266_RTOS_SDK-2.0.0
export BIN_PATH=$(pwd)/../bin

​ 第一句意思是将变量SDK_PATH(用来编译的集成环境)设置为当前目录下(application)的上一级的ESP8266_RTOS_SDK-2.0.0

​ 第二局意思是将变量BIN_PATH(用来生成编译完成的可执行文件)设置为当前目录下(application)的上一级的bin文件(如果没有就新建一个)

从以上的分析中可以得出,user目录下的makefile文件(以下用user.makefile表示,后边同理),包含了application.makefile,而application.makefile又包含了ESP8266SDK.makefile,结构如下图所示。

ESP8266_RTOS_SDK-2.0.0工程编译makefile 流程分析_第1张图片

3.流程分析

(1)脚本文件分析

​ 当我们在application目录下执行命令sh gen_misc_sh时(用来编译整体工程),便会执行脚本文件gen_misc_sh

#!/bin/bash

#在编译之前要先设置好编译环境以及编译生成的可执行文件存放目录,这里采用相对目录的写法

export SDK_PATH=$(pwd)/../ESP8266_RTOS_SDK-2.0.0         
export BIN_PATH=$(pwd)/../bin

export PATH=/opt/toolchains/xtensa-lx106-elf/bin:$PATH

#如果是第一次编译,就先创建一个bin目录,存放编译生成的文件

if [ ! -d "../bin" ]; then
   mkdir ../bin
fi

#打印信息

echo "gen_misc.sh version 20150911"
echo ""

if [ $SDK_PATH ]; then
    echo "SDK_PATH:"
    echo "$SDK_PATH"
    echo ""
else
    echo "ERROR: Please export SDK_PATH in gen_misc.sh firstly, exit!!!"
    exit
fi

if [ $BIN_PATH ]; then
    echo "BIN_PATH:"
    echo "$BIN_PATH"
    echo ""
else
    echo "ERROR: Please export BIN_PATH in gen_misc.sh firstly, exit!!!"
    exit
fi

#确定开发板型号之后可以将变量写死,避免每次编译时都需要输入相同的配置选项
#如 input= 固定值,这里我是修改过的
echo "Please check SDK_PATH & BIN_PATH, enter (Y/y) to continue:"
input=y

if [[ $input != Y ]] && [[ $input != y ]]; then
    exit
fi

echo ""

echo "Please follow below steps(1-5) to generate specific bin(s):"
echo "STEP 1: use boot_v1.2+ by default"
boot=new

echo "boot mode: $boot"
echo ""

echo "STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)"
echo "enter (0/1/2, default 0):"

read input

if [ -z "$input" ]; then
    if [ $boot != none ]; then
    	boot=none
	echo "ignore boot"
    fi
    app=0
    echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
elif [ $input == 1 ]; then
    if [ $boot == none ]; then
    	app=0
	echo "choose no boot before"
	echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
    else
	app=1
        echo "generate bin: user1.bin"
    fi
elif [ $input == 2 ]; then
    if [ $boot == none ]; then
    	app=0
	echo "choose no boot before"
	echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
    else
    	app=2
    	echo "generate bin: user2.bin"
    fi
else
    if [ $boot != none ]; then
    	boot=none
	echo "ignore boot"
    fi
    app=0
    echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
fi

echo ""

echo "STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)"
echo "enter (0/1/2/3, default 2):"
input=2

if [ -z "$input" ]; then
    spi_speed=40
elif [ $input == 0 ]; then
    spi_speed=20
elif [ $input == 1 ]; then
    spi_speed=26.7
elif [ $input == 3 ]; then
    spi_speed=80
else
    spi_speed=40
fi

echo "spi speed: $spi_speed MHz"
echo ""

echo "STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)"
echo "enter (0/1/2/3, default 0):"
input=0

if [ -z "$input" ]; then
    spi_mode=QIO
elif [ $input == 1 ]; then
    spi_mode=QOUT
elif [ $input == 2 ]; then
    spi_mode=DIO
elif [ $input == 3 ]; then
    spi_mode=DOUT
else
    spi_mode=QIO
fi

echo "spi mode: $spi_mode"
echo ""

echo "STEP 5: choose spi size and map"
echo "    0= 512KB( 256KB+ 256KB)"
echo "    2=1024KB( 512KB+ 512KB)"
echo "    3=2048KB( 512KB+ 512KB)"
echo "    4=4096KB( 512KB+ 512KB)"
echo "    5=2048KB(1024KB+1024KB)"
echo "    6=4096KB(1024KB+1024KB)"
echo "    7=4096KB(2048KB+2048KB) not support ,just for compatible with nodeMCU board"
echo "    8=8192KB(1024KB+1024KB)"
echo "    9=16384KB(1024KB+1024KB)"
echo "enter (0/2/3/4/5/6/7/8/9, default 0):"
input=5

if [ -z "$input" ]; then
    spi_size_map=0
    echo "spi size: 512KB"
    echo "spi ota map:  256KB + 256KB"
elif [ $input == 2 ]; then
    spi_size_map=2
    echo "spi size: 1024KB"
    echo "spi ota map:  512KB + 512KB"
elif [ $input == 3 ]; then
    spi_size_map=3
    echo "spi size: 2048KB"
    echo "spi ota map:  512KB + 512KB"
elif [ $input == 4 ]; then
    spi_size_map=4
    echo "spi size: 4096KB"
    echo "spi ota map:  512KB + 512KB"
elif [ $input == 5 ]; then
    spi_size_map=5
    echo "spi size: 2048KB"
    echo "spi ota map:  1024KB + 1024KB"
elif [ $input == 6 ]; then
    spi_size_map=6
    echo "spi size: 4096KB"
    echo "spi ota map:  1024KB + 1024KB"
elif [ $input == 7 ]; then
    spi_size_map=7
    echo"not support ,just for compatible with nodeMCU board"
    exit
elif [ $input == 8 ]; then
    spi_size_map=8
    echo "spi size: 8192KB"
    echo "spi ota map:  1024KB + 1024KB"
elif [ $input == 9 ]; then
    spi_size_map=9
    echo "spi size: 16384KB"
    echo "spi ota map:  1024KB + 1024KB"
else
    spi_size_map=0
    echo "spi size: 512KB"
    echo "spi ota map:  256KB + 256KB"
fi

echo ""

echo "start..."
echo ""

make clean

make BOOT=$boot APP=$app SPI_SPEED=$spi_speed SPI_MODE=$spi_mode SPI_SIZE_MAP=$spi_size_map

​ 执行命令后,脚本文件需要编译者输入配置信息,如OTA支持与否,SPI speed,SPI mode,size_map等信息,具体的配置需要根据开发板以及乐鑫官网的技术文档进行输入。

​ 当输入完成之后,脚本文件便开始运行,首先执行make clean命令,该命令会立刻去找该目录下的Makefile文件(或者是makefile文件运行),由于脚本文件gen_misc_sh是在application目录下,因此先执行application.makefile,由于application.makefile包含了ESP8266SDK.makefile,因此先去找这两个文件中是否又clean命令,在ESP8266SDK.makefile中,存在clean命令

clean:
  $(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clean;)
	$(RM) -r $(ODIR)/$(TARGET)/$(FLAVOR)

​ 上边第二句的意思是,将变量SUBDIRS的值,放到d中,利用d执行后边的命令,而在application.makefile中定义了变量SUBDIRS

ifndef PDIR # {
GEN_IMAGES= eagle.app.v6.out
GEN_BINS= eagle.app.v6.bin
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
SUBDIRS=    \
	user    
endif # } PDIR

​ 由上述程序可得, make -C user clean,既执行user.application中的clean命令。又因为,user.makefile包含了application.makefile以及ESP8266SDK.makefile,因此会直接执行user.makefile下包含的ESP8266SDK.makefile中的clean命令。这里有人会问,从application.makefile的clean命令 ,跳转到user.makefile包含ESP8266SDK.makefile的clean不会构成死循环吗?由于在
user.makefile中含有这样一句PDIR := …/$(PDIR),该句定义了变量PDIR为上一级目录,而变量SUBDIRS的定义是建立在变量PDIR没有定义的基础上,如下图

ifndef PDIR # {
GEN_IMAGES= eagle.app.v6.out
GEN_BINS= eagle.app.v6.bin
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
SUBDIRS=    \
	user    \
	hilinksdk

endif # } PDIR

因此,变量SUBDIRS没有被定义,在user.makefile中包含的ESP8266SDK.makefile循环语句不会执行,只执行清除命令$(RM) -r ( O D I R ) / (ODIR)/ (ODIR)/(TARGET)/$(FLAVOR)

清除之后,退回到application目录下。因此在整个工程编译过程中,一定要有一个意识,既,脚本文件执行的make命令是在application.makefile开始的,他会跳转到user.makefile执行相应的命令后跳转回application.makefile继续执行接下来的命令,二者对应的关系更像是下图所示

ESP8266_RTOS_SDK-2.0.0工程编译makefile 流程分析_第2张图片

执行完make clean命令后,就该执行make命令了

(2)编译指令 make 流程分析

​ 首先,gen_misc_sh脚本执行make命令,将去同目录下的makefile找make指令,在其包含的ESP8266SDK.makefile中找到all之后开始执行,all由6个依赖构成,因此分别开始执行。注意此时还在application目录下的执行命令(all 在application.makefile中包含的ESP8266SDK.makefile,仍属于application目录)

all:	.subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
  1. 执行 .subdirs (application目录下)

​ 定义:

.subdirs:
	@set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);)

​ 由之前分析可得,变量SUBDIRS在未进入user目录时是存在定义的,因此,该循环语句会进入user目录下执行make命令

  1. 进入user.makefile执行make(user目录下)

    ​ 由于user.makefile文件中定义了变量PDIR,因此变量SUBDIRS将不会被定义,user.makefile下的 all 的$(foreach d, $(SUBDIRS), $(MAKE) -C $(d)命令由于变量SUBDIRS的未定义而不去执行,直接进入$(OBJS)的执行

  2. user.makefile下执行$(OBJS)(user目录下)

    定义:

    CSRCS ?= $(wildcard *.c)
    #OBJODIR = .output/eagle/debug/obj     将变量OBJODIR展开
    OBJODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/obj
    
    OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \
            $(CPPSRCS:%.cpp=$(OBJODIR)/%.o) \
            $(ASRCs:%.s=$(OBJODIR)/%.o) \
            $(ASRCS:%.S=$(OBJODIR)/%.o)
    

    上述语句的意思为,在CSRCS获取的当前目录下的.c文件中全部加上OBJODIR的路径,并将.c替换成.o,实际变量OBJS展开的形式为

    .output/eagle/debug/obj/gpio.o .output/eagle/debug/obj/hw_timer.o .output/eagle/debug/obj/uart.o

    这些.o文件的生成以及依赖如下图

    $(OBJODIR)/%.o: %.c 
    	@mkdir -p $(OBJODIR);
    	$(CC) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $<
    

    生成这些.o文件,首先需要有对应的.c文件,以及执行2,3语句,先建立.output/eagle/debug/obj目录,根据gcc编译规则编译出.o文件存放在该目录中。

  3. user.makefile下执行$(OLIBS)(user目录下)

    ​ 定义

    # LIBODIR = .output/eagle/debug/lib
    LIBODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/lib
    
    # OLIBS = .output/eagle/debug/lib/libuser.a
    OLIBS := $(GEN_LIBS:%=$(LIBODIR)/%)
    
    

    ​ 变量OLIBS是将变量LIBODIR所代表的路径加入到变量GEN_LIBS的前缀,而变量GEN_LIBS的定义如下

    ifndef PDIR
    GEN_LIBS = libuser.a
    endif
    

    ​ 此时变量GEN_LIBS存在定义,需要找到目标文件(.output/eagle/debug/lib/libuser.a)所需要的依赖文件。

    ​ 此时注意到有一个循环语句定义如下:

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

    ​ 该语句中有的eval函数原型 $(eval text),它的意思是 text 的内容将作为makefile的一部分而被make解析和执行。call调用的MakeLibrary就会被展开在makefile里面,basename函数将取变量lib中.的前缀,作为MakeLibrary中的参数值,而MakeLibrary将在该语句处展开,定义如下

    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
    

    ​ 可以看到目标文件(.output/eagle/debug/lib/libuser.a)的依赖有$$(OBJS)构成,其余的变量均为空值,运行可运行命令,创建目录,连接文件等,即可在相应文件夹内生成库文件。

  4. user.makefile下执行$(OIMAGES)和(OBINS)以及(SPECIAL_MKTARGETS)(user目录下)

    定义

    #OIMAGES = .output/eagle/debug/image/eagle.app.v6.out
    OIMAGES := $(GEN_IMAGES:%=$(IMAGEODIR)/%)
    
    # OBINS = .output/eagle/debug/bin/eagle.app.v6.bin
    OBINS := $(GEN_BINS:%=$(BINODIR)/%)
    

    application.makefile中的定义

    ifndef PDIR # {
    GEN_IMAGES= eagle.app.v6.out
    GEN_BINS= eagle.app.v6.bin
    SPECIAL_MKTARGETS=$(APP_MKTARGETS)
    SUBDIRS=    \
    	user    \
    	hilinksdk
    
    endif # } PDIR
    

    ​ 由于这两个变量是分别将BINODIRIMAGEODIR放置到变量GEN_BINSGEN_IMAGES的前缀中,而后两个变量由于在user.makefile变量中PDIR定义的存在(ifndef PDIR)而未被定义,因此两者在user目录下都不执行。同样的变量SPECIAL_MKTARGETS也不执行。因此有application.makefile跳转到user目录下的user.makefile后,只执行变量 ( O B J S ) 与 (OBJS)与 (OBJS)(OLIBS),之后便跳回application.mamkefile下继续执行。

  5. appliaction.makefile下执行$(OBJS)命令

    ​ 从前边分析可知,执行该命令需要获取到当前目录下的.c文件而在application目录下没有.c文件的存在,因此,该命令不执行,执行下一条命令

  6. appliaction.makefile下执行$(OLIBS)命令

    根据之前对于变量OLIBS的分析可得,该变量依赖变量GEN_LIBS,而在application.makefile以及包含的ESP8266SDK.makefile中,并没有对于变量GEN_LIBS的定义,因此这个命令在application下不执行。

  7. appliaction.makefile下执行$(OIMAGES)命令

    定义:

    #OIMAGES = .output/eagle/debug/image/eagle.app.v6.out
    OIMAGES := $(GEN_IMAGES:%=$(IMAGEODIR)/%)
    
    ifndef PDIR # {
    GEN_IMAGES= eagle.app.v6.out
    GEN_BINS= eagle.app.v6.bin
    SPECIAL_MKTARGETS=$(APP_MKTARGETS)
    SUBDIRS=    \
    	user    \
    	hilinksdk
    
    endif # } PDIR
    

    由于appliication目录下的application.makefile未包含user.makefile,因此没有定义变量PDIR,因此变量GEN_IMAGES存在定义。

    该变量的目标文件以及依赖文件的关系如下

    define MakeImage
    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)))     
    $$(IMAGEODIR)/$(1).out: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1))
    	@mkdir -p $$(IMAGEODIR)
    	$$(CC) $$(LDFLAGS) $$(if $$(LINKFLAGS_$(1)),$$(LINKFLAGS_$(1)),$$(LINKFLAGS_DEFAULT) $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1))) -o $$@ 
    endef
    
    # GEN_IMAGES= eagle.app.v6.out
    $(foreach image,$(GEN_IMAGES),$(eval $(call MakeImage,$(basename $(image)))))
    
    

    ​ 变量OIMAGES展开可得(.output/eagle/debug/image/eagle.app.v6.out),这个目标文件的生成依赖与$$(OBJS)(之前分析过),执行下面的可执行程序创建文件夹,编译链接等,此时会将include文件以及提供的库文件一同作为选项进行编译,生成可执行文件。

    1. 生成可下载进开发板中的可执行文件(application文件下)

      ​ 根据脚本文件中的make命令传递的参数,调用ESP8266SDK目录下的tool工具,生成可下载文件,该工具采用python环境(实在是看不懂)。

      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 "!!!"
      

    至此便可以在project目录下的bin文件中看到编译生成的,可烧录进开发板中的程序。

    最后附上我分析过程中在ESP8266SDK.makefile上标记的注释

#  copyright (c) 2010 Espressif System
#
ifndef PDIR

endif

ifeq ($(COMPILE), xcc)
    AR = xt-ar
	CC = xt-xcc
	NM = xt-nm
	CPP = xt-xt++
	OBJCOPY = xt-objcopy
	OBJDUMP = xt-objdump
else
	AR = xtensa-lx106-elf-ar
	CC = xtensa-lx106-elf-gcc
	NM = xtensa-lx106-elf-nm
	CPP = xtensa-lx106-elf-g++
	OBJCOPY = xtensa-lx106-elf-objcopy
	OBJDUMP = xtensa-lx106-elf-objdump
endif
#?= 是如果没有被赋值过就赋予等号后面的值   可以作为默认选项
BOOT?=none 
APP?=0
SPI_SPEED?=40
SPI_MODE?=QIO
SPI_SIZE_MAP?=0

ifeq ($(BOOT), new)
    boot = new
else
    ifeq ($(BOOT), old)
        boot = old
    else
        boot = none
    endif
endif

ifeq ($(APP), 1)
    app = 1
else
    ifeq ($(APP), 2)
        app = 2
    else
        app = 0
    endif
endif

ifeq ($(SPI_SPEED), 26.7)
    freqdiv = 1
else
    ifeq ($(SPI_SPEED), 20)
        freqdiv = 2
    else
        ifeq ($(SPI_SPEED), 80)
            freqdiv = 15
        else
            freqdiv = 0
        endif
    endif
endif


ifeq ($(SPI_MODE), QOUT)
    mode = 1
else
    ifeq ($(SPI_MODE), DIO)
        mode = 2
    else
        ifeq ($(SPI_MODE), DOUT)
            mode = 3
        else
            mode = 0
        endif
    endif
endif

addr = 0x01000

ifeq ($(SPI_SIZE_MAP), 1)
  size_map = 1
  flash = 256
else
  ifeq ($(SPI_SIZE_MAP), 2)
    size_map = 2
    flash = 1024
    ifeq ($(app), 2)
      addr = 0x81000
    endif
  else
    ifeq ($(SPI_SIZE_MAP), 3)
      size_map = 3
      flash = 2048
      ifeq ($(app), 2)
        addr = 0x81000
      endif
    else
      ifeq ($(SPI_SIZE_MAP), 4)
        size_map = 4
        flash = 4096
        ifeq ($(app), 2)
          addr = 0x81000
        endif
      else
        ifeq ($(SPI_SIZE_MAP), 5)
          size_map = 5
          flash = 2048
          ifeq ($(app), 2)
            addr = 0x101000
          endif
        else
          ifeq ($(SPI_SIZE_MAP), 6)
            size_map = 6
            flash = 4096
            ifeq ($(app), 2)
              addr = 0x101000
            endif
          else
            ifeq ($(SPI_SIZE_MAP), 8)
              size_map = 8
              flash = 8192
              ifeq ($(app), 2)
                addr = 0x101000
              endif
            else
              ifeq ($(SPI_SIZE_MAP), 9)
                size_map = 9
                flash = 16384
                ifeq ($(app), 2)
                  addr = 0x101000
                endif
              else
                size_map = 0
                flash = 512
                ifeq ($(app), 2)
                addr = 0x41000
                endif
              endif
            endif
          endif
        endif
      endif
    endif
  endif
endif
# 选择链接工具
# 根据脚本文件设置的size_map选择对应的Ld内存块文件
# 这里选择了主目录下的 ld/eagle.app.v6.new.2048
LD_FILE = $(LDDIR)/eagle.app.v6.ld

ifneq ($(boot), none)
ifneq ($(app),0)
    ifneq ($(findstring $(size_map),  6  8  9),)
      LD_FILE = $(LDDIR)/eagle.app.v6.$(boot).2048.ld
    else
      ifeq ($(size_map), 5)
        LD_FILE = $(LDDIR)/eagle.app.v6.$(boot).2048.ld
      else
        ifeq ($(size_map), 4)
          LD_FILE = $(LDDIR)/eagle.app.v6.$(boot).1024.app$(app).ld
        else
          ifeq ($(size_map), 3)
            LD_FILE = $(LDDIR)/eagle.app.v6.$(boot).1024.app$(app).ld
          else
            ifeq ($(size_map), 2)
              LD_FILE = $(LDDIR)/eagle.app.v6.$(boot).1024.app$(app).ld
            else
              ifeq ($(size_map), 0)
                LD_FILE = $(LDDIR)/eagle.app.v6.$(boot).512.app$(app).ld
              endif
            endif
          endif
        endif
      endif
    endif
    #定义的适用于OTA的配置与名称定义
    BIN_NAME = user$(app).$(flash).$(boot).$(size_map)
endif
else
    app = 0
endif
#  $(wildcard PATTERN…) 它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表
#在$(patsubst %.c,%.o,$(dir) )中,patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o
#notdir把展开的文件去除掉路径信息
CSRCS ?= $(wildcard *.c)
CPPSRCS ?= $(wildcard *.cpp)
ASRCs ?= $(wildcard *.s)
ASRCS ?= $(wildcard *.S)
SUBDIRS ?= $(patsubst %/,%,$(dir $(wildcard */Makefile)))
#定义各个输出目录的路径
ODIR := .output
#OBJODIR = .output/eagle/debug/obj
OBJODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/obj

#$(var:a=b) 或 ${var:a=b},含义:把变量var中的每一个值结尾用b替换掉a
#静态模式,简单讲就是将将该目录下的.c都找出来,替换成.o
#在user目录下OBJS = .output/eagle/debug/obj/gpio.o .output/eagle/debug/obj/hw_timer.o .output/eagle/debug/obj/uart.o .output/eagle/debug/obj/user_main.o 
#在hilink目录下 OBJS = .output/eagle/debug/obj/hilink_device.o .output/eagle/debug/obj/hilink_device_sdk.o .output/eagle/debug/obj/hilink_ota.o
#在application目录下 OBJS为空,因为找不到.c文件
OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \
        $(CPPSRCS:%.cpp=$(OBJODIR)/%.o) \
        $(ASRCs:%.s=$(OBJODIR)/%.o) \
        $(ASRCS:%.S=$(OBJODIR)/%.o)

DEPS := $(CSRCS:%.c=$(OBJODIR)/%.d) \
        $(CPPSRCS:%.cpp=$(OBJODIR)/%.d) \
        $(ASRCs:%.s=$(OBJODIR)/%.d) \
        $(ASRCS:%.S=$(OBJODIR)/%.d)

# LIBODIR = .output/eagle/debug/lib
LIBODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/lib

# OLIBS = .output/eagle/debug/lib/libuser.a
OLIBS := $(GEN_LIBS:%=$(LIBODIR)/%)

# IMAGEODIR = .output/eagle/debug/image
IMAGEODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/image

#OIMAGES = .output/eagle/debug/image/eagle.app.v6.out
OIMAGES := $(GEN_IMAGES:%=$(IMAGEODIR)/%)

# BINODIR = .output/eagle/debug/bin
BINODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/bin
# OBINS = .output/eagle/debug/bin/eagle.app.v6.bin
OBINS := $(GEN_BINS:%=$(BINODIR)/%)

CCFLAGS += 			\
	-g			\
	-Wpointer-arith		\
	-Wundef			\
	-Werror			\
	-Wl,-EL			\
	-fno-inline-functions	\
	-nostdlib       \
	-mlongcalls	\
	-mtext-section-literals \
	-ffunction-sections \
	-fdata-sections	\
	-fno-builtin-printf	\
	-fno-jump-tables
#	-Wall			

# DEFINES = -DICACHE_FLASH
# EXTRA_CCFLAGS 为空

CFLAGS = $(CCFLAGS) $(DEFINES) $(EXTRA_CCFLAGS) $(INCLUDES)
DFLAGS = $(CCFLAGS) $(DDEFINES) $(EXTRA_CCFLAGS) $(INCLUDES)


#############################################################
# Functions
# 命令前加 @ 字符可以不输出本局命令,如果不加的话,命令会执行,不过,会将这条命令打印一遍,加上 @ 就不打印了
define ShortcutRule

$(1): .subdirs $(2)/$(1)
endef

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

#第279行程序是链接语句
#DEP_LIBS_$(1)   DEP_OBJS_$(1) 为空
define MakeImage
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)))
$$(IMAGEODIR)/$(1).out: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1))
	@mkdir -p $$(IMAGEODIR)
	$$(CC) $$(LDFLAGS) $$(if $$(LINKFLAGS_$(1)),$$(LINKFLAGS_$(1)),$$(LINKFLAGS_DEFAULT) $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1))) -o $$@ 
endef

# BINODIR = .output/eagle/debug/bin
$(BINODIR)/%.bin: $(IMAGEODIR)/%.out
	@mkdir -p $(BIN_PATH)
	@mkdir -p $(BINODIR)
	
# OBJDUMP  linux下反汇编指令
# -s  除了显示test的全部Header信息,还显示他们对应的十六进制文件代码
# -S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。
# -x 显示所可用的头信息,包括符号表、重定位入口。-x 等价于-a -f -h -r -t 同时指定。
# 每一次编译都会将之前编译出的.s和.dump文件删除,重新反编译出汇编文件

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

# --only-section=sectionname  只将由 sectionname 指定的 section 拷贝到输出文件,可以多次指定,并且注意如果使用不当会导致输出文件不可用。
# -O binary 将文件转换成  binary 格式
	@$(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

	@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 "!!!"

#############################################################
# Rules base
# Should be done in top-level makefile only
#

# SPECIAL_MKTARGETS为空,不执行

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

#将变量SUBDIRS中每一个定义的.c文件夹中的./output/eagle/debug删除

clean:
  $(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clean;)
	$(RM) -r $(ODIR)/$(TARGET)/$(FLAVOR)

# 它会删除所有设置所生成的所有的output与中间文件。等价于 rm -rf out/
clobber: $(SPECIAL_CLOBBER)
	$(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clobber;)
	$(RM) -r $(ODIR)

.subdirs:
	@set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);)

#.subdirs:
#	$(foreach d, $(SUBDIRS), $(MAKE) -C $(d))

ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),clobber)
ifdef DEPS
sinclude $(DEPS)
endif
endif
endif


#OBJODIR = .output/eagle/debug/obj
# gcc -M main.c 命令 自动获取源文件中包含的头文件,并生成一个依赖关系 输出为main.o : main.c defs.h
# gcc -I 路径 是让gcc去路径中找依赖关系
$(OBJODIR)/%.o: %.c 
	@mkdir -p $(OBJODIR);
	$(CC) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $<

$(OBJODIR)/%.d: %.c
	@mkdir -p $(OBJODIR);
	@echo DEPEND: $(CC) -M $(CFLAGS) $<
	@set -e; rm -f $@; \
	$(CC) -M $(CFLAGS) $< > $@.$$$$; \
	sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$
	
$(OBJODIR)/%.o: %.cpp
	@mkdir -p $(OBJODIR);
	$(CPP) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $<

$(OBJODIR)/%.d: %.cpp
	@mkdir -p $(OBJODIR);
	@echo DEPEND: $(CPP) -M $(CFLAGS) $<
	@set -e; rm -f $@; \
	$(CPP) -M $(CFLAGS) $< > $@.$$$$; \
	sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

$(OBJODIR)/%.o: %.s
	@mkdir -p $(OBJODIR);
	$(CC) $(CFLAGS) -o $@ -c $<

$(OBJODIR)/%.d: %.s
	@mkdir -p $(OBJODIR); \
	set -e; rm -f $@; \
	$(CC) -M $(CFLAGS) $< > $@.$$$$; \
	sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

$(OBJODIR)/%.o: %.S
	@mkdir -p $(OBJODIR);
	$(CC) $(CFLAGS) -D__ASSEMBLER__ -o $@ -c $<

$(OBJODIR)/%.d: %.S
	@mkdir -p $(OBJODIR); \
	set -e; rm -f $@; \
	$(CC) -M $(CFLAGS) $< > $@.$$$$; \
	sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$


#define ShortcutRule

#$(1): .subdirs $(2)/$(1)
#endef

# OLIBS = .output/eagle/debug/lib/libuser.a
# LIBODIR = .output/eagle/debug/lib
# GEN_LIBS = libuser.a

#展开为 libuser.a : .subdirs .output/eagle/debug/lib/libuser.a
$(foreach lib,$(GEN_LIBS),$(eval $(call ShortcutRule,$(lib),$(LIBODIR))))


# GEN_IMAGES= eagle.app.v6.out
# 展开为 eagle.app.v6.out : .subdirs .output/eagle/debug/image/eagle.app.v6.out(在user下展不开,定义了PDIR,因此user下$(OIMAGES)为空)
$(foreach image,$(GEN_IMAGES),$(eval $(call ShortcutRule,$(image),$(IMAGEODIR))))



# GEN_BINS= eagle.app.v6.bin
# BINODIR = .output/eagle/debug/bin

#展开为 eagle.app.v6.bin : .subdirs .output/eagle/debug/bin/eagle.app.v6.bin(在user下展不开,定义了PDIR,因此user下$(OBINS)为空)
$(foreach bin,$(GEN_BINS),$(eval $(call ShortcutRule,$(bin),$(BINODIR))))


# GEN_LIBS = libuser.a

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


# GEN_IMAGES= eagle.app.v6.out
$(foreach image,$(GEN_IMAGES),$(eval $(call MakeImage,$(basename $(image)))))

#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
#   corresponding to the common APIs applicable to modules
#   rooted at that subtree. Accordingly, the INCLUDE PATH
#   of a module can only contain the include directories up
#   its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#

INCLUDES := $(INCLUDES) -I $(SDK_PATH)/include -I $(SDK_PATH)/extra_include
INCLUDES += -I $(SDK_PATH)/driver_lib/include
INCLUDES += -I $(SDK_PATH)/include/espressif
INCLUDES += -I $(SDK_PATH)/include/lwip
INCLUDES += -I $(SDK_PATH)/include/lwip/ipv4
INCLUDES += -I $(SDK_PATH)/include/lwip/ipv6
INCLUDES += -I $(SDK_PATH)/include/nopoll
INCLUDES += -I $(SDK_PATH)/include/spiffs
INCLUDES += -I $(SDK_PATH)/include/ssl
INCLUDES += -I $(SDK_PATH)/include/json
INCLUDES += -I $(SDK_PATH)/include/openssl
INCLUDES += -I $(SDK_PATH)/include/mqtt

你可能感兴趣的:(物联网,iot)