project.json

Multi-targeting the world: a single project to rule them all

January 4, 2017 Coding 9 comments , , , , , , ,

Multi-targeting the world: a single project to rule them all

Starting with Visual Studio 2017, you can now use a single project to build platform-specific libraries for all project types. This blog will explore why you might want to do this, how to do it and workarounds for some point-in-time issues with the tooling.

Contents

Intro

Since the beginning of .NET Core, the project.json format has enabled multi-targeting, that is compiling to multiple target frameworks in parallel and creating an output for each. With ASP.NET Core, it’s common to target both net45 and netcoreapp1.0 so you can deploy the site to either the desktop framework, which runs on Windows, or to the CoreCLR, which runs cross-platform. Multi-targeting is nothing more than compiling the same code multiple times, once per target platform. Each target can specify its own dependencies and ifdef‘s, so you can easily tailor the code to the specific platform.

Another example may have a library target netstandard1.0, netstandard1.3, and net45 to enable different levels of functionality based on the available surface area.

While it was also possible to target UWP, Win8, or profile-based PCL’s, using project.json, doing so required hacks like private copies of all reference assemblies, WinMD files and more. Beyond that, some things didn’t work correctly as some platforms require additional targets to generate additional outputs like .pri files on UWP for resource lookup. So while technically possible, full multi-targeting was brittle and required you to stay in a very narrow path, avoiding things like resources or GUI elements that require the full tool-chain to process.

Enter MSBuild

With the move to MSBuild as part of the .NET Core Tooling direction change, the picture gets much better, so much so that with VS 2017 RC2, you can correctly multi-target all platform types, including UWP, profile-based PCL’s, and Xamarin iOS/Android. Not only that, but by conditionally including/excluding directories based on globs, you can reduce the need for ifdef‘s in many cases.

As part of being open sourced and enabled to run cross-platform, the build targets and tasks required to actually do the build were combined into an SDK. This went along with drastic simplification of the csproj file to have a minimal footprint, that will get even smaller, like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NETCore.App" Version="1.0.1" />
  </ItemGroup>
</Project>

Microsoft’s blog details all of the improvements in this area. For current lack of a better term, I’ll call projects based on these new tools “SDK style.” The easiest way to identify these “SDK style” projects is by looking for the Sdk attribute in the top Project element.

Multi-targeting vs. .NET Standard Libraries vs. PCL’s

Before we go further, let’s answer this question that many people have asked — why would you want to multi-target vs just use a single portable library, whether that’s .NET Standard or an older profile-based PCL?

There are several answers to that question — first, if your code can all fit within a single .NET Standard-based library, then there’s no reason to multi-target. If you’re using a legacy profile-based PCL, at the very least consider moving up to the equivalent .NET Standard version. Don’t make more work for yourself. The decision to multi-target falls out of a need to use functionality that doesn’t exist within a .NET Standard version or if you need to target an earlier platform that doesn’t support the .NET Standard version you need. A common example is that many libraries still need to support .NET 4.5. Despite a significant amount of functionality available in .NET Standard 1.3, that .NET Standard version only supports .NET 4.6+. Chances are though that the code would work “just fine” on .NET 4.5, so it’s easy to multi-target to both net45 and netstandard1.3.

The other main reason why you’d need to multi-target is to use platform-specific code within your library. For example, on iOS you might want to use SecKeyChain for saved credentials, on Android use its Context to access shared services like preferences, and on Windows its Credential Manager. You might have a common method called GetCredential that other code uses to get the data. Today you might use dependency injection or reflection to access a “.Platform” library with a specific implementation that your common code uses. Instead, you can choose to multi-target and access the platform code directly.

How to multi-target

Let me start by saying that the methods here are based on the new “SDK-style” projects that VS 2017 provides. They orchestrate using the existing project types that are installed by Visual Studio. As such, the build itself won’t work on a box without the other tools installed (so you’re building on a Windows box, much like you probably are today). Some of these may work on a Mac with Visual Studio for Mac but I have not tested that in any way. When you install Visual Studio 2017, make sure to install all of the tools for the project types you need (Xamarin, UWP, etc) and also the .NET Core Tooling.

