I faced a question this weekend: how best should I support conditional compilation by framework? The proximate reason for asking this was a need to use BigInteger in the Cavity project in order to be able to support 128-bit base-36 notation with a minimum of fuss. This is a new value type in Framework 4.0 and up until now Framework 3.5 have been the target for Cavity assemblies. As 3.5 is going to be around for a good while yet (heck, there is still plenty of 2.0 in production today) I didn’t want to orphan support for that version. I also didn’t want to add complexity to my build or maintenance activities. This ruled out brute force options such as creating a branch for 3.5 or creating a new solution and project files for one or other of the framework versions.
A search yielded a varied collection of advice. Some was plainly potty but a couple of StackOverflow answers [1] [2] looked promising. Taking the answers and combining a bit of common sense, I put together a quick spike to verify that I had the gist of it. Here is what I learnt:
- The best way to control targeting is to use property parameters with a build file. Here is a .bat file to build release versions targeting each major framework version:
MSBUILD build.xml /p:Configuration=Release /p:TargetFrameworkVersion=v2.0 MSBUILD build.xml /p:Configuration=Release /p:TargetFrameworkVersion=v3.5 MSBUILD build.xml /p:Configuration=Release /p:TargetFrameworkVersion=v4.0
- You don’t need to do anything in the build.xml or the .sln file for this to work. You do, however, need to do a little work in the .csproj files:
- First, you need to decide which framework version you want to work with in Visual Studio (typically, this will be the latest version) and configure TargetFrameworkVersion accordingly:
<TargetFrameworkVersion Condition=" '$(TargetFrameworkVersion)' == '' ">v4.0</TargetFrameworkVersion>
- To make the build output obvious, set the OutputPath to use properties:
<OutputPath>bin\$(Configuration) $(TargetFrameworkVersion)\</OutputPath>
- Next, you should set up some conditional property groups:
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v2.0' "> <DefineConstants>NET20</DefineConstants> <TargetFrameworkVersionNumber>2.0</TargetFrameworkVersionNumber> </PropertyGroup> <PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.5' "> <DefineConstants>NET35</DefineConstants> <TargetFrameworkVersionNumber>3.5</TargetFrameworkVersionNumber> </PropertyGroup> <PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.0' "> <DefineConstants>NET40</DefineConstants> <TargetFrameworkVersionNumber>4.0</TargetFrameworkVersionNumber> </PropertyGroup>
- This allows you to configure framework-specific assembly references, such as System.Core and Microsoft.CSharp:
<Reference Include="System.Core" Condition=" '$(TargetFrameworkVersionNumber)' >= '3.5' " /> <Reference Include="Microsoft.CSharp" Condition=" '$(TargetFrameworkVersionNumber)' >= '4.0' " />
- You should now be able to target framework versions in a straightforward manner by running the batch file above.
- First, you need to decide which framework version you want to work with in Visual Studio (typically, this will be the latest version) and configure TargetFrameworkVersion accordingly:
- If you want to conditionally include classes, simply apply a framework condition:
<Compile Include="Class20.cs" Condition=" '$(TargetFrameworkVersion)' == 'v2.0' " />
- I also defined constants above to allow conditional compilation at code level:
namespace Example { public sealed class Class1 { #if NET20 public void Net20() { } #endif #if NET35 public void Net35() { } #endif #if NET40 public void Net40() { } #endif } }
The only pain I have really encountered so far is getting MVC web applications to play nicely.
2 comments:
Nice article! I haven't been able to use >= conditions though... I get this error:
error MSB4086: A numeric comparison was attempted on "$(TargetFrameworkVersion)" that evaluates to "v4.0.30319" instead of a number, in condition "'$(TargetFrameworkVersion)' >= 'v4.0'".
Any ideas?
You have to character encode the greater than symbol, i.e >
Post a Comment