Erweiterbarkeitsmodell für .NET Core-CLI-Tools.NET Core CLI tools extensibility model

Dieses Dokument behandelt die unterschiedlichen Möglichkeiten, wie Sie die .NET Core-CLI-Tools erweitern und die Szenarios erklären können, die jedes steuert.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. Sie sehen, wie Sie die Tools verarbeiten und wie unterschiedliche Typen von Tools erstellt werden können.You'll see how to consume the tools as well as how to build the different types of tools.

So können Sie CLI-Tools erweiternHow to extend CLI tools

Die CLI-Tools können auf drei Arten erweitert werden:The CLI tools can be extended in three main ways:

  1. Projektweise über NuGet-PaketeVia NuGet packages on a per-project basis

Die projektbezogenen Tools sind im Kontext des Projekts enthalten, aber sie ermöglichen eine einfache Installation über die Wiederherstellung.Per-project tools are contained within the project's context, but they allow easy installation through restoration.

  1. Über NuGet-Pakete mit benutzerdefinierten ZielenVia NuGet packages with custom targets

Mithilfe benutzerdefinierter Ziele können Sie den Buildprozess einfach mit benutzerdefinierten Aufgaben erweitern.Custom targets allow you to easily extend the build process with custom tasks.

  1. Über die Systemvariable PATHVia the system's PATH

Auf PATH basierende Tools eignen sich für allgemeine projektübergreifende Tools, die auf einem einzelnen Computer verwendet werden.PATH-based tools are good for general, cross-project tools that are usable on a single machine.

Die drei oben beschriebenen Erweiterbarkeitsmechanismen schließen sich nicht gegenseitig aus.The three extensibility mechanisms outlined above are not exclusive. Sie können beide oder nur einen verwenden.You can use one, or all, or a combination of them. Welchen Sie auswählen, hängt größtenteils davon ab, welches Ziel Sie mit der Erweiterung erreichen möchten.Which one to pick depends largely on the goal you are trying to achieve with your extension.

Projektbezogene ErweiterbarkeitPer-project based extensibility

Projektbezogene Tools sind frameworkabhängige Bereitstellungen, die als NuGet-Pakete verteilt werden.Per-project tools are framework-dependent deployments that are distributed as NuGet packages. Tools sind nur im Kontext des Projekts verfügbar, das auf sie verweist und für das sie gespeichert sind.Tools are only available in the context of the project that references them and for which they are restored. Ein Aufruf außerhalb des Projektkontexts (z.B. außerhalb des Verzeichnisses, das das Projekt enthält) schlägt fehl, da der Befehl nicht gefunden werden kann.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.

Diese Tools sind ideal für Buildserver, da nichts außer der Projektdatei erforderlich ist.These tools are perfect for build servers, since nothing outside of the project file is needed. Der Buildprozess führt die Wiederherstellung für das Projekt, das es erstellt, aus, und die Tools stehen zur Verfügung.The build process runs restore for the project it builds and tools will be available. Sprachprojekte, wie F#, befinden sich auch in dieser Kategorie, da jedes Projekt nur in einer bestimmten Sprache geschrieben werden kann.Language projects, such as F#, are also in this category since each project can only be written in one specific language.

Schließlich bietet dieses Erweiterbarkeitsmodell Unterstützung für die Erstellung von Tools, die Zugriff auf die Buildausgabe des Projekts benötigen.Finally, this extensibility model provides support for creation of tools that need access to the built output of the project. Verschiedene Razor-Ansicht-Tools in ASP.NET-MVC-Anwendungen fallen z.B. in diese Kategorie.For instance, various Razor view tools in ASP.NET MVC applications fall into this category.

Tools pro Projekt verwendenConsuming per-project tools

