Search code examples
c#.net-corec++-cli

Use newer dotnet sdk than TargetFramework when building C++/CLI


Is it possible to use a newer dotnet SDK building C++/CLI vcxproj than their target framework?

We have a large solution containing C#, C++ and a handful of C++/CLI projects. The target framework is set to .NET 6 (latest LTS and all that). At the same time I'd like to use C# 11 and which requires the use of a .NET 7 sdk when building.

For normal C# projects I can simply use a global.json to specify .NET 7 (when using a new enough VS) while keeping TargetFramework=6.0.

When doing the same with C++/CLI projects I get a NETSDK1145 error:

Error NETSDK1145 The Apphost pack is not installed and NuGet package restore is not supported. Upgrade Visual Studio, remove global.json if it specifies a certain SDK version, and uninstall the newer SDK. For more options visit https://aka.ms/targeting-apphost-pack-missing Pack Type:Apphost, Pack directory: C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64, targetframework: net6.0, Pack PackageId: Microsoft.NETCore.App.Host.win-x64, Pack Package Version: 6.0.16 C:\Program Files\dotnet\sdk\7.0.203\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets 135

The link says something about setting

<ItemGroup>
  <KnownAppHostPack Update="@(KnownAppHostPack)">
    <AppHostPackVersion Condition="'%(TargetFramework)' == 'TARGETFRAMEWORK'">EXISTINGVERSION</AppHostPackVersion>
  </KnownAppHostPack>
</ItemGroup>

but does not say anything about possible problems or the consequences of doing so. I'm also not sure how I'd do that if people might have different .NET 7 SDKs installed (I don't want to force a single SDK).


Solution

  • So far just setting the app host package to a .NET 6 variant while building using .NET 7 seems to be working just fine.

    To make this streamlined to use, I wrote a simple inline roslyn code factory that finds the newest app host package for the required target framework (nb the hardcoded runtime identifier - you'll have to see what msbuild identifier exists for a general solution).

    On the build system the used .NET SDK and its runtime version are hardcoded for reproducability

    <UsingTask TaskName="GetAppHostVersionTask"
        TaskFactory="RoslynCodeTaskFactory"
        AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
        <ParameterGroup>
            <NetCoreTargetingPackRoot ParameterType="System.String" Required="true" />
            <KnownAppHostPack ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
            <RuntimeIdentifiers ParameterType="System.String" Required="true" />
            <TargetFramework ParameterType="System.String" Required="true" />
            <HighestSupportedVersion ParameterType="System.String" Output="true" />
        </ParameterGroup>
        <Task>
            <Code Type="Fragment" Language="cs">
    <![CDATA[
    #pragma warning disable CS0162 // False positive warning
    var runtimeName = this.KnownAppHostPack[0].ItemSpec;
    var packDirectory = Path.Combine(this.NetCoreTargetingPackRoot, $"{runtimeName}.Host.{this.RuntimeIdentifiers}");
    var supportedVersion = this.TargetFramework.Substring("net".Length);
    var versionToUse = Directory.EnumerateDirectories(packDirectory)
          .Select(Path.GetFileName)
          .Where(dirName => dirName.StartsWith(supportedVersion, StringComparison.OrdinalIgnoreCase))
          .OrderByDescending(Version.Parse)
          .FirstOrDefault();
    if (versionToUse is null)
    {
     this.Log.LogError($"'{packDirectory}' does not contain any pack runtime for target framework {this.TargetFramework}.");
     return false;
    }
    this.Log.LogMessage(MessageImportance.High, $"AppHostPackVersion set to {versionToUse}");
    this.HighestSupportedVersion = versionToUse;
    return true;
    ]]>
    </Code>
        </Task>
    </UsingTask>
    
    <Target Name="SetAppHostPackVersion" BeforeTargets="ProcessFrameworkReferences">
        <GetAppHostVersionTask
            NetCoreTargetingPackRoot="$(NetCoreTargetingPackRoot)"
            KnownAppHostPack="@(KnownAppHostPack)"
            RuntimeIdentifiers="win-$(Platform)"
            TargetFramework="$(TargetFramework)"
            Condition=" '$(MyAppHostPackVersion)' == '' ">
            <Output TaskParameter="HighestSupportedVersion" PropertyName="MyAppHostPackVersion" />
        </GetAppHostVersionTask>
        <ItemGroup>
            <KnownAppHostPack Update="@(KnownAppHostPack)">
                <AppHostPackVersion>$(MyAppHostPackVersion)</AppHostPackVersion>
            </KnownAppHostPack>
            <KnownFrameworkReference Update="@(KnownFrameworkReference)">
                <TargetingPackVersion>$(MyAppHostPackVersion)</TargetingPackVersion>
            </KnownFrameworkReference>
        </ItemGroup>
    </Target>
    

    I'll report back if there's any bug reports that can be traced back to this, but so far it looks good. Seems like the ABI is stable across versions.