Preserving Code in AjGenesis
One of the issues to resolve when we use a code generation engine, is how to add our own code to a generated source file, without loss it in the next generation phase. Sometimes, we generate code only as a one-off activity, and modify the code without worry about a new generation, but in other situations, we use code generation many times. In this case, it would be great to have the capability of preserving manually added code.
During the past month, I added that functionality to my open source code generation engine AjGenesis. Thanks to the spanish group grupo de generación de código, and all its members that tested the new feature.
You can use it since version 0.5:
to download from CodePlex.
An example
In that version, under directory examples\HelloPreserve, there is an example of preserving code.
The sample is generated from command line, entering
make
that invokes a .bat file containing:
..\..\bin\AjGenesis.Console Model.xml Build.ajg
This command loads a model of a Hello World application, from Model.xml:
<Project Name="HelloWorld"> <Messages> <Message>Hello</Message> <Message>World</Message> <Message>Again</Message> </Messages> </Project>
(You can read a detailed description of this kind of simple model in a previos post:
Code Generation with AjGenesis: A Hello World Application
A more advanced example at
Application Generation using AjGenesis
)
The make command invokes a small program written in an interpreter, AjBasic. The file is Build.ajg:
PrintLine "Generating HelloWorld" TransformerManager.Transform("ModuleVb.tpl", "HelloWorld.vb", Environment, "PRESERVE")
There is something new: the third parameter in Transformer.Transform. Transformer is a helper object, that is implanted in the variable environment of AjGenesis, that is accesible from AjBasic programs. Using this object we can invoke the template engine. It has a method Transform with two parameters: the file nam of the template to process, and the name of the file to generate. In this new variant, there is a third parameter, that is used as a mark to delimitate a code preserving block inside the template.
This is the example template, ModuleVb.tpl:
' ' Project ${Project.Name} ' Automatically generated by AjGenesis ' http://www.ajlopez.com/ajgenesis ' ' PRESERVE comment start ' PRESERVE comment end Module Module1 Sub Main() System.Console.WriteLine( _ <# n = 0 for each msg in Project.Messages if n then Print "& " end if n = n + 1 #> "${msg}" _ <# end for #> ) End Sub ' PRESERVE code start ' PRESERVE code end End Module
All in this code that contains the mark PRESERVE (that is, the third parameter passed to Transform method), until other line containing the same mark, it will be preserve during code generation.
NOTE: the algorithm matches the lines using the full content of the first line in the preserving block. That is, if the template contains a line:
‘ PRESERVE comment start
this is the line that is searched inside the original code, in order to preserve the block. The blocks to preserver are identified by the FULL CONTENT of the line that initiates each block.
Later, if you modified the generated code, adding text to that blocks, that text is preserved:
' ' Project HelloWorld ' Automatically generated by AjGenesis ' http://www.ajlopez.com/ajgenesis ' ' PRESERVE comment start ' This is my comment two ' PRESERVE comment end Module Module1 Sub Main() System.Console.WriteLine( _ "Hello" _ & "World" _ & "Again" _ ) End Sub ' PRESERVE code start Sub MyMethod() Console.WriteLine("My own inmortal method") End Sub ' PRESERVE code end End Module
Some points
Let’s review some points in this process. Why we must indicate a parameter as mark? Why not simply use a predefined mark? First, to maintain compatibility with previous templates. But, more important: you decide what is the mark to use to identify code to preserve. You can use different marks in each project or type of generated text file. You are in charge.
If you want to explore how this feature is implemented, you can examine the file Utilities\FileMerger.vb in project AjGenesis.GenericTransformer. In short, that code apply the transform to the template, generates a temporary file, and merge it with the previous file (I use a temp file, so, in case of any errors during the transform, you can read the generated code so far). Then, it merge the new file with the old one. The preserving code are determined by the supplied mark and the full content of its first line.
I hope you find this feature useful. I’m sure that we can apply our knowledge about application building in a smart way: delegating repetitive and menial tasks to software.
Angel “Java” Lopez
http://www.ajlopez.com/en