Skip to content

Instantly share code, notes, and snippets.

@rainersigwald
Last active June 21, 2016 19:45
Show Gist options
  • Save rainersigwald/54e91c8648418c8e47ec58f8a46f758d to your computer and use it in GitHub Desktop.
Save rainersigwald/54e91c8648418c8e47ec58f8a46f758d to your computer and use it in GitHub Desktop.
NuGet flow in the UAP (csproj + project.json) world

A quick description of how NuGet works today for UAP (csproj + project.json) projects. Some documentation from NuGet is available.

I created a boring bog-standard console executable csproj, which produces these files:

c:\src\play\ConsoleApplication2>tree /f
Folder PATH listing for volume BOOTCAMP
Volume serial number is FC56-66FC
C:.
│   .gitattributes
│   .gitignore
│   ConsoleApplication2.sln
│
└───ConsoleApplication2
    │   App.config
    │   ConsoleApplication2.csproj
    │   Program.cs
    │
    └───Properties
            AssemblyInfo.cs

Then I added a project.json file next to the csproj. It contains a package reference to a package that delivers build logic (GitVersioning) and one that just provides a build- and runtime reference (Dataflow):

{
  "dependencies": {
    "Nerdbank.GitVersioning": "1.4.41",
    "Microsoft.Tpl.Dataflow": "4.5.24"
  },
  "frameworks": {
    "net452": {}
  },
  "runtimes": {
    "win": {}
  }
}

By itself, this does nothing. If I build with MSBuild (without doing nuget restore), the project doesn't use the additional logic that comes in the NuGet package. If my code actually used the Dataflow reference, the build would fail (because it wouldn't have the DLL to refer to).

But if I do restore, I get some interesting changes:

c:\src\play\ConsoleApplication2>nuget restore ConsoleApplication2.sln
MSBuild auto-detection: using msbuild version '14.0' from 'C:\Program Files (x86)\MSBuild\14.0\bin'.
Feeds used:
  C:\Users\raines\AppData\Local\NuGet\Cache
  C:\Users\raines\.nuget\packages\
  https://api.nuget.org/v3/index.json
  C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\

Restoring packages for c:\src\play\ConsoleApplication2\ConsoleApplication2\project.json...
All packages are compatible with .NETFramework,Version=v4.6.
Generating MSBuild file ConsoleApplication2.nuget.targets.

c:\src\play\ConsoleApplication2>tree /f
Folder PATH listing for volume BOOTCAMP
Volume serial number is FC56-66FC
C:.
│   .gitattributes
│   .gitignore
│   ConsoleApplication2.sln
│
└───ConsoleApplication2
    │   App.config
    │   ConsoleApplication2.csproj
    │   ConsoleApplication2.nuget.targets
    │   Program.cs
    │   project.json
    │   project.lock.json
    │
    └───Properties
            AssemblyInfo.cs

Specifically, there's a project.lock.json that contains information about the packages needed for the project (with conflicts resolved across the whole solution).

There's also ConsoleApplication2.nuget.targets:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Condition="'$(NuGetPackageRoot)' == ''">
    <NuGetPackageRoot>$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
  </PropertyGroup>
  <ImportGroup>
    <Import Project="$(NuGetPackageRoot)\Nerdbank.GitVersioning\1.4.41\build\dotnet\Nerdbank.GitVersioning.targets" Condition="Exists('$(NuGetPackageRoot)\Nerdbank.GitVersioning\1.4.41\build\dotnet\Nerdbank.GitVersioning.targets')" />
  </ImportGroup>
</Project>

NuGet detected what packages contained build logic and included them in this generated file. This file is then included from C:\Program Files (x86)\MSBuild\Microsoft\NuGet\Microsoft.NuGet.targets via

<Import Project="$(MSBuildProjectDirectory)\$(MSBuildProjectName).nuget.targets" Condition="Exists('$(MSBuildProjectDirectory)\$(MSBuildProjectName).nuget.targets') AND '$(IncludeNuGetImports)' != 'false'">

So now the build logic from the package is available to the project. To get the DLL references, at build time a target named ResolveNuGetPackageAssets runs the ResolveNuGetPackageAssets task, which loads project.lock.json and emits assemblies into the @(Reference) item. Then the usual reference-resolution machinery kicks in to get the right compiler command lines and so on.

Target "ResolveNuGetPackageAssets: (TargetId:20)" in file "C:\Program Files (x86)\MSBuild\Microsoft\NuGet\Microsoft.NuGet.targets" from project "c:\src\play\ConsoleApplication2\ConsoleApplication2\ConsoleApplication2.csproj" (target "ResolveAssemblyReferences" depends on it):
Task "MSBuild" skipped, due to false condition; ('%(_MSBuildProjectReferenceExistent.Extension)' == '.xproj') was evaluated as ('' == '.xproj').
Using "ResolveNuGetPackageAssets" task from assembly "C:\Program Files (x86)\MSBuild\Microsoft\NuGet\Microsoft.NuGet.Build.Tasks.dll".
Task "ResolveNuGetPackageAssets" (TaskId:10)
  Task Parameter:IncludeFrameworkReferences=True (TaskId:10)
  Task Parameter:RuntimeIdentifier=win (TaskId:10)
  Task Parameter:ProjectLanguage=C# (TaskId:10)
  Task Parameter:ProjectLockFile=project.lock.json (TaskId:10)
  Task Parameter:
      ContentPreprocessorValues=
          rootnamespace
                  Value=ConsoleApplication2
          assemblyname
                  Value=ConsoleApplication2
          fullpath
                  Value=c:\src\play\ConsoleApplication2\ConsoleApplication2
          outputfilename
                  Value=ConsoleApplication2.exe
          filename
                  Value=ConsoleApplication2.csproj (TaskId:10)
  Task Parameter:ContentPreprocessorOutputDirectory=obj\Debug\\NuGet (TaskId:10)
  Task Parameter:TargetMonikers=.NETFramework,Version=v4.5.2 (TaskId:10)
  Output Item(s): 
      ReferenceCopyLocalPaths=
          C:\Users\raines\.nuget\packages\Microsoft.Tpl.Dataflow\4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll
                  NuGetIsFrameworkReference=false
                  NuGetPackageId=Microsoft.Tpl.Dataflow
                  NuGetPackageVersion=4.5.24
                  NuGetSourceType=Package
                  Private=false (TaskId:10)
  Output Item(s): 
      _ReferencesFromNuGetPackages=
          C:\Users\raines\.nuget\packages\Microsoft.Tpl.Dataflow\4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll
                  NuGetIsFrameworkReference=false
                  NuGetPackageId=Microsoft.Tpl.Dataflow
                  NuGetPackageVersion=4.5.24
                  NuGetSourceType=Package
                  Private=false (TaskId:10)
  Output Item(s): 
      ReferencedNuGetPackages=
          Microsoft.Tpl.Dataflow
          Nerdbank.GitVersioning (TaskId:10)
Done executing task "ResolveNuGetPackageAssets". (TaskId:10)
Added Item(s): 
    Reference=
        C:\Users\raines\.nuget\packages\Microsoft.Tpl.Dataflow\4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll
                NuGetIsFrameworkReference=false
                NuGetPackageId=Microsoft.Tpl.Dataflow
                NuGetPackageVersion=4.5.24
                NuGetSourceType=Package
                Private=false
Set Property: DTARUseReferencesFromProject=true
Task "CreateItem" skipped, due to false condition; ('@(_NuGetContentItems)' != '') was evaluated as ('' != '').
Done building target "ResolveNuGetPackageAssets" in project "ConsoleApplication2.csproj".: (TargetId:20)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment