加入收藏 | 设为首页 | 会员中心 | 我要投稿 | RSSRSS-巴斯仪表网
您当前的位置:首页 > 电子发烧 > 单片机学习

uClinux 中Makefile文件分析

时间:2013-09-08  来源:123485.com  作者:9stone

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 执行。


分享到:
来顶一下
返回首页
返回首页
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
栏目导航->单片机学习
  • 电子应用基础
  • 电源技术
  • 无线传输技术
  • 信号处理
  • PCB设计
  • EDA技术
  • 单片机学习
  • 电子工具设备
  • 技术文章
  • 精彩拆解欣赏
  • 推荐资讯
    使用普通运放的仪表放大器
    使用普通运放的仪表放
    3V与5V混合系统中逻辑器接口问题
    3V与5V混合系统中逻辑
    数字PID控制及其改进算法的应用
    数字PID控制及其改进
    恶劣环境下的高性价比AD信号处理数据采集系统
    恶劣环境下的高性价比
    栏目更新
    栏目热门