There’s no UI in VS for adding additional target frameworks, but I have some samples that show what to do.

First, create a new .NET Core Class Library project. If you don’t see the following option, make sure to install the .NET Core workload in the VS Installer.

New .NET Core Class Library
.NET Core workload

Right-click the project and select “Edit project file…”. This is new in VS 2017 – the ability to edit the project file while it’s open and have changes instantly reflected.

In the editor, after noticing how much less boilerplate code there is now, look for the TargetFramework property that looks like this: <TargetFramework>netstandard1.3</TargetFramework> property. Change that to <TargetFrameworks>netstandard1.3;net45</TargetFrameworks> to target .NET 4.5 and NET Standard 1.3. You can add however many targets you want by adding to that semi-colon list. It’s subtle, but note the difference in property names between TargetFramework and TargetFrameworks with a plural. It’s easy to miss.

For some frameworks, like .NET 4.5, that’s all you need to do. However, targeting .NET Standard and .NET 4.x is far from “the world.” We can do better! You would think it should be as easy as adding additional TFM’s like uap10.0, xamarin.ios10 or MonoAndroid70 to the list, and hopefully by the time the tools RTM it will be, but for now we need to add extra properties to the project file to tell MSBuild what to do with those.

Fortunately, and here’s the real secret, the “SDK-style” build system has a LanguageTargets property that you can specify per TFM to import the targets for that project type instead of the vanilla Microsoft.CSharp.targets import. That means we can use the “Windows Xaml”, Android, iOS, or any other platform tool-chain we need.

Xamarin Example

In the example here, I have a class library that multi-targets to net45, uap10.0, netstandard1.3, Xamarin.iOS10 and MonoAndroid70. In this contrived library, I have a Greeter class that’s calling a Hello() method that needs platform specific code. I’m using a pattern where I have a directory for each TFM where code in there only gets included there, so no ifdef‘s are needed. For Android, Resources are supported if you need them. While the example doesn’t currently use them, you could use PList‘s, xib‘s or Story Boards on iOS, Page‘s on UWP, or any other “native” file type supported by the platform.

Win81/WP8/PCL/Wpa81/Xamarin/Net45 Example

As a more realistic example, one of my libraries, Zeroconf, an mDNS discovery library, targets “the world.” It currently has concrete implementations for wp8, Wpa81, Win8, portable-Wpa81+Win81, uap10.0, net45, and netstandard1.3 (which supports Xamarin and CoreCLR.) In addition to the the concrete implementations, it provides a netstandard1.0 fa├žade to support being used in portable libraries. The different concrete implementations are required due to differences in the networking stacks between the various Windows networking stacks. For now, the uap10.0 version cannot use the netstandard1.3 version until NetworkInformation is fully supported by the platform, so it continues to use the WinRT variant. You can see the platform-specific code in the platforms directory and then how they’re conditionally included by the csproj in the ItemGroups

The property groups at the top contain the LanguageTargets and properties needed. For portable-Wpa81+Win81 two extra items are required as the special PCL profile also supports WinRT. The ItemGroup here has two TargetPlatform to pull in the correct .winmd references.

Building

You can build the libraries either in VS 2017 or the command-line. If you use the command line, you’ll want to run the following from a VS 2017 Developer Command Prompt: msbuild /t:restore followed by msbuild /t:build. If you want to create a NuGet package, you can run msbuild /t:pack. It’s important to note that you must currently use msbuild, the desktop version in the VS 2017 path, to build these and not dotnet build. The reason is that while dotnet build calls MSBuild, it’s currently using a CoreCLR version even though the desktop version is present in your VS installation. The engineering team is aware of this and in the future, dotnet build will be smart enough to call the desktop version of msbuild when present. The “regular” targets file we’re using to support the platform-specific features are designed for Desktop MSBuild. They do not yet have support for CoreCLR tasks. Bottom line, as of the current release: if your targets use build tasks, then you need to provide both CoreCLR and Desktop versions of the library in order to support both “regular” MSBuild and dotnet build.

