Chapter 2. The Porting Process

For every expert there is an equal and opposite expert.

Anon

This chapter discusses various strategies for porting code to the Symbian platform. There are many different types of software project and a number of different ways of porting the code for any specific project. My view is that there is no single 'right' way to approach a port although there are several 'wrong' ones! Most successful efforts will follow a very similar high-level process. I present that process with descriptions of the options available at each stage so that developers can make informed choices and understand the potential consequences of following a particular strategy. A lot of the decisions involved are about trade-offs between maintenance issues, such as keeping a common code-base, and other factors, such as porting effort, performance, usability, native look-and-feel, and so on. Often it is tempting to create a 'fork'[30] of the original project for speed or simplicity but it's important to bear in mind the lifetime cost of this decision, including maintaining your own version of the software you've branched from. This is such an important topic that we dedicate Chapter 15 to portability and maintenance issues. While this chapter presents several options that could result in a 'fork' being created, please consider the alternatives very carefully before making the choice.

There are usually a lot of unknowns at the beginning of a porting project and the top-down approach, analyzing all of the code in fine detail before starting to work on it, is unlikely to be the most efficient. The bottom-up approach of just trying to compile the project unmodified and seeing what happens can also be sub-optimal, particularly if the project turns out to be inappropriate for the platform. The key to success, as with most engineering, is finding the right balance between top-down and bottom-up strategies for the specific project.

There are several steps involved in a successful port and some of those that follow will be optional, depending on your goals:

  • choose a project to port

  • analyze the code

  • re-architect (if necessary)

  • set up the development environment

  • integrate your project with the Symbian build system

  • compile

  • fix problems

  • run and test

  • debug

  • re-integrate with the original code (if desirable).

This is not a linear sequence and you would expect to iterate through parts of the sequence several times (see Figure 2.1). For example, you may select a project to port because it's widely used but then, after analyzing the code, decide to go back and select a different one! Similarly, it's unlikely that you'll fix all the compilation problems or bugs in the project at the first attempt. It can often make sense to have a first pass through the code and modify or comment out parts that obviously won't work before trying to compile it for the first time.

Choosing a Project

In many cases, you will have a choice of several projects to port, but on some occasions you may not have to make a choice at all – the project may be selected for you or perhaps you're the author of an original project that you want to port. Where there are multiple projects available, it is often possible to make an initial selection purely by examining project websites and documentation.

Making a selection is not an exact science and depends on a number of personal and technical preferences, such as whether you are familiar with the software as a user, or are more comfortable with C than with C++.

The porting process

Figure 2.1. The porting process

Here are some other things to bear in mind when making a selection:

  • Is the project actively maintained?

    There is a lot of 'abandonware' out there, software which is unfinished or at best only beta quality. Trying to fix bugs in the original code could be very difficult unless it is well documented (which is very rare) or easy to understand.

  • How many people were involved in the project?

    The vast majority of open source projects are developed by one or two people. Projects with multiple contributors tend to be better documented (or at least commented) and follow better engineering practices.

  • Will you have co-operation from the original authors?

    If the original authors or current maintainers of the project are supportive of your port, they may be able to give advice or even do some of the work for you. For larger projects it is always worth making contact at an early stage, particularly if the aim is to re-integrate any changes.

  • Has the project already been ported to other platforms?

    The more platforms a project has already been ported to, the easier it is likely to be to port to another one. This is because the platform-specific code should have been separated from portable code during previous ports.

  • Is the project suitable for your target hardware?

    Factors to consider here are both practical (e.g. screen size and input method) and technical (e.g. required processing power and RAM and use of floating-point arithmetic).

  • Are there any dependencies?

    Open source projects often build upon other open source libraries. Proprietary projects might use third-party libraries with commercial licenses. Are the libraries used in the project already available on the Symbian platform? If not, will you have to port them too and can you legally do so? Many commonly used libraries are available on the Symbian platform (see Chapter 4).

  • What license is the project distributed under?

    If you (or your company) own the copyright to the code then this is not an issue: you can use the code in any way you wish. For open source projects, there are hundreds of different licenses available and you need to be careful not to violate the terms of use. One of the main issues is the strength of any 'copyleft' clause, which broadly divides the licenses into three categories:

    • unrestricted (you can keep any changes to yourself), e.g. Apache and BSD licenses

    • weak copyleft (you must release the original code and, in many cases, any changes under the same license but you can add or link with proprietary code), e.g. LGPL and Eclipse licenses

    • strong copyleft (you must release the source for any project which uses this code under the same license, typically using one of the GPL versions).

If you are using open source code in a commercial project then you need to check the license details very carefully and it would be wise to have your company's legal advisors give approval to proceed. However, even open source developers need to be careful as there can be incompatibilities between licenses which prevent the combination of source code licensed under different terms.

Analyzing the Code

Having selected a candidate project, it's time to have a look at the code. It is definitely worth spending some time on this to avoid nasty surprises later. The goal here isn't to understand how everything works but to get an idea of how the project is structured. You want to work out roughly how much of the code will work unchanged, what will need modifying and which parts will need to be re-written. This should give you a rough idea of how much work is involved, which is useful to know before you start, even if you haven't got a manager breathing down your neck demanding estimates!

