Saturday, January 06, 2007

Building a server executable

Part of the Cohatoe architecture is the Cohatoe server. On the Eclipse side it is represented by a singleton object (of class de.leiffrenzel.cohatoe.server.core.CohatoeServer), where clients can access it. The server runs in a separate process, and the singleton on the Java side communicates with it via a socket connection. It is implemented in Haskell, and it is able to locate object code that has been contributed via Cohatoe, load and execute it (using hs-plugins).

That the server is implemented in Haskell means also that it has to be compiled into a native executable, which is of course a different one for each supported OS/WS platform. Currently, that is only Windows (strictly speaking: only 32 bit Windows). But the mechanisms for supporting other platforms are there (mostly provided by Eclipse already), and the Haskell code for the server does not make use of platform specifics (as far as I can see). Cohatoe will automatically locate the correct executable for the platform it runs on (if one is there). Therefore, the only thing that is needed is to build the executable for all the other platforms. In what follows, I'll describe how to do this and explain a few background details on the way.

The Haskell source code for the Cohatoe server is located in the plugin de.leiffrenzel.cohatoe.server.core, in the directory INTERNAL/hs/src/. The native executables for the various platforms (i.e. Windows, Linux/GTK, MacOSX etc.) do not reside in the same plugin. Instead, they are contained in fragments. Fragments are a special concept in Eclipse's plugin architecture. (They have also recently become part of the OSGi specification, which is implemented by the Eclipse runtime.)

A fragment is, like a plugin, an OSGi bundle. In the workspace, it lives as a fragment project (instead of as a plugin project), and it contains a fragment.xml instead of a plugin.xml. But they have a similar directory structure, and they have the usual META-INF/MANIFEST.MF file where the meta information is located. When deployed, a fragment looks exactly like a plugin. It (usually) lives in a jar file in the plugins/ folder.

The specific thing about fragments is that they do not exist on their own - they are dependent on a plugin, which is called their host plugin. They have to declare which plugin their host is by the host's plugin id and version. As an example, here is how this looks like for the fragment with the Cohatoe server executable for Windows:

When Eclipse starts, it collects all fragments for a given host and adds their contents to the host plugin's. The contents are overlayed only virtually, they are not physically copied to the host plugin's folder. This means that all classes from .jar files in the fragment appear on the classpath of the host plugin, all files (such as icons, or internationalization properties files) appear on the host plugin's resource path, and so on.

Take an example: Suppose there is a plugin p, and a fragment f that declares a as its host plugin. Suppose further that f contains an icon file icons/eview16/myview.gif (p does not contain a file with that path and name). Now any code in a plugin that depends on p will get that icon file from p, if f is present, even though p does not contain it (and in fact doesn't even know about it).

This mechanism is used by the Eclipse project itself for providing internationalization texts. The plugins of the Eclipse IDE themselves contain only English UI texts. But there is a set of fragments (collectively called the 'language packs') that provide translations of these texts into a number of other languages. These fragments consist of just a lot of .properties files. When they are present, these files are used by Eclipse to display UI texts in their localized versions.

Fragments of the Cohatoe server plugin (de.leiffrenzel.cohatoe.server.core) which contain the server executables are platform-specific. That means that there is one fragment for Windows (which you can recognize from the win32.win32.x86 in the name), one for Linux (linux.gtk.x86) and so on. The three elements in the fragment name stand for Operating System, os (e.g. linux), Window System, ws (e.g. gtk) and OS Architecture, arch (x86).

The same naming convention is used in Eclipse for other things that are also related to platform-specific code. Another example (in addition to the platform fragments for the server executable which are my topic here) is the location of the Haskell object code which I have mentioned in an earlier post (the simple example walkthrough). Remember that the folder names there also had these 'os' and 'win32' bits? I'll explain the particulars of that in more details in an upcoming post.

For providing a server executable for a platform, the only thing that is needed is a fragment for that platform, and that fragment should contain a folder server, in which the executable is placed. The name of the executable must start with haskellserver. (On Windows, it is called haskellserver.exe, on other platforms it will be very likely just called haskellserver.)

I have organized the Windows fragment so that the build scripts for building the Windows executable are located with the fragment, not with the plugin (where the source code is). This is because I think that there will be platform-specific build scripts in all the other fragments too. The build scripts are in the folder INTERNAL/integration/ in the windows
fragment project.
As you can see, I'm using Cabal for building the executable. That makes it convenient to declare additional package dependencies. The packages that are currently needed are base, network and parsec, of course hs-plugins and the Cohatoe API package (cohatoe-api), and HaXml. Further dependencies may be added in the future, depending on how much more functionality will go into the server.

I have wrapped the several Cabal build steps (configure, build, clean) in a Windows batch file. There I'm also doing a pragmatic step to copy the created executable to the correct destination folder. I'm sure that there is a way to extend Cabal so that this can be done in a Cabal build hook - so if I've got a bit of time, I'll probably see to beautify that :-).

Finally, to execute that build script (i.e. the Windows batch file), I have declared an External Tool in Eclipse. This functionality in Eclipse is very useful, but seemingly not so well-known. You can create a launch configuration for an external tool ('external' means usually a separate process, but the file may be located inside the workspace, as it is in our case) and then have the launch configuration settings written into a workspace file. Mine is in the Haskell server exectuable - win32.launch file that you can see in the build folder in the screenshot above. You can configure it on the dialog that you get from the menu Run > External Tools > External Tools ....

You can see here that I have just declared the batch file as the executable and the folder which contains it as the working directory. In addition, it is very helpful to specify, on the Refresh tab, that the entire project should be refreshed automatically after the tool has run.

Finally, on the Common tab you can specify a location where the launch configuration settings should be stored.

Now that we have the launch configuration we can simply run it from the menu or from the global Eclipse toolbar (look out for the 'External Tools' button). What's even better, we can also register the External Tool launch configuration as a builder on the Eclipse project that contains the sources, so that it is automatically triggered when these sources change. (But that is left as an exercise to the reader ;-).

No comments: