Sunday, April 16, 2017

How to make a dotnet CLI Tool

Good news, everyone! It is remarkably easy to make a new dotnet CLI (Command Line Interface) tool! I recently created a CLI tool for one of my new projects, Tact.NET RPC, and in this post I will be referencing that project as my example.

The Basics

Step 1) Create your CLI Console App

All you have to do is...

  1. Create a normal .NET Core Console App.
    • NOTE: Currently, the dotnet CLI only supports netcoreapp1.0
  2. Rename the assembly to be prefixed with "dotnet-"
  3. dotnet pack the project and put the package in your local NuGet package source

...that is it! It is literally that easy to create your CLI tool!

Recommendation: use Microsoft.Extensions.Configuration to parse your command line arguments in a standard way.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.0</TargetFramework>
    <AssemblyName>dotnet-tactrpcgen</AssemblyName>
    <PackageId>Tact.Rpc.Generator</PackageId>
    <Version>1.0.3</Version>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="1.1.1" />
  </ItemGroup>
</Project>

Step 2) Consume your CLI Tool NuGet Package

Edit your csproj file and add a DotNetCliToolReference element that references your package.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Tact.Rpc.Generator" Version="1.0.3" />
  </ItemGroup>
</Project>

Now you are able to invoke your console app via the dotnet command line whenever it is executed in the same path as the csproj.

That's it; it really is that easy!

Development Tips

Here is a simple way to automate the creation and consumption of your CLI tool NuGet package during development.

Create a local package directory

I recommend that you create a packages folder at the root of your solution along with a NuGet.config file; this will allow you to automatically build and consume NuGet packages from this location.

<configuration>
  <packageSources>
    <add key="LocalPackages" value="packages" />
  </packageSources>
</configuration>

Pack on post build

Go back and update your CLI tool's csproj to include a post build step that packs your project and places the output in your package folder. (There is a csproj GeneratePackageOnBuild setting, however at this time I do not believe that you can customize the output directory, so instead I recommend using a post build step.)

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.0</TargetFramework>
    <AssemblyName>dotnet-tactrpcgen</AssemblyName>
    <PackageId>Tact.Rpc.Generator</PackageId>
    <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
    <PostBuildEvent>
      dotnet pack $(SolutionDir)rpc\src\Tact.Rpc.Generator -o $(SolutionDir)packages --no-build
    </PostBuildEvent>
    <Version>1.0.3</Version>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="1.1.1" />
  </ItemGroup>
</Project>

Execute on pre build

Obviously this is a completely optional step that should only be used when necessary; in the case of Tact.NET RPC, the active code generation is powered by the CLI tool, and thus it needs to run every build.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
    <PreBuildEvent>
      cd $(SolutionDir)rpc\demo\Demo.Rpc
      dotnet tactrpcgen
    </PreBuildEvent>
  </PropertyGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Tact.Rpc.Generator" Version="1.0.3" />
  </ItemGroup>
</Project>

Increment version to update

NOTE: Despite updating the your local package directory each build, the package will actually be cached in your global NuGet cache, and thus updates will not be picked up unless you increment the version number.

Enjoy,
Tom

No comments:

Post a Comment

Real Time Web Analytics