Writing a NuGet package for VS2015 RTM [repost]


There are some exciting changes coming to NuGet in VS2015 RTM, under the name NuGet 3.1, also known as "project.json". You should read about them on the NuGet team blog [link].

How should you prepare for this as a NuGet package author? This article explains.

 

Disclaimer: I'm not on the NuGet team, and this post is my non-authoritative personal opinion

Motivation

As explained on the NuGet team blog, project.json is a new way for Visual Studio projects to consume NuGet packages. It brings several benefits, which is why NuGet package consumers will like it, and why NuGet package authors will have to support it too:

  • Your .vbproj/.csproj no longer gets polluted with NuGet artifacts. They are kept completely separate.
  • You can change your app target platform, and change debug/release and x86/x64/arm/anycpu, and NuGet packages will now pick this up immediately -- before you had to uninstall+reinstall them.
  • You can have two different solutions in two different directories that include the same project (this is particularly useful when you're working across two different repositories)
  • Your SolutionExplorer>References node looks cleaner because it only includes the packages you've actually installed, rather than all their dependencies too.
  • Uninstalling NuGet packages is easier, again because you only have to uninstall the ones you installed rather than all their dependencies.
  • Packages are cached globally (on a per-user per-machine) basis rather than being downloaded and unzipped into every single solution that uses them.
  • File > New and Managed NuGet Packages > Install both become faster.
  • You get more precise control over NuGet package upgrades, and version mismatches.

The intent of project.json is this: "Installing a NuGet package is nothing more than adding a line to the project.json file; at build-time the libs and .targets from that NuGet package get consumed by msbuild."

Project.json - for now - will be used in the following kinds of projects in VS2015. Look for this list to grow longer as new project types are introduced which will also use project.json:

  • In ASP.NET 5 ("DNX") projects
    • Currently still in preview form
  • For Windows 10 Universal app projects ("UWP")
    • Requires installation of the VS Universal Windows app development tools. If they're not already installed, you can install them with "Add/Remove Programs > VisualStudio2015 > Modify".
  • For modern Portable Class Library ("PCL") development
    • "Modern PCLs" are ones whose targets are limited to amongst ASP.NET Core 5.0 ("DNX"), Windows 10 ("UWP") and .NET46.
    • Again this requires installation of the VS universal windows tools.
  • For any other project type,
    • You can delete packages.config and add a project.json file manually, to take advantage of its benefits [instructions].
    • This path isn't officially supported. It mostly works, except that if you have a P2P (Project-to-project) reference from a packages.config project to a project.json library. In that case, any NuGet dependencies in project.json will fail to be copied to the consuming project.
    • VS universal windows tools are required.

Master table of all the various .NET frameworks

There have been many different versions of the .NET framework over the years. Key points:

  1. As a NuGet package author you have to be aware of the different versions to write your NuGet packages effectively.
  2. Microsoft has gotten on top of the problem with a solution called the "dotnet" TxM, described below (TxM stands for "Target x Moniker" and refers to the codes in the table below). The project.json approach was introduced in part to support dotnet.

Various codes, such as "net45" and "win81" and "dotnet" are an essential part of your NuGet package authoring. It is important to understand why they're available, and what they're compatible with.

The following table is based upon that in the NuGet team blog, but contains additional color codes you'll find useful when authoring your class libraries.

Backwards-compatibility works within a row, but there's no compatibility across different rows. For instance, a library or NuGet package designed for "net40" will work in "net45" but might not work (and NuGet will refuse to add a reference) in "win81". That's because it might depend on some .NET APIs that are present in net40 but absent in win81.

As explained in the NuGet team blog , Microsoft's solution is the "dotnet" TxM. You won't author NuGet package "for a particular platform or set of platforms" any longer. Instead you will author your NuGet package for "dotnet", and you will call out which particular APIs your package depends upon. This way your package will install fine on any platform, past present or future, which can offer those APIs.

For instance, if your code uses List<T> internally, then you'll author your NuGet package with a dependency on Microsoft's System.Collections NuGet package since that's where List<T> is implemented. Your package will install fine on every platform that System.Collections can be installed onto. (Microsoft will author their System.Collections package with diligence, and will be quick to make sure it's updated for all new platforms…) Let's look at some interesting platforms…

  • .NET46. This is the framework that's installed as part of the Windows Desktop+Server operating system. If you write an app which targets this, and you add a reference to the System.Collections NuGet package, all it says is " nothing to do here since System.Collections is already part of the OS platform which this code will run on". 
  • UAP10.0 (Windows 10 Universal Apps) and DNXCORE50 (ASP.Net 5 Core). These platforms expect apps to carry their .NET runtime and framework with them, app-locally. If you write an app which targets either of these platforms, and you add a reference to the System.Collections NuGet package, then it copies the actual DLL which implements List<T> right there into your application's "bin" directory, adjacent to your EXE.

