Uboot11之主Makefile分析2
时间:2018.3.25 作者:Tom 工作:HWE 说明:如需转载,请注明出处。
VERSION = 1 //主版本号
PATCHLEVEL = 3 //次版本号
SUBLEVEL = 4 //再次版本号
EXTRAVERSION = //另外附加的版本信息
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
前面五行我们很好理解,就是定义变量然后引用变量,主要作用就是定义了uboot的版本号:1.3.4。然后用点连起来---Makefile中版本号最终生成了一个变量U_BOOT_VERSION,这个变量记录了Makefile中配置的版本号。
VERSION_FILE = $(obj)include/version_autogenerated.h
---include/version_autogenerated.h文件是编译过程中自动生成的一个文件,所以源目录中没有,但是编译过后的uboot中就有了。它里面的内容是一个宏定义,宏定义的值内容就是我们在Makefile中配置的uboot的版本号。在此位置只是定义了一个文件,并没有执行生成,生成是在后面。我们来看下生成的结果是:
HOSTARCH := $(shell uname -m | \
sed -e s/i.86/i386/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/powerpc/ppc/ \
-e s/ppc64/ppc/ \
-e s/macppc/ppc/)
上面8条指令就是使用uname –m取出主机类型,然后看使用sed –e指令进行替换,如果有i.86都替换成i386,如果有arm.*都替换成arm等。
知识点1:在makefile中使用shell指令:
uname -m 查看主机类型 uname -s 查看主机操作系统
知识点2:shell中"|"叫做管道。
管道的作用就是把管道前面一个运算式的输出作为后面一个的输入再去做处理,最终的输出才是我们整个式子的输出。
知识点3:sed指令替换字符
"sed –e"表示后面跟的是一串命令脚本,而表达式"s/abc/def/"表示要从标准输入中,查找到内容为"abc"的,然后替换成"def"。其中"abc"表达式用可以使用"."作为通配符。
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/')
"uname –s"输出主机内核名字,比如Ubuntu16.04,因此"uname –s"结果是"Linux"。"tr '[:upper:]' '[:lower:]'"作用是将标准输入中的所有大写字母转换为响应的小写字母。因此执行结果是将HOSTOS 设置为"linux"。然后在通过sed流编辑器匹配所有的在这个系统名中出现的"\(cygwin\).*"模式,然后再用"cygwin"模式替换。(用来处理"cygwin"环境下编译的环境变量)
Cygwin是一个在windows平台上运行的类UNIX模拟环境。它对于学习UNIX/Linux操作环境,或者从UNIX到Windows的应用程序移植,或者进行某些特殊的开发工作,尤其是使用GNU工具集在Windows上进行嵌入式系统开发,非常有用。
export HOSTARCH HOSTOS
# Deal with colliding definitions from tcsh etc.
VENDOR=
变量为空
#########################################################################
# Allow for silent builds
ifeq (,$(findstring s,$(MAKEFLAGS)))
XECHO = echo
else
XECHO = :
endif
1)平时默认编译时命令行会打印出来很多编译信息。但是有时候我们不希望看到这些编译信息,就后台编译即可。这就叫静默编译。2) 使用方法就是编译时make -s,-s会作为MAKEFLAGS传给Makefile,在50-54行这段代码作用下XECHO变量就会被变成空(默认等于echo),于是实现了静默编译。
#########################################################################
#
# U-boot build supports producing a object files to the separate external
# directory. Two use cases are supported:
#
# 1) Add O= to the make command line
# 'make O=/tmp/build all'
#
# 2) Set environement variable BUILD_DIR to point to the desired location
# 'export BUILD_DIR=/tmp/build'
# 'make'
#
# The second approach can also be used with a MAKEALL script
# 'export BUILD_DIR=/tmp/build'
# './MAKEALL'
#
# Command line 'O=' setting overrides BUILD_DIR environent variable.
#
# When none of the above methods is used the local build is performed and
# the object files are placed in the source directory.
#
1)编译复杂项目,Makefile提供2种编译管理方法。默认情况下是当前文件夹中的.c文件,编译出来的.o文件会放在同一文件夹下。这种方式叫原地编译。原地编译的好处就是处理起来简单。
2)原地编译有一些坏处:第一,污染了源文件目录。第二的缺陷就是一套源代码只能按照一种配置和编译方法进行处理,无法同时维护2个或2个以上的配置编译方式。
3)为了解决以上2种缺陷,uboot支持单独输出文件夹方式的编译(linux kernel也支持,而且uboot的这种技术就是从linux kernel学习来的)。基本思路就是在编译时另外指定一个输出目录,将来所有的编译生成的.o文件或生成的其他文件全部丢到那个输出目录下去。源代码目录不做任何污染,这样输出目录就承载了本次配置编译的所有结果。
4)具体用法:默认的就是原地编译。如果需要指定具体的输出目录编译则有2种方式来指定输出目录。(具体参考Makefile 56-76行注释内容)
第一种:make O=输出目录
第二种:export BUILD_DIR=输出目录 然后再make
如果两个都指定了(既有BUILD_DIR环境变量存在,又有O=xx),则O=xx具有更高优先级,听他的。
5)两种编译的实现代码在Makefile的78-123行。
ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif
很明显,第一种我们没有定义,所以看下面第二种。
知识点4:函数$( origin, variable) 输出的结果是一个字符串,输出结果由变量variable定义的方式决定,若variable在命令行中定义过,则origin函数返回值为"command line"。假若在命令行中执行了"export BUILD_DIR=/tmp/build"的命令,则"$(origin O)"值为"command line",而BUILD_DIR被设置为"/tmp/build"。
ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
Shell指令:-d选项判断${BUILD_DIR}这个目录存在与否。若${BUILD_DIR}表示的目录没有定义,则创建目录。
知识点5:shell中的test指令:
测试[右边和]左边必须要有一个空格。然后使用echo $?打印上次命令的返回值可以看出。
# 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),)
执行shell:cd命令,进入到新建的目录里,然后执行pwd命令来得到当前目录的绝对地址。为什么需要这样做呢,因为前面的创建目录工作可能不成功,所以导致后面的cd命令也没有进去,所以需要后面的pwd命令来确认一下。
知识点6:
if(a,b,c)这样的形式,执行步骤为,先判断a的真假,如果为真,则执行b,如果为假,则执行c。所以这里的意思就是判断目录建成没有建成,如果建成,则什么也不干,没建成,就使用error,输出错误信息且退出。
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
若$(BUILD_DIR)为空,则将其赋值为当前目录路径(源代码目录)。并检查$(BUILD_DIR)目录是否存在。CURDIR是make中的标准变量 不指定-C就是当前目录。由于当前Make在U-Boot顶层目录执行Makefile,因此CURDIR此时就是U-Boot顶层目录。
export TOPDIR SRCTREE OBJTREE //导出环境变量
MKCONFIG := $(SRCTREE)/mkconfig
变量MKCONFIG:这个变量指向一个脚本,即顶层目录的mkconfig。
export MKCONFIG
ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD := 1
export REMOTE_BUILD //导出环境变量
endif
这四句话是来判断我们编译的输出目录和当前的源文件目录是否一致 ,如果不一样则REMOTE_BUILD=1,否则为0;这个只是做个标记。而实际我们编译的两个路径并不一致。
# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
上面的英文注释,说明这时还没有包含config.mk文件,但是因为要用到所以定义了obj src变量
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
这8句话同样还是判断编译输出目录与源文件目录是否一致,同时赋值obj和src。至此我们来整理下上面程序所建立的文件夹:
SRCTREE,src变量就是U-Boot代码顶层目录---源码目录,编译出的.o文件存放的目录的根目录
OBJTREE,obj变量就是编译输出目录,若没有定义BUILD_DIR环境变量,则SRCTREE,src变量与OBJTREE,obj变量都是U-Boot源代码目录。实际上我们定义并创建了这个文件夹,是存在的。
LNDIR 是链接目录,与OBJTREE,obj一致。
TOPDIR顶层目录,与SRCTREE,src一致。
MKCONFIG则表示U-Boot根目录下的mkconfig脚本。
至此我们编译运行所需要的目录都已经建立好了!!接着我们找我们编译的目标、依赖、命令!
# Make sure CDPATH settings don't interfere
unexport CDPATH
不导出CDPATH
#########################################################################
ifeq ($(ARCH),powerpc)
ARCH = ppc
endif
上面三句不管。
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
这句话对应400多行的:
中间有一大堆。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
else # !config.mk
all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
$(SUBDIRS) $(VERSION_FILE) gdbtools updater env depend \
dep tags ctags etags cscope $(obj)System.map:
@echo "System not configured - see README" >&2
@ exit 1
endif # config.mk
首先,它判断$(OBJTREE)/include/config.mk与$(wildcard $(OBJTREE)/include/config.mk)得到的值是否一样。
知识点7:wildcard是Makefile中的关键字,它的作用是让通配符在变量中展开。
$(wildcard PATTERN)的使用就是查找到与PATTERN相符合的,并且是存在的,以空格分开的文件列表。
#说白了,这句话就是判断$(OBJTREE)/include/config.mk文件是否存在。如果存在,则执行咱们忽略的内容,如果不存在,则执行咱们没有忽略的内容。
先看不存在的情况。不存在的时候,就把所有目标,都指向到一条依赖上了
# @echo "System not configured - see README" >&2
# @ exit 1
然后不管执行make xxx命令,都会显示System not configured - see README,然后它把这个输出输到标准错误输出文件里了,退出了执行。
所以这里就明说了,如果$(OBJTREE)/include/config.mk文件不存在,则编译不能进行下去了。如果直接运行make的话,u-boot是编译不过去的,因为缺少$(OBJTREE)/include/config.mk这个文件。然后出错提示让去看README,那咱们就去看看这个README里面写的是什么东西:
这个README文件里介绍了一下u-boot这个工程,然后说明了一下好些个参数是做什么用的,其中有一句话是包含跟咱们编译有关的信息的make <board_name>_config,也就是说,编译的时候,make后面是需要跟参数的,那么根据make的语法,这个<board_name>_config肯定是在Makefile里定义了的一个target了。然后再看一下Makefile文件,果然以_config结尾的target很多。比如我的板子是仿制三星的S3C2440的,所以与我最接近的,让我在移植的时候代码改动量最小的,就是smdk2410_config这个target了。如果是新手,对自己没有什么信心,那就可以直接使用这个target来编译,直接修改与这个target相关的源码就可以了。
其实我个人倒是建议另外给自己的板子取一个名字,然后在Makefile里给自己的板子按着相应的规则给加一条target。然后把自己需要的与现在板子的环境最接近的板子的相关的文件都拷到相应的位置,然后慢慢修改它们就可以了。
# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
(1)接下来有2个很重要的环境变量。一个是ARCH,上面导出的,值来自于我们的配置过程,它的值会影响后面的CROSS_COMPILE环境变量的值。ARCH的意义是定义当前编译的目标CPU的架构。
(2)CROSS_COMPILE是定义交叉编译工具链的前缀的。定义这些前缀是为了在后面用(用前缀加上后缀来定义编译过程中用到的各种工具链中的工具)。我们把前缀和后缀分开还有一个原因就是:在不同CPU架构上的交叉编译工具链,只是前缀不一样,后缀都是一样的。因此定义时把前缀和后缀分开,只需要在定义前缀时区分各种架构即可实现可移植性。
(3)CROSS_COMPILE在136-182行来确定。CROSS_COMPILE是被ARCH所确定的,只要配置了ARCH=arm,那么我们就只能在ARM的那个分支去设置CROSS_COMPILE的值。这个设置值只要能保证找到那个交叉编译工具链即可,不一定非得是全路径的,相对路径也可以。(如果已经将工具链导出到环境变量,并且设置了符号链接,这样CROSS_COMPILE = arm-linux-就可以)
(4)实际运用时,我们可以在Makefile中去更改设置CROSS_COMPILE的值,也可以在编译时用make CROSS_COMPILE=xxxx来设置,而且编译时传参的方法可以覆盖Makefile里面的设置。
上面为加入编译环境,根据CPU类型来为交叉编译工具加上相应的前缀开头一句中的include,它是把后面跟的文件内容给加到它出现的地方,这里就是把$(OBJTREE)/include/config.mk文件的内容加入到了这里,所以就得到了ARCH,BOARD与CPU的值,如果VENDOR与SOC也定义了,也会得到它们的值。
ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),$(ARCH)) //HOSTARCH=i386,ARCH=ARM,
CROSS_COMPILE =
else
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
ifeq ($(ARCH),arm)
#CROSS_COMPILE = arm-linux-
#CROSS_COMPILE = /usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux-
#CROSS_COMPILE = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
Endif
定义交叉编译工具链有两种方式:
#1.在编译时用make CROSS_COMPILE=xxxx来设置,编译时传参的方法可以覆盖Makefile里面的设置。
#2.在Makefile中去更改设置CROSS_COMPILE的值
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
endif # HOSTARCH,ARCH
endif # CROSS_COMPILE
export CROSS_COMPILE
导出主机的交叉编译工具链。
# load other configuration
include $(TOPDIR)/config.mk
将源目录下的config.mk包含进来。
-------------------------------config.mk讲解--------------------------------------------
该config.mk位于uboot源码的根目录下, 其包含了子目录下许多同名的config.mk。所以千万注意这些同名文件的主次区别
该文件内容主要结构为:
1. 设置各种路径
2. 设置主机环境的编译选项
3. 确定各交叉编译工具
4. 确定各种级别的编译选项
5. 指定链接脚本
6. 获得起始链接地址
7. 设置头文件搜寻路径
8. 使用起始链接地址
9. 设置自动推导规则
需要注意的是,结构顺序并不一定代表代码执行顺序
#########################################################################
1. 设置各种路径
ifneq ($(OBJTREE),$(SRCTREE))
# 如果源码目录与输出目录不是同一目录
ifeq ($(CURDIR),$(SRCTREE))
dir :=
#判断当前目录是否是顶层目录,如果是,设置dir为空
else
dir := $(subst $(SRCTREE)/,,$(CURDIR))
# 如果不是,那么dir路径为 (CURDIR)去掉(SRCTREE)的部分
endif
# 如果dir不为空,则obj路径在(OBJTREE)/dir/目录下。# 否则obj路径在(OBJTREE)目录下
obj := $(if $(dir),$(OBJTREE)/$(dir)/,$(OBJTREE)/)
src := $(if $(dir),$(SRCTREE)/$(dir)/,$(SRCTREE)/)
# 设置src路径,如果dir目录不为空,则src路径指向dir,否则指向(SRCTREE)
$(shell mkdir -p $(obj))
else
obj :=
src :=
endif
本段代码还是在进行原地编译和外部输出编译的一些路径设置,说到底,其实是把主Makefile中和路径有关的变量导入进来
# clean the slate ...
PLATFORM_RELFLAGS =
PLATFORM_CPPFLAGS = #编译选项
PLATFORM_LDFLAGS = #连接选项
几个FLAG清除.用这3个变量表示交叉编译器的编译选项,在后面Make会检查交叉编译器支持的编译选项,然后将适当的选项添加到这3个变量中。
#########################################################################
2. 设置主机环境的编译选项
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
知识点8:先讲"\",它是续行符(如果空间允许其它那些代码是可以写在同一行上的)
SHELL if[-x filename] 表示如果这个filename文件是可执行的,则为真,
如果$BASH值非空就将它赋于CONFIG_SHELL;要不若/bin/bash存在,则CONFIG_SHELL置值为/bin/sh;否则CONFIG_SHELL置值为sh。
ifeq ($(HOSTOS)-$(HOSTARCH),darwin-ppc)
HOSTCC = cc
else
HOSTCC = gcc
endif
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTSTRIP = strip
shell的一些配置过程,和主机编译器的一些配置,确定编译工具是gcc还是cc。#########################################################################
4. 确定各种级别的编译选项
# Option checker (courtesy linux kernel) to ensure
# only supported compiler options are used
#
cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
可以看出这是一条编译命令,变量CC是编译器,变量CFLAGS是编译选项,其中选项
-S:编译后立即结束,不进行汇编等操作。
-o /dev/null : 生成文件到/dev/null,即不生成任何编译结果,要编译的文件也为空。
-xc: 指定按c语言编译
https://www.cnblogs.com/xianghang123/archive/2012/03/23/2413381.html。
把/dev/null 看作"黑 洞" . 它非常等价于一个只写文件. 所有写入它的内容都会永远丢失. 而尝试从它那儿读取内容则什么也读不到. 然而, /dev/null 对命令行和脚本都非常的有 用.
Linux的IO输入输出有三类
Standard Input 代码 0
Standard Output 代码 1
Standard Error 代码 2
ls thereisno 1> out.txt 标准输出重定向 也可以不加1写成 ls thereisno > out.txt
ls thereisno 2> out.txt 标准错误重定向
ls thereisno > out.txt 2>&1 把错误定向到标准输出,然后统一重定向
ls thereisno &> out.txt 所有IO重定向(&>中的&可以代表任意,0,1,2,3……)
调用此语句如:call cc-option,-a,-b 则如果支持-a选项则返回-a否则返回-b。
这句话的意思是,测试cc是否支持第一个参数,然后根据返回结果选用不同的参数
变量CC和CFLAGS在后面的代码定义为延时变量,其中的CC即arm-linux-gcc。函数cc-option用于检查编译器CC是否支持某选项。将2个选项作为参数传递给cc-option函数,该函数调用CC编译器检查参数1是否支持,若支持则函数返回参数1,否则返回参数2 (因此CC编译器必须支持参数1或参数2,若两个都不支持则会编译出错)。可以像下面这样调用cc-option函数,并将支持的选项添加到FLAGS中:
FLAGS +=$(call cc-option,option1,option2)
#
# Include the make variables (CC, etc...)
#
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
LDR = $(CROSS_COMPILE)ldr
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
RANLIB = $(CROSS_COMPILE)RANLIB
定义交叉编译链工具,确定了完整的各交叉编译工具,通过顶层makefile中得到的CROSS_COMPILE变量(即工具链的前缀),由此定义各工具的名称
对于arm开发板,其中的CROSS_COMPILE在lib_arm/config.mk文件中定义:
CROSS_COMPILE ?= arm-linux-
因此以上代码指定了使用前缀为"arm-linux-"的编译工具,即arm-linux-gcc,arm-linux-ld等等。
#########################################################################
设置CPU相关路径,配置文件等
# Load generated board configuration
# 加载配置文件
sinclude $(OBJTREE)/include/autoconf.mk
(1)autoconfig.mk文件不是源码提供的,是配置过程自动生成的。
(2)这个文件的作用就是用来指导整个uboot的编译过程。这个文件的内容其实就是很多CONFIG_开头的宏(可以理解为变量),这些宏/变量会影响我们uboot编译过程的走向(原理就是条件编译)。在uboot代码中有很多地方使用条件编译进行编写,这个条件编译是用来实现可移植性的。(可以说uboot的源代码在很大程度来说是拼凑起来的,同一个代码包含了各种不同开发板的适用代码,用条件编译进行区别。)
(3)这个文件不是凭空产生的,配置过程也是需要原材料来产生这个文件的。原材料在源码目录的inlcude/configs/xxx.h头文件。(X210开发板中为include/configs/x210_sd.h)。这个h头文件里面全都是宏定义,这些宏定义就是我们对当前开发板的移植。每一个开发板的移植都对应这个目录下的一个头文件,这个头文件里每一个宏定义都很重要,这些配置的宏定义就是我们移植uboot的关键所在。
ifdef ARCH
sinclude $(TOPDIR)/$(ARCH)_config.mk # include architecture dependend rules
endif
$(ARCH)的值是"arm",因此将"lib_arm/config.mk"包含进来。lib_arm/config.mk脚本指定了交叉编译器,添加了一些跟CPU架构相关的编译选项,最后还指定了cpu/arm920t/u-boot.lds为U-Boot的连接脚本。
ifdef CPU
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk # include CPU specific rules
endif
$(CPU)的值是"arm920t",因此将"cpu/arm920t/config.mk"包含进来。这个脚本主要设定了跟arm920t处理器相关的编译选项。
ifdef SOC
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk # include SoC specific rules
endif
$(SOC)的值是s3c24x0,因此Make程序尝试将cpu/arm920t/s3c24x0/config.mk包含进来,而这个文件并不存在,但是由于用的是"sinclude"命令,所以并不会报错。
ifdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
Endif
$(BOARD)的值是mini2440,VENDOR的值是samsung,因此BOARDDIR的值是samsung/mini2440。BOARDDIR变量表示开发板特有的代码所在的目录。
ifdef BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
Make将"board/samsung/mini2440/config.mk"包含进来。该脚本只有如下的一行代码:
TEXT_BASE = 0x33F80000
本段最开始包含了一个autoconf.mk文件,此文件也不是源码自带的,其内容全部都是CONFIG_开头的变量,makefile利用这些变量来指导编译过程的走向(.c文件条件编译)
autoconf.mk其实是由顶层Makefile利用根目录下include/configs/x210_sd.h生成的,其内容与x210_sd.h没什么区别,其生成方式在顶层makefile的470多行左右
uboot的可移植性很大程度来源于x210_sd.h文件,它也是移植工作的关键所在
后面几行分别inlude了和ARCH、CPU、SOC、VENDOR、BOARD相关的子config.mk,也就是包含了各种级别的编译属性选项
#########################################################################
5. 指定链接脚本
关于编译器的一些设置:链接脚本
ifneq (,$(findstring s,$(MAKEFLAGS)))
ARFLAGS = cr
else
ARFLAGS = crv
endif
RELFLAGS= $(PLATFORM_RELFLAGS)
DBGFLAGS= -g # -DDEBUG
OPTFLAGS= -Os #-fomit-frame-pointer
ifndef LDSCRIPT
#LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug
ifeq ($(CONFIG_NAND_U_BOOT),y)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
else
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif
endif
OBJCFLAGS += --gap-fill=0xff
本段最开始先配置了一些编译选项,连接脚本的选择。
(1)随后检测是否定义过LDSCRIPT这个和链接脚本有关的变量,如果没有定义,则判断CONFIG_NAND_U_BOOT(autoconf.mk中定义)这个值是否为y
(2)若未使用nandflash,则链接脚本为指定路径下的u-boot.lds。若使用了nandflash,则链接脚本为指定路径下的u-boot-nand.lds
(3)实际在board\samsung\x210目录下有u-boot.lds,这个就是链接脚本。我们在分析uboot的编译链接过程时就要考虑这个链接脚本。
gccincdir := $(shell $(CC) -print-file-name=include)
6. 获得起始链接地址
CPPFLAGS := $(DBGFLAGS) $(OPTFLAGS) $(RELFLAGS) \
-D__KERNEL__
ifneq ($(TEXT_BASE),)
CPPFLAGS += -DTEXT_BASE=$(TEXT_BASE)
CFLAGS 只用于 C 预编处理,
CXXFLAGS 只用于 C++ 预编处理
CPPFLAGS 可以 用于 C 和 C++ 两者
endif
本段最开始先配置了一些编译选项,随后做了一些和TEXT_BASE相关的工作。
(1)Makefile中在配置X210开发板时,在board/samsung/x210目录下生成了一个文件config.mk,其中的内容就是:TEXT_BASE = 0xc3e00000相当于定义了一个变量。
(2)TEXT_BASE是将来我们整个uboot链接时指定的链接地址。因为uboot中启用了虚拟地址映射,因此这个C3E00000地址就等于0x23E00000(也可能是33E00000具体地址要取决于uboot中做的虚拟地址映射关系)。
(3)回顾裸机中讲的链接地址的问题,再想想dnw方式先下载x210_usb.bin然后再下载uboot.bin时为什么第二个地址是23E00000.
在前面120多行左右include了一个board级别的config.mk文件 ,这个.mk文件源码中不存在,是在顶层makefile中由x210_sd_config创建的,并在ARCH、SOC等变量后面添加TEXT_BASE,展开后就获得了一个TEXT_BASE变量
TEXT_BASE这个变量的含义是uboot将来被链接时的起始地址,是规定好的,但由于uboot使用虚拟地址映射,所以这个地址并不是真正的物理地址
最后进行判断,如果TEXT_BASE不为空,就将其设置到CPPFLAGS里面去
7. 设置头文件搜寻路径
ifneq ($(OBJTREE),$(SRCTREE))
CPPFLAGS += -I$(OBJTREE)/include2 -I$(OBJTREE)/include
endif
CPPFLAGS += -I$(TOPDIR)/include
CPPFLAGS += -fno-builtin -ffreestanding -nostdinc \
-isystem $(gccincdir) -pipe $(PLATFORM_CPPFLAGS)
本段设置了头文件的搜寻路径,添加了顶层目录下的include文件夹作为搜索路径。然后进行了一些其他设置(比如禁止标准的include路径)
这样,编译器在编译的时候就能正确的读取到include文件夹下的头文件(其实是配置阶段创建的符号链接)了
ifdef BUILD_TAG
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes \
-DBUILD_TAG='"$(BUILD_TAG)"'
else
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes
endif
CFLAGS += $(call cc-option,-fno-stack-protector)
# avoid trigraph warnings while parsing pci.h (produced by NIOS gcc-2.9)
# this option have to be placed behind -Wall -- that's why it is here
ifeq ($(ARCH),nios)
ifeq ($(findstring 2.9,$(shell $(CC) --version)),2.9)
CFLAGS := $(CPPFLAGS) -Wall -Wno-trigraphs
endif
endif
我们不看
# $(CPPFLAGS) sets -g, which causes gcc to pass a suitable -g<format>
# option to the assembler.
AFLAGS_DEBUG :=
# turn jbsr into jsr for m68k
ifeq ($(ARCH),m68k)
ifeq ($(findstring 3.4,$(shell $(CC) --version)),3.4)
AFLAGS_DEBUG := -Wa,-gstabs,-S
endif
endif
我们不看
8. 使用起始链接地址
AFLAGS := $(AFLAGS_DEBUG) -D__ASSEMBLY__ $(CPPFLAGS)
U-Boot编译时将使用TEXT_BASE作为代码段连接的起始地址。
LDFLAGS += -Bstatic -T $(LDSCRIPT) $(PLATFORM_LDFLAGS)
ifneq ($(TEXT_BASE),)
LDFLAGS += -Ttext $(TEXT_BASE)
Endif
执行完以上代码后,LDFLAGS中包含了"-Bstatic -T u-boot.lds "和"-Ttext 0x33F80000"的字样。
本段是把TEXT_BASE用-Ttext传给了链接脚本,故链接脚本将从TEXT_BASE开始链接
CFLAGS 表示用于 C 编译器的选项,
CXXFLAGS 表示用于 C++ 编译器的选项。
这两个变量实际上涵盖了编译和汇编两个步骤。
CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。
LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以把那个包的lib路径加入的LDFALGS中试一下。
LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv
简单地说,LDFLAGS是告诉链接器从哪里寻找库文件,而LIBS是告诉链接器要链接哪些库文件。不过使用时链接阶段这两个参数都会加上,所以你即使将这两个的值互换,也没有问题。
有时候LDFLAGS指定-L虽然能让链接器找到库进行链接,但是运行时链接器却找不到这个库,如果要让软件运行时库文件的路径也得到扩展,那么我们需要增加这两个库给"-Wl,R":
LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib
如果在执行./configure以前设置环境变量export LDFLAGS="-L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib" ,注意设置环境变量等号两边不可以有空格,而且要加上引号(shell的用法)。那么执行configure以后,Makefile将会设置这个选项,链接时会有这个参数,编译出来的可执行程序的库文件搜索路径就得到扩展了。
判断TEST_BASE是否存在。然后追加至LDFFLAGS
# Location of a usable BFD library, where we define "usable" as
# "built for ${HOST}, supports ${TARGET}". Sensible values are
# - When cross-compiling: the root of the cross-environment
# - Linux/ppc (native): /usr
# - NetBSD/ppc (native): you lose ... (must extract these from the
# binutils build directory, plus the native and U-Boot include
# files don't like each other)
#
# So far, this is used only by tools/gdb/Makefile.
ifeq ($(HOSTOS)-$(HOSTARCH),darwin-ppc)
BFD_ROOT_DIR = /usr/local/tools
else
ifeq ($(HOSTARCH),$(ARCH))
# native
BFD_ROOT_DIR = /usr
else
#BFD_ROOT_DIR = /LinuxPPC/CDK # Linux/i386
#BFD_ROOT_DIR = /usr/pkg/cross # NetBSD/i386
BFD_ROOT_DIR = /opt/powerpc
endif
endif
ifeq ($(PCI_CLOCK),PCI_66M)
CFLAGS := $(CFLAGS) -DPCI_66M
endif
#########################################################################
export CONFIG_SHELL HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE \
AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP \
MAKE
export TEXT_BASE PLATFORM_CPPFLAGS PLATFORM_RELFLAGS CPPFLAGS CFLAGS AFLAGS
导出上述的所有变量,编译项,编译工具,链接器等等。
#########################################################################
9. 设置自动推导规则
ifndef REMOTE_BUILD
%.s: %.S
$(CPP) $(AFLAGS) -o $@ $<
$@代表规则中的目标。
OBJS = ifconfig.o tftp_util.o ping.o
SRCS = $(OBJS:.o=.c)
PHONY += all
all : $(BUILT_IN_OBJ)
$(BUILT_IN_OBJ) : $(OBJS)
$(LD) $(LDFLAGS) -r $^ -o $@
$(OBJS) : $(SRCS)
$(CC) $(CFLAGS) -c $^
PHONY += clean
clean :
@rm -vf *.o
.PHONY : $(PHONY)
说明:$(BUILT_IN_OBJ) 和 $(PHONY)来自上层目录g-bios/app/Makefile,可以不用理解。
$@ 代表目标 上例为$(BUILT_IN_OBJ)
$^ 代表所有的依赖对象 上例第一个$^为$(OBJS),即 ifconfig.o tftp_util.o ping.o,上例第二个$^为$(SRCS),即$(OBJS:.o=.c) (Makefile解释后为: ifconfig.c tftp_util.c ping.c)
$< 代表第一个依赖对象 前面例子修改如下:
$(BUILT_IN_OBJ) : $(OBJS)
$(LD) $(LDFLAGS) -r $< -o $@
则$<表示为ifconfig.o
%.o: %.S
$(CC) $(AFLAGS) -c -o $@ $<
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
else
$(obj)%.s: %.S
$(CPP) $(AFLAGS) -o $@ $<
$(obj)%.o: %.S
$(CC) $(AFLAGS) -c -o $@ $<
$(obj)%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
endif
本段是makefile的自动推导规则,和顶层Makefile内的规则配合使用,就能实现众多文件的编译
#########################################################################
整个源目录的cofig,mk就结束了。回顾一下整个文件做了那些东西:
1. 设置各种路径
2. 设置主机环境的编译选项
3. 确定各交叉编译工具
4. 确定各种级别的编译选项
5. 指定链接脚本
6. 获得起始链接地址
7. 设置头文件搜寻路径
8. 使用起始链接地址
9. 设置自动推导规则
-----------------------------------------------------------------------------------------
下面为U-boot需要的目标文件。 顺序很重要,start.o必须放第一位。
#########################################################################
# U-Boot objects....order is important (i.e. start must be first)
.a是库文件
OBJS = cpu/$(CPU)/start.o
//确定目标文件构成
OBJS := $(addprefix $(obj),$(OBJS))
//给待生成的目标文件带上路径
LIBS = lib_generic/libgeneric.a
LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \
"board/$(VENDOR)/common/lib$(VENDOR).a"; fi)
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS。$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += drivers/bios_emulator/libatibiosemu.a
LIBS += drivers/block/libblock.a
LIBS += drivers/dma/libdma.a
LIBS += drivers/hwmon/libhwmon.a
LIBS += drivers/i2c/libi2c.a
LIBS += drivers/input/libinput.a
LIBS += drivers/misc/libmisc.a
LIBS += drivers/mmc/libmmc.a
LIBS += drivers/mtd/libmtd.a
LIBS += drivers/mtd/nand/libnand.a
LIBS += drivers/mtd/nand_legacy/libnand_legacy.a
LIBS += drivers/mtd/onenand/libonenand.a
LIBS += drivers/mtd/ubi/libubi.a
LIBS += drivers/mtd/spi/libspi_flash.a
LIBS += drivers/net/libnet.a
LIBS += drivers/net/sk98lin/libsk98lin.a
LIBS += drivers/pci/libpci.a
LIBS += drivers/pcmcia/libpcmcia.a
LIBS += drivers/spi/libspi.a
LIBS += drivers/rtc/librtc.a
LIBS += drivers/serial/libserial.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/video/libvideo.a
LIBS += common/libcommon.a
LIBS += libfdt/libfdt.a
LIBS += api/libapi.a
LIBS += post/libpost.a
//确定库文件构成.LIBS变量指明了U-Boot需要的库文件,包括平台/开发板相关的目录、通用目录下相应的库,都通过相应的子目录编译得到的。
LIBS := $(addprefix $(obj),$(LIBS))
//给待生成的库文件带上路径
.PHONY : $(LIBS) $(VERSION_FILE)
LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).a
LIBBOARD := $(addprefix $(obj),$(LIBBOARD))
//将带生成的库文件作为伪目标来处理
# Add GCC lib
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
//添加系统标准库
# The "tools" are needed early, so put this first
# Don't include stuff already done in $(LIBS)
SUBDIRS = tools \
examples \
api_examples
.PHONY : $(SUBDIRS)
//定义make执行要首先处理的目录
ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif
ifeq ($(CONFIG_ONENAND_U_BOOT),y)
ONENAND_IPL = onenand_bl1
U_BOOT_ONENAND = $(obj)u-boot-onenand.bin
endif
//假若定义了CONFIG_NAND_U_BOOT,那么将总目标all中添加$(obj)u-boot-nand.bin,否则不添加
__OBJS := $(subst $(obj),,$(OBJS))
__LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))
//OBJS、LIBS除去$(obj)部分的路径
#以上为UBOOT需要的库文件。
#根据上面的include/config.mk文件定义的ARCH、CPU、BOARD、SOC这些变量。
#硬件平台依赖的目录文件可以根据这些定义来确定。SMDK2410平台相关目录及对应生成的库文件如下。
#board/smdk2410/ :库文件board/smdk2410/libsmdk2410.a
#cpu/arm920t/ :库文件cpu/arm920t/libarm920t.a
#cpu/arm920t/s3c24x0/ : 库文件cpu/arm920t/s3c24x0/libs3c24x0.a
#lib_arm/ : 库文件lib_arm/libarm.a
#include/asm-arm/ :下面两个是头文件。
#include/configs/smdk2410.h
#########################################################################
#########################################################################
#下面是最终生成的各种镜像文件:
#这里就是定义了各种目标的target。我们最终想要的目标就是$(obj)u-boot,然后看后面的,
#它都信赖了好多其它的目标,然后这些个目标,也是在此处定义的,
#然后有相应的$(MAKE)来执行相应的操作,所以这样就实现了一个Makefile文件,
#套很多个其它的Makefile文件来编译整个工程的情况。
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND) $(obj)u-boot.dis
//总目标的构成,也可以人为添加其他的目标
all: $(ALL)
//定义总目标all
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
//生成16进制的可执行程序
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.ldr: $(obj)u-boot
$(LDR) -T $(CONFIG_BFIN_CPU) -f -c $@ $< $(LDR_FLAGS)
$(obj)u-boot.ldr.hex: $(obj)u-boot.ldr
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@ -I binary
$(obj)u-boot.ldr.srec: $(obj)u-boot.ldr
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@ -I binary
//生成2进制的可执行程序
$(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e 0 \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@
$(obj)u-boot.sha1: $(obj)u-boot.bin
$(obj)tools/ubsha1 $(obj)u-boot.bin
$(obj)u-boot.dis: $(obj)u-boot
$(OBJDUMP) -d $< > $@
//生成反汇编文件u-boot.dis
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
这里生成的$(obj)u-boot目标就是ELF格式的U-Boot文件了。由于CONFIG_KALLSYMS未定义,因此ifeq ($(CONFIG_KALLSYMS),y)与endif间的代码不起作用。其中depend,$(SUBDIRS),$(OBJS),$(LIBBOARD),$(LIBS),$(LDSCRIPT), $(obj)u-boot.lds是$(obj)u-boot的依赖,而$(GEN_UBOOT)编译命令。
$(obj)u-boot.lds: $(LDSCRIPT)
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@
以上执行结果实质上是将cpu/arm920t/u-boot.lds经编译器简单预处理后输出到U-Boot顶层目录下的u-boot.lds文件。其中的cpu/arm920t/u-boot.lds文件内容如下:
--------------------------------------uboot-lds讲解-------------------------------------
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-
arm")*/OUTPUT_ARCH(arm)
ENTRY(_start)ENTRY(_start)用来指定整个程序的入口地址。所谓入口地址就是整个程序的开头地址,可以认为就是整个程序的第一句指令。有点像C语言中的main。之前在裸机中告诉大家,指定程序的链接地址有2种方法:一种是在Makefile中ld的flags用-Ttext 0x20000000来指定;第二种是在链接脚本的SECTIONS开头用.=0x20000000来指定。两种都可以实现相同效果。其实,这两种技巧是可以共同配合使用的,也就是说既在链接脚本中指定也在ld flags中用-Ttext来指定。两个都指定以后以-Ttext指定的为准。
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/s5pc11x/start.o (.text)
cpu/s5pc11x/s5pc110/cpu_init.o (.text)
board/samsung/x210/lowlevel_init.o (.text)
cpu/s5pc11x/onenand_cp.o (.text)
cpu/s5pc11x/nand_cp.o (.text)
cpu/s5pc11x/movi.o (.text)
common/secure_boot.o (.text)
common/ace_sha1.o (.text)
cpu/s5pc11x/pmic.o (.text)
*(.text)
}
* 在代码段中注意文件排列的顺序。指定必须放在前面部分的那些文件就是那些必须安排在前16KB内的文件,这些文件中的函数在前16KB会被调用。在后面第二部分(16KB之后)中调用的程序,前后顺序就无所谓了。cpu/arm920t/start.o放在最前面,保证最先执行的是start.o */
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
.mmudata : { *(.mmudata) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
链接脚本中除了.text .data .rodata .bss段等编译工具自带的段之外,编译工具还允许我们自定义段。譬如uboot总的.u_boot_cmd段就是自定义段。自定义段很重要。
//生成elf格式的u-boot文
$(OBJS): depend $(obj)include/autoconf.mk
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
上规则表明,对于OBJS包含的每个成员,都进入cpu/$(CPU)目录(即cpu/arm920t)编译它们。//中间目标文件生成
$(LIBS): depend $(obj)include/autoconf.mk
$(MAKE) -C $(dir $(subst $(obj),,$@))
//中间库文件生成,上面的规则表明,对于LIBS中的每个成员,都进入相应的子目录执行"make"命令编译它们。例如对于LIBS中的"common/libcommon.a"成员,程序将进入common目录执行Makefile,生成libcommon.a 。
$(LIBBOARD): depend $(LIBS) $(obj)include/autoconf.mk
$(MAKE) -C $(dir $(subst $(obj),,$@))
这里LIBBOARD的值是 $(obj)board/samsung/mini2440/libmini2440.a。make执行board/samsung/mini2440/目录下的Makefile,生成libmini2440.a 。
$(SUBDIRS): depend $(obj)include/autoconf.mk
$(MAKE) -C $@ all
******假想目标$(SUBDIRS)******$@代表规则中的目标,所以这条命令会在$(SUBDIRS)下执行的make all,$(SUBDIRS)的值上面已经说过。依赖目标depend参考上面。即会在tool,example等目录下make。
执行tools ,examples/standalone ,examples/api目录下的Make//SUBDIRS目录处理
$(LDSCRIPT): depend $(obj)include/autoconf.mk
$(MAKE) -C $(dir $@) $(notdir $@)
"$(MAKE) -C $(dir $@) $(notdir $@)"命令经过变量替换后就是"make -C cpu/arm920t/ u-boot.lds"。也就是转到cpu/arm920t/目录下,执行"make u-boot.lds"命令。这个目标的变量是一个连接的脚本,上文有提到。make它
$(NAND_SPL): $(VERSION_FILE) $(obj)include/autoconf.mk
$(MAKE) -C nand_spl/board/$(BOARDDIR) all
$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin $(obj)include/autoconf.mk
cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin
#$(ONENAND_IPL): $(VERSION_FILE) $(obj)include/autoconf.mk
# $(MAKE) -C onenand_ipl/board/$(BOARDDIR) all
#
#$(U_BOOT_ONENAND): $(ONENAND_IPL) $(obj)u-boot.bin $(obj)include/autoconf.mk
# cat $(obj)onenand_ipl/onenand-ipl-2k.bin $(obj)u-boot.bin > $(obj)u-boot-onenand.bin
# cat $(obj)onenand_ipl/onenand-ipl-4k.bin $(obj)u-boot.bin > $(obj)u-boot-flexonenand.bin
$(ONENAND_IPL): $(VERSION_FILE) $(obj)include/autoconf.mk
$(MAKE) -C $(obj)onenand_bl1/$(BOARD) all
$(U_BOOT_ONENAND): $(ONENAND_IPL) $(obj)u-boot.bin $(obj)include/autoconf.mk
cat $(obj)onenand_bl1/$(BOARD)/BL1.bin.padding $(obj)u-boot.bin > $(U_BOOT_ONENAND)
$(VERSION_FILE):
@( printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' "$(U_BOOT_VERSION)" \
'$(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion $(TOPDIR))' \
) > $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
//版本头文件生成
gdbtools:
$(MAKE) -C tools/gdb all || exit 1
updater:
$(MAKE) -C tools/updater all || exit 1
env:
$(MAKE) -C tools/env all MTD_VERSION=${MTD_VERSION} || exit 1
depend dep: $(VERSION_FILE)
for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
//执行make depend或者make dep都能触发命令.对于$(SUBDIRS),cpu/$(CPU),$(dir $(LDSCRIPT))中的每个元素都进入该目录执行"make _depend",生成各个子目录的.depend文件,.depend列出每个目标文件的依赖文件。
TAG_SUBDIRS += include
TAG_SUBDIRS += lib_generic board/$(BOARDDIR)
TAG_SUBDIRS += cpu/$(CPU)
TAG_SUBDIRS += lib_$(ARCH)
TAG_SUBDIRS += fs/cramfs
TAG_SUBDIRS += fs/fat
TAG_SUBDIRS += fs/fdos
TAG_SUBDIRS += fs/jffs2
TAG_SUBDIRS += net
TAG_SUBDIRS += disk
TAG_SUBDIRS += common
TAG_SUBDIRS += drivers/bios_emulator
TAG_SUBDIRS += drivers/block
TAG_SUBDIRS += drivers/hwmon
TAG_SUBDIRS += drivers/i2c
TAG_SUBDIRS += drivers/input
TAG_SUBDIRS += drivers/misc
TAG_SUBDIRS += drivers/mmc
TAG_SUBDIRS += drivers/mtd
TAG_SUBDIRS += drivers/mtd/nand
TAG_SUBDIRS += drivers/mtd/nand_legacy
TAG_SUBDIRS += drivers/mtd/onenand
TAG_SUBDIRS += drivers/mtd/spi
TAG_SUBDIRS += drivers/net
TAG_SUBDIRS += drivers/net/sk98lin
TAG_SUBDIRS += drivers/pci
TAG_SUBDIRS += drivers/pcmcia
TAG_SUBDIRS += drivers/qe
TAG_SUBDIRS += drivers/rtc
TAG_SUBDIRS += drivers/serial
TAG_SUBDIRS += drivers/spi
TAG_SUBDIRS += drivers/usb
TAG_SUBDIRS += drivers/video
tags ctags:
ctags -w -o $(obj)ctags `find $(SUBDIRS) $(TAG_SUBDIRS) \
-name '*.[ch]' -print`
etags:
etags -a -o $(obj)etags `find $(SUBDIRS) $(TAG_SUBDIRS) \
-name '*.[ch]' -print`
cscope:
find $(SUBDIRS) $(TAG_SUBDIRS) -name '*.[ch]' -print \
> cscope.files
cscope -b -q -k
$(obj)System.map: $(obj)u-boot
@$(NM) $< | \
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
sort > $(obj)System.map
//生成map文件
#
# Auto-generate the autoconf.mk file (which is included by all makefiles)
#
# This target actually generates 2 files; autoconf.mk and autoconf.mk.dep.
# the dep file is only include in this top level makefile to determine when
# to regenerate the autoconf.mk file.
$(obj)include/autoconf.mk.dep:
$(obj)include/config.h include/common.h
@$(XECHO) Generating $@ ; \
set -e ; \
: Generate the dependancies ; \
$(CC) -x c -DDO_DEPS_ONLY -M $(HOST_CFLAGS) $(CPPFLAGS) \
-MQ $(obj)include/autoconf.mk include/common.h > $@
include/autoconf.mk依赖于make <board_name>_config 命令生成的include/config.h。因此执行make <board_name>_config命令后再执行make all将更新include/autoconf.mk。
编译选项"-dM"的作用是输出include/common.h中定义的所有宏。根据上面的规则,编译器提取include/common.h中定义的宏,然后输出给tools/scripts/define2mk.sed脚本处理,处理的结果就是include/autoconf.mk文件。其中tools/scripts/define2mk.sed脚本的主要完成了在include/common.h中查找和处理以"CONFIG_"开头的宏定义的功能。
include/common.h文件包含了include/config.h文件,而include/config.h文件又包含了config_defaults.h,configs/mini2440.h,asm/config.h文件。因此include/autoconf.mk实质上就是config_defaults.h,configs/mini2440.h,asm/config.h三个文件中"CONFIG_"开头的有效的宏定义的集合。
$(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 > $@
sinclude $(obj)include/autoconf.mk.dep
#########################################################################
else # !config.mk
all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
$(SUBDIRS) $(VERSION_FILE) gdbtools updater env depend \
dep tags ctags etags cscope $(obj)System.map:
@echo "System not configured - see README" >&2
@ exit 1
endif # config.mk
.PHONY : CHANGELOG
CHANGELOG:
git log --no-merges U-Boot-1_1_5.. | \
unexpand -a | sed -e 's/\s\s*$$//' > $@
#########################################################################
unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
$(obj)board/$(VENDOR)/$(BOARD)/config.mk
看得出来,它是删掉了包括$(obj)include/config.mk文件在内的,几个在编译的时候生成的配置文件,这个$(obj)include/config.mk与$(OBJTREE)/include/config.mk是同一个文件。这样做是为了保证重新生成文件的纯粹性。
########################################################################
## ARM Integrator boards - see doc/README-integrator for more info.
x210_sd_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
这里的$(MKCONFIG)就会被替换成上文中所说的$(SRCTREE)/mkconfig文件路径,相关于是把这个文件的内容,当可执行程序执行了,其实,它就是一个shell脚本。一定要注意这句$(@:_config=),其实这里是用了一个比较高级的Makefile语法,$(a:patternA=patternB),这样的语法表示把a变量里的形式为patternA的换成为patternB,然后输出。那么在这个例子里,a变量换成了@,它的意思与shell里@的意思是一样的,就是输入进来的所有的参数,但是中间不用环境变量IFS所定义的分隔符分开。patternA就是_config,patternB就是空。然后再看看咱们执行这个Makefile的时候输入的语句:
make smdk2410_config
那么在这里的话,传到$(SRCTREE)/mkconfig这个脚本里的参数实际上就是 smdk2410 arm arm920t smdk2410 NULL s3c24x0
我们来看一下MkCONFIG的代码:
#!/bin/sh -e
# Script to create header files and links to configure
# U-Boot for a specific board.
#确定开发板名称BOARD_NAME
# Parameters: Target Architecture CPU Board [VENDOR] [SOC]
#
# (C) 2002-2006 DENX Software Engineering, Wolfgang Denk <wd@denx.de>
#
/* $#: ./mkconfig smdk2410 arm arm920t smdk2410 samsung s3c24x0命令行参数的个数
* $0 $1 $2 $3 $4 $5 $6
* $符号总结:
* $#: 代表后接的参数个数,以上为例这里为[6]
* $@: 代表["$1" "$2" "$3" "$4"]之意,每个变量是独立的(用双引号括起来)
* $*: 代表["$1c$2c$3c$4"],其中c为分割字符,默认为空格键,所以本例中代表["$1 $2 $3 $4"]
*
* -gt: great than; -lt: less than
*/
APPEND=no # Default: Create new config file
# no表示创建新的配置文件,yes表示追加到配置文件中
BOARD_NAME="" # Name to print in make output
while [ $# -gt 0 ] ; do
case "$1" in
/* shift命令:
* 变量号码偏移功能,简单来说就是移动变量
*这里第一个参数是 smdk_2410,所以不进入这个分支。不过需要说明一个东西,
shift:
简要说来shift,用于把shell脚本的参数迁移。
比如说,我们执行了一次shift,那么 $1 没了,$2变为$1,$3变为$2。并且参数个的个数$#也跟着改变。
同理,如果是shift 3,那么$4变为s$1。$5变成$2。依次类推。
shitf 不跟参数,等于shitf 1。
*/
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done
# 为来方便理解,这里用一个类似 C 语法表示:
#while(xxx
#{
# switch "$1" {
# case --: shift ; break ;
# case -a: shift ; APPEND=yes ;
# case -n: shift ; BOARD_NAME="${1%%_config}" ; shift ;
# case -t: shift ; TARGETS="`echo $1 | sed 's:_: :g'` ${TARGETS}" ; shift ;
# default : break ;
# }
#}
#
# 备注: $# 参数个数 $1 第一个参数
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
环境变量$#表示传递给脚本的参数个数,这里的命令有6个参数,因此$#是6 。shift的作用是使$1=$2,$2=$3,$3=$4….,而原来的$1将丢失。因此while循环的作用是,依次处理传递给mkconfig脚本的选项。由于我们并没有传递给mkconfig任何的选项,因此while循环中的代码不起作用。最后将BOARD_NAME的值设置为$1的值,在这里就是"mini2440"。
[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1
从开始的shell注释中可以看出,我们这个脚本的参数个数为 4-6个
****$#表示输入进来的参数个 数,-gt(great than)表示左边大于右边则返回真
SHELL常用内部参数:
$# ----传递给程序的总的参数数目
$? ----上一个代码或者shell程序在shell中退出的情况,如果正常退出则返回0,反之为非0值。
$* ----传递给程序的所有参数组成的字符串。
$n ----表示第几个参数,$1 表示第一个参数,$2 表示第二个参数 ...
$0 ----当前程序的名称
$@----以"参数1" "参数2" ... 形式保存所有参数
$$ ----本程序的(进程ID号)PID
$! ----上一个命令的PID
mkconfig脚本的6个参数
$(@:_config=) arm s5pc11x x210 samsung s5pc110
x210_sd_config里的_config部分用空替换,得到:x210_sd,这就是第一个参数,所以:
$1: x210_sd
$2: arm
$3: s5pc11x
$4: x210
$5: samsumg
$6: s5pc110
所以,$# = 6
echo "Configuring for ${BOARD_NAME} board..."
这样语句是在执行make smdk_config这个命令的时候,输出的唯一output
#
# Create link to architecture specific headers
创建到目标板相关的目录的链接
#
#
判断是否在源代码目录下编译 Uboot , 是的话, 直接跳到 else 处执行 , if 内的不执行
if [ "$SRCTREE" != "$OBJTREE" ] ; then
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$2 asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$2
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
第一个:在include目录下创建asm文件,指向asm-arm。(46-48行)
else
cd ./include
rm -f asm
ln -s asm-$2 asm
fi
* -f: 删除是不显示提示信息,对于不存在的文件,会忽略掉
* asm: 上次配置过程中建立的连接文件
*/
若将目标文件设定为输出到源文件所在目录,则以上代码在include目录下建立了到asm-arm目录的符号链接asm。其中的ln -s asm-$2 asm即ln -s asm-arm asm 。
rm -f asm-$2/arch
if [ -z "$6" -o "$6" = "NULL" ] ; then
ln -s ${LNPREFIX}arch-$3 asm-$2/arch
/* -z STRING: 判断字符串STRING是否为0,若STRING为空字符串,则为true
* -o: or或的意思
*/
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
/* arch->arch/arm/include/asm/arch-s3c24x0 */
fi
第二个:在inlcude/asm-arm下创建一个arch文件,指向include/asm-arm/arch-s5pc110
建立符号链接include/asm-arm/arch ,若$6(SOC)为空,则使其链接到include/asm-arm/arch-arm920t目录,否则就使其链接到include/asm-arm/arch-s3c24x0目录。(事实上include/asm-arm/arch-arm920t并不存在,因此$6是不能为空的,否则会编译失败)
# create link for s5pc11x SoC
if [ "$3" = "s5pc11x" ] ; then
rm -f regs.h
ln -s $6.h regs.h
rm -f asm-$2/arch
ln -s arch-$3 asm-$2/arch
fi
第三个:在include目录下创建regs.h文件,指向include/s5pc110.h,删除第二个
第四个:在inlcude/asm-arm下创建一个arch文件,指向include/asm-arm/arch-s5pc11x
if [ "$2" = "arm" ] ; then
rm -f asm-$2/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc
/* proc->arch/arm/include/asm/proc-armv */
fi
第五个:在include/asm-arm下创建一个proc文件,指向include/asm-arm/proc-armv
若目标板是arm架构,则上面的代码将建立符号连接include/asm-arm/proc,使其链接到目录proc-armv目录。
#
# Create include file for Make
#
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk
/* Create include file for Make
* >: 定向输出到文件,若文件不存在创建空文件
* >>: 追加内容到指定的文件末尾
*/
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
上面代码将会把如下内容写入文件inlcude/config.mk文件
$1: x210_sd
$2: arm
$3: s5pc11x
$4: x210
$5: samsumg
$6: s5pc110
#
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
若APPEND为yes,则将新的配置内容追加到include/config.h文件后面。由于APPEND的值保持"no",因此config.h被创建了,并添加了如下的内容:
exit 0
下面总结命令make mini2440_config执行的结果(仅针对编译目标输出到源代码目录的情况):
(1)创建到目标板相关的文件的链接
ln -s asm-arm asm
ln -s arch-s3c24x0 asm-arm/arch
ln -s proc-armv asm-arm/proc
(2)创建include/config.mk文件,内容如下所示:
ARCH = arm
CPU = arm920t
BOARD = mini2440
VENDOR = samsung
SOC = s3c24x0
(3)创建与目标板相关的文件include/config.h,如下所示:
/* Automatically generated - do not edit */
#define CONFIG_BOARDDIR board/samsung/mini2440
#include <config_defaults.h>
#include <configs/mini2440.h>
#include <asm/config.h>
后,总结下mkconfig文件的作用:
1、确定ARCH、CPU、BOARD等变量的值,并存到./include/config.mk文件中
2、建立板相关的 ./include/config.h文件
3、建立指向其他文件的软链接
-----------------------------------------------------------------------------------------
# TRAB default configuration: 8 MB Flash, 32 MB RAM
xtract_trab = $(subst _bigram,,$(subst _bigflash,,$(subst _old,,$(subst _config,,$1))))
trab_config \
trab_bigram_config \
trab_bigflash_config \
trab_old_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/trab
@[ -z "$(findstring _bigram,$@)" ] || \
{ echo "#define CONFIG_FLASH_8MB" >>$(obj)include/config.h ; \
echo "#define CONFIG_RAM_32MB" >>$(obj)include/config.h ; \
$(XECHO) "... with 8 MB Flash, 32 MB RAM" ; \
}
@[ -z "$(findstring _bigflash,$@)" ] || \
{ echo "#define CONFIG_FLASH_16MB" >>$(obj)include/config.h ; \
echo "#define CONFIG_RAM_16MB" >>$(obj)include/config.h ; \
$(XECHO) "... with 16 MB Flash, 16 MB RAM" ; \
echo "TEXT_BASE = 0x0CF40000" >$(obj)board/trab/config.tmp ; \
}
@[ -z "$(findstring _old,$@)" ] || \
{ echo "#define CONFIG_FLASH_8MB" >>$(obj)include/config.h ; \
echo "#define CONFIG_RAM_16MB" >>$(obj)include/config.h ; \
$(XECHO) "... with 8 MB Flash, 16 MB RAM" ; \
echo "TEXT_BASE = 0x0CF40000" >$(obj)board/trab/config.tmp ; \
}
@$(MKCONFIG) -a $(call xtract_trab,$@) arm arm920t trab NULL s3c24x0
#########################################################################
#########################################################################
#########################################################################
#########################################################################
//clean删除
clean:
@rm -f $(obj)examples/82559_eeprom $(obj)examples/eepro100_eeprom \
$(obj)examples/hello_world $(obj)examples/interrupt \
$(obj)examples/mem_to_mem_idma2intr \
$(obj)examples/sched $(obj)examples/smc91111_eeprom \
$(obj)examples/test_burst $(obj)examples/timer
@rm -f $(obj)tools/bmp_logo $(obj)tools/easylogo/easylogo \
$(obj)tools/env/{fw_printenv,fw_setenv} \
$(obj)tools/envcrc \
$(obj)tools/gdb/{astest,gdbcont,gdbsend} \
$(obj)tools/gen_eth_addr $(obj)tools/img2srec \
$(obj)tools/mkimage $(obj)tools/mpc86x_clk \
$(obj)tools/ncb $(obj)tools/ubsha1
@rm -f $(obj)board/cray/L1/{bootscript.c,bootscript.image} \
$(obj)board/netstar/{eeprom,crcek,crcit,*.srec,*.bin} \
$(obj)board/trab/trab_fkt $(obj)board/voiceblue/eeprom \
$(obj)board/{integratorap,integratorcp}/u-boot.lds \
$(obj)board/{bf533-ezkit,bf533-stamp,bf537-stamp,bf561-ezkit}/u-boot.lds \
$(obj)cpu/blackfin/bootrom-asm-offsets.[chs]
@rm -f $(obj)include/bmp_logo.h
@rm -f $(obj)nand_spl/{u-boot-spl,u-boot-spl.map,System.map}
@rm -f $(obj)onenand_ipl/onenand-{ipl,ipl.bin,ipl-2k.bin,ipl-4k.bin,ipl.map}
@rm -f $(obj)api_examples/demo $(VERSION_FILE)
@find $(OBJTREE) -type f \
\( -name 'core' -o -name '*.bak' -o -name '*~' \
-o -name '*.o' -o -name '*.a' \) -print \
| xargs rm -f
//除了调用clean删除,还要再执行额外的删除命令
clobber: clean
@find $(OBJTREE) -type f \( -name .depend \
-o -name '*.srec' -o -name '*.bin' -o -name u-boot.img \) \
-print0 \
| xargs -0 rm -f
@rm -f $(OBJS) $(obj)*.bak $(obj)ctags $(obj)etags $(obj)TAGS \
$(obj)cscope.* $(obj)*.*~
@rm -f $(obj)u-boot $(obj)u-boot.map $(obj)u-boot.hex $(ALL)
@rm -f $(obj)tools/{crc32.c,environment.c,env/crc32.c,md5.c,sha1.c,inca-swap-bytes}
@rm -f $(obj)tools/{image.c,fdt.c,fdt_ro.c,fdt_rw.c,fdt_strerror.c,zlib.h}
@rm -f $(obj)tools/{fdt_wip.c,libfdt_internal.h}
@rm -f $(obj)cpu/mpc824x/bedbug_603e.c
@rm -f $(obj)include/asm/proc $(obj)include/asm/arch $(obj)include/asm
@rm -f $(obj)include/regs.h
@[ ! -d $(obj)nand_spl ] || find $(obj)nand_spl -lname "*" -print | xargs rm -f
@[ ! -d $(obj)onenand_ipl ] || find $(obj)onenand_ipl -lname "*" -print | xargs rm -f
@[ ! -d $(obj)api_examples ] || find $(obj)api_examples -lname "*" -print | xargs rm -f
ifeq ($(OBJTREE),$(SRCTREE))
mrproper \
distclean: clobber unconfig//倘若目标目录与源目录相同,distclean调用clobber unconfig来删除
else
mrproper \
distclean: clobber unconfig//倘若目标目录与源目录不相同,distclean调用clobber unconfig来删除
rm -rf $(obj)*//而且,还要删除OBJTREE目录下的所有内容
endif
backup://打包备份
F=`basename $(TOPDIR)` ; cd .. ; \ //跳到当前目录的外边,然后打包整个文件夹
gtar --force-local -zcvf `date "+$$F-%Y-%m-%d-%T.tar.gz"` $$F
//打包,名字中插入日期
#########################################################################
make all命令执行过程:
若没有执行过"make <board_name>_config"命令就直接执行"make all"命令则会出现如下的才错误信息,然后停止编译:
System not configured - see README
U-Boot是如何知道用户没有执行过"make <board_name>_config"命令的呢?阅读U-Boot源代码就可以发现了,Makefile中有如下代码:
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk)) # config.mk存在
all:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk
… …
else # config.mk不存在
… …
@echo "System not configured - see README" >&2
@ exit 1
… …
endif # config.mk
若include/config.mk 文件存在,则$(wildcard $(obj)include/config.mk) 命令执行的结果是"$(obj)include/config.mk"展开的字符串,否则结果为空。由于include/config.mk是"make <board_name>_config"命令执行过程生成的,若从没有执行过"make <board_name>_config"命令则include/config.mk必然不存在。因此Make就执行else分支的代码,在输出"System not configured - see README"的信息后就返回了。
Uboot11之主Makefile分析2相关推荐
- uboot源码——主Makefile分析
以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 一.配置编译初体验 1.uboot来源于官方(uboot官网下载),或者SoC官方(研发s5pv210芯片的公司推出的开发板,SMDKV21 ...
- uboot主Makefile分析
version表示主版本号 patchlevel表示补丁版本号 sublevel表示次版本号 extreversion表示附加版本信息 U_BOOT_VERSION=1.3.4表示最 ...
- Linux内核移植之一:内核源码结构与Makefile分析
内容来自 韦东山<嵌入式Linux应用开发完全手册> 一.内核介绍 1.版本及其特点 Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如下面几行它们构成了Linu ...
- U-Boot顶层Makefile分析
参考:U-Boot顶层Makefile介绍 作者:一只青木呀 发布时间: 2020-10-22 16:22:17 网址:https://blog.csdn.net/weixin_45309916/ar ...
- u-boot的Makefile分析
U-BOOT是一个LINUX下的工程,在编译之前必须已经安装对应体系结构的交叉编译环境,这里只针对ARM,编译器系列软件为arm-linux-*. U-BOOT的下载地址: http://source ...
- (二)u-boot2013.01.01 for TQ210:《Makefile分析》
当时写的时候看的是2012-10版本的,但是略对比了一遍和2013.01.01没什么改动,所以这不影响对2013.01.01版本的makefile的理解.本文比较侧重于语法句意的分析,框 ...
- imx6ull:uboot顶层Makefile分析
版本号 MAKEFLAGES变量 20 MAKEFLAGS += -rR --include-dir=$(CURDIR) Makefile有两个特殊的变量:"SHELL"和&quo ...
- uboot笔记之makefile分析
uboot笔记之makefile分析开始学习uboot,对于linux我还是个新手,在这只是对学习uboot做下笔记,文中错误之处请谅解.使用的uboot版本是2009.11. 要了解一个linux ...
- 水稻微生物组时间序列分析精讲1-模式图与主坐标轴分析
写在前面 上周五我们分享了3月底发表的的 <水稻微生物组时间序列分析>的文章,大家对其中图绘制过程比较感兴趣.一上午收到了超30条留言,累计收到41个小伙伴的留言求精讲. 我们也争取花时间 ...
最新文章
- 【GStreamer】在x264enc中设置profile级别
- Docker+Jenkins持续集成环境(3)集成PMD、FindBugs、Checkstyle静态代码检查工具并邮件发送检查结果...
- React系列---Redux高阶运用
- 自用零散博文-route_state.ts
- Android local.properties 文件读取
- 采用.NET CORE的全异步模式打造一款免费的内网穿透工具--NSmartProxy
- java内存模型概述_Java内存模型-快速概述和注意事项
- 学习笔记_jquery(js)遍历页面标签
- 线程中task取消_Rust Async: async-task源码分析
- call to member function bind_param() on boolean...........
- leetcode题解167-两数之和 II - 输入有序数组
- linux设备驱动归纳总结--转载小白的博客
- abstract class和interface有什么区别?_程序员必须掌握了解的21个Java核心技术,还在等什么?...
- POJ2676 Sudoku
- HP电脑的增霸卡功能操作详解
- mysql数据库是什么语言_mysql是一种程序设计语言吗?
- MATLAB2014b安装(Ubuntu 14.10)
- 数据可视化_科学统计图表5——ggplot绘制南丁格尔玫瑰图
- 哪个大学开python课_2017春Python语言程序设计(天津大学仁爱学院)
- 使用pyhton采用多线程方式ping IP