Skip to content

LV115-menuconfig过程解析

前面的图形界面怎么实现的?又是怎么实现配置的?接下来就来看一下这个 make menuconfig 的配置原理。

一、%config 目标

我们可以在顶层 Makefile 找到 %config 这个配置项:

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

其中:

makefile
Q=@       # 或为空
MAKE=make
@=%config # make menuconfig的话,这里 @ 表示的就是menuconfig

build 定义在 Kbuild.include - scripts/Kbuild.include 中,定义如下:

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

这里的 srctree 的值就是 .,所以这里的 build 其实就是:

shell
build := -f ./scripts/Makefile.build obj

这个%config 在前面分析顶层 Makefile 时有分析过,可以看这个笔记《LV05-02-U-Boot-06-01-顶层 Makefile 基础解析.md》——make xxx_defconfig 这个过程的分析。

所以,这里简单展开的话就是:

makefile
%config: scripts_basic outputmakefile FORCE
	@make -f ./scripts/Makefile.build obj=scripts/kconfig $@

这里其实就是会调用 Makefile.build - scripts/Makefile.build 这个 makefile 文件去完成对应的目标创建,在调用 Makefile.build - scripts/Makefile.build 的时候还会传入参数,至少会有 obj 这个变量。

二、 %config 的依赖分析

1. FORCE

在顶层 MakefileFORCE 定义如下:

Makefile
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 是没有规则和依赖的,所以每次都会重新生成 FORCE。当 FORCE 作为 其他目标的依赖时,由于 FORCE 总是被更新过的,因此依赖所在的规则总是会执行的。

2. scripts_basic

2.1 依赖规则分析

在顶层 Makefilescripts_basic 定义如下:

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

这是 scripts_basic 的规则,其对应的命令用到了变量 Q、MAKE 和 build。这里展开就是:

makefile
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
	@make -f $(srctree)/scripts/Makefile.build obj=scripts/basic # 也可以没有@,视配置而定
	@rm -f .tmp_quiet_recordmcount

可以看到这里也是调用了 Makefile.build - scripts/Makefile.build ,并且 obj = scripts/basic

2.2 Makefile.build

接下来我们就来看一下 Makefile.build 脚本里面都有什么。前面分析到 scripts_basic 展开后如下:

makefile
 @make -f ./scripts/Makefile.build obj=scripts/basic

接下来就来看一下这个命令会怎么执行,最终会得到什么。

2.2.1 src 与 prefix 变量

先看一下 Makefile.build - scripts/Makefile.build 最开始的两个变量:

makefile
# 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

第 2 行,变量 prefix 值为 tpl。

第 3 行定义了变量 src,这里用到了函数 patsubst,此行代码展开后为:

makefile
$(patsubst tpl/%,%, scripts/basic)

patsubst 是替换函数,格式如下:

makefile
$(patsubst <pattern>,<replacement>,<text>)

此函数用于在 text 中查找符合 pattern 的部分,如果匹配的话就用 replacement 替换掉。 pattern 是可以包含通配符“%”,如果 replacement 中也包含通配符“%”,那么 replacement 中的 这个“%”将是 pattern 中的那个“%”所代表的字符串。函数的返回值为替换后的字符串。因 此,第 3 行就是在“scripts/basic”中查找符合“tpl/%”的部分,然后将“tpl/”取消掉,但是 “scripts/basic”没有“tpl/”,所以 src = scripts/basic。

第 4 行判断变量 obj 和 src 是否相等,相等的话条件成立,很明显,此处条件成立。

第 6 行和第 3 行一样,只是这里处理的是“spl”,“scripts/basic”里面也没有“spl/”,所以 src 继续为 scripts/basic。

第 8 行因为变量 obj 和 src 相等,所以 prefix =. 。

2.2.2 kbuild-dir 与 kbuild-file

我们继续往下看 Makefile.build - scripts/Makefile.build,这里有两个变量:

makefile
# 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)

我们前面分析过 srctree =.,src = scripts/basic, 将 kbuild-dir 展开后为:

makefile
kbuild-dir := $(if $(filter /%, scripts/basic), scripts/basic, ./scripts/basic)

因为没有以“/”为开头的单词,所以$(filter /%, scripts/basic)的结果为空,kbuild-dir =./scripts/basic。得到这个变量后,我们将 kbuild-file 展开后为:

