Skip to content

Commit 75e514c

Browse files
authored
Merge pull request #33 from kzu/dev
Dev > Main
2 parents da5d096 + d95ef23 commit 75e514c

11 files changed

+265
-38
lines changed

README.md

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ Simple, flexible, intuitive and powerful NuGet packaging.
99
[![GitHub](https://img.shields.io/badge/-source-181717.svg?logo=GitHub)](https://github.com/kzu/stunts)
1010

1111
[![CI Version](https://img.shields.io/endpoint?url=https://shields.kzu.io/vpre/nugetizer/main&label=nuget.ci&color=brightgreen)](https://pkg.kzu.io/index.json)
12-
[![GH CI Status](https://github.com/kzu/nugetizer/workflows/build/badge.svg?branch=main)](https://github.com/kzu/nugetizer/actions?query=branch%3Amain+workflow%3Abuild+)
13-
[![AzDO CI Status](https://dev.azure.com/kzu/oss/_apis/build/status/nugetizer?branchName=main)](http://build.azdo.io/kzu/oss/44)
12+
[![CI Status](https://github.com/kzu/nugetizer/workflows/build/badge.svg?branch=main)](https://github.com/kzu/nugetizer/actions?query=branch%3Amain+workflow%3Abuild+)
1413

1514

1615
# Why
@@ -176,7 +175,7 @@ As usual, you can change this default behavior by using `Pack=false` metadata.
176175

177176
### ProjectReference
178177

179-
Unlike SDK Pack that [considers project references as package references by default](https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#project-to-project-references), NuGetizer has an explicit contract between projects: the `GetPackageContents` target. This target is invoked when packing project references, and it returns whatever the referenced project exposes as package contents (including the inference rules above). If the project is *packable* (that is, it produces a package, denoted by the presence of a `PackageId` property), it will be packed as a dependency/package reference instead.
178+
Unlike SDK Pack that [considers project references as package references by default](https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#project-to-project-references), NuGetizer has an explicit contract between projects: the `GetPackageContents` target. This target is invoked when packing project references, and it returns whatever the referenced project exposes as package contents (including the inference rules above). If the project is *packable* (that is, it produces a package, denoted by the presence of a `PackageId` property or `IsPackable=true`, for compatibility with SDK Pack), it will be packed as a dependency/package reference instead.
180179

181180
This means that by default, things Just Work: if you reference a library with no `PackageId`, it becomes part of whatever output your main project produces (analyzer, tools, plain lib). The moment you decide you want to make it a package on its own, you add the required metadata properties to that project and it automatically becomes a dependency instead.
182181

@@ -262,3 +261,58 @@ Authoring, testing and iterating with your nuget packages should be easy and str
262261
c. Clean the NuGet HTTP cache: this avoids a subsequent restore from a test/sample project from getting an older version from there, in case you build locally the same version of a previously restored one from an HTTP source.
263262

264263
These cleanups only apply in local builds, never in CI, and you can turn them all off by setting `EnablePackCleanup=false`.
264+
265+
## Advanced Features
266+
267+
This section contains miscelaneous useful features that are typically used in advanced scenarios and
268+
are not necessarily mainstream.
269+
270+
### Packing arbitrary files from referenced packages
271+
272+
If you want to pack files from referenced packages, you can simply add `PackageReference` attribute
273+
to `PackageFile`. Say we want to resuse the awesome icon from the
274+
[ThisAssembly](https://nuget.org/packages/ThisAssembly) package, we can just bring it in with:
275+
276+
```xml
277+
<ItemGroup>
278+
<PackageFile Include="icon-128.png" PackagePath="icon.png" PackageReference="ThisAssembly" />
279+
</ItemGroup>
280+
```
281+
282+
The project will need to reference that package too, of course:
283+
284+
```xml
285+
<ItemGroup>
286+
<PackageReference Include="ThisAssembly" Version="1.0.0" GeneratePathProperty="true" Pack="false" />
287+
</ItemGroup>
288+
```
289+
290+
Note that we had to add the `GeneratePathProperty` to the reference, so that the package-relative
291+
path `icon-128.png` can be properly resolved to the package install location. Also note that in this
292+
particular case, we don't want to pack the reference as a dependency (it's a build-only or development
293+
dependency package). That is, this feature does not require a package dependency for the package reference
294+
content we're bringing in.
295+
296+
It even works for inferred content item types, such as `None`:
297+
298+
```xml
299+
<PropertyGroup>
300+
<PackNone>true</PackNone>
301+
</PropertyGroup>
302+
<ItemGroup>
303+
<None Include="icon-128.png" PackageReference="ThisAssembly" />
304+
</ItemGroup>
305+
```
306+
307+
### Skip Build during Pack
308+
309+
If you are building explicitly prior to running `Pack` (and you're not using
310+
`PackOnBuild=true`), you might want to optimize the process by skipping the
311+
automatic `Build` run that happens by default when you run `Pack` by setting
312+
`BuildOnPack=false`. Not building before `Pack` with `BuildOnPack=false`
313+
can cause the target run to fail since output files expected by the packaging
314+
might be missing (i.e. the primary output, content files, etc.).
315+
316+
This option is useful in combination with `BuildProjectReferences=false` when
317+
packing on CI, since at that point all that's run are the P2P protocol involving
318+
`GetPackageContents`.

src/Directory.Build.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
<!-- Allows source control information to always be present in ThisAssembly -->
3131
<EnableSourceLink>true</EnableSourceLink>
3232
<EnableSourceControlManagerQueries>true</EnableSourceControlManagerQueries>
33+
34+
<!-- We explicitly Build separately from Pack, because otherwise tasks assembly gets locked -->
35+
<BuildOnPack>false</BuildOnPack>
3336
</PropertyGroup>
3437

3538
<PropertyGroup Label="CI" Condition="'$(CI)' == ''">

src/NuGetizer.Tasks/InferImplicitPackageReference.cs

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,31 +51,27 @@ public override bool Execute()
5151
}
5252
}
5353

54-
var inferred = new HashSet<PackageIdentity>();
55-
56-
foreach (var reference in PackageReferences.Where(x =>
57-
"all".Equals(x.GetMetadata("PrivateAssets"), StringComparison.OrdinalIgnoreCase) &&
58-
// Unless explicitly set to Pack=false
59-
(!x.TryGetBoolMetadata("Pack", out var pack) || pack != false) &&
60-
// NETCore/NETStandard are implicitly defined, we never need to bring them as deps.
61-
!(bool.TryParse(x.GetMetadata("IsImplicitlyDefined"), out var isImplicit) && isImplicit)))
54+
var inferred = new Dictionary<PackageIdentity, ITaskItem>();
55+
56+
foreach (var reference in PackageReferences)
6257
{
6358
var identity = new PackageIdentity(reference.ItemSpec, reference.GetMetadata("Version"));
59+
var originalMetadata = (IDictionary<string, string>)reference.CloneCustomMetadata();
6460
foreach (var dependency in FindDependencies(identity, packages))
6561
{
66-
inferred.Add(dependency);
62+
if (!inferred.ContainsKey(dependency))
63+
{
64+
var item = new TaskItem(dependency.Id);
65+
foreach (var metadata in originalMetadata)
66+
item.SetMetadata(metadata.Key, metadata.Value);
67+
68+
item.SetMetadata("Version", dependency.Version);
69+
inferred.Add(dependency, item);
70+
}
6771
}
6872
}
6973

70-
ImplicitPackageReferences = inferred
71-
.Select(x => new TaskItem(
72-
x.Id,
73-
new Dictionary<string, string>
74-
{
75-
{ "Version", x.Version } ,
76-
{ "PrivateAssets", "all" },
77-
}))
78-
.ToArray();
74+
ImplicitPackageReferences = inferred.Values.ToArray();
7975

8076
return true;
8177
}

src/NuGetizer.Tasks/NuGetizer.Inference.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ Copyright (c) .NET Foundation. All rights reserved.
298298

299299
</Target>
300300

301-
<Target Name="_CollectPrimaryOutputDependencies" DependsOnTargets="BuildOnlySettings;ResolveReferences" Returns="@(ImplicitPackageReference)">
301+
<Target Name="_CollectPrimaryOutputDependencies" DependsOnTargets="BuildOnlySettings;RunResolvePackageDependencies;ResolveReferences" Returns="@(ImplicitPackageReference)">
302302
<Error Code="NG1003" Text="Centrally managed package versions is only supported when using the Microsoft.NET.Sdk."
303303
Condition="'$(ManagePackageVersionsCentrally)' == 'true' and '$(UsingMicrosoftNETSdk)' != 'true'" />
304304
<ItemGroup>

src/NuGetizer.Tasks/NuGetizer.Shared.targets

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,23 +239,20 @@ Copyright (c) .NET Foundation. All rights reserved.
239239
=================================================================================================
240240
-->
241241
<PropertyGroup Label="Hidden">
242-
<!-- If we're packing on build, just add Pack as a dependency for Build -->
242+
<_IsInnerBuild Condition="'$(TargetFramework)' != '' and '$(TargetFrameworks)' != ''">true</_IsInnerBuild>
243243
<_ShouldPackOnBuild Condition="'$(PackOnBuild)' == 'true' And '$(IsPackable)' == 'true'">true</_ShouldPackOnBuild>
244-
<BuildDependsOn Condition="'$(NoBuild)' != 'true' And '$(_ShouldPackOnBuild)' == 'true'">
245-
$(BuildDependsOn);
246-
Pack;
247-
</BuildDependsOn>
248-
<!-- If we're not packing on build, set up a dependency from Pack>Build for non-NuProj that are packable, so Build runs before Pack -->
249-
<PackDependsOn Condition="'$(NoBuild)' != 'true' And '$(IsPackagingProject)' != 'true' And '$(_ShouldPackOnBuild)' != 'true' And '$(IsPackable)' == 'true'">
250-
Build;
251-
</PackDependsOn>
252244
<PackDependsOn>
253245
$(PackDependsOn)
254246
GetPackageTargetPath;
255247
GetPackageContents
256248
</PackDependsOn>
257249
</PropertyGroup>
258250

251+
<Target Name="_PackAfterBuild" AfterTargets="Build" DependsOnTargets="Pack"
252+
Condition="'$(_ShouldPackOnBuild)' == 'true' And '$(_IsInnerBuild)' != 'true'" />
253+
<Target Name="_BuildBeforePack" BeforeTargets="Pack" DependsOnTargets="Build"
254+
Condition="'$(BuildOnPack)' != 'false' And '$(_IsInnerBuild)' != 'true' And '$(NoBuild)' != 'true' And '$(IsPackagingProject)' != 'true' And '$(_ShouldPackOnBuild)' != 'true' And '$(IsPackable)' == 'true'" />
255+
259256
<Target Name="Pack" DependsOnTargets="$(PackDependsOn)" Returns="@(_PackageTargetPath)" Condition="'$(IsPackable)' == 'true'">
260257
<ItemGroup Condition="'@(NuspecFile)' == ''">
261258
<NuspecFile Include="$(NuspecFile)" />

src/NuGetizer.Tasks/NuGetizer.Tasks.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
@@ -25,6 +25,7 @@
2525
<None Update="NuGetizer.MultiTargeting.targets" PackagePath="buildMultiTargeting\NuGetizer.targets" />
2626
<None Include="NuGetizer.PackageMetadata.targets;dotnet-nugetize.props;dotnet-nugetize.targets" PackagePath="buildMultiTargeting\%(Filename)%(Extension)" Pack="true" />
2727
<None Include="..\..\img\nugetizer-256.png" Link="icon.png" PackagePath="icon.png" />
28+
<None Update="NuGetizer.Tasks.targets" Pack="false" />
2829
</ItemGroup>
2930

3031
<ItemGroup>

src/NuGetizer.Tasks/NuGetizer.props

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ Copyright (c) .NET Foundation. All rights reserved.
5050
the automatic discovery will annotate those with Implicit instead.
5151
This allows the duplicate item detection to either warn (Implicit) or error (Explicit). -->
5252
<Source>Explicit</Source>
53+
<!-- Used to include files from referenced packages -->
54+
<PackageReference />
55+
<PackageReferencePathProperty />
56+
<PackageReferencePath />
57+
<!-- Populated by content inference to preserve original identity -->
58+
<OriginalItemSpec />
5359
</PackageFile>
5460
<PackageReference>
5561
<!-- See https://github.com/NuGet/Home/wiki/PackageReference-Specification -->
@@ -136,8 +142,10 @@ Copyright (c) .NET Foundation. All rights reserved.
136142
</ItemGroup>
137143

138144
<Target Name="_GetPackFolders" Returns="@(PackFolderKind)" />
139-
<!-- Redefined in Common targets. See PackageOutputGroup target -->
145+
<!-- Redefined in Current\Bin\Microsoft.Common.CurrentVersion.targets. See PackageOutputGroup target -->
140146
<Target Name="AllProjectOutputGroups" />
147+
<!-- Redefined in Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets -->
148+
<Target Name="RunResolvePackageDependencies" />
141149

142150
<PropertyGroup Label="Hidden">
143151
<!-- Flag this project as having been "nugetized" -->

src/NuGetizer.Tasks/NuGetizer.targets

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,40 @@ Copyright (c) .NET Foundation. All rights reserved.
7070
</PackageFile>
7171
</ItemGroup>
7272

73+
<ItemGroup>
74+
<_FromPackageReference Include="@(PackageFile -> '%(PackageReference)')" Condition="'%(PackageReference)' != ''" />
75+
<FromPackageReference Include="@(_FromPackageReference -> Distinct())">
76+
<PathProperty>Pkg$([MSBuild]::ValueOrDefault('%(_FromPackageReference.Identity)', '').Replace('.', '_'))</PathProperty>
77+
</FromPackageReference>
78+
<FromPackageReference>
79+
<PathValue>$(%(FromPackageReference.PathProperty))</PathValue>
80+
</FromPackageReference>
81+
</ItemGroup>
82+
83+
<Error Condition="'@(FromPackageReference)' != '' and '%(FromPackageReference.PathValue)' == ''"
84+
Code="NG0014"
85+
Text="In order to reference content from package '%(FromPackageReference.Identity)', make sure its package reference specifies GeneratePathProperty='true'." />
86+
87+
<ItemGroup Condition="'@(FromPackageReference)' != ''">
88+
<PackageFile Condition="'%(PackageReference)' != ''">
89+
<PackageReferencePathProperty>Pkg$([MSBuild]::ValueOrDefault('%(PackageReference)', '').Replace('.', '_'))</PackageReferencePathProperty>
90+
</PackageFile>
91+
<PackageFile Condition="'%(PackageReference)' != ''">
92+
<PackageReferencePath>$(%(PackageReferencePathProperty))</PackageReferencePath>
93+
</PackageFile>
94+
<PackageFileFromPackageReference Include="@(PackageFile -> '%(PackageReferencePath)/%(Identity)')" Condition="'%(PackageReference)' != '' and '%(OriginalItemSpec)' == ''" />
95+
<PackageFileFromPackageReference Include="@(PackageFile -> '%(PackageReferencePath)/%(OriginalItemSpec)')" Condition="'%(PackageReference)' != '' and '%(OriginalItemSpec)' != ''" />
96+
<PackageFile Remove="@(PackageFile)" Condition="'%(PackageReference)' != ''" />
97+
<PackageFile Include="@(PackageFileFromPackageReference)">
98+
<!-- Preserve original PackageReference for reference -->
99+
<OriginalPackageReference>%(PackageReference)</OriginalPackageReference>
100+
<!-- Clear values to avoid re-processing item in P2P scenarios -->
101+
<PackageReference />
102+
<PackageReferencePathProperty />
103+
<PackageReferencePath />
104+
</PackageFile>
105+
</ItemGroup>
106+
73107
<!-- We batch depending on the IsPackaging metadata, which is only specified for referenced content
74108
if the current project is building a package, to force retargeting of the referenced content. -->
75109
<AssignPackagePath Files="@(PackageFile)" KnownFolders="@(PackFolderKind)" IsPackaging="%(PackageFile.IsPackaging)">

src/NuGetizer.Tasks/Resources.resx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,15 @@
121121
<value>Package file '{0}' must have either 'PackFolder' or 'PackagePath' metadata.</value>
122122
</data>
123123
<data name="ErrorCode_NG0011" xml:space="preserve">
124-
<value>Project references to include in package must be nugetized.</value>
124+
<value>Some project references cannot be properly packaged. Please install the NuGetizer package on the following projects: {0}.</value>
125125
</data>
126126
<data name="ErrorCode_NG0012" xml:space="preserve">
127127
<value>Duplicate package source files with distinct content detected. Duplicates are not allowed in the package. Please remove the conflict between these files: {0}</value>
128128
</data>
129129
<data name="ErrorCode_NG0013" xml:space="preserve">
130130
<value>Content files cannot specify the reserved 'contentFiles' relative directory. Please use the 'CodeLanguage' and 'TargetFramework' item metadata for '{0}' to control its relative path within 'contentFiles'.</value>
131131
</data>
132+
<data name="ErrorCode_NG0014" xml:space="preserve">
133+
<value>In order to reference content from package '{0}', make sure its package reference specifies GeneratePathProperty="true".</value>
134+
</data>
132135
</root>

src/NuGetizer.Tests/Builder.NuGetizer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public static TargetResult BuildScenario(
152152
if (OpenBuildLogAttribute.IsActive)
153153
Process.Start(scenarioName + ".binlog");
154154

155-
return new TargetResult(projectOrSolution, result, target, logger);
155+
return new TargetResult(projectOrSolution, result, target.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Last(), logger);
156156
}
157157

158158
public class TargetResult : ITargetResult

0 commit comments

Comments
 (0)