-
Notifications
You must be signed in to change notification settings - Fork 0
/
readme.html
558 lines (543 loc) · 39.8 KB
/
readme.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title> nugetizer | NuGetizer </title>
<meta name="viewport" content="width=device-width">
<meta name="title" content=" nugetizer | NuGetizer ">
<meta name="generator" content="docfx 2.59.4.0">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="styles/docfx.vendor.css">
<link rel="stylesheet" href="styles/docfx.css">
<link rel="stylesheet" href="styles/main.css">
<meta property="docfx:navrel" content="">
<meta property="docfx:tocrel" content="">
<meta property="docfx:rel" content="">
</head>
<body data-spy="scroll" data-target="#affix" data-offset="120">
<div id="wrapper">
<header>
<nav id="autocollapse" class="navbar navbar-inverse ng-scope" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">
<img id="logo" class="svg" src="logo.svg" alt="">
</a>
</div>
<div class="collapse navbar-collapse" id="navbar">
<form class="navbar-form navbar-right" role="search" id="search">
<div class="form-group">
<input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
</div>
</form>
</div>
</div>
</nav>
<div class="subnav navbar navbar-default">
<div class="container hide-when-search" id="breadcrumb">
<ul class="breadcrumb">
<li></li>
</ul>
</div>
</div>
</header>
<div class="container body-content">
<div id="search-results">
<div class="search-list">Search Results for <span></span></div>
<div class="sr-items">
<p><i class="glyphicon glyphicon-refresh index-loading"></i></p>
</div>
<ul id="pagination" data-first="First" data-prev="Previous" data-next="Next" data-last="Last"></ul>
</div>
</div>
<div role="main" class="container body-content hide-when-search">
<div class="article row grid">
<div class="col-md-10">
<article class="content wrap" id="_content" data-uid="">
<h1 id="iconhttpsrawgithubusercontentcomdevloopednugetizermainimgnugetizer-32png-nugetizer"><img src="https://raw.githubusercontent.com/devlooped/nugetizer/main/img/nugetizer-32.png" alt="Icon"> nugetizer</h1>
<p>Simple, flexible, intuitive and powerful NuGet packaging.</p>
<p><a href="https://www.nuget.org/packages/NuGetizer"><img src="https://img.shields.io/nuget/vpre/NuGetizer.svg?color=royalblue" alt="Version"></a> <a href="https://www.nuget.org/packages/NuGetizer"><img src="https://img.shields.io/nuget/dt/NuGetizer?color=darkmagenta" alt="Downloads"></a> <a href="https://github.com/devlooped/nugetizer/blob/main/license.txt"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a> <a href="https://github.com/devlooped/nugetizer"><img src="https://img.shields.io/badge/-source-181717.svg?logo=GitHub" alt="GitHub"></a></p>
<p><a href="https://pkg.kzu.io/index.json"><img src="https://img.shields.io/endpoint?url=https://shields.kzu.io/vpre/nugetizer/main&label=nuget.ci&color=brightgreen" alt="CI Version"></a> <a href="https://github.com/devlooped/nugetizer/actions?query=branch%3Amain+workflow%3Abuild+"><img src="https://github.com/devlooped/nugetizer/workflows/build/badge.svg?branch=main" alt="CI Status"></a></p>
<h1 id="why">Why</h1>
<p>The .NET SDK has built-in support for packing. The design of its targets, property
and item names it not very consistent, however. When packing non-trivial solutions
with multiple projects, it's quite hard to actually get it to pack exactly the
way you want it to.</p>
<p>An <a href="https://github.com/NuGet/Home/wiki/NuGetizer-3000">alternative clean and clear design</a>
was proposed and I got to implement the initial spec, but it never got traction
with the NuGet team.</p>
<h1 id="how">How</h1>
<p>You <em>must</em> install the <a href="https://nuget.org/packages/nugetizer">NuGetizer</a> package on all
projects that are directly or indirectly being packed, since NuGetizer relies heavily on
MSBuild to provide discovery of contributed package content from projects and their
project references.</p>
<p>Package Manager:</p>
<pre><code>Install-Package NuGetizer
</code></pre><p>CLI:</p>
<pre><code>dotnet add package NuGetizer
</code></pre><p>MSBuild:</p>
<pre><code><PackageReference Include="NuGetizer" Version="..." />
</code></pre><p>You don't need to set <code>PrivateAssets=all</code> for NuGetizer: it will automatically
exclude itself from your packed dependencies. </p>
<h1 id="what">What</h1>
<p>With the learnings from years of building and shipping packages of different
levels of complexity, as well as significant use of the SDK Pack functionality
and its various extension points, NuGetizer takes a fresh look and exposes a
clean set of primitives so that you never have to create <code>.nuspec</code> files again.</p>
<p>All the <a href="https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-target">built-in properties</a>
are supported.</p>
<p>A key difference is that adding arbitrary content to the package is supported
with the first-class <code>PackageFile</code> item for absolute control of the package
contents.</p>
<pre><code class="lang-xml"><ItemGroup>
<PackageFile Include=".." PackagePath="..." />
</ItemGroup>
</code></pre><p>Another key design choice is that any package content inference should be trivial
to turn off wholesale in case the heuristics don't do exactly what you need. Just set
<code>EnablePackInference=false</code> and you will only get explicit <code>PackageFile</code> items
in your package. This gives you ultimate control without having to understand any of the inference rules explained below. </p>
<p>All <a href="src/NuGetizer.Tasks/NuGetizer.Inference.targets">inference rules are laid out in a single .targets</a> file that's easy to inspect them to learn more, and the file is not imported at all when <code>EnablePackInference=false</code>.</p>
<h2 id="package-readme">Package Readme</h2>
<p>Since the introduction of <a href="https://docs.microsoft.com/en-us/nuget/nuget-org/package-readme-on-nuget-org">package readme on nuget.org</a>,
more and more packages are leveraging this feature to make a package more discoverable and user friendly. One common
need that arises is reusing existing documentation content that exists elsewhere in the project repository, such as
on the root readme for the project (which typically contains additional information beyond user facing documentation,
such as how to clone, build and contribute to the repository). In order to maximize reuse for these documentation files,
NuGetizer supports includes in the package readme, such as:</p>
<pre><code>This is the package readme.
<!-- include ../../../readme.md#usage -->
<!-- include ../../../footer.md -->
</code></pre><p>This readme includes a specific section of the repository root readme (via <code>#usage</code>), which is defined as follows:</p>
<pre><code># Project Foo
This is a general section on cloning, contributing, CI badges, etc.
<!-- #usage -->
# Usage
Here we explain our awesome API...
<!-- #usage -->
...
</code></pre><p>By defining both starting and closing <code>#usage</code> markup, the package readme can include a specific section.
The footer, by contrast, is included wholesale. </p>
<p>When the <code>.nupkg</code> is created, these includes are resolved automatically so you keep content duplication to a
minimum. Nested includes are also supported (i.e. <code>footer.md</code> might in turn include a <code>sponsors.md</code> file or
a fragment of it).</p>
<h2 id="dotnet-nugetize">dotnet-nugetize</h2>
<p>Carefully tweaking your packages until they look exactly the way you want them should not be a tedious and slow process. Even requiring your project to be built between changes can be costly and reduce the speed at which you can iterate on the packaging aspects of the project. Also, generating the final <code>.nupkg</code>, opening it in a tool and inspecting its content, is also not ideal for rapid iteration.</p>
<p>For this reason, NuGetizer provides a dotnet global tool to make this process straightforward and quick. Installation is just like for any other dotnet tool:</p>
<pre><code>> dotnet tool install -g dotnet-nugetize
</code></pre><p>After installation, you can just run <code>nugetize</code> from the project directory to quickly get a report of the package that would be generated. This is done in the fastest possible way without compromising your customizations to the build process. They way this is achieved is by a combination of a simulated <a href="https://github.com/dotnet/project-system/blob/master/docs/design-time-builds.md">design-time build</a> that skips the compiler invocation and avoids the output file copying entirely, and built-in support in NuGetizer to emit the entire contents of the package as MSBuild items with full metadata, that the tool can use to render an accurate report that contains exactly the same information that would be used to emit the final <code>.nupkg</code> without actually generating it.</p>
<p>Here's a sample output screenshot:</p>
<p><img src="https://raw.githubusercontent.com/devlooped/nugetizer/main/img/dotnet-nugetize.png" alt="nugetize screenshot"></p>
<h2 id="inner-devloop">Inner Devloop</h2>
<p>Authoring, testing and iterating on your nuget packages should be easy and straightforward. NuGetizer makes it trivial to consume your locally-built packages from a sample test project to exercise its features, by automatically performing the following cleanups whenever you build a new version of a package:</p>
<p> a. Clean previous versions of the same package in the package output path
b. Clean NuGet cache folder for the package id (i.e. <em>%userprofile%.nuget\packages\mypackage</em>)
c. Clean the NuGet HTTP cache: this avoids a subsequent restore from a consuming project from getting a cached older version, in case you build locally the same version number that was previously restored.</p>
<p>This means that to iterate quickly, these are the only needed steps:</p>
<ol>
<li>Build/Pack a new version</li>
<li>Run Restore/Build on the sample project</li>
</ol>
<p>To make the process smoother, consider the following tweaks:</p>
<ul>
<li><p>Use single <code>PackageOutputPath</code>: if you create multiple packages, it's helpful to place them all in a single output directory. This can be achieved easily by adding the property to a <code>Directory.Build.props</code> file and place it at your repository root (or your <code>src</code> folder).:</p>
<pre><code class="lang-xml"><PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)..\bin</PackageOutputPath>
</code></pre></li>
<li><p>Use <code><RestoreSources></code> in your consuming/test projects: this allows you to point to that common folder and even do it selectively only if the folder exists (i.e. use local packages if you just built them, use regular feed otherwise). You can place this too in a <code>Directory.Build.props</code> for all your consuming sample/test projects to use:</p>
<pre><code class="lang-xml"><RestoreSources>https://api.nuget.org/v3/index.json;$(RestoreSources)</RestoreSources>
<RestoreSources Condition="Exists('$(MSBuildThisFileDirectory)..\..\bin\')">
$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\bin'));$(RestoreSources)
</RestoreSources>
</code></pre></li>
</ul>
<h2 id="package-contents-inference">Package Contents Inference</h2>
<p>Package content inference provides some built-in heuristics for common scenarios so you
don't have to customize the project much and can instead let the rules build up the contents
of your package by interpreting your existing project elements. It works by transforming various built-in
items into corresponding <code>PackageFile</code> items, much as if you had added them by hand.</p>
<p>For example, if you create a <code>readme.md</code> file alongside the project, it will (by default) be
automatically included in the package and set as the <code>Readme</code> metadata. Likewise, if you provide
the <code>$(PackageReadmeFile)</code> property pointing to a different filename (say, <code>readme.txt</code>), it will
also be automatically added to the package, without you having to add an explicit <code>PackageFile</code> or
update the item with <code><None Update='readme.txt' Pack='true' /></code> so it packs properly. </p>
<blockquote><p>NOTE: package readme inference can be turned off with the <code>PackReadme=false</code> project property.</p>
</blockquote>
<p>Inference can be turned off for specific items by just adding <code>Pack="false"</code>
item metadata. It can also be turned off by default for all items of a given type with an item definition group:</p>
<pre><code class="lang-xml"><ItemDefinitionGroup>
<PackageReference>
<Pack>false</Pack>
</PackageReference>
</ItemDefinitionGroup>
</code></pre><p>The basic item metadata that drive pack inference are:</p>
<ol>
<li><strong>Pack</strong>: <em>true</em>/<em>false</em>, determines whether inference applies to the item at all.</li>
<li><strong>PackagePath</strong>: final path within the package. Can be a directory path ending in <code>\</code> and in that case the item's <em>RelativeDir</em>, <em>Filename</em> and <em>Extension</em> will be appended automatically. Linked files are also supported automatically.</li>
</ol>
<p>If the item does <strong>not</strong> provide a <em>PackagePath</em>, and <em>Pack</em> is not <em>false</em>, the inference targets wil try to determine the right value, based on the following additional metadata:</p>
<ul>
<li><strong>PackFolder</strong>: typically one of the <a href="https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Packaging/PackagingConstants.cs#L19">built-in package folders</a>, such as <em>build</em>, <em>lib</em>, etc.</li>
<li><strong>FrameworkSpecific</strong>: <em>true</em>/<em>false</em>, determines whether the project's target framework is used when building the final <em>PackagePath</em>.</li>
<li><strong>TargetPath</strong>: optional PackFolder-relative path for the item. If not provided, the relative path of the item in the project (or its <em>Link</em> metadata) is used.</li>
</ul>
<p>When an item specifies <em>FrameworkSpecific=true</em>, the project's target framework is added to the final package path, such as <code>lib\netstandard2.0\My.dll</code>. Since the package folder itself typically determines whether it contains framework-specific files or not, the <em>FrameworkSpecific</em> value has sensible defaults so you don't have to specify it unless you want to override it. The <a href="src/NuGetizer.Tasks/NuGetizer.props">default values from NuGetizer.props</a> are:</p>
<table>
<thead>
<tr>
<th>PackFolder</th>
<th>FrameworkSpecific</th>
</tr>
</thead>
<tbody>
<tr>
<td>content (*)</td>
<td>true</td>
</tr>
<tr>
<td>lib</td>
<td>true</td>
</tr>
<tr>
<td>dependency (**)</td>
<td>true</td>
</tr>
<tr>
<td>frameworkReference (**)</td>
<td>true</td>
</tr>
<tr>
<td>build</td>
<td>false</td>
</tr>
<tr>
<td>all others (***)</td>
<td>false</td>
</tr>
</tbody>
</table>
<p>* Since the plain <em>content</em> folder <a href="https://docs.nuget.org/ndocs/schema/nuspec#using-the-contentfiles-element-for-content-files">is deprecated as of NuGet v3+</a>, we use <em>content</em> to mean <em>contentFiles</em> throughout the docs, targets and implementation. They are interchangeable in NuGetizer and always mean the latter.</p>
<p>** <em>dependency</em> and <em>frameworkReference</em> are pseudo folders containing the package references and framework (<code><Reference ...</code>) references.</p>
<p>** tool(s), native, runtime(s), ref, analyzer(s), source/src, any custom folder.</p>
<p>The <code>PackFolder</code> property (at the project level) determines the <em>PackFolder</em> metadata value for the build outputs of the project (and its xml docs, pdb and other related files like satellite assemblies). It defaults to <code>lib</code>.</p>
<p>For files that end up mapping to <em>content</em>, you can also specify <em>BuildAction</em>, <em>CopyToOutput</em> and <em>Flatten</em> item metadata, <a href="https://docs.nuget.org/ndocs/schema/nuspec#using-the-contentfiles-element-for-content-files">as supported by NuGet v4+</a>. In addition to those, NuGetizer also supports <em>CodeLanguage</em> and <em>TargetFramework</em> to <a href="https://docs.microsoft.com/en-us/nuget/reference/nuspec#package-folder-structure">control the subfolders</a> too.</p>
<p>Since it wouldn't be much fun having to annotate everything with either <em>PackFolder</em> or <em>PackagePath</em> (and also the additional <em>content</em> file metadata as needed), most common item types have sensible defaults too, defined in <a href="src/NuGetizer.Tasks/NuGetizer.Inference.targets">NuGetizer.Inference.targets</a>. </p>
<table>
<thead>
<tr>
<th>ItemType</th>
<th>Default Metadata</th>
</tr>
</thead>
<tbody>
<tr>
<td>Content<br>EmbeddedResource<br>ApplicationDefinition<br>Page<br>Resource<br>SplashScreen<br>DesignData<br>DesignDataWithDesignTimeCreatableTypes<br>CodeAnalysisDictionary<br>AndroidAsset<br>AndroidResource<br>BundleResource</td>
<td>PackFolder="content" <br>BuildAction="[<em>ItemType</em>]"</td>
</tr>
<tr>
<td>None</td>
<td>PackFolder="" <br>BuildAction="None"</td>
</tr>
<tr>
<td>Compile</td>
<td>PackFolder="content" <br>BuildAction="Compile"<br>CodeLanguage="$(DefaultLanguageSourceExtension)"</td>
</tr>
</tbody>
</table>
<p><code>None</code> is sort of special in that the package folder is root of the package by default.</p>
<p>Whether items are packed by default or not is controlled by properties named after the item type (such as <code>PackEmbeddedResource</code>, <code>PackNone</code> and so on). Except for the ones below, they all default to <em>false</em> (or more precisely, empty, so, not <em>true</em>).</p>
<table>
<thead>
<tr>
<th>Property</th>
<th>Default Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>PackBuildOutput</td>
<td>true</td>
</tr>
<tr>
<td>PackReadme</td>
<td>true</td>
</tr>
<tr>
<td>PackSymbols</td>
<td>true if PackBuildOutput=true (*)</td>
</tr>
<tr>
<td>PackDependencies</td>
<td>empty (**)</td>
</tr>
<tr>
<td>PackFrameworkReferences</td>
<td>true if PackFolder=lib, false if PackDependencies=false</td>
</tr>
<tr>
<td>PackProjectReferences</td>
<td>true</td>
</tr>
</tbody>
</table>
<p>* Back in the day, PDBs were Windows-only and fat files. Nowadays, portable PDBs
(the new default) are lightweight and can even be embedded. Combined with <a href="https://github.com/dotnet/sourcelink">SourceLink</a>, including them in the package (either standalone or embeded) provides the best experience for your users, so it's the default.</p>
<p>** In some scenarios, you might want to turn off packing behavior for all PackageReference and FrameworkReferences alike. Setting PackDependencies=false achieves that.</p>
<p>The various supported item inference are surfaced as <code><PackInference Include="Compile;Content;None;..." /></code> items, which are ultimately evaluated together with the metadata for the individual items. These make the package inference candidates. You can also provide an exclude expression for that evaluation so that certain items are excluded by default, even if every other item of the same type is included. For example, to pack all <code>Content</code> items, except those in the <code>docs</code> folder, you can simply update the inference item like so:</p>
<pre><code class="lang-xml"><ItemGroup>
<PackInference Update="Content" PackExclude="docs/**/*.*" />
</ItemGroup>
</code></pre><p>Of course you could have achieved a similar effect by updating the Content items themselves too instead:</p>
<pre><code class="lang-xml"><ItemGroup>
<Content Update="docs/**/*.*" Pack="false" />
</ItemGroup>
</code></pre><p>By default (see <a href="src/NuGetizer.Tasks/NuGetizer.Inference.props">NuGetizer.Inference.props</a>), <code>Compile</code> has the following exclude expression, so generated intermediate compile files aren't packed:</p>
<pre><code class="lang-xml"><ItemGroup>
<PackInference Include="Compile"
PackExclude="$(IntermediateOutputPath)/**/*$(DefaultLanguageSourceExtension)" />
</ItemGroup>
</code></pre><h3 id="copytooutputdirectory">CopyToOutputDirectory</h3>
<p>There is a common metadata item that's used quite frequently: <em>CopyToOutputDirectory</em>, which is typically set to <em>PreserveNewest</em> to change it from its default behavior (when empty or set to <em>Never</em>).</p>
<blockquote><p>NOTE: if you're using <em>Always</em>, you're likely ruining your build performance for no reason.</p>
</blockquote>
<p>When copying items to the output directory, you're implicitly saying that those items are needed in order to run/execute the built output. For example, if you have build targets/props in a build-only project (i.e. the one that builds the tasks), then those files are needed alongside the built output when packaging.</p>
<p>Given this common scenario, NuGetizer changes the default <code>PackFolder</code> metadata for packable items (i.e. those with explicit <code>Pack=true</code> metadata or defaulted to <em>true</em>, such as <code>Content</code> items) to match the <code>PackFolder</code> property defined for the project's built output, whenever <code>CopyToOutputDirectory</code> is not empty or <em>Never</em>.</p>
<p>Like other default inference behaviors, you can always opt out of it by specifying an explicit <code>PackFolder</code> item metadata.</p>
<p>In addition, the resulting <code>PackageFile</code> items for these items point to the location in the project's output folder, rather than the source location. This makes it easier to have custom behavior that might modify the item after copying to the output directory.</p>
<h3 id="packagereference">PackageReference</h3>
<p>Package references are turned into package dependencies by default (essentially converting <code><PackageReference></code> to <code><PackageFile ... PackFolder="Dependency"></code>), unless <code>PackDependencies</code> property is <code>false</code>. If the package reference specifies <code>PrivateAssets="all"</code>, however, it's not added as a dependency. Instead, in that case, all the files contributed to the compilation (more precisely: all copy-local runtime dependencies) are placed in the same <code>PackFolder</code> as the project's build output (if packable, depending on <code>PackBuildOutput</code> property).</p>
<p>Build-only dependencies that don't contribute assemblies to the output (i.e. analyzers or things like <a href="https://github.com/devlooped/GitInfo">GitInfo</a> or <a href="https://github.com/devlooped/ThisAssembly">ThisAssembly</a> won't cause any extra items.</p>
<p>This even works transitively, so if you use <em>PrivateAssets=all</em> on package reference <em>A</em>, which in turn has a package dependency on <em>B</em> and <em>B</em> in turn depends on <em>C</em>, all of <em>A</em>, <em>B</em> and <em>C</em> assets will be packed. You can opt out of the transitive packing with <code>PackTransitive=false</code> metadata on the <code>PackageReference</code>.</p>
<p>As usual, you can change this default behavior by using <code>Pack=false</code> metadata.</p>
<p>You can also control precisely what assets are surfaced from your dependencies, by
using <code>PackInclude</code> and <code>PackExclude</code> metadata on the <code>PackageReference</code>. This will
result in the corresponding <code>include</code>/<code>exclude</code> attributes as documented in the
<a href="https://learn.microsoft.com/en-us/nuget/reference/nuspec#dependencies-element">nuspec reference</a>. If not defined, both are defaulted to the package
reference <code>IncludeAssets</code> and <code>ExcludeAssets</code> metadata.</p>
<h3 id="projectreference">ProjectReference</h3>
<p>Unlike SDK Pack that <a href="https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#project-to-project-references">considers project references as package references by default</a>, NuGetizer has an explicit contract between projects: the <code>GetPackageContents</code> 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 <em>packable</em> (that is, it produces a package, denoted by the presence of a <code>PackageId</code> property or <code>IsPackable=true</code>, for compatibility with SDK Pack), it will be packed as a dependency/package reference instead.</p>
<p>This means that by default, things Just Work: if you reference a library with no <code>PackageId</code>, 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.</p>
<p>This works flawlessly even when multi-targeting: if the main (packable) project multitargets <code>net472;netcoreapp3.1</code>, say, and it references a <code>netstandard2.0</code> (non-packable) library, the package contents will be:</p>
<pre><code> /lib/
net472/
library.dll
library.pdb
sample.dll
sample.pdb
netcoreapp3.1/
library.dll
library.pdb
sample.dll
sample.pdb
</code></pre><p>If the packaging metadata is added to the library, it automatically turns to:</p>
<pre><code>Package: Sample.1.0.0.nupkg
...\Sample.nuspec
Authors : sample
Description : Sample
Version : 1.0.0
Dependencies:
net472
Library, 1.0.0
netcoreapp3.1
Library, 1.0.0
Contents:
/lib/
net472/
sample.dll
sample.pdb
netcoreapp3.1/
sample.dll
sample.pdb
</code></pre><p>If you need to tweak target folder of a referenced project, you can also do so
via the <code>PackFolder</code> attribute on the <code>ProjectReference</code> itself:</p>
<pre><code class="lang-xml"> <ProjectReference Include="..\MyDesktopLibrary\MyDesktopLibrary.csproj"
PackFolder="lib\net6.0\SpecificFolder" />
</code></pre><blockquote><p>NOTE: this is a convenience shortcut since you can already pass additional project
properties for project references using the built-in
<a href="https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-task?view=vs-2019#pass-properties-to-projects"><code>AdditionalProperties</code> attribute</a>.</p>
</blockquote>
<p>Finally, you can focedly turn a project reference build output into a private asset even if it defines a <code>PackageId</code> by adding <code>PrivateAssets=all</code>. This is very useful for build and analyzer packages, which typically reference the main library project too, but need its output as private, since neither can use dependencies at run-time.</p>
<h2 id="packaging-projects">Packaging Projects</h2>
<p>Typically, when creating a package involves more than one project (i.e. main library, some
build tasks + targets, some other runtime tools), you will want to create a separate packaging
project that is <em>not</em> a typical class library. For that purpose, you can create an <code>.msbuildproj</code>
which has built-in support in Visual Studio. It can use the <code>Microsoft.Build.NoTargets</code> SDK as follows:</p>
<pre><code class="lang-xml"><?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.Build.NoTargets/3.5.0">
<PropertyGroup>
<PackageId>MyPackage</PackageId>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
</code></pre><blockquote><p>NOTE: the requirement of a <code>TargetFramework</code> comes from the underlying SDK and the .NET SDK
targets themselves, but this kind of project will not build any output. Running the <code>nugetize</code>
on this project (after a <code>dotnet restore</code>) would render:</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/devlooped/nugetizer/main/img/nugetize-authoring-1.png" alt="nugetize authoring screenshot"></p>
<p>If you add a project reference to a build tasks project like the following:</p>
<pre><code class="lang-xml"><Project Sdk='Microsoft.NET.Sdk'>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackFolder>buildTransitive</PackFolder>
</PropertyGroup>
<ItemGroup>
<PackageReference Include='Microsoft.Build.Tasks.Core' Version='16.6.0' />
</ItemGroup>
</Project>
</code></pre><blockquote><p>NOTE: this project would contain MSBuild tasks, and likely a [PackageId].targets alongside so that
it's automatically imported in consuming projects. </p>
</blockquote>
<p>The packaging project would now look as follows:</p>
<pre><code class="lang-xml"><Project Sdk='Microsoft.Build.NoTargets/3.5.0'>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>MyPackage</PackageId>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include='..\Tasks\Tasks.csproj' />
</ItemGroup>
</Project>
</code></pre><p>And <code>nugetize</code> would show the following package structure:</p>
<p><img src="https://raw.githubusercontent.com/devlooped/nugetizer/main/img/nugetize-authoring-2.png" alt="nugetize authoring screenshot"></p>
<p>Note that the targets file was automatically added to the package as expected. Packaging projects
can reference other packaging projects in turn for complex packing scenarios too.</p>
<p>If the packaging project references both a build-targeting project (such as the one above) and
also a regular library project, the package contents becomes the aggregation of the contents
contributed by each referenced project automatically. For example, if you add a project reference
from the packaging project to the following class library project:</p>
<pre><code class="lang-xml"><Project Sdk='Microsoft.NET.Sdk'>
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include='System.Text.Json' Version='6.0.0' />
</ItemGroup>
</Project>
</code></pre><p>The content would now be:</p>
<p><img src="https://raw.githubusercontent.com/devlooped/nugetizer/main/img/nugetize-authoring-3.png" alt="nugetize authoring screenshot"></p>
<p>You can also add a reference to a CLI <em>tools</em> program like the following:</p>
<pre><code class="lang-xml">
<Project Sdk='Microsoft.NET.Sdk'>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<PackFolder>tools</PackFolder>
<!-- We don't need this particular tool in a framework-specific subfolder under /tools -->
<BuildOutputFrameworkSpecific>false</BuildOutputFrameworkSpecific>
</PropertyGroup>
<ItemGroup>
<PackageReference Include='System.CommandLine' Version='2.0.0-beta3.22114.1' PrivateAssets='all' />
</ItemGroup>
</Project>
</code></pre><p><img src="https://raw.githubusercontent.com/devlooped/nugetizer/main/img/nugetize-authoring-4.png" alt="nugetize authoring screenshot"></p>
<p>As you can see, it's quite trivial to build fairly complex packages using very intuitive defaults and
content inference. </p>
<h2 id="advanced-features">Advanced Features</h2>
<p>This section contains miscellaneous useful features that are typically used in advanced scenarios and
are not necessarily mainstream.</p>
<h3 id="dynamically-extending-package-contents">Dynamically Extending Package Contents</h3>
<p>If you need to calculate additional items to inject into the package dynamically, you can run a target
before <code>GetPackageContents</code>, which is the target NuGetizer uses before packing to determine what needs
to be included. At this point you can add arbitrary <code><PackageFile ... PackagePath=... /></code> items laying
out precisely what it is you want to inject into the .nupkg. For example:</p>
<pre><code class="lang-xml"><Target Name="AddPackageContents" BeforeTargets="GetPackageContents">
<ItemGroup>
<PackageFile Include="$(MSBuildProjectDirectory)\..\docs\**\*.md" PackagePath="docs\%(RelativeDir)%(Filename)%(Extension)" />
</ItemGroup>
</Target>
</code></pre><p>This example will add all markdown files in a <code>docs</code> folder one level above the current project, and
place them all under the <code>docs</code> folder in the <code>.nupkg</code>, preserving their original folder structure.</p>
<h3 id="packing-arbitrary-files-from-referenced-packages">Packing arbitrary files from referenced packages</h3>
<p>If you want to pack files from referenced packages, you can simply add <code>PackageReference</code> attribute
to <code>PackageFile</code>. Say we want to reuse the awesome icon from the
<a href="https://nuget.org/packages/ThisAssembly">ThisAssembly</a> package, we can just bring it in with:</p>
<pre><code class="lang-xml"><ItemGroup>
<PackageFile Include="icon-128.png" PackagePath="icon.png" PackageReference="ThisAssembly" />
</ItemGroup>
</code></pre><p>The project will need to reference that package too, of course:</p>
<pre><code class="lang-xml"><ItemGroup>
<PackageReference Include="ThisAssembly" Version="1.0.0" GeneratePathProperty="true" Pack="false" />
</ItemGroup>
</code></pre><p>Note that we had to add the <code>GeneratePathProperty</code> to the reference, so that the package-relative
path <code>icon-128.png</code> can be properly resolved to the package install location. You can also set that
metadata for all your <code>PackageReference</code>s automatically by adding the following to your <code>Directory.Build.props</code>
(or .targets): </p>
<pre><code class="lang-xml"> <ItemDefinitionGroup>
<PackageReference>
<!-- This enables referencing arbitrary files from any package by adding PackageReference="" to any packable item -->
<GeneratePathProperty>true</GeneratePathProperty>
</PackageReference>
</code></pre><p>Also note that in the scenario shown before, we don't want to pack the reference as a dependency (it's a build-only or development
dependency package). That is, this feature does not require a package <em>dependency</em> for the referenced package content
we're bringing in.</p>
<p>It even works for inferred content item types, such as <code>None</code>:</p>
<pre><code class="lang-xml"><ItemGroup>
<None Include="icon-128.png" PackageReference="ThisAssembly" />
</ItemGroup>
</code></pre><h3 id="skip-build-during-pack">Skip Build during Pack</h3>
<p>If you are building explicitly prior to running <code>Pack</code> (and you're not using
<code>PackOnBuild=true</code>), you might want to optimize the process by skipping the
automatic <code>Build</code> run that happens by default when you run <code>Pack</code> by setting
<code>BuildOnPack=false</code>. Not building before <code>Pack</code> with <code>BuildOnPack=false</code>
can cause the target run to fail since output files expected by the packaging
might be missing (i.e. the primary output, content files, etc.).</p>
<p>This option is useful in combination with <code>BuildProjectReferences=false</code> when
packing on CI, since at that point all that's run are the P2P protocol involving
<code>GetPackageContents</code>.</p>
<!-- include https://github.com/devlooped/sponsors/raw/main/footer.md -->
<h1 id="sponsors">Sponsors</h1>
<!-- sponsors.md -->
<p><a href="https://github.com/clarius"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/clarius.png" alt="Clarius Org" title="Clarius Org"></a>
<a href="https://github.com/MelbourneDeveloper"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MelbourneDeveloper.png" alt="Christian Findlay" title="Christian Findlay"></a>
<a href="https://github.com/augustoproiete"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/augustoproiete.png" alt="C. Augusto Proiete" title="C. Augusto Proiete"></a>
<a href="https://github.com/KirillOsenkov"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/KirillOsenkov.png" alt="Kirill Osenkov" title="Kirill Osenkov"></a>
<a href="https://github.com/MFB-Technologies-Inc"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MFB-Technologies-Inc.png" alt="MFB Technologies, Inc." title="MFB Technologies, Inc."></a>
<a href="https://github.com/sandrock"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/sandrock.png" alt="SandRock" title="SandRock"></a>
<a href="https://github.com/eeseewy"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/eeseewy.png" alt="Eric C" title="Eric C"></a>
<a href="https://github.com/agocke"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/agocke.png" alt="Andy Gocke" title="Andy Gocke"></a></p>
<!-- sponsors.md -->
<p><a href="https://github.com/sponsors/devlooped"><img src="https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor.png" alt="Sponsor this project" title="Sponsor this project"></a>
</p>
<p><a href="https://github.com/sponsors">Learn more about GitHub Sponsors</a></p>
<!-- https://github.com/devlooped/sponsors/raw/main/footer.md -->
</article>
</div>
<div class="hidden-sm col-md-2" role="complementary">
<div class="sideaffix">
<div class="contribution">
<ul class="nav">
<li>
<a href="https://github.com/devlooped/nugetizer/blob/docs/readme.md/#L1" class="contribution-link">Improve this Doc</a>
</li>
</ul>
</div>
<nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
<h5>In This Article</h5>
<div></div>
</nav>
</div>
</div>
</div>
</div>
<footer>
<div class="grad-bottom"></div>
<div class="footer">
<div class="container">
<span class="pull-right">
<a href="#top">Back to top</a>
</span>
<span>Generated by <strong>DocFX</strong></span>
</div>
</div>
</footer>
</div>
<script type="text/javascript" src="styles/docfx.vendor.js"></script>
<script type="text/javascript" src="styles/docfx.js"></script>
<script type="text/javascript" src="styles/main.js"></script>
</body>
</html>