当我们开发嵌入式Linux软件时,一般都要经过交叉编译这一步。如果是开发PC机上的软件,我们只要用Linux本身提供的强大的编译器(如gcc)直接编译就可以了。而一般的嵌入式设备所用的CPU和PC机上的CPU有着不同的架构,这样就要求我们在PC机上编译出适用于嵌入式设备CPU架构的软件,这就是我们所说的交叉编译。下面我们用一个例子来详细说明交叉编译是如何工作的。
一、 所用平台
Redhat 9.0
二、 建立交叉编译环境
很显然,要交叉编译就得有一个特殊的“环境”作前提,这里的环境就是一个与PC机不同的一套库函数和编译器。用这样的库函数和编译器编译出来的应用程序就可以在嵌入式设备上跑了。Sitsang板提供的交叉编译工具链为:gnupro-bin-glibc2.2.4.tgz,xscale-arm-linux-toolchain.tgz:
cd / //回根目录
tar zxvf xscale-arm-linux-toolchain.tgz //解压,解压后文件在/usr/local/下面
echo “export PATH=/usr/local/arm-linux/bin:${PATH}” >>~/.bashrc //设置环境变量
source ~/.bashrc
cd /usr //进入/usr文件夹,为了把glibc库安装在该目录下
tar zxvf /tmp/gnupro-bin-glibc2.2.4.tgz //解压
这样我们的交叉编译环境就建立好了,怎么样,是不是很简单。下面我们就来测试一个简单的例子。
/*Hello.c*/
#include
main()
{
char *hello=”hello!/n”;
printf(“%s”,hello);
}
程序输好以后确认无误,保存。进入程序文件所在目录比如在/root/hello/下面:
cd /root/hello
arm-linux-gcc hello.c –o hello(-o 可以理解为“目标为生成”)
第一行改不用解释了吧,第二行中arm-linux-gcc是第一次出现,有人可能会问这个哪里来的,不妨打开刚才安装的交叉编译工具目录/usr/local/arm-linux/arm-linux/bin/可以发现里面有一个arm-linux-gcc文件,这个就是针对arm的CPU的gcc编译器了。以后用其它编译工具链式也可以通过这种方法看看其编译器是什么了。编译好了以后就可以下载到目标机进行测试了。当然也可以先在PC机上测试正误。用gcc
hello.c –o hello就可以生成PC机上程序了,在运行./hello 就可以发现终端显示hello!字样。
三、 提高篇
前面我们编译了一个小例子,当然这个例子只有一个hello!显示,没有任何其他的人机交互,所以当然也就不过瘾了。
开发嵌入式Linux的交互界面可以有许多开发环境。我们这里选用Qt/embedded作为界面开发平台。首先当然是编译Qt/embedded了。所用的软件包为:qt-embedded-2.3.2.tar.gz。不过我们的开发板带有一块触摸屏,为了让用Qr/embedded做出来的程序能够支持触摸屏,必须要把触摸屏的库进行同时编译,开发板提供的触摸屏库为tslib.tar,下面开始编译(设当前路径为/opt,两个软件包也放在这个路径下):
1.TSLIB
bzip2 -dc tslib.tar.bz2 | tar xv //解压,生成的文件夹名为tslib,具体bzip2的用法可以用man bzip2来查看
cd tslib //进入该文件夹
export CC=arm-linux-gcc
//用CC这个符号代替arm-linux-gcc,有了这一句后,我们前面编译时用arm-linux-gcc
就可以用CC来代替了
./autogen.sh --host=arm-linux
//这一句表示将编译适合arm架构cpu的库,其实打开autogen.sh这个文件,我们将发现这是一个批处理的shell文件其中—host=arm-linux是./configure的参数。其中内容如下:
#!/bin/sh
# $Id: autogen.sh,v 1.1.1.1 2001/12/22 21:12:06 rmk Exp $
libtoolize --force --copy
aclocal
autoheader
automake --add-missing --copy
autoconf
./configure $*
具体每条的含义是:
make //当然是编译啦,具体说是对前面configure好以后的配置进行编译
2.安装 Qt/Embedded...
tar xfz qt-embedded-2.3.2.tar.gz //解压,生成qt-2.3.2文件夹,具体tar的用法也可以用man tar来查看
mv qt-2.3.2 qte //改文件夹名,完全是为了简单,没什么实际意义。可以不用
cd qte //进入文件夹
export QTDIR=$PWD //设置QTDIR替代当前目录,$PWD可以取得当前目录
export PATH=$QTDIR/bin:$PATH
//添加路径到环境变量,以便以后运行命令是可以从这个路径找
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH //添加库文件路径
cp –a ../tslib/src/.libs/* ../qte/lib/
//把触摸屏库.libs文件夹中库文件拷贝到qte的库文件夹中等待一起编译
cp -a ../tslib/plugins/.libs/*.so ../qte/lib/
//同上
./configure -xplatform linux-arm-g++
//配置,-xplatform参数表示将编译面向arm平台的程序
make //编译
cd .. //退出
1.编译TMAKE
tar xfz tmake-1.11.tar.gz //解压
cd tmake-1.11 //进入目录
export TMAKEDIR=$PWD //用TMAKEDIR替代当前路径,也就是tmake的安装路径
export TMAKEPATH=$TMAKEDIR/lib/qws/linux-arm-g++
//设置这个环境变量是说明用tmake时调用linux-arm-g++也就是适合arm架构的C++编译器
export PATH=$TMAKEDIR/bin:$PATH
//设置tmake的工具路径(里面有progen,tmake,tmake-win三个工具,下面会有介绍)
经过以上几个编译后我们来回顾一下:首先,触摸屏的库有了,也编译好了;其次,也有了设计交互界面的平台Qt/embedded,并且把触摸屏的库也编译进去了,也就是说用这个平台做的界面编译后可以支持触摸屏;最后,编译面向arm架构的强大的tmake编译器也有了,可谓万事俱备,只欠东风了。下面我们就来试试我们的环境好不好用(注意编译以上三个软件包都应该在同一个终端中,后面会有解释):
/*a simple interface*/
1 #include
2 #include
3 int main(int argc, char *argv[])
4 {
5 QApplication app (argc, argv);
6 QLabel *label = new QLabel("Hello Qt!", 0);
7 app.setMainWidget(label);
8 label->show();
9 return app.exec();
10 }
程序这里就不解释了,大家可参阅C++GUI Programming with
Qt3。这里我们只说编译方法。首先,我们可以用qmake来编译一个可以在PC机上运行的程序,这时先把这个文件存在一个叫hello文件夹(也可以不用,不过这样方便)中,名字为hello.cpp,然后再命令行中切换到这个目录中
qmake –project
//以当前文件夹名生成.pro文件(如文件夹名为xxx,则生成xxx.pro文件)
qmake hello.pro //生成Makefile文件
make //编译
一会儿我们既可以编译出来了。命令行切换到该目录,打入./hello运行可见:
{这里本来会显示一个图片的}
现在我们开始用我们刚才搭建的交叉编译环境编译一下看看,前提是刚才编译环境的终端还没有关闭。那么,命令行直接切换到hello目录下:
progen –n hello –o hello.pro //生成.pro文件
tmake hello.pro –o Makefile //生成Makefile文件
make //编译
在第一句完成以后有一处小改动,打开hello.pro文件,其内容如下:
TEMPLATE = app
CONFIG = qt warn_on release
HEADERS =
LIBS += -lts //加上这句
SOURCES = hello.cpp
INTERFACES =
TARGET = hello
加上那一句是因为触摸屏的库在“另一个地方”,所以要指定路径给编译程序进行链接编译。这样,适用于arm架构CPU的第一个程序就编译好了。下载到目标板运行就可以知道结果了。
同时也可以改Makefile文件,其里面的内容为:
#############################################################################
# Makefile for building hello
# Generated by tmake at 20:35, 2004/08/10
# Project: hello
# Template: app
#############################################################################
####### Compiler, tools and options
CC = arm-linux-gcc
CXX = arm-linux-g++
CFLAGS = -pipe -Wall -W -O2 -DNO_DEBUG
CXXFLAGS= -pipe -DQWS -fno-exceptions -fno-rtti -Wall -W -O2 -DNO_DEBUG
INCPATH = -I$(QTDIR)/include
LINK = arm-linux-gcc
LFLAGS =
LIBS = $(SUBLIBS) -L$(QTDIR)/lib -lm -lqte –lts ////////这里就是要加的东西
MOC = $(QTDIR)/bin/moc
UIC = $(QTDIR)/bin/uic
TAR = tar -cf
GZIP = gzip -9f
####### Files //后面省略。。。。。。。。。。。。
回到刚才,我们指明了一定不要关闭编译Qt和tmake的终端,是因为编译时设置的环境变量只是暂时设置(除了设置工具链那一句设置环境变量一句),且只对当前终端有效,而我们编译程序时就要用到这些“环境变量”,关了终端以后这些环境变量就没了。所以不能关。但话又说回来,总不能永远不关吧,所以我们要设置一下用户环境变量。新打开一个终端,输入:vi
.bashrc打开环境变量文件,进入编辑模式,在最后加上下面几句:
export QTDIR=/home/qte
export TMAKEPATH=/home/tmake-1.11/lib/qws/linux-arm-g++
export PATH=/home/qte/bin:/home/tmake-1.11/bin:$PATH
export LD_LIBRARY_PATH=/home/qte/lib:$LD_LIBRARY_PATH
其实说白了就是一句话,那就是把刚才编译几个软件的时候所设的环境变量写入用户的环境变量中。
好了,说得罗罗嗦嗦,也不知道能不能让初学者“完全通”。