前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >make menuconfig makefile kconfig详解

make menuconfig makefile kconfig详解

作者头像
全栈程序员站长
发布2022-09-01 11:11:40
1.8K0
发布2022-09-01 11:11:40
举报

大家好,又见面了,我是你们的朋友全栈君。

前面有一片文章分析make menuconfig执行过程:http://blog.csdn.net/xinyuan510214/article/details/50964808

今天,将一下make menuconfig makefile kconfig等几个容易混淆的关键操作。

=== 1、内核Makefile概述(linux 2.6) Linux内核的Makefile分为5个部分: Makefile 最顶层Makefile .config 内核当前配置文件,编译时成为顶层Makefile的一部分 arch/$(ARCH)/Makefile 和体系结构相关的具体架构的Makefile scripts/Makefile.* 一些Makefile的通用的规则,面向所有的Kbuild Makefiles。 kbuildMakefiles 内核各级目录源代码中大约有500个这样的文件,编译时根据上层Makefile传下来的宏定义和其他编译规则,将源代码编译成模块或编入内核。

  • 相互作用关系

顶层的Makefile文档读取内核配置文件.config文档的内容,确定内核配置情况,通过递归向下访问子目录的形式完成build内核和模块的工作。.config文件的内容是在makemenuconfig的时候,通过Kconfig文档配置的结果;arch/$(ARCH)/Makefile则提供补充体系结构相关的信息;每一个子目录都有一个KbuildMakefile文件,用来执行从其上层目录传递下来的命令。KbuildMakefile从.config文件中提取信息,生成Kbuild完成内核编译所需的文件列表。scripts/Makefile.*文档包含了任何用来根据kbuildMakefile 构建内核所需的定义和规则。

=== 2 、内核中Kconfig文档的作用

2.6内核的源码树目录下一般都会有两个文件:Kconfig和Makefile。分布在各目录下的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文件相关的内核配置菜单。在内核配置makemenuconfig(或xconfig等)时,从Kconfig中读出配置菜单,用户配置完后保存到.config(在顶层目录下生成)中。在内核编译时,主Makefile调用这个.config,就知道了用户对内核的配置情况。

上面的内容说明:Kconfig就是对应着内核的配置菜单。假如要想添加新的驱动到内核的源码中,可以通过修改Kconfig来增加对我们驱动的配置菜单,这样就有途径选择我们的驱动,假如想使这个驱动被编译,还要修改该驱动所在目录下的Makefile。(见第3小节举例)

因此,一般添加新的驱动时需要修改的文件有两种(注意不只是两个)

*Kconfig *Makefile

要想知道怎么修改这两种文件,就要知道两种文档的语法结构。

  • Kconfig 语法结构

每个菜单项都有一个关键字标识,最常见的就是config。

语法: config symbol

options

symbol就是新的菜单项,options是在这个新的菜单项下的属性和选项

======================================================

其中options部分有:

1、类型定义: 每个config菜单项都要有类型定义,bool:布尔类型,tristate三态:内建、模块、移除,string:字符串,hex:十六进制, integer:整型

例如 configHELLO_MODULE bool“hello testmodule”

bool类型的只能选中或不选中,tristate类型的菜单项多了编译成内核模块的选项,假如选择编译成内核模块,则会在.config中生成一个CONFIG_HELLO_MODULE=m的配置,假如选择内建,就是直接编译成内核影响,就会在.config中生成一个CONFIG_HELLO_MODULE=y的配置.

2、依赖型定义dependson或requires 指此菜单的出现是否依赖于另一个定义

configHELLO_MODULE bool“hello testmodule” dependsonARCH_PXA 这个例子表明HELLO_MODULE这个菜单项只对XScale处理器有效,即只有在选择了ARCH_PXA,该菜单才可见(可配置)。

3、帮助性定义 只是增加帮助用关键字help—help—

  • === 3 、应用举例

☃最后举个例子: 假设想把自己写的一个flash的驱动程序加载到工程中,而且能够通过menuconfig配置内核时选择该驱动该怎么办呢?可以分三步:

