Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ You can also add a reference to a CLI *tools* program like the following:
<Project Sdk='Microsoft.NET.Sdk'>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType>
<PackFolder>tools</PackFolder>
<!-- We don't need this particular tool in a framework-specific subfolder under /tools -->
<BuildOutputFrameworkSpecific>false</BuildOutputFrameworkSpecific>
Expand All @@ -465,6 +466,20 @@ content inference.
This section contains miscellaneous useful features that are typically used in advanced scenarios and
are not necessarily mainstream.

### PackAsPublish for CLI tools

When a project's output type is `Exe` and it's not set to `PackAsTool=true` (used specifically for .NET tools),
it will default to be use the `Publish` output for packing. This is typically what you want for a CLI
project, since dependencies are included in the publish directory automatically without having to annotate
any references with `PrivateAssets=all`.

This can be turned off by setting `PackAsPublish=false` on the project, which will cause the project
to be packed as a regular class library, with the dependencies inference rules applied (such as
`PrivateAssets=all` for package reference and `CopyLocal=true` for references).

When packing as publish, the output won't be framework-specific by default, and will just contribute
the published contents to the specified `PackFolder`.

### Dynamically Extending Package Contents

If you need to calculate additional items to inject into the package dynamically, you can run a target
Expand Down
41 changes: 38 additions & 3 deletions src/NuGetizer.Tasks/NuGetizer.Inference.targets
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ Copyright (c) .NET Foundation. All rights reserved.
</Target>

<Target Name="InferPackageContents" DependsOnTargets="$(InferPackageContentsDependsOn);_CollectInferenceCandidates" Returns="@(PackageFile)">
<Error Code="NG1004" Condition="'$(PackAsTool)' == 'true' and '$(PackAsPublish)' == 'true'" Text="PackAsTool and PackAsPublish are mutually exclusive." />

<!-- Even if all these conditions are false, the user can still explicitly pack the file, of course -->
<ItemGroup Label="Readme" Condition="'$(PackReadme)' == 'true' and '$(PackageReadmeFile)' != '' and '$(IsPackable)' == 'true'">
Expand Down Expand Up @@ -294,7 +295,7 @@ Copyright (c) .NET Foundation. All rights reserved.
'%(_InferenceCandidateWithTargetPath.CopyToOutputDirectory)' == 'Never'" />
</ItemGroup>

<ItemGroup Label="BuildOutput Inference" Condition="'$(PackBuildOutput)' == 'true'">
<ItemGroup Label="BuildOutput Inference" Condition="'$(PackBuildOutput)' == 'true' and '$(PackAsPublish)' != 'true'">
<!-- Unfortunately, even with https://github.com/Microsoft/msbuild/pull/1115, when multi-targeting
.NETFramework, the desktop WinFX.targets are imported which don't have the fix, so we need to
do it "the old way" for this particular output group -->
Expand All @@ -318,9 +319,20 @@ Copyright (c) .NET Foundation. All rights reserved.
<_InferredPackageFile Include="@(_InferredProjectOutput -> Distinct())" />
</ItemGroup>

<ItemGroup Label="Publishable Inference" Condition="'$(PackAsPublish)' == 'true' and '$(PackAsTool)' != 'true'">
<_InferredPublishItem Include="@(PublishItemsOutputGroupOutputs -> '%(OutputPath)')" />
<_InferredPackageFile Include="@(_InferredPublishItem -> '%(FullPath)')">
<PackFolder>$(PackFolder)</PackFolder>
<FrameworkSpecific>false</FrameworkSpecific>
<!-- NOTE: we don't set `BuildOutputFrameworkSpecific` since we're not packing the build output
but rather the *publish* output. Users could change that by setting a TF-specific PackFolder -->
</_InferredPackageFile>
</ItemGroup>

<ItemGroup Label="References Inference">
<_InferredPackageFile Include="@(PackageReference)"
Condition="'$(PackAsTool)' != 'true' and
'$(PackAsPublish)' != 'true' and
'%(PackageReference.Identity)' != 'NuGetizer' and
'%(PackageReference.Identity)' != 'NETStandard.Library' and
'%(PackageReference.PrivateAssets)' != 'all' and
Expand All @@ -334,6 +346,7 @@ Copyright (c) .NET Foundation. All rights reserved.
TBD: maybe include ResolvedFrom=ImplicitlyExpandDesignTimeFacades too? -->
<_InferredPackageFile Include="@(ReferencePath->'%(OriginalItemSpec)')"
Condition="'$(PackAsTool)' != 'true' and
'$(PackAsPublish)' != 'true' and
'$(PackFrameworkReferences)' == 'true' and
'%(ReferencePath.ResolvedFrom)' == '{TargetFrameworkDirectory}' and
'%(ReferencePath.Pack)' != 'false'">
Expand Down Expand Up @@ -385,7 +398,9 @@ Copyright (c) .NET Foundation. All rights reserved.