makefile
kbuild-file := $(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild, ./scripts/basic/Makefile)

因为 scrpts/basic 目录中没有 Kbuild 这个文件,所以 kbuild-file = ./scripts/basic/Makefile。最后将第 4 行展开,即:

makefile
include ./scripts/basic/Makefile

也就是读取 Makefile - scripts/basic/Makefile 文件。

2.2.3 hostprogs-y

接下来看这一行 Makefile.build - scripts/Makefile.build - hostprogs-y ,这里也会包含一个 Makefile 文件:

makefile
# Do not include host rules unless needed
ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host
endif

我们可以看一下这个 Makefile.host - scripts/Makefile.host 文件:

makefile
# SPDX-License-Identifier: GPL-2.0
# ==========================================================================
# Building binaries on the host system
# Binaries are used during the compilation of the kernel, for example
# to preprocess a data file.
#
# Both C and C++ are supported, but preferred language is C for such utilities.
#
# Sample syntax (see Documentation/kbuild/makefiles.txt for reference)
# hostprogs-y := bin2hex
# Will compile bin2hex.c and create an executable named bin2hex
#
# hostprogs-y    := lxdialog
# lxdialog-objs := checklist.o lxdialog.o
# Will compile lxdialog.c and checklist.c, and then link the executable
# lxdialog, based on checklist.o and lxdialog.o
#
# hostprogs-y      := qconf
# qconf-cxxobjs   := qconf.o
# qconf-objs      := menu.o
# Will compile qconf as a C++ program, and menu as a C program.
# They are linked as C++ code to the executable qconf

其实这里主要就是把用到的工具的源码在主机上编译出对应的工具,例如 mconf、qconf 这些。

2.2.4 __build

我们继续向下看 Makefile.build - scripts/Makefile.build,会看到这个__build 目标:

makefile
# We keep a list of all modules in $(MODVERDIR)

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

__build 是默认目标,因为下面这条命令

makefile
@make -f ./scripts/Makefile.build obj=scripts/basic

没有指定目标,所以会使用到默认目标:__build。在顶层 Makefile 中,KBUILD_BUILTIN 为 1,KBUILD_MODULES 为 0,因此展开后目标__build 为:

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

可以看出目标__build 有 5 个依赖:builtin-target、lib-target、extra-y、subdir-ym 和 always。 这 5 个依赖的具体内容我们就不通过源码来分析了,直接在 Makefile.build - scripts/Makefile.build 中输入以下代码:

makefile
# We keep a list of all modules in $(MODVERDIR)

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

然后我重新执行以下命令:

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_defconfig

会看到有如下打印信息出现:

shell
builtin-target=
lib-target=
extra-y=
subdir-ym=
always=scripts/basic/fixdep

可以看出,只有 always 有效,因此__build 最终为:

makefile
__build:scripts/basic/fixdep
	@:

__build 依赖于 scripts/basic/fixdep,所以要先编译 fixdep.c - scripts/basic/fixdep.c,生成 fixdep,前面已经读取了 Makefile - scripts/basic/Makefile 文件。

2.2.5 总结

需要注意的是,在 Makefile.build - scripts/Makefile.build 文件中将会包含两个 Makefile,其中 Makefile.host - scripts/Makefile.host 是固定的,会编译出所需要的工具,还有一个受到参数的影响,不同的功能对应不同的 Makefile,主要是根据 Makefile.build - scripts/Makefile.build - kbuild-file 来确定包含哪个 Makefile。

2.3 总结

总的来说 scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 这个软件。我们可以执行以下命令看一下打印信息:

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- disclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

然后就会看到以下内容:

shell
make -f ./scripts/Makefile.build obj=scripts/basic
  cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11     -o scripts/basic/fixdep scripts/basic/fixdep.c  
rm -f .tmp_quiet_recordmcount

可以看到与上面的分析结果一致。这个应用是用来生成 头文件依赖 的,在编译过程中,使用-MD 选项可以生成依赖关系文件。这些文件包含了源代码文件和它们之间的依赖关系,通常以 Makefile 的规则格式保存,这样可以在后续的编译过程中更好地管理文件的依赖关系。这在大型项目中是很有用的,因为它可以确保在修改一个源文件后,只重新编译与之相关的文件,而不是整个项目。

