这次分析源码根目录下的Makefile,它负责读入配置过的信息,通过OBJS、LIBS等变量设置能够参与镜像链接的目标文件,设定编译的目标等等。

 HOSTARCH := $(shell uname -m | \
sed -e s/i./x86/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/ppc64/powerpc/ \
-e s/ppc/powerpc/ \
-e s/macppc/powerpc/\
-e s/sh.*/sh/) HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/') # Set shell to bash if possible, otherwise fall back to sh
SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi; fi) export HOSTARCH HOSTOS SHELL

  上述代码通过shell处理字符数据,得到宿主机的架构、操作系统、shell类型等变量,Makefile中调用shell命令的方式有两种,一种是$(shell cmd_name),另一种是命令前后加上反引号``,如$(shell uname -m)或者`uname -m`。这里可以看到,sed的选项可以是多个,之前不知道还能这么用,学习了。

 ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR) # Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}) # Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),) OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SPLTREE := $(OBJTREE)/spl
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE SPLTREE MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD :=
export REMOTE_BUILD
endif

  BUILD_DIR可以通过命令行中O=xxx指定。BUILD_DIR和SRCTREE是相同的,代表native build,REMOTE_BUILD = 0。最后将各个变量输出到全局,TOPDIR在子Makefile中经常使用,它的内容是源码根目录的绝对路径。

  MKCONFIG代表根目录下的mkconfig脚本,这个脚本的内容在前面已经分析过了。

 all:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk

  这里出现了all,虽然它的确是我们的目标,但是这里并不是要描述它的依赖和生成命令,而是为了防止sinclude的文件中的某个目标成为默认目标,造成严重的逻辑错误。这是一个小技巧,纠正了我对于Makefile的错误认识:之前认为某个目标的依赖关系必须在一条描述中写完,要是这个目标的多个依赖分散在多处写的话,每一部分依赖都有一条命令与之对应。这是错的,其实目标的依赖关系可以分成多次写完,你只需要在某一处指明要执行的命令就行了,在U-Boot的Makefile中,由源文件生成目标文件的命令都是放在模式规则中描述的,而目标文件对于头文件的依赖是在其他地方描述的,这一点已经在分析具体子Makefile的时候说过了。

  从内容上来说,它从autoconf.mk文件中读入了很多配置信息。

 include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC

  前面分析mkconfig脚本的时候,我们知道include/config.mk文件的主要内容是指定ARCH CPU SOC VENDOR BOARD等信息,这里又输出到全局,对于后面的配置影响很大。

 OBJS  = $(CPUDIR)/start.o
ifeq ($(CPU),x86)
OBJS += $(CPUDIR)/start16.o
OBJS += $(CPUDIR)/resetvec.o
endif
ifeq ($(CPU),ppc4xx)
OBJS += $(CPUDIR)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += $(CPUDIR)/resetvec.o
endif OBJS := $(addprefix $(obj),$(OBJS))

  重要变量OBJS登场,它表示U-Boot必须要编译链接的那些目标文件,而后面可选的驱动、文件系统、网络配置都放在LIBS变量中描述。OBJS变量中,一定要注意目标文件的顺序,这关系到处理器能否正常工作,倘若你把启动配置cpu的代码放在了镜像文件的后面,而非开始位置,那么一定会出错。

 HAVE_VENDOR_COMMON_LIB = $(if $(wildcard board/$(VENDOR)/common/Makefile),y,n)

 LIBS-y += lib/libgeneric.o
LIBS-y += lib/lzma/liblzma.o
LIBS-y += lib/lzo/liblzo.o
LIBS-y += lib/zlib/libz.o
LIBS-$(CONFIG_TIZEN) += lib/tizen/libtizen.o
LIBS-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/lib$(VENDOR).o
LIBS-y += $(CPUDIR)/lib$(CPU).o
ifdef SOC
LIBS-y += $(CPUDIR)/$(SOC)/lib$(SOC).o
endif
ifeq ($(CPU),ixp)
LIBS-y += drivers/net/npe/libnpe.o
endif
LIBS-$(CONFIG_OF_EMBED) += dts/libdts.o
LIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o
LIBS-y += fs/cramfs/libcramfs.o \
fs/ext4/libext4fs.o \
fs/fat/libfat.o \
fs/fdos/libfdos.o \
fs/jffs2/libjffs2.o \
fs/reiserfs/libreiserfs.o \
fs/ubifs/libubifs.o \
fs/yaffs2/libyaffs2.o \
fs/zfs/libzfs.o

  首先说一下Makefile中的wildcard函数,它的本意是通配符,但是这里的用途是判断 一个路径是否存在,如果你写的路径不合法,不存在,那么返回空字符串。

  可以看到LIBS-y变量开始收割了,它包含了lib fs arch net drivers等各个目录下的目标文件,因为这些功能都是可选的,因此以lib的形式出现。那么浏览一下它包含了哪些目标文件吧!

  1. lib目录下的通用库文件

  2. arch/arm/cpu/armv7/libarmv7.o        实现armv7的cache等部件的管理。

  3. arch/arm/cpu/armv7/s5pc1xx/libs5pc1xx.o   实现了reset clock等部件的管理。

  4. arch/arm/lib/libarm.o              架构相关的底层库。

  5. fs/下的诸多文件系统

  6. net目录下的libnet.o             网络协议栈相关的库。

  7. drivers/ :block dma fpga gpio i2c input misc mmc mtd net  power pci spi等文件下的libxx.o

 ifeq ($(SOC),s5pc1xx)
LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o
endif

  包含s5p系列soc的公用库,比如timer srom watchdog等。

 LIBS := $(addprefix $(obj),$(sort $(LIBS-y)))
.PHONY : $(LIBS) LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).o
LIBBOARD := $(addprefix $(obj),$(LIBBOARD))

  LIBS-y经过排序、清理掉重复目标文件之后,放在新的变量LIBS中,并将它声明为伪目标。

  LIBBOARD := board/samsung/goni/libgoni.o,这也是一个重要的变量。

 # Add GCC lib
ifdef USE_PRIVATE_LIBGCC
ifeq ("$(USE_PRIVATE_LIBGCC)", "yes")
PLATFORM_LIBGCC = $(OBJTREE)/arch/$(ARCH)/lib/libgcc.o
else
PLATFORM_LIBGCC = -L $(USE_PRIVATE_LIBGCC) -lgcc
endif
else
PLATFORM_LIBGCC := -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
endif
PLATFORM_LIBS += $(PLATFORM_LIBGCC)
export PLATFORM_LIBS

  这里添加LIBGCC,看不明白,暂不研究。

 __OBJS := $(subst $(obj),,$(OBJS))
#__LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD)) __LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst $(obj),,$(LIBS))

  重量级变量登场!__OBJS是除去了obj前缀的OBJS,就是start.o。__LIBS则由两部分组成,LIBBOARD在前,LIBS在后,其实官方源码中两者的顺序是相反的。为什么要修改呢?这就跟S5PV210的启动流程有关系了。移植的时候,我们选择修改goni的代码,其中要修改的文件之一是board/samsung/goni/lowlevel_init.S,根据路径可以知道这里的文件都会被链接进libgoni.o中,也就是LIBBOARD变量里,如果它放在LIBS后面,那么lowlevel_init.S中的代码将会在镜像靠后的位置,绝对在16KB以后,而S5PV210在启动的时候会将SD卡的前16KB搬移到iRAM中,这将无法运行我们修改的代码,所以修改。

 BOARD_SIZE_CHECK = \
@actual=`wc -c $@ | awk '{print $$1}'`; \
limit=$(CONFIG_BOARD_SIZE_LIMIT); \
if test $$actual -gt $$limit; then \
echo "$@ exceeds file size limit:"; \
echo " limit: $$limit bytes"; \
echo " actual: $$actual bytes"; \
echo " excess: $$((actual - limit)) bytes"; \
exit 1; \
fi

  这里就是一个检查镜像大小的函数,如果超出了用户设定的大小就报错,理解起来很简单,不多说,一般可以不用这个功能。

 ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map

 ALL-$(CONFIG_NAND_U_BOOT) += $(obj)u-boot-nand.bin