第一:将您写的flashtest.c 文档添加到/driver/mtd/maps/目录下。

第二:修改/driver/mtd/maps目录下的kconfig文档: configMTD_flashtest tristate“ap71flash”

这样当make menuconfig时 ,将会出现 ap71 flash选项。

第三:修改该目录下makefile文档。 添加如下内容:obj-$(CONFIG_MTD_flashtest) +=flashtest.o

这样,当您运行make menucofnig时,您将发现ap71flash选项,假如您选择了此项。该选择就会保存在.config文档中。当您编译内核时,将会读取.config文档,当发现ap71flash 选项为yes 时,系统在调用/driver/mtd/maps/下的makefile 时,将会把 flashtest.o加入到内核中。即可达到您的目的。

上面了解了其关系及基本的操作,下面再详细讲解一下具体的执行过程。

在编译内核前,一般是根据已有的配置文件(一般在内核根目录下的arch/arm/configs/文件夹下,把该目录下的xxx_defconfig文件拷贝到内核根目录下,并重命名为.config)来进行编译; 或者需要先配置裁剪内核。

假设我们要基于一块ARM芯片的开发板配置裁剪内核时,在内核的根目录下运行:make ARCH=arm menuconfig命令后,会弹出如下配置界面:

clip_image002
clip_image002

当我们在内核的根目录下运行make ARM=arm menuconfig这条命令时,内核根目录下的Makefile是怎样被执行的呢?

回答这个问题之前,我们要先了解make这个工具的执行过程。GNU make找寻默认的Makefile规则是在当前目录下按顺序依次找三个文件 —“GNUmakefile”、“makefile”和“Makefile”,一旦找到就开始读取这个文件并执行。make menuconfig命令没有指定makefile文件,因此默认执行的是 make –f Makefile menuconfig,即执行$(srctree)/Makefile文件中目标menuconfig的相关规则。一般来说,make的最终目标是makefile中的第一个目标,而其它目标一般是由这个目标连带出来的。这是make的默认行为。

如果你的makefile中的第一个目标是由许多个目标组成,你可以指示make,让其完成你所指定的目标。要达到这一目的很简单,需在make命令后直接跟目标的名字就可以完成(如make clean)。任何在makefile中的目标都可以被指定成终极目标,但是 除了以“-”打头,或是包含了“=”的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。甚至没有被我们明确写出来的目标也可以成为make的终极目标,也就是说,只要make可以找到其隐含规则推导规则,那么这个隐含目标同样可以被指定成终极目标。

当在Linux内核(内核版本为3.18.42)顶层目录执行”make ARCH=arm menuconfig”时,命令行对内核根目录下Makefile文件的ARCH这个变量赋值为arm ,并且指定了make的目标是menuconfig。“menuconfig”这个目标在根目录下的Makefile中找到的匹配的目标是“%config”,因此会执行如下的规则:

%config: scripts_basic outputmakefile FORCE (Q) (Q)(MAKE) $(build)=scripts/kconfig $@

上面的规则等价于:

menuconfig: scripts_basic outputmakefile FORCE (Q) (Q)(MAKE) $(build)=scripts/kconfig menuconfig

“menuconfig”这个目标有三个依赖:scripts_basic、outputmakefile、FORCE。先来分析下“menuconfig”这个目标下的命令: (Q) (Q)(MAKE) (build)=scripts/kconfig @

1、$(Q)

看下变量Q在Makefile的定义:

# Beautify output # ————————————————————————— # # Normally, we echo the whole command before executing it. By making # that echo ( ((quiet)$(cmd)), we now have the possibility to set # $(quiet) to choose other forms of output instead, e.g. # # quiet_cmd_cc_o_c = Compiling (RELDI R ) / (RELDIR)/@ # cmd_cc_o_c = (CC ) (CC)(c_flags) -c -o @ @< # # If $(quiet) is empty, the whole command will be printed. # If it is set to “quiet_”, only the short version will be printed. # If it is set to “silent_”, nothing will be printed at all, since # the variable $(silent_cmd_cc_o_c) doesn’t exist. # # A simple variant is to prefix commands with $(Q) – that’s useful # for commands that shall be hidden in non-verbose mode. # # (Q)ln (Q)ln@ :< # # If KBUILD_VERBOSE equals 0 then the above command will be hidden. # If KBUILD_VERBOSE equals 1 then the above command is displayed. # # To put more focus on warnings, be less verbose as default # Use ‘make V=1’ to see the full commands 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