Zum Nutzen dieser Tools müssen Sie das Element <DotNetCliToolReference> für jedes Tool hinzufügen, das Sie der Projektdatei verwenden möchten.Consuming these tools requires you to add a <DotNetCliToolReference> element to your project file for each tool you want to use. Im Element <DotNetCliToolReference> verweisen Sie auf das Paket, in dem sich das Tool befindet, und geben die Version an, die Sie benötigen.Inside the <DotNetCliToolReference> element, you reference the package in which the tool resides and specify the version you need. Nach der Ausführung von dotnet restore werden das Tool und die zugehörigen Abhängigkeiten wiederhergestellt.After running dotnet restore, the tool and its dependencies are restored.

Hinweis

Starting with .NET Core 2.0, you don't have to run dotnet restore because it's run implicitly by all commands, such as dotnet build and dotnet run, that require a restore to occur. It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as continuous integration builds in Visual Studio Team Services or in build systems that need to explicitly control the time at which the restore occurs.

Für Tools, die die Buildausgabe des Projekts zur Ausführung laden müssen, gibt es normalerweise eine andere Abhängigkeit, die unter den regulären Abhängigkeiten in der Projektdatei angezeigt wird.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. Da die CLI als Buildmodul MSBuild verwendet, wird empfohlen, diese Teile des Tools als benutzerdefinierte MSBuild-Ziele und -Aufgaben zu schreiben, da sie auf diese Weise am gesamten Buildprozess teilnehmen können.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. Außerdem können sie mühelos sämtliche Daten abrufen, die über den Build erstellt werden, z.B. den Speicherort der Ausgabedateien, die aktuell erstellte Konfiguration usw. Alle diese Informationen bilden eine Gruppe von MSBuild-Eigenschaften, die von jedem beliebigen Ziel gelesen werden können.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. Weiter unten in diesem Dokument erfahren Sie, wie über NuGet ein benutzerdefiniertes Ziel hinzugefügt wird.You can see how to add a custom target using NuGet later in this document.

Betrachten wir ein Beispiel, bei dem ein Tools-Only-Tool zu einem einfachen Projekt hinzugefügt wird.Let's review an example of adding a simple tools-only tool to a simple project. Für einen Beispielbefehl namens dotnet-api-search, mit dem Sie die NuGet-Pakete nach der angegebenen API durchsuchen können, sehen Sie hier die Projektdatei einer Konsolenanwendung, die dieses Tool verwendet: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>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

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

Das Element <DotNetCliToolReference> weist eine ähnliche Struktur auf wie das Element <PackageReference>.The <DotNetCliToolReference> element is structured in a similar way as the <PackageReference> element. Er benötigt die Paket-ID des Pakets mit dem Tool und dessen Version, damit ein Wiederherstellungsvorgang ausgeführt werden kann.It needs the package ID of the package containing the tool and its version to be able to restore.

Erstellen von ToolsBuilding tools

Wie bereits erwähnt, sind Tools nur portable Konsolenanwendungen.As mentioned, tools are just portable console applications. Sie würden ein Tool erstellen, so wie Sie alle Konsolenanwendungen erstellen würden.You build tools as you would build any other console application. Nachdem Sie es erstellt haben, führen Sie den dotnet pack-Befehl aus, um ein NuGet-Paket (NUPKG-Datei) zu erstellen, das Ihren Code, die Informationen zu seinen Abhängigkeiten usw. enthält.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. Die können den Paketnamen benennen, aber die Anwendung darin, das tatsächliche Binär-Tool, muss der dotnet-<command>-Konvention entsprechen, damit dotnet es aufrufen kann.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.

Hinweis

In Vor-RC3-Versionen der .NET Core-Befehlszeilentools hat der Befehl dotnet pack einen Fehler, der dazu führt, dass die Datei runtime.config.json nicht mit dem Tool gepackt wird.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. Fehlt diese Datei, treten zur Laufzeit Fehler auf.Lacking that file results in errors at runtime. Wenn dieses Verhalten auftritt, sollten Sie auf die neuesten Tools aktualisieren und erneut versuchen, den Befehl dotnet pack auszuführen.If you encounter this behavior, be sure to update to the latest tooling and try the dotnet pack again.

