Skip to content

NuGetizer Core Features

Daniel Cazzulino edited this page Nov 10, 2016 · 14 revisions

This in the minimum set of core features required to support multi-platform and bait-and-switch packages.

Virtual Packages

A GetPackageContents target is provided (via the NuGet.Build.Packaging nuget targets and eventually via MSBuild global imports to all projects that use the Common targets).

This target returns an item list containing the entire contents of a "virtual package", the exact contents that the project would contribute to a .nupkg if one were to be built. The type of item being contributed can be determined by inspecting the %(Kind) metadata, which can be:

Kind Description
Lib The assembly, symbols (.pdb) and xml documentation files that end up under the lib folder inside the package.
Metadata The package metadata, defined in project properties.
Dependency The package dependencies.
Build, Tools, ContentFiles, Native, Runtimes, Ref, Analyzers, Source The files in the various known folders in a package.
TBD: maybe we should remove these from the doc for now? Not clear how some are used currently?

Common metadata for these items that can be used to analyze how they contribute to a package:

Common Metadata Description
%(PackageId) The project's $(PackageId), if any.
%(PackagePath) If the item represents actual file in the package, its relative path inside it (i.e. lib\net45\foo.dll).
%(TargetFrameworkMoniker) The TFM of the project contributing the file (i.e. .NETFramework,Version=v4.5).
%(TargetFramework) The short framework name, such as net45

The GetPackageContents target depends on the GetPackageContentsDependsOn property, allowing other targets to inject information into the virtual package. It's also possible to just create a new target and specify BeforeTargets="GetPackageContents" instead, to contribute additional items.

The virtual package is returned regardless of whether the project is actually configured to build a package. If the project is not configured to build a package, no metadata item will be included. It is not only used when creating nupkgs, it also allows projects to inspect package metadata and items from the projects that they reference.

NOTE: The $(IsPackable) property can be used in conditions to determine if a project generates an actual nupkg. This property is 'true' if the project defines a 'PackageId'. The built-in GetTargetPath target is redefined and extended to include this metadata as well as an IsNuGetized=true metadata value, so it's easy for MSBuild targets to determine if a project reference produces a nuget package or not, and if it has been 'nugetized' or not, without risking invoking non-existing targets (i.e. 'GetPackageContents').

Metadata

Metadata is specified as project properties. This metadata can be retrieved from a project in two ways:

  • Via the GetPackageTargetPath target, which gets just the metadata item, and is analogous to the GetTargetPath.
  • Alternatively, via the GetPackageContents, by inspecting the item with the %(Kind)='Metadata' metadata.