If you're exceptionally fortunate, your project will be neatly divided into portable and platform-specific code, perhaps even with a porting guide explaining which parts need to be re-written and how to go about it. In most cases, you won't be anywhere near this lucky and will have to scan through individual files or folders to assess the scale and difficulty of the task. If you're dealing with an open source project, please consider documenting this process for those that follow you, porting to other platforms. It might even be you that benefits from this documentation, if you need to port your entire project to another platform, or if you come back to this project later after changing your mind and attempting to port another one (I've actually been in this situation – all I can say is that documentation for academic projects sometimes rather exaggerates the actual quality of the code!). However good the documentation and code appear to be, it is always useful to compile and run the code on a supported platform. Doing this can not only give you a feel for the maturity and stability of the project but also provide additional information about configuration and the level of compiler warnings to expect.

If your code is pure C or C++ (i.e., it does not call external libraries) then it will probably run on the Symbian platform unchanged. Most standard C/C++ and POSIX libraries are also supported but user interface and multimedia functionality, or anything that directly accesses other hardware, is probably not. For details of which libraries are supported and how to use them on the Symbian platform, see Chapter 4. Examples of libraries that are not currently available on the Symbian platform and how to port software that uses them is explained in Chapter 5.

If you have more than one suitable candidate project, it would be a good idea to confirm some of your assumptions from scanning the project documentation or website. How well documented or commented is the code? Check the included header files to see if there are any unexpected dependencies. Are there likely to be problems with limited resources? In some cases, it may be worth analyzing the code for several projects before making a final selection.

Re-architecting

Having analyzed the code, you should consider any general architectural changes required in order to make the porting process (and future maintenance) easier. Where some of the code is portable and other parts need to be re-written, it usually helps to have a modular architecture in which the portable parts are separated from the rest of the project. For applications with a graphical interface, you want to find a nice clean separation between the user interface (UI) and the engine[34] – if that isn't the case you might consider re-factoring the code on the original platform before you start the port. For small applications, it may not matter but for a large application it may be a sign that you should go back and select another project to port, assuming this is an option. If you are re-factoring a project to increase portability, it's advisable to discuss this with developers working on the project on other platforms if you want to re-integrate your changes when you are done. This kind of major re-factoring is likely to affect everyone using the code-base and integration of the changes will have to be managed carefully.

A popular way of splitting the UI and the engine for Symbian applications is to have them in separate binaries with the UI executable interfacing to a portable DLL. If this split doesn't already exist for the project you are porting then it may not always be sensible to create it artificially. However, you can still split the UI from the engine at a source code (file) level and compile to a single hybrid executable. Historically, one of the major reasons for the split into separate binaries on Symbian OS has been the desire to support multiple UI platforms with a single application engine. The single Symbian platform will reduce the benefit of separating at a binary level as device manufacturers make the transition to a single UI framework. There are still benefits to designing an application with such a split on any platform; for example, it makes it easy to exercise the engine from a test harness and also allows easy stubbing of the engine functions to test the UI. It is debatable whether introducing such a split to a ported project is worth the extra effort unless the core functionality is likely to be re-used in other applications. These alternative application architectures are illustrated in Figure 2.2.

On several platforms, applications often use a polling paradigm: they check the status of various input devices or ongoing operations each time they are scheduled. Depending on the type of application, this may not be very power efficient. For example, it's fine for an arcade-style game[35] where the user is constantly interacting anyway but perhaps not so good for a chess game or a document viewer where the user only interacts occasionally. On the Symbian platform, the preferred model is event-driven programming (with active objects – see Chapter 3 for details), in which an application's process is suspended, waiting to be notified by the system that an event in which it is interested has occurred. The event-driven paradigm is also used for multi-tasking on the Symbian platform in preference to multi-threading, for most use cases. However, for a ported application it is usually best to change as little as possible, since fewer changes mean fewer opportunities to introduce bugs. For that reason, it is generally recommended to keep existing polling and multi-threading designs where possible. Get the code working first and then determine whether performance or power consumption is a problem. Unfortunately, the lack of standard API availability in UI and multimedia areas forces design changes in some cases. Chapter 5 provides some examples.

Application architectures

Figure 2.2. Application architectures

Apart from major architectural changes that may be required to make the port possible or maintainable, you may also need minor changes to deal with quirks of the platform or performance issues. Some of these changes will require very little modification to code, for example by moving some functionality into another thread or having it run in a callback rather than polling a device. In these cases, you need to think carefully about how the change impacts the rest of the code and what you need to test to make sure it works.

At this stage, you also need to decide what to do with missing dependencies. There are three main options:

  • port the dependency

  • write a porting layer

  • re-write the parts that use the dependency.

A porting layer involves writing wrappers with the original function names from the missing dependency and implementing the underlying functionality in native Symbian C++ code. This can be a good strategy if your project uses only a small subset of the functionality exposed by a missing library. Your choice also depends on how the project is going to be maintained. If it is to be maintained as part of the original project with conditional compilation, rather than as a separate project, then you will probably want to port the dependency or write a porting layer to minimize changes to the common core of the application.

If you need to use some native Symbian functionality to replace a missing dependency, it may also require some changes to the architecture. A common example is an executable that runs a main loop and needs to use Symbian functions that depend on active objects. Active objects require an active scheduler and that blocks the thread in which it is running. The obvious solution here is to run the active scheduler and associated active objects in a separate thread, making the request from the main thread and then polling for the result. However, if you are planning to wait for the result of the operation (i.e. you want a synchronous call), then you can simply start the active scheduler when you make the request and stop it again when you get the response, running the whole thing within one cycle of the main loop. This may not be a great idea if the main loop is running your user interface.