Here's a subtle but practical question… Why is uap10.0 in the same row as win81? Let's walk through these platforms: The win81 platform is guaranteed to provide lots of .NET APIs since they're part of the operating system. When you do File>New>Universal>Blank to create a new uap10.0 app then it creates a project which includes all those APIs (and more) app-locally via NuGet references. The app author is at liberty to delete those NuGet references, resulting in something with fewer .NET APIs that win81. So how can the table honestly say that uap10.0 is in the same row and bigger than win81?

Well, uap10.0 is in the same row as win81 simply because that reflects the rules that NuGet uses for judging compatibility. As for the possibility that an app might delete their default NuGet references from their app? You as a NuGet package author must defend against this by specifying dependencies on everything you depend upon.
It seems likely all of Microsoft's future .NET platforms will operate the same way as UAP10.0 and DNXCORE50: they won't expect the .NET framework to be part of the operating system, they will instead expect it to be delivered app-locally via NuGet packages, and the way to target them will be to author your NuGet packages with "dotnet".

Just how large are we talking? The .NET runtime and framework together add up to ~20mb, which seems a lot. On platforms that use .NET Native, this strips away everything that you're not using and gets it down to ~6mb. On platforms that don't, you can probably expect disk-deduplication or other techniques to reduce the footprint to something manageable.
Color code for the above table:

 

Red: These are older platforms that are not recommended for future development. When you code for these then your DLL generally references "mscorlib" (which makes it hard to know which APIs you're using and impossible to use on new platforms). And/or these platforms lack modern language functionality such as async/await.

 

Yellow: by all means support them. When you code for these then your DLL references "contracts" from which you can easily figure out which APIs (i.e. which NuGet packages) your library depends upon. These platforms provide a modern base level of functionality.

 

 

Green: these platforms are the future. If you write a DLL which targets only green platforms, then it will be built from the ground up with NuGet dependencies.

 

 

Green with red border: these platforms don't assume that .NET is part of the platform. Everything -- the .NET runtime, Microsoft's .NET framework libraries, third-party libraries --all are referenced as NuGet packages and deployed app-locally.

*

"win" and "windows" can be used interchangeably.
"wp" and "windowsphone" can be used interchangeably.
"net" can be omitted; it is the default

A worked example

Goal: I wish to write NuGet package which can be used by as many project types as possible. However, I'm only going to bother making it work on >=.NET45 project types, i.e. ones where I can use modern features like Async/Await and CallerMemberInfo without fuss. Also, my package uses Json.Net internally.

NuGet is hugely expressive. There's a many goals you can achieve, and a large number of ways to accomplish any one goal. I can't explain everything. So what I'll do is pick just one goal, present my solution, explain my rationale for it, and describe the under-the-hood algorithms that NuGet uses. From that you'll be able to figure out how to accomplish your own goals.

Step 1: Create the DLL

Do File > New > ClassLibrary (Portable) and pick the following targets. This is sometimes known as a "Profile 259 PCL" [ see this webpage for why].

Rationale: As NuGet package authors we start by thinking about all the platforms that we want our package to be used by. I referred to the Master Table earlier in this document and for each row (i.e. each possible target platform), I picked the oldest version that was still yellow. That gives me the widest reach, without bogging myself down into the difficult-and-niche areas.

Exception: I have to unselect the ASP.NET Core 5.0 checkbox. This is counter-intuitive since my DLL will still run fine on ASP.NET 5, thanks to being in a NuGet package with "dependencies". It's just a point-in-time bug that will be fixed in the first VS2015 update.

Note: the bottom three "Xamarin" rows appear in the dialog because I've installed the Xamarin tools into VS. But even if you haven't installed the Xamarin tools, no matter: these three checkboxes are placebos, since the other checkmarks implicitly imply that my PCL will also work on Xamarin.
Portable Class Library. I want PCLs to be able to consume my NuGet package. The deal is: given that I create my ClassLibrary1.dll with the targets above, then my ClassLibrary1.dll can be consumed by any PCL whose targets are the same or more relaxed. A more relaxed set of targets is where either some rows are unchecked, or they're switched to higher versions. (Thus, to make my library maximally consumable, that's why I checked as many targets as I could, and picked versions as low as possible).
Testing that my PCL can be consumed by the different project types. This step isn't strictly necessary but it's useful. And indeed this is how I debug my PCLs.

