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 :
<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 :
<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 :
<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 :
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):
# 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:
<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:
[julien@mars QQ_CG01]# ant
Output should be something like:
1 2 3 4 5 6 7 8 9 10 |
Buildfile: /home/julien/dev/quidquid/QQ_CG01/build.xml generate: [fmpp] - Executing: fr\quidquid\sample\flex...\ModuleConstants.as [fmpp] Summary: 1 exe. + 0 xml. + 0 cop. = 1 succ.; 0 warn.; 0 failed [move] Moving 1 file to /home/julien/dev/quidquid/QQ_CG01/OUT/fr/quidquid/sample/flex/modules/qqt01 BUILD SUCCESSFUL Total time: 1 second [julien@mars QQ_CG01]# |
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
.