Skip to content

Commit

Permalink
Improve compatibility with SDK Pack for tools
Browse files Browse the repository at this point in the history
This brings nugetizer pretty much to parity with SDK Pack for CLI tools too :).

We leverage the SDK tool packing targets to determine the files to pack, but otherwise preserve nugetizer's inference and packing behavior. Best of both worlds!

The new `InferToolContents` target is the one that transforms the `PackTool` target's `TfmSpecificPackageFile` into our own `None` items for packing. It can also be extended to trim the tool's contents via a custom target that removes items, such as::

```xml
  <Target Name="TrimRuntimes" AfterTargets="InferToolContents">
    <ItemGroup>
      <None Remove="@(None)" Condition="$([MSBuild]::ValueOrDefault('%(None.PackagePath)', '').Contains('/runtimes/'))" />
    </ItemGroup>
  </Target>
```

(this target would remove all runtimes from the tool package, but otherwise preserve the package contents).

Fixes #134
  • Loading branch information
kzu committed May 31, 2022
1 parent 88eb598 commit 4991a85
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 4 deletions.
5 changes: 4 additions & 1 deletion src/NuGetizer.Tasks/NuGetizer.Compatibility.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ Copyright (c) .NET Foundation. All rights reserved.

<PropertyGroup Label="SDK Pack Compat">
<PackOnBuild Condition="'$(GeneratePackageOnBuild)' == 'true'">true</PackOnBuild>
<PackFolder Condition="'$(IsTool)' == 'true' or '$(PackAsTool)' == 'true'">tool</PackFolder>
<GeneratePackageOnBuild Condition="'$(GeneratePackageOnBuild)' == '' and '$(PackOnBuild)' == 'true'">true</GeneratePackageOnBuild>

<PackFolder Condition="'$(PackFolder)' == '' and ('$(IsTool)' == 'true' or '$(PackAsTool)' == 'true')">tools</PackFolder>
<PackFolder Condition="'$(BuildOutputTargetFolder)' != ''">$(BuildOutputTargetFolder)</PackFolder>
<PackSymbols Condition="'$(PackSymbols)' == '' and '$(IncludeSymbols)' != ''">$(IncludeSymbols)</PackSymbols>
<PackContent Condition="'$(PackContent)' == '' and '$(IncludeContentInPack)' != ''">$(IncludeContentInPack)</PackContent>
Expand All @@ -31,6 +33,7 @@ Copyright (c) .NET Foundation. All rights reserved.
</PropertyGroup>

<PropertyGroup Label="Legacy NuGetizer Compat">
<PackFolder Condition="'$(PackFolder)' == 'tool'">tools</PackFolder>
<PackFolder Condition="'$(PackFolder)' == '' and '$(BuildOutputKind)' != ''">$(BuildOutputKind)</PackFolder>
<PackFolder Condition="'$(PackFolder)' == '' and '$(PrimaryOutputKind)' != ''">$(PrimaryOutputKind)</PackFolder>

Expand Down
39 changes: 36 additions & 3 deletions src/NuGetizer.Tasks/NuGetizer.Inference.targets
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ Copyright (c) .NET Foundation. All rights reserved.

<!-- Whether to include @(Content) items with CopyToOutputDirectory != '' in the package -->
<PackContent Condition="'$(PackContent)' == ''">true</PackContent>

<!-- Whether to include @(BuiltProjectOutputGroupOutput), @(DocumentationProjectOutputGroupOutput) and @(SatelliteDllsProjectOutputGroupOutput) items in the package -->
<PackBuildOutput Condition="'$(PackBuildOutput)' == '' and '$(IsPackagingProject)' != 'true'">true</PackBuildOutput>
<!-- When packing as a tool (SDK compatibility mode), primary output should not be packed by us, since the SDK PackTool target will already collect the output of /publish instead. -->
<PackBuildOutput Condition="'$(PackBuildOutput)' == '' and '$(IsPackagingProject)' != 'true' and '$(PackAsTool)' != 'true'">true</PackBuildOutput>

<!-- Whether to include @(DebugSymbolsProjectOutputGroupOutput) items in the package -->
<PackSymbols Condition="'$(PackSymbols)' == '' and '$(PackBuildOutput)' == 'true'">true</PackSymbols>

Expand Down Expand Up @@ -206,6 +209,34 @@ Copyright (c) .NET Foundation. All rights reserved.
</ItemGroup>
</Target>

<Target Name="InferToolContents" BeforeTargets="_CollectInferenceCandidates"
Condition="'$(UsingMicrosoftNETSdk)' == 'true' and '$(PackAsTool)' == 'true'"
DependsOnTargets="PackTool">

<PropertyGroup>
<!-- When packing as SDK pack, primary output should not be packed by us, since the SDK PackTool will
already collect the output of /publish instead. -->
<PackBuildOutput>false</PackBuildOutput>
</PropertyGroup>

