Code generator with ant FMPP and FreeMarker
CodeDevEnglish
A simple code generator example with FMPP and FreeMarker with the help of ant and antelope for automation.
Content :
– Why should we do code generators ?
– How it works
– Prepare ant tasks
– Let’s try with a simple file
– Now let’s do something bigger
Why should we do code generators
Some people are…pretty anxious when I advocate the use of a code generator. Most of the time they think spending time on a code generator isn’t productive enough for their short term objectives… These kind of person spend their lives running to catch a train that goes to fast for their capacity. Actually, code generators are a perfect answer for short term project, but not the first one (and again this isn’t obvious).
Code generators are part of your company’s (or personal) normalisation, industrialisation, and process simplification.
– Developers can focus more on business than technical implementation,
– Maintenance is easier : all code base is similar
– If your company turnover is important, process are simpler to acquire
So actually you can code faster, with a better quality, the ROI is always (if code generator is correctly done) positive.
How it works
Creating a code generator project is really fast, just :
1- use a real project you want to generate and copy it somewhere,
2- identify the dynamic stuff inside this project, like :
– classnames,
– packages,
– folders,
– …
3- replace dynamic patterns with a property tag
4- generate it once and do the adjustments if needed
In order to have an idea, a code generator creating around fifty files, that creates a Flex module with BlazeDS Java endpoints takes about 4 hours of work and tuning before beiing live.
Prepare ant tasks
Download Fmpp and AntelopeTasks jars and put them inside your ANT_HOME/lib folder.
Then inside your build.xml add those 2 lines after your project definition :
1 2 | < taskdef name = "antelope" classname = "ise.antelope.tasks.StringUtilTask" /> < taskdef name = "fmpp" classname = "fmpp.tools.AntTask" /> |
Now to use fmpp just do something like this inside a target :
1 2 3 4 5 6 | < fmpp sourceRoot = "${basedir}/src/MODULE/entity_and_code" outputroot = "${module_base_dir}/${entity_and_code}" alwaysCreateDirectories = "true" logfile = "${basedir}/logs/${entity_and_code}.log" > < data >antProperties()</ data > </ fmpp > |
Antelope in this project will be used mostly for its lowercase/upperCase/replace commands. Example :
1 2 3 4 5 6 7 | < property name = "entity_and_code" value = "${entity_and_project}_${module_code}" /> < antelope string = "${entity_and_code}" property = "entity_and_code" > < lowercase /> </ antelope > < antelope string = "${entity_and_code}" property = "ENTITY_AND_CODE" > < uppercase /> </ antelope > |
Let’s try with a simple file
You can get the files here : QQ_CG01.tar
The src folder contains this hierarchy : fr/quidquid/sample/flex/modules/MODULE_CODE
This folder contains the ModuleConstants.as file.
Inside the ModuleConstants.as file you’ll find freemarker tags :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package ${module_base_package}.flex.${module_code} { public class ModuleConstants{ // *********************************************** // ******** EVENTS FOR ${module_code?upper_case}_${module_name} public static const EVENT_${module_code?upper_case}_TESTBUTTON_SUBMIT: String = "EVENT_${module_code?upper_case}_TESTBUTTON_SUBMIT" ; // ####_DO_NEVER_(RE)MOVE_THIS_LINE_NOR_CHANGE_IT_#### EVENTS // *********************************************** // ******** NOTIF FOR ${module_code?upper_case}_${module_name} public static const NOTIF_${module_code?upper_case}_I18N_GET_OK: String = "NOTIF_${module_code?upper_case}_I18N_GET_OK" ; // ####_DO_NEVER_(RE)MOVE_THIS_LINE_NOR_CHANGE_IT_#### NOTIF } } |
Our goal is to replace these tags and after generation rename the MODULE_CODE folder with the right name.
First step to see how it works, in the root folder open the PROJECT.properties, it contains our dynamic stuffs (quite simple on this example):
1 2 3 4 5 6 7 8 9 | # Output folder of the code generator output_dir = OUT # Base package, will be suffixed with the module_code upper case module_base_package = fr.quidquid.sample.flex.modules # Functionnal name module_name = Quidquid Test # Module unique name in the whole application module_code = QQT01 |
Then let’s look at the build.xml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | < project name = "SimpleCodeGenerator" default = "generate" basedir = "." > <!-- Initialize antelope and fmpp --> < taskdef name = "antelope" classname = "ise.antelope.tasks.StringUtilTask" /> < taskdef name = "fmpp" classname = "fmpp.tools.AntTask" /> < target name = "generate" > <!-- Get properties from property file --> < property file = "${basedir}/PROJECT.properties" /> <!-- I need module_code to be lower case --> < antelope string = "${module_code}" property = "module_code" > < lowercase /> </ antelope > <!-- I need module_base_package to be lower case --> < antelope string = "${module_base_package}" property = "module_base_package" > < lowercase /> </ antelope > <!-- Get the path from the package ( just replaces . with / )--> < antelope string = "${module_base_package}" property = "module_base_path" > < replace regex = "\." replacement = "/" /> </ antelope > <!-- MODULE GENERATION --> < fmpp sourceRoot = "${basedir}/src/" outputroot = "${output_dir}/" alwaysCreateDirectories = "true" logfile = "${basedir}/${module_code}.log" > < data >antProperties()</ data > </ fmpp > <!-- Rename the module folder --> < move todir = "${output_dir}/${module_base_path}/${module_code}" > < fileset dir = "${output_dir}/${module_base_path}/MODULE_CODE" /> </ move > </ target > </ project > |
You can now open a command line ant do:
1 | [julien@mars QQ_CG01] # ant |
Output should be something like:
Now you should have an OUT folder containing your fr/quidquid/sample/flex/modules/qqt01/ModuleConstants.as with all the freemarker tags replaced… Easy no ?
Some freeMarker expressions you can use:
${module_code?upper_case}
${module_code?lower_case}
${module_code?cap_first}
To change the name of a filename:
<@pp.dropOutputFile />
<@pp.changeOutputFile name=module_name?cap_first + "Mediator.as" />
Well just go through the manual: http://freemarker.sourceforge.net/docs/dgui_template_exp.html
.