Conditional Compilation and Framework Targets

Conditional compilation and framework targets

One of the best ways to accomplish this is to create different build configurations in your project:

<PropertyGroup Condition="  '$(Framework)' == 'NET20' ">
<DefineConstants>NET20</DefineConstants>
<OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>


<PropertyGroup Condition=" '$(Framework)' == 'NET35' ">
<DefineConstants>NET35</DefineConstants>
<OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>

And in one of your default configurations:

<Framework Condition=" '$(Framework)' == '' ">NET35</Framework>

Which would set the default if it wasn't defined anywhere else. In the above case the OutputPath will give you a separate assembly each time you build each version.

Then create a AfterBuild target to compile your different versions:

<Target Name="AfterBuild">
<MSBuild Condition=" '$(Framework)' != 'NET20'"
Projects="$(MSBuildProjectFile)"
Properties="Framework=NET20"
RunEachTargetSeparately="true" />
</Target>

This example will recompile the entire project with the Framework variable set to NET20 after the first build (compiling both and assuming that the first build was the default NET35 from above). Each compile will have the conditional define values set correctly.

In this manner you can even exclude certain files in the project file if you want w/o having to #ifdef the files:

<Compile Include="SomeNet20SpecificClass.cs" Condition=" '$(Framework)' == 'NET20' " />

or even references

<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
<HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>

Is conditional compilation bad practice when targeting .NET Standard and .NET Framework?

Well, you probably shouldn't multi-target in the first place, unless you're writing a library that will absolutely (not might, but absolutely!) be used to support legacy projects. Today, there is no reason not to write .Net5 code, unless you're writing UWP/Xamarin apps.

However, if you are multi-targetting, you need to write code that compiles under all your targets. That involves both #if and MsBuild targetted items, to conditionally include entire source code files.

Conditional code execution in C# based on Targetframework of hosting project

IMO the correct way to do this is at the csproj level via multi-targeting - <TargetFrameworks> etc, and either #if (in the C#) or conditional file includes (again in the csproj). Ultimately, what you're describing is exactly what multi-targeting is designed to do, and any solution other than "just use multi-targeting" is missing out on a huge feature explicitly aimed at solving these problems. The build mechanism has a "bait and switch" layer whose job it is to ask "what is the target framework of the host application? ok, I'll give them {this version} of the dependency" - which is why the job of transitive dependency resolution is deferred to the top-level application build, rather than library build (libraries don't reliably know the host TFM, for the reasons I'm about to get to).


However, putting that aside:

Your biggest problem here is going to be that: .NET Standard doesn't exist at runtime, only at compile-time; I mean multiple things by this, including the reality that .NET Framework can host .NET Standard, so when you ask:

what is the target framework of the hosting process.

it will never be .NET Standard. If that is just a typo and you mean .NET Core, then you can probably be reasonably safe borrowing these 2 lines from PlatformDetection.cs:

using System.Runtime.InteropServices;
// ...
public static bool IsFullFramework => RuntimeInformation.FrameworkDescription.StartsWith(
".NET Framework", StringComparison.OrdinalIgnoreCase);
public static bool IsNetCore => RuntimeInformation.FrameworkDescription.StartsWith(
".NET Core", StringComparison.OrdinalIgnoreCase);

is it clean? no. Will it work: yes.

Is it possible to conditionally compile to .NET Framework version?

There are no builtin precompiler constants that you can use. But, it is easy enough create your own build configurations in VS with each configuration having its own set of defined constants and of course a target framework version. A lot of people do this to conditionally compile based 32 or 64 bit differences.

How do I conditionally build .Net Core 3.0 only if supported by tooling?

I reviewed the conditional compilation documentation, but it seems to
talk only about changing settings when compiling, not whether to do
the compilation in the first place.

Yes, msbuild conditions should work inside the <Project> node. For your requirement, I can only imagine one possible way that you add the conditions to the first PropertyGroup( Condition="'$(VisualStudioVersion)'=='16.0'"), something similar to this format:

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup Condition="'$(VisualStudioVersion)'=='16.0'">
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.0;net48</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Condition="'$(VisualStudioVersion)'!='16.0'">
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>

<!-- ... other PropertyGroup with conditions-->

<!--I use this script to confirm that when building the project in VS2017(msbuild15.0),the VS version is 15.0 while in VS2019(msbuild16.0), VS version is 16.0-->
<Target Name="TestVersion" AfterTargets="build">
<Message Text="version=>$(VisualStudioVersion)" Importance="high"/>
</Target>
</Project>

Have to say most of the time, we use Multi-targets for class library projects instead of console projects, not sure about your specific reason to do this... Hope it makes some help :)

Update:

To build .net projects when VS is not installed, we can consider using Build Tools for VS package, here's the link of build tools for VS2019(contains the msbuild 16.0). In All downloads=>Tools for VS2019 we can find the build tools package.

To make sure we can use the msbuild.exe from this package to build .net framework4.8 and .net core3.0 console projects, make sure we enabled these workloads and components when installing:

Sample Image

Sample Image

We can navigate to Individual Components to make sure .net core 3.0 and .net 4.8 is enabled.

Conditional compilation for .NET 4

The compiler isn't aware of any particular .NET Framework version. All it sees is the reference assemblies. Nor is there any guarantee that your program will run with the .NET version that you target. It is quite possible to run with the .NET 4.0 CLR even if you built for 2.0.

Use Environment.Version instead.

Detect target framework version at compile time

The linked SO question with 'create N different configurations' is certainly one option, but when I had a need for this I just added conditional DefineConstants elements, so in my Debug|x86 (for instance) after the existing DefineConstants for DEBUG;TRACE, I added these 2, checking the value in TFV that was set in the first PropertyGroup of the csproj file.

<DefineConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">RUNNING_ON_4</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">NOT_RUNNING_ON_4</DefineConstants>

You don't need both, obviously, but it's just there to give examples of both eq and ne behavior - #else and #elif work fine too :)

class Program
{
static void Main(string[] args)
{
#if RUNNING_ON_4
Console.WriteLine("RUNNING_ON_4 was set");
#endif
#if NOT_RUNNING_ON_4
Console.WriteLine("NOT_RUNNING_ON_4 was set");
#endif
}
}

I could then switch between targeting 3.5 and 4.0 and it would do the right thing.



Related Topics



Leave a reply



Submit