備注:分析的是OK210開發板自帶的uboot_smdkv210,可能有些部分和其他版本不太一樣,但是原理都類似。
make forlinx_linux_config
make
首先生成配置文件,然後編譯源碼,依次看看這些命令都干了些什麼事情
#forlinx add
forlinx_linux_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x smdkc110 samsung s5pc110 linux
@echo "TEXT_BASE = 0xcc800000" > $(obj)board/samsung/smdkc110/config.mk
此目標依賴unconfig目標,因此先調用unconfig的命令,命令如下
unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
$(obj)board/$(VENDOR)/$(BOARD)/config.mk
執行的操作主要是刪除上一次配置生成的配置文件。
在Makefile中以@開頭的命令表示,在命令執行的時候不在終端上打印信息。
$(MKCONFIG)變量在Makefile的101行有定義,如下:
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
$(@:_config=)
這句話的意思是將 forlinx_linux_config的_config用空白代替只剩下forlinx_linux,
其中$(srcfiles:xxx=ccc)
是Makefile的規則,用等號後邊的ccc替代srcfiles中等號前邊的xxx, $(@) = $@
代表目標,即在命令行輸入的make forlinx_linux_config中的forlinx_linux_config。
因此@$(MKCONFIG) $(@:_config=) arm s5pc11x smdkc110 samsung s5pc110 linux
的意思就是調用u-boot根目錄的mkconfig腳本並將 forlinx_linux arm s5pc11x smdkc110 samsung s5pc110 linux作為參數傳遞給腳本。
在文件的頭部的注釋已經交代了文件的作用和調用的格式
# Script to create header files and links to configure
# U-Boot for a specific board.
#
# Parameters: Target Architecture CPU Board [VENDOR] [SOC]
#
即
$0 = mkconfig (腳本文件名)
$1 = Target (目標) forlinx_linux
$2 = Architecture (架構) arm
$3 = CPU (CPU型號)s5pc11x
$4 = Board (開發板名)smdkc110
$5 = VENDOR (生產商)samsung
$6 = SOC (芯片名)s5pc110
最後一個linux是OK210的工程師自己添加的用於區別Android版本的內核和linux的內核,沒有什麼實質用途。(個人猜測)
APPEND=no # Default: Create new config file
BOARD_NAME="" # Name to print in make output
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done
接下來定義了兩個變量為後邊配置過程做准備,用途注釋已經寫的很清楚,APPEND=no是表示需要重新創建配置文件的標志。
再然後循環:如果參數個數大於零就執行循環,然後再循環裡判斷第一個參數(腳本可以有參數選項),很明顯都不符合,所以直接跳出循環。
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
[ $# -lt 4 ] && exit 1
[ $# -gt 7 ] && exit 1
echo "Configuring for ${BOARD_NAME} board..."
將BOARD_NAME賦值為forlinx_linux,然後判斷參數個數,小於4大於7(一般情況為大於6應該還是OK210的工程師修改了)就直接退出腳本,放棄執行。
接下來創建很多鏈接文件,這些操作是mkconfig腳本的主要工作,主要創建的鏈接文件如下:
if [ "$SRCTREE" != "$OBJTREE" ] ; then
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$2 asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$2
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
else
cd ./include
rm -f asm
ln -s asm-$2 asm
fi
./include/asm --> asm-$2 (asm-arm)
# create link for s5pc11x SoC
if [ "$3" = "s5pc11x" ] ; then
rm -f regs.h
ln -s $6.h regs.h
rm -f asm-$2/arch
ln -s arch-$3 asm-$2/arch
fi
regs.h --> $6.h (s5pc110.h)
asm-$2/arch --> arch-$3(arch-s5pc11x)
u-boot支持很多種開發板,很多種類型的CPU,所以要有一種通用得配置方式來方便移植;
u-boot甚至linux內核都采用了這種方式:針對不同的處理器或者板子都有自己的頭文件和文件夾,但是真正的編譯過程卻不直接使用,而是在配置階段創建這些頭文件或者文件夾的軟連接,並命名為一種通用的名字。在編譯的時候通過這些軟連接訪問真正需要的文件。
例如,在u-boot的include目錄下有一個asm的目錄,此目錄就是在配置完後生成的一個軟連接,當配置的是arm架構時,此文件是asm-arm的軟連接。
#
# Create include file for Make
#
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
#
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
#forlinx add
if [ "$7" = "linux" ]
then
echo "#define CONFIG_LINUX_FORLINX 1" >>config.h
else
echo "#define CONFIG_ANDROID_FORLINX 1" >>config.h
fi
#echo "#include <configs/$1.h>" >>config.h
#forlinx change for linux and android
echo "#include <configs/smdkv210single.h>" >>config.h
因為上邊已經把APPEND設置為NO,所以執行> config.h
即創建config.h頭文件,並向config.h中寫入 /* Automatically generated - do not edit */
"#define CONFIG_LINUX_FORLINX 1
以及#include <configs/smdkv210single.h>
同樣是OK210的工程師加入的 #include <configs/$1.h>
即#include <configs/forlinx_linux.h>
注意:創建軟連接的操作全都是在u-boot根目錄的include子目錄中執行的。
配置腳本mkconfig結束。
一共創建了三個連接文件, ./include/asm --> asm-$2 (asm-arm)
regs.h --> $6.h (s5pc110.h)
asm-$2/arch --> arch-$3(asm-arm -> arch-s5pc11x)
以及include/config.mk和include/config.h,其中include/config.mk文件中記錄著u-boot的版本信息,include/config.h中包含了 configs/$1.h,此文件裡記錄著是很多很多以CONFIG_開頭的宏,是開發板u-boot的主配置信息。
VERSION = 1
PATCHLEVEL = 3
SUBLEVEL = 4
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
Makefile剛開始的時候仍然是定義當前使用的u-boot的版本,此版本和linux內核版本類似都由多個字段組成,各字段意義如下:
VERSION : 主版本號
PATCHLEVEL :次版本號
SUBLEVEL : 修正版本號
EXTRAVERSION:擴展版本號 (可以自己定義,用於區別自己移植的版本號 )
U_BOOT_VERSION 就是以上幾個字段的組合字符串。
VERSION_FILE 保存的是u-boot版本號的文件,其中obj變量在後邊定義。version_autogenerated.h文件也在後邊生成(此文件不是u-boot源碼目錄本身有的文件,是由配置過程自動生成的),其內容如下: #define U_BOOT_VERSION "U-Boot 1.3.4"
HOSTARCH := $(shell uname -m | \
sed -e s/i.86/i386/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/powerpc/ppc/ \
-e s/ppc64/ppc/ \
-e s/macppc/ppc/)
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/')
export HOSTARCH HOSTOS
這一段話是檢測編譯時主機的架構和操作系統,其中使用了sed工具進行了相應的處理, sed -e s/i.86/i386/
意思就是不管是i686\i386還是i586,都用i386替代。 tr '[:upper:]' '[:lower:]'
的意思是將操作系統名字中可能存在的大寫字母全部轉換為小寫字母。
筆者用的是i686的Linux操作系統,因此執行的結果是 HOSTARCH=i386, HOSTOS=linux,
最後調用export導出為環境變量供其他地方使用。
所謂靜默編譯就是在編譯的時候不打印任何信息(錯誤和警告信息除外)。
# Allow for silent builds
ifeq (,$(findstring s,$(MAKEFLAGS)))
XECHO = echo
else
XECHO = :
endif
這段代碼的意思是,在執行make時輸入的參數中搜索’s’,如果有的話就給XECHO賦值為echo,否則就賦值為空,然後在其他地方用XECHO來打印信息,就實現了是否啟用靜默編譯。 make -s
就啟用了靜默編譯。
u-boot除了支持靜默編譯外還支持在不同目錄進行編譯,即將配置過程生成的文件以及編譯產生的中間文件全都輸出到一個指定的目錄,從而避免污染源碼目錄。
從56-76的注釋中可以看出有兩種方式指定
1、在命令行通過make O=/tmp/build
指定輸出目錄為/tmp/build
2、指定BUILD_DIR=/tmp/build
環境變量
注意:通過閱讀根目錄下的README文件,如果要在其他目錄進行編譯,在配置階段和編譯階段都要指定輸出的目錄位置,如下:
1. Add O= to the make command line invocations:
make O=/tmp/build distclean
make O=/tmp/build NAME_config
make O=/tmp/build all
2. Set environment variable BUILD_DIR to point to the desired location:
export BUILD_DIR=/tmp/build
make distclean
make NAME_config
make all
Makefile中的78-93行完成工作就是,如果指定了O參數,就判斷O後邊指定的目錄存在不存在,如果不存在就創建,並把指定的路徑賦值給BUILD_DIR變量 ——
然後就是給幾個下邊變量賦值
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)) //輸出目錄
SRCTREE := $(CURDIR) //源碼目錄
TOPDIR := $(SRCTREE) //頂層目錄(源碼根目錄)
LNDIR := $(OBJTREE) //鏈接目錄
export TOPDIR SRCTREE OBJTREE //導出為環境變量
MKCONFIG := $(SRCTREE)/mkconfig //mkconfig配置腳本的路徑
export MKCONFIG //導出為環境變量
然後是編譯要用的$(obj) 和$(src)
# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
如果編譯輸出目錄和源碼目錄不是通一個目錄,就將源碼目錄的路徑賦值給$(src),將輸出的路徑賦值給$(obj)。
否則就置空。
從124行開始一直到182行都是在確定要使用的交叉編譯工具。
其中在133行的時候導入了一個配置文件
# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
$(obj)include/config.mk
此文件在執行mkconfig腳本的時候生成,裡邊保存著開發板以及CPU的型號和架構信息
ARCH = arm
CPU = s5pc11x
BOARD = smdkc110
VENDOR = samsung
SOC = s5pc110
然後再將這些信息導出為環境變量。
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
export CROSS_COMPILE
再然後就是指定交叉編譯工具,並將CROSS_COMPILE
導出為環境變量了。
此外
在185行導入了U-boot根目錄下的config.mk文件
# load other configuration
include $(TOPDIR)/config.mk
此文件主要完成以下工作
1、定義交叉編譯工具,例如 CC=arm-linux-gcc, LD=arm-linux-ld等等。
2、導入配置文件
$(OBJTREE)/include/autoconf.mk
$(TOPDIR)/$(ARCH)_config.mk
$(TOPDIR)/cpu/$(CPU)/config.mk
$(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk
$(VENDOR)/$(BOARD)
$(TOPDIR)/board/$(BOARDDIR)/config.mk
3、編譯選項相關的一大坨,一般不用管
4、make的自動推導規則
OBJS是編譯時的目標
LIBS是依賴的庫
此外還有GCC的庫等等
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND) $(obj)u-boot.dis
ifeq ($(ARCH),blackfin)
ALL += $(obj)u-boot.ldr
all: $(ALL)
U-Boot源代碼下載地址 http://www.linuxidc.com/Linux/2011-07/38897.htm