Deprecated

Linn now recommend T4 for general purpose text transformation. An open source implementation of T4 is available as part of the MonoDevelop? project

Introduction

Kodegen is a general purpose text transformation system written in C#. Its primary use is to generate source code from a collection of input stimuli (normally xml files).

Kodegen System

The Kodegen system follows a standard pattern. A Kodegen installation contains three kinds of entity:

  • Kodegen executable
  • Kodegen template files
  • Kodegen plugin modules (optional)

Kodgen Template Files

A Kodegen template file is a set of instructions for dynamically generating text from well-defined, but variable input stimuli.

Consider the following example. We have a set of constants that need to be shared by programs written in C#, C++, and C.

The constants and their value are:

  • Company: Linn
  • Website: www.linn.co.uk
  • Year: 1972

We could do this by simply writing three different source files by hand:

Constants.cs
============

    public class Constants
    {
        public static readonly string kCompany = "Linn";
        public static readonly string kWebsite = "www.linn.co.uk";
        public static readonly uint kYear = 1972;
    }
Constants.hpp
=============

    class Constants
    {
    public:
        static const char* kCompany = "Linn";
        static const char* kWebsite = "www.linn.co.uk";
        static const unsigned int kYear = 1972;
    };
Constants.h
============

    #define CONSTANTS_COMPANY "Linn"
    #define CONSTANTS_WEBSITE "www.linn.co.uk"
    #define CONSTANTS_YEAR 1972

Alternatively, we could describe the constants and their values in a language-neutral file and generate all three language-specific source files from this single language-neutral 'source' file.

The benefits of this approach should be obvious.

So, using XML as our language-neutral 'language', we create the following file:

Constants.xml
=============

<constants>
    <entry>
        <name>Company</name>
        <type>string</name>
        <value>Linn</value>
    </entry>
    <entry>
        <name>Website</name>
        <type>string</name>
        <value>www.linn.co.uk</value>
    </entry>
    <entry>
        <name>Year</name>
        <type>ui4</name>
        <value>1972</value>
    </entry>
</constants>

Then, using three different template files, we use Kodegen to transform this single XML file into the three language-specific source files described above.

We'll go into the details of the template files later, but for the time being, lets assume that we have created the template files already and they are called ConstantsCs?.kode, ConstantsCpp?.kode, and ConstantsC.kode.

Executing Kodegen

So, how do we apply the transformations?

Simply issue the three commands:

    Kodegen.exe Constants.cs ContantsCs.kode Constants.xml
    Kodegen.exe Constants.hpp ContantsCpp.kode Constants.xml
    Kodegen.exe Constants.h ContantsC.kode Constants.xml

In the general case, Kodegen takes any number of arguments. But the first two, which are consumed by Kodegen itself are always:

  • Argument 1: Output file
  • Argument 2: Kodegen template file

Any subsequent arguments are passed to the template file, so their format and meaning are defined by the particular template file that is being used. However, in this case, and in fact in many cases, the third argument is an XML file. This is because XML is the most useful way of conveying information to the template generator in a totally cross-platform way.

Kodegen is written in C#, and its template files are written in a hybrid language that mixes C# with output text. It can run using both the Microsoft ,NET 2.0 runtime and Mono.

Using the XML facilities of .Net it is possible, for instance, to use Kodegen to generate the source code for Upnp proxy objects directly from the XML Upnp Service Description files that are published by the Upnp Forum. Linn uses Kodegen in a similar way to generate Upnp device code in C++ that runs on its DS products.

Kodegen Template Language

The Kodegen template file language is a highly modified version of  TemplateMaschine. Many thanks to Stefan Sarstedt for providing the original source upon which Kodegen is based.

For the time being at least, the  TemplateMaschine website is the primary resource for understanding how to write Kodegen template files.

There are some differences between Kodegen and  TemplateMaschine, which you should be able to work out by referring to the sample template file below.

Howeverr, here is a brief description of the differences:

<%@ Assembly Name="System.Xml" %>

This is not supported. Assembly references are made by naming each assembly on a separate line of the <* reference *> section of the template file.

<%@ Import NameSpace?="System.Threading" %>

This is not supported. Import by writing import commands into the <* import *> section of the template file

<%@ Argument Name="name" Type="string" %>