Common gotcha’s

There are several bugs in the tool-chain currently that are in the process of being fixed:

  • Some Project-to-project (p2p) references aren’t resolving correctly. Whereas they should resolve to the “best” match, they are resolving to the first TFM in the list.
  • Another bug is preventing a “legacy” csproj from doing a p2p reference with a “Portable Library can only reference other portable library” error.
  • Files that are conditionally included won’t show up in the Solution Explorer. As a workaround, include all files with None as the first item group (see example).
  • for iOS (and possibly Android), you need to set DebugType to full as the Xamarin ConvertPdb2Mdb task doesn’t yet support the new Portable PDB format generated by this tool-chain.
  • Win8, Win81, and uap10.0 aren’t correctly understood by the NuGet targets today. As a workaround, you need to include the NugetTargetMoniker property set to the full TFM as shown here. Similarly, for legacy PCL targets, it requires Version=v0.0 in the NugetTargetMoniker here. These should hopefully be fixed by GA.
  • Windows assemblies that use resources need a .pri file alongside them. They’re currently missing from the generated NuGet. Workaround is to use your own .NuSpec for now until the bug is fixed.

Into the weeds, how it all works

This is by no means an official explanation, it’s what I’ve found from exploring the SDK build targets. Some of the terminology and concepts may change over time.

The “SDK style” projects consist of a set of targets/tasks that are pre-installed with MSBuild (and the CLI tools). You can see them in the following directory: C:\Program Files (x86)\Microsoft Visual Studio\2017\<sku>\MSBuild\Sdks where <sku> is Community, Professional, or Enterprise, depending on what you installed. The two SDK’s you’re likely to use directly are Microsoft.NET.Sdk and Microsoft.NET.Sdk.Web.

The Sdk attribute causes an Sdk.props and Sdk.targets within the specified SDK’s \Sdk directory to be imported before and after the project file. The Microsoft.NET.Sdk SDK’s targets defines an “outer” and “inner” build. The “outer-loop” is what your project file directly defines, including several TFM’s in the TargetFrameworks property. If you only have a single build with a TargetFramework property defined, then there’s only an “inner-loop”.

For an “outer-loop” build, the SDK targets imports props/targets in a buildCrossTargeting directory (soon to be renamed to buildMultiTargeting). Those get auto-included before and after the main project file (props before, targets after.) The “outer-loop” targets will eventually loop through each of the TargetFrameworks calling msbuild again in an “inner-loop” with TargetFramework set to one TFM. This “inner-loop” build is what we currently have in today’s “normal” project types. The “inner-loop” build provides an extension point for providing your language-specific targets (the Import that was at the bottom of your old csproj before) in place of the “vanilla” one it’ll include by default. By providing a LanguageTargets property for the “inner-loop,” conditioned by TFM, we can use the “original” targets that invoke the full tool-chain for the target platform. See here, here and here for UWP, iOS, and Android, respectively.

Within each conditionally defined property group, we can set properties that are specific to a particular “inner-loop.” These correspond to the properties in your existing platform-specific project file and are used by the platform-specific targets specified.

One thing you give-up currently is any UI in VS for configuring these properties. Perhaps they’ll return sometime in the future. For now, one thing I’ve found helpful is to maintain a few “dummy” projects where I can edit some settings to see the values and then put them into my multi-targeting csproj.

Looking forward

As of today (January 4, 2017), the tooling is in a fairly rough state. The .NET Core tooling is rightfully in an “alpha” state. The MSBuild SDK is under active development and things will change before GA. There are a number of issues in the tooling that can make it hard to use today, but I expect those to be fixed soon. Most of the bugs I’ve found are slated to be fixed in the RC3 time-frame, and I’d expect things to be better with that release.

