This is a series on the latest 2.* .Net Core bits, Following on from the original .Net Core Series

(At the time of writing, 2.1.4. I use windows, you don't have to!)

We can now take everything we have learned and start publishing our applications. Luckily, there is a command for that too.

The Setup

I don't know why I bother saying anything here, this is pretty straight-forward by now.

mkdir MyPackagedApp
cd MyPackagedApp
mkdir MyApp
cd MyApp
dotnet new console
cd ../
dotnet new sln
dotnet sln add .\MyApp\MyApp.csproj
dotnet build

And again, we replace the contents of Program.cs. This time, let's try printing a few verses of Dr. Seuss Explaining Computers

using System;

namespace MyApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Dr. Seuss Explains Computers");
            Console.WriteLine("============================");
            Console.WriteLine("");
            Console.WriteLine("If a Packet Hits a pocket on a socket on a port, and the bus is interrupted as a very last resort, and the address of the memory makes your floppy disk abort, then the socket packet pocket has an error to report.");
            Console.WriteLine("If your cursor finds a menu item followed by a dash, and the double-clicking icon puts your window in the trash, and your data is corrupted 'cause the index doesn't hash, then the situation's hopeless and your systems gonna crash!");
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("Read the rest at: http://computerjokes.net/112.asp");
            Console.WriteLine("");
        }
    }
}

And for good measure another build.

dotnet build

Publishing

To publish our application we use the most obvious of commands. As before with dotnet test, running this against the solution will result in publishing all projects that are publishable.

dotnet publish

Which results in the following file structure.

MyPackagedApp
 +-- MyApp
      |-- MyApp.csproj
      |-- Program.cs
      +-- bin
           +-- Debug
                +-- netcoreapp2.0
                     |-- MyApp.deps.json
                     |-- MyApp.dll
                     |-- MyApp.pdb
                     |-- MyApp.runtimeconfig.dev.json
                     |-- MyApp.runtimeconfig.json
                     +-- publish
                          |-- MyApp.deps.json
                          |-- MyApp.dll
                          |-- MyApp.pdb
                          \-- MyApp.runtimeconfig.json

The publish command adds a new build artifact folder - publish. Inside are all of the files for the application to distribute (to someone who has the runtime available). In this case, our application is a netcoreapp2.0 app, so the distribution is the dll, to run with .Net Core runtime.

We can execute our application using the command dotnet MyApp.dll from inside the publish folder, or from the project folder using a relative path dotnet .\MyApp\bin\Debug\netcoreapp2.0\MyApp.dll.

Output folder

We probably want to publish to a different location, so we can use the option -o <OUTPUT_PATH> to publish somewhere else. At the same time, we might use the -c <CONFIGURATION> option to specify a release build. (Note that output path is relative to the folder containing the csproj, not the folder running the command.)

dotnet publish -o "../Pack/MyApp" -c Release

Which will give you the following in the new Pack folder:

Pack
 +-- MyApp
      |-- MyApp.deps.json
      |-- MyApp.dll
      |-- MyApp.pdb
      \-- MyApp.runtimeconfig.json

Include Files

We can also include extra files into our output folder. For instances, lets add a README.md file beside our program.cs file.

MyNewApp
========

version 1.0.0

This app prints the first verse of a poem.

In our csproj file we can use the standard MSBuild settings for adding files.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  
  <ItemGroup>
    <Content Include="README.md" CopyToPublishDirectory="Always" />
  </ItemGroup>

</Project>

dotnet publish -o "../Pack/MyApp" -c Release

That's how easy it is to include extra files when publishing.

Publishing .Net 4.5.2 applications

Or 4.6, 4.6.2 & 4.7. They all basically follow the same principles.

It all starts with the csproj file. For reality sake, I am going to add to rather than replace the netcoreapp2.0 target (as seen in the last article).

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp2.0;net452</TargetFrameworks>
  </PropertyGroup>
  
  <ItemGroup>
    <Content Include="README.md" CopyToPublishDirectory="Always" />
  </ItemGroup>

</Project>
dotnet build  
dotnet publish -o "..\Pack\MyApp_Net452" -c Release -f net452

Your Pack folder should now have two packaged application folders like the following:

Pack
 +-- MyApp
 |    |-- MyApp.deps.json
 |    |-- MyApp.dll
 |    |-- MyApp.pdb
 |    |-- MyApp.runtimeconfig.json
 |    \-- README.md
 +-- MyApp_Net452
     |-- MyApp.exe
     |-- MyApp.pdb
     \-- README.md

We now have a .Net 4.5.2 EXE that we can run anywhere where .Net 4.5.2 has been installed.

One thing missing is an App.Config, which some of your existing applications likely use for useful things like configuration. You can simply add an include with a transform to map a file called App.Config, with a transformation to the more expected name of %APPNAME%.exe.config. (Although there are now better ways to do config, you may find this useful as a transition path.)

Add our App.Config file.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <!-- Settings go here, you know how to use them already? -->
  </appSettings>
</configuration>

And that's it. As on SDK 2.1 that I am using, this is automatically loaded and used the same as it was in classic .Net.

dotnet build
dotnet publish -f net452
dotnet publish -f netcoreapp2.0

Remember, if you use build, pack, or publish without using -f <framework> you will get all target frameworks included. (And if you use -f and -o together with publish, it will dump the targets' output on top of each other in the same folder. Probably don't do that.)

Let's take one last look at the file tree in the MyApp project folder:

MyPackagedApp
 +-- MyApp
      |-- MyApp.csproj
      |-- Program.cs
      +-- bin
           +-- Debug
                +-- net452
                |    |-- MyApp.exe
                |    |-- MyApp.exe.config
                |    |-- MyApp.pdb
                |    +-- publish
                |         |-- MyApp.exe
                |         |-- MyApp.exe.config
                |         |-- MyApp.pdb
                |         \-- README.md
                +-- netcoreapp2.0
                     |-- MyApp.deps.json
                     |-- MyApp.dll
                     |-- MyApp.dll.config
                     |-- MyApp.pdb
                     |-- MyApp.runtimeconfig.dev.json
                     |-- MyApp.runtimeconfig.json
                     +-- publish
                          |-- MyApp.deps.json
                          |-- MyApp.dll
                          |-- MyApp.dll.config
                          |-- MyApp.pdb
                          |-- MyApp.runtimeconfig.json
                          \-- README.md

Inside our publish folders, which are our publish outputs, it includes both the earlier README.md and the transformed app.config file ready to zip up and distribute as required.

In reality, you might have to look for a bridging library to actually load the app.config in netcoreapp2.0 but I haven't tried that recently so I leave that to you, if you want/need it. Investigate the new configuration. Really.

Onward to the finish!

We are almost wrapped up, only one last piece to go, Self-contained Applications. Stay tuned.