<ItemGroup>
<!-- Ensure we have a full relative path including file name as PackagePath -->
<TfmSpecificPackageFile>
<PackageFile>%(Filename)%(Extension)</PackageFile>
</TfmSpecificPackageFile>
<TfmSpecificPackageFile>
<PackagePathHasFilename>false</PackagePathHasFilename>
<PackagePathHasFilename Condition="$([MSBuild]::ValueOrDefault('%(PackagePath)', '').EndsWith('%(PackageFile)'))"></PackagePathHasFilename>
</TfmSpecificPackageFile>
<TfmSpecificPackageFile Condition="'%(PackagePathHasFilename)' == 'false'">
<PackagePath>$([MSBuild]::ValueOrDefault('%(PackagePath)', '').TrimEnd('/'))/%(PackageFile)</PackagePath>
</TfmSpecificPackageFile>
<None Include="@(TfmSpecificPackageFile)" />

</ItemGroup>

</Target>

<Target Name="InferPackageContents" DependsOnTargets="$(InferPackageContentsDependsOn);_CollectInferenceCandidates" Returns="@(PackageFile)">

<!-- Even if all these conditions are false, the user can still explicitly pack the file, of course -->
Expand Down Expand Up @@ -275,7 +306,8 @@ Copyright (c) .NET Foundation. All rights reserved.

<ItemGroup Label="References Inference">
<_InferredPackageFile Include="@(PackageReference)"
Condition="'%(PackageReference.Identity)' != 'NuGetizer' and
Condition="'$(PackAsTool)' != 'true' and
'%(PackageReference.Identity)' != 'NuGetizer' and
'%(PackageReference.Identity)' != 'NETStandard.Library' and
'%(PackageReference.PrivateAssets)' != 'all' and
'%(PackageReference.Pack)' != 'false'">
Expand All @@ -286,7 +318,8 @@ Copyright (c) .NET Foundation. All rights reserved.
it also includes mscorlib which we don't need
TBD: maybe include ResolvedFrom=ImplicitlyExpandDesignTimeFacades too? -->
<_InferredPackageFile Include="@(ReferencePath->'%(OriginalItemSpec)')"
Condition="'$(PackFrameworkReferences)' == 'true' and
Condition="'$(PackAsTool)' != 'true' and
'$(PackFrameworkReferences)' == 'true' and
'%(ReferencePath.ResolvedFrom)' == '{TargetFrameworkDirectory}' and
'%(ReferencePath.Pack)' != 'false'">
<PackFolder>FrameworkReference</PackFolder>
Expand Down
89 changes: 89 additions & 0 deletions src/NuGetizer.Tests/given_a_tool_project.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Xunit;
using Xunit.Abstractions;

namespace NuGetizer
{
public class given_a_tool_project
{
ITestOutputHelper output;

public given_a_tool_project(ITestOutputHelper output) => this.output = output;

[Fact]
public void when_pack_as_tool_then_packs_no_dependencies()
{
var result = Builder.BuildProject(@"
<Project Sdk='Microsoft.Build.NoTargets/3.5.0'>
<PropertyGroup>
<PackageId>MyTool</PackageId>
<TargetFramework>net6.0</TargetFramework>
<PackFolder>tools</PackFolder>
<PackAsTool>true</PackAsTool>
</PropertyGroup>
<ItemGroup>
<PackageReference Include='Microsoft.Extensions.DependencyModel' Version='6.0.0' />
</ItemGroup>
</Project>",
"GetPackageContents", output);

result.AssertSuccess(output);
Assert.DoesNotContain(result.Items, item => item.Matches(new
{
Identity = "Microsoft.Extensions.DependencyModel"
}));
Assert.Contains(result.Items, item => item.Matches(new
{
PackageFile = "Microsoft.Extensions.DependencyModel.dll"
}));
}

[Fact]
public void when_pack_as_tool_then_packs_dotnet_tool_runtime_assets()
{
var result = Builder.BuildProject(@"
<Project Sdk='Microsoft.NET.Sdk'>
<PropertyGroup>
<AssemblyName>MyTool</AssemblyName>
<PackageId>MyTool</PackageId>
<TargetFramework>net6.0</TargetFramework>
<PackFolder>tools</PackFolder>
<PackAsTool>true</PackAsTool>
</PropertyGroup>
</Project>",
"GetPackageContents", output);

result.AssertSuccess(output);
Assert.Contains(result.Items, item => item.Matches(new
{
PackageFile = "DotnetToolSettings.xml"
}));
Assert.Contains(result.Items, item => item.Matches(new
{
PackageFile = "MyTool.deps.json"
}));
}

[Fact]
public void when_pack_folder_tool_but_no_pack_as_tool_then_packs_dependencies_normally()
{
var result = Builder.BuildProject(@"
<Project Sdk='Microsoft.Build.NoTargets/3.5.0'>
<PropertyGroup>
<PackageId>MyTool</PackageId>
<TargetFramework>net6.0</TargetFramework>
<PackFolder>tools</PackFolder>
</PropertyGroup>
<ItemGroup>
<PackageReference Include='Microsoft.Extensions.DependencyModel' Version='6.0.0' />
</ItemGroup>
</Project>",
"GetPackageContents", output);

result.AssertSuccess(output);
Assert.Contains(result.Items, item => item.Matches(new
{
Identity = "Microsoft.Extensions.DependencyModel"
}));
}
}
}

0 comments on commit 4991a85

Please sign in to comment.