Daniel Fisher (lennybacon.com)

SOA, DATA & THE WEB

MsBuild finding directories if not given as parameter

I’m a lazy developer. Being lazy does not mean I avoid to work. It means that I like to reflect things I am doing and optimize and atomize stuff to get more time on the valuable tasks. Code generation is a tool I tend to use quite regularly and T4 is at most my generator of choice.

Generated files can cause a lot of merging conflicts. So they are not to be checked into my source control system (currently my choice is HG/Mercurial).

For the build server this means the files do not exist when the repository is freshly checked out – they need to be generated during the build right before the compile happens.

With code generation I usually tend to use a pattern that is for example also used by ASP.NET MVC Views/Controller generation: If there is a local directory containing code generation templates, use it. Otherwise utilize the system wide templates.

MsBuild version 4.0 comes with a feature called property functions. This allows to place for instance a “find directories” inside a property group.

I use them to:

  • Allow to set a system wide template directory using the command line/the MsBuild API.
  • If no directory is set use a local directory.
  • If non of the above is applied set a fallback default.

Here is the XML snippet that shows how to use the pattern:

<?xml version="1.0" encoding="utf-8"?>
<Project 
    DefaultTargets="Generate" 
	ToolsVersion="4.0" 
	xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	
	<PropertyGroup>
		<RootFolder>..\src</RootFolder>
		<CodeGeneratorDir 
			Condition=" '$(CodeGeneratorDir)' == '' " />
		<LocalCodeGeneratorDir>
			CodeGenerationTemplates
		</LocalCodeGeneratorDir>
		<CodeGeneratorDirFallback>
			C:\CodeGenerationTemplates
		</CodeGeneratorDirFallback>
	</PropertyGroup>
	
	<ItemGroup>
		<ModelFiles 
			Include="..\src\**\*.xdml" 
		/>
		<TemplateDirectory 
			Include="$(CodeGeneratorDir);" 
		/>
		<TemplateDirectory 
			Condition=" '$(CodeGeneratorDir)' == '' " 
			Include="$([System.IO.Directory]::GetDirectories(
				&quot;$(RootFolder)&quot;, 
				&quot;$(LocalCodeGeneratorDir)&quot;, 
				System.IO.SearchOption.AllDirectories))"
		/>
		<TemplateDirectory 
			Condition=" '@(TemplateDirectory)' == '' " 
			Include="$(CodeGeneratorDirFallback)" 
		/>
	</ItemGroup>

	<Target Name="Generate">
		<Message Text="@(TemplateDirectory)" />
		<Message Text="@(ModelFiles)" />
  </Target>
  
</Project>

Comments

Write a comment