Setting Up the Development Environment

If you are new to Symbian development, then there are a number of tools and resources you will need to set up before you can get started with your port.

System Requirements

The system requirements for C/C++ development on Symbian are as follows:

  • Microsoft Windows 2000 Professional with Service Pack 3 or Microsoft Windows XP Professional with Service Pack 2[36]

  • at least 512 MB of RAM (as with all software development, more is better – 2 GB is recommended when using the IDE)

  • a Pentium-class, 1 GHz or faster processor (again, faster is better)

  • at least 1 GB of free disk space (more required for multiple SDKs)

  • a 16-bit color display capable of 1024×768 resolution.

  • Java Runtime Environment (JRE) (available from java.sun.com; the correct version will be linked from or bundled with the SDK)

  • ActivePerl 5.6.1 (available from activestate.com; the correct version will be linked from or bundled with the SDK)

  • local administrator rights for installation and removal of software.

Integrated Development Environment

The recommended integrated development environment (IDE) for Symbian C++ development is called Carbide.c++. It is based on the popular open source Eclipse IDE and comes in three editions: Developer, Professional and OEM. Since late 2008, with the release of Carbide.c++ v2.0, all of the editions are free and I have listed them in order of increasing feature set. All editions have support for on-device debugging, which can be extremely useful for some types of application. There are additional features in the Professional edition, such as a code performance analysis tool, that you might as well have, since they are free, unless disk space is a serious issue. The OEM edition is only intended for developers working on device firmware; its extra features are not usable by most third-party developers. All of these IDE versions are available as part of the Application Development Toolkit (ADT) from developer.symbian.org/main/tools_and_kits. Historically, the main alternative was Carbide.vs, a plug-in for Microsoft Visual Studio, but this has now been withdrawn.

Of course you don't have to use an IDE at all; it is possible to do all of your Symbian development using command line tools and a simple text editor but this is unnecessarily masochistic in the 21st century. You would miss out on lots of useful automation of the build and deployment process as well as excellent debugging features. Throughout the examples in this book, we assume you are using Carbide.c++. However, for large projects, there are distinct speed advantages when building from the command line and only using the IDE for debugging.

SDKs and Plug-ins

Prior to the formation of the Symbian Foundation, Symbian devices were divided between three user interface (UI) platforms: S60, UIQ and MOAP(S). S60 was developed by Nokia, UIQ was originally developed by Symbian and then spun off into a separate company (later owned by Sony Ericsson and Motorola) and MOAP(S) is used by NTT DoCoMo in Japan but it doesn't allow the installation of third-party applications. Within the Symbian Foundation, technologies from all three UI platforms will be combined into a single platform. However, it will only provide backward compatibility with S60 3rd Edition and later versions. For this reason, all of the examples in this book are developed for S60 only, although UIQ-based devices will still be in use for some time, with the first devices based on the unified Symbian platform not expected to start shipping until 2010.[39]

Download and install S60 SDKs from: www.forum.nokia.com/Tools_Docs_and_Code/Tools/Platforms/S60_Platform_SDKs. The only extra advice I would give, having seen lots of people get this wrong, is not to assume that the latest SDK is the one you want. For the widest possible device compatibility, you'll need either the S60 3rd Edition (Maintenance Release) or UIQ 3.0 SDKs. If you use a more recent SDK than this, it may contain new features not compatible with older devices. Only use newer SDKs if you want to target specific devices and features, or you discover a compatibility break in a newer device (they do happen sometimes despite best efforts to prevent them). The S60 SDKs advance the edition for a major version change and add 'feature packs' for minor version updates, so you have S60 3rd Edition FP1 and FP2, for example. The UIQ SDKs simply used major and minor version numbers, such as UIQ 3.0 and UIQ 3.1. However, UIQ has closed down and there is no longer an official developer portal that distributes the SDKs. You can discover what version a specific device is running by checking developer.symbian.org/wiki/index.php/Symbian_Devices.

The exception to the rule about not using newer features is where they are directly relevant for a port. Several standard APIs have been added since the initial releases of S60 3rd Edition and UIQ 3.0. The libraries containing these APIs are made available as plug-ins to the relevant SDKs and SIS files for installation on devices that were shipped without them. Further details of these libraries and the related plug-ins, known as P.I.P.S. and Open C/C++, can be found in Chapter 4. Additionally, the port of the Qt application framework to S60 is only supported from S60 3rd Edition FP1 onwards.

Compilers

There are three compilers commonly used in Symbian C++ development:

  • Nokia ×86 compiler for the Symbian platform emulator on Microsoft Windows (referenced as WINSCW within the build system)

  • the open source GCC-E compiler, used to cross-compile for target hardware based on the ARM processor (referenced as GCCE within the build system)

  • ARM RealView Compilation Tools (RVCT), the high-performance commercial alternative to GCC-E, used by device manufacturers to build device firmware (referenced as ARMV5 within the build system).

The first two of these are free and come bundled with the IDE and SDK respectively; the third should produce higher performance code and smaller binaries for target hardware but must be licensed separately from ARM at a cost.

