目录
- VERSION = 2016
- PATCHLEVEL = 03
- SUBLEVEL =
- EXTRAVERSION =
- NAME =
MAKEFLAGS += -rR --include-dir=$(CURDIR)
当编译uboot时,如果加上V=1参数,就会详细打印编译信息,如果不加入只打印简单的编译信息
- ifeq ("$(origin V)", "command line")
- KBUILD_VERBOSE = $(V)
- endif
- ifndef KBUILD_VERBOSE
- KBUILD_VERBOSE = 0
- endif
-
- ifeq ($(KBUILD_VERBOSE),1)
- quiet =
- Q =
- else
- quiet=quiet_
- Q = @
- endif
当编译的时候加上-s参数,就会什么都不输出,简单就是quiet=silent_,最后会将三个变量导出去给子makefile
- ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
- ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
- quiet=silent_
- endif
- else # make-3.8x
- ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
- quiet=silent_
- endif
- endif
-
- export quiet Q KBUILD_VERBOSE
make 的时候使用“O”来指定 输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中
也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在 同一个目录内,一般我们不指定 O 参数
- ifeq ($(KBUILD_SRC),)
- ifeq ("$(origin O)", "command line")
- KBUILD_OUTPUT := $(O)
- endif
- PHONY := _all
- _all:
- $(CURDIR)/Makefile Makefile: ;
- ifneq ($(KBUILD_OUTPUT),)
- saved-output := $(KBUILD_OUTPUT)
- KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \ && /bin/pwd)
- endif
- endif
使用命令
- ifeq ("$(origin C)", "command line")
- KBUILD_CHECKSRC = $(C)
- endif
- ifndef KBUILD_CHECKSRC
- KBUILD_CHECKSRC = 0
- endif
允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的
- ifdef SUBDIRS
- KBUILD_EXTMOD ?= $(SUBDIRS)
- endif
-
- ifeq ("$(origin M)", "command line")
- KBUILD_EXTMOD := $(M)
- endif
- PHONY += all
-
- ifeq ($(KBUILD_EXTMOD),)
- _all: all
- else
- _all: modules
- endif
- ifeq ($(KBUILD_SRC),)
- srctree := .
- else
- ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
- srctree := ..
- else
- srctree := $(KBUILD_SRC)
- endif
- endif
- objtree := .
- src := $(srctree)
- obj := $(objtree)
- VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
-
- export srctree objtree VPATH
- HOSTARCH := $(shell uname -m | \
- sed -e s/i.86/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/')
-
- export HOSTARCH HOSTOS
编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 ,“ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE
或者直接在主makefile里加入,就不需要每次编译设置了
- ifeq ($(HOSTARCH),$(ARCH))
- CROSS_COMPILE ?=
- endif
- ARCH ?= arm
- CROSS_COMPILE ?= arm-linux-gnueabihf-
- KCONFIG_CONFIG ?= .config
- export KCONFIG_CONFIG
- scripts/Kbuild.include: ;
- include scripts/Kbuild.include
之前设置了交叉编译的前缀,交叉编译器其他的工具还没设置
- AS = $(CROSS_COMPILE)as
- ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
- LD = $(CROSS_COMPILE)ld.bfd
- else
- LD = $(CROSS_COMPILE)ld
- endif
- 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
因为导出了很多变量,我只看几个ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR,这 7 个变量是在其他文件里面定义的
可以输出这几个变量查看,加入下面代码,运行make mytest就可以查看
- mytest:
- @echo ‘ARCH=' $(ARCH)
- @echo ‘CPU =' $(CPU )
- @echo ‘BOARD =' $(BOARD )
- @echo ‘VENDOR =' $(VENDOR )
- @echo ‘SOC =' $(SOC )
- @echo ‘CPUDIR =' $(CPUDIR )
- @echo ‘BOARDDIR=' $(BOARDDIR)
在uboot学习:(一)中的步骤1.1中,会进行清理过程,下面是分析,在主makefile搜索distclean
- PHCNY += distclean
- distclean: mrproper
- @find $(srctree) $(RCS_FIND_IGNORE) \
- \( -name '*.orig' -o -name '*.rej' -o -name '*~'\
- ...
- -type f -print | xargs rm -f
- @rm -f boards.cfg
- # 定义了变量 version_h,这变量保存版本号文件,此文件是自动生成的
- version_h := include/generated/version_autogenerated.h
-
- # 定义了变量 timestamp_h,此变量保存时间戳文件,此文件也是自动生成的
- timestamp_h := include/generated/timestamp_autogenerated.h
-
- # 定义了变量 no-dot-config-targets
- no-dot-config-targets := clean clobber mrproper distclean \
- help %docs check% coccicheck \
- ubootversion backup
-
- # 定义了变量 config-targets,初始值为 0
- config-targets := 0
-
- # 定义了变量 mixed-targets,初始值为 0
- mixed-targets := 0
-
- # 定义了变量 dot-config,初始值为 1
- dot-config := 1
-
- # 判断 MAKECMDGOALS 中是否包含 no-dot-config-targets 中的任何目标
- # make xxx_defconfig 则MAKECMDGOALS=xxx_defconfig 则dot-config依旧为1
- ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
- # 判断 MAKECMDGOALS 中是否只包含 no-dot-config-targets 中的目标
- ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
- # 如果上述条件成立,则将dot-config变量设置为0。这意味在这种情况下,不需要使用配置文件
- dot-config := 0
- endif
- endif
-
- # 行判断KBUILD_EXTMOD是否为空
- ifeq ($(KBUILD_EXTMOD),)
- # 检查 MAKECMDGOALS 是否包含以 config 开头的目标
- ifneq ($(filter config %config,$(MAKECMDGOALS)),)
- # 说明 MAKECMDGOALS 包含以 config 开头的目标,则将 config-targets 变量设置为 1
- config-targets := 1
- # 检查 MAKECMDGOALS 中的目标是否超过一个
- ifneq ($(words $(MAKECMDGOALS)),1)
- # 说明 MAKECMDGOALS 中包含不止一个目标,则将 mixed-targets 变量设置为 1
- mixed-targets := 1
- endif
- endif
- endif
-
- # 判断是否有混合目标,如果 mixed-targets 变量的值为1,表示存在混合目标
- ifeq ($(mixed-targets),1)
- # 将 MAKECMDGOALS 中的目标以及 __build_one_by_one 添加到伪目标(PHONY)列表中
- PHONY += $(MAKECMDGOALS) __build_one_by_one
- # 过滤出MAKECMDGOALS中除了__build_one_by_one之外的目标,并将其依赖设为 __build_one_by_one。使用@:使其成为一个空目标
- $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one @:
- # 定义 __build_one_by_one 目标,执行以下命令
- __build_one_by_one:
- # 循环遍历 MAKECMDGOALS 中的每一个目标,并分别调用 make 进行构建
- $(Q)set -e; \
- for i in $(MAKECMDGOALS); do \
- $(MAKE) -f $(srctree)/Makefile $$i; \
- done
- else
- # 如果 mixed-targets 不为1,则判断是否有配置目标
- ifeq ($(config-targets),1)
- # 设置默认的配置文件为 sandbox_defconfig
- KBUILD_DEFCONFIG := sandbox_defconfig
- # 导出 KBUILD_DEFCONFIG 和 KBUILD_KCONFIG 变量
- export KBUILD_DEFCONFIG KBUILD_KCONFIG
- # 定义 config 目标,其依赖为 scripts_basic、outputmakefile 和 FORCE
- config: scripts_basic outputmakefile FORCE
- $(Q)$(MAKE) $(build)=scripts/kconfig $@
- %config: scripts_basic outputmakefile FORCE
- $(Q)$(MAKE) $(build)=scripts/kconfig $@
- else
- # 如果 config-targets 不为1,则判断 dot-config 是否为1
- ifeq ($(dot-config),1)
- # 如果 dot-config 为1,则包含 include/config/auto.conf 文件
- -include include/config/auto.conf
- ......
在uboot学习:(一)中的步骤1.2中,会进行默认配置make xxx_defconfig,下面是配置过程分析
我们主要看下面后面得两段代码
- %config: scripts_basic outputmakefile FORCE
- $(Q)$(MAKE) $(build)=scripts/kconfig $@
在顶层makefile中有定义,可以搜索看看
- PHONY += FORCE
- FORCE:
在顶层makefile中有定义,可以搜索看看
- PHONY += outputmakefile
- outputmakefile:
- # 判断 KBUILD_SRC 是否为空。如果 KBUILD_SRC 变量不为空,表示内核源码树与构建目录是分开的
- ifneq ($(KBUILD_SRC),)
- # 创建一个符号链接 source,指向 $(srctree),其中 $(srctree) 是内核源码树的路径
- $(Q)ln -fsn $(srctree) source
- # 使用 $(CONFIG_SHELL) 运行脚本 $(srctree)/scripts/mkmakefile
- # 该脚本生成一个 Makefile 文件,用于在源码树和对象树之间进行构建
- $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
- # 传递给脚本参数srctree源码树,objtree对象树,内核版本号和补丁级别VERSION和PATCHLEVEL
- $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
- endif
outputmakefile
的伪目标,该目标主要用于在源码树与构建目录分开时,创建符号链接并生成必要的 Makefile 文件。通过这种方式,可以在构建目录中正确引用源码树,并执行内核构建过程KBUILD_SRC
是什么,是否为空,可以输出看看是否为空,一般是为空
KBUILD_SRC
:表示源码树的路径,如果为空,则表示构建在源码树内进行;如果不为空,则表示构建在源码树外进行在顶层makefile中有定义,可以搜索看看
- PHONY += scripts_basic
- scripts_basic:
- # 使用 $(MAKE) 命令进入 scripts/basic 目录进行构建
- $(Q)$(MAKE) $(build)=scripts/basic
- # 删除文件 .tmp_quiet_recordmcount
- $(Q)rm -f .tmp_quiet_recordmcount
- # 定义一个模式规则,表示scripts/basic/目录下的所有目标都依赖于scripts_basic目标
- scripts/basic/%: scripts_basic ;
scripts/basic
目录中的基本脚本总是首先被构建scripts_basic
伪目标,并设置依赖关系,可以确保这些基本脚本在构建过程中总是处于最新状态,并且在构建 scripts/basic
目录下的其他目标之前已经被构建里面最重要得就是$(Q)$(MAKE) $(build)=scripts/basic,下面是分析
build := -f $(srctree)/scripts/Makefile.build obj
最后回到 分析 make xxx_defconfig 里的一句 $(Q)$(MAKE) $(build)=scripts/kconfig $@
现在我们得到了两条命令
src := $(patsubst $(prefix)/%,%,$(obj))
- kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
- kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuilddir)/Kbuild,$(kbuild-dir)/Makefile)
- include $(kbuild-file)
将 kbuild-dir 展开后为$(if $(filter /%, scripts/basic), scripts/basic, ./scripts/basic)
因为没有以“/”为开头的单词,所以$(filter /%, scripts/basic)的结果为空
最后kbuilddir=./scripts/basic
将 kbuild-file 展开后为$(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild, ./scripts/basic/Makefile)
因为 scrpts/basic 目录中没有 Kbuild 这个文件
最后kbuild-file= ./scripts/basic/Makefile
最后一行就为 include ./scripts/basic/Makefile
也就是读取 scripts/basic 下面的 Makefile 文件
因为第一条命令参数只有一个参数赋值, 没有默认目标,就表示需要默认目标,就要找到__build,而__build的规则为以下代码
- __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target)$(extra-y)) \
- $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
- $(subdir-ym) $(always)
- @:
其中要用到顶层makefile的变量,KBUILD_BUILTIN=1和KBUILD_MODULES=0,可以打印出来查看
因此展开后目标__build 为
- __build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
- @:
可以看出目标__build 有 5 个依赖:builtin-target、lib-target、extra-y、subdir-ym 和 always,可以打印这几个依赖来查看
最后只有always=scripts/basic/fixdep,其他都是空
因此__build 最终为__build: scripts/basic/fixdep
所以要编译出scripts/basic/fixdep这个文件(软件),对应的.c就是scripts/basic/fixdep.c,在scripts/basic/Makefile 里可以编译出来,前面已经读取了 scripts/basic/Makefile 文件,就已经编译出来了
与第一条命令不同的是 obj = scripts/kconfig 和 xxx_defconfig
和第一条命令一样先从src开始分析,因为obj换了,所以src也会更换,对应的其他变量也变了
- %_defconfig: $(obj)/conf
- $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@$(Kconfig)
- %_config: %_defconfig
- @:
上面是分析了uboot学习:(一)中的1.1和1.2这两条命令
现在开始是uboot学习:(一)中的1.3这条命令,目的是为了生成u-boot.bin文件
- # 默认目标_all
- PHONY := _all
- _all:
-
- PHONY += all
- # 如果没有参数就默认依赖于all,KBUILD_EXTMOD表示要不要编译模块
- ifeq ($(KBUILD_EXTMOD),)
- _all: all
- else
- _all: modules
- endif
-
- all: $(ALL-y)
-
- ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
- ...
- ifeq ($(CONFIG_OF_SEPARATE),y)
- u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
- $(call if_changed,cat)
- u-boot.bin: u-boot-dtb.bin FORCE
- $(call if_changed,copy)
- else
- u-boot.bin: u-boot-nodtb.bin FORCE
- $(call if_changed,copy)
- endif
- u-boot-nodtb.bin: u-boot FORCE
- $(call if_changed,objcopy)
- $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
- $(BOARD_SIZE_CHECK)
- u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
- $(call if_changed,u-boot__)
- ifeq ($(CONFIG_KALLSYMS),y)
- $(call cmd,smap)
- $(call cmd,u-boot__) common/system_map.o
- endif
- u-boot.lds: $(LDSCRIPT) prepare FORCE
- $(call if_changed_dep,cpp_lds)
- cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o
- drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o