As to whether-or-not to take the plunge today: I’d suggest that if you have a tolerance for figuring this out and reporting issues you’ll encounter, then go for it. If you have a complex project today that already multi-targets a different way (most likely by using multiple “head” projects and shared code project types), I would recommend trying this out in a branch to see how far you get. I’ll be happy to help, just give me a shout. The more the community bangs on this stuff up front, the more issues can be addressed prior to GA.

Acknowledgments

Many thanks to Brad Wilson, Joe Morris, and Daniel Plaisted for reviewing this post and providing feedback.

Using Xamarin Forms with .NET Standard

July 9, 2016 Coding 10 comments , , , ,

Using Xamarin Forms with .NET Standard

With the release of .NET Core and the .NET Standard Library last week, many people want to know how they can use packages targeting netstandard1.x with their Xamarin projects. It is possible today if you use Visual Studio; for Xamarin Studio users, support is coming soon.

Prerequisites

Using .NET Standard pretty much requires you to use project.json to eliminate the pain of “lots of packages” as well as properly handle transitive dependencies. While you may be able to use .NET Standard without project.json, I wouldn’t recommend it.

You’ll need to use the following tools:

Getting Started

As of now, the project templates for creating a new Xamarin Forms project start with an older-style packages.config template, so whether you create a new project or have an existing project, the steps will be pretty much the same.

Step 1: Convert your projects to project.json following the steps in my previous blog post.

Step 2: As part of this, you can remove dependencies from your “head” projects that are referenced by your other projects you reference. This should simplify things dramatically for most projects. In the future, when you want to update to the next Xamarin Forms version, you can update it in one place, not 3-4 places. It also means, you only need the main Xamarin.Forms package, not each of the packages it pulls in.

If you hit any issues with binaries not showing up in your bin directories (for your Android and iOS “head” projects), make sure that you have set CopyNuGetImplementations to true in your csproj as per the steps in the post.

At this point, your project should be compiling and working, but not yet using netstandard1.x anywhere.

Step 3: In your Portable Class Library projects, find the highest .NET Standard version you need/want to support.

Here’s a cheat sheet:

  • If you only want to support iOS and Android, you can use .NET Standard 1.6. In practicality though, most features are currently available at .NET Standard 1.3 and up.
  • If you want to support iOS, Android and UWP, then NET Standard 1.4 is the highest you can use.
  • If you want to support Windows Phone App 8.1 and Windows 8.1, then NET Standard 1.2 is your target.
  • If you’re still supporting Windows 8, .NET Standard 1.1 is for you.
  • Finally, if you need to support Windows Phone 8 Silverlight, then .NET Standard 1.0 is your only option.

Once you determine the netstandard version you want, in your PCL’s project.json, change what you might have had:

{
    "dependencies": {
        "Xamarin.Forms": "2.3.0.107"        
    },
    "frameworks": {        
        ".NETPortable,Version=v4.5,Profile=Profile111": { }
    },
    "supports": { }
}

to

{
    "dependencies": {
        "NETStandard.Library": "1.6.0",
        "Xamarin.Forms": "2.3.0.107"        
    },
    "frameworks": {        
        "netstandard1.4": {
            "imports": [ "portable-net45+wpa81+wp8+win8" ]
         }
    },
    "supports": { }
}

Note the addition of the imports section. This is required to tell NuGet that specified TFM is compatible here because the Xamarin.Forms package has not yet been updated to use netstandard directly.

Then, edit the csproj to set the TargetFrameworkVersion element to v5.0 and remove any value from the TargetFrameworkProfile element.

At this point, when you reload the project, it should restore the packages and build correctly. You may need to do a full clean/rebuild.

Seeing it in action

I created a sample solution showing this all working over on GitHub. It’s a good idea to clone, build and run it to ensure your environment and tooling is up-to-date. If you get stuck converting your own projects, I’d recommend referring back to that repo to find the difference.

