Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$(TargetFramework) is empty when targeting multiple frameworks

I have a project targeting multiple frameworks using <TargetFrameworks> (plural) in my .csproj file.

This works fine, but I'm unable to do framework-specific things in .csproj since the $(TargetFramework) property is always empty.

If I target a single framework using <TargetFramework> (singular) everything works as expected.

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

    <PropertyGroup>
        <TargetFrameworks>net5.0;net6.0;net7.0</TargetFrameworks>
    </PropertyGroup>

    <Target Name="DoSomethingFrameworkSpecific" AfterTargets="Build">
        <Message Text="$(TargetFramework)" /><!-- Empty string -->
    </Target>

    <ItemGroup>
        <None Include="MyProject.targets">
            <Pack>True</Pack>
            <PackagePath>build\$(TargetFramework)</PackagePath><!-- File ends up in \build, not build\net5.0 etc -->
        </None>
    </ItemGroup>

</Project>

I was under the impression that MSBuild would pass the current framework version for each pass (i.e. for each framework) so that we can do things like framework-specific paths or other conditionals depending on the version being built?

The official docs suggest this is possible: https://learn.microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-a-target-framework

Related questions on SO seem to deal with preprocessor directives only, not getting the current framework version in MSBuild scripts.

like image 874
Ted Nyberg Avatar asked May 26 '26 23:05

Ted Nyberg


1 Answers

If you need to use $(TargetFramework) in a nuget pack path when targeting multiple TFMs, change <None> to <TfmSpecificPackageFile> otherwise it won't work. By default nuget will evaluate files to pack from the outside build not the TFM-specific build, but if you use <TfmSpecificPackageFile> then it will re-evaluate the pack item from the inner TFM-specific build.

As for the message - I tested it and it works fine, although I do get an empty message at the end for the outer build plus the TFM-specific messages above that in the output. If you want to remove the empty outer build message then just add a condition. Final result should look something like this:

    <Target Name="DoSomethingFrameworkSpecific" AfterTargets="Build" Condition="'$(TargetFramework)' != ''">
        <Message Text="Message from TFM: $(TargetFramework)" />
    </Target>

    <ItemGroup>
        <TfmSpecificPackageFile Include="MyProject.targets">
            <Pack>True</Pack>
            <PackagePath>build\$(TargetFramework)</PackagePath>
        </TfmSpecificPackageFile>
    </ItemGroup>
like image 101
Mike Marynowski Avatar answered May 28 '26 13:05

Mike Marynowski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!