Exclude target framework from NuGet package
Asked Answered
P

2

5

How can i exclude specific target framework from nuspec (NuGet package) generation?

This is my csproj:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFrameworks>netstandard2.0;net5.0;net5.0-windows</TargetFrameworks>
        <IsPackable>true</IsPackable>
    </PropertyGroup>

    <PropertyGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
        <IsPackable>false</IsPackable>
    </PropertyGroup>
</Project>

dotnet pack command generates NuGet package that contains all target frameworks not only netstandard2.0 and net5.0

Generated nuspec:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>ExampleLibrary</id>
    <version>1.0.0</version>
    <authors>ExampleLibrary</authors>
    <description>Package Description</description>
    <dependencies>
      <group targetFramework="net5.0" />
      <group targetFramework="net5.0-windows7.0" />
      <group targetFramework=".NETStandard2.0" />
    </dependencies>
  </metadata>
</package>
Playboy answered 9/10, 2021 at 17:29 Comment(2)
I don't know if such thing makes sense. If your library uses something specific from net5.0-windows, then it will be not compatible with netstandard2.0, nor with net5.0. Do you really need net5.0-windows in library?Repercussion
The library has additonal logic for windows forms thats why it targets net5.0-windows too, but is consumend via project reference no need for NuGet package.Playboy
P
10
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFrameworks>netstandard2.0;net5.0;net5.0-windows7.0</TargetFrameworks>
        <IsPackable>true</IsPackable>
        <GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);_ExcludeTargetFramework;_ExcludeTargetFrameworkDependency</GenerateNuspecDependsOn>
    </PropertyGroup>

    <Target Name="_ExcludeTargetFramework" AfterTargets="_GetTargetFrameworksOutput" BeforeTargets="_WalkEachTargetPerFramework">
        <ItemGroup>
            <_TargetFrameworks Remove="net5.0-windows7.0" />
        </ItemGroup>
    </Target>

    <Target Name="_ExcludeTargetFrameworkDependency" AfterTargets="_WalkEachTargetPerFramework" BeforeTargets="GenerateNuspec">
        <ItemGroup>
            <_FrameworksWithSuppressedDependencies Include="net5.0-windows7.0" />
        </ItemGroup>
    </Target>
</Project>

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>ExampleLibrary</id>
    <version>1.0.0</version>
    <authors>ExampleLibrary</authors>
    <description>Package Description</description>
    <dependencies>
      <group targetFramework="net5.0" />
      <group targetFramework=".NETStandard2.0" />
    </dependencies>
  </metadata>
</package>
Playboy answered 10/10, 2021 at 12:59 Comment(0)
O
0

Remove all the dependencies (including TargetFramework)

In case you want to remove all dependencies (ie all target frameworks), the above solution won't work. Build will fail with the following error:

error MSB4044: The "ResolvePackageAssets" task was not given a value for the required parameter "TargetFramework".

Easy way

In case you just want to remove all dependencies, you can use the SuppressDependenciesWhenPacking property.

<PropertyGroup>
    <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
</PropertyGroup>

Full control of Nuspec

Otherwise, if you want to have a full control on the content of Nuspec after a first pass of generation, you can use a variation of this:

<?xml version="1.0" encoding="utf-8"?>
<Project>

  <UsingTask
    TaskName="RemoveDependencies"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup>
      <Nuspec ParameterType="System.String" Required="true"/>
    </ParameterGroup>
    <Task>
      <Reference Include="System.Xml"/>
      <Reference Include="System.Xml.Linq"/>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Using Namespace="System.Xml.Linq"/>
      <Code Type="Fragment" Language="cs">
        <!-- Modify XML of the NuSpec -->
        <![CDATA[
            XElement doc = XElement.Load(Nuspec);
            var dependenciesElement = doc.Descendants(XName.Get("dependencies", "http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd")).FirstOrDefault();
            if (dependenciesElement != null)
            {
                dependenciesElement.Remove();
            }

            using (var textWriter = File.CreateText(Nuspec))
            {
                doc.Save(textWriter);
            }]]>
      </Code>
    </Task>
  </UsingTask>

  <!-- From https://mcmap.net/q/2030275/-how-to-inject-a-custom-dependency-in-an-msbuild-nuget-pack-generated-nuspec -->

  <!-- Disable nupkg generation before running pack -->
  <Target Name="__DisablePacking" BeforeTargets="GenerateNuspec" Condition="$(NuspecFile) == '' And $(IsPackable) != 'false'">
    <PropertyGroup>
      <ContinuePackingAfterGeneratingNuspec>false</ContinuePackingAfterGeneratingNuspec>
    </PropertyGroup>
  </Target>

  <!-- Remove .Net Standard 2.0 dependency as it's a native package -->
  <!-- Modify the generated nuspec file and rerun the pack target -->
  <Target Name="__ExcludeTargetFrameworkDependency" AfterTargets="Pack" Condition="$(NuspecFile) == '' And $(IsPackable) != 'false'">
    <!-- Get the nuspec file name -->
    <PropertyGroup>
      <_NugetPackOutputAsProperty>@(NuGetPackOutput)</_NugetPackOutputAsProperty>
    </PropertyGroup>
    <ItemGroup>
      <_NugetPackOutputAsItem Remove="@(_NugetPackOutputAsItem)"/>
      <_NugetPackOutputAsItem Include="$(_NugetPackOutputAsProperty.Split(';'))" />
    </ItemGroup>
    <PropertyGroup>
      <__NuspecFileName>%(_NugetPackOutputAsItem.Identity)</__NuspecFileName>
    </PropertyGroup>

    <Message Importance="High" Text="Remove Dependencies from $(__NuspecFileName)" />
    <RemoveDependencies Nuspec="$(__NuspecFileName)" />

    <!-- Invoke the Pack target again with packing enabled -->
    <PropertyGroup>
      <ContinuePackingAfterGeneratingNuspec>true</ContinuePackingAfterGeneratingNuspec>
    </PropertyGroup>

    <MsBuild
            Projects="$(MSBuildProjectFullPath)"
            Targets="Pack"
            Properties="NuspecFile=$(__NuspecFileName);NoPackageAnalysis=true">
    </MsBuild>
  </Target>

</Project>

Notes:

  • GenerateNuspec target is doing both generation and packing. To disable packing after nuspec generation, we can use the ContinuePackingAfterGeneratingNuspec property.
  • Pack target is invoked twice (once by the user) and the second time with the system after first pack.
  • __ExcludeTargetFrameworkDependency rely on the Nuspec File value to be empty to execute on the first pack and to not execute on the second run. It obviously won't work if a Nuspec file is provided for the first run (with NuspecFile property).
  • Note the usage of And $(IsPackable) != 'false' to avoid invoking the target on test projects.
  • All NuGet targets like GenerateNuspec are located in C:\Program Files\dotnet\sdk\6.0.308\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets
  • All NuGet tasks for .NET 6, like PackTask, are located in C:\Program Files\dotnet\sdk\6.0.308\Sdks\NuGet.Build.Tasks.Pack\Desktop\NuGet.Build.Tasks.Pack.dll
Ona answered 23/1, 2023 at 9:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.