{"id":228304,"date":"2020-02-12T10:19:02","date_gmt":"2020-02-12T18:19:02","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/visualstudio\/?p=228304"},"modified":"2020-11-04T14:29:45","modified_gmt":"2020-11-04T22:29:45","slug":"creating-net-core-global-tools-on-macos","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/visualstudio\/creating-net-core-global-tools-on-macos\/","title":{"rendered":"Creating .NET Core global tools on macOS"},"content":{"rendered":"<p>One of the really cool aspects about .NET Core is the support for <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/tools\/global-tools\">global tools<\/a>. You can use global tools to simplify common tasks during your development workflow. For example, you can create tools to minify image assets, simplify working with source control, or perform any other task that you can automate with the command line. After developing your tool, you can distribute it on <a href=\"https:\/\/www.nuget.org\/\">NuGet.org<\/a>, or any other NuGet repository, to share the tool with others. Since .NET Core is cross platform, your global tools will also work cross platform, assuming your code doesn\u2019t contain any platform specific code. You can find existing global tools <a href=\"https:\/\/github.com\/natemcmaster\/dotnet-tools\">here<\/a>. You can also create local tools, those that are associated with specific projects and not available globally. For more info on local tools see the <em>.NET Core Tools \u2014 local installation<\/em> section in <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-core-3-0\/\">Announcing .NET Core 3.0<\/a>.<\/p>\n<p>In this post we will discuss how you can create global tools when developing on macOS as well as how to prepare them to distribute using NuGet. Let\u2019s get started with our first global tool. Today, we will be using Visual Studio for Mac, but you can follow similar steps if you are using a different IDE or editor. To ensure you have everything you need to follow this tutorial, <a href=\"https:\/\/visualstudio.microsoft.com\/vs\/mac\/\">download Visual Studio for Mac<\/a>. The code we will be reviewing in this post is available on GitHub, a link is at the end of this post.<\/p>\n<h2>Hello World<\/h2>\n<p>Let\u2019s create a very basic global tool that will print \u201cHello World\u201d to the user. To create our tool, we will work through the following steps:<\/p>\n<ol>\n<li>Create the project<\/li>\n<li>Modify the project file to make it a global tool<\/li>\n<li>Implement our code<\/li>\n<li>Test our new global tool<\/li>\n<\/ol>\n<p>The first thing you\u2019ll want to do when creating a global tool is to create a project. Since global tools are console applications, we will use the console project template to get started. After launching Visual Studio for Mac you\u2019ll see the dialog below, click New to begin creating the project. If you already have Visual Studio open, you could also use the \u21e7\u2318N shortcut to open the new project dialog.<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-228305 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-1024x627.png\" alt=\"Image vsmac new project\" width=\"640\" height=\"392\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-1024x627.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-300x184.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-768x470.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project.png 1440w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>From here we will create a .NET Core Console project by going to <em>.NET Core &gt; App &gt; Console Application<\/em>.<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-228307 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-console-1024x743.png\" alt=\"visual studio for mac new console project\" width=\"640\" height=\"464\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-console-1024x743.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-console-300x218.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-console-768x557.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-console-1536x1115.png 1536w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-new-project-console.png 1802w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/p>\n<p>After selecting Console Application, click Next to select the version of .NET Core. I have selected .NET Core 3.1. Click Next after selecting that, and then provide the name and location for the project. I have named the project HelloTool.<\/p>\n<p>&nbsp;<\/p>\n<h2>Customize the project for NuGet<\/h2>\n<p>Now that we\u2019ve created the project, the first thing to do is to customize the project file to add properties that will make this a global tool. To edit the project file, right click on the project in the Solution Pad and select <em>Tools &gt; Edit File<\/em>. This is demonstrated in the following image.<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-228313 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-edit-project-file-1024x884.png\" alt=\"visual studio for mac menu option to edit the project file\" width=\"640\" height=\"553\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-edit-project-file-1024x884.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-edit-project-file-300x259.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-edit-project-file-768x663.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-edit-project-file.png 1386w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/p>\n<p><em>Note: the menu option to edit the project file is moving to the top level in the context menu as Edit Project File soon.<\/em><\/p>\n<p>The .csproj file, an MSBuild file that defines the project, will be opened in the editor. To make the project into a global tool, we must enable the project to be packed into a NuGet package. You can do this by adding a property named <em>PackAsTool<\/em> and setting it to <em>true <\/em>in the .csproj file. If you are planning to publish the package to NuGet.org you will also want to specify some additional properties that NuGet.org will surface to users. You can see the full list of NuGet related properties that can be set over at <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/tools\/csproj#nuget-metadata-properties\">NuGet metadata properties<\/a>. Let\u2019s look at the properties I typically set when creating a global tool. I\u2019ve pasted the .csproj file below.<\/p>\n<pre class=\"lang:xhtml decode:true \">&lt;Project\u00a0Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\u00a0\u00a0&lt;PropertyGroup&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;TargetFramework&gt;netcoreapp3.1&lt;\/TargetFramework&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0&lt;!--\u00a0global\u00a0tool\u00a0related\u00a0properties\u00a0--&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;PackAsTool&gt;true&lt;\/PackAsTool&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;ToolCommandName&gt;hellotool&lt;\/ToolCommandName&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;PackageOutputPath&gt;.\/nupkg&lt;\/PackageOutputPath&gt;\r\n\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0&lt;!--\u00a0nuget\u00a0related\u00a0properties\u00a0--&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;Authors&gt;Sayed\u00a0Ibrahim\u00a0Hashimi&lt;\/Authors&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;Description&gt;My\u00a0hello\u00a0world\u00a0global\u00a0tool&lt;\/Description&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;Version&gt;1.0.0&lt;\/Version&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;Copyright&gt;Copyright\u00a02020\u00a0\u00a9\u00a0Sayed\u00a0Ibrahim\u00a0Hashimi.\u00a0All\u00a0rights\u00a0reserved.&lt;\/Copyright&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;PackageLicenseExpression&gt;Apache-2.0&lt;\/PackageLicenseExpression&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;RepositoryUrl&gt;https:\/\/github.com\/sayedihashimi\/global-tool-sample&lt;\/RepositoryUrl&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;RepositoryType&gt;git&lt;\/RepositoryType&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;PackageType&gt;DotNetCliTool&lt;\/PackageType&gt;\r\n\u00a0\u00a0&lt;\/PropertyGroup&gt;\r\n&lt;\/Project&gt;<\/pre>\n<p>There are two sections of properties that I have added here. Below you\u2019ll find a description of each of these properties.<\/p>\n<table style=\"border-collapse: collapse; width: 100%; height: 336px;\">\n<tbody>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\"><strong>Property Name<\/strong><\/td>\n<td style=\"width: 50%; height: 28px;\"><strong>Description<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">PackAsTool<\/td>\n<td style=\"width: 50%; height: 28px;\">Set this to true for all global tools, this will enable packing the project into a NuGet package.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">ToolCommandName<\/td>\n<td style=\"width: 50%; height: 28px;\">Optional name for the tool.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">PackageOutputPath<\/td>\n<td style=\"width: 50%; height: 28px;\">Path to where the .nupkg file should be placed.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">Authors<\/td>\n<td style=\"width: 50%; height: 28px;\">Name of the author(s) of the project.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">Description<\/td>\n<td style=\"width: 50%; height: 28px;\">Description that will be shown in nuget.org and other places.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">Version<\/td>\n<td style=\"width: 50%; height: 28px;\">Version of the NuGet package. For each release to nuget.org this must be unique.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">Copyright<\/td>\n<td style=\"width: 50%; height: 28px;\">Copyright declaration.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">PackageLicenseExpression<\/td>\n<td style=\"width: 50%; height: 28px;\">An <a href=\"https:\/\/spdx.org\/licenses\/\">SPDX license identifier<\/a> or expression.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">RepositoryUrl<\/td>\n<td style=\"width: 50%; height: 28px;\">Specifies the URL for the repository where the source code for the package resides and\/or from which it&#8217;s being built.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">RepositoryType<\/td>\n<td style=\"width: 50%; height: 28px;\">Repository type. Examples: git, tfs.<\/td>\n<\/tr>\n<tr style=\"height: 28px;\">\n<td style=\"width: 50%; height: 28px;\">PackageType<\/td>\n<td style=\"width: 50%; height: 28px;\">For tools specify this as <em>DotNetCliTool<\/em>.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>It\u2019s a good idea to specify these properties now, so that you can focus on the code for the global tool. If you\u2019re just creating a tool to play around, or for personal use, I recommend just setting <em><span class=\"TrackedChange BCX0 SCXW45112930\"><span class=\"TextRun BCX0 SCXW45112930\" lang=\"EN-US\" xml:lang=\"EN-US\" data-contrast=\"auto\"><span class=\"NormalTextRun SpellingErrorV2 CommentStart BCX0 SCXW45112930\">PackAsTool<\/span><\/span><\/span><\/em><span class=\"TextRun BCX0 SCXW45112930\" lang=\"EN-US\" xml:lang=\"EN-US\" data-contrast=\"auto\"><span class=\"NormalTextRun BCX0 SCXW45112930\">,\u00a0<\/span><\/span><em><span class=\"TrackedChange BCX0 SCXW45112930\"><span class=\"TextRun BCX0 SCXW45112930\" lang=\"EN-US\" xml:lang=\"EN-US\" data-contrast=\"auto\"><span class=\"NormalTextRun SpellingErrorV2 BCX0 SCXW45112930\">ToolCommandName<\/span><\/span><\/span><\/em><span class=\"TextRun BCX0 SCXW45112930\" lang=\"EN-US\" xml:lang=\"EN-US\" data-contrast=\"auto\"><span class=\"NormalTextRun BCX0 SCXW45112930\">\u00a0and\u00a0<\/span><\/span><em><span class=\"TrackedChange BCX0 SCXW45112930\"><span class=\"TextRun BCX0 SCXW45112930\" lang=\"EN-US\" xml:lang=\"EN-US\" data-contrast=\"auto\"><span class=\"NormalTextRun SpellingErrorV2 BCX0 SCXW45112930\">PackageOutputPath<\/span><\/span><\/span><\/em>. Now let\u2019s take a closer look at the code.<\/p>\n<p>In the Program.cs file you\u2019ll find that the following code was added when we created the project.<\/p>\n<pre class=\"lang:c# decode:true \">using System;\r\n\r\nnamespace HelloWorld {\r\n    class Program {\r\n        static void Main(string[] args) {\r\n            Console.WriteLine(\"Hello World!\");\r\n       }\r\n    }\r\n}<\/pre>\n<p>Since the code is already printing \u201cHello World!\u201d, we can use this as is with no modifications for now. Let\u2019s move on to try executing this as a global tool at the command line. We will first need to package this as a NuGet package.<\/p>\n<h2>Pack and Test the tool<\/h2>\n<p>To create a NuGet package from this project you can use the built in Pack command offered by Visual Studio for Mac. To get there, right-click your project and then select Pack as seen in the next image.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228310 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-pack-menu-item.png\" alt=\"visual studio for mac pack menu option\" width=\"940\" height=\"642\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-pack-menu-item.png 940w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-pack-menu-item-300x205.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-pack-menu-item-768x525.png 768w\" sizes=\"(max-width: 940px) 100vw, 940px\" \/><\/p>\n<p>After you invoke the Pack command, the NuGet package (.nupkg file) will be created in the directory we specified in the <em>PackageOutputPath<\/em> property. In our case it will go into a folder named <em>nupkg<\/em> in the project folder. Now that we have created a NuGet package from the project, we will register the global tool and try it from the command line.<\/p>\n<p>To install and test the global tool, first open the Terminal app, or your favorite alternative. You\u2019ll want to change directory into the project directory and run the commands from there. You will need to register the package as a tool using the following command.<\/p>\n<pre class=\"lang:batch decode:true\">dotnet tool install --global --add-source .\/nupkg HelloTool<\/pre>\n<p>Here we are calling <em>dotnet tool<\/em> with the <em>install<\/em> command to install the tool. By passing<em> \u2013global<\/em>, the tool will be available from any folder on your machine. We passed <em>\u2013add-source<\/em> with the location to the folder where the .nupkg file is located so that our new tool can be located and installed. After executing this command, you should see output like the following:<\/p>\n<pre class=\"lang:batch decode:true\">You can invoke the tool using the following command: hellotool\r\n\r\nTool 'hellotool' (version '1.0.0') was successfully installed.<\/pre>\n<p>Let\u2019s try to invoke the tool with <em>hellotool<\/em> to see if it\u2019s working.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228311 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-01.png\" alt=\"output from hellotool\" width=\"398\" height=\"154\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-01.png 398w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-01-300x116.png 300w\" sizes=\"(max-width: 398px) 100vw, 398px\" \/><\/p>\n<p>If you run into a command not found error, you may need to modify your PATH variable. You should ensure that the full path to <em>~\/.dotnet\/tools<\/em> is include in the PATH variable. By full path, I mean the ~ should be expanded, for example <em>\/Users\/sayedhashimi\/.dotnet\/tools<\/em> in my case. Now that we have seen how to get started with a tool, let\u2019s do something more interesting by adding some code to the project.<\/p>\n<h2>Adding parameters using DragonFruit<\/h2>\n<p>To make this more realistic we want to add some features like; adding support for parameters, displaying help and more. We could implement all of this directly by using <a href=\"https:\/\/github.com\/dotnet\/command-line-api\">System.CommandLine<\/a>, but the .NET Core team is working on a layer that will simplify it for us called <a href=\"https:\/\/github.com\/dotnet\/command-line-api\/wiki\/DragonFruit-overview\">DragonFruit<\/a>. We will use the DragonFruit to help us create this command quickly.<\/p>\n<p><strong>Note: DragonFruit is currently an experimental app model for System.CommandLine. This information is subject to change as it is being developed.<\/strong><\/p>\n<p>Now we want to add a couple of parameters to the app to make it more realistic. Before we do that, let\u2019s first add the DragonFruit NuGet package to the project and then go from there. To add the NuGet package right click on your app and select <em>Manage NuGet Packages<\/em>.<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-228308 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-manage-nuget-pkgs-1024x754.png\" alt=\"visual studio for mac manage nuget packages menu option\" width=\"640\" height=\"471\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-manage-nuget-pkgs-1024x754.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-manage-nuget-pkgs-300x221.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-manage-nuget-pkgs-768x565.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-manage-nuget-pkgs.png 1030w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/p>\n<p>When the Manage NuGet Packages dialog appears, first check the checkbox to <em>Show pre-release packages<\/em> in the lower left, and the search for <em>System.CommandLine.DragonFruit<\/em>. After that click on the <em>Add Package<\/em> button to add the package to your project. See the following image.<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-228315 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-nuget-dragonfruit-1024x670.png\" alt=\"visual studio for mac add dragonfruit nuget package\" width=\"640\" height=\"419\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-nuget-dragonfruit-1024x670.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-nuget-dragonfruit-300x196.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-nuget-dragonfruit-768x502.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-nuget-dragonfruit-1536x1005.png 1536w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-nuget-dragonfruit.png 1682w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/p>\n<p>Now that we have added the package, we are ready to add some parameters to the global tool. With DragonFruit it\u2019s really easy to add parameters to your tools, you just declare the parameters as arguments in the main method itself. Let\u2019s add a name and age parameter to this global tool. The updated code for Program.cs is shown below.<\/p>\n<pre class=\"lang:c# decode:true\">using System;\r\n\r\nnamespace HelloTool {\r\n\u00a0\u00a0\u00a0 class Program {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 static void Main(string name = \"World\", int age = 0) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 string message = age &lt;= 0 ? $\"Hello there {name}!\" : $\"Hello there {name}, who is {age} years old\";\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Console.WriteLine(message);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n}<\/pre>\n<p>In the code above we have added the parameters as arguments in the Main method, and then we craft a new message using those values. Now we want to test that the changes that we have made are working correctly before making further changes. If you want to just run the app you can use <em>Run &gt; Start without Debugging<\/em>, or <em>Run &gt; Start Debugging<\/em> from the menu bar to run it as a vanilla console app. What we want to do is to test it as a .NET Core global tool as well. To do that we will follow the steps below.<\/p>\n<ol>\n<li>Pack the project in Visual Studio for Mac<\/li>\n<li>Uninstall the global tool<\/li>\n<li>Install the global tool<\/li>\n<li>Run the tool<\/li>\n<\/ol>\n<p>Since we will need to install\/uninstall the tool often, we can simplify that by creating a new Custom Tool in Visual Studio for Mac to facilitate this. To get started go to <em>Tools &gt; Add Custom Tool<\/em>.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228316 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-menu.png\" alt=\"visual studio for mac add custom tool menu option\" width=\"818\" height=\"698\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-menu.png 818w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-menu-300x256.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-menu-768x655.png 768w\" sizes=\"(max-width: 818px) 100vw, 818px\" \/><\/p>\n<p>This will bring up a new dialog where we can create the two custom tools to handle install\/uninstall. To start, click the Add button and then configure each tool.<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-228317 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-add-button-1024x749.png\" alt=\"visual studio for mac custom tool add button\" width=\"640\" height=\"468\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-add-button-1024x749.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-add-button-300x219.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-add-button-768x562.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-add-button-1536x1123.png 1536w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-add-button.png 1920w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/p>\n<p>We want to create two tools with the following values:<\/p>\n<p><u>Install Tool<\/u><\/p>\n<ul>\n<li>Title = <em>Install project global tool<\/em><\/li>\n<li>Command = <em>dotnet<\/em><\/li>\n<li>Arguments = <em>tool install &#8211;global &#8211;add-source .\/nupkg ${ProjectName}<\/em><\/li>\n<li>Working directory = <em>${ProjectDir}<\/em><\/li>\n<\/ul>\n<p><u>Uninstall Tool<\/u><\/p>\n<ul>\n<li>Title = <em>Install project global tool<\/em><\/li>\n<li>Command = <em>dotnet<\/em><\/li>\n<li>Arguments = <em>tool uninstall &#8211;global ${ProjectName}<\/em><\/li>\n<li>Working directory = <em>${ProjectDir}<\/em><\/li>\n<\/ul>\n<p>The Uninstall tool, for example, should look like the following:<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-228318 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-custom-tool-uninstall-1024x749.png\" alt=\"visual studio for mac custom tool uninstall sample\" width=\"640\" height=\"468\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-custom-tool-uninstall-1024x749.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-custom-tool-uninstall-300x219.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-custom-tool-uninstall-768x562.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-custom-tool-uninstall-1536x1123.png 1536w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-custom-tool-uninstall.png 1920w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/p>\n<p>After adding these tools you\u2019ll see them appear in the Tools menu as shown below.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228319 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-menu-with-custom.png\" alt=\"visual studio for mac tools menu with custom tools\" width=\"444\" height=\"664\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-menu-with-custom.png 444w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-add-custom-tool-menu-with-custom-201x300.png 201w\" sizes=\"(max-width: 444px) 100vw, 444px\" \/><\/p>\n<p>To invoke these newly added tools, you can simply click on the command. Since we authored these tools using the parameter ${ProjectName} these commands should work on your other global tool projects assuming the tool name is the same as the project name. Let\u2019s try the uninstall command. Take a look at the experience in the animate gif below, which shows the tool being invoked and the output being displayed in the Application Output Pad.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228327 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-pack-and-run-tool.gif\" alt=\"gif showing visual studio mac pack and install via custom tool\" width=\"1090\" height=\"632\" \/><\/p>\n<p>We can see that the tool was successfully installed. Now we can go back to the terminal to test the global tool itself. Go back to the terminal and execute <em>hellotool<\/em>, and verify that you see the message <em>Hello there World!<\/em><\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228322 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-hellotool-output-1.png\" alt=\"output from running hellotool\" width=\"658\" height=\"162\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-hellotool-output-1.png 658w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-hellotool-output-1-300x74.png 300w\" sizes=\"(max-width: 658px) 100vw, 658px\" \/><\/p>\n<p>The drawback to this approach is that you have to perform three separate steps in the IDE; pack, uninstall and install. You can simplify this by modifying the project file, the .csproj file. Add the following target to your .csproj file immediately before &lt;\/Project&gt;.<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;Target Name=\"InstallTool\" DependsOnTargets=\"Pack\"&gt;\r\n\u00a0\u00a0\u00a0 &lt;Exec Command=\"dotnet tool uninstall --global $(ToolCommandName)\" IgnoreExitCode=\"true\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;Exec Command=\"dotnet tool install --global --add-source $(PackageOutputPath) $(ToolCommandName)\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;Exec Command=\"$(ToolCommandName) --help\" \/&gt;\r\n&lt;\/Target&gt;<\/pre>\n<p>This is an MSBuld target that we can call to take care of all three steps for us. It will also call the tool to display its help output after it\u2019s installed. After adding this target to your .cspoj file, you can execute it with dotnet build -t:InstallTool. In Visual Studio for Mac you can create a new Custom Tool with the following properties to invoke this target.<\/p>\n<ul>\n<li>Title = <em>Install tool<\/em><\/li>\n<li>Command = <em>dotnet<\/em><\/li>\n<li>Arguments = <em>-t:InstallTool<\/em><\/li>\n<li>Working directory = <em>${ProjectDir}<\/em><\/li>\n<\/ul>\n<p>Then you can invoke this new custom tool instead of the three steps we outlined. Since it\u2019s not always feasible to edit the project file, this doc will continue using the previous approach.<\/p>\n<h2>Help output<\/h2>\n<p>Now let\u2019s take a look at the default help output that we get when the DragonFruit package is in the project. Let\u2019s execute hellotool -h, the output is shown below.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228323 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-1.png\" alt=\"help output from hellotool\" width=\"946\" height=\"434\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-1.png 946w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-1-300x138.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-1-768x352.png 768w\" sizes=\"(max-width: 946px) 100vw, 946px\" \/><\/p>\n<p>With the default help output, the names of the parameters are shown as the description. This is helpful, but not ideal. Let\u2019s improve it. To do that all we need to do is to add some \/\/\/ comments to the main method, with the descriptions. The updated code is shown in the following code block.<\/p>\n<pre class=\"lang:c# decode:true\">using System;\r\n\r\nnamespace HelloTool {\r\n\u00a0\u00a0\u00a0 class Program {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/\/ &lt;summary&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/\/ A simple global tool with parameters.\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/\/ &lt;\/summary&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/\/ &lt;param name=\"name\"&gt;Your name (required)&lt;\/param&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/\/ &lt;param name=\"age\"&gt;Your age&lt;\/param&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 static void Main(string name = \"World\", int age = 0) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 string message = age &lt;= 0 ? $\"Hello there {name}!\" : $\"Hello there {name}, who is {age} years old\";\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Console.WriteLine(message);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n}<\/pre>\n<p>All we have done is add some descriptive comments to the Main method for each parameter. DragonFruit will take care of wiring up for us. Now let\u2019s go through the flow of pack, install, uninstall and test one more time. After going through that when you invoke hellotool -h the output should be as shown below. If you are still seeing the old output, ensure you&#8217;ve used the Pack command for the project before install.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228324 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-2.png\" alt=\"hellotool help output\" width=\"944\" height=\"548\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-2.png 944w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-2-300x174.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/hellotool-help-output-2-768x446.png 768w\" sizes=\"(max-width: 944px) 100vw, 944px\" \/><\/p>\n<p>Now we can see that the help output contains some descriptive text. This is looking much better now! Let\u2019s invoke the tool and pass in some parameters. Let\u2019s invoke hellotool &#8211;name dotnet-bot &#8211;age 5 and examine the output.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-228325 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-hellotool-output-2.png\" alt=\"hellotool output\" width=\"786\" height=\"148\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-hellotool-output-2.png 786w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-hellotool-output-2-300x56.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/02\/vsmac-hellotool-output-2-768x145.png 768w\" sizes=\"(max-width: 786px) 100vw, 786px\" \/><\/p>\n<p>It looks like the tool is behaving as expected. From here you can continue developing your command line tool and then publish it to NuGet.org, or another NuGet repository, to share it with others. Since we have already configured the NuGet properties in the project we can upload the .nupkg that is created after invoking the Pack menu option. After you have published the NuGet package, users can install it with the following command.<\/p>\n<pre class=\"lang:batch decode:true\">dotnet tool install --global &lt;packagename&gt;<\/pre>\n<p>This will download the package from the NuGet repository and then install the tool globally for that user. The uninstall command that users will use is the same as what you\u2019ve been using during development. When you make changes to your tool and republish to NuGet.org, remember to change the version number in the .csproj file. Each package published to a NuGet repository needs to have a unique version for that package.<\/p>\n<h2>Summary &amp; Wrap Up<\/h2>\n<p>In this post we covered a lot of material on how to create a .NET Core global tool. If you\u2019d like to learn more about creating global tools, take a look at the additional resources section below. If you have any questions or feedback, please leave a comment on this post.<\/p>\n<h2>Additional Resources<\/h2>\n<p>Join us for our upcoming <a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/announcing-visual-studio-for-mac-refresh-event-on-february-24\/\">Visual Studio for Mac: Refresh() event on February 24<\/a> for deep dive sessions into .NET development using Visual Studio for Mac, including a full session on developing Blazor applications.<\/p>\n<p>Make sure to follow us on Twitter at <a href=\"https:\/\/twitter.com\/VisualStudioMac\">@VisualStudioMac<\/a> and reach out to the team. Customer feedback is important to us and we would love to hear your thoughts. Alternatively, you can head over to <a href=\"https:\/\/aka.ms\/feedback\/vsm-home\">Visual Studio Developer Community<\/a> to track your issues, <a href=\"https:\/\/aka.ms\/vsmac-suggestion\">suggest a feature<\/a>, ask questions, and find answers from others. We use your feedback to continue to improve Visual Studio 2019 for Mac, so thank you again on behalf of our entire team.<\/p>\n<p>Documentation links<\/p>\n<ul>\n<li><a href=\"https:\/\/visualstudio.microsoft.com\/vs\/mac\/\">Download Visual Studio for Mac<\/a><\/li>\n<li>Code from this post is available at <a href=\"https:\/\/github.com\/sayedihashimi\/global-tool-sample\">https:\/\/github.com\/sayedihashimi\/global-tool-sample<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/tools\/global-tools\">.NET Core global tools overview<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/tools\/global-tools-how-to-create\">Create a global tool<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>One of the really cool aspects about .NET Core is the support for global tools. You can use global tools to simplify common tasks during your development workflow. For example, you can create tools to minify image assets, simplify working with source control, or perform any other task that you can automate with the command [&hellip;]<\/p>\n","protected":false},"author":357,"featured_media":228324,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[155],"tags":[3743,452],"class_list":["post-228304","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-visual-studio","tag-visual-studio-2019-for-mac","tag-visual-studio-for-mac"],"acf":[],"blog_post_summary":"<p>One of the really cool aspects about .NET Core is the support for global tools. You can use global tools to simplify common tasks during your development workflow. For example, you can create tools to minify image assets, simplify working with source control, or perform any other task that you can automate with the command [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/228304","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/users\/357"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/comments?post=228304"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/228304\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media\/228324"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media?parent=228304"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/categories?post=228304"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/tags?post=228304"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}