u-boot 2014-10 Makefile 配置過程分析

詆調發表於2015-02-09

u-boot 2014-10的Makefile工程管理結構與以前的有了很大的區別,要做移植工作,就要了解整個工程的配置和編譯過程。從u-boot檔案配置到生成bin檔案需要這些兩個操作
1. 配置
在命令列中輸入 make xxx_defconfig
2. 編譯
在命令列中輸入make命令


配置

首先分析的配置部分,當我們在命令列下輸入make xxx_defconfg 命令時,頂層的Makefile中唯一的匹配目標(以smdk2410為例)

%config: scripts_basic outputmakefile FORCE
    +$(Q)$(CONFIG_SHELL) $(srctree)/scripts/multiconfig.sh $@

其中依賴scripts_basic

PHONY += scripts_basic
scripts_basic:
    $(Q)$(MAKE) $(build)=scripts/basic
    $(Q)rm -f .tmp_quiet_recordmcount
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

scripts_basic展開後

make -f scripts/Makefile.build obj=scripts/basic
rm -f .tmp_quiet_recordmcount

這裡呼叫scripts/Makefile.build進行處理,留在後面對齊進行分析。
依賴條件outputmakefile

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

由於KBUILD_SRC為空,所以這段程式碼不被執行
依賴條件FORCE

PHONY += FORCE
FORCE:

依賴條件分析完成後,接著分析命令。當執行的make xxx_defconfig 後執行執行條件

%config: scripts_basic outputmakefile FORCE
     +$(Q)$(CONFIG_SHELL) $(srctree)/scripts/multiconfig.sh $@ 

命令中的一些變數意義如下:

$(Q) = @ //當make xxx_defconfig V=1時 $(@) =   //控制回顯
$(CONFIG_SHELL) = /bin/sh
$(srctree) = .  //當前資料夾路徑
$@ 表示目標檔案

替換擴充後

smdk2410_defconfig:scripts_basic
    +@/bin/sh ./ scripts/multiconfig.sh smdk2410_defconfig

從最終的makefile中可以看出,Makefile呼叫了multiconfig.sh來完成接下來的配置工作接下來分析multiconfig.sh檔案,當執行multiconfig.sh後它會到最後執行一個case語句

progname=$(basename $0)
target=$1
case $target in
*_defconfig)
    do_board_defconfig $target;;
*_config)
    # backward compatibility
    do_board_defconfig ${target%_config}_defconfig;;
silentoldconfig)
    do_silentoldconfig;;
defconfig)
    do_defconfig;;
savedefconfig)
    do_savedefconfig;;
*)
    do_others $target;;
esac

其中

Progname = multiconfig.sh
Target = smdk2410_defconfig

根據case匹配,shell會呼叫do_board_defconfig函式

# Usage:
#  do_board_defconfig <board>_defconfig
do_board_defconfig () {
    defconfig_path=$srctree/configs/$1
    tmp_defconfig_path=configs/.tmp_defconfig

    if [ ! -r $defconfig_path ]; then
        echo >&2 "***"
        echo >&2 "*** Can't find default configuration \"configs/$1\"!"
        echo >&2 "***"
        exit 1
    fi

    mkdir -p arch configs
    # defconfig for Normal:
    #  pick lines without prefixes and lines starting '+' prefix
    #  and rip the prefixes off.
    sed -n -e '/^[+A-Z]*:/!p' -e 's/^+[A-Z]*://p' $defconfig_path \
                        > configs/.tmp_defconfig

    run_make_config .tmp_defconfig || {
        cleanup_after_defconfig
        exit 1
    }

    for img in $(get_enabled_subimages)
    do
        symbol=$(echo $img | cut -c 1 | tr '[a-z]' '[A-Z]')
        # defconfig for SPL, TPL:
        #   pick lines with 'S', 'T' prefix and rip the prefixes off
        sed -n -e 's/^[+A-Z]*'$symbol'[A-Z]*://p' $defconfig_path \
                        > configs/.tmp_defconfig
        run_make_config .tmp_defconfig $img || {
            cleanup_after_defconfig
            exit 1
        }
    done
    cleanup_after_defconfig
}

在do_board_defconfig函式中先將smdk2410_defconfig中的內容負責一份到config/.tmp_defconfig中,然後將.tmp_defconfig當做引數給run_make_config,最後變數替換後run_make_config為

run_make_config () {
    target=.tmp_defconfig
    objdir=
    # Linux expects defconfig files in arch/$(SRCARCH)/configs/ directory,
    # but U-Boot has them in configs/ directory.
    # Give SRCARCH=.. to fake scripts/kconfig/Makefile.
    options="SRCARCH=.. KCONFIG_OBJDIR="
    if [ "$objdir" ]; then
        options="$options KCONFIG_CONFIG=$objdir/$KCONFIG_CONFIG"
        mkdir -p $objdir
    fi
    build scripts/kconfig "SRCARCH=.. KCONFIG_OBJDIR=" .tmp_defconfig
}

然後呼叫檔案內的build函式

# Useful shorthands
build () {
    debug $progname: $MAKE -f $srctree/scripts/Makefile.build obj="$@"
    $MAKE -f $srctree/scripts/Makefile.build obj="$@"
}

變數替換擴充後

