MSBuild–Target Dispatch via Metadata

 

In a (somewhat) recent post, I elaborated on using ItemDefinitionGroup to create the blueprint for a class of items, i.e. the items based on that blueprint. As mentioned, one could see an instance of such an item as an object instantiation of its underlying class, i.e. the ItemDefinition.

Now, while item properties actually make up for the data part of an object, the behaviour part was spared from discussion. In this post, I want to give a way of associating behaviour with a particular item. Intentionally I did not write “I want to make behaviour part of an item” or “make some item implement a desired behaviour”. The reason for this will become obvious in the following lines of MSBuild.

 <Project ToolsVersion="4.0"
    xmlns="https://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Dispatcher">
  
  <!--Define the layout of a task, calltarget, name, ... -->
  <ItemDefinitionGroup>
    <MyTask>
      <CallTarget>Default</CallTarget>
    </MyTask>
  </ItemDefinitionGroup>
 
  <Target Name="DefineTasks">
    
    <!--Construct concrete tasks-->
    <ItemGroup>
 
      <!--use a special task-->
      <MyTask Include="Task1">
        <CallTarget>Special1</CallTarget>
      </MyTask>
      <!--Use the default target-->
      <MyTask Include="Task2">
      </MyTask>
 
    </ItemGroup>
  </Target>
 
  <Target Name="Dispatcher" DependsOnTargets="DefineTasks">
    
    <Message Text="Dispatcher Thread" Importance="normal"/>
    <Message Text="MyTasks: @(MyTask)"/>
    <Message Text="MyTasks-CallTargets: @(MyTask->'%(CallTarget)')"/>
    <CallTarget
        RunEachTargetSeparately="true"
        Targets="@(MyTask->'%(CallTarget)')"/>
 
  </Target>
 
 
  <Target Name = "Default">
    <Message Text="Executing Default" Importance="low"/>
  </Target>
 
  <Target Name="Special1">
    <Message Text="Executing Special1" Importance="low"/>
  </Target>
 
</Project>

The purpose of this MSBuild program is to create some items and associate behaviour with possibly each of them. Behaviour is realized in the form of an MSBuild target. Now, the actual target (behaviour) to be invoked for a particular item is stored as data, i.e. the MyTask’s metadata property CallTarget of that particular item. If no specialization is given, the item’s default behaviour (CallTarget equals Default) will be used – think of it like the default statement in typical switch-case control flow.

The property CallTarget in and by itself does not do anything. Just like in regular programming data is just that data, i.e. symbols. Only the interpretation of data is what makes behaviour emerge.

This interpretation is realized in the MSBuild target named Dispatcher. The target Dispatcher does just that, it will dispatch to targets mentioned in an MyTask item’s CallTarget metadata property. This is achieved by making the CallTarget task evaluate item’s CallTarget properties and providing a Target for each behaviour specified by a MyTask item.

Now, while my targets (Default and Special1) don’t do nothing interesting, you may imagine all sort of MSBuild behaviour stuffed inside of them.

The MyTask items presented here don’t look (and are) that interesting, you may surely come up with some more meaningful form of piggybacking. For example, think of an item as a definition of workflow, i.e. an ItemDefinition will define a null-workflow (does not do anything). But derivations will specify a sequence of operations (targets), potentially using conditionals and more involved constructs.