下面是一个简单的例子,假设有一个 C 语言项目,包含三个源文件:main.c,utils.c,和 header.h。main.c 包含了 main 函数,utils.c 包含了一些工具函数,而 header.h 包含了函数的声明。

假设我们使用 GCC 编译器,可以通过以下命令使用-MD 选项生成依赖关系文件:

shell
gcc -MD -o main main.c utils.c

这个命令会生成 main.d 文件,内容可能如下:

D
main.o: main.c header.h
utils.o: utils.c header.h

这表示在编译 main.c 时,依赖于 main.c 和 header.h;在编译 utils.c 时,依赖于 utils.c 和 header.h。这样,如果修改了 header.h,只有依赖于它的文件会重新编译,而不是整个项目。

这对于 Makefile 来说非常有用,因为它们可以根据这些依赖关系文件来判断哪些文件需要重新编译,从而提高编译效率。 fixdep 是一个用于生成 Makefile 依赖关系的工具。它主要用于处理头文件之间的依赖关系,以确保在构建 U-Boot 时,当某个头文件发生变化时,只重新编译与之相关的文件,而不是整个项目。具体来说,fixdep 主要完成以下任务:

(1)解析源代码文件中的#include 指令:fixdep 分析源代码文件,找到其中包含的头文件。

(2)生成 Makefile 规则: 通过分析源代码文件中的 #include 指令,fixdep 生成一个包含依赖关系的 Makefile 规则。这些规则通常包含了源文件和其所依赖的头文件,以及它们之间的关系。

(3)输出依赖关系: fixdep 将生成的 Makefile 规则输出到标准输出或指定的文件中,以便后续的构建工具(如 Make)使用。

例如,在 U-Boot 的 Makefile 中可能包含类似如下的使用 fixdep 的命令:

makefile
depend:
    $(SRCTREE)/scripts/fixdep $(CFLAGS) $(CPPFLAGS) $(SRCARCH) $(SRC) > .depend

这个命令使用 fixdep 生成头文件依赖关系,并将结果保存到.depend 文件中。在 Makefile 的其他地方可以包含这个文件,以便 Make 工具能够使用这些依赖关系。

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)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
	    $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

这个没有详细分析,可以加个打印看一眼:

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

然后执行:

shell
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- disclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

会发现 KBUILD_SRC 值为空:

image-20250209102416540

所以下面的几个命令其实是没有执行的,这里就先不管了。

三、%config 分析

上面已经分析完依赖了,现在再来分析这个 %config 中的命令。

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

这里我们可以展开一下:

makefile
%config: scripts_basic outputmakefile FORCE
	@make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig

可以看到,还是要继续分析 Makefile.build - scripts/Makefile.build 。按照前面的分析,相关的变量值如下所示:

makefile
src= scripts/kconfig
kbuild-dir = ./scripts/kconfig
kbuild-file = ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile

可以看出,在这个命令下,Makefilke.build 会读取 Makefile - scripts/kconfig/Makefile 中的内容,此文件中有如下所示内容:

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

其中 $< 表示依赖文件中的第一个文件。也就是 $(obj)/mconf,所以其实就是 scripts/kconfig/mconf 文件,silent 就是一个静默输出的控制标志,这里先不管。Kconfig = Kconfig(Makefile - scripts/kconfig/Makefile · Kconfig),所以这里可以展开:

makefile
menuconfig: scripts/kconfig/mconf
	scripts/kconfig/mconf Kconfig

1. $(obj)/mconf

先来看这个 $(obj)/mconf ,我们知道 obj = scripts/kconfig,所以这里就是 scripts/kconfig/mconf,说实话这里我还真没理解,但是看打印信息的话我们可以自己猜测出来这个工具的编译过程,下面有一个自己写的编译实例。uboot 的编译系统还是有些复杂,里面其实有很多可以借鉴的地方,感觉目前没必要深挖,越挖东西越多,先这样,后面有空再深入了解。

2. 总结

编译出 mconf 后,就可以使用这个可执行程序来读取 Kconfig 文件了:

makefile
scripts/kconfig/mconf Kconfig

然后就会调用对应的图形库,渲染出一个简单的配置界面。

四、自定义带 Kconfig 的工程

linux-dev-org/imx6ull-app-demo - Gitee.com