It is recommended that you accept the default paths and settings while installing the SDK and compilers. If you need to change installation paths for some reason, then ensure that the path to the GCC-E compiler doesn't contain any space characters or your builds will fail with 'missing separator' errors.

Integrating with the Symbian Build System

Prior to the introduction of Raptor[40] in the latest version of the platform, the Symbian build system had been in roughly the same form since the early days of the operating system. This meant it was overdue a complete overhaul. Whatever platform you are porting from, you will need to adapt to the build system. This can be a difficult area for larger projects and a major headache if you want to add Symbian to the platforms already supported by a project under a common build system.[41]

However, both Symbian build systems work with the same input files. In order to port build files to the Symbian system, you will need some understanding of how the builds work on both the original platform and the Symbian platform.

We can't cover all available build systems in this book, although this isn't a very difficult task and most build systems are very similar at an abstract level. The majority of open source software projects use make and all but the simplest also have some kind of configure script[42] to generate makefiles from templates in order to adapt the build to the host platform.

Symbian has a similar makefile generation system in order to produce makefiles for different host (Windows-based emulator) and target (GCCE or ARM RVCT cross-compilation) builds. The primary inputs for the Symbian build system are a bld.inf file and MMP files. The bld.inf file is very simple: it specifies the target platforms, any exported files (headers, test data, configuration files, etc.) and one or more MMP files. The MMP files have a fairly simple syntax[43] that lists the build target, target type and path as well as the source files, libraries and include paths used to build it. Many of these can be copied from the (possibly generated) makefile on the original platform, although note that all executables from Symbian OS v9 onwards must be installed to sysin.

Build Process

I hope you already understand the build system for the platform you are porting from; if not, you need to consult the relevant documentation. Let's take a look at the workings of the Symbian build system. I'll use the command-line build steps for simplicity but an IDE will either call the command-line utilities in the background or use internal routines that have the same effect.

  1. Run bldmake bldfiles. This calls bldmake.bat, which simply passes any command-line arguments to a Perl script, bldmake.pl. This Perl script creates an appropriate abld.bat file for the project.

  2. Invoke abld build, with optional arguments to specify a target and debug or release variant (otherwise it builds all targets and variants). This runs the batch file generated by Step 1, which executes abld.pl to perform several stages of the build process. The most important of these are the 'makefile' stage, which executes makmake.pl to generate makefiles from the MMP files, and the 'target' stage, which uses a version of make and other tools to build the final binaries.

Curious readers will find all of the scripts mentioned above in the epoc32 ools sub-directory in their SDK.

Build Files

Since the bld.inf and MMP files are the ones you actually have to work with, it is worth examining them in a little more detail. Here is a simple example of a bld.inf file:

PRJ_PLATFORMS,
DEFAULT
PRJ_EXPORTS
..incSoundTouch.h
..incFIFOSamplePipe.h
..incFIFOSampleBuffer.h
..incSTTypes.h
PRJ_MMPFILES
SoundTouch.mmp

This specifies that the project should generate makefiles for the default platforms (currently WINSCW, ARMV5 and GCCE), export four header files and build a single component which is specified in SoundTouch.mmp. The corresponding MMP file looks like this:

TARGET          SoundTouch.dll
TARGETTYPE      dll
UID             0x1000008D 0x0839739D
CAPABILITY      None
USERINCLUDE     ..inc
SYSTEMINCLUDE   epoc32include epoc32includestdapis
SYSTEMINCLUDE   epoc32includestdapissys
SYSTEMINCLUDE   epoc32includestdapisstlport
SOURCEPATH      ..src
SOURCE          SoundTouch.cpp AAFilter.cpp
SOURCE          FIFOSampleBuffer.cpp FIRFilter.cpp
SOURCE          RateTransposer.cpp TDStretch.cpp
LIBRARY         euser.lib
LIBRARY         libc.lib libm.lib libstdcpp.lib

The first two lines specify that we want to build a DLL called Sound-Touch.dll. From the fifth line onwards we define user and system include paths, the path where the source is located, names of the source files to compile and any libraries to link against. These should be easily recognizable and familiar from almost any other platform. Note that you can repeat the keywords here as many times as you like with one or more files or paths after each, separated by spaces and terminated by a new line.

If this is your first Symbian project then the third and fourth lines, UID and CAPABILITY, won't be familiar. Every executable on a Symbian device has three unique identifiers (UIDs), one which identifies the target type (e.g. exe or dll), another which identifies a specific sub-type (e.g. a plug-in for a specific framework) and a third which is globally unique for the executable. The two hexadecimal numbers after the UID keyword in the MMP file define the second and third UIDs (commonly known as UID2 and UID3 respectively). The CAPABILITY keyword is related to application signing and the platform security model. You can find more details about Symbian's platform security in Chapter 14. By default, applications have no special capabilities. Certain APIs on the Symbian platform require executables be granted special capabilities in order to use them. The capabilities required for each API are listed in the relevant SDK documentation. This is the basis for a secure platform which limits the harm that can be done by badly written or malicious code. Some capabilities, such as reading or writing user data and recording from the microphone, are not considered particularly sensitive and can be granted by the end user at installation time. Other capabilities, such as direct access to device drivers or unencrypted DRM content, can only be granted by Symbian Signed[44] and in some cases only with the approval of the device manufacturer.