This is not supported. All template arguments are passed as string[] args (like a static Main function).

<%@ Include File="another.template" %>

This is not supported. Achieve a similar result by creating a standard C# assembly and referencing it inside the template file.

<script runat="template">

This is not supported. You can create local functions by including them in the <* function *> section of the template file.

<%=name%>

Aah, finally, this is supported. It is essential. It evaluates expression "name" and writes it to the output.

<% for(int i=0; i<100; i++) { %>

This is supported. It is essential. It introduces a C# code block into the template which is executed during the generator phase (don't forget to close braces, e.g. by somewhere later writing "<% } %>")

<% Response.Write("Hello!"); %>

This is not exactly supported. Use <% Write("Hello!"); %> instead.

Kodegen Template Sections

As you can see,  TemplateMaschine syntax has been replaced, and hopefully simplified, by dividing a template file into several named sections.

Here is an example template file to show how these sections work:

// Kodegen Template
//
// A Kodegen Template is broken into a the following sections:
//
//   <* reference *>
//   <* import *>
//   <* function *>
//   <* body *>
//
// An explanation of each section is provided next to its
// implementation
//
//
// <* reference *>
//
// Use the reference section to name additional assemblies
// required by the template.
//
// These may be general purpose assemblies that are normally
// loaded from the GAC.
//
// Alternatively, they may be purpose built Kodegen plugins
// deployed in to the same directory as the Kodegen executable.
//
// Typically a Kodegen plugin will validate an xml file qagainst
// an XML schema deployed into the same directory and preprocess
// its contents on behalf of the client template.
//
// A single Kodegen plugin would, therefore, be associated with
// a particular kind of XML file and would typically be reused
// by number of templates, possibly transforming the XML file's
// contents into code in many different languages or into modules
// of varied functionality.
//
// The following assemblies are provided by default:
//		System.dll
//		System.Xml.dll
//		LinnGen.exe
//
// Name one additional assembly on each line of the reference section.
//
// When referencing these assemblies the current directory is
// temporarily changed to the directory which contains the
// Kodegen executable.

<* reference *>

KodegenUpnp.dll

<* reference *>

// <* import *>
//
// Use the import section for additional using directives.
//
// The following directives are provided by default
//
//		using System;
//		using System.Text;
//		using System.IO;
//		using System.Xml;
//		using System.Xml.XPath;
//		using System.Collections.Generic;

<* import *>

using Kodegen.Upnp;

<* import *>

// <* function *>
//
// Use the function section to provide helper functions
// that can be called from within the body section

<* function *>

string accesstype;
string accesstypecurrent;

public void SetAccessType(string type)
{
	if (type == accesstypecurrent) {
		accesstype = null;
	}
	else {
		accesstype = type + ":\r\n";
		accesstypecurrent = type;
	}
}

public void ForceSetAccessType(string type)
{
	accesstype = type + ":\r\n";
	accesstypecurrent = type;
}

<* function *>

// <* body*>
//
// The body section contains the generated text embedded with
// control logic written in C#.
//
// <% %> tags surround the embedded control logic
//
// <%=expression%> outputs the value of the given expression
//
// args is an array of strings containing the template arguments
// passed from the command line when Kodegen was invoked.

<* body *>
<%
    if (args.Length < 1)
    {
        throw (new TemplateException("Xml file not specified"));
    }
    
    if (args.Length < 2)
    {
        throw (new TemplateException("Upnp service name not specified"));
    }
    
    string name = args[1];
	
    UpnpXml u = new UpnpXml(args[0]);
%>
#ifndef DEFINE_LINN_SERVICES_<%=name.ToUpper()%>
#define DEFINE_LINN_SERVICES_<%=name.ToUpper()%>

#include <Linn/Standard.h>
#include <Linn/Event.h>
#include <Linn/Control/Service.h>

namespace Linn {
namespace Services {

class <%=name%>Base : public Linn::Control::Service
{
<%      ForceSetAccessType("public"); %><%=accesstype%>
<%      foreach (Action a in u.actions) %>
<%      { %>
        virtual void <%=a.name%>() = 0;
<%      } %>
};

}; // namespace Services
}; // namespace Linn

#endif // DEFINE_LINN_SERVICES_<%=name.ToUpper()%>

<* body *>