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=$(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 [email protected]

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,[email protected]表示当前目标,即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文件。

时间: 2024-10-10 07:02:51

Make ARCH=arm CROSS_COMPILE=arm-linux-gcc menuconfig 分析的相关文章

如何将lua移植到arm平台的linux内核

将脚本移植到内核是一件很酷的事情,lua已经被移植到NetBSD的内核中,也有一个叫lunatik的项目把lua移植到了linux内核,只可惜只支持x86,不支持arm,在网上搜索了下,没有找到现成的,于是自己研究了下,现将它分享出来. 移植到arm平台,主要是要重新实现setjmp和longjmp两个函数,网上相关的资料很少,最后终于找到一个klibc的项目,里面有setmp和longjmp的arm平台的实现,于是直接拿来用了,不用说,当看到脚本在内核中执行并打印出"hello,world&q

【嵌入式Linux+ARM】ARM体系结构与编程(ARM概述)

ARM体系结构与编程 本文章记录一些看<ARM体系结构与编程>这一本书的记录: 个人觉得,学习ARM体系时,不需要死记硬背,只要把某些关键的大致记住,比如ARM寄存器(通用寄存器.PC.LR.SP.CPSR.SPSR).ARM中断处理体系.常用的ARM汇编指令等. 本文基本都是从书上截图,都是一些关键的知识,需要我们时常去复习的. 一.ARM概述 ARM处理器的7种工作模式: ARM处理器寄存器: ARM中PC(R15)寄存器: ARM中CPSR寄存器: ARM异常处理模式--响应过程和返回过

ARM上电启动及Uboot代码分析

注意:由于文档是去年写的,内有多个图片,上传图片很麻烦(需要截图另存插入等等),我把文章的PDF版本上传到了CSDN下载资源中.为了给自己赚点积分,所以标价2分,没有积分的同学可以直接留言跟我要,记得留下邮箱. 以下是文章内容,由于我懒得编辑图片了,所以文章看来会很不爽,强烈推荐点击以上红色链接下载pdf版. 文件编号:DCC01 版本号:1.0 ARM上电启动及Uboot代码分析 部    门:                          作    者:                 

【ARM】arm系列知识框架

[ARM编程模型] 硬件: 电路原理图 软件: 体系结构, 指令集, 寄存器组 [ARM编程技术] 汇编/C语言 编译, 链接, 烧写和调试 windows: MDK linux  : gcc [ARM接口编程] 电路原理图 datasheet ------> 裸机程序(不带操作系统,直接操作硬件) 中断技术 初始化程序 [ ARM基础知识] 1. 冯·诺依曼结构特点:      采用二进制表示数据和程序      事先存储程序      利用控制流来驱动程序      五大部件  2. CPU

arm处理器的u-boot.lds文件分析

1)u-boot的实现分为stage1与stage2两个阶段,其中依赖与CPU体系结构的代码通常都是放在stage1里,并且通常用汇编语言实现.stage2通常用C语言实现,可以实现更加复杂的功能,并且有更好的移植性与可读性. 2)U-Boot 的 Stage1 通常是在 start.S 文件中实现,并且都是用汇编语言编写.一个可执行性 image 文件必须有一个入口点, 并且只能有一个全局入口点, 通常这个入口点的地址放在 ROM(Flash)0x0 位置,因此必须使编译器知道这个入口地址,该

【嵌入式开发】ARM 芯片简介 (ARM芯片类型 | ARM处理器工作模式 | ARM 寄存器 | ARM 寻址)

作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42375701 相关资源下载 :  -- 三星 ARM Architecture Reference Manual 文档 : http://download.csdn.net/detail/han1202012/8324641 一. ARM 芯片类型 1. ARM 分类 (1) ARM 分类类型(芯片 | 核 | 指令架构) ARM 分类 : -- ARM 芯片类型

嵌入式Linux GCC常用命令

本文和大家分享的主要是嵌入式Linux GCC常用命令相关内容,一起来看看吧,希望对大家学习嵌入式Linux有所帮助. 1.简介 GCC 的意思也只是 GNU C Compiler 而已.经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言:它现在还支持 Ada 语言.C++ 语言.Java 语言.Objective C 语言.Pascal 语言.COBOL语言,以及支持函数式编程和逻辑编程的 Mercury 语言,等等.而 GCC 也不再单只是 GNU C 语言编译器的意思了,而是变成了 

Azure ARM (6) Azure ARM (5) ARM Template简单介绍

<Windows Azure Platform 系列文章目录>      Azure ARM (1) 概览      Azure ARM (2) 概览      Azure ARM (3) ARM支持的服务类型      Azure ARM (4) 开始创建ARM Resource Group并创建存储账户 在上一节中,笔者介绍了如何从现有的Azure Resource Group导出Template. 接下来,我们将总体介绍Azure Template. 1.首先,我们打开文本编辑器,创建一

Linux gcc getcwd()的实现 zhuan

通过getcwd()可以获取当前工作目录. 1 #include <unistd.h> 2 3 char *getcwd(char *cwdbuf, size_t size); Linux gcc getcwd()的实现 zhuan