The main alternatives for creating your bld.inf and MMP files are to write them from scratch, modify them from an existing application or use a wizard in the IDE to create a skeleton project and add your source files and necessary libraries to the MMP files as you go through the port. I'd strongly recommend this last option if your application has a graphical user interface since it also creates all the necessary boilerplate code for you to interact with the application framework. If you create your project with a wizard, it is automatically assigned a random UID from a testing range. If you distribute the project publicly as a SIS file, the UID should be replaced with one assigned by Symbian Signed. If you copy the MMP or write it from scratch, you'll need to select your own UID from the test range or request one from Symbian Signed.

Once you have your build files, you're almost ready to start porting the code. If you didn't create the project with a wizard then you need to 'import' from the bld.inf file to create a new project. Refer to the help in your chosen IDE if it isn't obvious how to proceed. In Carbide.c++ you simply select File, Import from the menu and then select 'Symbian OS bld.inf file' from the dialog that appears.

Packaging for Device Installation

Having created the build files and a project in your IDE, you should have everything you need to start working on the emulator. To get your code running on a real device, it also needs to be appropriately packaged and signed.

For packaging, you need a PKG (extension .pkg) file, which specifies where to get the executables and resource files generated by your build and where to put them in the device's file system. It also includes information on any dependencies that must already be present on the device and a UID which the installer can use for upgrading. Your options for creating the PKG file are the same as for the MMP file and the syntax[45] is again fairly simple. Here is the content of the simple PKG file that Carbide.c++ generates for the SoundTouch project in the build file examples above:

#{"SoundTouch DLL"},(0x0839739D),1,0,0
;Localised Vendor name
%{"Vendor-EN"}
;Unique Vendor name
:"Vendor"
"$(EPOCROOT)Epoc32
elease$(PLATFORM)$(TARGET)SoundTouch.dll"
                                -"!:systemlibsSoundTouch.dll"

This is an extremely simple example and the full package file syntax allows for fine control of the installation process with conditional file installation based on language variants, user selection or other testable conditions. The first line is the package header; it specifies the component name, UID (often, but not necessarily, the same as UID3 in the MMP) and version numbers for the component. The localized vendor name is displayed in installation dialogs; it should be replaced with the name of the person, group or company that owns or develops the package. The unique vendor name is not localized and is used for matching package upgrades. Note that the last two lines should be typed on a single line. Also, this package file is specific to Carbide.c++: $(EPOCROOT), $(PLATFORM) and $(TARGET) should be replaced with the appropriate values, if you want to use it with command-line tools.