1. mconf/conf 的编译

可以看这个:LV41_kconfig/01_kconfig_demo/scripts/kconfig/Kconfig.mk · linux-dev-org/imx6ull-app-demo

makefile
TARGET_mconf    ?= mconf
TARGET_conf     ?= conf

CROSS_COMPILE   ?=
CC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld
OBJCOPY 		:= $(CROSS_COMPILE)objcopy
OBJDUMP 		:= $(CROSS_COMPILE)objdump

HOSTCFLAGS       = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11
COBJ_FLAGS      := $(HOSTCFLAGS)

OBJ_DIR         ?= ./
SRC_DIR         ?= ./
# SHELL used by kbuild
CONFIG_SHELL    := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
					else if [ -x /bin/bash ]; then echo /bin/bash; \
					else echo sh; fi ; fi)

# lxdialog stuff
check-lxdialog  := ./lxdialog/check-lxdialog.sh

# Use recursively expanded variables so we do not call gcc unless
# we really need to do so. (Do not call gcc as part of make mrproper)
HOST_EXTRACFLAGS        += $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) \
                    		-DLOCALE
# Add environment specific flags
HOST_EXTRACFLAGS        += $(shell $(CONFIG_SHELL) ./check.sh $(CC) $(HOSTCFLAGS))

# generated files seem to need this to find local include files
HOSTCFLAGS_zconf.lex.o	:= -I$(SRC_DIR)
HOSTCFLAGS_zconf.tab.o	:= -I$(SRC_DIR)
HOSTLOADLIBES_mconf      = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(CC))
HOSTLOADLIBES_conf       = 

COBJ_FLAGS      += $(HOST_EXTRACFLAGS)

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)

$(TARGET_mconf): $(mconf-objs)
	$(CC) -o $@ $(mconf-objs) $(HOSTLOADLIBES_mconf)

$(TARGET_conf): $(conf-objs)
	$(CC) -o $@ $(conf-objs) $(HOSTLOADLIBES_conf)


zconf.tab.o: zconf.tab.c zconf.lex.c
	$(CC) -Wp,-MD,.$@.d $(COBJ_FLAGS) $(HOSTCFLAGS_zconf.tab.o) -c -o $@ zconf.tab.c

zconf.tab.c:
	bison -o $@ -t -l zconf.y

zconf.lex.c:
	flex -o $@ -L zconf.l

$(lxdialog): lxdialog/%.o : lxdialog/%.c
	$(CC) -Wp,-MD,$@.d $(COBJ_FLAGS) -c $< -o $@

$(TARGET_mconf).o:
	$(CC) -Wp,-MD,.$@.d $(COBJ_FLAGS) -Iscripts/kconfig -c -o $@ mconf.c

$(TARGET_conf).o:
	$(CC) -Wp,-MD,.$@.d $(COBJ_FLAGS) -Iscripts/kconfig -c -o $@ conf.c

menuconfig: $(TARGET_mconf)
	./$(TARGET_mconf) Kconfig
	@python menu_anno.py .config config.h 0
	@rm -f .config.old .config.cmd

%_defconfig: $(TARGET_conf)
	@if [ -e ./configs/$@ ];then \
		./$(TARGET_conf)  --defconfig=lxdialog/../configs/$@ Kconfig; \
	else \
		echo "$@ not exists"; \
	fi
	@python menu_anno.py .config config.h 0
	@rm -f .config.old .config.cmd

.PHONY: clean clean_c clean_obj clean_d clean_config

clean: clean_c clean_obj clean_d clean_config

clean_c:
	@rm -rvf zconf.lex.c
	@rm -rvf zconf.tab.c

clean_obj:
	@find . -name $(TARGET_mconf) -exec rm -rvf {} \;
	@find . -name \*.o -exec rm -rvf {} \;

clean_d:
	@find . -name \*.d -exec rm -rvf {} \;

clean_config:
	@rm -rvf .config .config.old config.h

2. 自动创建头文件

当我们可以正常生成.config 文件的时候,可以通过这个文件生成一个 config.h 头文件,方便 C 语言工程调用,可以通过 Python 来实现:

LV41_kconfig/01_kconfig_demo/scripts/kconfig/config_head_create.py · linux-dev-org/imx6ull-app-demo

python
#!/usr/bin/env python3

