在树莓派里编译CLDC1.1

由于最近想玩j2me的游戏,找到一个模拟器,但是无法联网,所以就想着去编译一下CLDC,看看有什么收获,没想到一搞就是一个星期!下面记录我的辛苦历程,以备后人参考。


【前期准备】:

1、j2me_cldc-1_1-fcs-src-winunix.zip,顾名思义,CLDC1.1的源码;

2、JDK7以下,由于我安装的树莓派是最新的,系统默认自带JDK8,所以需要删除JDK8,再去安装JDK7。步骤如下:

sudo apt-get remove oracle-java8-jdk
sudo apt-get install oracle-java7-jdk

为什么要安装JDK7以下的版本呢,因为编译过程中会生成jcc工具,jcc里面使用了“sun.misc.Compare”和“sun.misc.Sort”这两个Api类,恰巧这个两个类在JDK8以后已经被移除,导致编译过程出错,因此需要安装JDK7以下的版本才能顺利编译;

3、GCC 4.9.3版本,这个使用的是系统自带的,跟版本关系不大,虽然会有点问题,但可以通过修改源码得到解决,后面会提到;


【正文】

首先解压文件:j2me_cldc-1_1-fcs-src-winunix.zip,我把它解压到桌面,并把文件夹命名“j2me_cldc_linux”。注意:我之后的所有操作,都使用此文件目录“/home/pi/Desktop/j2me_cldc_linux/”。

然后直接到build/linux目录下,直接make,因为我的树莓派系统是基于Linux的,所以直接使用linux编译

cd /home/pi/Desktop/j2me_cldc_linux/build/linux
make

编译到api的目录时,如果JDK版本大于等于5的话,就会报错

src/java/lang/Object.java:132: error: cannot access StringBuilder
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
                                          ^
  class file for java.lang.StringBuilder not found


这是应为Java在使用字符串拼接时,JDK5.0版本以上会使用StringBuilder的方式,但是CLDC1.1里面并没有这个,因此需要指定JDK版本源。

解决方法:进入api目录,修改makefile,将 “JAVAC     = javac” 修改为 “JAVAC     = javac -source 1.4” 后,重新make。

接下来编译到tools/jcc工具时,如果JDK版本大于等于5的话,同样会报错

src/util/ClassReader.java:85: error: as of release 5, 'enum' is a keyword, and may not be used as an identifier

        Enumeration enum = zf.entries();

                    ^

  (use -source 1.4 or lower to use 'enum' as an identifier)

src/util/ClassReader.java:86: error: as of release 5, 'enum' is a keyword, and may not be used as an identifier

        while (enum.hasMoreElements()) {

               ^

  (use -source 1.4 or lower to use 'enum' as an identifier)

src/util/ClassReader.java:87: error: as of release 5, 'enum' is a keyword, and may not be used as an identifier

            ZipEntry ent = (ZipEntry)enum.nextElement();

                                     ^

  (use -source 1.4 or lower to use 'enum' as an identifier)


解决方法跟上面的一样,进入到tools/jcc,修改makefile,将 “JAVAC     = javac” 修改为 “JAVAC     = javac -source 1.4” ,继续make。

之前的错误没有,但如果系统的语言设置为中文或英文,并且设置字符集为UTF-8的话,就会报错

src/com/sun/cldc/util/j2me/CalendarImpl.java:2: 警告: 编码UTF8的不可映射字符

 * Copyright  2003 ? Sun Microsystems, Inc. All rights reserved.

......

并且即使通过修改所有的源码,将特殊字符去掉,后续还会出现

preverify -d classes tmpclasses

Segmentation fault

解决方法:将系统语言设置为英文,并且字符集改为en_US.ISO8859-1。以我的树莓派系统为例,点击左上角树莓派图标,首选项->Raspberry Pi Configuration->Localisation->Locale栏目,将Language改为en(English),Country选择US(United States),Character 选择ISO-8859-1,设置完后重启电脑。

PS: 网上还有一个解决方法,就是在终端运行

export LANG=en_US.ISO8859-1

不过这个方法对在我这没用,各位可以试试。

继续回到build/linux目录下,make,好吧,等了一会,又报错了,看看

../../../kvm/VmUnix/src/runtime_md.c: In function 'InitializeFloatingPoint':

../../../kvm/VmUnix/src/runtime_md.c:171:41: error: '_FPU_EXTENDED' undeclared (first use in this function)

     fpu_control_t cw = (_FPU_DEFAULT & ~_FPU_EXTENDED) | _FPU_DOUBLE;

                                         ^~~~~~~~~~~~~

../../../kvm/VmUnix/src/runtime_md.c:171:41: note: each undeclared identifier is reported only once for each function it appears in

../../../kvm/VmUnix/src/runtime_md.c:171:58: error: '_FPU_DOUBLE' undeclared (first use in this function)

     fpu_control_t cw = (_FPU_DEFAULT & ~_FPU_EXTENDED) | _FPU_DOUBLE;

                                                          ^~~~~~~~~~~

解决方法,到kvm/VmUnix/src目录下,修改runtime_md.c源码,将InitializeFloatingPoint方法里的那两句注释掉,因为系统不支持,继续编译。

到了这步,已经接近尾声了,如果你的GCC版本是大于4.0的,很遗憾,此时又会报了一个错误,错误就不贴出来了,是关于“Vfy_verifyMethod”和“Vfy_checkNewInstructions”,这个两个方法的。由于GCC 4.0以上版本不支持将一个静态函数的预先定义直接放到另一个函数的函数体内部。因此,需要修改/kvm/VmCommon/src/verifierUtil.c文件,将两个静态函数function 'Vfy_verifyMethod'和function 'Vfy_checkNewInstructions'的预先定义移出到所在函数体的前面。

修改完后,继续编译,此时所有的东西都编译完了,进入链接阶段。不幸的是又有个错误:

obj/ROMjavaUnix.o:(.rodata+0x6988): undefined reference to `Java_com_sun_cldc_io_GeneralBase_iowait'

网上说的需要做GeneralBase.ioWait()到Waiter.waitForIO()的映射,在目录api/src/com/sun/cldc/io里,GeneralBase.ioWait()是CLDC1.04里面的,在CLDC1.1里面对应的是Waiter.waitForIO(),如果你的目录下没有GeneralBase文件,则将CLDC1.04的api/src/com/sun/cldc/io里面的GeneralBase文件copy到1.1相应的目录里,再到CLDC1.1 api/src/java/io目录下,将DateInputStream和DateOutputStream两个类里面将方法 writeFloat、writeDouble、readFloat、readDouble四个方法copy到api/src/com/sun/cldc/io目录下GeneralBase里面,并将GeneralBase的 ioWait()这个native方法删除。

修改完后,先make clean,再make,一系列编译链接后,会在kvm/VmUnix/build目录下生成一个KVM文件。大功告成!!!

备注:记得每次遇到错误后,重新编译需要进行一次

make clean


整个过程虽然看似简单,实际上花了我很多时间,期间还绕了几次弯路,因此写个文章,以备日后查阅。


猜你喜欢

转载自blog.csdn.net/klc12345678/article/details/80938092