从上面的注释和Makefile语句可以看到,当在命令行传人V这个变量的值为1(V=1)时,就会使能quiet、Q变量的值为空,make在执行Makefile命令时就会向屏幕输出所执行的命令;当在命令行不传入V这个变量或者V的值为0(V=0)时,就会使能quiet=quiet_、Q= @,make在执行Makefile命令时就不会向屏幕输出所执行的命令。

2、$(MAKE)

MAKE是内嵌变量,其值为make。

3、$(build)

build这个变量是一个通用的变量,它定义在$(srctree)/scripts/Kbuild.include文件中:

### # Shorthand for (Q) (Q)(MAKE) -f scripts/Makefile.build obj= # Usage: # (Q) (Q)(MAKE) $(build)=dir build := -f $(srctree)/scripts/Makefile.build obj

在内核的根目录下的Makefile包含了$(srctree)/scripts/Kbuild.include这个文件:

# We need some generic definitions (do not try to remake the file). $(srctree)/scripts/Kbuild.include: ; include $(srctree)/scripts/Kbuild.include

分析$(srctree)/scripts/Kbuild.include: ; 这条语句前我们先了解下make书写规则。规则的命令部分有两种书写方式:

a、目标、依赖描述和命令放在同一行,目标和依赖描述使用冒号(:)分隔开,在依赖文件列表后使用分号(;)把依赖文件列表和命令分开。

b、目标和依赖描述放在同一行,目标和依赖描述使用冒号(:)分隔开;命令行在目标、依赖描述的下一行。当作为独立的命令行时此行必须以[Tab]字符开始。在Makefile中,在第一个规则之后出现的所有以[Tab]字符开始的行都会被当作命令来处理。

$(srctree)/scripts/Kbuild.include: ; 这条语句使用的是第一种make书写规则,这条规则只有目标,没有依赖和命令。???因此make在执行这条规则的时候怎么执行???

include (srctree)/scripts/Kbuild.include这条规则把(srctree)/scripts/Kbuild.include这个文件包含到了内核根目录下的Makefile文件中。

从上面的分析可以知道build这个变量的值为-f $(srctree)/scripts/Makefile.build obj。

4、$@

$@是make的自动环变量,表示当前目标,即menuconfig。

下面来分析下scripts_basic、outputmakefile、FORCE这三个依赖:

1、FORCE

FORCE的定义为:

PHONY += FORCE FORCE: # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable so we can use it in if_changed and friends. .PHONY: $(PHONY)

从上面看到,FORCE 既没有依赖的规则,其底下也没有可执行的命令。如果一个规则没有命令或者依赖,并且它的目标不是一个存在的文件名。在执行此规则时,目标总会被认为是最新的。就是说:这个规则一旦被执行,make就认为它的目标已经被更新过。这样的目标在作为一个规则的依赖时,因为依赖总被认为被更新过,因此作为依赖所在的规则中定义的命令总会被执行。FORCE所在规则为空,也是什么都不做。FORCE被定义为一个伪目标,所以它作为依赖时总是被认为是最新的(比目标新),故有FORCE作为依赖的目标每次make时必然会重新生成,在这里FORCE伪目标的规则命令为空,故FORCE在Kbuild体系中,就是相当于是一个关键字,如果我们想要某个目标每次make的时候都一定会被重新生成,就把FORCE写为该目标的依赖。

2、scripts_basic

scripts_basic的定义为:

# Basic helpers built in scripts/ PHONY += scripts_basic scripts_basic: (Q) (Q)(MAKE) $(build)=scripts/basic $(Q)rm -f .tmp_quiet_recordmcount

scripts_basic这个目标没有依赖,且scripts_basic也不是一个存在的文件,因此scripts_basic所定义的命令总会被执行。上述scripts_basic的定义等价为:

# Basic helpers built in scripts/ PHONY += scripts_basic scripts_basic: $(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/basic $(Q) rm -f .tmp_quiet_recordmcount

(Q) make -f (srctree)/scripts/Makefile.build obj=scripts/basic这条命令指定了执行的是

接下来我们来分析下$(srctree)/scripts/Makefile.build这个Makefile文件。

obj这个变量传递进$(srctree)/scripts/Makefile.build中的src这个变量:

src := $(obj)

即src := scripts/basic。

$(srctree)/scripts/Makefile.build把src (即scripts/basic)目录下的Makefile包含进来(如果有Kbuild则包含Kbuild)

# The filename Kbuild has precedence over Makefile kbuild-dir := (if (if(filter /%, (src)), (src)),(src), (srctree)/ (srctree)/(src)) kbuild-file := (if (if(wildcard (kbuild−d i r ) / K build),(kbuild−dir)/Kbuild),(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) include $(kbuild-file)

$(srctree)/scripts/Makefile.build也包含了scripts/Makefile.lib这个文件:

# If the save-* variables changed error out ifeq ($(KBUILD_NOPEDANTIC),) ifneq (“$(save-cflags)“,”$(CFLAGS)“) (errorCF L A G S waschangedin“(errorCFLAGSwaschangedin”(kbuild-file)”. Fix it to use ccflags-y) endif endif include scripts/Makefile.lib

在$(srctree)/scripts/Makefile.build这个Makefile文件中的第一个目标是:

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

KBUILD_BUILTIN、KBUILD_MODULES在顶层Makefile中定义:

# Decide whether to build built-in, modular, or both. # Normally, just do built-in. KBUILD_MODULES := KBUILD_BUILTIN := 1 # If we have only “make modules”, don’t compile built-in objects. # When we’re building modules with modversions, we need to consider # the built-in objects during the descend as well, in order to # make sure the checksums are up to date before we record them. ifeq ($(MAKECMDGOALS),modules) KBUILD_BUILTIN := (if (if(CONFIG_MODVERSIONS),1) endif # If we have “make <whatever> modules”, compile modules # in addition to whatever we do anyway. # Just “make” or “make all” shall build modules as well ifneq ( (filterall a llmodules,(filterallallmodules,(MAKECMDGOALS)),) KBUILD_MODULES := 1 endif ifeq ($(MAKECMDGOALS),) KBUILD_MODULES := 1 endif export KBUILD_MODULES KBUILD_BUILTIN export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD

通过export关键字定义,使在makefile递归进行时,这两个变量被传递进子makefile。

这里:

KBUILD_MODULES :=

KBUILD_BUILTIN := 1

KBUILD_BUILTIN和KBUILD_MODULES在顶层makefile文件中定义赋值后,就没有被改变过。所以此处__build目标的依赖就是 (builtin−target) (builtin−target)(lib-target) (extra−y) (extra−y)(subdir-ym) $(always)。

__build规则展开为:

__build: (builtin−target) (builtin−target)(lib-target) (extra−y) (extra−y)(subdir-ym) $(always) @:

规则的命令是一个冒号命令”:”,冒号(:)命令是bash的内建命令,通常把它看作true命令。bash的help解释(help :)为:No effect; the command does nothing. A zero exit code is returned.(没有效果,该命令是空操作,退出状态总是0)。

__build的依赖除了(always), (builtin−target) (builtin−target)(lib-target) (extra−y) (extra−y)(subdir-ym)这些变量在(srctree)/scripts/basic/Makefile中没有定义,因此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前,我们要保证该库已经被安装在本地。

3、outputmakefile

outputmakefile在内核根目录下的Makefile中的定义为:

PHONY += outputmakefile # outputmakefile generates a Makefile in the output directory, if using a # separate output directory. This allows convenient use of make in the # output directory. outputmakefile: ifneq ($(KBUILD_SRC),) $(Q)ln -fsn $(srctree) source (Q) (Q)(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) endif

由于这里KBUILD_SRC为空,所以这个脚本并不会被执行。

