如何用 Linux 開發 Palm 程式

Goldencat ruili@worldnet.att.net

Palm

是麼是 Palm 大家一定知道.現在越來越多的人投入到 Palm 程式開發的行列中來.今天,就讓我們自己也來體會一下如 和在 Linux 上開發 Palm 的程式吧.

為什麼是 Linux

原因很簡單,因為 Linux 是免費的.因為 Linux 下的很 多工具也都是免費的. 同時, Linux 本身就是一個最好的程式開 發平台.

在 Linux 中寫 Palm 程式,除了需要一個可以正常工作的 Linux 環境以外,我們還需要以下的東西:

這裡用於測試的系統是 RedHat 7.3

安裝 Palm SDK

首先 tar vxfz sdk50.tar.gz 這時侯會出現一個 palmos-sdk 的 rpm 文件. 用 rpm -ivh 給安裝上去就好. SDK 會被安裝在 /opt/palmdev/sdk-5 中. 我們現在需要做一個 symbolic link, 把 sdk-5 link 到 sdk 上面去.

cd /opt/palmdev
ln -s sdk-5 sdk
現在我們已經安裝好了 Palm SDK 了. 接下來就該安裝開發所需要的環境 了.

安裝 PRC-Tools 以及 cross compile 所需要的工具

收先我們建立一個新的目錄
mkdir /tmp/prc
然後把 prc-tools-2.0.92.tar.gz binutils-2.9.1.tar.bz2 gcc-2.95.3.tar.bz2 gdb-5.0.tar.bz2 make-3.77.tar.gz 都 copy 過去.

cp prc-tools-2.0.92.tar.gz /tmp/prc
cp binutils-2.9.1.tar.bz2 /tmp/prc
cp gcc-2.95.3.tar.bz2 /tmp/prc
cp gdb-5.0.tar.bz2 /tmp/prc
cp make-3.77.tar.gz /tmp/prc
然後 cd 到 /tmp/prc 目錄下. 執行解壓縮的動作:

收先把所有 .gz 的解壓縮

for i in *.gz; do tar vxfz $i; done 

接下來是一些用 bzip2 壓縮的 .bz2 文件

for i in *.bz2; do tar vxfj $i; done

prc-tools 為以上的這些程式,有做一些改動,所以我們需要 把上面的程式碼 patch 一下:

cat prc-tools-2.0.92/*.palmos.diff | patch -p0
現在我們進入 prc-tools-2.0.92 的目錄,首先我們需要做的是 把 binutils-2.91, gdb-5.0, gcc-2.95.3 跟 make-3.77 這幾 個目錄用 symbolic link 分別 link 到 prc-tools-2.0.92 這 個目錄中.

cd prc-tools-2.0.92
ln -s ../binutils-2.9.1 binutils
ln -s ../gcc-2.95.3 gcc
ln -s ../gdb-5.0 gdb
ln -s ../make-3.77 make
為了保持我們程式源碼目錄的整潔與完整性,所以我們在編譯的時侯, 單另建立一個目錄 build:

cd ..
mkdir build
現在我們只要跑 prc-tools-2.0.92 中的那個 configure 就好了

../prc-tools-2.0.92/configure --target=m68k-palmos --enable-languages=c,c++
這裡面我們分別傳給 configure 兩個參數, 一個是告訴我們的 target 為 m68k-palmos 另一個是 enable C/C++ 這兩種語言. prc-tools 中的這只 configure script 會幫我們 把 binutils, gcc, gdb, make 這四個程式所需要的 Makefile 也同時幫我們建立好. 現在在我們的 build 目錄中,就有了以下這些東西:

binutils      config.log     crt  gcc  include  libm  Makefile
config.cache  config.status  doc  gdb  libc     make  tools


現在我們只要用 make all-install 就好了. (如果您不是用 root 帳號, 不要忘記 su 成 root)

make all-install 
因為需要 compile 的時間滿長的.如果您的系統中有超過一顆的 CPU 記得在 make 後面加上 -j number 的選項,其中 number 的 value 就是您 CPU 的數量 加一. 例如您有兩顆 CPU 就可以用 make -j3 all-install, 如果有三顆就用 make -j4 all-install. 如果您的機器又很大的 RAM (好幾 GB)然後也有個 十幾顆的 CPU, 那麼也可以用 make -j all-install 來編譯.如果 -j 後面沒 有跟任何數字,那麼 make 就不會對 jobs 做任何的限制. 詳細的情況請自行 man make 看一下.

在我們編譯的過程中,還有己個小小的問題需要解決一下(至少這個問題存在 於 RedHat 版本的系統中) 首先遇到的問題是, 在 gdb 的編譯中有一個錯誤:

In file included from /usr/include/curses.h:111,
                 from ../../../prc-tools-2.0.92/gdb/gdb/utils.c:28:
/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdbool.h:39: conflicting types for `false'
../bfd/bfd.h:101: previous declaration of `false'
/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdbool.h:41: conflicting types for `true'
../bfd/bfd.h:101: previous declaration of `true'

我們這裡看到在 gdb/gdb.utils.c 中的第 28 行有 include curses.h 這個 文件.而 curses.h 中的第 111 行又 include 了 stdbool.h, stdbool.h 中 定義了 false, 而 bfd/bfd.h 中同時也定義了 false, 這樣兩個 false 就衝 突了.所以造成編譯失敗.這裡用最簡單也是最偷懶的作法,打開

/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdbool.h
把:
typedef enum
  {
    false = 0,
    true = 1
  } _Bool;

這己行給暫時 mark 起來

/*
typedef enum
  {
    false = 0,
    true = 1
  } _Bool;
*/

