Fulcrum Toolkit Developer Quick Start


This document is intended to outline what developers need to do write a Fulcrum plugin. This document will take more about high level work items, rather than about specific interfaces and steps because those things may change during Fulcrum’s lifetime – especially in early versions.

Code Generation Basics

Before we start lets review the basics of code generation. Mulitple flavors of code generation exist, but we’ll focus on the style employed by Fulcrum which is template code generation.

Some term delimitations are:
  • Metadata – Data fed into the data generation process, usually the domain data for the code being generated
  • Template – A “fill in the blanks” version of the code to be generated. The blanks are filled with the relevant metadata.
  • Metalogic – Logic used to “massage” the metadata so it can be consumed by the template
  • Generated code – The output produced by combining the template with the metadata, processed by the metalogic.
  • Handwritten – Code written manually – the old fashioned way.

How To Write A Fulcrum Plugin

A Fulcrum plugin serves 2 purposes – inform the engine about settings (template file, output directory, etc…) and provides data for the template to work against.

The FulcrumWeakEventManager is a simple plugin which serves the purpose of being a sample. Study it and follow its example while writing your plugin.

The first step in creating a plugin is to create a new VS project. Do all the grunt stuff to setup the namespace you desire, build settings, references, etc…. It is recommended you look at the other plugin projects to see what they do, and do something similar.

Next you need to create your entry point class. This is a class which derives from GenerationUnit inside the Fulcrum assembly. The recommended name is XxxxGenerationUnit where Xxxx is the name of the plugin. You need to mark it with the Export attribute so Fulcrum can find it at run-time. Implement the appropriate virtual methods. See the GenerationUnit class for documentation, and the sample for an example of a likely implementation. There are 4 key things you need to know about GenerationUnit:
  • Name – this is provided to the constructor. This is the name which will be used on the command line to identify this plugin.
  • Settings – more on this in the next step
  • DataContexts – a collection of DataContext objects. Each DataContext gets passed into a template file to serve as the data for the code generation.
  • CreateDataContexts – a virtual method which makes it easy to lazy initialize the DataContexts

The Settings object is next. This class implements the ISettings interface, and the recommended name ix XxxxSettings. The class should provide get/set properties for settings to pass into the GenerationUnit. Using the SettingsAttribute you can expose these settings to the FulcrumConsole command line interface. Simply slap the attribute onto the appropriate properties, and initialize the attribute with the desired values. At run-time the FulcrumConsole will parse the command line and call the setters as appropriate.

Lastly implement your DataContext class or classes. If you only have one then XxxxDataContext is the recommended name. The DataContext class must inherit from GenerationDataContext. Implement the appropriate methods/properties – the most key being TemplateFile which is the name of the template file to use with the data context. Extend the class with additional properties which contain the metadata your template file will need.

How To Write a Template

Fulcrum is build upon the T4 code generation technology inside Visual Studio. The template files consist of text which makes up the generated code, along with metalogic declared within special tags.

The metalogic can be any .NET language, which may be significantly different from the language of the generated code.

T4 syntax plugins exist for Visual Studio. Finding them by searching the Internet should be trivial. These will help with the editing process. Also on-line is documentation about T4 syntax and behavior.

The metadata will be passed in via the GenerationDataContext.CurrentDataContext static property. This value can be cast to the type specified in the plugin, in order to access additional members not present in the base class.

Like the plugin code, the best way to learn is to look at the sample project’s template file.

Unit Testing

Code generation is a great multiplier. It allows a developer to be more productive. But if the code generation process has a bug, the effects of that bug can also be multiplied. One way to ensure the quality of the generated code is by unit testing.

Generating the unit test code smacks of circular reasoning. Handwritten unit tests for the generated code negates the benefits of generating the code in the first place.

The alternative is to unit test the metadata, metalogic and template.

The metadata can be hard to unit test, especially if the metadata is handwritten. “Unit testing” the metadata can often only be done via code review. However one can unit test the code which loads the metadata from whatever store holds it.
The metalogic can be unit tested, provided it is in a regular assembly. Metalogic embedded in the template is difficult, if not impossible, to unit test. Therefore put as much metalogic as possible into the Fulcrum plugin, and simply call it from the template. Not only does this enable unit testing, but it reduces template complexity and makes it easier to build multiple templates (say for different output languages) for the same metadata.

The template can only be tested by generating code with sample metadata, and then unit testing the resulting code.
Fulcrum plugin writers are encouraged to follow this unit testing strategy to ensure quality of their generated code.

Putting It All Together

Now that we gone through each step individually, lets walk through how this happens at run-time.
  • User runs FulcrumConsole, specifying the plugin name and other command line parameters
  • Fulcrum loads the plugin assemblies and locates the right GenerationUnit, based on the command line option
  • Other command line settings are applied to the Settings object attached to the GenerationUnit
  • The GenerationUnit is asked for its DataContexts. This may cause the metadata source to be accessed to build up the list of data contexts.
  • For each DataContext
    • The template file associated with the DataContext is loaded
    • The DataContext is set as the CurrrentDataContext
    • The T4 engine is run over template
      • The template’s metalogic accesses the CurrentDataContext to replace sections of the template with the appropriate result based on the metadata
    • The output file is saved with the correct name to the correct location
  • Code generation is done

Last edited Mar 31, 2010 at 6:51 PM by NathanNesbit, version 1


No comments yet.