Search code examples
c#msbuildcsproj

Only build one TargetFramework while defining multiple <TargetFrameworks> to support custom builds for other TargetFramework


The idea is simple: I want to have a single project file, that can build multiple target frameworks and have references to different DLLs based on the framework used.

The issue that I am running into, is that when I select build on the project, it will build all frameworks defined in - but the .DLL referenced is newer than one of those target frameworks (which I need to build the same project with an older framework)

I only want to build a single framework automatically, then build the others manually.

<Project Sdk="Microsoft.NET.Sdk" InitialTargets="Test">
  <Target Name="Test">
    <Message Importance="high" Text="-- Building $(MSBuildProjectFile), TF=$(TargetFramework), Config=$(Configuration), Version=$(MyCustomVersion) --" />
  </Target>
  <PropertyGroup>
    <TargetFrameworks>net472;net48</TargetFrameworks>
    <MyCustomVersion Condition="'$(MyCustomVersion)' == ''">2022</MyCustomVersion>
    <OutputPath>bin\$(Configuration)\$(MyCustomVersion)\</OutputPath>
    <Configurations>Debug;Release</Configurations>
  </PropertyGroup>
  <PropertyGroup Condition="'$(MyCustomVersion)' == '2021' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);V2021</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition="'$(MyCustomVersion)' == '2021' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);V2021</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition="'$(MyCustomVersion)' == '2021' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);V2021</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition="'$(MyCustomVersion)' == '2022' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);V2022</DefineConstants>
  </PropertyGroup>
  <ItemGroup Condition="'$(MyCustomVersion)' == '2022' ">
    <Reference Include="Some48API">
      <HintPath>path\to\Some48API.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <Target Name="BuildOthers" BeforeTargets="DispatchToInnerBuilds" Condition="'$(MyCustomVersion)' == '2022'">
    <Message Importance="high" Text="*** building 2019 ***" />
    <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);TargetFramework=net472;MyCustomVersion=2019" />
    <Message Importance="high" Text="*** building 2020 ***" />
    <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);TargetFramework=net472;MyCustomVersion=2020" />
    <Message Importance="high" Text="*** building 2021 ***" />
    <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);TargetFramework=net48;MyCustomVersion=2021" />
  </Target>
</Project>

In the above project file, I have two TargetFrameworks: net472 and net48. If I instead replace that with <TargetFramework>net48</TargetFramework> (and modify BeforeTargets="DispatchToInnerBuilds" to BeforeTargets="PreBuildEvent"), I will get a compile error

0>C:\Program Files\dotnet\sdk\6.0.202\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(267,5): Error NETSDK1005 : Assets file 'C:\Users\me\source\repos\Solution1\ClassLibrary1\obj\project.assets.json' doesn't have a target for 'net472'. Ensure that restore has run and that you have included 'net472' in the TargetFrameworks for your project.

So it seems like I need to provide both but I don't want to build both of them without my own custom control over it...

This issue is inspired by TheBuildingCoder and specifically the RevitAPI dll files are net48 only for 2021/2022 - so I don't want to build net47 as I get compile errors due to broken reference...


Solution

  • I realize that what I was trying to do doesn't make a whole lot of sense... I want to prevent VS from building the other targets, only to manually build the other targets...

    So instead of trying to prevent other targets getting built automatically, I can embrace that instead.

    The changed project file looks like this (also more simplified):

    <Project Sdk="Microsoft.NET.Sdk" InitialTargets="Test">
      <Target Name="Test">
        <Message Importance="high" Text="-- Building $(MSBuildProjectFile), TF=$(TargetFramework), Config=$(Configuration), Version=$(MyCustomVersion) --" />
      </Target>
      <PropertyGroup>
        <TargetFrameworks>net472;net48</TargetFrameworks>
          <MyCustomVersion Condition="'$(MyCustomVersion)' == '' and '$(TargetFramework)' == 'net472'">2019</MyCustomVersion>
          <MyCustomVersion Condition="'$(MyCustomVersion)' == '' and '$(TargetFramework)' == 'net48'">2022</MyCustomVersion>
        <OutputPath>bin\$(Configuration)\$(MyCustomVersion)\</OutputPath>
        <Configurations>Debug;Release</Configurations>
        <IsPublishable>False</IsPublishable>
      </PropertyGroup>
      <PropertyGroup>
        <PlatformTarget>x64</PlatformTarget>
        <DefineConstants>$(DefineConstants);V$(MyCustomVersion)</DefineConstants>
      </PropertyGroup>
      <Target Name="BuildOthers" BeforeTargets="DispatchToInnerBuilds" Condition=" '$(MyCustomVersion)' == '' ">
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);TargetFramework=net472;MyCustomVersion=2020" />
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);TargetFramework=net48;MyCustomVersion=2021" />
      </Target>
    </Project>
    

    The only downside is that it doesn't build them in preferable order:

    1>-- Building ClassLibrary1.csproj, TF=, Config=Debug, Version= --
    1>-- Building ClassLibrary1.csproj, TF=net472, Config=Debug, Version=2020 --
    1>ClassLibrary1 -> C:\Users\me\source\repos\Solution1\ClassLibrary1\bin\Debug\2020\net472\ClassLibrary1.dll
    1>-- Building ClassLibrary1.csproj, TF=net48, Config=Debug, Version=2021 --
    1>ClassLibrary1 -> C:\Users\me\source\repos\Solution1\ClassLibrary1\bin\Debug\2021\net48\ClassLibrary1.dll
    1>-- Building ClassLibrary1.csproj, TF=net472, Config=Debug, Version=2019 --
    1>ClassLibrary1 -> C:\Users\me\source\repos\Solution1\ClassLibrary1\bin\Debug\2019\net472\ClassLibrary1.dll
    1>-- Building ClassLibrary1.csproj, TF=net48, Config=Debug, Version=2022 --
    1>ClassLibrary1 -> C:\Users\me\source\repos\Solution1\ClassLibrary1\bin\Debug\2022\net48\ClassLibrary1.dll
    

    2020,2021,2019,2022 but that is a minor inconvenience and can probably be fixed with some shuffling