Modèle d’extensibilité des outils CLI .NET Core.NET Core CLI tools extensibility model

Ce document décrit les différentes méthodes permettant d’étendre les outils de l’interface de ligne de commande (CLI) .NET Core et explique les scénarios dans lesquels elles sont utilisées.This document covers the different ways you can extend the .NET Core Command-line Interface (CLI) tools and explain the scenarios that drive each one of them. Vous verrez comment utiliser les outils et comment créer les différents types d’outils.You'll see how to consume the tools as well as how to build the different types of tools.

Extension des outils CLIHow to extend CLI tools

Vous pouvez étendre les outils CLI de trois façons :The CLI tools can be extended in three main ways:

  1. Par le biais des packages NuGet, par projetVia NuGet packages on a per-project basis

    Les outils par projet sont contenus dans le contexte du projet, mais ils permettent une installation rapide grâce à une restauration.Per-project tools are contained within the project's context, but they allow easy installation through restoration.

  2. Par le biais des packages NuGet avec des cibles personnaliséesVia NuGet packages with custom targets

    Les cibles personnalisées vous permettent d’étendre facilement le processus de génération avec des tâches personnalisées.Custom targets allow you to easily extend the build process with custom tasks.

  3. Par le biais du chemin (PATH) du systèmeVia the system's PATH

    Les outils basés sur le chemin sont efficaces pour les outils généraux multiprojets qui sont utilisables sur un seul ordinateur.PATH-based tools are good for general, cross-project tools that are usable on a single machine.

Les trois mécanismes d’extensibilité présentés ci-dessus ne sont pas exclusifs.The three extensibility mechanisms outlined above are not exclusive. Vous pouvez utiliser un seul, une partie ou la totalité d’entre eux.You can use one, or all, or a combination of them. Le choix de la méthode dépend en grande partie de l’objectif de votre extension.Which one to pick depends largely on the goal you are trying to achieve with your extension.

Extensibilité par projetPer-project based extensibility

Les outils par projet sont des déploiements dépendants du framework qui sont distribués dans les packages NuGet.Per-project tools are framework-dependent deployments that are distributed as NuGet packages. Les outils sont uniquement disponibles dans le contexte du projet qui les référence et pour lequel ils sont restaurés.Tools are only available in the context of the project that references them and for which they are restored. Les appels en dehors du contexte du projet (par exemple, en dehors du répertoire qui contient le projet) échouent parce que la commande est introuvable.Invocation outside of the context of the project (for example, outside of the directory that contains the project) will fail because the command cannot be found.

Ces outils sont parfaits pour les serveurs de build, puisque rien en dehors du fichier projet n’est nécessaire.These tools are perfect for build servers, since nothing outside of the project file is needed. Le processus de génération exécute la restauration pour le projet qu’il génère, et des outils seront disponibles.The build process runs restore for the project it builds and tools will be available. Les projets de langage, tels que F#, figurent également dans cette catégorie puisque chaque projet ne peut être écrit que dans un langage.Language projects, such as F#, are also in this category since each project can only be written in one specific language.

Enfin, ce modèle d’extensibilité prend en charge la création d’outils qui ont besoin d’accéder à la sortie générée du projet.Finally, this extensibility model provides support for creation of tools that need access to the built output of the project. Par exemple, les outils d’affichage Razor des applications MVC ASP.NET appartiennent à cette catégorie.For instance, various Razor view tools in ASP.NET MVC applications fall into this category.

Utilisation des outils par projetConsuming per-project tools

Vous devez ajouter un élément <DotNetCliToolReference> à votre fichier projet pour chaque outil que vous souhaitez utiliser.Consuming these tools requires you to add a <DotNetCliToolReference> element to your project file for each tool you want to use. À l’intérieur de l’élément <DotNetCliToolReference>, vous référencez le package dans lequel réside l’outil et spécifiez la version dont vous avez besoin.Inside the <DotNetCliToolReference> element, you reference the package in which the tool resides and specify the version you need. Après l’exécution de dotnet restore, l’outil et ses dépendances sont restaurés.After running dotnet restore, the tool and its dependencies are restored.

Notes

À partir du kit SDK .NET Core 2.0, vous n’avez pas à exécuter dotnet restore, car il est exécuté implicitement par toutes les commandes qui réclament une restauration, comme dotnet new, dotnet build et dotnet run.Starting with .NET Core 2.0 SDK, you don't have to run dotnet restore because it's run implicitly by all commands that require a restore to occur, such as dotnet new, dotnet build and dotnet run. Cette commande reste néanmoins valide dans certains scénarios où une restauration explicite est nécessaire, comme des builds d’intégration continue dans Azure DevOps Services ou dans les systèmes de génération qui doivent contrôler explicitement le moment auquel la restauration se produit.It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as continuous integration builds in Azure DevOps Services or in build systems that need to explicitly control the time at which the restore occurs.

