menuconfig是干什么的?
编译过uboot或者内核的朋友一定清楚这个命令是干什么的,如果不知道,请记住了,这个命令是用来配置的。什么是配置?就是诸如选择什么CPU,选择什么soc,选择什么特性,是否编译成模块等等。uboot和内核的配置功能非常强大。menuconfig是一类通过ncurses实现的简易图形配置功能。
我们可以通过cd到uboot根目录,然后键入:
make menuconfig
就会出来一个图形配置界面:

然后,就可以通过按键来选择你需要的功能和特性,配置完成之后保存并退出。
menuconfig入口在哪里?
我们知道,make命令带上一个参数,make就会到Makefile里面找特定的目标。这里参数是menuconfig,那么make就会在根目录的Makefile里面去寻址menuconfig目标。我们打开根目录Makefile,很可惜,无论你怎么寻找,都没能找到目标是“menuconfig”的规则。
实际上,如果对make语法比较熟悉的话,你会发现,有一种叫做“模式规则”的东西,就是一个目标,并不是通过全名匹配,而是包含了模式字符'%'。这个'%'可以匹配任意非空字符串。如果研究过make help命令里面的各种命令,你会发现,类似make *config的命令还有好几个,远不止menuconfig这一个。但是都有个共同点,就是都以config结尾。比如:
Configuration targets:
config - Update current config utilising a line-oriented program
nconfig - Update current config utilising a ncurses menu based program
menuconfig - Update current config utilising a menu based program
xconfig - Update current config utilising a QT based front-end
gconfig - Update current config utilising a GTK based front-end
oldconfig - Update current config utilising a provided .config as base
localmodconfig - Update current config disabling modules not loaded
localyesconfig - Update current config converting local mods to core
silentoldconfig - Same as oldconfig, but quietly, additionally update deps
defconfig - New config with default from ARCH supplied defconfig
savedefconfig - Save current config as ./defconfig (minimal config)
allnoconfig - New config where all options are answered with no
allyesconfig - New config where all options are accepted with yes
allmodconfig - New config selecting modules when possible
alldefconfig - New config with all symbols set to default
randconfig - New config with random answer to all options
listnewconfig - List new options
olddefconfig - Same as silentoldconfig but sets new symbols to their default value
我的天,居然有这么多。后面的简介说明了每个config是干嘛用的。这么多config,很显然如果每个都要在Makefile里面写一个规则,那也太重复了。于是,我们在根目录下的Makefile第473行,发现了这么一句:
config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
上面那个规则不是模式规则,完全匹配“config”。下面那条规则就匹配所有以config结尾的目标规则。这个规则依赖scripts_basic、outputmakefile和FORCE。具体这些依赖对应的规则,大家可以找一下看看,这里不展开了,看了之后其实非常简单的。这里我们需要重点研究一下这个命令。
menuconfig去到了哪里?
以上我们发现了menuconfig的入口,我们看这个规则的命令部分:
$(Q)$(MAKE) $(build)=scripts/kconfig $@
是不是用到了build?如果不清楚,请回头看下前一篇文章《kbuild框架简要分析》,我们将这个命令展开,得到:
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig
从这条命令我们知道,调用了子make命令,以Makefile.build为make输入规则,参数有两个,obj和menuconfig。好了,执行到这里,马上就要离开主Makefile了。我们进入Makefile.build。
首先,Makefile.build这个文件我们打开,发现里面语句还是挺多的,一下看不过来。还是那句话,先不管无关的东西,我们只关注我们代入的两个参数obj和menuconfig。Makefile.build开头,我们看到:
# Modified for U-Boot
prefix := tpl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := spl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := .
endif
endif
第三行,我们看到obj通过make函数,变换赋值给了src。此时src := scripts/kconfig。于是$(obj)和$(src)就相等,进入ifeq第一个分支里面。展开后,src还是等于scripts/kconfig,又进入下面一个ifeq里面,此时prefix赋值为'.'。这个src在以下命令里面继续处理:
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
详细的展开过程就留给大家当个练习了。这里我们直接给结果,经过这样一个处理,现在有:
kbuild-dir := ./scripts/kconfig
kbuild-file := ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile
于是,这里引入了另一个重要的Makefile。
经过搜索,你会发现,其实Makefile.build里面,其实是找不到menuconfig的匹配目标的,这个menuconfig匹配到了./scripts/kconfig/Makefile这个Makefile里面。我们打开看下,发现第36行有如下语句:
menuconfig: $(obj)/mconf
$< $(silent) $(Kconfig)
menuconfig最终执行者
终于被我们找到这个鬼了。绕了一大圈,居然隐藏在这个鬼地方。而且这个规则极其简单,学过make的朋友都能分析出来这个规则干了些什么。这个规则只有一个依赖,那就是$(obj)/mconf,这个依赖的目标规则在哪里呢?我们找一找吧。
经过简单的字符串查找,我们发现,mconf在./scripts/kconfig/Makefile中的第190行,有这么个定义:
hostprogs-y := conf nconf mconf kxgettext qconf gconf
这里将mconf赋值给了hostprogs-y,具体hostprogs-y的规则,是在Makefile.host里面,为了不走偏本文主题,我们不详细展开,这里需要说明的是,mconf是一个本机程序,是通过本机gcc编译并在本机运行的。这个mconf是一个C语言程序,功能就是用来读取kconfig配置,然后生成图形配置界面,然后保存最终的配置文件。回到menuconfig规则,我们假设mconf已经准备好了,这个规则的命令展开如下:
$(obj)/mconf $(silent) $(Kconfig)
这里有个$(Kconfig)变量,我们看到./scripts/kconfig/Makefile第17行开始有这些赋值:
ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := Kconfig
endif
ifeq ($(quiet),silent_)
silent := -s
endif
所以,对于上述规则的命令,我们可以展开为:
$(obj)/mconf Kconfig
这个命令就是启动mconf程序,以Kconfig作为其输入。运行之后,就看到了那个漂亮的配置界面,然后,你就可以尽情去选择配置了。menuconfig路线我们梳理完毕。

menuconfig是uboot和内核配置的关键命令,用于选择CPU、SOC、特性等。通过make menuconfig启动图形配置界面,它并非直接在根目录Makefile中定义,而是通过模式规则匹配到scripts/kconfig/Makefile,最终由mconf程序执行生成配置界面。
6643

被折叠的 条评论
为什么被折叠?