For all project types that I want to consume my NuPkg, I can actually first try having them reference my PCL directly -- either with a Project-to-Project "P2P" reference (Reference > AddReference > Solution > ClassLibrary1) or with a raw file reference (References > AddReference > Browse).

There are a few known limitations:

  • You can't P2P reference your Profile259 PCL from a Modern PCL that has ASP.NET Core checked. (A "Profile259 PCL" is one whose targets are as picked at the top of this section; a Modern PCL is one whose targets are limited to amongst .NET46, Win10, ASP.NET Core). This is a point-in-time bug that will be fixed in the first VS2015 update. As a workaround you can still do a raw file reference. And once you turn your PCL into a NuGet package then it will work fine. Another workaround, for testing purposes, is to just go to your consuming project's targets and remove ASP.NET Core and then the P2P reference will work.
  • You can't P2P reference your Profile259 PCL from a "DNX" project (i.e. ASP.Net 5). And you can't even add a raw file reference because the "Browse" button doesn't work in DNX projects. Don't worry. Once you turn your PCL into a NuGet package then it will work fine.

 

Test in .NET Native. I urge everyone to test their libraries from a Win10 application ("UWP") in Release mode. That's especially true if your library uses reflection or serialization in any way.

"UWP Release mode" is the first .NET platform to use Ahead-Of-Time "AOT" compilation: it compiles the application and its libraries into native machine code; later, when the app is then deployed and run on customer machines, it no longer even uses the CLR! (As you can imagine, native machine code has a harder time with reflection and serialization than does the CLR). I wouldn't be surprised to see AOT on more .NET platforms in future. That's why I'd prefer to validate my library right now while I'm first architecting it, rather than having to work around issues after-the-fact.

If your library runs into problems, there's a technique called " rd.xml files" by which your library can provide hints to the AOT compiler. If you discover you need these, then the rd.xml file should be deployed in your NuGet package. Best way to do this is to set the build action for your rd.xml to be "embedded resource" - more details here.

Step 2: plan the NuPkg directory structure

My NuPkg will have the following structure. Note that there are two identical copies of ClassLibrary1.dll in it.

WorkedExample.1.0.0.0.nupkg
+-- WorkedExample.nuspec
+-- lib
+-- dotnet
|    +-- ClassLibrary1.dll
+-- portable-net45+win8+wpa81+wp8
+-- ClassLibrary1.dll

Rationale . This requires an in-depth explanation. I want to start by saying that the "lib\dotnet" subdirectory is the way of the future. We recommend using the dotnet folder structure as a best-practice for all packages that expect to target multiple platforms. The only reason I have an additional subdirectory in here is so that my NuGet can be consumed by traditional PCLs, which can't consume the "lib\dotnet" subdirectory. This table shows which directories are used by which project types:

Directory

Used by these project types

<no match; fails to install>

Any platform < .NET45

lib\portable-net45+win8+wpa81+wp8

Traditional PCLs.

lib\dotnet

Everything else (e.g. UWP apps, .NET Framework apps, Xamarin apps, Modern PCLs)