然後再重新 

make all-install

現在我們會遇到第二個問題:

gcc -DEXEC_PREFIX=\"/usr/local\" -I. -I../../prc-tools-2.0.92/tools/../binutils/include -I../binutils/bfd -g -O2  -o def.yy.o -c def.yy.c
../../prc-tools-2.0.92/tools/def.l:35:19: utils.h: 沒有此一檔案或目錄
../../prc-tools-2.0.92/tools/def.l:37:23: pfdheader.h: 沒有此一檔案或目錄


找不到 utils.h 跟 pfdheader.h 這個很容易解決,把 prc-tools-2.0.92/tools 下面 這兩個文件複製到 build/tools 下面就好了,這裡我們把全部的東西都給複製過去, 不然等會還會繼續說找不到 utils.h 的文件

cp ../prc-tools-2.0.92/tools/* tools/

然後繼續 make all-install

接下來又會有一個錯誤中斷編譯:

gcc -DEXEC_PREFIX=\"/usr/local\" -I. -I../../prc-tools-2.0.92/tools/../binutils/include -I../binutils/bfd -g -O2  -o def.yy.o -c def.yy.c
../../prc-tools-2.0.92/tools/def.l:39: conflicting types for `dup'
/usr/include/unistd.h:441: previous declaration of `dup'

這裡是說,在 def.l 中第 39 行定義的 dup 已經在 /usr/include/unistd.h 中的 第 441 行定義過了.所以產生了衝突.


還是用最簡單的方法來解決:
首先要做的是

vim tools/def.yy.c
把第十二行的 #include 改成 #include "unistd.h" 這樣編譯 def.yy.c 的時侯,就不會去找 /usr/include/unistd.h 這個文件,而是找 tools 下面的 unistd.h

接下來

cp /usr/include/unistd.h tools/

把 unistd.h 複製到 tools 下面,再把 unistd.h 改動一下

最後 

vim tools/unistd.h

把第 441 行的 extern int dup (int __fd) __THROW;
給 mark 起來
// extern int dup (int __fd) __THROW;
存檔後重新 make all-install 就再沒有問題了. 這時侯,在您的 /usr/local/bin 下面就已經有了我們 做 cross compile 所需要的全部工具了.

PS. 不要忘記把了把 /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdbool.h 改回原貌喔.

安裝 pilrc

現在到了最後一步,安裝 pilrc

首先把 pilrc-2.9.tar.gz 複製到 /tmp 中

cp pilrc-2.9.tar.gz /tmp

然後解壓縮:

tar vxfz pilrc-2.9.tar.gz
pilrc-2.9 這個版本,有一點小問題. 如果直接編譯,那麼 pilrcui ( resource viewr )則沒有辦法正常工作.遺憾 的是, pilrc 的官方網站並沒有放出 patch 文件.所以就需 要自己動手改一下了.這裡我們需要改兩個文件. 一個是 main.c 另外一個是 pilrc.c

收先打開 main.c 在倒數第三行會看到

ParseFile(szInputFile, szOutputPath, szResFile, szIncFile, fontType);

把這行刪除不要,改成下面的這行, 我們要用 FreeRcpfile 來叫 ParseFile

FreeRcpfile(ParseFile(szInputFile, szOutputPath, szResFile, szIncFile, fontType));

然後存檔離開.再打開 pilrc.c 把倒數第五行的
FreeRcpfile(prcpfile);
給 mark 起來
//FreeRcpfile(prcpfile);
這行我們不要.

現在就可以執行 ./configure 程式了, 然後執行 
make 
make install

現在我們就已經在 linux 下安裝好了一個 palm 的開發環境了.

最後的測試

現在為了測試我們的 palm 開發環境是否一切都真的工作了, 就讓我們寫一個最簡單的 hello word 來測試一下吧 我從電腦中找了個 hello 的 palm 程式出來.忘記是哪裡的 一個範例了,借來用用.

hello.rpc:

#include "hello.h"
VERSION ID 1 "1.00"
MENU ID MainMenu
BEGIN
        PULLDOWN "Help"
        BEGIN
                MENUITEM "About..." ID HelpMenuAbout
        END
END
FORM HelloForm AT (0 0 160 160)
        MENUID MainMenu
        USABLE
BEGIN
        LABEL "Hello, world!" AUTOID AT (CENTER 50)
END
ALERT ID AboutAlert
BEGIN
        TITLE "About..."
        MESSAGE "Hello, world! demo app\nVersion 1.00\n\nCopyright \251 2003\nStudy-Area.\n"
        BUTTONS "OK"
END

hello.h:

#define MainMenu 1000

#define HelpMenuAbout 1010

#define HelloForm 1100

#define AboutAlert 2000

hello.c:

#include <PalmOS.h>
#include <PalmUtils.h>
#include "hello.h"
static FormPtr gpForm;
static Err StartApplication() 
{
	FrmGotoForm(HelloForm);
	return 0;
}
static void StopApplication() 
{
	FrmCloseAllForms();
}
Boolean HelloFormEventHandler(EventPtr event) 
{
	Boolean handled = false;

	switch (event->eType) {
		case frmOpenEvent: {
			gpForm = FrmGetActiveForm();
			FrmDrawForm(gpForm);
			handled = true;
			break;
		}

		case frmCloseEvent:
			FrmEraseForm(gpForm);
			FrmDeleteForm(gpForm);
			gpForm = 0;
			handled = true;
			break;

		default:
			break;
		}

	return handled;
}
static Boolean ApplicationEventHandler(EventPtr event) 
{
	Boolean handled = false;

	switch (event->eType) {
		case frmLoadEvent: {
			FormPtr pForm = FrmInitForm(event->data.frmLoad.formID);
			FrmSetActiveForm(pForm);
			FrmSetEventHandler(pForm, HelloFormEventHandler);
			handled = true;
			break;
	   			   }
		case menuEvent:
	   			   switch (event->data.menu.itemID) {
		   			   case HelpMenuAbout:
			   			   FrmAlert(AboutAlert);
						   break;
				   }
				   handled = true;
				   break;
		default:
		break;
	}

	return handled;
}
static void EventLoop() 
{
	EventType event;
	UInt16 error;
	do {
	EvtGetEvent(&event, evtWaitForever);

		if (!SysHandleEvent(&event)) {
		if (!MenuHandleEvent(0, &event, &error)) {
		if (!ApplicationEventHandler(&event)) {
			FrmDispatchEvent(&event);
				}
			}
		}
	}
	while (event.eType != appStopEvent);
}
UInt32 PilotMain(UInt16 launchCode, MemPtr cmdPBP, UInt16 launchFlags) 
{
Err err = 0;

	if (launchCode == sysAppLaunchCmdNormalLaunch) {
		if ((err = StartApplication()) == 0) {
		EventLoop();
		StopApplication();
		}
		}

	return err;
}

最後寫一個 Makefile:

PROGRAM=hello
CC=m68k-palmos-gcc
PILRC=pilrc
OBJRES=m68k-palmos-obj-res
ICONTEXT='hello'
BUILDPRC=build-prc
APID=TEST
CFLAGS=-palmos -Wall -g
LFLAGS=-g

SOURCES=hello.c
OBJS=hello.o
PRC=$(PROGRAM).prc
RESOURCES=$(PROGRAM).rcp

all: $(PRC)

$(PRC): $(PROGRAM) bin.res
        $(BUILDPRC) $(PRC) $(ICONTEXT) $(APID) *.bin *.grc

$(PROGRAM): $(OBJS)
        $(CC) -o $(PROGRAM) $(OBJS) $(LFLAGS)
        $(OBJRES) $(PROGRAM)

bin.res: $(RESOURCES) hello.h icon.bmp
        $(PILRC) $(RESOURCES)
        touch bin.res

ctags:
        ctags $(SOURCES)

clean:
        rm -f *.bak *.bin *.grc *.o bin.res tags $(PRC) $(PROGRAM)

hello.o: hello.c hello.h

然後我們打 make 最後看到成功了:
m68k-palmos-gcc -palmos -Wall -g   -c -o hello.o hello.c
m68k-palmos-gcc -o hello hello.o -g
m68k-palmos-obj-res hello
pilrc hello.rcp
PilRC v2.9 patch release 2
  Copyright 1997-1999 Wes Cherry   (wesc@ricochet.net)
  Copyright 2000-2001 Aaron Ardiri (aaron@ardiri.com)

Generating 68K resources from 'hello.rcp'.
Writing tver0001.bin
5 bytes
Writing MBAR03e8.bin
86 bytes
Writing tFRM044c.bin
102 bytes
Writing Talt07d0.bin
90 bytes
touch bin.res
build-prc hello.prc 'hello' TEST *.bin *.grc

好啦,現在一切都 ok 了.如果您也同樣的 make 成功.那麼恭喜您,您的 linux 中 已經有了一個可以寫 palm 程式的環境了.