1. 程式人生 > >u-boot主makefile分析1

u-boot主makefile分析1

 1.u-boot版本號

VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 6
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
  • Makefile中版本號最終生成了一個變數U_BOOT_VERSION,這個變數記錄了Makefile中配置的版本號。
  • include/version_autogenerated.h檔案是編譯過程中自動生成的一個檔案,所以源目錄中沒有,但是編譯過後的uboot中就有了。它裡面的內容是一個巨集定義,巨集定義的值內容就是我們在Makefile中配置的uboot的版本號。  obj是在後邊定義的(注意是個等號)

 2.HOSTARCH和HOSTOS

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/macppc/ppc/)

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
	    sed -e 's/\(cygwin\).*/cygwin/')

export	HOSTARCH HOSTOS

(1)直接在shell中執行uname -m得到i686,得到的值其實你當前執行這個命令的電腦的CPU的版本號。

(2)shell中的 | 叫做管道,管道的作用就是把管道前面一個運算式的輸出作為後面一個的輸入再去做處理,最終的輸出才是我們整個式子的輸出。

(3)HOSTARCH這個名字:HOST是主機,就是當前在做開發用的這臺電腦就叫主機;ARCH是architecture(架構)的縮寫,表示CPU的架構。所以HOSTARCH就表示主機的CPU的架構。

(4)這兩個環境變數是主機的作業系統和主機的CPU架構,得出後儲存備用,後面自然會用到。

sed -e s/i.86/i386/     碰見i.86就替換成i386    .是個智慧匹配

3.2種編譯方法(原地編譯和單獨輸出資料夾編譯)

(1)編譯複雜專案,Makefile提供2種編譯管理方法。預設情況下是當前資料夾中的.c檔案,編譯出來的.o檔案會放在同一資料夾下。這種方式叫原地編譯。原地編譯的好處就是處理起來簡單。

(2)原地編譯有一些壞處:第一,汙染了原始檔目錄。第二的缺陷就是一套原始碼只能按照一種配置和編譯方法進行處理,無法同時維護2個或2個以上的配置編譯方式。

(3)為了解決以上2種缺陷,uboot支援單獨輸出資料夾方式的編譯(linux kernel也支援,而且uboot的這種技術就是從linux kernel學習來的)。基本思路就是在編譯時另外指定一個輸出目錄,將來所有的編譯生成的.o檔案或生成的其他檔案全部丟到那個輸出目錄下去。原始碼目錄不做任何汙染,這樣輸出目錄就承載了本次配置編譯的所有結果。

(4)具體用法:預設的就是原地編譯。如果需要指定具體的輸出目錄編譯則有2種方式來指定輸出目錄。(具體參考Makefile 註釋內容)

#########################################################################
#
# U-boot build supports producing a object files to the separate external
# directory. Two use cases are supported:
#
# 1) Add O= to the make command line
# 'make O=/tmp/build all'
#
# 2) Set environement variable BUILD_DIR to point to the desired location
# 'export BUILD_DIR=/tmp/build'
# 'make'
#
# The second approach can also be used with a MAKEALL script
# 'export BUILD_DIR=/tmp/build'
# './MAKEALL'
#
# Command line 'O=' setting overrides BUILD_DIR environent variable.
#
# When none of the above methods is used the local build is performed and
# the object files are placed in the source directory.
#

第一種:make O=輸出目錄

第二種:export BUILD_DIR=輸出目錄 然後再make

如果兩個都指定了(既有BUILD_DIR環境變數存在,又有O=xx),則O=xx具有更高優先順序,聽他的。

實現這兩種編譯方式的程式碼如下:

ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif

ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)

# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)

 關於ifeq ("$(origin O)", "command line")這一句話的理解請看博文:

http://blog.sina.com.cn/s/blog_13f1300a80102w742.html

4.OBJTREE、SRCTREE、TOPDIR、MKCONFIG

OBJTREE		:= $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE		:= $(CURDIR)
TOPDIR		:= $(SRCTREE)
LNDIR		:= $(OBJTREE)
export	TOPDIR SRCTREE OBJTREE