NuGet algorithm . How can you figure out which lib\subdirectories you need? And how can you figure out which project types will use which lib\subdirectory? Here I'll spell out the algorithm that NuGet uses. When you have a project which wants to consume a NuGet package, NuGet will look at your project type, and look at the name of all the lib\subdirectories, and figure out which single subdirectory is the "best match". The rules for best-match are complicated, and depend crucially on the Master Table earlier in this article.

  • If your project isn't a portable class library, then it uses these tie-breakers in order to search for the best match…
    1. Look for an "exact match platform". For instance, if your project is "uap10.0" and there is a subdirectory called "lib\uap10.0" then use it.
    2. Failing that, look for an earlier version of the platform or for the unnumbered code for that platform (i.e. in the Master Table, look in the same row, but further to the left)
      • Example: if the project is .NET45, and there's no "lib\net45" subdirectory, then look for "lib\net40", failing that "lib\net35" and so on.
      • Example: if the project is UWP and there's no "lib\uap10.0" subdirectory, then look for "lib\uap", failing that "lib\win81" or "lib\netcore451", and so on.
    3. Failing that, and if your project is >=45, then look for "lib\dotnet"
    4. Failing that, look for all subdirectories of the form "lib\portable-xyz" where your project type is amongst those "xyz" as per rules 1..4. Amongst all of those portable-* candidates, there's a rule for picking the best one, but I don't know it.
    5. Failing that, packages.config used to look just in the root "lib\" folder, but that functionality is no longer supported in packages.json. If your nupkg used to put DLLs directly in the root "lib\" folder then it has to change.
  • If your project is a PCL,
    1. If your project is a "modern PCL" and there exists a subdirectory "lib\dotnet", then use it. (A "modern PCL" is one which targets some or all of .NET46, Win10 and ASP.NetCore, and which targets nothing other than those three.)
    2. Failing that, calculate your project's targets. In the case of a traditional PCL, the targets are those listed in the PCL targets dialog. In the case of a modern PCL, I don't know what the targets are. Then look for a lib subdirectory that is an exact match for your project's targets.
    3. Failing that, look for the best-match "lib\portable-*" subdirectory. I don't know the rules for "best-match" in this case.

Attentive readers might spot that I could have got away with just a single subdirectory, called "portable-net45+win8+wpa81+wp8+dnxcore50". That's possible, but not best-practice. Best practice is to embrace the "lib\dotnet" folder. The reason for this approach is to make it easier on you the package author. It's so that your NuGet package will continue to work when Microsoft or Xamarin or anyone else introduces a new platform that isn't net/win/wpa/wp/dnxcore, but yet still does understand lib\dotnet.

Step 3: Craft the .nuspec dependencies

I create a .nuspec file as follows. This .nuspec file should be part of your project. We will discuss the <dependencies> section below...

<?xml version="1.0"?>
<package>
<metadata>
<id>SampleNuGetPackage</id>
<version>1.0.0.2</version>
<title>Sample NuGet Package</title>
<authors>Lucian Wischik</authors>
<owners>Lucian Wischik</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is a demo portable .NET class library</description>
<summary>It is referencable by all >=.NET45 clients, including WindowsStore and Phone</summary>
<dependencies>
<group targetFramework ="net">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="win">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="wp">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="wpa">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="MonoAndroid">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="MonoTouch">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="Xamarin.iOS">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="portable-net45+win8+wpa81+wp8">
<dependency id="Newtonsoft.Json" version="7.0.1" />
</group>
<group targetFramework ="uap">
<dependency id="Newtonsoft.Json" version="7.0.1" />
<dependency id="System.Runtime" version="4.0.0"/>
<dependency id="System.Resources.ResourceManager" version="4.0.0"/>
<dependency id="System.Collections" version="4.0.0"/>
<dependency id="System.Linq" version="4.0.0"/>
</group>
<group targetFramework ="dotnet">
<dependency id="Newtonsoft.Json" version="7.0.1" />
<dependency id="System.Runtime" version="4.0.0"/>
<dependency id="System.Resources.ResourceManager" version="4.0.0"/>
<dependency id="System.Collections" version="4.0.0"/>
<dependency id="System.Linq" version="4.0.0"/>
</group>
</dependencies>
</metadata>
</package>

