Dela via


Inbyggda uppgifter för MSBuild

MSBuild-uppgifter skapas vanligtvis genom att kompilera en klass som implementerar ITask gränssnittet. Mer information finns i Uppgifter.

När du vill undvika kostnaderna för att skapa en kompilerad uppgift kan du skapa en infogad aktivitet i projektfilen eller i en importerad fil. Du behöver inte skapa en separat sammansättning som värd för uppgiften. Med hjälp av en infogad uppgift blir det enklare att hålla reda på källkoden och enklare att distribuera uppgiften. Källkoden är integrerad i MSBuild-projektfilen eller den importerade filen, vanligtvis en .targets fil.

Du skapar en infogad aktivitet med hjälp av en koduppgiftsfabrik. För aktuell utveckling bör du använda RoslynCodeTaskFactory, inte CodeTaskFactory. CodeTaskFactory stöder endast C#-versioner upp till 4.0.

Infogade uppgifter är avsedda som en bekvämlighet för små uppgifter som inte kräver komplicerade beroenden. Felsökningsstöd för infogade uppgifter är begränsat. Vi rekommenderar att du skapar en kompilerad uppgift i stället för infogad aktivitet när du vill skriva mer komplex kod, referera till ett NuGet-paket, köra externa verktyg eller utföra åtgärder som kan skapa felvillkor. Dessutom kompileras infogade uppgifter varje gång du skapar, så det kan ha en märkbar inverkan på byggprestanda.

Strukturen för en infogad uppgift

En infogad aktivitet finns i ett UsingTask-element . Den infogade aktiviteten och elementet UsingTask som innehåller den ingår vanligtvis i en .targets fil och importeras till andra projektfiler efter behov. Här är en grundläggande infogad uppgift som inte gör något, men som illustrerar syntaxen:

 <!-- 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>