到这里我们分析完了menuconfig的依赖,在处理完这些依赖后就开始执行规则的命令:把 (Q) (Q)(MAKE)(build)=scripts/kconfig @这条命令展开:

(Q) make -f (srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig

这条命令指定要执行scripts/Makefile.build这个makefile文件。

在$(srctree)/scripts/Makefile.build中src变量的定义为:

src := $(obj) PHONY := __build __build: # Init all relevant variables used in kbuild files so # 1) they have correct type # 2) they do not inherit any value from the environment obj-y := obj-m := lib-y := lib-m := always := targets := subdir-y := subdir-m := EXTRA_AFLAGS := EXTRA_CFLAGS := EXTRA_CPPFLAGS := EXTRA_LDFLAGS := asflags-y := ccflags-y := cppflags-y := ldflags-y := subdir-asflags-y := subdir-ccflags-y := # Read auto.conf if it exists, otherwise ignore -include include/config/auto.conf include scripts/Kbuild.include # For backward compatibility check that these variables do not change save-cflags := $(CFLAGS) # The filename Kbuild has precedence over Makefile kbuild-dir := (if (if(filter /%, (src)), (src)),(src), (srctree)/ (srctree)/(src)) kbuild-file := (if (if(wildcard (kbuild−d i r ) / K build),(kbuild−dir)/Kbuild),(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) include $(kbuild-file)

由make -f scripts/Makefile.build obj=scripts/kconfig menuconfig可知,src值为scripts/kconfig,与/%的字串模式相符,因此 (filter/ (filter/(src))就是scripts/kconfig,故kbuild-dir就被赋值为(src),即kbuild-dir为scripts/kconfig。由于scripts/kconfig目录下并没有Kbuild文件,因此函数 (wildcard (wildcard(kbuild-dir)/Kbuild)查找失败,返回为空,从而kbuild-file值被赋为(kbuild-dir)/Makefile,也即scripts/kconfig/Makefile。接着,

scripts/Makefile.build包含scripts/kconfig/Makefile文件(include $(kbuild-file))。目标menuconfig定义在scripts/kconfig/Makefile中,找到menuconfig目标后,然后执行以menuconfig为目标的规则:

PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config \ localmodconfig localyesconfig ifdef KBUILD_KCONFIG Kconfig := $(KBUILD_KCONFIG) else Kconfig := Kconfig endif # We need this, in case the user has it in its environment unexport CONFIG_ … … menuconfig: $(obj)/mconf $< $(Kconfig) … …

menuconfig目标的规则的命令是 < <(Kconfig),展开为 (obj)/mconf (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 conf-objs := conf.o zconf.tab.o mconf-objs := mconf.o zconf.tab.o $(lxdialog) nconf-objs := nconf.o zconf.tab.o nconf.gui.o kxgettext-objs := kxgettext.o zconf.tab.o qconf-cxxobjs := qconf.o qconf-objs := zconf.tab.o gconf-objs := gconf.o zconf.tab.o hostprogs-y := conf nconf mconf kxgettext qconf gconf clean-files := qconf.moc .tmp_qtcheck .tmp_gtkcheck clean-files += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h clean-files += config.pot linux.pot # Check that we have the required ncurses stuff installed for lxdialog (menuconfig) PHONY += $(obj)/dochecklxdialog (add p r e f ix(addprefix(obj)/,$(lxdialog)): $(obj)/dochecklxdialog $(obj)/dochecklxdialog: (Q) (Q)(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOSTLOADLIBES_mconf) always := dochecklxdialog

如果在编译内核的过程中,需要现编译出一些可执行文件供内核编译阶段使用,就需要借助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文件。

保存配置信息后会在内核根目录下生成一个.config文件,该文件保存了所做的内核配置信息。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/140702.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 上面的内容说明:Kconfig就是对应着内核的配置菜单。假如要想添加新的驱动到内核的源码中,可以通过修改Kconfig来增加对我们驱动的配置菜单,这样就有途径选择我们的驱动,假如想使这个驱动被编译,还要修改该驱动所在目录下的Makefile。(见第3小节举例)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档