The IDE should 'self-sign' SIS files for you by default; that is, it should generate its own certificate (which doesn't grant any capabilities) and sign the installation file with it. This is fine as long as your project only uses user-grantable capabilities, otherwise you need to request an appropriate certificate from Symbian Signed and let your IDE know where to find it, or sign your SIS file online using Open Signed (from the Symbian Signed website) before you install it.

Incremental Integration

As a final tip for this section, if your source files are not too interdependent, you may find it easier to add them to the project incrementally and get it to compile a piece at a time. You should be able to simply add your source and header files in the IDE and find them automatically added to the MMP. If your port is primarily a learning exercise then you can start with the simplest parts to build confidence.

In a commercial environment, it may make more sense to begin with the most complex parts of the port so that you find any critical issues early, improve timescale estimates or even abandon the port before too much effort has been invested. The things you learn while getting the first parts to compile may enable you to make fixes to other parts of the project before you've even attempted to compile them.

Compiling

This is the easy part! Simply press the Build button in your IDE and wait for it to spit out a great mass of errors. Unless you aren't expecting any problems or the project is very small and simple then it can be a very good idea to comment or #ifdef out large sections of the code and focus on getting some core functionality working. Also, make sure you try building with both the emulator and target compilers regularly to ensure you aren't introducing anything that doesn't work in both environments.

It's important to recognize that build errors generated during a port may be caused by various factors:

  • Compiler and linker differences: C and C++ may have published standards but they are not interpreted or enforced in the same way by all compilers and linkers. Open source projects have usually been developed with the GNU toolchain so you shouldn't find many new errors from this source with GCC-E but there may be some for emulator builds or the ARM RVCT compiler. GCC-E tends to enforce ANSI standards more strictly than other compilers but the linker seems to be more forgiving. Also, if the project uses compiler-specific features, such as #pragma directives or inline assembler, then be ready for problems.

  • Missing dependencies: Being unable to open a header file is a classic sign of a missing dependency. If you're lucky, this is just because the necessary include path isn't specified in the MMP. Sometimes a build system may add some include paths or libraries by default; this kind of implicit dependency won't usually show itself until the build fails. Often a necessary header or function isn't available on the Symbian platform. General strategies for dealing with missing dependencies are discussed in Section 2.3.

  • Mixing C and C++: Where a project uses both C and C++ on the Symbian platform but was all C code on the original platform, there are often a lot of linker errors. Usually these appear to state that a function is not defined when you can see that it is. The solution here is almost always to wrap extern "C" around the declarations and definitions of functions written in C. This prevents the C++ compiler 'mangling' the names and gives them standard C linkage instead. To retain portability, you can use __cplusplus, which should be defined by all C++ compilers, like this:

    #ifdef __cplusplus
    extern "C" {
    #endif
    <your C code here>
    #ifdef __cplusplus
    }
    #endif
  • Exports from DLLs: On the Symbian platform, a function is only exported from a DLL if this is explicitly stated by specifying EXPORT_C for the definition and IMPORT_C for the declaration. This requirement is removed for the special target type STDDLL from Symbian OS v9.3 onwards. Alternatively, macros for EXPORT_C and IMPORT_C can simply be defined with no value on non-Symbian platforms[46] to maintain portability. Static data is never exported from a Symbian DLL and must be wrapped with an appropriate function. This can be hidden from calling code by defining a corresponding macro that replaces the variable name with a function that retrieves a reference to it (see Chapter 4 for an example).

  • Other platform-specific differences: Data types, macros and functions may be defined in slightly different ways on different platforms. Examine the relevant definitions on both platforms and work out how to proceed. This requires careful judgment for each individual case, particularly where size or quantity limits are involved.

    Another thing to be aware of is that some platform or compiler differences may not become apparent until run-time errors occur. A common error of this type is where code makes assumptions about byte positions or word alignment of elements within a structure. This is not portable because padding within a data structure is platform dependent. Any code like this should also be fixed on the original platform. Chapter 15 contains more tips and guidelines for writing portable code.

Fixing Problems

Here's where it gets interesting. Unless you are porting a purely computational library or an application or utility that only uses standard APIs that are available on the Symbian platform (see Chapter 4), then you need some knowledge of both the original platform and Symbian platform APIs in order to complete this stage. If you've already got plenty of both then this isn't a problem. If you're porting from one of the other mobile platforms covered in this book, then turn to the relevant chapter (Chapters 7 to 9) for further advice. Otherwise you'll need to consult the available documentation on each platform for any problem areas. As a good start, the first place to look for replacements for missing function calls is the Symbian Developer Library supplied with your SDK. The search function works reasonably well and problems only tend to arise where similar functionality isn't available or has a different name. There are far too many cases like this to list (and I'm sure I'm not aware of them all) but the various discussion forums[47] are packed with questions and answers from thousands of developers that have trodden the same path before you. If you can't find what you're looking for, just post a new question.

Common problems when moving code to a mobile device platform from a desktop environment are resource limitations and handling the variation in device specifications. Screen size and the available input methods are also limited on mobile devices. While this can cause design issues, it is not as significant as the variation in these features across devices.

RAM

By default, a process on the Symbian platform is limited to 1 MB of RAM for the heap and 8 KB for the stack. These can be increased via the epocheapsize and epocstacksize statements in the MMP file, although note that the maximum stack size is 80 KB and there are still lots of Symbian phones in circulation where you are very unlikely to find 10 MB free for the heap. You may find that you need to change the design or architecture in order to reduce heap usage, particularly for image or video manipulation. While 80 KB should be sufficient stack for all but the most deviously recursive algorithms or those that incorrectly allocate large objects on the stack, the default 8 KB is fairly easy to exceed and overflows aren't checked at run time. If you see data corruption or random crashes (particularly of the KERN EXEC 3 variety) then it's worth increasing the stack size to see if this fixes the error. Projects originating from desktop platforms also often leak memory and fail to handle outof-memory situations gracefully; this requires extra testing and fixes (see Appendix A).

Storage Space (Flash Memory)

The main problem here is not size but availability. Most devices running Symbian OS v9 and later versions support expandable storage up to multiple gigabytes via SD cards or similar. High-end devices may also have several gigabytes of built-in storage but older or mid-range models are likely to have only a few tens of megabytes in total. The key is to handle out-of-memory situations gracefully and, preferably, give the user the option of storing resources on different drives.

Processing Power

The first Symbian OS v9.x devices ran on around 200MHz ARM 9 processors with no floating-point unit. That gives performance fairly similar to the PC I was using in 1996. Popular high-end devices at the time of writing have around 300MHz ARM 11 processors with hardware 3D graphics acceleration. The near future will bring devices with 400–600MHz processors, featuring multiple cores at the high end. Often, performance has not been optimized at all on desktop platforms and it may be possible to improve slow-running routines by orders of magnitude in some cases, for example by converting floating-point arithmetic to fixed-point arithmetic (see Section 6.3.2). General performance optimization for C/C++ code is beyond the scope of this book.

Battery Life

Mobile device users expect the battery life of devices to be of the order of days, not hours. Be particularly careful with any application designed to run as a background service. If it is essential to poll some device or network connection then make sure the timeout used for polling is as long as possible without compromising usability.

Screen Resolution

Devices vary quite widely in their specifications. For example, QVGA (240 pixels by 320 pixels) was the most popular screen resolution for mobile devices in 2008, with higher resolutions just starting to appear at the high end. S60 3rd Edition phones have also been shipped with 176×208, 208×208, 352×416 and 800×352 resolution displays (measured in pixels). Additionally, very few mobile devices have a full QWERTY keyboard (although there is a trend to include full QWERTY or mini-QWERTY keyboards in some of the latest devices). Most have a touchscreen, a standard ITU-T keyboard and some softkeys, or both. Any code that uses direct key mappings must be reconsidered for usability.

There are several common approaches to dealing with this:

  • developing for the lowest common denominator requires the minimum of maintenance and release work but fails to exploit the full capabilities of high-end devices

  • developing multiple versions for different classes of device enables greater exploitation of device capabilities at the cost of additional maintenance and release work

  • dynamically adapting to device capabilities adds complexity to the ported project and increases the differences from the original but allows a full range of device capabilities to be targeted while retaining a single version to maintain and release.

None of the schemes listed above is ideal and this kind of fragmentation is one of the major complexities of mobile software development. It is very common for two or even all three of these techniques to be used on the same project for different areas of variation. For example, a 3D game may require two versions, one for devices with hardware graphics acceleration and one for those with only a software renderer; however, both versions can dynamically adapt to the screen size of the device at run time.

Although it might be tempting to select different algorithms dynamically, based on the processor speed, this isn't advisable since the function which queries the speed simply returns a constant value which has not always been accurately updated in device firmware builds.

Running and Testing

Once you have a subset of your code compiling and linking to produce an executable then you can run it in the emulator or install the generated SIS file to your device. We recommend that you do both as early as possible to ensure you aren't relying on features of the emulator (such as the fact that it is running on an x86 architecture processor rather than an ARM-based one) or using APIs that are not supported in your target device. How early you can sensibly test on the device depends on what you are developing and whether you have access to on-device debugging. The ability to set breakpoints, examine variable values and step through code on a real device is extremely valuable when porting code that doesn't produce many visible (or audible) results.

The subject of testing is too big and contentious to cover in detail in this book. However, there are a couple of questions worth addressing that are very relevant to porting. The first question is, 'How do I know when I've finished the port?' If you're working on a small personal project then the answer is probably, 'Whenever you're happy with it or have had enough!' For larger projects, those that you plan to release or re-integrate with the original code base and, particularly, for commercial projects, you will want some more objective criteria. I believe an excellent approach is the one championed in the Symbian ecosystem by Charles Weir of Penrillian[48] (devise a set of tests on the original platform and make sure they pass there; port the tests as part of the project; when they all pass, you're done). Automated testing is preferred where possible – it allows you to apply test-driven development (TDD) practices to legacy code.[49] If your project already has a test suite then you're all set. If not, then you either write one or define a set of manual test cases depending on the scale and type of project.

