Tworzenie zadania wbudowanego programu MSBuild za pomocą polecenia RoslynCodeTaskFactory
Podobnie jak w przypadku codeTaskFactory, roslynCodeTaskFactory używa kompilatorów roslyn międzyplatformowych do generowania zestawów zadań w pamięci do użycia jako zadań wbudowanych. Zadania RoslynCodeTaskFactory są przeznaczone dla platformy .NET Standard i mogą działać w środowiskach uruchomieniowych .NET Framework i .NET Core, a także na innych platformach, takich jak Linux i macOS.
Uwaga
Element RoslynCodeTaskFactory jest dostępny tylko w programie MSBuild 15.8 lub nowszym. Wersje programu MSBuild są zgodne z wersjami programu Visual Studio, więc program RoslynCodeTaskFactory jest dostępny w programie Visual Studio 2017 w wersji 15.8 lub nowszej.
Struktura zadania wbudowanego z elementem RoslynCodeTaskFactory
Zadania wbudowane RoslynCodeTaskFactory są deklarowane w taki sam sposób jak CodeTaskFactory, a jedyną różnicą jest to, że są one przeznaczone dla platformy .NET Standard. Wbudowane zadanie i UsingTask
element, który go zawiera, są zwykle uwzględniane w pliku .targets i importowane do innych plików projektu zgodnie z potrzebami. Oto podstawowe zadanie wbudowane. Zwróć uwagę, że nic nie robi.
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This simple inline task does nothing. -->
<UsingTask
TaskName="DoNothing"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
<ParameterGroup />
<Task>
<Reference Include="" />
<Using Namespace="" />
<Code Type="Fragment" Language="cs">
</Code>
</Task>
</UsingTask>
</Project>
Element UsingTask
w przykładzie ma trzy atrybuty opisujące zadanie i wbudowaną fabrykę zadań, która ją kompiluje.
Atrybut
TaskName
nazywa zadanie , w tym przypadkuDoNothing
.Atrybuty
TaskFactory
nazwiją klasę, która implementuje fabrykę zadań wbudowanych.Atrybut
AssemblyFile
udostępnia lokalizację wbudowanej fabryki zadań. Alternatywnie można użyć atrybutuAssemblyName
, aby określić w pełni kwalifikowaną nazwę wbudowanej klasy fabryki zadań, która jest zwykle zlokalizowana w globalnej pamięci podręcznej zestawów (GAC).
Pozostałe elementy DoNothing
zadania są puste i są udostępniane w celu zilustrowania kolejności i struktury zadania wbudowanego. Bardziej niezawodny przykład zostanie przedstawiony w dalszej części tego tematu.
Element
ParameterGroup
jest opcjonalny. Gdy zostanie określony, deklaruje parametry zadania. Aby uzyskać więcej informacji na temat parametrów wejściowych i wyjściowych, zobacz Parametry wejściowe i wyjściowe w dalszej części tego tematu.Element
Task
opisuje i zawiera kod źródłowy zadania.Element
Reference
określa odwołania do zestawów .NET używanych w kodzie. Jest to odpowiednik dodawania odwołania do projektu w programie Visual Studio. AtrybutInclude
określa ścieżkę przywołytowanego zestawu.Element
Using
zawiera listę przestrzeni nazw, do których chcesz uzyskać dostęp. Przypomina to instrukcjęUsing
w języku Visual C#. AtrybutNamespace
określa przestrzeń nazw do uwzględnienia.
Reference
i Using
elementy są niezależne od języka. Zadania wbudowane można napisać w jednym z obsługiwanych języków .NET CodeDom, na przykład Visual Basic lub Visual C#.
Uwaga
Elementy zawarte przez Task
element są specyficzne dla fabryki zadań, w tym przypadku fabryki zadań kodu.
Element kodu
Ostatnim elementem podrzędnym Task
, który ma pojawić się w elemecie , jest Code
element . Element Code
zawiera lub lokalizuje kod, który chcesz skompilować w zadaniu. To, co umieścisz w elemenie Code
, zależy od tego, jak chcesz napisać zadanie.
Atrybut Language
określa język, w którym jest napisany kod. Dopuszczalne wartości to cs
C#, vb
dla języka Visual Basic.
Atrybut Type
określa typ kodu, który znajduje się w elemecie Code
.
Jeśli wartość
Type
toClass
,Code
element zawiera kod klasy pochodzącej z interfejsu ITask .Jeśli wartość
Type
toMethod
, kod definiuje przesłonięćExecute
metodę interfejsu ITask .Jeśli wartość
Type
toFragment
, kod definiuje zawartośćExecute
metody, ale nie podpis lub instrukcjęreturn
.
Sam kod zwykle pojawia się między znacznikiem <![CDATA[
a znacznikiem ]]>
. Ponieważ kod znajduje się w sekcji CDATA, nie musisz martwić się o ucieczkę zastrzeżonych znaków, na przykład "<" lub ">".
Alternatywnie możesz użyć Source
atrybutu Code
elementu, aby określić lokalizację pliku zawierającego kod zadania. Kod w pliku źródłowym musi być typu określony przez Type
atrybut . Source
Jeśli atrybut jest obecny, wartość domyślna to Type
Class
. Jeśli Source
nie ma wartości, wartość domyślna to Fragment
.
Uwaga
Podczas definiowania klasy zadań w pliku źródłowym nazwa klasy musi być zgodna z atrybutem TaskName
odpowiedniego elementu UsingTask .
Witaj, świecie
Oto bardziej niezawodne zadanie wbudowane w rozwiązaniu RoslynCodeTaskFactory. Zadanie HelloWorld wyświetla komunikat "Hello, world!" na domyślnym urządzeniu rejestrowania błędów, które jest zazwyczaj konsolą systemową lub oknem danych wyjściowych programu Visual Studio. Element Reference
w przykładzie jest dołączany tylko do ilustracji.
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This simple inline task displays "Hello, world!" -->
<UsingTask
TaskName="HelloWorld"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
<ParameterGroup />
<Task>
<Reference Include="System.Xml"/>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
// Display "Hello, world!"
Log.LogError("Hello, world!");
]]>
</Code>
</Task>
</UsingTask>
</Project>
Zadanie HelloWorld można zapisać w pliku o nazwie HelloWorld.targets, a następnie wywołać je z projektu w następujący sposób.
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="HelloWorld.targets" />
<Target Name="Hello">
<HelloWorld />
</Target>
</Project>
Parametry wejściowe i wyjściowe
Wbudowane parametry zadania to elementy podrzędne ParameterGroup
elementu. Każdy parametr przyjmuje nazwę elementu, który go definiuje. Poniższy kod definiuje parametr Text
.
<ParameterGroup>
<Text />
</ParameterGroup>
Parametry mogą mieć co najmniej jeden z następujących atrybutów:
Required
jest opcjonalnym atrybutem, który jestfalse
domyślnie. Jeślitrue
parametr jest wymagany i musi mieć wartość przed wywołaniem zadania.ParameterType
jest opcjonalnym atrybutem, który jestSystem.String
domyślnie. Można go ustawić na dowolny w pełni kwalifikowany typ, który jest elementem lub wartością, którą można przekonwertować na i z ciągu przy użyciu elementu System.Convert.ChangeType. (Innymi słowy, dowolny typ, który można przekazać do i z zadania zewnętrznego).Output
jest opcjonalnym atrybutem, który jestfalse
domyślnie. Jeślitrue
parametr , należy podać wartość przed zwróceniem z metody Execute.
Przykład:
<ParameterGroup>
<Expression Required="true" />
<Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<Tally ParameterType="System.Int32" Output="true" />
</ParameterGroup>
definiuje następujące trzy parametry:
Expression
jest wymaganym parametrem wejściowym typu System.String.Files
jest wymaganym parametrem wejściowym listy elementów.Tally
jest parametrem wyjściowym typu System.Int32.
Code
Jeśli element ma Type
atrybut Fragment
lub Method
, właściwości są tworzone automatycznie dla każdego parametru. W pliku RoslynCodeTaskFactory, jeśli Code
element ma Type
atrybut Class
, nie trzeba określać ParameterGroup
elementu , ponieważ jest on wnioskowany z kodu źródłowego (jest to różnica od CodeTaskFactory
). W przeciwnym razie właściwości muszą być jawnie zadeklarowane w kodzie źródłowym zadania i muszą dokładnie odpowiadać ich definicjom parametrów.
Przykład
Następujące wbudowane zadanie rejestruje niektóre komunikaty i zwraca ciąg.
<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="15.0">
<UsingTask TaskName="MySample"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<Parameter1 ParameterType="System.String" Required="true" />
<Parameter2 ParameterType="System.String" />
<Parameter3 ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Log.LogMessage(MessageImportance.High, "Hello from an inline task created by Roslyn!");
Log.LogMessageFromText($"Parameter1: '{Parameter1}'", MessageImportance.High);
Log.LogMessageFromText($"Parameter2: '{Parameter2}'", MessageImportance.High);
Parameter3 = "A value from the Roslyn CodeTaskFactory";
]]>
</Code>
</Task>
</UsingTask>
<Target Name="Demo">
<MySample Parameter1="A value for parameter 1" Parameter2="A value for parameter 2">
<Output TaskParameter="Parameter3" PropertyName="NewProperty" />
</MySample>
<Message Text="NewProperty: '$(NewProperty)'" />
</Target>
</Project>
Te wbudowane zadania mogą łączyć ścieżki i pobierać nazwę pliku.
<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="15.0">
<UsingTask TaskName="PathCombine"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<Paths ParameterType="System.String[]" Required="true" />
<Combined ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Combined = Path.Combine(Paths);
]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="PathGetFileName"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<Path ParameterType="System.String" Required="true" />
<FileName ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
FileName = System.IO.Path.GetFileName(Path);
]]>
</Code>
</Task>
</UsingTask>
<Target Name="Demo">
<PathCombine Paths="$(Temp);MyFolder;$([System.Guid]::NewGuid()).txt">
<Output TaskParameter="Combined" PropertyName="MyCombinedPaths" />
</PathCombine>
<Message Text="Combined Paths: '$(MyCombinedPaths)'" />
<PathGetFileName Path="$(MyCombinedPaths)">
<Output TaskParameter="FileName" PropertyName="MyFileName" />
</PathGetFileName>
<Message Text="File name: '$(MyFileName)'" />
</Target>
</Project>
Zapewnianie zgodności z poprzednimi wersjami
RoslynCodeTaskFactory
po raz pierwszy stał się dostępny w programie MSBuild w wersji 15.8. Załóżmy, że masz sytuację, w której chcesz obsługiwać poprzednie wersje programów Visual Studio i MSBuild, gdy RoslynCodeTaskFactory
były niedostępne, ale CodeTaskFactory
były, ale chcesz użyć tego samego skryptu kompilacji. Możesz użyć konstrukcji korzystającej Choose
$(MSBuildVersion)
z właściwości , aby zdecydować w czasie kompilacji, czy użyć RoslynCodeTaskFactory
obiektu lub powrócić do CodeTaskFactory
elementu , jak w poniższym przykładzie:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<Choose>
<When Condition=" '$(MSBuildVersion.Substring(0,2))' >= 16 Or
('$(MSBuildVersion.Substring(0,2))' == 15 And '$(MSBuildVersion.Substring(3,1))' >= 8)">
<PropertyGroup>
<TaskFactory>RoslynCodeTaskFactory</TaskFactory>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<TaskFactory>CodeTaskFactory</TaskFactory>
</PropertyGroup>
</Otherwise>
</Choose>
<UsingTask
TaskName="HelloWorld"
TaskFactory="$(TaskFactory)"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup />
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
Log.LogError("Using RoslynCodeTaskFactory");
]]>
</Code>
</Task>
</UsingTask>
<Target Name="RunTask" AfterTargets="Build">
<Message Text="MSBuildVersion: $(MSBuildVersion)"/>
<Message Text="TaskFactory: $(TaskFactory)"/>
<HelloWorld />
</Target>
</Project>