.NET Tools are great, and we've known them for a while in .NET Core as ".NET Core Global Tools", but often we don't want these tools to be shared globally across our machine. This is where "Local Tools" come in handy, introduced in .NET Core SDK 3.0.

As an example, let's look at the Cake (Cake build) tool, however this applies to any .NET Tool.

We could install Cake.Tool globally like this:

dotnet tool install --global Cake.Tool

This will work just fine, but if we have multiple projects, we are going to want to have different versions of the Cake tool installer per project.

The Old Way

Previously we could do this by running the following command:

dotnet tool install --tool-path .tools Cake.Tool

Where .tools is the path you'd like to install the tool to. With this we could run cake like this (on macOS, it works similarly on Windows):

./.tools/dotnet-cake --help

That was fine, however there are a few ways the overall approach lacked:

  • You didn't have a file with a discoverable list of tools used for the project, you'd typically have the above commands in a script file, and you'd need to think about should it be pwsh,  bash or other.
  • If you had the same version of a tool in x different folders, you'd have x copies of the tool on your disk (packages here would be restored to .tools/.store/).
  • Launching the tool was platform dependant due to the tools file suffix.
  • The initial command was hard to remember, I always needed to look up --tool-path.

The New Way ✨

With the .NET Core 3 CLI, you we can now use Local Tools.

First you need a tools manifest file, this will eventually contain the list of installed tools. We can create it using this command:

dotnet new tool-manifest

Now we have a file created at .config/dotnet-tools.json with the following contents:

{
  "version"1,
  "isRoot"true,
  "tools": {}
}

Now we can start installing tools, now with an easier to remember command:

dotnet tool install Cake.Tool

I don't know about you, but I've ran this command many times before they made this "a thing" expecting it to work.

Now if we look at our dotnet-tools.json file it looks like this:

{
  "version"1,
  "isRoot"true,
  "tools": {
    "cake.tool": {
      "version""0.34.1",
      "commands": [
        "dotnet-cake"
      ]
    }
  }
}

Now we can run our command with:

dotnet cake --help
# which is short for
dotnet dotnet-cake --help
# you can alternatively write it as:
dotnet tool run dotnet-cake --help

Now we can check in our .config and other users of our project can discover the tools we have added. The tool is installed to the global nuget package cache, like a typical nuget package, if the user does not have the package present they will get the following message when trying to first run the tool:

Run "dotnet tool restore" to make the "dotnet-cake" command available.

So they are just one simple command away from restoring the tool and being able to use it.