import os
import sys

def write_config(config_filename, filename, chipname):
    config_h = open(filename, 'w')
    config_h.write('#ifndef __CONFIG_H__\n')
    config_h.write('#define __CONFIG_H__\n\n')

    config = open(config_filename, 'r')

    empty_line = 1

    for line in config:
        line = line.lstrip(' ').replace('\n', '').replace('\r', '')

        if len(line) == 0:
            continue

        if line[0] == '#':
            if len(line) == 1:
                if empty_line:
                    continue

                config_h.write('\n')
                empty_line = 1
                continue

            line = line[2:]

            config_h.write('/*%s */\n' % line)
            empty_line = 0

        else:
            empty_line = 0
            setting = line.split('=')
            if len(setting) >= 2:
                if setting[1] == 'y':
                    config_h.write('#define %s\n' % setting [0])
                else:
                    config_h.write('#define %s %s\n' % (setting [0], setting [1]))

    config_h.write('\n')
    config_h.write('#endif\n')
    config_h.close()

if __name__ == '__main__':

    if len(sys.argv) < 2:
        sys.exit(1)

    config_filename = sys.argv[1]
    filename = sys.argv[2]
    chipname = sys.argv[3]

    write_config(config_filename, filename, chipname)

修改 Makefile 如下:

makefile
TARGET          ?= mconf.out

CROSS_COMPILE   ?=
CC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld
OBJCOPY 		:= $(CROSS_COMPILE)objcopy
OBJDUMP 		:= $(CROSS_COMPILE)objdump

HOSTCFLAGS       = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11
COBJ_FLAGS      := $(HOSTCFLAGS)

OBJ_DIR         ?= ./

# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
	  else if [ -x /bin/bash ]; then echo /bin/bash; \
	  else echo sh; fi ; fi)

# lxdialog stuff
check-lxdialog  := ./lxdialog/check-lxdialog.sh

# Use recursively expanded variables so we do not call gcc unless
# we really need to do so. (Do not call gcc as part of make mrproper)
HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) \
                    -DLOCALE
# Add environment specific flags
HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) ./check.sh $(CC) $(HOSTCFLAGS))

HOSTLOADLIBES_mconf   = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(CC))

COBJ_FLAGS       += $(HOST_EXTRACFLAGS)

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)

$(TARGET): $(mconf-objs)
	$(CC) -o $@ $(mconf-objs) $(HOSTLOADLIBES_mconf)

zconf.tab.o: zconf.tab.c zconf.lex.c
	$(CC) -Wp,-MD,.$@.d $(COBJ_FLAGS) -Iscripts/kconfig -c -o zconf.tab.o zconf.tab.c

zconf.tab.c:
	bison -o zconf.tab.c -t -l zconf.y

zconf.lex.c:
	flex -o zconf.lex.c -L zconf.l

$(lxdialog): lxdialog/%.o : lxdialog/%.c
	$(CC) -Wp,-MD,$@.d $(COBJ_FLAGS) -c $< -o $@

mconf.o:
	$(CC) -Wp,-MD,.$@.d $(COBJ_FLAGS) -Iscripts/kconfig -c -o mconf.o mconf.c

menuconfig: $(TARGET)
	./$(TARGET) Kconfig
	@python menu_anno.py .config config.h 0
	@rm -f .config.old .config.cmd

.PHONY: clean clean_c clean_obj clean_d clean_config

clean: clean_c clean_obj clean_d clean_config

clean_c:
	@rm -rvf zconf.lex.c
	@rm -rvf zconf.tab.c

clean_obj:
	@find . -name $(TARGET) -exec rm -rvf {} \;
	@find . -name \*.o -exec rm -rvf {} \;

clean_d:
	@find . -name \*.d -exec rm -rvf {} \;

clean_config:
	@rm -rvf .config .config.old config.h

3. 功能完善

其实直接把 uboot 中的 scripts 目录拷贝出来,写好对应的顶层 Makefile,是可以实现 menuconfig 的,我这里拷贝了一份,并且重新编写了 Makefile,只能实现 make menuconfig,也许以后会用于 app 的编译:LV41_kconfig/01_kconfig_demo/scripts/kconfig/config_head_create.py · linux-dev-org/imx6ull-app-demo,这个的话就可以和其他的工程结合到一起,方便扩展。