Conditional Compile based on .net framework version

Ok, I’ve been after this one for quite a while. I want to include stuff like Moth.Linq and ExtensionAttribute in .net 2.0 compilations, but exclude them from .net 3.5 compilations. In effect, I want to cross compile to .net 2.0 and .net 3.5. Not only do I want to do this from within Visual Studio, I also want the Continuous Integration server to do it too. Tall order? Yes.

A bit of background:

Moth.Linq is a mechanism for producing LINQ functionality in assemblies compiled in Visual Studio 2008 targeted to the .net 2.0 framework. You add this piece of code, add a ‘using Moth.Linq;’ at the top, and you can use LINQ. Very cool. (Disclaimer: this isn’t LINQ to SQL or LINQ to Entities, just LINQ – e.g. lamda expressions in SQL style syntax.)

– Daniel Moth has another awesome trick for using Extension Methods in .net 2.0 deployed assemblies. Since Extension Methods are a compiler trick, not a function of the language, at compile time, the method itself is put back into a static class, and the calls to that method are magically transformed into calls to that static class’s method passing in the parameter that the extension method was attached to. None of this needs the magic System.Core.dll file that adds all the magic C# 3.0 sauce. Well, to make this compiler trick work requires the ExtensionAttribute inside the System.Runtime.CompilerServices namespace – Inside System.Core.dll. What does it do? I have no idea. But create this attribute, and extension methods just work. Very cool.

– I use NAnt triggered by Cruise Control .NET. NAntContrib has a fabulous task called msbuild that you can pass a solution file to, and it’ll build everything in the solution. Jeffrey Palermo has a great trick for enabling NAnt to build .net 3.5 projects. Combine these, and the standard NAnt build file that’s been working forever now works with Visual Studio 2008 projects as well. Very cool.

– Some developers on the team have VS 2005, some have VS 2008. Some deployment servers run .net 2.0, some .net 3.5. So, the code just has to work in both scenarios. Those of us in VS 2005 hate it when those of us in VS 2008 use the cool C# 3.0 features. Those of us in VS 2008 hate holding back.

So, the task at hand is clear: create a way that NAnt and Visual Studio can conditionally include things like Moth.Linq and Extension Methods. The clear solution: pre-processor flags. I wrap the Extension Method code like so:

#if NET_20
namespace System.Runtime.CompilerServices {
  public class ExtensionAttribute : Attribute { }
}
#endif

Well, that’s all fine and good, but how to get the target .net framework to trigger defining the preprocessor flag? Sadly, I haven’t found a good solution to that, but I did find this:

The idea is from Karsten: define preprocessor flags on the msbuild command line like so:

msbuild /p:DefineConstants=NET_20 /t:Rebuild

Ok, I’m 12 way there. NAnt’s syntax for defining the command line args is like so:

<arg value="/p:DefineConstants=NET_20" />

I put that inside the <msbuild> task, insure I’ve defined the framework version NAnt is targeting like so:

<property name="nant.settings.currentframework" value="net-2.0" />

and away we go. This task now defines a perfect .net 2.0 build.

Want a .net 3.5 build? Set the property nant.settings.currentframework to “net-3.5” and don’t set the arg in the msbuild task. I chose to put both “NET_20” and “net-2.0” into properties that I could set to “” and “net-3.5” based on a task passed into NAnt, and we’re good. We now have Continuous Integration building .net 2.0 compatible code. Excelent.

How do we get the VS 2005 guys to do the same? Well, that seems a task for another day. My vote was to give them all VS 2008. We’ll see how that goes over. :D My backup plan is defining project configurations for “Debug 2.0” and “Release 2.0” that define these. That’s likely incredibly fragile though – add a project and you tank that convention. Next in line is make them run NAnt to build, but getting out of Visual Studio to build seems very unintuitive. Make it a menu option in the external tools menu? That seems ripe for confusion for the new employees stepping onto the scene.

Well, in any event, 12 the problem is now solved. Wicked cool.

Rob