The second question is, 'What do I need to test?' The easy answer to that question is, 'Enough to make sure it works' although that isn't very useful. Optimists (or ostriches sticking their heads in the sand, depending on your point of view) will say, 'If I haven't changed a piece of code it doesn't need testing.' This isn't necessarily true since any number of differences to the environment in which the code is running could cause it to break: timing issues, resource constraints, differences in the compiler and platform can all be the source of new bugs in previously working code. Pessimists (or software quality engineers) will point to the issues I've just listed and say, 'Everything must be tested again!' In practice, it may not be feasible to do a complete re-test of a ported project, particularly if it doesn't already have an automated test suite. As usual, the real answer for each individual project lies somewhere between the extremes and you have to use your judgment.

Debugging

The process of debugging ported code on the Symbian platform is not significantly different from debugging on other platforms. A key difference from a standard POSIX environment is the lack of stdout and stderr by default (although you can have a console which provides them, it would be in the way of a graphical application). Instead, you can write debugging output to a file or include e32debug.h and use the native Symbian RDebug::Printf() as a direct replacement.[50]

Another important difference is what happens to errors from the system. Native Symbian C++ functions with names that end L, LC or LD may 'leave'.[51] A leave that is not caught in an appropriate TRAP macro causes an application to 'panic' – its thread or process is terminated and an error message and code are displayed in a dialog box on the screen. Incorrect use of some system servers may also cause them to panic a thread. There is a system panic reference[52] in the developer library to help you interpret the cause.

A common source of trouble at this stage is an ALLOC panic when you exit the application. A dialog pops up after your application closes that says 'Panic: ALLOC' followed by two hexadecimal numbers (the second one is usually zero). This means you have a memory leak and the first of the two numbers specifies the address of the heap cell that has been leaked. Other platforms tend to be much more forgiving about memory leaks than Symbian, so they may go undetected for years. Fortunately, thanks to each Symbian process having its own virtual address space, if the program makes the same allocations the same heap cell is leaked on subsequent runs.[53] This allows for a simple method of debugging these leaks:[54]

  1. Note the hexadecimal address you get with the ALLOC panic.

  2. Put a breakpoint somewhere in your application before the memory is allocated (this might require some guessing).

  3. After hitting this breakpoint, add a data breakpoint for the address of the leaked cell. If you are unable to set the breakpoint then the memory region is not in use yet; move your original breakpoint further down the application and try again.

  4. After successfully setting the breakpoint it should be hit when you allocate the leaked memory. The memory may then be released and re-used. Keep going until you find the code that makes the last allocation of this memory before the application exits with the panic.

There are slightly more complex and scientific methods of tracking down leaks but this one usually suffices. If your leaked memory address changes because it relates to allocations that depend on remote content or user interaction then you may find the HookLogger an excellent tool to help you track them down.[55]

Another point worth noting here is that, while the details of a panic are provided in the emulator by default, they are not provided on target devices. To get this information when working on target hardware it is necessary to enable it, or use a third-party crash monitoring tool. For further information on this, see wiki.forum.nokia.com/index.php/Extended_panic_code.

Re-integrating