Pour les outils qui doivent charger la sortie de génération du projet pour l’exécution, il existe généralement une autre dépendance qui est répertoriée sous les dépendances régulières du fichier projet.For tools that need to load the build output of the project for execution, there is usually another dependency which is listed under the regular dependencies in the project file. Étant donné que l’interface CLI utilise MSBuild comme moteur de génération, nous vous recommandons d’écrire ces parties de l’outil sous forme de cibles et tâches MSBuild personnalisées puisqu’elles peuvent ensuite prendre part à l’ensemble du processus de génération.Since CLI uses MSBuild as its build engine, we recommend that these parts of the tool be written as custom MSBuild targets and tasks, since they can then take part in the overall build process. Elles peuvent aussi facilement récupérer tout ou partie des données produites par la génération, notamment l’emplacement des fichiers de sortie ou la configuration en cours de génération. Toutes ces informations deviennent un jeu de propriétés MSBuild qui peut être lu à partir de toutes les cibles.Also, they can get any and all data easily that is produced via the build, such as the location of the output files, the current configuration being built, etc. All this information becomes a set of MSBuild properties that can be read from any target. Vous verrez comment ajouter une cible personnalisée à l’aide de NuGet plus loin dans ce document.You can see how to add a custom target using NuGet later in this document.

Voyons un exemple d’ajout d’un outil simple de type « tools-only » à un projet simple.Let's review an example of adding a simple tools-only tool to a simple project. Prenons un exemple de commande appelé dotnet-api-search qui vous permette de parcourir les packages NuGet à la recherche de l’API spécifiée. Voici le fichier projet de l’application console qui utilise cet outil :Given an example command called dotnet-api-search that allows you to search through the NuGet packages for the specified API, here is a console application's project file that uses that tool:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <!-- The tools reference -->
  <ItemGroup>
    <DotNetCliToolReference Include="dotnet-api-search" Version="1.0.0" />
  </ItemGroup>
</Project>

L’élément <DotNetCliToolReference> est structuré de la même façon que l’élément <PackageReference>.The <DotNetCliToolReference> element is structured in a similar way as the <PackageReference> element. Pour procéder à la restauration, il a besoin de l’ID de package du package qui contient l’outil et sa version.It needs the package ID of the package containing the tool and its version to be able to restore.

Outils de créationBuilding tools

Comme nous l’avons mentionné précédemment, les outils sont des applications console portables.As mentioned, tools are just portable console applications. Vous les créez comme toute autre application console.You build tools as you would build any other console application. Une fois l’outil créé, utilisez la commande dotnet pack pour créer un package NuGet (fichier .nupkg) devant contenir votre code, les informations sur ses dépendances, etc.After you build it, you use the dotnet pack command to create a NuGet package (.nupkg file) that contains your code, information about its dependencies, and so on. Vous pouvez donner n’importe quel nom au package. Toutefois, l’application qui s’y trouve, le fichier binaire de l’outil, doit respecter la convention de dotnet-<command> pour pouvoir être appelée par dotnet.You can give any name to the package, but the application inside, the actual tool binary, has to conform to the convention of dotnet-<command> in order for dotnet to be able to invoke it.

Notes

Dans les versions antérieures à RC3 des outils en ligne de commande .NET Core, la commande dotnet pack présentait un bogue qui empêchait la compression de runtime.config.json avec l’outil.In pre-RC3 versions of the .NET Core command-line tools, the dotnet pack command had a bug that caused the runtime.config.json to not be packed with the tool. L’absence de ce fichier aboutit à des erreurs lors de l’exécution.Lacking that file results in errors at runtime. Si vous rencontrez ce comportement, veillez à utiliser les outils les plus récents et essayez dotnet pack une nouvelle fois.If you encounter this behavior, be sure to update to the latest tooling and try the dotnet pack again.

Étant donné que les outils sont des applications portables, l’utilisateur qui utilise un outil doit disposer de la version des bibliothèques .NET Core à l’aide desquelles l’outil a été développé, afin de pouvoir l’exécuter.Since tools are portable applications, the user consuming the tool must have the version of the .NET Core libraries that the tool was built against in order to run the tool. Toute autre dépendance que l’outil utilise et qui ne figure pas dans les bibliothèques .NET Core est restaurée et placée dans le cache de NuGet.Any other dependency that the tool uses and that is not contained within the .NET Core libraries is restored and placed in the NuGet cache. L’outil entier est, par conséquent, exécuté à l’aide des assemblys des bibliothèques .NET Core et du cache de NuGet.The entire tool is, therefore, run using the assemblies from the .NET Core libraries as well as assemblies from the NuGet cache.

Ces types d’outils ont un graphique de dépendance qui est complètement distinct du graphique de dépendance du projet qui les utilise.These kinds of tools have a dependency graph that is completely separate from the dependency graph of the project that uses them. Le processus de restauration restaure d’abord les dépendances du projet, puis restaure chacun des outils et leurs dépendances.The restore process first restores the project's dependencies and then restores each of the tools and their dependencies.

Vous trouverez des exemples plus complets dans le dépôt sur les outils CLI .NET Core.You can find richer examples and different combinations of this in the .NET Core CLI repo. Vous pouvez également voir l’implémentation des outils utilisés dans le même dépôt.You can also see the implementation of tools used in the same repo.

Cibles personnaliséesCustom targets

NuGet peut empaqueter des fichiers de cibles et de propriétés MSBuild personnalisées.NuGet has the capability to package custom MSBuild targets and props files. Suite à l’adoption de MSBuild dans les outils CLI .NET Core, le même mécanisme d’extensibilité s’applique désormais aux projets .NET Core.With the move of the .NET Core CLI tools to use MSBuild, the same mechanism of extensibility now applies to .NET Core projects. Vous utilisez ce type d’extensibilité quand vous souhaitez étendre le processus de génération, accéder à tous les artefacts dans le processus de génération, tels que les fichiers générés, inspecter la configuration sous laquelle la build est appelée, etc.You would use this type of extensibility when you want to extend the build process, or when you want to access any of the artifacts in the build process, such as generated files, or you want to inspect the configuration under which the build is invoked, etc.

Dans l’exemple suivant, vous pouvez voir le fichier projet de la cible à l’aide de la syntaxe csproj.In the following example, you can see the target's project file using the csproj syntax. Celle-ci indique à la commande dotnet pack les éléments à empaqueter. Les fichiers de cibles et les assemblys sont placés dans le dossier build à l’intérieur du package.This instructs the dotnet pack command what to package, placing the targets files as well as the assemblies into the build folder inside the package. Notez l’élément <ItemGroup> dont la propriété Label a la valeur dotnet pack instructions, et la cible définie en dessous.Notice the <ItemGroup> element that has the Label property set to dotnet pack instructions, and the Target defined beneath it.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <Description>Sample Packer</Description>
    <VersionPrefix>0.1.0-preview</VersionPrefix>
    <TargetFramework>netstandard1.3</TargetFramework>
    <DebugType>portable</DebugType>
    <AssemblyName>SampleTargets.PackerTarget</AssemblyName>
  </PropertyGroup>
  <ItemGroup>
    <EmbeddedResource Include="Resources\Pkg\dist-template.xml;compiler\resources\**\*" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
    <None Include="build\SampleTargets.PackerTarget.targets" />
  </ItemGroup>
  <ItemGroup Label="dotnet pack instructions">
    <Content Include="build\*.targets">
      <Pack>true</Pack>
      <PackagePath>build\</PackagePath>
    </Content>
  </ItemGroup>
  <Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles">
    <!-- Collect these items inside a target that runs after build but before packaging. -->
    <ItemGroup>
      <Content Include="$(OutputPath)\*.dll;$(OutputPath)\*.json">
        <Pack>true</Pack>
        <PackagePath>build\</PackagePath>
      </Content>
    </ItemGroup>
  </Target>
  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="1.0.1-beta-000933"/>
    <PackageReference Include="Microsoft.Build.Framework" Version="0.1.0-preview-00028-160627" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="0.1.0-preview-00028-160627" />
    <PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
  </ItemGroup>
  <ItemGroup />
  <PropertyGroup Label="Globals">
    <ProjectGuid>463c66f0-921d-4d34-8bde-7c9d0bffaf7b</ProjectGuid>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
    <DefineConstants>$(DefineConstants);NETSTANDARD1_3</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
    <DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
  </PropertyGroup>
</Project>

L’utilisation de cibles personnalisées s’effectue en fournissant un <PackageReference> qui pointe vers le package et sa version dans le projet en cours d’extension.Consuming custom targets is done by providing a <PackageReference> that points to the package and its version inside the project that is being extended. Contrairement aux outils, le package des cibles personnalisées n’est pas inclus dans la fermeture de dépendance du projet de consommation.Unlike the tools, the custom targets package does get included into the consuming project's dependency closure.