ALL-$(CONFIG_ONENAND_U_BOOT) += $(obj)u-boot-onenand.bin
ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin
ALL-$(CONFIG_OF_SEPARATE) += $(obj)u-boot.dtb $(obj)u-boot-dtb.bin # enable combined SPL/u-boot/dtb rules for tegra
ifeq ($(SOC),tegra20)
ifeq ($(CONFIG_OF_SEPARATE),y)
ALL-y += $(obj)u-boot-dtb-tegra.bin
else
ALL-y += $(obj)u-boot-nodtb-tegra.bin
endif
endif all: $(ALL-y) $(SUBDIR_EXAMPLES)

  终于开始描述all的依赖了,all的依赖是ALL-y,可以看到这个变量的内容是u-boot.srec u-boot.bin System.map,另外,我们还可以通过宏来给它增添新的内容:u-boot-nand.bin u-boot-onenand.bin u-boot-spl.bin u-boot.dtb u-boot-dtb.bin。

  上面这段代码的最后就写清楚了all的所有依赖:ALL-y $(SUBDIR_EXAMPLES),看一看这两个变量里面的内容吧。

  ALL-y其实指定的是u-boot镜像的生成格式。u-boot是包含各种调试信息、符号表等元素的镜像文件,可以用于阅读分析,但是不适合在嵌入式平台上烧写,因为文件容量大。u-boot.bin是不包含地址信息的二进制指令数据流,文件容量小,我们一般使用这个进行烧写。u-boot.srec,这是一种S-Record格式的文件,是由Motorola公司推出的一种用于flash烧写的镜像文件格式,每一行都由S字母开头,后面有类型字段、地址字段、数据字段以及校验和等等。u-boot.hex,这也是一种烧写flash的镜像文件格式,它每行都由0x3a这个字节开始,后面有一字节的行长度字段、两字节的地址字段、一字节的数据类型字段、数据以及校验和。u-boot里还支持生成其他格式的镜像文件,但是我暂时用不着,先不分析。

  SUBDIR_EXAMPLES指向了examples目录下的文件,主要生成一些测试文件,暂不深究。

  我们关注的是u-boot.bin文件,因此看一下它的依赖:

 $(obj)u-boot:   depend \
$(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)

  u-boot的依赖有7个:depend SUBDIR_TOOLS OBJS LIBBOARD LIBS LDSCRIPT u-boot.lds,生成的命令是$(GEN_UBOOT),下面逐个看一下。

 # Explicitly make _depend in subdirs containing multiple targets to prevent
# parallel sub-makes creating .depend files simultaneously.
depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE) \
$(obj)include/autoconf.mk \
$(obj)include/generated/generic-asm-offsets.h \
$(obj)include/generated/asm-offsets.h
for dir in $(SUBDIRS) $(CPUDIR) $(LDSCRIPT_MAKEFILE_DIR) ; do \
$(MAKE) -C $$dir _depend ; done

  depend、dep两个目标拥有同样的依赖关系,执行相同的命令。不难发现depend的命令中并没有生成depend这个文件,因此它的作用相当于一个伪目标,每次执行make命令,都会发现depend目标不存在,接着就开始检查depend的各个依赖。更新depend目标时,也是更换到其他路径下执行make _depend命令,结合前面对于rules.mk的分析知道,_depend也是一个伪目标,用于生成某个文件夹下的所有文件依赖关系,这里很绕,慢慢体会。下面看一看depend的依赖。

  include/autoconf.mk这个文件中有很多重要的配置信息,它的依赖关系如下所示。

 $(obj)include/autoconf.mk: $(obj)include/config.h