Goal. My DLL depends on certain other DLLs. I must ensure that when an app references my NuGet package then it also transitively references certain other NuGet packages that contain those other DLLs. The exact set of DLLs that I depend upon varies by platform. I don't want to cause an app to take on more NuGet dependencies than it strictly needs. (This is particularly important for traditional packages.config projects, where each additional NuGet dependency looks poor in Solution Explorer > References, and is awkward to uninstall via Manage NuGet References).
There's a tool to auto-generate the dependencies . Oren Novotny has written a great tool called NuSpec.ReferenceGenerator. It's a NuGet package. You add it do your class-library project, and automatically generates the correct dependencies in the .nuspec file within your project.

In the rest of this section I'm going to explain how to do stuff manually instead of using the tool. This is merely so you can understand what's going on under-the-hood, or in case the tool doesn't work for whatever reason. Let's first take stock of what we know...

Source code . My library project's source code is listed below. The code is obviously using Json.Net, and Linq, and List<T>, and String.Format.

Public Class Class1
Public Shared Function f() As String     Dim s = "[""hello"",""world""]"
Dim x = Newtonsoft.Json.JsonConvert.DeserializeObject(Of List(Of String))(s)
Return $"{x.First} I am here"
End Function

End Class

 

Packages.config . My library project's "packages.config" file is below. My library obviously depends upon Json.Net.

<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="7.0.1" />
</packages>

Assembly manifest. I built my library, and opened the resulting DLL in the "ildasm" tool (which comes with Visual Studio 2015). The MANIFEST section looks like this. This provides a list of DLLs which it depends upon.

.assembly extern System.Runtime {.ver 4:0:0:0 }

.assembly extern System.Resources.ResourceManager { .ver 4:0:0:0 }

.assembly extern System.Collections { .ver 4:0:0:0 }

.assembly extern Newtonsoft.Json { .ver 7:0:0:0 }

.assembly extern System.Linq { .ver 4:0:0:0 }

 

