uClinux 中Makefile文件分析

admin @ 2014-03-25 , reply:0

1、概述

uClinux/目錄下的這個Makefile 是個總領式的文件,通過它又層層包含調用各個目錄、子目錄下面對應Makefile,就這樣層層調用下去,從而完成整個軟體系統的編譯。

2、具體分析

下面根據uClinux/Makefile 文件的內容(內容有刪節)大致介紹一下整個編譯的調用關係。

----------------------------------------------------------------------------------------
include common.mk
----------------------------------------------------------------------------------------

首先包含common.mk,它裡面定義了一些通用的全局變數,例如:common.mk 文件中有如下內容:
.EXPORT_ALL_VARIABLES:
(相當於C 中的extern 關鍵字,表示下面的宏變數可以為其它文件所使用)
ROOTDIR = $(shell pwd)
TOOLS = $(ROOTDIR)/tools

----------------------------------------------------------------------------------------
.EXPORT_ALL_VARIABLES
----------------------------------------------------------------------------------------

輸出下面所有全局變數

----------------------------------------------------------------------------------------
IMAGEFILE = image.bin
IMAGEZFILE = imagez.bin
ELFFILE = image.elf
SRECFILE = image.srec
IMAGE = $(ROOTDIR)/images/$(IMAGEFILE)
IMAGEZ = $(ROOTDIR)/images/$(IMAGEZFILE)
ELFIMAGE = $(ROOTDIR)/images/$(ELFFILE)
SRECIMAGE = $(ROOTDIR)/images/$(SRECFILE)
ROMFS = $(ROOTDIR)/romfs
ROMFSIMG = $(ROOTDIR)/images/romfs.img
TOPDIR = $(ROOTDIR)/linux
HOSTCC = unset GCC_EXEC_PREFIX ; gcc -I$(TOPDIR)/include

DIRS = linux lib user
----------------------------------------------------------------------------------------

all 告訴編譯器執行make 都要分完成哪些工作步驟,這是最重要的地方!
看一個Makefile 首先要從它的all 看起,就相當於C 語言的main()函數的作用。
然後再從all 層層分析下去。

----------------------------------------------------------------------------------------
all: config-test subdirs build-romfs $(IMAGE)
----------------------------------------------------------------------------------------

這裡共四步工作,即:

----------------------------------------------------------------------------------------
* config-test
* subdirs
* build-romfs
* $(IMAGE),即上面的IMAGE 變數,Makefile 中用$(變數名)來使用變數,這中用法對許多熟悉各種腳本語言的用戶並不陌生。

下面在Makefile 分別找到各步工作對應的部分:

----------------------------------------------------------------------------------------
(1) config-test:
@if [ ! -f .config -o ! -f linux/.config -o ! -f
vendors/.config ]; then \
----------------------------------------------------------------------------------------

若找不到.config 文件或者找不到linux/.config 或者找不到
vendors/.config 文件,就提示需要make config 或者make xconfig

----------------------------------------------------------------------------------------
echo "ERROR: you need to do a 'make config' first" ; \
exit 1 ; \
fi
@if [ ! -d romfs ]; then \
【必須有romfs 這個目錄】
echo "ERROR: you need to run 'make romfs' as root first" ;\
exit 1 ; \
fi


(2)subdirs

subdirs:
@if [ ! -f linux/.depend ] ; then \
【若沒找到linux/.depend 文件,就提示要make dep】
echo "ERROR: you need to do a 'make dep' first" ;
exit 1 ; \
fi
for dir in $(DIRS) ; do make -C $$dir || exit 1 ; done
----------------------------------------------------------------------------------------

【上面這一句雖短,但確是最主要的工作所在,注意到前面定義了:
DIRS = linux lib user
因此這一句:
make -C $$dir
就完成了對內核(linux 目錄)的編譯、對libC 庫(lib)的編譯、對所有應用程序(user 下所有指定要編譯的目錄)的編譯。
make -C $$dir 就調用對應那個目錄下的Makefile , 即分別是linux/Makefile,lib/Makefile 和user/Makefile,這Makefile 又層層包含調用下面的各個目錄的Makefile,從而完成整個編譯過程。】

其中編譯user 下各個應用程序時,每個應用程序目錄下的Makefile 中都要執行如下一句:
$(CONVERT)
這個宏是在uClinux/user/arch/coldfire/目錄下的build.mk 文件中指定的,這個文件的作用就相當於uClinux/common.mk,它為應用程序的編譯定義了許多公共的宏,例如所採用的編譯器(CC)等。 LINUX 內核都是用m68k-coff-gcc 編譯的,但應用程序可以採用不同的gcc 編譯器。