MKCONFIG	:= $(SRCTREE)/mkconfig
export MKCONFIG

ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD 	:= 1
export REMOTE_BUILD
endif

OBJTREE        := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

先看CURIDR變數,這是一個MAKEFILE的內嵌變數,代表當前路徑,為了驗證,可以如下做個測試:

在/tmp/test目錄下新建一個Makefile檔案,在裡面新增如下內容:

    all::

               @echo $(CURDIR)

 然後在/tmp/test路徑下執行Make命令,顯示的就是/tmp/test

如果定義了BUILD_DIR,就將BUILD_DIR賦給OBJTREE,如果沒有定義,就將CURIDR賦給OBJTREE

 

(1)OBJTREE:編譯出的.o檔案存放的目錄的根目錄。在預設編譯下,OBJTREE等於當前目錄;在O=xx編譯下,OBJTREE就等於我們設定的那個輸出目錄。

(2)SRCTREE: 原始碼目錄,其實就是原始碼的根目錄,也就是當前目錄。

總結:在預設編譯下,OBJTREE和SRCTREE相等;在O=xx這種編譯下OBJTREE和SRCTREE不相等。Makefile中定義這兩個變數,其實就是為了記錄編譯後的.o檔案往哪裡放,就是為了實現O=xx的這種編譯方式的。

(3)MKCONFIG是Makefile中定義的一個環境變數(在這裡定義,在後面使用),它的值就是我們原始碼根目錄下面的mkconfig。這個mkconfig是一個指令碼,這個指令碼就是uboot配置階段的配置指令碼。

5.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

 如果是預設編譯的話,obj和src這兩個變數是空

6.include/config.mk

ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk))
# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export	ARCH CPU BOARD VENDOR SOC

include/config.mk不是原始碼自帶的(你在沒有編譯過的原始碼目錄下是找不到這個檔案的),要在配置過程(make 100ask24x0_config)中才會生成這個檔案。因此這個檔案的值和我們配置過程有關,是由配置過程根據我們的配置自動生成的。

配置完成config.mk中的內容為:

這一句話就是判斷config.mk是否已經生成,生成的話包含到當前的makefile,並且匯出環境變數。

具體的理解參考一下博文,該博文詳細介紹了wildcard的詳細用法:

https://blog.csdn.net/qq_32220231/article/details/52601032

關於為什麼會生成這些變數請看博文:
u-boot的配置過程:

7.CROSS_COMPILE

ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),ppc)
CROSS_COMPILE =
else
ifeq ($(ARCH),ppc)
CROSS_COMPILE = powerpc-linux-
endif
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
ifeq ($(ARCH),i386)
ifeq ($(HOSTARCH),i386)
CROSS_COMPILE =
else
CROSS_COMPILE = i386-linux-
endif
endif
ifeq ($(ARCH),mips)
CROSS_COMPILE = mips_4KC-
endif
ifeq ($(ARCH),nios)
CROSS_COMPILE = nios-elf-
endif
ifeq ($(ARCH),nios2)
CROSS_COMPILE = nios2-elf-
endif
ifeq ($(ARCH),m68k)
CROSS_COMPILE = m68k-elf-
endif
ifeq ($(ARCH),microblaze)
CROSS_COMPILE = mb-
endif
ifeq ($(ARCH),blackfin)
CROSS_COMPILE = bfin-elf-
endif
ifeq ($(ARCH),avr32)
CROSS_COMPILE = avr32-
endif
endif
endif

export	CROSS_COMPILE

這一段程式碼就是根據配置過程產生的ARCH環境變數來決定CROSS_COMPILE環境變數的值,根據分析:CROSS_COMPILE = arm-linux-

實際運用時,我們可以在Makefile中去更改設定CROSS_COMPILE的值,也可以在編譯時用make CROSS_COMPILE=xxxx來設定,而且編譯時傳參的方法可以覆蓋Makefile裡面的設定。

REF:

朱友鵬課堂筆記