menuconfig是在Linux平台下编译大型项目主流的图形化配置界面,如U-Boot, Kernel,Buildroot等,都是使用此工具进行管理。
在项目根目录执行make menuconfig命令,即可打开图形界面,之后选择对应的配置项修改即可,配置项包含状态如下。
# 启动menuconfig
make menuconfig
# 配置项选择说明
[*] : 表示选择,编译到内核
[] : 不包含在内核中,不会编译
<> : 同[]
<M> : 以模块的形式编译,但不会编译到内核
() : 一般为输入的字符串,输入编译或者系统启动时的信息
可以看到配置项的修改如下所示。
执行保存就会在根目录下生成如下.config文件,内容一般如下所示。
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=110201
CONFIG_CLANG_VERSION=0
CONFIG_AS_IS_GNU=y
......
#
# Rust hacking
#
# end of Rust hacking
# end of Kernel hacking
这里说明下在图形化配置下的一些技巧。
关于图形化配置的操作并不困难,应该说很轻松就能修改,不过知道我们要设置什么配置项,在配置项如何添加板级硬件信息,就需要进一步深入去学习,这包含两部分内容。
这里先演示下简化的Makefile脚本,有助于理解整个.config的执行流程。
CXX = g++
CXXFLAGS ?= -std=c++11
INCLUDES :=
BUILD := run
LOCAL = $(shell pwd)
#通过CONFIG控制模块是否最终加入编译选项
CONFIG_MAIN1 = y
CONFIG_MAIN2 = n
obj-${CONFIG_MAIN1} += main1.o
obj-${CONFIG_MAIN2} += main2.o
%.o : %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@ $(INCLUDES)
all: $(obj-y)
$(CXX) $(CXXFLAGS) $(obj-y) -o $(BUILD)
clean:
rm -rf $(obj-y)
rm -rf $(BUILD)
#目录下执行Makefile,编译结果如下
#参考目录file/ch02-02/
g++ -std=c++11 -c main1.cpp -o main1.o
g++ -std=c++11 main1.o -o run
可以看到,main1.cpp最终加入编译,而main2.cpp未加入编译,这是因为最终all导入的是obj-y变量,而main2.cpp被加入obj-n变量中,所以仅main1.cpp被编译,上述就是整个编译的基本原理。 在实际应用中,图形化界面保存后生成的.config文件中是大量的CONFIG_xxx=y或CONFIG_xxx=”“的选项,这些选项如何引入项目编译流程,就更加复杂,去相应的目录下就可以理解。参考内核中tools/build/Documentation/Build.txt,整个编译框架中包含两个部分.
下面是tools/build/Makefile.build完成编译的核心代码。
#定义obj项
obj-y := $($(obj)-y)
subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
obj-y := $(patsubst %/, %/$(obj)-in.o, $(obj-y))
subdir-obj-y := $(filter %/$(obj)-in.o, $(obj-y))
#in-target由obj-y生成
$(in-target): $(obj-y) FORCE
$(call rule_mkdir)
$(call if_changed,$(host)ld_multi)
#编译入口
__build: $(in-target)
@:
可以看到,最终定义为obj-y的变量被最终编译。这里展示.config和Makefile的代码。
#.config文件
CONFIG_HW_RANDOM=y
# CONFIG_HW_RANDOM_TIMERIOMEM is not set
# CONFIG_HW_RANDOM_BA431 is not set
# CONFIG_HW_RANDOM_VIRTIO is not set
CONFIG_HW_RANDOM_IMX_RNGC=y
CONFIG_HW_RANDOM_OPTEE=y
# CONFIG_HW_RANDOM_CCTRNG is not set
# CONFIG_HW_RANDOM_XIPHERA is not set
CONFIG_HW_RANDOM_ARM_SMCCC_TRNG=y
#drivers/char/hw_random/Makefile文件
obj-$(CONFIG_HW_RANDOM) += rng-core.o
rng-core-y := core.o
obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
obj-$(CONFIG_HW_RANDOM_BA431) += ba431-rng.o
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
...
obj-$(CONFIG_HW_RANDOM_ARM_SMCCC_TRNG) += arm_smccc_trng.o
obj-$(CONFIG_HW_RANDOM_CN10K) += cn10k-rng.o
obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o
基于此,可以发现其实不使用图形界面配置,直接修改.config的配置项也可以影响最终结果,不过不建议这种方式,这种修改在重新修改图形配置时会被覆盖丢失。事实上系统提供其它配置初值的方法,在menuconfig启动时可以导入,例如编译内核时的命令。
#导入配置项,启动图形界面
make menuconfig imx_v6_v7_defconfig
其中imx_v6_v7_defconfig就是设置的初始化配置项,这个一般由芯片厂商提供,不过我们可以在此基础上修改来支持自己的硬件板。这时就需要参考说明实现自己的配置选项,默认配置文件目录如下。
上述说明了如何通过CONFIG_XX配置项控制编译的输出,下面进行界面生成方法的讲解。
图形界面是基于Kconfig语法描述的树形结构,包含一系列的菜单入口和配置项的管理,详细参考Linux目录下文档:Documentation/kbuild/kconfig-language.rs,这里列出关键的kconfig语法
上述选项通过重复构建,就实现了完整的管理界面,例如config HW_RANDOM被选择后,最终就生成CONFIG_HW_RANDOM=y的语句,下一步更重要的就是具体配置(config下语句),主要包含如下属性。
这里就按照上述语法实现测试代码块,添加到Linux配置菜单中。
#根目录Kconfig
source "Kconfig.debug"
#KConfig.debug
# SPDX-License-Identifier: GPL-2.0
#
# For a description of the syntax of this configuration file,
# see Documentation/kbuild/kconfig-language.rst.
#
menu "KConfig Debug"
menuconfig KCONFIG_DEBUG
bool "kconfig debug"
default "y"
help
Say Y here to enable support for KCONFIG DEBUG drivers.
#if KCONFIG_DEBUG
comment "KConfig Debug Region"
config KCONFIG_DEBUG_INIT
bool "debug init"
depends on KCONFIG_DEBUG
default "y"
help
Say yes here if you want to use DEBUG INIT.
If unsure, say Y.
config KCONFIG_DEBUG_PLUS
bool "debug plus"
depends on KCONFIG_DEBUG_INIT
default "y"
help
Say yes here if you want to use DEBUG PLUS.
If unsure, say Y.
config KCONFIG_DEBUG_PATH
string "debug path"
depends on KCONFIG_DEBUG_INIT
default ""
help
Set debug path.
config KCONFIG_DEBUG_HEX
hex "debug hex"
depends on KCONFIG_DEBUG_INIT
default "0x80000000"
help
Set debug hex.
config KCONFIG_DEBUG_INT
int "debug int"
depends on KCONFIG_DEBUG_INIT
default "100000"
help
Set debug int.
#必须是可选择子菜单下的选项,最终生成CONFIG_KCONFIG_DEBUG_TEST1=1的选项
choice
prompt "debug test choose"
depends on KCONFIG_DEBUG
default KCONFIG_DEBUG_TEST1
config KCONFIG_DEBUG_TEST1
bool "debug test1"
help
This enables support for debug test 1.
config KCONFIG_DEBUG_TEST2
bool "debug test2"
help
This enables support for debug test 2.
endchoice
#endif
endmenu
编译后,菜单根目录下可以增加如下界面。
这大致就是整个KConfig中最核心的语法。在开发过程中,对于模块添加配置项时,只要找到所属模块下的KConfig文件,添加对应Config项。如果是系统添加模块,则需要增加KConfig文件,在使用Source导入到系统框架中。
上面讲述了如何基于Kconfig构建界面,并基于界面生成.config,从而控制编译输出的全部流程。不过对于U-Boot,Kernel还有文件系统中的图形界面管理维护和调试,还需要结合框架和模块划分去理解运用。界面是编译最终输出的一部分,但背后原理也是我们开发中必须理解的一环,从只能简单运用,到能够自己去修改维护,才能算真正的理解掌握这部分知识。
直接开始下一小节:uboot移植.