</Target>

<Target Name="_CollectPrimaryOutputDependencies" DependsOnTargets="ReferenceCopyLocalPathsOutputGroup;RunResolvePackageDependencies" Returns="@(ImplicitPackageReference)">
<Target Name="_CollectPrimaryOutputDependencies"
DependsOnTargets="ReferenceCopyLocalPathsOutputGroup;RunResolvePackageDependencies"
Returns="@(ImplicitPackageReference)">
<Error Code="NG1003" Text="Centrally managed package versions is only supported when using the Microsoft.NET.Sdk."
Condition="'$(ManagePackageVersionsCentrally)' == 'true' and '$(UsingMicrosoftNETSdk)' != 'true'" />
<ItemGroup>
Expand Down Expand Up @@ -416,6 +431,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<Target Name="_ResolvePackageDependencies" Condition="'$(UsingMicrosoftNETSdk)' == 'true'" DependsOnTargets="RunResolvePackageDependencies" />

<Target Name="InferPrimaryOutputDependencies"
Condition="'$(PackAsTool)' != 'true' and '$(PackAsPublish)' != 'true'"
Inputs="@(_PrimaryOutputRelatedFile)"
Outputs="%(_PrimaryOutputRelatedFile.NuGetPackageId)"
Returns="@(_InferredPackageFile)"
Expand Down Expand Up @@ -470,7 +486,7 @@ Copyright (c) .NET Foundation. All rights reserved.
</ItemGroup>
</Target>

<Target Name="_SetInferPackageContentsDependsOn" AfterTargets="_SetPropertiesFromCapabilities">
<Target Name="_SetInferenceProperties" AfterTargets="_SetPropertiesFromCapabilities">
<PropertyGroup>
<!-- NOTE: this avoids taking dependencies on targets that are only available when the project supports the concept of references -->
<_SupportsReferences Condition="
Expand Down Expand Up @@ -500,7 +516,26 @@ Copyright (c) .NET Foundation. All rights reserved.
$(InferPackageContentsDependsOn);
AllProjectOutputGroups
</InferPackageContentsDependsOn>

<!-- Smarter inference for publishable console apps
* PackAsTool should still do what SDK Pack does.
* PackAsPublish=false turns off this inference.
* IsPublishable is set by the SDK, but for everything, even libs :(,
but we can use that as an alternative off switch too.
* Finally, the CrossPlatformExecutable capability is set by the SDK for
.NETCoreApp projects that are actually executable (Exe or WinExe)
-->
<PackAsPublish Condition="'$(PackAsPublish)' == '' and
'$(PackAsTool)' != 'true' and
'$(IsPublishable)' == 'true' and
$(_AllProjectCapabilities.Contains('CrossPlatformExecutable'))">true</PackAsPublish>

<InferPackageContentsDependsOn Condition="'$(PackAsPublish)' == 'true'">
$(InferPackageContentsDependsOn);
PublishItemsOutputGroup
</InferPackageContentsDependsOn>
</PropertyGroup>

</Target>

</Project>
54 changes: 54 additions & 0 deletions src/NuGetizer.Tests/given_a_tool_project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,59 @@ public void when_pack_folder_tool_but_no_pack_as_tool_then_packs_dependencies_no
Identity = "Microsoft.Extensions.DependencyModel"
}));
}

[Fact]
public void when_pack_folder_tool_no_pack_as_tool_and_executable_then_packs_as_publish_with_no_dependencies()
{
var result = Builder.BuildProject(
"""
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<PackageId>MyTool</PackageId>
<TargetFramework>net6.0</TargetFramework>
<PackageId>MyTool</PackageId>
<PackFolder>tools</PackFolder>
</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.All(result.Items, item => item.Matches(new
{
OutputGroup = "PublishItemsOutputGroup",
}));
}

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

Assert.Equal(Microsoft.Build.Execution.BuildResultCode.Failure, result.BuildResult.OverallResult);
}

}
}
3 changes: 1 addition & 2 deletions src/dotnet-nugetize/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@ int Run(string[] args)
"-p:SkipCompilerExecution=true",
"-p:DesignTimeBuild=true",
"-p:DesignTimeSilentResolution=true",
"-p:ResolveAssemblyReferencesSilent=true",
"-p:IsPublishable=false"
"-p:ResolveAssemblyReferencesSilent=true"
});

if (help)
Expand Down