When you've got it all working, you may want to submit your changes back to the original project. This may not be practical if you've had to make significant changes. Where the changes are only minor, it's important to make sure that you can still compile and run on the original platform (and any others supported) by using the appropriate conditional compilation flags (__SYMBIAN32__ is defined for all Symbian targets). Even if you're not planning to re-integrate your code, you may still want to upgrade your port with a future version of the original. Bear this in mind while porting and keep your changes localized (small, neat and separate from the main code whenever possible).

If you're porting C++ code then a good strategy for coping with areas of unavoidable variation is to encapsulate them in a class with a common abstract interface (this could be an abstract base class for two classes, one for each platform). Platform-specific versions of the class can be kept in separate files and the appropriate one selected by the build system. If you can get this refactoring of the original code accepted back into the project then it will be much easier to maintain, with very little chance of merges required for the platform-specific files.

When you do intend to re-integrate your changes, the most important thing is communication with the original developers. It is difficult to give general advice other than that you should follow whatever processes and procedures the project already has in place. Further strategies for writing code to ease future maintenance are discussed in Chapter 15.

Summary

In this chapter, I've explained a process that you can follow to port code to the Symbian platform. General advice for the various steps of the process is provided along with options for different approaches that you can tailor to the specific needs of your project. Along the way, I've covered some common issues you might face while porting but general advice will only take you so far. To make your porting projects successful, you need to get into the details. The next three chapters provide the core knowledge you need to work with the Symbian platform. Chapter 3 introduces Symbian idioms and compares them with those used in standard C/C++ development. It also explains how to write safe standard C/C++ code for a resource-constrained device. Chapter 4 goes on to introduce the standard libraries that are supported on the Symbian platform and how you can use them to make porting easier. In contrast, Chapter 5 explains which popular libraries are not available to Symbian developers and what you can do about it.

Throughout this book, particularly in Chapters 1012, there are examples of open source projects ported to the Symbian platform. The process described in this chapter has been used for those ports and I describe them in that context. Feel free to refer back here to refresh your memory while working through the examples.



[30] A fork happens when developers take a copy of source code from a project and start independent development on it, creating a distinct piece of software. This is considered a bad thing in free and open source software development and is strongly discouraged because it tends to cause a large duplication of effort. Forking of proprietary code is more common, in my experience, but the consequences are the same.

[31] The EPL is available from www.eclipse.org/legal/epl-v10.html.

[32] The GPL is available from www.gnu.org/copyleft/gpl.html.

[33] See www.gnu.org/licenses/gpl-faq.html#GPLIncompatibleLibs for the exact details.

[34] Options for modularizing Symbian applications are discussed in Willee, H. (2008) developer.symbian.org/wiki/index.php/Modularizing_Symbian_C++_Applications.

[35] Chapter 2 of Stichbury, J. et al. (2008) Games on Symbian OS covers implementation of a main loop and polling for input (available from developer.symbian.com/gamesbook).

[36] Windows Vista is not officially supported by older Symbian SDKs (S60 3rd Edition and Feature Pack 1 in particular): although it can be made to work, the installation process is rather more complex. There is an extensive wiki page on the issues involved in getting S60 SDKs to install on Windows Vista at wiki.forum.nokia.com/index.php/Moving_to_Windows_Vista.

[37] www.martin.st/symbian.

[38] www.winehq.org.

[39] Visit www.symbian.org for the latest information about Symbian platform releases.

[40] developer.symbian.org/wiki/index.php/Introduction_to_RAPTOR.

[41] Forum Nokia Champion Harry Li is attempting to port the Mozilla platform to Symbian. This requires the Symbian version to be built using Autoconf and GNU Make. He has documented his efforts to port the build files at wiki.mozilla.org/Mobile/Symbian/Build.

[42] A configure script is often created by Autoconf, available from www.gnu.org/software/autoconf.

[43] You can find the complete syntax in the Symbian Developer Library supplied with your SDK or at developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/ToolsAndUtilities94/Build-ref/Mmp-ref/.

[44] See www.symbiansigned.com for details of UID ranges and application signing.

[45] developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/ToolsAndUtilities94/Installing-ref/PKG_format/.

[46] Microsoft Windows platforms have very similar conventions for function exports; projects that already support Microsoft Windows usually have appropriate defines in place.

[47] In particular, see developer.symbian.org/forum, Forum Nokia at discussion.forum.nokia.com and NewLC at www.newlc.com/forum.

[48] www.penrillian.com/porting.

[49] TDD practices are described and explained fully in Feathers, M.C. (2004) Working Effectively with Legacy Code, Prentice Hall Pearson Education.

[50] If you're developing a simple console application or standalone server with P.I.P.S. or Open C (see Chapter 4), writes to stderr are mapped to RDebug::Printf() by default. On some SDKs, you have to enable RDebug output for the emulator in epoc.ini before you see anything in the console of your debugger or the epocwind.out file.

[51] A 'leave' is the Symbian version of throwing an exception (and from Symbian OS v9, is implemented with a standard C++ exception – see Chapter 3).

[52] developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/refer-ence/SystemPanics/.

[53] On the emulator, only the least significant four hexadecimal digits of the address are guaranteed to be the same due to address space layout randomization on Microsoft Windows. In practice, I have not found this to be a problem.

[54] Adapted from Mika Raento's Symbian programming pages at mikie.iki.fi/symbian/leaks.html.

[55] developer.symbian.org/wiki/index.php/Using_Hooklogger.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.217.134