The availability of a decent Java VM for Windows Mobile in its various incarnations has been a serious issue for several years. in my blog on On-board Java compiling, I discussed the open-source, but slow and beta-quality, MySaifu VM, and the commercial (but freely provided on HTC and some other WinMo devices) Esmetec Jbed J2ME MIDP2 JVM. In future blogs I will discuss other serious offerings from IBM and Sun. But here I want to show how the results of the PhoneME open-source project can be used to replace both JBed and MySaifu in the on-board tool-chain I have been discussing. The advantages of this substitution are an order of magnitude increase in compilation and verification speeds, an additional J2ME test bed derived from a very different code source, a key new MIDP file access capability, and a completely free and open source tool-chain for those with smartphones or PDAs without a built-in midlet manager.
I will also, along the way and in subsequent postings, demonstrate how Mortscript, which has thus far served only as a application launcher, can serve as the “make” (automated build) for this system. I remind you that I consider the development of J2ME midlets to be the target of this effort, with my (previously stated) reasons being that midlets are the appropriate level of technology for present day Java mobile application delivery. I am interested in commercial return for my software designs, and therefore interested in the 2 billion plus J2ME capable phones. Nevertheless, the process generalizes directly to on-board Foundation and Personal Profile development, which is roughly comparable to Java 1.1 desktop development.
In order to motivate the present discussion, I must admit that I am, as I write this, already working in an environment that has five midlet mangers for testing (two different Jbeds, Sun’s JavaFx, IBM’s J9, PhoneME MIDP) and three Foundation or better JVMs to run the compiler tools (PhoneME Personal Profile, MySaifu, and J9 Foundation). This, mind you, is on-board my Dash — the desktop tools, SDKs, emulators, and associated on-device tools are being ignored but available.
This is a poor-man’s very rich environment for addressing “fragmentation,” i. e., the fact that a midlet runs differently on every device/platform/midlet manager. I mention it here and now, because I need to convince you, dear reader, that you will have to commit to some investment in the chosen scripting technology in order to go with me any further on this “adventure.” This automation tool will be the medium by which the myriad incantations I have discovered will be recorded for you; but to make use of this information in your own projects a working knowledge of Mortscript and access to the documentation, such as it is, will be a requirement. I have converted the Mortscript manual, a PDF file, to both HTML and mobi formats in order to read it conveniently on my smartphone, and have the “commands.txt” file, a brief command summary, at the ready.
The main content of this missive is in fact the script changes needed so that PhoneME Personal Profile can replace MySaifu in our compiling framework. First, we need to install a suitable version of PhoneME, which is available in a number of different profiles and for a variety of Windows Mobile and Pocket PC varients. The work I have been doing utilizes Davy Preuveneers’ WinMo6 build of PhoneME Advanced - Personal Profile. (The features we need for compiling could probably be satisfied by the smaller “headless” (i. e. no graphics or GUI) Foundation Profile, but I dream of getting the AWT of the Personal Profile working some day.) (Davy is busy updating and improving this code base so I’m not going to provide a version number for you here.)
While you are at Davy’s site, be sure to pick up the MIDP version of PhoneME too. This is a valuable midlet manager with speed and additional capabilities beyond those of the Dash’s built-in Jbed, and it appears to approach commercial finish and quality.
I have, through the generosity of a friend (thank you Steve), obtained a working Windows Mobile 2003 2nd Edition PDA (an Ipaq rx3115). Preliminary testing of Davy’s MIDP build for that platform looks good, and there is no doubt that I will (or could) produce a complete toolchain for that device, particularly since both Sun’s Personal Java and several command console utilities are working. I will keep you posted, and may prepare a separate blog entry about what this platform can uniquely do — I have already compiled windowed applications in C#, C++, and Forth using onboard tools that I can not get working on the smartphone.
The PhoneME Personal Profile implementation (which I will refer to as PhPP), in comparison to the MySaifu JVM, has different but very similar command line syntax and characteristics. It can be instructed to write its output and error streams to text files, and is more memory efficient, as well as very much faster.
And it appears to fully support the java-based Kopisusu compiler and the preverifier Proguard, the keystones of my present toolchain.
I want to begin by describing the script subroutine “Compile” which is the code that runs the Kopisusu java compiler using PhPP. A typical implementation looks like this:
Sub Compile
If(FileModifyTime(srcpath&srcfile&".java") > FileModifyTime(srcpath&srcfile&".class"))
Delete("\OUT.txt")
Delete("\ERR.txt")
ShowWaitCursor
RunWait("\Storage Card\Program Files\pMEa PP\bin\cvm.exe",
" -Xopt:stdioPrefix=/ -jar ""\Storage Card\java\ksusu.jar"" --classpath " & classpath &
";" & srcpath & " " & srcpath & srcfile & ".java")
HideWaitCursor
Call "prERR"
Call "prOUT"
Endif
EndSub
The subroutine first checks if the file srcpath/srcfile.class is younger than srcpath/srcfile.java; if so no compilation is necessary. (Here “srcpath,” “srcfile,” and “classpath” are global string variables that must be set before “Compile” is called.) Then the output files “\OUT.txt” and “\ERR.txt” are cleared since we will be checking if anything gets written to these files during the compilation process. The function “ShowWaitCursor” puts the WinMo spinner on the screen to show the human that we’re working on something — otherwise the script doesn’t show anything on the screen and may not appear to be working at all.
Invocation of the compiler comes next; the PhPP executable cvm.exe is run with the kopisusu compiler jar file. The option to cvm.exe “-Xopt:stdioPrefix=/” redirects the JVM’s stdio and error output streams to files OUT.txt and ERR.txt in the specified directory.
The remaining options are for Kopisusu (ksusu.jar). Because Kopisusu has difficulty parsing paths that have embedded spaces, I typically keep the script and source files in a directory “\src” (rather than on my “\Storage Card” — and if I find the idiots who came up with that name and “Program Files” well then it really will be Jihad). (Why is Microsoft so stupid so often?).
We finish up by turning the “wait” spinner off, and then printing the error and output files via other subroutines which check that there is indeed something to print. They look like this:
Sub prOUT
If(FileExists("\OUT.txt"))
output = ReadFile("\OUT.txt")
If(Length(output) > 0)
Message(output,"output")
Endif
Endif
EndSub
Sub prERR
If(FileExists("\ERR.txt"))
errors = ReadFile("\ERR.txt")
If(Length(errors) > 0)
Message(errors,"errors")
Endif
EndIf
EndSub
These subroutines accomplish single file compilation, and thus require that classes referenced by other classes be compiled first. This will fail when a set of classes mutually reference each other (a circular reference); in that case the set of java files must be compiled together. I have been too lazy thus far to generalize the “Compile” subroutine to handle this situation, prefering to hack the “srcfile” string on an individual basis. I also think that there is no purpose to the usual java package usage in MIDP developement since there is no real third party library access. But if compiling a project that does involve a package hierarchy, an appropriately augmented “classpath” variable can be used with the –classpath to point to the root of that hierarchy. The “classpath” variable would normally just reference the (stubbed) library jar files, e. g.,
classpath = “\cldc_1.1.jar;\midp_2.0.jar”
Once all the source files have been compiled, a “Preverify” subroutine translates all of the class files into the preverified form. This routine looks like this:
Sub Preverify
Delete("\OUT.txt")
Delete("\ERR.txt")
ShowWaitCursor
RunWait("\Storage Card\Program Files\pMEa PP\bin\cvm.exe", " -Xopt:stdioPrefix=/
-jar ""\Storage Card\java\proguard\proguard.jar"" -dontshrink -dontoptimize -dontobfuscate
-microedition -libraryjars " & classpath & " -injars " & srcpath & "(**.class,**.png)
-outjars " & verifiedpath)
HideWaitCursor
Call "prERR"
Call "prOUT"
EndSub
Concentrating on the RunWait command (the other cruft having been discussed above), we see that we can specify the input files as a directory (specified by the option “injars”) together with a filter “(**.class,**.png)” which in this case means preverify the class files and move both class and png files to the output directory.
Proguard could probably be prevented from running in the case where no compilation or preverification is needed but it will detect when no work needs to be done — and you shouldn’t be running the script them anyway. We’ll talk about the other things Proguard can do another time.
Finally we run the finished MIDlet via a “RunJava” subroutine which executes the JBed midlet manager with command line options; the variables “runpath,” “runfile” and “dotrun” specify the classpath, main class (extends MIDlet), and package hierarchy to main class, if there is one:
Sub RunJava
If(FileExists(runpath & runfile & ".class"))
Delete("\OUT.txt")
Delete("\ERR.txt")
RunWait(runVM, runOpts&" -classpath " & runpath & " " & dotrun)
Endif
Call "prOUT"
Call "prERR"
EndSub
These subroutines are combined in a single file with a prelude that specifies the source files; here is an example where MIDlet class “ManyBalls” depends on class “ManyCanvas” which depends on class “SmallBall”:
# compiling and execution subroutines
pkg = ""
srcfile = pkg & "SmallBall"
srcpath = "\src\"
classpath = "\cldc_1.1.jar;\midp_2.0.jar"
runVM = "\Windows\jeodek.exe"
runOpts = ""
Call Compile
srcfile = pkg & "ManyCanvas"
Call Compile
srcfile = pkg & "ManyBalls"
Call Compile
verifiedpath = "\preverified\"
Call Preverify
runpath = verifiedpath
dotrun = "ManyBalls"
runfile = pkg & "ManyBalls"
Call RunJava
Exit
So that’s it for the substitution of PhoneME Personal Profile for MySaifu and the next degree of automation. Stay tuned to this channel for the final steps of the MIDlet development process — the final packaging of MIDlet suites into jad /jar file pairs for distribution and final testing. I hope to also show you where to get other free midlet managers for testing, and other useful compiler technology.