星星博客's Archiver

cnangel 发表于 2004-3-12 18:58

[分享]使用GNU GCC创建BREW应用

摘要:本文介绍了如何在Windows平台上安装设置用于BREW应用开发的GNU GCC编译器,并演示了使用GNU GCC创建BREW应用模块的一个实例。

关键字:BREW,GNU GCC编译器,GNUDE,ARM,ADS。

什么是BREW:

BREW(Binary Runtime Environment for Wireless,无线二进制运行环境)是美国高通公司为无线数据服务提供的一整套端到端的解决方案。通过调用BREW SDK提供的API,开发者可以使用C/C++开发BREW应用。

什么是GNU GCC:

GNU GCC(GNU Compiler Collection,GNU编译器集合)是基于开源项目的广为使用的免费编译器集合,支持C、C++、Objective C、Chill、Fortran和Java等语言。这篇文章中我们讨论使用GNU GCC编译C/C++程序生成ARM目标代码。

关于GNUDE:

GNUDE(GNU Development Environment,GNU开发环境)是基于GNU GCC编译器集合的开发工具套件,集成了支持多种微处理器平台应用开发的模拟器和调试器,目前目标平台支持ARM7、ARM9和XScale,工作环境为Windows NT/2000/XP。

为什么使用GNU GCC:

目前所有支持BREW平台的手持终端都采用基于ARM的处理器,而开发者通常都是使用基于Intel架构的x86 PC作为开发平台,所以需要使用一种交叉编译器来生成ARM平台的目标代码。
有两种交叉编译器可供选择:商业软件ADS(ARM Development Suite)和自由软件GNU GCC。
作为商业软件的ADS比GNU GCC有很多优势:专业的技术支持,目标代码更可靠更优化,等等。可是评估版的ADS只能使用45天,高昂的注册费用令广大中小软件开发商望而却步。因此问题的关键是,免费的GNU GCC能否胜任我们的需要?就目前使用的情况看,答案是肯定的,许多使用GNU GCC编译的BREW应用已经成功运行在各种手机终端上。

所以,让我们开始吧!