From this I can produce the list of NuGet packages that my library depends upon:

  1. Newtonsoft.Json version 7.0.1
    1. This comes straight from my library project's "packages.config"
    2. Note that 7.0.1 is the version of the Newtonsoft.Json NuGet package which I depend upon and I read this version number from packages.config; the number { .ver 7:0:0:0 } doesn't refer to a NuGet package version; it is instead the assembly version of the file Newtonsoft.Json.dll which is contained within that NuGet package.
    3. It's good to write the exact NuGet package version number you depend upon (rather than just writing an earlier number like 7.0.0). This way, NuGet package restore can avoid an extra search.
  2. System.Runtime version 4.0.0
    System.Resources.ResourceManager version 4.0.0
    System.Collections version 4.0.0
    System.Linq version 4.0.0

    1. For the .NET framework, Microsoft adopted a simple 1:1 mapping between DLL names, and the NuGet packages that contain them.
    2. (This 1:1 mapping only works because the PCL targets none of the red platforms in the Master Table. Those red platforms simply use mscorlib as a reference, which wouldn't provide any clues as to which NuGet packages are needed).
    3. I had no idea which NuGet package versions to use. So I just did File>New>UWP as a throwaway example of a modern platform, built it, then opened the "project.lock.json" file. From this I was able to find some typical NuGet package version numbers for each of these NuGet packages.

Second, craft platform-specific .nuspec dependencies

Now that I have this full list of NuGet package dependencies, I could simply write it into the .nuspec file as below.

<dependencies>
<dependency id="Newtonsoft.Json" version="7.0.1" />
<dependency id="System.Runtime" version="4.0.0"/>
<dependency id="System.Resources.ResourceManager" version="4.0.0"/>
<dependency id="System.Collections" version="4.0.0"/>
<dependency id="System.Linq" version="4.0.0"/>
</dependencies>

This approach would certainly work. But I think it's inelegant because anyone who references my NuGet package from one of the traditional platforms like .NET45 will get those additional NuGet references. They don't even need them.


NuGet algorithm . The way I solved this problem was through use of nuspec dependency groups. The .nuspec <dependency> section can have either a flat list of dependencies (as above) or a list of groups of dependencies (as at the start of this section), each one tagged with a "targetFramework".

In the case where the .nuspec <dependency> section has a list of groups, then it picks a single best-match group for the current project. It does this using exactly the same algorithm as it used to pick a single best-match lib\subdirectory for the current project.

If you consult the Master Table, and work it through with the NuGet algorithm for picking best match, then here's how my selection of dependency groups will be used by different project types:

Dependency group targetFramework

Used by these project types

net

net45, net451, net452, net46

win

win8, win81,

wp

wp8, wp81

wpa

wpa81

MonoAndroid
MonoTouch
Xamarin.iOS

MonoAndroid10

MonoTouch10
Xamarin.iOS10

uap

uap10.0

dotnet

dnxcore, modern PCLs

portable-net45+win8+wpa81+wp8

traditional PCLs

 

Rationale . For the modern project types with a red border around them (uap10 and dnxcore50), plus for any conjectured future project types (likely based on dotnet and deploying the framework app-locally), then I need to spell out all of the dependencies including the framework ones:

  • [Full dependencies] Newtonsoft.Json, System.Runtime, System.Resources.ResourceManager, System.Collections, System.Linq

But for all other project types, I can safely assume that the framework dependencies are already satisfied by the platform/OS, so all I want to do is spell out the extra ones:

  • [Brief dependencies] Newtonsoft.Json
  • (Pedantically, you were supposed to add <frameworkAssembly> directives to your .nuspec just in case the user had manually deleted important framework references from their References node. But no one ever used these directives correctly, and it wasn't worth doing.)

The only way I could figure out how to do this, going by the Master Table and the NuGet best-match algorithm, was to spell out every single traditional dependency group and also spell out uap10.0 explicitly. In a nutshell, the reason it ends up like this is (1) I need to specify full dependencies for dotnet; (2) dotnet matches every traditional platform, but the traditional platforms should only have brief dependencies, so each one must be explicitly exempted; (3) uap10.0 the traditional "win" platform, but needs full dependencies, so it has to be exempted once again.

(If anyone can see an easier way to achieve this, please let me know!)
The lazy approach

The work to craft a proper .nuspec <dependencies> section is a bit awkward. I want to explicitly rule out some tempting "lazy" alternatives, because they don't give a good experience for the consumer of your NuGet package…

  1. I might omit the <dependencies> section entirely. But then if my NuGet package is referenced by a project which doesn't also reference Newtonsoft.Json, then it will fail at runtime with a DllLoadException.
  2. I might merely put into <dependencies> the items in my library project's packages.config, in this case Newtonsoft.Json. This will run fine on all traditional platforms. It will likely run fine on uap10.0 unless the app author has gone out of their way to delete the default NuGet references, or unless Microsoft in future decides to trim down the set of default NuGet references for a faster leaner system. It will likely fail on dnxcore50, since those projects have just a tiny set of default NuGet references. It will likely fail on future platforms, since they're most likely to follow the lead of dnxcore50.
  3. I might merely put into the <dependencies> those items in my library project's packages.config, plus the single catch-all "Microsoft.NETCore" . The Microsoft.NETCore package represents all of the default NuGet references. This approach will run fine on uap10. It will run fine on dnxcore50 and on future platforms, but it will bloat them due to including "the whole world and the kitchen sink" while they aim to be lean. It will work on traditional platforms, but will look really terrible since 100+ NuGet references will be added to them.
  4. I might abandon the complicated "dependency groups", and merely use a flat list of dependencies . As discussed above, this will work on all platforms. On traditional platforms, however, it adds superfluous NuGet references to traditional platforms -- in this case just 4 superfluous references plus their transitive dependencies, but in more realistic libraries it might add 50 or more.

Step 4: produce the .nupkg file as part of your build

It's best to make the .nupkg be automatically generated as part of my PCL project's build. This section shows how.

First, from my PCL project, I add a NuGet reference to the "NuGet.CommandLine" package.

Second, I unload my project, edit the .vbproj/.csproj, and add these lines near the end:

<Target Name="AfterBuild">
<Copy SkipUnchangedFiles="true"
SourceFiles="bin\$(Configuration)\ClassLibrary1.dll"
DestinationFiles="bin\$(Configuration)\nupkg\lib\dotnet \ClassLibrary1.dll" />
<Copy SkipUnchangedFiles="true"
SourceFiles="bin\$(Configuration)\ClassLibrary1.xml"
DestinationFiles="bin\$(Configuration)\nupkg\lib\dotnet \ClassLibrary1.xml" />
<Copy SkipUnchangedFiles="true"
SourceFiles="bin\$(Configuration)\ClassLibrary1.pdb"
DestinationFiles="bin\$(Configuration)\nupkg\lib\dotnet \ClassLibrary1.pdb" />
<Copy SkipUnchangedFiles="true"
SourceFiles="bin\$(Configuration)\ClassLibrary1.dll"
DestinationFiles="bin\$(Configuration)\nupkg\lib\portable- net45+win8+wpa81+wp8\ClassLibrary1.dll" />
<Copy SkipUnchangedFiles="true"
SourceFiles="bin\$(Configuration)\ClassLibrary1.xml"
DestinationFiles="bin\$(Configuration)\nupkg\lib\portable- net45+win8+wpa81+wp8\ClassLibrary1.xml" />
<Copy SkipUnchangedFiles="true"
SourceFiles="bin\$(Configuration)\ClassLibrary1.pdb"
DestinationFiles="bin\$(Configuration)\nupkg\lib\portable- net45+win8+wpa81+wp8\ClassLibrary1.pdb" />
<Copy SkipUnchangedFiles="true"
SourceFiles="SampleNugetPackage.nuspec"
DestinationFiles="bin\$(Configuration)\nupkg \SampleNugetPackage.nuspec" />
<Exec Command="..\..\..\..\packages \NuGet.CommandLine.2.8.6\tools\NuGet.exe pack SampleNugetPackage.nuspec -OutputDirectory .."
WorkingDirectory="bin\ $(Configuration)\nupkg"
Outputs="bin\ $(Configuration)\*.nupkg" />
</Target>

Rationale . After the project has been built, these msbuild steps create a "bin\Debug\nupkg" directory which contains the .nuspec and contains the "lib\" directory structure we discussed earlier. Then it runs the "nuget.exe" command-line tool to turn it into a .nupkg and sticks it into your bin\Debug directory. Or Release directory, if you're building in Release mode.

I'm not a great expert on msbuild. These msbuild steps have a few shortcomings, which I'm sure an msbuild expert can point out J But they're the best I've got.

The easiest way to test your .nupkg is Tools > Options > NuGet > PackageSources, and add a new source which points straight to your project's bin\debug directory. Then you can easily add a NuGet reference to it from any other app.

Special Circumstances

There are a number of advanced NuGet features. Some of them no longer work with project.json. I've colored these back-compat breaks in red. Some of them are also discussion on the NuGet team blog.

"Content" no longer supported by project.json. A few NuGet packages use "content". These are files such as .cs or .png which, in traditional packages.config, got added as source files to your project when you install a NuGet package. You were even able to modify these files once they've been added. (Content files are no longer supported by project.json and are simply ignored. You must either rewrite your package to avoid it, or join in the discussion with the NuGet team about how content should work.

"Lib\files" no longer supported by project.json. It used to be you could include files directly in the root of the lib\ directory, and if no appropriate lib\subdirectory was found, then NuGet would pick files straight from the root. This is no longer supported by project.json and such files are simply ignored. You should consult the Master Table to figure out which lib\subdirectory (or subdirectories) are more appropriate now.

"Build\files" no longer supported by project.json. Likewise it used to be you could include files directly in the root of the build\ directory. This is not currently supported by project.json. You must either rewrite your package to avoid it, again by consulting the Master Table, or follow the NuGet team's progress on fixing this bug.

"install.ps1" no longer supported by project.json. It used to be you could have an arbitrary powershell script be invoked when the package is added or removed. This no longer makes sense in the world where NuGet packages are resolved by msbuild, and so install.ps1 and uninstall.ps1 are now simply ignored. You must figure out alternatives. A really powerful alternative is .targets files in a build\subdirectory.

Analyzers have changed since VS2015 RC. An "Analyzer" is a mechanism by which a NuGet package can provide additional warning messages, error messages and quick-fixes inside the VS2015 code editor for VB/C#. You get started with an analyzer by installing the .NET Compiler Platform SDK and doing File>New>Extensibility>Analyzer. How do Analyzer NuGet packages install? In packages.config projects, they install via "install1.ps". In project.json projects, they install by convention that they're inside an "analyzers\dotnet" directory (or "analyzers\dotnet\vb" or "analyzers\dotnet\cs" if the analyzer is VB or C# specific). Note that "analyzers\dotnet" here doesn't follow the same rules as "lib\subdirectory"... it should be "dotnet" exactly. (This rule isn't currently enforced, but it might be in future). If you created an analyzer prior to VS2015 RTM (release date July 29th 2015) then I suspect your analyzer NuGet package is out of date. You should do File>New>Extensibility>Analyzer once again and copy+paste your old code.

mscorlib-based projects. If you want to write a NuGet package which is very portable, including to .NET4-era targets, things get more complicated. (1) When you try to write such a PCL, it is "mscorlib"-based. That means the DLL contains an assembly reference to "mscorlib". I don't know how to figure out which NuGet packages this corresponds to. (2) Such an mscorlib-based PCL cannot be referenced by a dnxcore50 project. Therefore, at the bare minimum, you must build two separate DLLs, one for older project types and one for newer ones. (3) You'll typically want to include references to packages like Microsoft.Bcl and Microsoft.Bcl.Async and Microsoft.Net.Http. I don't know which of these packages should also be depended upon by modern targets.

Comments (8)
  1. Oren Novotny says:

    With the .rd.xml files, it only works adjacent in UWP projects if there's a PRI file. Otherwise, the most reliable way of ensuring those directives are picked up is to select Embedded Resource as the build action on that file in your project.

  2. Thanks @Oren. I've updated the article.

  3. Michael says:

    Hello Lucian!

    Thanks for the comprehensive article!

    In re-linq, I went with multiple projects, each targeting a different .NET platform (.NET 3.5, .NET 4.0, PCLs) and in my buildscript, I use the nuspec file to aggregate all the build outputs into hardcoded folders. The dotnet-type dependency generation certainly looks like a new, 'fun' thing to include in the output. The suggested generator looks interesting, but it only works when the nuspec file is actually part of your project. Up until now, I went with a shared nuspec file to avoid code duplication and to not copy/modify it during the build process. The package process then took care of the dependencies. Now, with the portable model, that approach will need to change.

    Best regards, Michael

  4. Sander Saares says:

    Can you add some guidance on publishing NuGet packages that are architecture-specific? For example, I am currently making a UWP library that cannot be AnyCPU due to referencing extensions that do not come as AnyCPU. However, I have no idea how I would go about making a nuspec for it.

  5. @Sander – I've been wondering the same thing (how to write a nupkg with architecture-specific binaries). I know it's possible but haven't yet found the documentation. I'll look into it.

  6. Sander Saares says:

    Okay, apparently it's like this:

    The architecture-specific binaries use go in runtimeswin10-armlibuap10.0MyLibrary.dll.

    And you need an AnyCPU reference assembly at refuap10.0}MyLibray.dll, as well. I just used the x86 build and stripped off the x86 flag with corflags, making it AnyCPU. Feels dirty but works fine.

    XML documentation file goes with the reference assembly.

    All other extra files (pdb, pri, resource xml directory) go with the architecture-specific binaries.

  7. Jimmy Recard says:

    Lucian, Fantastic article !!!! Are you able to provide the source of your sample project for reference?

  8. HarryDev says:

    What about packaging native dlls? How can that be done? And how can these be reference by the project using the package so they are copied to build output directory i.e. Libs_x86, Libs_x64 and so on so these paths can be added at app startup in environment variable and dlls can be loaded from there, this allows native dlls to be referenced independent of architecture, assuming a AnyCPU pinvoke C# assembly or declarations are used.

Comments are closed.

Skip to main content