Da Tools portable Anwendungen sind, muss der Benutzer des Tools über die Version der .NET Core-Bibliotheken verfügen, für die das Tool entwickelt wurde, um das Tool auszuführen.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. Jede andere Abhängigkeit, die das Tool verwendet und nicht in den .NET Core-Bibliotheken enthalten ist, wird wiederhergestellt und im NuGet-Cache gespeichert.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. Das gesamte Tool wird daher mithilfe der Assemblys aus den .NET Core-Bibliotheken sowie Assemblys aus dem NuGet-Cache ausgeführt.The entire tool is, therefore, run using the assemblies from the .NET Core libraries as well as assemblies from the NuGet cache.

Diese Art von Tools haben ein Abhängigkeitsdiagramm, das komplett unabhängig ist vom Abhängigkeitsdiagramm des Projekts, welches sie verwendet.These kinds of tools have a dependency graph that is completely separate from the dependency graph of the project that uses them. Der Wiederherstellungsvorgang wird zuerst die Projektabhängigkeiten wiederherstellen und dann jedes der Tools und deren Abhängigkeiten.The restore process first restores the project's dependencies and then restores each of the tools and their dependencies.

Umfangreichere Beispiele und verschiedene Kombinationen dessen finden Sie im .NET Core-CLI-Repository.You can find richer examples and different combinations of this in the .NET Core CLI repo. Sie finden auch die Implementierung von verwendeten Tools im gleichen Repository.You can also see the implementation of tools used in the same repo.

Benutzerdefinierte ZieleCustom targets

NuGet kann benutzerdefinierte MSBuild-Ziele und PROPS-Dateien packen.NuGet has the capability to package custom MSBuild targets and props files. Mit dem Schritt der .NET Core-CLI-Tools zum Verwenden von MSBuild gilt nun derselbe Mechanismus für Erweiterbarkeit für .NET Core-Projekte.With the move of the .NET Core CLI tools to use MSBuild, the same mechanism of extensibility now applies to .NET Core projects. Sie nutzen diese Art von Erweiterbarkeit, wenn Sie den Buildprozess erweitern möchten, im Buildprozess auf Artefakte wie generierte Dateien zugreifen oder die Konfiguration überprüfen möchten, in der der Build aufgerufen wird usw.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.

Im folgenden Beispiel können Sie die Projektdatei des Ziels mithilfe der csproj-Syntax anzeigen.In the following example, you can see the target's project file using the csproj syntax. Dies zeigt dem dotnet pack-Befehl, was genau gepackt werden soll, wobei die Zieldateien sowie die Assemblys in den Ordner Erstellen im Paket platziert werden.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. Beachten Sie das <ItemGroup>-Element, das über die Eigenschaft Label verfügt, die auf dotnet pack instructions festgelegt ist, sowie das Ziel, das darunter definiert ist.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>

Die Nutzung benutzerdefinierter Ziele erfolgt durch Bereitstellung einer <PackageReference>, die auf ein Paket und dessen Version innerhalb des Projekts verweist, das erweitert wird.Consuming custom targets is done by providing a <PackageReference> that points to the package and its version inside the project that is being extended. Im Gegensatz zu den Tools wird das benutzerdefinierte Zielpaket in die Hülle der Abhängigkeiten des nutzenden Projekts eingeschlossen.Unlike the tools, the custom targets package does get included into the consuming project's dependency closure.

Die Verwendung des benutzerdefinierten Ziels hängt ausschließlich von seiner Konfiguration ab.Using the custom target depends solely on how you configure it. Da dies ein MSBuild-Ziel ist, kann es von einem angegebenen Ziel abhängen, nach einem anderen Ziel ausgeführt werden und mit dem Befehl dotnet msbuild /t:<target-name> auch manuell aufgerufen werden.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.

