首页 > 代码库 > Make ARCH=arm CROSS_COMPILE=arm-linux-gcc menuconfig 分析

Make ARCH=arm CROSS_COMPILE=arm-linux-gcc menuconfig 分析

在编译LINUX内核时,首先要修改内核源码顶层目录下的makefile文件,将其中ARCH ?= $(SUBARCH)修改为ARCH ?= arm,将CROSS_COMPILE    ?= 修改为CROSS_COMPILE    ?= arm-linux-gcc,或者不修改,而是将ARCH和CROSS_COMPILE的值通过命令行传入。然后在linux内核源码目录下,执行make menuconfig,那之后发生了什么?

make命令在未指定文件的情况下,默认寻找名为Makefile或GNUMakefile的文件(文件名不区分大小写,无后缀名)。make menuconfig命令没有指定文件,因此默认执行的是 make –f Makefile menuconfig,即执行$(srctree)/Makefile文件中目标menuconfig的相关规则。我使用的源码顶层目录名为linux2.6.30.4,因此$(srctree)/Makefile即linux2.6.30.4/Makefile。

下边只列出了执行make menuconfig后,linux2.6.30.4/Makefile文件中与该命令执行相关的目标规则(此处将makefile文件中include的文件也包含进去并展开了,在include关键字下边,都用{ }括起来,用来表示include进来的文件展开后的内容):

linux2.6.30.4/Makefile


... ...

# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
ifdef SUBDIRS
    KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifdef M
    ifeq ("$(origin M)", "command line")
        KBUILD_EXTMOD := $(M)
    endif
endif

ifeq ($(KBUILD_SRC),)

ifdef O
    ifeq ("$(origin O)", "command line")
        KBUILD_OUTPUT := $(O)
    endif
endif

… …

ifneq ($(KBUILD_OUTPUT),)

… …

sub-make: FORCE
    $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \
    KBUILD_SRC=http://www.mamicode.com/$(CURDIR) /
    KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \
    $(filter-out _all sub-make,$(MAKECMDGOALS))

… …

skip-makefile := 1

endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)

NOTE:  因为make menuconfig命令没有定义M、O、也没有定义skip-makefile,且没有SUBDIRS、KBUILD_EXTMOD、KBUILD_SRC这三个环境变量(除非自己去设置,否则linux系统不会有这三个个环境变量),因此KBUILD_OUTPUT为空,skip-makefile也为空,KBUILD_EXTMOD为空,KBUILD_SRC为空。

ifeq ($(skip-makefile),)

… …

srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))

NOTE:  KBUILD_SRC为空,所以if函数的返回值是$(CURDIR),CURDIR是make的内嵌变量,其值是make命令执行所在目录,对于我这个例子,是进入到D:\linux2.6.30.4后make menuconfig的,因此CURDIR就是D:\linux2.6.30.4

… …

SRCARCH     := $(ARCH)

… …

include $(srctree)/scripts/Kbuild.include

{

… …

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

NOTE:KBUILD_SRC为空,所以$(srctree)/的值不被返回,即上句实际为build := -f scripts/Makefile.build obj,

… …

}

… …

PHONY += scripts_basic
scripts_basic:
    $(Q)$(MAKE) $(build)=scripts/basic

… …

PHONY += outputmakefile
outputmakefile:
ifneq ($(KBUILD_SRC),)
    $(Q)ln -fsn $(srctree) source
    $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
        $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

… …

config-targets := 0
mixed-targets  := 0

... ...

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
                        mixed-targets := 1
                endif
        endif
endif

NOTE: MAKECMDGOALS变量指的是从make命令行参数中传递过来的目标字符串,make menuconfig命令对应的MAKECMDGOALS就是menuconfig。KBUILD_EXTMOD为空,MAKECMDGOALS字串与%config模式符合,故config-targets := 1,而MAKECMDGOALS中除了%config模式外,没有其他模式的字串了,因此$(filter-out config %config,$(MAKECMDGOALS))为空,故mixed-targets仍然为0


ifeq ($(mixed-targets),1)

… …

else

ifeq ($(config-targets),1)

… …

%config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

else

… …

endif #ifeq ($(config-targets),1)
endif #ifeq ($(mixed-targets),1)

… …

endif    # skip-makefile

… …

PHONY += FORCE
FORCE:

.PHONY: $(PHONY)

NOTE: make menuconfig后,经过一些逻辑判断(就是前面ifeq ($(config-targets),1)之类的),最终来执行%config目标的规则,%config目标的规则中有两条命令,第一条创建两个目录,第二条执行make命令,Q的值被定义为@或者为空,而@……的意思是不将此行规则在执行时显示在屏幕上,因此$(Q)无关紧要。MAKE是内嵌变量,其值为make,$@表示当前目标,即menuconfig,故第二条规则实际就是make -f scripts/Makefile.build obj=scripts/kconfig menuconfig,进入到scripts/makefile.build文件中,make的目标是menuconfg。menuconfg目标有三个依赖scripts_basic outputmakefile FORCE 。scripts_basic目标的规则的命令为$(Q)$(MAKE) $(build)=scripts/basic,展开即make -f scripts/Makefile.build obj=scripts/basic。查看Makefile.build文件,可以看到,该文件的默认目标是__build,__build目标有两条规则,第一条是空规则,第二条规则有命令。按照makefile规则,当一个目标有多条规则时,只能有一条规则有生成目标的命令,多条规则的中的命令和依赖在makefile文件被读取时合并,因此说,这里__build目标的实质性规则是

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
     $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
     $(subdir-ym) $(always)
    @:

KBUILD_BUILTIN、KBUILD_MODULES在顶层makefile文件中定义,并通过export关键字定义,使在makefile递归进行时,这两个变量被传递进子makefile。KBUILD_BUILTIN和KBUILD_MODULES在顶层makefile文件中定义赋为1后,就没有被改变过。所以此处__build目标的依赖就是$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)。命令“:”在bash中表示什么都不干,只是单纯的返回true. 经过分析,发现builtin-target、lib-target、extra-y、subdir-ym都为空串,只有always有值,always在scripts/kconfig/Makefile中定义为dochecklxdialog,而dochecklxdialog目标所在规则的注释写着# Check that we have the required ncurses stuff installed for lxdialog (menuconfig),也就是说,__build目标的依赖dochecklxdialog是用来检查生成配置对话框所需的ncurses库是不是已经安装在本机了,如果没有安装,make过程会报错退出。因此在make menuconfig前,我们要保证该库已经被安装在本地。

outputmakefile 目标所在规则实际上什么也不做。FORCE所在规则为空,也是什么都不做。FORCE被定义为一个伪目标,所以它作为依赖时总是被认为是最新的(比目标新),故有FORCE作为依赖的目标每次make时必然会重新生成,在这里FORCE伪目标的规则命令为空,故FORCE在Kbuild体系中,就是相当于是一个关键字,如果我们想要某个目标每次make的时候都一定会被重新生成,就把FORCE写为该目标的依赖。

linux2.6.30.4/scripts/Makefile.build


src := $(obj)

PHONY := __build
__build:

… …

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

{

… …

ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := arch/$(SRCARCH)/Kconfig
endif

… …

menuconfig: $(obj)/mconf
    $< $(Kconfig)

… …

NOTE: 由make -f scripts/Makefile.build obj=scripts/kconfig menuconfig可知,src值为scripts/kconfig,与/%的字串模式相符,因此$(filter /%,$(src))就是scripts/kconfig,故kbuild-dir就被赋值为$(src),即kbuild-dir为scripts/kconfig。由于scripts/kconfig目录下并没有Kbuild文件,因此函数$(wildcard $(kbuild-dir)/Kbuild)查找失败,返回为空,从而kbuild-file值被赋为$(kbuild-dir)/Makefile,也即scripts/kconfig/Makefile。接着include $(kbuild-file),目标menuconfig就被找到了。menuconfig目标的规则的命令是$< $(Kconfig),展开为$(obj)/mconf $(Kconfig), obj的值为scripts/kconfig,因为没有定义KBUILD_KCONFIG,而且SRCARCH之前已被赋值为$(ARCH),即SRCARCH为arm,因此Kconfig的值为arch/arm/Kconfig。故menuconfig目标的规则的命令为scripts/kconfig/mconf arch/arm/Kconfig。mconf在这里实际上是scripts/kconfig目录下的一个可执行文件,此条命令里arch/arm/Kconfig字符串作为命令行参数传入该可执行文件运行,该可执行文件实际上就是依据arch/arm/Kconfig文件提供的菜单配置,生成配置界面。NOTE: 这里为什么说scripts/kconfig/mconf就是一个可执行文件呢?继续往下看scripts/kconfig/Makefile中的内容:

lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o
lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o

… …

mconf-objs    := mconf.o zconf.tab.o $(lxdialog)

… …

ifeq ($(MAKECMDGOALS),menuconfig)
    hostprogs-y += mconf
endif

… …

# Check that we have the required ncurses stuff installed for lxdialog (menuconfig)
PHONY += $(obj)/dochecklxdialog
$(addprefix $(obj)/,$(lxdialog)): $(obj)/dochecklxdialog
$(obj)/dochecklxdialog:
    $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES)

always := dochecklxdialog

… …

}
NOTE: 如果在编译内核的过程中,需要现编译出一些可执行文件供内核编译阶段使用,就需要借助Kbuild框架的本机程序支持的特性。Kbuild 框架中,专门使用hostprogs-y变量来指示在内核编译阶段需要使用的一些可执行文件,通过hostprogs-y += mconf,就向make程序指明mconf是一个编译阶段需要使用的可执行文件。另外,Kbuild框架使用-objs后缀来指明相应的可执行文件需要通过多个目标文件来链接生成,mconf-objs    := mconf.o zconf.tab.o $(lxdialog)就是向make指明,mconf文件是由mconf.o zconf.tab.o lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o链接生成的。再有,未明确写明生成规则时,KBuild框架默认.o文件是由同名.c或.S文件编译生成的。我们在scripts\kconfig以及scripts\kconfig\lxdialog目录下可以找到前边8个.o文件的同名.c文件。

Make ARCH=arm CROSS_COMPILE=arm-linux-gcc menuconfig 分析