Note that project properties sometimes have the Package prefix to disambiguate values, such as $(PackageRequireLicenseAcceptance), $(PackageLicenseUrl), $(PackageProjectUrl)and $(PackageTags). The manifest metadata in retrieved by either of the above ways never contains the *Package* since there is no need to disambiguate its purpose (i.e.PackageLicenseUrlbecomesLicenseUrl` and so on).

Package Version

The version of the package to build can be specified as the PackageVersion project property.

It is pretty common for this value to be more dynamic, so you can either redefine the GetPackageVersion target or add your target to the GetPackageVersionDependsOn to populate the $(PackageVersion) property. The NuGetizer itself uses this mechanism to version its package leveraging Git information.

The default behavior of the built-in GetPackageVersion target is to set $(PackageVersion) to $(Version) if it's empty by the time the target runs, which is the same property used by [.NET Core] (https://github.com/dotnet/sdk/blob/master/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.GenerateAssemblyInfo.targets#L120-L122).

Package Files

By default, the virtual package will include .NET libraries (from BuiltProjectOutputGroup) and their symbols (from DebugSymbolsProjectOutputGroup), xml docs (from DocumentationProjectOutputGroup), satellite assemblies (from SatelliteDllsProjectOutputGroup), framework references (from '%(ReferencePath.ResolvedFrom)' == '{TargetFrameworkDirectory}', and content files (from ContentFilesProjectOutputGroup), however platform-specific targets can add additional platform-specific files.

NOTE: these are the built-in common targets output groups.

To opt out of these defaults, set $(IncludeOutputsInPackage), $(IncludeSymbolsInPackage), $(IncludeFrameworkReferencesInPackage) and $(IncludeContentInPackage) to false as needed. For Content items, you can opt out specific items by setting the IncludeInPackage=false item metadata instead of turning content inclusion off entirely via $(IncludeContentInPackage)=false.

Since content files have particular requirements on placement (code language and target framework are required), support for None items is also provided: just annotate them with IncludeInPackage=true item metadata, and they will be included as package files in the relative target path they have within the project. So you can, for example, define your own None items under a contentFiles\cs\any\ folder to get them included in consuming C# projects of any target framework.

Ultimately, all these built-in supported item types are turned into PackageFile items.

A PackageFile item type allows projects and targets to add arbitrary files to the virtual package at the package-relative location specified by the PackagePath item metadata. These should not generally need to be used directly in project files. Platform-specific targets should add targets to GetPackageContentsDependsOn that synthesizes PackageFile items as appropriate from platform-specific items such as Content and BundleResource. You can see a concrete example of this usage in the NuGetizer [build tasks project]https://github.com/NuGet/NuGet.Build.Packaging/blob/dev/src/Build/NuGet.Build.Packaging.Tasks/NuGet.Build.Packaging.Tasks.targets#L36) itself, in the AddBuiltOutput target.

For example, the iOS build targets might do the following, so that a library's BundleResource items would end up in any referencing projects.

<PackageFile Include="@(_BundleResourceWithOutputPath)">
    <Kind>Content</Kind>
    <TargetPath>%(OutputPath)</TargetPath>
    <BuildAction>BundleResource</BuildAction>
</PackageFile>

PackageFile items that have Kind=Content may also specify BuildAction, CopyToOutput and Flatten metadata, which map to the corresponding NuGet contentFiles nuspec attributes. Additionally, a CodeLanguage metadata can be provided to restrict the language of the consuming projects to add it to, such as:

<PackageFile Include="ApiExample.cs">
    <Kind>Content</Kind>
    <CodeLanguage>cs</CodeLanguage>
    <TargetPath>Samples\%(Filename)%(Extension)</TargetPath>
</PackageFile>

This file will end up in /contentFiles/cs/any/Samples/ApiExample.cs.

By default, CodeLanguage and TargetFramework are set to any.

PackagePath vs TargetPath

When a PackageFile provides a PackagePath, that is the final relative path within the nupkg where the file will end up and no further processing is performed for them by the AssignPackagePath task.

If is quite common, however, to want the files to end up in whatever the right framework directory is depending on the current project's TFM. For example, if you're providing a localized XML intellisense file, you would want it to end up in the right lib\[TFM] root folder, under a relative path of (say) es-AR. You can achieve this with the TargetPath as follows:

<PackageFile Include="MyLibrary.es-AR.xml">
    <Kind>Lib</Kind>
    <TargetPath>es-AR\MyLibrary.xml</TargetPath>
</PackageFile>

If the project being built is a NETStandard 1.6 one, this file will end up in /lib/netstandard1.6/es-ar/MyLibrary.xml. If you later decide to retarget the project to NETStandard 2.0, no changes will be necessary to the PackageFile declaration above.

Dependencies

The virtual package will implicitly depend on the project's dependencies:

  • NuGet packages referenced directly via project.json or PackageReference items.

  • The project's project references ("P2P").

Project references can be excluded from the virtual package by setting IncludeInPackage=false metadata, which is analogous to ReferenceOutputAssembly:

<ProjectReference Include="..\OtherProject\OtherProject.csproj">
    <IncludeInPackage>false</IncludeInPackage>

When generating its virtual package, a referencing project will call the GetPackageContents target on each of its project references that have not been excluded from the package. If the referenced project reference as package metadata, it will be added to the project's own virtual package as a package dependency. Otherwise, its outputs are merged into the referencing project's own virtual package.

Package Merging

When merging a referenced virtual package into a referencing virtual package, the package's files will be added directly to the merged package, with local paths adjusted appropriately.

The referenced package's dependencies will merged into the referencing package. They will be treated as specific to that package's supported frameworks, however they will be re-unified into non-framework-specific dependencies where possible.

Package Creation

A Pack build target is provided, which will use the virtual package to build an actual nupkg file in the project's output directory. The $(PackageOutputPath) property can be specified to override this default.

A PackOnBuild=true property causes the Pack target to be added as a BuildDependsOn post-build step to allow building all NuGets in a solution via msbuild Foo.sln /p:PackOnBuild=true.

Package Projects

A new project type will be introduced, a Package Project, with its own build targets.

The package project targets consumes the same targets as regular projects. Package projects will also define Build, Clean and Rebuild targets for compatibility with the solution-level targets, however these will be empty.

The only items supported by the package project targets will be PackageReference, PackageFile and ProjectReference.

NOTE; we support project.json and packages.config to define package references too. This allows us to reuse the NuGet package manager without changes.

PackageReferenceand PackageFile allow using package projects to create nupkgs with full control over content and dependencies.

Multi-platform packages

The primary use case of packaging projects is for creating multi-platform projects. The packaging project would reference several projects that build a library with the same name, target different frameworks, and do not have package metadata.

Since the referenced packages did not have metadata, their virtual packages would be merged into the packaging project. These virtual packages would have the binaries and content in platform specific directories, and the packaging project would else up with all of them in one package.

Metapackages

A packaging project can also be used to create metapackages. In this case, it would reference several projects in the solution that have packaging metadata, and thus would build an empty package with dependencies on the packages built by those projects.

IDE Support

A new project flavor will be added for package projects. It will support project references, package references, and files. Building this project type causes a package to be created.

A Create NuGet Package command is available in the solution tree on solutions and all projects. When it's run for a solution, it will set the PackOnBuild=true property and build the solution normally, causing all configured packages to be created. When run for a project, it will first show a dialog with the package metadata (to allow modifications prior to generating the package).

A project template will be added for creating package projects. When run, it will show a dialog prompting the user to provide basic metadata and to choose which frameworks they would like to support.

Contributing

What's Being Worked On?

Check out the proposals in the accepted & proposed folders on the repository, and active PRs for proposals being discussed today.

Common Problems

Clone this wiki locally