Wenn Sie jedoch Ihren Benutzern eine bessere Erfahrung bieten möchten, können Sie projektbezogene Tools und benutzerdefinierte Ziele kombinieren.However, if you want to provide a better user experience to your users, you can combine per-project tools and custom targets. In diesem Szenario akzeptiert das projektbezogene Tool lediglich die benötigten Parameter, die in den erforderlichen Aufruf von dotnet msbuild übersetzt werden, über den das Ziel ausgeführt wird.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. Sie sehen ein Beispiel dieser Art von Synergie im Repository mit den MVP Summit 2016 Hackathon-Beispielen im Projekt 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.

PFAD-basierte ErweiterbarkeitPATH-based extensibility

Die PFAD-basierte Erweiterbarkeit wird in der Regel für Entwicklungscomputer verwendet, bei denen Sie ein Tool brauchen, das konzeptionell mehr als ein einzelnes Projekt abdeckt.PATH-based extensibility is usually used for development machines where you need a tool that conceptually covers more than a single project. Der größte Nachteil dieses Extensionsmechanismus ist, dass es mit dem Computer verknüpft ist, auf dem das Tool existiert.The main drawback of this extension mechanism is that it's tied to the machine where the tool exists. Wenn Sie ihn auf einem anderen Computer benötigen, müssten Sie ihn bereitstellen.If you need it on another machine, you would have to deploy it.

Dieses Muster der Erweiterbarkeit des CLI-Toolsets ist sehr einfach.This pattern of CLI toolset extensibility is very simple. Wie in der Übersicht über die .NET Core-CLI beschrieben, kann der dotnet-Treiber jeden Befehl ausführen, der nach der dotnet-<command>-Konvention benannt ist.As covered in the .NET Core CLI overview, dotnet driver can run any command that is named after the dotnet-<command> convention. Die Standardauflösungslogik wird zuerst mehrere Speicherorte überprüfen und schließlich an den SYSTEMPFAD fallen.The default resolution logic first probes several locations and finally falls back to the system PATH. Wenn der angeforderte Befehl im SYSTEMPFAD vorhanden und eine Binärdatei ist, die aufgerufen werden kann, wird sie der dotnet-Treiber aufrufen.If the requested command exists in the system PATH and is a binary that can be invoked, dotnet driver will invoke it.

Bei der Datei muss es sich um eine ausführbare Datei handeln.The file must be executable. Auf Unix-Systemen bedeutet dies alles, das das ausführbare Bit über chmod +x festgelegt hat.On Unix systems, this means anything that has the execute bit set via chmod +x. Unter Windows können Sie CMD-Dateien verwenden.On Windows, you can use cmd files.

Sehen wir uns nun die einfache Implementierung eines „Hello World“-Tools an.Let's take a look at the very simple implementation of a "Hello World" tool. Wir verwenden jeweils bash und cmd unter Windows.We will use both bash and cmd on Windows. Der folgende Befehl liefert einfach „Hello World“ an die Konsole.The following command will simply echo "Hello World" to the console.

#!/bin/bash

echo "Hello World!"
echo "Hello World"

Auf Mac OS können wir dieses Skript als dotnet-hello speichern und sein ausführbares Bit mit chmod +x dotnet-hello festlegen.On macOS, we can save this script as dotnet-hello and set its executable bit with chmod +x dotnet-hello. Wir erstellen dann eine symbolische Verknüpfung in /usr/local/bin mit dem Befehl 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/. So kann der Befehl mit der dotnet hello-Syntax aufgerufen werden.This will make it possible to invoke the command using the dotnet hello syntax.

Wir können unter Windows das Skript als dotnet-hello.cmd speichern und es an einem Speicherort speichern, der sich in einem Systempfad befindet (oder Sie können es einem Ordner hinzufügen, der sich bereits im Pfad befindet).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). Verwenden Sie danach einfach dotnet hello, um dieses Beispiel auszuführen.After this, you can just use dotnet hello to run this example.