Elementet UsingTask i exemplet har tre attribut som beskriver uppgiften och den infogade aktivitetsfabrik som kompilerar den.

  • Attributet TaskName namnger uppgiften, i det här fallet DoNothing.

  • Attributet TaskFactory namnger klassen som implementerar den infogade aktivitetsfabriken.

  • Attributet AssemblyFile ger platsen för den infogade aktivitetsfabriken. Du kan också använda AssemblyName attributet för att ange det fullständigt kvalificerade namnet på den infogade aktivitetsfabriksklassen, som vanligtvis finns i $(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll.

De återstående elementen i DoNothing aktiviteten är tomma och tillhandahålls för att illustrera ordningen och strukturen för en infogad aktivitet. Ett fullständigt exempel visas senare i den här artikeln.

  • Elementet ParameterGroup är valfritt. När den anges deklareras parametrarna för aktiviteten. Mer information om indata- och utdataparametrar finns i Indata- och utdataparametrar senare i den här artikeln.

  • Elementet Task beskriver och innehåller aktivitetens källkod.

  • Elementet Reference anger referenser till de .NET-sammansättningar som du använder i koden. Att använda det här elementet motsvarar att lägga till en referens till ett projekt i Visual Studio. Attributet Include anger sökvägen till den refererade sammansättningen. Sammansättningar i mscorlib, .NET Standard, Microsoft.Build.Framework och Microsoft.Build.Utilities.Core, samt vissa sammansättningar som transitivt refereras till som beroenden, är tillgängliga utan Reference.

  • Elementet Using visar de namnområden som du vill komma åt. Det här elementet motsvarar using direktivet i C#. Attributet Namespace anger det namnområde som ska inkluderas. Det fungerar inte att placera ett using direktiv i den infogade koden, eftersom den koden placeras i en metodtext, där using direktiv inte tillåts.

Reference och Using element är språkagnostiska. Infogade uppgifter kan skrivas i Visual Basic eller C#.

Anmärkning

Element som ingår i elementet Task är specifika för aktivitetsfabriken, i det här fallet koduppgiftsfabriken.

Kodelement

Det sista underordnade elementet som visas i elementet Task är elementet Code . Elementet Code innehåller eller letar upp den kod som du vill kompilera till en uppgift. Vad du lägger till i elementet Code beror på hur du vill skriva uppgiften.

Attributet Language anger det språk där koden skrivs. Acceptabla värden är cs för C#, vb för Visual Basic.

Attributet Type anger vilken typ av kod som finns i elementet Code .

  • Om värdet Type för är Classinnehåller elementet Code kod för en klass som härleds från ITask gränssnittet.

  • Om värdet Type för är Methoddefinierar koden en åsidosättning av Execute -metoden för ITask gränssnittet.

  • Om värdet Type för är Fragmentdefinierar koden innehållet i Execute metoden, men inte signaturen eller -instruktionen return .

Själva koden visas vanligtvis mellan en <![CDATA[ markör och en ]]> markör. Eftersom koden finns i ett CDATA-avsnitt behöver du inte bekymra dig om att undvika reserverade tecken, till exempel "<" eller ">".

Du kan också använda Source -attributet för -elementet Code för att ange platsen för en fil som innehåller koden för din uppgift. Koden i källfilen måste vara av den typ som anges av attributet Type . Om attributet Source finns är Classstandardvärdet Type för . Om Source inte finns är Fragmentstandardvärdet .

Anmärkning

När du definierar aktivitetsklassen i källfilen måste klassnamnet överensstämma med TaskName attributet för motsvarande UsingTask-element .

HelloWorld

Här är ett exempel på en enkel infogad uppgift. HelloWorld-aktiviteten visar "Hello, world!" på standardfelloggningsenheten, som vanligtvis är systemkonsolen eller Visual Studio-utdatafönstret .

<Project>
  <!-- This simple inline task displays "Hello, world!" -->
  <UsingTask
    TaskName="HelloWorld"
    TaskFactory="RoslynCodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup />
    <Task>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Code Type="Fragment" Language="cs">
<![CDATA[
// Display "Hello, world!"
Log.LogError("Hello, world!");
]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

Du kan spara HelloWorld-aktiviteten i en fil med namnet HelloWorld.targets och sedan anropa den från ett projekt på följande sätt.

<Project>
  <Import Project="HelloWorld.targets" />
  <Target Name="Hello">
    <HelloWorld />
  </Target>
</Project>

Indata- och utdataparametrar

Infogade aktivitetsparametrar är underordnade element i ett ParameterGroup element. Varje parameter tar namnet på det element som definierar det. Följande kod definierar parametern Text.

<ParameterGroup>
  <Text />
</ParameterGroup>

Parametrar kan ha ett eller flera av följande attribut:

  • Required är ett valfritt attribut som är false som standard. Om truekrävs parametern och måste anges ett värde innan aktiviteten anropas.
  • ParameterType är ett valfritt attribut som är System.String som standard. Den kan anges till valfri fullständigt kvalificerad typ som antingen är ett objekt eller ett värde som kan konverteras till och från en sträng med hjälp ChangeTypeav . (Med andra ord alla typer som kan skickas till och från en extern uppgift.)
  • Output är ett valfritt attribut som är false som standard. Om truemåste parametern ges ett värde innan den returneras från metoden Kör.

Ett exempel:

<ParameterGroup>
  <Expression Required="true" />
  <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
  <Tally ParameterType="System.Int32" Output="true" />
</ParameterGroup>

definierar dessa tre parametrar:

  • Expression är en obligatorisk indataparameter av typen System.String.

  • Files är en obligatorisk indataparameter för objektlistan.

  • Tally är en utdataparameter av typen System.Int32.

Om elementet Code har Type attributet Fragment eller Methodskapas egenskaperna automatiskt för varje parameter. Annars måste egenskaperna uttryckligen deklareras i uppgiftskällans kod och måste exakt matcha deras parameterdefinitioner.

Felsöka en infogad uppgift

MSBuild genererar en källfil den infogade uppgiften och skriver utdata till textfilen med ett GUID-filnamn i mappen temporära filer, AppData\Local\Temp\MSBuildTemp. Utdata tas normalt bort, men för att bevara den här utdatafilen kan du ange miljövariabeln MSBUILDLOGCODETASKFACTORYOUTPUT till 1.

Exempel 1

Följande infogade uppgift ersätter varje förekomst av en token i den angivna filen med det angivna värdet.

<Project>

  <UsingTask TaskName="TokenReplace" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <ParameterGroup>
      <Path ParameterType="System.String" Required="true" />
      <Token ParameterType="System.String" Required="true" />
      <Replacement ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs"><![CDATA[
string content = File.ReadAllText(Path);
content = content.Replace(Token, Replacement);
File.WriteAllText(Path, content);

]]></Code>
    </Task>
  </UsingTask>

  <Target Name='Demo' >
    <TokenReplace Path="Target.config" Token="$MyToken$" Replacement="MyValue"/>
  </Target>
</Project>

Exempel 2

Följande infogade uppgift genererar serialiserade utdata. Det här exemplet visar användningen av en utdataparameter och en referens.

<Project>
  <PropertyGroup>
    <RoslynCodeTaskFactoryAssembly Condition="$(RoslynCodeTaskFactoryAssembly) == ''">$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll</RoslynCodeTaskFactoryAssembly>
  </PropertyGroup>

    <UsingTask 
    TaskName="MyInlineTask" 
    TaskFactory="RoslynCodeTaskFactory" 
    AssemblyFile="$(RoslynCodeTaskFactoryAssembly)">
    <ParameterGroup>
      <Input ParameterType="System.String" Required="true" />
      <Output ParameterType="System.String" Output="true" />
    </ParameterGroup>
    <Task>
      <Reference Include="System.Text.Json" /> <!-- Reference an assembly -->
      <Using Namespace="System.Text.Json" />   <!-- Use a namespace -->
      <Code Type="Fragment" Language="cs">
        <![CDATA[
          Output = JsonSerializer.Serialize(new { Message = Input });
        ]]>
      </Code>
    </Task>
  </UsingTask>

  <Target Name="RunInlineTask">
    <MyInlineTask Input="Hello, Roslyn!" >
      <Output TaskParameter="Output" PropertyName="SerializedOutput" />
    </MyInlineTask>
    <Message Text="Serialized Output: $(SerializedOutput)" />
  </Target>
</Project>