multiconfig.sh: make -f /home/edward/ok6410/u-boot-2014.10/scripts/Makefile.build obj=scripts/kconfig SRCARCH=..  KCONFIG_OBJDIR=  .tmp_defconfig

後面在分析Makefile.build,現在接著分析do_board_defconfig()函式。
在執行完成run_make_config()函式,接著進入一個for迴圈,但是由於smdk2410是Normal image,併為使用SPL和TPL所以for迴圈得不到執行(具體資訊可以結合configs/smdk2410_defconfig和doc/README.kbuild).

接下來分析Makefile.build指令碼。在配置的過程中總共呼叫到兩次makefile.build指令碼

make -f scripts/Makefile.build obj=scripts/basic
make -f /home/edward/ok6410/u-boot-2014.10/scripts/Makefile.build obj=scripts/kconfig SRCARCH=.. KCONFIG_OBJDIR= .tmp_defconfig

makefile.build指令碼是參考了linux kernel 中的kbuild框架設計的,如果深入瞭解可以檢視核心文件說明。這裡就不在贅述,只需要知道makfile.build指令碼根據obj引數呼叫obj目標下的makefile,然後根據obj目錄下的makefile對obj目錄下的檔案進行編譯。當有這個認知後在看第一條指令

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

這裡使用Mkaefile.build指令碼去編譯scripts/basic目錄下的檔案。具體的檔名可以檢視該目錄下啊的Makefile檔案

hostprogs-y := fixdep
always      := $(hostprogs-y)
# fixdep is needed to compile other host programs
$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep

這裡編譯fixdep.c檔案(可以在配置的時候加入V=1引數)

 cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c  

接著分析第二條Makefile.build指令

make -f /home/edward/ok6410/u-boot-2014.10/scripts/Makefile.build obj=scripts/kconfig SRCARCH=.. KCONFIG_OBJDIR= .tmp_defconfig

這裡使用makefile.build指令碼去呼叫scripts/kconfig中的makefile檔案,並將SRCARCH=.. KCONFIG_OBJDIR= .tmp_defconfig 傳入makefile。執行如下命令

%_defconfig: $(obj)/conf
    $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

擴充後

.tmp_defconfig: scripts/kconfig/conf
    scripts/kconfig/conf  --defconfig=arch/../configs/.tmp_defconfig Kconfig

這裡可以看出makefile.build首先是生成scripts/kconfig/conf程式,然後呼叫scripts/kconfig/conf程式配置檔案,scripts/kconfig/conf編譯過程如下

/bin/sh /home/edward/ok6410/u-boot-2014.10/scripts/multiconfig.sh smdk2410_defconfig
  cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer   -DCURSES_LOC="<ncurses.h>" -DLOCALE   -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c
  cat scripts/kconfig/zconf.tab.c_shipped > scripts/kconfig/zconf.tab.c
  cat scripts/kconfig/zconf.lex.c_shipped > scripts/kconfig/zconf.lex.c
  cat scripts/kconfig/zconf.hash.c_shipped > scripts/kconfig/zconf.hash.c
  cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer   -DCURSES_LOC="<ncurses.h>" -DLOCALE  -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c
  cc  -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o  
scripts/kconfig/conf --defconfig=arch/../configs/.tmp_defconfig Kconfig

scripts/kconfig/conf的作用是生成配置資訊。其原始碼位於scripts/kconfig/conf.c檔案中。首先通過getopt_long獲取—defconfig

while ((opt = getopt_long(ac, av, "", long_opts, NULL)) != -1) {
        input_mode = (enum input_mode)opt;
        switch (opt) {
        case silentoldconfig:
            sync_kconfig = 1;
            break;
        case defconfig:
        case savedefconfig:
            defconfig_file = optarg;
            break;
        case randconfig:
        ....
}

此時的的input_mode為defconfig,所以執行第三個case語句,將optarg賦值給defconfig_file,該值為arch/../configs/.tmp_defconfig。接著獲取Kconfig並該名字放入到name中

name = av[optind];

然後呼叫conf_parse(name)函式,解析所有的Kconfig檔案,並將配置資訊放入到struct symbol結構體連結串列中中。
接著呼叫con_read函式讀取.tmp_defconfig中的內容,並根據這些內容生成新的配置資訊

switch (input_mode) {
    case defconfig:
        if (!defconfig_file)
            defconfig_file = conf_get_default_confname();
        if (conf_read(defconfig_file)) {
            printf(_("***\n"
                "*** Can't find default configuration \"%s\"!\n"
                "***\n"), defconfig_file);
            exit(1);
        }
        break;
    case savedefconfig:
    ....
}

接著呼叫conf_set_all_new_sysmbols更新關係連結串列

switch (input_mode) {
    ....
    case defconfig:
        conf_set_all_new_symbols(def_default);
        break;
    case savedefconfig:
        break;
        ....
}

最後呼叫conf_write函式將struct sysbol連結串列中的配置資訊寫入到.config檔案

.....
 else if (input_mode != listnewconfig) {
        if (conf_write(NULL)) {
            fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n"));
            exit(1);
        }
    }

這裡只是列出大概的配置流程,如果想要獲取更多的資訊可以閱讀u-boot原始碼。

相關文章