这次分析源码根目录下的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. 2019 年 React 学习路线图(转)

    转自:https://www.infoq.cn/article/AEkiVAiJf25LZmoUe_yc 之前我们已经介绍了2019 年 Vue 学习路线图,而 React 作为当前应用最广泛的前端框 ...

  2. 如何看iOS崩溃日志

    重点:Triggered by Thread这句话后边的线程号,快速定位问题出现在那个线程,是否是你的锅:Triggered by Thread所指的线程表示导致异常.崩溃的线程 下边内容转自简书 简 ...

  3. Excel uploading date format

    if l_wa_field-value eq 'ZFIRST_REQ_DATE'. clear lv_length. lv_length = strlen( l_wa_excel-value ). c ...

  4. 解决在vscode中eslint在vue后缀文件中保存时无法自动格式化的问题

    在setting.json中加入如下内容 { "eslint.autoFixOnSave": true, "eslint.validate": [ " ...

  5. hbase——b树,b+树,lsm树

    b树 b树,又叫做平衡多路查找树.一个m阶的b树的特性如下: 树中的每个节点,最多有m个子节点. 除了根节点之外,其他的每个节点至少有ceil(m/2)个子节点,ceil函数为取上限函数. 所有的叶子 ...

  6. hbase-写操作

    hbase连接过程 hbase client在写入的数据的过程中,是直接和rs进行通信的,整个的数据写入流程并不涉及到HMaster.那么client是如何找到对应的rs呢?流程如下: client从 ...

  7. py文件的运行

    安装过程及配置 安装过程准备: 下载好Python的安装程序后,开始安装,在进入安装界面后一定确保勾选将Python加入到系统环境变量的路径里.如图所示: 2 如果没有选取,那么按照下面的步骤进行操作 ...

  8. vue项目中编写一个图片预览的公用组件

    今天产品提出了一个查看影像的功能需求. 在查看单据的列表中,有一列是影像字段,一开始根据单据号调用接口查看是否有图片附件,如果有则弹出一个全屏的弹出层,如果没有给出提示.而且,从列表进入详情之后,附件 ...

  9. linux学习第十五天 (Linux就该这么学) 找到一本不错的Linux电子书,附《Linux就该这么学》章节目录

    今天收尾DNS内容复习了,还有分享解析配置,都没有记,主要访问同一个域名,就近访问,

  10. STS中db.properties配置文件

    db.name=root db.password=root db.url=jdbc:mysql://localhost:3306/day13?useUnicode=true&character ...