<ANTENNA/>

Antenna

An Ant-to-End Solution For Wireless Java

Version 1.2.1

(c) 2002-2010


Erik Wetterberg
Sourceforge project


News | Overview | Download | Setup | JAD | Build | Package | MakePRC | RAPC
Run | Preverify | Obfuscate | SmartLink | Preprocess | Deploy | Sign | History |

Preprocessor task Directives Comparision Operators Eclipse plugin

Preprocessor

The new preprocessor backend is developed by Omry Yadan (omry at yadan dot net).

The preprocessor supports operations on variables and access to a device database (Currently only the J2ME polish devices database is supported) is accsible from the WtkPreprocess task and from an Eclipse plugin. This preprocessor is modeled after the NetBeans mobility suite preprocessor (but supports a few additional directives).

The WtkPreprocess task provides a preprocessor, similar to the ones known from C and other languages. It allows for conditional compilation and inclusion of one source file into another and is helpful when trying to maintain a single source for several devices, each having its own bugs, add-on APIs, etc.

Preprocessing source files to manage implementation differences is quite unusual in Java programming. Actually it might be considered "evil technology" by OO purists. While the latter is somewhat true - interfaces, abstract classes and loading classes by name are definitely the cleaner approach - pure OO is something you often cannot afford when developing for devices that accept only JAR files up to 30 KB. A simple interface easily costs a kilobyte or more in the JAR. Loading implementation-specific classes by name usually gets in the way of obfuscation. A conditionally compiled block, on the other hand, costs absolutely nothing (apart, maybe, from a bit of pride, in case you consider yourself an OO purist, but isn't that a small price to pay for a MIDlet that works merrily on dozens of different mobile phones?).

Please note that the task only preprocesses the source files, but doesn't compile them. So you still have to run the Java compiler on the preprocessed files afterwards. Since the preprocessor doesn't filter "inactive" conditional parts by removing them, but by commenting them out instead, line numbers for compiler errors are usually the same as for the original code (unless you are using #include).

In addition to conditional compilation the preprocessor replaces Ant-style properties in the source-code.

The task provides the following parameters:

Parameter Type Required Purpose
version int no Preprocessor backend version.
  • 1: The old preprocessor backend (Documentation available here)
  • 2: The new netbeans-like preprocessor
The default is 2.
device string no A device from the device database, for example "Nokia/6280".
All the symbols for this device will be automatically injected into the symbols list. (supported only by backend-version 2)
devicedbpath dir no Optional path for the devices xml files
debuglevel Debug level no Turn debug level on, value should be one of [debug|info|warn|error|fatal]
printsymbols boolean no Used to debug, if true all preprocesor symbols will be printer prior to preprocessor.
savesymbols Filename no Saves all the preprocessor symbols to a file
srcdir file yes The source directory from which Java source files are being preprocessed.
destdir file yes The destination directory to which preprocessed files are written.
newext string no Specifies a new file extension for all files that have gone through the preprocessor. Useful when you save the files to preprocess with a special extension to distinguish them from normal Java files. The new extension replaces everything from (and including) the first dot in the filename up to the end, so when you specify newext=".java", a file "MyFile.j2me.pp" becomes "MyFile.java" once it goes through the peprocessor. If you don't specify this parameter, file extensions are not changed.
indent boolean no Controls indentation of comments that the preprocessor writes into the target files. Defaults to true, which results in comments having the same indentation as the the original line. Setting it to false results in comments starting at the beginning of a line.
symbols string no A list of define symbols that are initially defined for each file the preprocessor is invoked on. Parsing of this list is done by the actual preprocessor backend, so the syntax changes from backend to backend.
  • Backend version 1 : Comma separated list (FOO,BAR,KAZ)
  • Backend version 2 : Comma separated list of key=value pairs or key (FOO,BAR=2,KAZ="abc")
encoding string no Specifies the encoding to use when reading or writing source files. Defaults to the platform default encoding, if not specified.
filter boolean no When set to true, the preprocessor filters all directives and "inactive" conditional parts. The resulting file contains only the actual Java source code that goes into the compiler. Defaults to false.
verbose boolean no Turn verbose output on or off.
if String no Provides fine-grained control over task execution based on a property definition. The task will only be executed if the given property is defined.
unless String no Provides fine-grained control over task execution based on a property definition. The task will only be executed if the given property is not defined.

Symbol files

It's possible to load symbol files into the preprocessor, files are loaded in the order they are declared
File Format supports the following directives:
KEY=VALUE		: Standard define, if key is already defined it will be replaced
add_if_new@KEY=fff	: Sets key only if it's not defined already
unset@KEY		: Clears the value of KEY

Parameter Type Required Purpose
Name string This or List Symbols file name to load/td>
List string This or name Comma separated list symbols file names to load
Example:

<wtkpreprocess 
	verbose="true"
	srcdir="src" 
	destdir="out" 
	device="Generic/Java"
	symbols="LIST='1,2,3',STR='String',PI=3.1415,VERSION=${VERSION}"
	printsymbols="true">

	<!-- Load a.symbols -->
	<symbols_file name="a.symbols"/>
		
	<!-- Load b.symbols -->
	<symbols_file name="b.symbols"/>
	
	<!-- Load c.symbols and d.symbols-->
	<symbols_file list="c.symbols,d.symbols"/> 
	
</wtkpreprocess>

		

Directives

The preprocessor supports the following directives inside a Java source file. All directives follow a "//" comment that starts at the beginning of a line (whitespace is allowed left of them, but no Java code). That way, they don't interfere with normal Java compilation. Directives must not span multiple lines of code. In addition, whitespace is allowed between the // and the # of the directive to maintain preprocessor commands validity after eclipse source formatting.

Directive Decription
#ifdef [identifier] The identifier represents a variable of any type (boolean, string, or integer) and checks whether or not the variable is defined. If true (the variable is defined), the code that follows is processed. Nested blocks are processed as well. If false (the variable is not defined), the code that follows is commented and nested blocks are not evaluated. The directive must be closed with #endif.
#ifndef [identifier] Works in the same manner as ifdef, but returns "True" if the variable is not defined. The directive must be closed with #endif.
#elifdef [identifier] Works as a standard else if statement, but automatically checks whether or not the identifier is defined. The directive can only complement inside blocks started by ifdef/ifndef.
#elifndef [identifier] Works as a standard else if statement but automatically checks whether the identifier is not defined. The directive can only complement inside blocks started by ifdef/ifndef.
#if [expression] Evaluates an expression passed to it and fires the appropriate action. The directive must be closed with endif.
#elif [expression] Works as a standard else if statement and can complement only in blocks started by an if statement. The directive preprocesses the code that follows based on the result of the expression.
#else Works as a standard else statement only preprocesses the code that follows when none of the previous conditions in the defining block were true. Complements inside any block started with the if/ifdef/ifndef directive.
#endif This directive must be used to close any block started with if/ifdef/ifndef.
#condition [expression] Must be on the first line in a file. This directive determines if the file should be included in the build based on the result of the expression.
#debug [level] Determines if the line following the directive should be commented or uncommented based on the debug level set in Compiling page of the project properties. If the debug level is omitted and the debug level is not set to "Off" in the project properties, the preprocessor will automatically debug the line in question. Used for debugging purposes with expressions such as System.out.println, for example. This directive can be nested.
#mdebug [level] Behaves the same as #debug, but instead comments or uncomments a whole block of lines following the line it is on until it reaches #enddebug. This directive is used for debugging purposes with expressions such as System.out.println, for example. This directive can be nested. If the mdebug block partially intersects an if/ifdef/ifndef block (for example, enddebug is outside a closed if block in which mdebug is called) the preprocessor will generate errors.
#enddebug Must terminate #mdebug block.
#define [identifier] #define [identifier=value] #define [identifier value] Adds temporary abilities or variables to the preprocessor memory. Can not be used in nested blocks. Global variables defined in the project configuration properties override these temporary variables.
#undefine [identifier] Removes temporary abilities/variables from the memory. This declaration can also be used to remove global variables defined in the project configuration properties from the preprocessor memory, but will not remove the variables from the list of project or configuration variables.
#expand LINE WITH MACROS Expand directive is used to replace build time defines in the code.
You can have a line like:
//#expand public static int VERSION = %VERSION%;

which will, assuming you have the define VERSION=5, be replaced with the two lines:
//#expand public static int VERSION = %VERSION%;
public static int VERSION = 5;


The reason //#expand is needed is to keep the preprocessing operation reversible (without it, it will not be possible to preprocess the same code and replace the number 5 with the correct version in case VERSION = 6 is defined).

Comparison Syntax in Preprocessor Directives

The preprocessor supports three types of variables:


Most common comparisons (<=, <, >=, > and ==) are supported between the variables.
Boolean operations such as &&, ||, ! and ^ can also be performed on all symbol types.
Integers behave the same as strings when it comes to boolean operations. However, they are compared as true integers and can be used for such tasks as preprocessing code where various screen resolutions require different images that are optimized for different resolutions. For example, the directive

//#if ScreenWidth>100 && ScreenHeight>120

can specify a code block which will import images only for devices whose screens are larger than 100x120.
If a variable is defined, then it is considered a boolean with the value, "true." If the variable is not defined anywhere as an ability or configuration. then it is considered a boolean with value "false."
Comparisons should not be done on different variable types. However, such comparisons do not break the build process. If different variable types are compared, the preprocessor issues a warning, and evaluates the expressions as follows:

Operators

Operator Decription
The "not" operator (!) This operator has the highest priority and can be used on variables and expressions with the syntax ! or !. For example, //#if !(ScreenWidth>100 && ScreenHeight>120) checks if the screen size is smaller than 100x120. Since the ! operator has the highest priority, expressions such as //#if !ScreenSize=="100x200" are illegal and yield syntax errors because a boolean result cannot be compared to a string.
Comparison operators Comparison operators have the second highest priority and perform typical comparison operations. Because the operators can compare strings lexically and integers mathematically, cross-type comparisons are supported. However, they can be used only in expressions and should compare two variables, not symbols.
There is also a special comparison operator which performs a "subset" relationship operation. This operator is denoted by the character @. Both left and right arguments should be strings which represent two sets of tokens delimited by specific delimiters. The operator first considers both left and right string arguments as sets and then determines if the set from the left argument is a subset of the set from the right argument. The valid word delimiters are ,',' and ';'. The delimiters can be mixed arbitralily within each argument, as shown in the following examples.

"gif" @ "gif86, jpeg, gifaboo" = false
"gif" @ "gif gif86 jpeg" = true
"1 2 4;7,8" @ "0,1,2,3,4,5,6,7,8,9" = true
"3 5 7 11 13" @ "0,1,2,3,4,5,6,7,8,9" = false
Boolean operators Boolean operators have the lowest priority relative to the rest of the operators. They also have different priorities amongst each other, just as in the Java language. Boolean operators perform typical logical operations such as &&, || and ^ on boolean expression results, or check for variable definitions and then treat those as Boolean expressions as well. Boolean operators are processed in the order: 1. && 2. ^ 3. ||

The overall processing order is:

Eclipse plugin

Requirements

The plugin was tested on eclipse 3.2.

Installation

Open eclipse, and go to Help->Software updates->Find and Install…



Select ‘Search for new features to install’:



And finally add the plugin update site http://antenna.sf.net/update:

Usage instructions

From the project menu, enable the preprocessing:



Open the project properties, and modify preprocessing parameters (Numbers appear in screenshot):
  1. User defines, this overrides device specific defines. For example, FOO=’bar’,LIST=’1,2,3’
  2. Validates the syntax of the user defines.
  3. The name of the device active for this project.
  4. Opens device search dialog, where you can select one of the devices from the included devices database (included in the jar).
  5. Current device defines. This is read-only, but can be overridden by the user defines text field.
  6. Project debug level. Code with lower debug level (//#debug or //#mdebug and //#enddebug directives) will be commented if they have a level lower than this level.


When preprocessing in active, blocks that are not active will be commented automatically by the prefix //@
The Preprocessing is performed when an open file is modified and saved in eclipse (ctrl+s), or when the parameters in the preprocessing properties page is modified.