This is a series on the new .Net Core bits.

Last time we created a new application. Once with dotnet CLI and once with Visual Studio.

We will have a look at some of the files and their contents. Since I assume the Program.cs file is self-explanatory, I will give it a miss.

project.json

Take a look at the two versions of project.json. Firstly, from our xproj project via Visual Studio:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },
  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.0"
    }
  },
  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

And a very similar one from dotnet new:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        }
      },
      "imports": "dnxcore50"
    }
  }
}

There are a few differences. So let's start with the similarities.
We have 4 top-level properties: version, buildOptions, dependencies and frameworks. Starting with the easy ones.

version

The version property is the minimum required metadata about the artifact you are building. Other meta-data includes 'name', title, description and may others.

The name option by default will use the parent folder name. To make things a bit more precise, you could add "name": "MyNewApp", at the top level.

buildOptions

The buildOptions section contains some useful properties to know about. Firstly, we have the emitEntryPoint. This is used to determine if the binary or exe produced is executable. By default, this means that you have a Program.Main() method that will be called to begin your program.

The one I found interesting is the "debugType": "portable". Using this option is required for the Visual Studio Code debugger to work. But it also means that your application will publish differently, depending on which value you select here. More on that later when we get to Publishing applications.

Other properties include nowarn, a list of compiler codes that should be suppressed, warningsAsErrors, which should be familiar to some people, and languageVersion which picks the flavor of C# the compiler will support.

Frameworks and dependencies

We get into some strange nesting territory when we start looking at frameworks and dependencies. The reason for this is that some dependencies support picking their framework when you target one for building. On the other hand, some frameworks need you to define different dependencies depending on what #if defines your code has.

Because of this, the two sections allow nesting. You can provide a dependency at the top level, at it will be available to all frameworks, regardless of which one you try and build against. And if you are building against a specific framework, you can have specific dependencies that you use when you build that framework. This even gives you the option to target different versions of the dependencies you use, depending on your target platform.

So with our examples above, we can see that the Visual Studio and dotnet CLI versions define the same result, just in two different ways. If we wanted to add new framework target, then we would need to work out which dependency configuration works best for us going forward.

Microsoft.NETCore.App

Let's look a bit closer at Microsoft.NETCore.App:

"Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.0"
    }

This is a dependency. This is a platform dependency. It's also a Nuget Package, which contains within it a bunch of system library NuGet packages.

Documentation for these is still a little unclear to me, so I will leave this as it is for now, and revisit when we need to change it later when we look at multi-targeting, referencing, and publishing.

netcoreapp1.0

Let's look a bit closer at netcoreapp1.0:

"frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }

The framework netcoreapp1.0 is one of many Target Framework Moniker (TFM) values that can be used here. As well as the old classics 'net45', 'net46', we now have some new ones, like this netcoreapp1.0. These seem to be similar and/or related to the TFMs that are supported as part of NuGet.

This article on TFMs attempts to explain these in a much greater detail than I can fit here in one paragraph.

But giving it a go, dotnetcoreapp1.0 is the reference to this release of .Net Core, as a target platform. So if you want to target this Framework(Runtime) Version, this is how.

As for "imports": "dnxcore50", It looks like this is just telling the compiler (or runtime, unsure) that if it sees anything targeting "dnxcore50", in a package, that is fine to use with this application. Reading that back, it probably is referring exactly to the NuGet Moniker that has been used during the early releases.

NETStandard.Library

Later on, we will look at libraries rather than applications, and we will discover a new dependency on "NETStandard.Library": "1.6.0". This is a NuGet package, that contains inside it multiple target versions, similar to the old Portable Class Library approach. Except now we have a more unified versioning strategy.

Again I defer to a blog post that better explains what this is, but again I will try.

Essentially it is a way to target a minimum supported base class library that you rely on. That way you can achieve better forwards compatibility when new versions of the existing platform come out (such as .Net Core 1.1, or 2.0 even), without having to re-release with new changes. It also means as new platforms come out (Such as Xbox Two), they just define their compatibility against a version of NetStandard, and we will be able to use any packages that target up to that version when building for that platform.

more on project.json

For information on how project.json interacts with NuGet itself, head over to the NuGet 3.0, .NET and project.json page on the nuget.org, which also has some format notes here. Notably, where there are differences between nuget and dotnet usages.

For a detailed look at all the things you can put in a project.json file from a .Net Core perspective, head on over to the project.json reference guide on the official docs pages.

Just a note from that page:

This topic is likely to change before release! You can track the status of this issue through our public GitHub issue tracker.

As I move forward into more complex examples, I'm sure we will discover more that can be added into the project.json file.

project.lock.json

When you run a dotnet restore from the command line, or whenever you save the project.json file in Visual Studio, the project.lock.json file is updated.

It appears that your project.json dependencies are reviewed, and a set of versions of NuGet packages is determined and recorded into this lock file. This is a similar pattern to that seen with npm, and RubyGems.

You can check this file into source control, but you also don't have to, since running dotnet restore will rebuild it for you.

MyNewApp.xproj

Taking a bit of a turn, we will look at the xproj file.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  </PropertyGroup>

  <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
  <PropertyGroup Label="Globals">
    <ProjectGuid>7be7fb75-24a9-4728-80bd-86b09c29ebf1</ProjectGuid>
    <RootNamespace>MyNewApp</RootNamespace>
    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
    <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
  </PropertyGroup>

  <PropertyGroup>
    <SchemaVersion>2.0</SchemaVersion>
  </PropertyGroup>
  <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Like the csproj files of the past, this file bridges between Visual Studio and MSBuild. In the same vein, this is an MSBuild file.

Unlike a csproj file, there isn't really much in here. All of the magic to make this a buildable project is inside the referenced targets and Props files, which then defer to the project.json file for the rest of the information they need.

From everything that has been said about the tooling, the balance of power between xproj and project.json will change, and we will probably see xproj swapped back for a csproj file again in the near future, say Visual Studio 2016 perhaps.

global.json

{
  "projects": [ "src", "test" ],
  "sdk": {
    "version": "1.0.0-preview2-003121"
  }
}

This is another magic file I still haven't managed to distil down. When I build in Visual Studio, the tooling does seem to use this to discover projects and determine which version of the (tools?) it should download as a dependency. The folders specified in projects have their top-level only searched for project folders.

Some little information on global.json (and again, the caveat of change) is found here. From what I have seen when playing around, even without this file you can still locate other projects as project references, so I am not sure of its true intent/power yet. That page does refer to build system, so it may be more tied to MSBuild​instead, rather than dotnet.

Next up

We now have a bit of an idea. All of the magic tying everything together seems to be locked up in the project.json file. Going forward we will try out some common use cases to determine what changes to the project.json we need to make to achieve our results.