L’utilisation de la cible personnalisée dépend uniquement de la façon dont vous la configurez.Using the custom target depends solely on how you configure it. Puisqu’il s’agit d’une cible MSBuild, elle peut dépendre d’une cible donnée, exécutée après une autre cible, et peut également être appelée manuellement à l’aide de la commande dotnet msbuild -t:<target-name>.Since it's an MSBuild target, it can depend on a given target, run after another target and can also be manually invoked using the dotnet msbuild -t:<target-name> command.

Toutefois, si vous souhaitez procurer une meilleure expérience à vos utilisateurs, vous pouvez combiner des outils par projet et des cibles personnalisées.However, if you want to provide a better user experience to your users, you can combine per-project tools and custom targets. Dans ce scénario, l’outil par projet se contenterait essentiellement d’accepter tous les paramètres nécessaires à partir desquels il générerait l’invocation dotnet msbuild nécessaire pour exécuter la cible.In this scenario, the per-project tool would essentially just accept whatever needed parameters and would translate that into the required dotnet msbuild invocation that would execute the target. Vous pouvez voir un exemple de ce type de synergie sur le dépôt des exemples du MVP Summit 2016 Hackathon du projet dotnet-packer.You can see a sample of this kind of synergy on the MVP Summit 2016 Hackathon samples repo in the dotnet-packer project.

Extensibilité basée sur le chemin (PATH)PATH-based extensibility

L’extensibilité basée sur le chemin est généralement utilisée pour les ordinateurs de développement qui nécessitent un outil qui traite conceptuellement plusieurs projets.PATH-based extensibility is usually used for development machines where you need a tool that conceptually covers more than a single project. Le principal inconvénient de ce mécanisme d’extension est qu’il est limité à l’ordinateur sur lequel est installé l’outil.The main drawback of this extension mechanism is that it's tied to the machine where the tool exists. Si vous avez besoin de l’installer sur un autre ordinateur, vous devrez le déployer.If you need it on another machine, you would have to deploy it.

Ce modèle d’extensibilité des outils CLI est très simple.This pattern of CLI toolset extensibility is very simple. Comme indiqué dans la présentation des outils CLI .NET Core, le pilote dotnet peut exécuter toutes les commandes dont le nom respecte la convention dotnet-<command>.As covered in the .NET Core CLI overview, dotnet driver can run any command that is named after the dotnet-<command> convention. La logique de résolution par défaut sonde d’abord plusieurs emplacements avant de revenir au chemin (PATH) système.The default resolution logic first probes several locations and finally falls back to the system PATH. Si la commande demandée existe dans le chemin système et s’il s’agit d’un fichier binaire qui peut être appelé, le pilote dotnet l’appellera.If the requested command exists in the system PATH and is a binary that can be invoked, dotnet driver will invoke it.

Le fichier doit être exécutable.The file must be executable. Sur les systèmes Unix, il peut s’agir de tous les fichiers dont le bit d’exécution est défini via chmod +x.On Unix systems, this means anything that has the execute bit set via chmod +x. Sur Windows, vous pouvez utiliser des fichiers cmd.On Windows, you can use cmd files.

Examinons l’implémentation simple d’un outil « Hello World ».Let's take a look at the very simple implementation of a "Hello World" tool. Nous allons utiliser bash et cmd sur Windows.We will use both bash and cmd on Windows. La commande suivante répète simplement « Hello World » dans la console.The following command will simply echo "Hello World" to the console.

#!/bin/bash

echo "Hello World!"
echo "Hello World"

Sur Mac OS, nous pouvons enregistrer ce script en tant que dotnet-hello et définir son bit exécutable avec chmod +x dotnet-hello.On macOS, we can save this script as dotnet-hello and set its executable bit with chmod +x dotnet-hello. Nous pouvons ensuite créer un lien symbolique vers le script dans /usr/local/bin à l’aide de la commande ln -s <full_path>/dotnet-hello /usr/local/bin/.We can then create a symbolic link to it in /usr/local/bin using the command ln -s <full_path>/dotnet-hello /usr/local/bin/. Cela permet d’appeler la commande à l’aide de la syntaxe dotnet hello.This will make it possible to invoke the command using the dotnet hello syntax.

Sur Windows, nous pouvons enregistrer ce script en tant que dotnet-hello.cmd et le placer dans un emplacement figurant dans un chemin système (ou vous pouvez l’ajouter à un dossier qui existe déjà dans le chemin).On Windows, we can save this script as dotnet-hello.cmd and put it in a location that is in a system path (or you can add it to a folder that is already in the path). Après cela, vous pouvez simplement utiliser dotnet hello pour exécuter cet exemple.After this, you can just use dotnet hello to run this example.