准备所需工具包:
gnude-arm-win.zip:在[url=http://sourceforge.net/project/showfiles.php?group_id=54319&release_id=95892]这里[/url]下载最新的GNUDE版本(72M左右)。
GCCSupportForBREW.zip:请发信至dev-cn-support@unicom-brew.com索取。
build_files.tar:请发信至dev-cn-support@unicom-brew.com索取。

安装工具包:
1.解压缩gnude-arm-win.zip至本地,例如c:\gnude\。
2.解压缩GCCSupportForBREW.zip,拷贝GCCResolver.c至<BREW SDK>\src目录(<BREW SDK>为BREW SDK安装路径,下同);拷贝BREWelf2mod.exe至<GNUDE DIR>\bin目录,例如c:\gnude\bin。
3.解压缩build_files.tar,拷贝make.exe和rm.exe至<GNUDE DIR>\目录。

设置环境变量:
控制面板->系统->高级->环境变量->系统变量 将<GNUDE DIR>和<GNUDE DIR>\bin(例如c:\gnude;c:\gnude\bin)加入Path环境变量中。

好了,你的GNU GCC已经准备运行了!

一个实例:

下面我们将以<BREW SDK>\Examples\WhiteBoard为例,演示如何使用GNU GCC编译程序生成ARM目标模块WhiteBoard.mod。
拷贝build_files.tar中的whiteboard-gcc.mak至<BREW SDK>\Examples\WhiteBoard目录,进入DOS窗口,在WhiteBoard所在目录输入:
make –f whiteboard-gcc.mak就会生成WhiteBoard.mod目标文件。

关于makefile的编写是初学者比较头痛的问题,这里简要介绍了一下makefile的写法,并给出了一个经过注释的makefile模板,稍微修改就可以用于任何BREW C/C++应用。

使用make

在开发软件时,开发者必须自行注意有哪些文件需要重新编译,随着项目规模的扩大,这件工作的强度会成几何级数增长,直至不可控制,而make的设计就是为了减轻这个负担,根据一定的规则编写makefile描述需要操作的步骤,make会自动完成相关的文件更新。
对于软件开发人员来讲,需要操作的步骤就是指程序各模块间的依赖关系,make只要知道这种依赖关系就知道什么时候该编译链接哪些文件。
在了解makefile写法之前,我们需要知道自己程序各模块间的依赖关系。图一将这种关系以树状结构图表示,解读的方式是由上往下看。例如我们要产生myprog,就必须要先产生a.o、b.o、c.o三个文件,而要产生a.o则必须先要有a.c和header.h两个文件。
从另一个角度(由下往上看),当a.c被更改时,a.o就“过期”了,必须重新产生以得到最新版本的a.o;而因为a.o被更新了,所以应该重新链接产生myprog。这种连锁反应就是a.c和myprog间的依赖关系。同样的,b.c、c.c与myprog也
都有类似的关系。Make是根据文件的保存时间来判断某个文件是否“过期”了。

                    myprog

                    /  |   \
                   /   |    \
                  /    |     \
                 /     |      \       <-- 编译关系
                /      |       \
             a.o      b.o      c.o
                       |        |
             /  \       |        |    <-- 链接关系
            /    \      |        |
          a.c header.h   b.c      c.c
          (图一)

根据图一写成的makefile如下:
myprog: a.o b.o c.o
<TAB>gcc a.o b.o c.o –o myprog
a.o: a.c header.h
<TAB>gcc –c a.c
b.o: b.c
<TAB>gcc –c b.c
c.o: c.c
<TAB>gcc –c c.c

注:<TAB>表示TAB制表符,下同。

makefile的格式:
1.注释:
在makefile中,任何以“#”开头的文字都被看作注释,make在解释makefile的时候会忽略这些文字。
2.续接下行:
在makefile中,如果一行不足以容纳该命令的时候,可以在该行的后面加一个反斜线“\”表示下一行是本行的延续,两行应视为一行来处理。
3.宏定义:
宏的格式为:<string>=<value>
例如:CFLAGS = -o2 –c –D_DEBUG
make本身已有很多默认的宏,如果要查看这些宏的话,可以用make –p命令。
4.规则:
格式如下:
<Target 1>: <depend>
<TAB><command 1>
<TAB><command 2>
……
<Target 2>: <depend>
<TAB><command 3>
<TAB><command 4>
……

从上面的myprog示例可以看到,其makefile是由4个基本单元组成的,分别描述myprog、a.o、b.o、c.o四个target的depend和command。例如第一个基本单元应该解读成:
myprog这个target是由a.o、b.o、c.o三个depend组成;要产生myprog的方式(command)为
gcc a.o b.o c.o –o myprog
其他三个基本单元类似。

有两点需要注意:
1.一个target可以由不同数目的构成部分,myprog的例子示范了一个到三个的情形,如果用到make更复杂的技巧,也有可能出现零个的情况。
2.command那行必须以<TAB>开头,如果输入了空格而不是<TAB>,执行时会报告“missing separator”错误。

注释过的makefile模板

#修改下一行为你的目标名称
TARGET=   AppName
EXETYPE=elf
BINARY  =bin
MODULE=mod

#---------------------------------------------------------------------------------------
# 在这里加入出$(TARGET).o之外其余的程序模块,可以为多个
#---------------------------------------------------------------------------------------
ADD_OBJS = ModName.o

APP_OBJS =AEEModGen.o AEEAppGen.o GCCResolver.o $(ADD_OBJS) $(TARGET).o

#-----------------------------------------------------------------------
# 定义目标编译时符号
# 指定编译动态模块
#-----------------------------------------------------------------------
DEFINES = -DDYNAMIC_APP

#--------------------------------------------------------------------------------------------
#定义软件工具环境变量
#   GCCHOMEPATH应当设定为你的GNUDE安装路径。
#   ELF2MODTOOLPATH应当设定为BREWelf2mod.exe的安装路径
#--------------------------------------------------------------------------------------------
GCCHOMEPATH  =  c:/gnude
GCCBINPATH = $(GCCHOMEPATH)/bin
ELF2MODTOOLPATH = $(GCCHOMEPATH)/bin

#-------------------------------------------------------------------------
#修改这里来定位你的AEEAppGen.c & AEEModGen.c
#-------------------------------------------------------------------------
AEESRCPATH = ../../src

#-------------------------------------------------------------------------
# 修改这里来定位你的BREW头文件
#-------------------------------------------------------------------------
AEEINCPATH = ../../inc

GCC=$(GCCBINPATH)/arm-elf-gcc.exe
LD=$(GCCBINPATH)/arm-elf-ld.exe
ELF2MODTOOL=$(ELF2MODTOOLPATH)/BREWelf2mod.exe

#-----------------------------------------------------------------------
# 编译器优化选项
#   -O0 取消优化.  
#-----------------------------------------------------------------------
OPT=-O2

END=-mlittle-endian
TARG =-mthumb
CODE =$(END) -fshort-enums -fno-builtin

#-----------------------------------------------------------------------
# 头文件搜索路径
#-----------------------------------------------------------------------
INC=-I$(AEEINCPATH) -I$(GCCHOMEPATH)\lib\gcc-lib\arm-elf\include -I$(GCCHOMEPATH)\arm-elf\include

#-----------------------------------------------------------------------
#库文件搜索路径
#-----------------------------------------------------------------------
LIBDIRS = -L$(GCCHOMEPATH)/lib/gcc-lib/arm-elf/3.3 -L$(GCCHOMEPATH)/arm-elf/lib

#-----------------------------------------------------------------------
# 处理器体系结构选项
# 目前所有BREW手机都使用ARM 7t芯片
#-----------------------------------------------------------------------
CPU=-mcpu=arm7tdmi

#-----------------------------------------------------------------------
# ARM Procedure Call Standard (APCS) options
# -fPIC             sets posititon independent code.  Other option: -fpic
# -mthumb-interwork enables switching between ARM and Thumb code
# -mapcs-frame      runs on systems with the frame ptr. specified in the
# APCS
#-----------------------------------------------------------------------
ROPI=
INTRWK=-mthumb-interwork
APCS=-mapcs-frame $(ROPI) $(INTRWK)

#-----------------------------------------------------------------------
# 编译器输出选项
# -c设置只有目标文件输出
#-----------------------------------------------------------------------
OUT=-c

#-----------------------------------------------------------------------
# 调试信息选项
#   -g 选项保留调试信息
#-----------------------------------------------------------------------
#DBG=-g
DBG=

#-----------------------------------------------------------------------
# 编译开关定义
#-----------------------------------------------------------------------
CFLAGS0 =$(OUT) $(DEFINES) $(CPU) $(APCS) $(CODE) $(DBG)
CFLAGS =$(CFLAGS0) $(INC) $(OPT)

#-----------------------------------------------------------------------
# 链接选项
# -o指定输出文件名称
#-----------------------------------------------------------------------
LINK_CMD  = -Ttext 0 --emit-relocs -entry AEEMod_Load -o
LIBS      = -lgcc -lsupc++ -lc

#-----------------------------------------------------------------------
# 链接开关定义
#-----------------------------------------------------------------------
LDFLAGS =$(LIBDIRS)

#-----------------------------------------------------------------------
# 默认目标
#-----------------------------------------------------------------------
default: $(TARGET).$(MODULE)

#-----------------------------------------------------------------------
# 所有目标
#-----------------------------------------------------------------------
all: $(TARGET).$(MODULE)

$(TARGET).$(MODULE) :   $(TARGET).$(EXETYPE)
$(ELF2MODTOOL) $(TARGET).$(EXETYPE) $(TARGET).$(MODULE)

$(TARGET).$(EXETYPE) :  $(APP_OBJS)
$(LD) $(LINK_CMD) $(TARGET).$(EXETYPE) $(LDFLAGS) \
$(APP_OBJS) $(LIBS) $(LINK_ORDER)

#-----------------------------------------------------------------------
#  清除所有自动创建目标
#-----------------------------------------------------------------------
clean:
rm -f $(APP_OBJS) $(TARGET).$(EXETYPE) $(TARGET).$(MODULE)

#-----------------------------------------------------------------------
#目标文件依赖关系:
#修改这里,加入你的依赖关系。
#-----------------------------------------------------------------------
$(TARGET).o:$(TARGET).c

AEEAppGen.o:
$(GCC) $(CFLAGS) -o AEEAppGen.o $(AEESRCPATH)/AEEAppGen.c
AEEModGen.o:
$(GCC) $(CFLAGS) -o AEEModGen.o $(AEESRCPATH)/AEEModGen.c
GCCResolver.o:
$(GCC) $(CFLAGS) -o GCCResolver.o $(AEESRCPATH)/GCCResolver.c
ModName.o:
$(GCC) $(CFLAGS) -o ModName.o ./ModName.c
AppName.o: ./AppName.c
$(GCC) $(CFLAGS) -o AppName.o ./AppName.c
注意事项:

l在BREW应用程序中不得使用全局或者静态变量。
l目前只支持创建ARM-mode(non-thumb mode)BREW二进制模块。
lMS VC和GNU GCC在实现上有差异,所以有些C/C++程序在模拟器上用MS VC可以编译通过,但是用GNU GCC创建ARM目标代码时编译失败,这时需要根据编译错误提示修改相应的代码段。例如使用GNU GCC编译CPPApp示例,就需要将appclasses.h和appclasses.cpp中operator new的参数从unsigned int更改为long unsigned int方能通过。
l使用GNU GCC编译C++程序时,确认在makefile中libsupc++被链接(LIB=-lgcc –lsupc++ -lc)。

参考链接:

BREW:
联通博路网站:www.unicom-brew.com
BREW开发者网站:http://www.qualcomm.com/brew/sch/developer/developer.html
BREW开发者论坛:http://brewforums.qualcomm.com/
BREW技术支持邮箱:mailto:dev-cn-support@unicom-brew.com

GNU GCC:
主页:http://gcc.gnu.org
make的基本介绍: http://freebsd.ntu.edu.tw/bsd/6/4/2/7/2.html
(本文make写法部分引用自这篇文章)

GNU make手册:http://www.gnu.org/manual/make/html_mono/make.html

ARM:
主页:http://www.arm.com

GNUDE:
主页:http://sourceforge.net/projects/gnude/

cnangel 发表于 2004-3-12 19:04

[分享]使用GNU GCC创建BREW应用

[UploadFile=7_20_1.zip.info]关于make的一些小编译程序

页: [1]

Powered by Discuz! Archiver 7.0.0  © 2001-2009 Comsenz Inc.