這個文件的部分內容如下:
.EXPORT_ALL_VARIABLES:
CC = $(TOOLS)/m68k-elf-gcc
CXX = $(TOOLS)/m68k-elf-g++
AR = $(TOOLS)/m68k-elf-ar
LD = $(TOOLS)/m68k-elf-ld
OBJCOPY = $(TOOLS)/m68k-elf-objcopy
RANLIB = $(TOOLS)/m68k-elf-ranlib
ELF2FLT = $(TOOLS)/elf2flt
GCC_EXEC_PREFIX = $(TOOLS)/m68k-elf-
LIBC = $(ROOTDIR)/lib/libc/libc.a
LIBM = $(ROOTDIR)/lib/libm/libmf.a
LIBNET = $(ROOTDIR)/lib/libnet/libnet.a
LIBDES = $(ROOTDIR)/lib/libdes/libdes.a
LIBPCAP = $(ROOTDIR)/lib/libpcap/libpcap.a
LIBSSL = $(ROOTDIR)/lib/libssl/libssl.a
LIBCRYPTO = $(ROOTDIR)/lib/libssl/libcrypto.a
LIBGCC = $(TOOLS)/gcc-lib/libgcc.a
ARCH = -m5200 -Wa,-m5200 -DCONFIG_COLDFIRE
DEFS = -Dlinux -D__linux__ -Dunix -DEMBED
INCS = $(INCGCC) $(INCLIBC) $(INCLIBM) $(INCVEND)
CCFLAGS = -O2 -msoft-float 【編譯參數】
CFLAGS = $(DEBUG_CFLAGS) $(ARCH) $(DEFS) $(CCFLAGS) $(INCS) -
fno-builtin 【編譯參數】
LDFLAGS = --sort-common -r $(STARTUP) 【鏈接參數】
最後定義了CONVERT 宏:
CONVERT = mv $$@.elf .$$@.elf; \
$(LD) -T $(LDSCRIPT) -Ur -o $$@.elf .$$@.elf; \
$(LD) -T $(LDSCRIPT) -o $$@.gdb .$$@.elf; \
rm -f .$$@.elf; \
$(ELF2FLT) $$(FLTFLAGS) -o $$@ $$@.elf
它採用elf2flt 工具將編譯生成的ELF 格式可執行文件轉換為uClinux 所支持的FLAT 文件格式。
【注】
uClinux 唯一支持的可執行文件格式就是FLAT 格式。其它都不支持。
所以所有要在uClinux 上跑的應用都必須轉換為FLAT 格式。PC REDHATLINUX 不支持這種格式的可執行文件,所以這些可執行文件都無法在PC 上
執行。


(3)build-romfs

build-romfs: images
make -f romfs.mk
build-romfs 後面跟了images,表明它依賴於images 先執行的結果:
images:
[ -d images ] || mkdir images
images 只是檢查是否有images 目錄,若沒有就創建該目錄。
下面的make -f romfs.mk 就調用執行romfs.mk。
下面是romfs.mk 文件中的內容:
這也是個Makefile,因此如前所述,要從它的all 入口看起:
all: copy-files
$(TOOLS)/genromfs -v -V "ROMdisk" -f $(ROMFSIMG) -d
$(ROMFSDIR)
all 要先依賴copy-files 部分的完成:
copy-files:
cp $(RAMFSy) romfs/etc/ramfs.img
[ ! "$(BINy)" ] || cp $(BINy) romfs/bin/.
rm romfs/etc/services
rm romfs/etc/inetd.conf
make -C user build-romfs
-find romfs -name CVS | xargs rm -rf
這裡主要的工作就是一句:
[ ! "$(BINy)" ] || cp $(BINy) romfs/bin/.
完成user/下各個參與的應用程序目錄下的編譯生成的可執行文件的收集,將它們複製到romfs/bin/目錄下。
下一步就是romfs 目錄的打包,生成romfs.img 二進IMAGE 文件,它就是我們常說的ROMFS 文件系統的映像文件:
$(TOOLS)/genromfs -v -V "ROMdisk" -f $(ROMFSIMG) -d $(ROMFSDIR)
至此就完成了romfs.mk 中的工作,下面返uClinux/Makefile 的內容繼續:

(4) $(IMAGE)

IMAGE 變數為:
IMAGE = $(ROOTDIR)/images/image.bin
這就是最後被複制到/tftpboot/目錄下的image.bin 文件,也就是最後可以燒寫或下載的二進位IMAGE 文件。
$(IMAGE): images images/linux.bin
cat images/linux.bin $(ROMFSIMG) > $(IMAGE)
$(TOOLS)/cksum -b -o 2 $(IMAGE) >> $(IMAGE)
cp $(IMAGE) /tftpboot
首先依賴於images 和image/linux.bin。
images/linux.bin: images linux/linux
$(TOOLS)/m68k-elf-objcopy -O binary
--set-section-flags=.romvec=CONTENTS,ALLOC,LOAD,READONLY,CODE
linux/linux images/linux.bin
image/linux.bin 又依賴linux/linux,它是前面make –C linux 時根據linux 目錄下的Makefile 規則編譯生成的。然後用m68k-elf-objcopy 將linux/linux 文件轉換為二進位文件images/linux.bin。
然後用:
cat images/linux.bin images/romfs.img > images/image.bin
最後,將images/image.bin 複製到/tftpboot 目錄下,從而完成整個編譯過程。
【注意】
    Makefile 中使用了許多.PHONY:節,它並沒有什麼作用,只是告知編譯器,它的:號後面並不是一個文件。例如:
.PHONY: romfs (沒有這句也可以,但是若目錄下有一個文件名字叫做romfs,則會報錯make: ` ‘romfs’ is up to date。加了PHONY 就是通知編譯器,不要把romfs 看作一個目標文件。)
romfs:
……
Makefile 還提供了許多獨立的目標,可以直接用make 命令指定目標單獨編譯。例如:
dep:
@if [ ! -f linux/.config ] ; then \
echo "ERROR: you need to do a 'make config'
first" ; \
exit 1 ; \
fi
make -C linux dep
就可以直接make dep 執行。
romfs:
make -f romfs_cvs.mk
[ "$(CONFIG_VENDOR)" ] || echo "******\nrun 'make config'
first\n******"
make -C vendors/$(CONFIG_VENDOR)/$(CONFIG_PRODUCT)/. romfs
就可以直接make romfs 執行。




[admin via 研發互助社區 ] uClinux 中Makefile文件分析已經有2179次圍觀

http://cocdig.com/docs/show-post-42572.html