As always, feel free to tweet me @onovotny as well.

Project.json all the things

February 8, 2016 Coding 34 comments ,

Project.json all the things

One of the less known features of Visual Studio 2015 is that it is possible to use project.json with any project type, not just “modern PCL’s,” UWP projects, or xproj projects. Read on to learn why you want to switch and how you can update your existing solution.

Background

Since the beginning of NuGet, installed packages were tracked in a file named packages.config placed alongside the project file. The package installation process goes something like this:

  1. Determine the full list of packages to install, walking the tree of all dependent packages
  2. Download all of those packages to a \packages directory alongside your solution file
  3. Update your project file with correct libraries from the package (looking at \lib\TFM
    • If the package contains a build directory, add any appropriate props or targets files found
  4. Create or update a packages.config file along the project that lists each package along with the current target framework

Terms

  • TFM – Target Framework Moniker. The name that represents a specific Platform (platforms being .NET Framework 4.6, MonoTouch, UWP, etc.)
  • Short Moniker – a short way of referring to a TFM in a NuGet file (e.g., net46). Full list is here.
  • Full Moniker – a longer way of specifying the TFM (e.g., .NETPortable,Version=v4.5,Profile=Profile111). Easiest way to determine this is to compile and let the NuGet error message tell you what to add (see below).

Limitations

The above steps are roughly the same for NuGet up to and including the 2.x series. While it works for basic projects, larger, more complex projects quickly ran into issues. I do not consider the raw number of packages that a project has to be an issue by itself – that is merely showing oodles of reuse and componentization of packages into small functional units. What does become an issue are the UI and the time it takes to update everything.

As mentioned, because NuGet modifies the project file with the relative location of the references, every time you update, it has to edit the project file. This is slow and can lead to merge conflicts across branches.

Furthermore, the system is unable to pivot on different compile-time needs. With many projects needing to provide some native support, NuGet v2.0 had no way of providing different dependencies based on build configuration.

One more issue surfaces with the use of “bait and switch” PCLs. Some packages provide a PCL for reference purpose (the bait), and then also provide platform-specific implementations that have the same external surface area (the switch). This enables libraries to take advantage of platform specific functionality that’s not available in a portable class library alone. The catch with these packages is that to function correctly in a multi-project solution containing a PCL and an application, the application must also add a NuGet reference to all of the packages its PCL libraries use to ensure that the platform-specific version winds up in the output directory. If you forget, you’ll likely get a runtime error due to an incomplete reference assembly being used.

NuGet v3 and Project.json to the rescue

NuGet 3.x introduces a number of new features aimed at addressing the above limitations:

  • Project files are no longer modified to contain the library location. Instead, an MSBuild task and target gets auto-included by the build system. This task creates references and content-file items at build time enabling the meta-data values to be calculated and not baked into a project file.
    • Per-platform files can exist by using the runtimes directories. See the native light-up section in the docs for the details.
  • Packages are now stored in a per-user cache instead of alongside the solution. This means that common packages do not have to be re-downloaded since they’ll already be present on your machine. Very handy for those packages you use in many different solutions. The MSBuild task enables this as the location is no longer baked into the project file.
  • Reference assemblies are now more formalized with a new ref top-level directory. This would be the “bait” assembly, one that could target a wide range of frameworks via either a portable- or dotnet or netstandard TFM. The implementation library would then reside in \lib\TFM. The version in the ref directory would be used as the compile-time reference while the version in the lib directory is placed in the output location.
  • Transitive references. This is a biggie. Now only the top-level packages you require are listed. The full chain of packages is still downloaded (to the shared per-user cache), but it’s hidden in the tooling and doesn’t get in your way. You can continue to focus on the packages you care about. This also works with project-to-project references. If I have a bait-and-switch package reference in my portable project, and I have an application that references that portable library, the full package list will be evaluated for output in the application and the per-architecture, per-platform assemblies will get put in the output directories. You no longer have to reference each package again in the application.

It is important to note that these features only work when a project is using the new project.json format of package management. Having NuGet v3 alone isn’t enough. The good news is that we can use project.json in any project type with a few manual steps.

Using project.json in your current solution

You can use project.json in your current solution. There are a couple of small caveats here:

  1. Only Visual Studio 2015 with Update 1 currently supports project.json. Xamarin Studio does not yet support it but it is planned. That said, Xamarin projects in Visual Studio do support project.json.
    • If you’re using TFS Team Build, you need TFS 2015 Update 1 on the build agent in addition to VS 2015 Update 1.
  2. Some packages that rely on content files being placed into the project may not work correctly. project.json has a different mechanism for this, so the package would need to be updated. The workaround would be to manually copy the content into your project file.
  3. All projects in your solution would need to be updated for the transitive references to resolve correctly. That’s to say that an application using NuGet v2/packages.config won’t pull in the correct transitive references of a portable project reference that’s using project.json.

With that out of the way, lets get started. If you’d like to skip this and see some examples, please look at the following projects that have been converted over. These are all libraries that have a combination of reference assemblies, platform specific implementations, test applications and unit tests, so the spectrum of scenarios should be covered there. They have everything you need in them:

One last note before diving deep: make sure your .gitignore file contains the following entries:

  • *.lock.json
  • *.nuget.props
  • *.nuget.targets

These files should not generally be checked in. In particular, the .nuget.props/targets files will contain a per-user path to the NuGet cache. These files are created by calling NuGet restore on your solution file.

Diving deep

As you start, have the following blank project.json handy as you’ll need it later:

{
    "dependencies": {        
    },
    "frameworks": {        
        "net452": { }
    },
    "runtimes": {
        "win": { }
    } 
}

This represents an empty project.json for a project targeting .NET 4.5.2. I’m using the short moniker here, but you can also use the full one. The string to use here is the thing you’ll likely hit the most trouble with. Fortunately, when you’re wrong and try to build, you’ll get what’s probably the most helpful error message of all time:

Your project is not referencing the “.NETPortable,Version=v4.5,Profile=Profile111” framework. Add a reference to “.NETPortable,Version=v4.5,Profile=Profile111” in the “frameworks” section of your project.json, and then re-run NuGet restore.

The error literally tells you how to fix it. Awesome! The fix is to put .NETPortable,Version=v4.5,Profile=Profile111 in your frameworks section to wind up with something like:

{
    "dependencies": {        
    },
    "frameworks": {        
        ".NETPortable,Version=v4.5,Profile=Profile111": { }
    },
    "supports": { }
}

The eagle-eyed reader will notice that the first example had a runtimes section with win in it. This is required for a desktop .NET Framework projects and for projects where CopyNuGetImplementations is set to true like your application (we’ll come back that in a bit), but is not required for other library project types. If you have the runtimes section, then there’s rarely, if ever, a reason to have both the supports section too.

The easiest way to think about this:

  • For library projects, use supports and not runtimes
  • For your application project, (.exe, .apk, .appx, .ipa, website) use runtimes and not supports
  • If it’s a desktop .NET Framework project, use runtimes for both class libraries and your application
  • If it’s a unit test library executing in-place and you need references copied to its output directory, use runtimes and not supports

Now, take note of any packages with the versions that you already have installed. You might want to copy/paste your packages.config file into a temporary editor window.

The next step is to remove all of your existing packages from your project. There are two ways to do this: via the NuGet package manager console or by hand.

Using the NuGet Package Manager Console

Pull up the NuGet Package Manager Console and ensure the drop-down is set to the project you’re working on. For each package in the project, uninstall each package with the following command:
Uninstall-Package <package name> -Force -RemoveDependencies
Repeat this for each package until they’re all gone.

By Hand

Delete your packages.config file, save the project file then right-click the project and choose “Unload project”. Now right-click the project and select Edit. We need to clean up a few things in the project file.

  • At the top of the project file, remove any .props files that were added by NuGet (look for the ones going to a \packages directory.
  • Find any <Reference> element where the HintPath points to a NuGet package library. Remove all of them.
  • At the bottom of the file, remove any .targets files that NuGet added. Also remove any NuGet targets or Tasks that NuGet added (might be a target that starts with the following line <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">).
  • If you have any packages that contain Roslyn Analyzers, make sure to remove any analyzer items that come from them.

Save your changes, right click the project in the solution explorer and reload the project.

Adding the project.json

In your project, add a new blank project.json file using one of the templates above. Ensure that the Build Action is set to None (should be the default). Once present, you might need to unload your project and reload it for NuGet to recognize it, so save your project, right-click your project and unload it and reload it.

Now you can either use the Manage NuGet Packages UI to re-add your packages or add them to the project.json by hand. Remember, you don’t necessarily have to re-add every package, only the top-level ones. For example, if you use Reactive Extensions, you only need Rx-Main, not the four other packages that it pulls in.

Build your project. If there are any errors related to NuGet, the error messages should guide you to the answer. Your project should build.

What you’ll notice for projects other than desktop .NET executables or UWP appx’s, is that the output directory will no longer contain every referenced library. This saves disk space and helps the build be faster by eliminating extra file copying. If you want the files to be in the output directory, like for unit test libraries that need to execute in-place, or for an application itself, there’s two extra steps to take:

  1. Unload the project once more and edit it to add the following to the first <PropertyGroup> at the top of the project file: <CopyNuGetImplementations>true</CopyNuGetImplementations>. This tells NuGet to copy all required implementation files to the output directory.
  2. Save and reload the project file. You’ll next need to add that runtimes section from above. The exact contents will depend on your project type. Rather than list them all out here, please see the Zeroconf or xUnit for Devices for the full examples.
    • For an AnyCPU Desktop .NET project win is sufficient
    • For Windows Store projects, you’ll need more

Once you repeat this for all of your projects, you’ll hopefully still have a working build(!) but now one where the projects are using the rich NuGet v3 capabilities. If you have a CI build system, you need to ensure that you’re using the latest nuget.exe to call restore on your solution prior to build. My preference is to always download the latest stable version from the dist link here: https://dist.nuget.org/win-x86-commandline/latest/nuget.exe.

Edge Cases

There may be some edge cases you hit when it comes to the transitive references. If you need to prevent any of the automatic project-to-project propagation of dependencies, the NuGet Docs can help.

In some rare cases, if you start getting compile errors due to missing System references, you may be hitting this bug, currently scheduled to be fixed in the upcoming 3.4 release. This happens if a NuGet package contains a <frameworkAssembly /> dependency that contains a System.* assembly. The workaround for now is to add <IncludeFrameworkReferencesFromNuGet>false</IncludeFrameworkReferencesFromNuGet> to your project file.

What this doesn’t do

There is often confusion between the use of project.json and its relation to the DNX/CLI project tooling that enables cross-compilation to different sets of targets. Visual Studio 2015 uses a new project type (.xproj) as a wrapper for these. This post isn’t about enabling an existing .csproj or .vbproj project type (the one most people have been using on “regular”) projects to start cross-compiling. Converting an existing project to use .xproj is a topic for another day and not all project types are supported by .xproj.

What this does do is enable the NuGet v3 features to be used by the existing project types today. If you have a .NET 4.6 desktop project, this will not change that. Likewise if your project is using the Xamarin Android 6 SDK, this won’t alter that either. It’ll simply make package management easier.

Acknowledgments

I would like to thank Andrew Arnott for his persistence in figuring out how to make this all work. He explained it to me as he was figuring it out and then recently helped to review this post. Thanks Andrew! A shout out is also due to Scott Dorman and Jason Malinowski for their valuable feedback reviewing this post.