@$(XECHO) Generating $@ ; \
set -e ; \
: Extract the config macros ; \
$(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h | \
sed -n -f tools/scripts/define2mk.sed > $@.tmp && \
mv $@.tmp $@

  include/autoconf.mk文件的依赖是include/config.h,config.h是执行make s5p_goni_config的时mkconfig脚本自动生成的文件,所以说,变更配置才会更新autoconf.mk文件,这也进一步说明autoconf.mk文件确实是用来描述当前配置选项的。

  分析一下上面用到的各条命令,第一句就是echo Generating include/autoconf.mk。第二句,set -e的作用是监测当前进程中命令的执行情况,当有命令执行出错时直接退出,这里的关键字是“当前进程”,如果你以 “./example.sh”方式执行某个脚本时出现的错误并不能被捕捉,因为这种方式的调用是以子进程的方式执行的,所以应该使用“source example.sh”或者“ .  example.sh”的方式。第三句,cpp -dM include/common.h | sed -n -f tools/scripts/define2mk.sed > include/autoconf.mk.tmp && mv include/autoconf.mk.tmp include/autoconf.mk,cpp是gcc的预处理器,-dM选项会输出很多预处理信息,比如宏定义,类型定义等等,而且它会递归地解析你包含的头文件,比如说这里的include/common.h文件中有一句#include <config.h>,那么cpp -dM include/common.h不但解析common.h文件中的内容并输出,而且会对config.h文件做相同的处理,显然,这条命令执行之后,就输出了全部的配置信息。接着通过管道操作,将这些信息用sed命令处理成特定的格式,最终输出到autoconf.mk文件中。

  TIMESTAMP_FILE和VERSION_FILE

 $(TIMESTAMP_FILE):
@mkdir -p $(dir $(TIMESTAMP_FILE))
@LC_ALL=C date +'#define U_BOOT_DATE "%b %d %C%y"' > $@.tmp
@LC_ALL=C date +'#define U_BOOT_TIME "%T"' >> $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
 $(VERSION_FILE):
@mkdir -p $(dir $(VERSION_FILE))
@( localvers='$(shell $(TOPDIR)/tools/setlocalversion $(TOPDIR))' ; \
printf '#define PLAIN_VERSION "%s%s"\n' \
"$(U_BOOT_VERSION)" "$${localvers}" ; \
printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' \
"$(U_BOOT_VERSION)" "$${localvers}" ; \
) > $@.tmp
@( printf '#define CC_VERSION_STRING "%s"\n' \
'$(shell $(CC) --version | head -n 1)' )>> $@.tmp
@( printf '#define LD_VERSION_STRING "%s"\n' \
'$(shell $(LD) -v | head -n 1)' )>> $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
 TIMESTAMP_FILE = $(obj)include/generated/timestamp_autogenerated.h
VERSION_FILE = $(obj)include/generated/version_autogenerated.h

  上述两个变量其实是include/genetated下的两个文件。

  timestamp生成的过程:提取TIMESTAMP_FILE路径,建立路径对应的文件夹,将date命令的输出信息重定位到表示时间戳的、后缀为.tmp的文件中,接着比较原有的时间戳文件和这个tmp文件的内容,如果相同,那么删除刚刚生成的文件,否则以tmp文件更换之前的时间戳文件。

  version生成的过程:提取VERSION_FILE路径,建立路径对应文件夹,将主Makefile开始定义的U-Boot version信息、gcc版本信息、ld版本信息都写到tmp结尾的文件中,接着比较version源文件和之前version文件,相同则删除tmp,否则覆盖原文件。

  其实Makefile中将以上两个变量声明为伪目标,因此每次执行make命令,都会在更新depend的时候更新时间戳文件和版本文件。

  depend的另外两个依赖generic-asm-offsets.h asm-offsets.h不是特别重要,暂不分析。

 SUBDIR_TOOLS = tools
 tools: $(VERSION_FILE) $(TIMESTAMP_FILE)
$(MAKE) -C $@ all

  SUBDIR_TOOLS是u-boot的第二个依赖,它的内容是tools这个文件夹,依赖是时间戳和版本文件,执行的命令是到tools文件夹下执行make,显然意思是到编译tools下面的各个文件,生成各种工具。

 $(OBJS):    depend
$(MAKE) -C $(CPUDIR) $(if $(REMOTE_BUILD),$@,$(notdir $@)) $(LIBS): depend $(SUBDIR_TOOLS)
$(MAKE) -C $(dir $(subst $(obj),,$@)) $(LIBBOARD): depend $(LIBS)
$(MAKE) -C $(dir $(subst $(obj),,$@)) $(SUBDIRS): depend
$(MAKE) -C $@ all $(SUBDIR_EXAMPLES): $(obj)u-boot $(LDSCRIPT): depend
$(MAKE) -C $(dir $@) $(notdir $@) $(obj)u-boot.lds: $(LDSCRIPT)
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@

  OBJS就是arch/arm/cpu/armv7/start.o,要生成它,就需要到CPUDIR下面执行make,这很容易理解。说一下makefile的函数notdir吧,它的意思是去除所有的地址信息,仅留下最后一个/符号后面的文件名字,也就是取文件名操作。与之对应的dir函数,则实现去除文件名,取路径名的作用。

  LIBS包含arch cpu soc board等各个层次上的公用库,文件系统库,还有各种驱动库。这其实是一个多目标的依赖关系描述,不过各目标的依赖相同,生成规则统一,都是到目标变量所在路径下去执行make。详细点,就是make -C drivers/bios_emulator,make -C drivers/block... ...

  LIBBOARD指的是board/samsung/goni/libgoni.o,生成的命令很简单,到BOARD_DIR目录下执行make即可。

  LDSCRIPT内容是arch/arm/cpu/u-boot.lds,此条依赖对应的命令是make -C arch/arm/cpu u-boot.lds,在arch/arm/cpu/目录下的确能看到链接文件,但是并没有makefile,这一点不能理解。

  u-boot的依赖文件就分析到这里,下面看一下u-boot的生成过程:

 ifeq ($(CONFIG_SANDBOX),y)
GEN_UBOOT = \
cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \
-Wl,--start-group $(__LIBS) -Wl,--end-group \
$(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map -o u-boot
else
GEN_UBOOT = \
UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
endif

  这里只看核心部分:$(LD) $(LDFLAGS) $(__OBJS) --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) -Map u-boot.map -o u-boot,简而言之,就是将__OBJS __LIBS __PLATFORM_LIBS链接成u-boot,写到这里,就很容易理解了。

  现在回到u-boot.bin。

 $(obj)u-boot.bin:   $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(BOARD_SIZE_CHECK)

  u-boot.bin依赖u-boot,生成的命令是arm-linux-objcopy,接着还检查了镜像文件的大小,很简单的过程,不多解释

  顺带提一句u-boot.hex,其实就是使用arm-linux-objcopy的时候改变一下控制参数而已,将binary改成ihex。u-boot.srec的生成就是将控制参数替换成srec。

  对U-Boot Makefile的初步探索终于到头了,这个过程虽然很粗略,但确实纠正了一些makefile不准确的认识,学到了不少makefile以及shell的知识,并对u-boot镜像文件的建立有了全局的了解。

U-Boot Makefile分析(5)主控Makefile分析的更多相关文章

  1. Spring Boot 启动(一) SpringApplication 分析

    Spring Boot 启动(一) SpringApplication 分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html ...

  2. Spring Boot 揭秘与实战 源码分析 - 工作原理剖析

    文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...

  3. Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机

    文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...

  4. Spring Boot自动装配原理源码分析

    1.环境准备 使用IDEA Spring Initializr快速创建一个Spring Boot项目 添加一个Controller类 @RestController public class Hell ...

  5. makefile中引用其他makefile方法

    在Makefile中引用其他Makefile文件的方法是,使用inclue   filename.mk

  6. 手机自动化测试:Appium源码分析之跟踪代码分析九

    手机自动化测试:Appium源码分析之跟踪代码分析九   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...

  7. 手机自动化测试:Appium源码分析之跟踪代码分析八

    手机自动化测试:Appium源码分析之跟踪代码分析八   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...

  8. 手机自动化测试:Appium源码分析之跟踪代码分析七

    手机自动化测试:Appium源码分析之跟踪代码分析七   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自 ...

  9. 手机自动化测试:Appium源码分析之跟踪代码分析六

    手机自动化测试:Appium源码分析之跟踪代码分析六   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自 ...

随机推荐

  1. Tomcat的三种安装方式:解压版、安装版、配置成Windows服务版

    https://blog.csdn.net/Jessica_XLF/article/details/81711429

  2. SpringCloud-day05-服务调用Ribbon

    6.服务调用Ribbon 6.1Ribbon简介 前面讲了eureka服务注册与发现,但是结合eureka集群的服务调用并没有谈到.这里就要用到Ribbon,结合eureka,来实现服务的调用: Ri ...

  3. vue加elementui开发的分页显示

    由于我的是公共引入样式表和css表所以,将公共的也写出来了(我接手的项目为基于vue开发的) 公共的index.html 引入js <script src="{MODULE_URL}s ...

  4. c++学习路线连接

    https://blog.csdn.net/qq_36482772/article/category/7396881/4?

  5. mysql explain执行详解

    1).id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行,id列为null的就表是这是一个结果集,不需要使用它来进行查询.2).select_type列常见的有:A:simple:表示不 ...

  6. TZOJ 2289 Help Bob(状压DP)

    描述 Bob loves Pizza but is always out of money. One day he reads in the newspapers that his favorite ...

  7. 摘选改善Python程序的91个建议2

       62.metaclass stackflow          中文翻译    63.Python对象协议   https://zhuanlan.zhihu.com/p/26760180     ...

  8. java 小数转换成二进制

    32位单精度二进制 = [1个符号位] [8个阶码位] [23个尾数位] 64位单精度二进制 = [1个符号位] [11个阶码位] [52个尾数位] 小数 = [正负符号位]  [整数部分] . [小 ...

  9. 从本地上传项目到 github 以及从github 下载项目到本地环境

    前置条件:成功安装github,安装成功后,要配置密钥,不然上传不成功,要报错 具体上传步骤: git init   //初始化 git add  文件名  //更新文件 git commit -m ...

  10. Eclipse 中 Maven 项目默认JDK版本为1.5 的解决方法

    在 Eclipse 中 Maven project 的默认 JDK 版本是 1.5, 如果不在 settings.xml 或者 pom.xml 中显示的指出 JDK 版本,每次 右键项目--> ...