Condividi tramite


Attività in linea di MSBuild

Le attività MSBuild vengono in genere create compilando una classe che implementa l'interfaccia ITask . Per altre informazioni, vedere Tasks.

Quando si desidera evitare il sovraccarico della creazione di un'attività compilata, è possibile creare un'attività inline nel file di progetto o in un file importato. Non è necessario creare un assembly separato per ospitare l'attività. L'uso di un'attività inline rende più semplice tenere traccia del codice sorgente e semplificare la distribuzione dell'attività. Il codice sorgente è integrato nel file di progetto MSBuild o nel file importato, in genere un .targets file.

Si crea un'attività inline usando una factory di attività di codice. Per lo sviluppo corrente, assicurarsi di usare RoslynCodeTaskFactory, non CodeTaskFactory. CodeTaskFactory supporta solo le versioni C# fino alla 4.0.

Le attività inline sono pensate per offrire praticità per compiti piccoli che non richiedono dipendenze complesse. Il supporto del debug per le attività inline è limitato. È consigliabile creare un'attività compilata anziché un'attività inline quando si vuole scrivere codice più complesso, fare riferimento a un pacchetto NuGet, eseguire strumenti esterni o eseguire operazioni che potrebbero produrre condizioni di errore. Inoltre, le attività inline vengono compilate ogni volta che si compila, in modo da avere un impatto notevole sulle prestazioni di compilazione.

Struttura di un'attività in linea

Un'attività inline è contenuta in un elemento UsingTask . L'attività inline e l'elemento UsingTask che lo contiene vengono in genere inclusi in un .targets file e importati in altri file di progetto in base alle esigenze. Ecco un'attività inline di base che non esegue alcuna operazione, ma illustra la sintassi:

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

L'elemento UsingTask nell'esempio ha tre attributi che descrivono l'attività e la factory di attività inline che la compila.

  • L'attributo TaskName assegna un nome all'attività, in questo caso . DoNothing

  • L'attributo TaskFactory assegna un nome alla classe che implementa la task factory inline.

  • L'attributo AssemblyFile fornisce la posizione della factory di attività inline. In alternativa, è possibile utilizzare l'attributo AssemblyName per specificare il nome completo della classe task factory inline, che si trova in genere in $(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll.

Gli elementi rimanenti dell'attività DoNothing sono vuoti e vengono forniti per illustrare l'ordine e la struttura di un'attività inline. Un esempio completo viene presentato più avanti in questo articolo.

  • L'elemento ParameterGroup è facoltativo. Se specificato, dichiara i parametri per l'attività. Per altre informazioni sui parametri di input e output, vedere Parametri di input e output più avanti in questo articolo.

  • L'elemento Task descrive e contiene il codice sorgente dell'attività.

  • L'elemento Reference specifica i riferimenti agli assembly .NET usati nel codice. L'uso di questo elemento equivale all'aggiunta di un riferimento a un progetto in Visual Studio. L'attributo Include specifica il percorso dell'assembly a cui si fa riferimento. Gli assembly in mscorlib, .NET Standard, Microsoft.Build.Framework e Microsoft.Build.Utilities.Core, nonché alcuni assembly a cui viene fatto riferimento transitivamente come dipendenze, sono disponibili senza .Reference

  • L'elemento Using elenca gli spazi dei nomi a cui si vuole accedere. Questo elemento equivale alla using direttiva in C#. L'attributo Namespace specifica lo spazio dei nomi da includere. Non è possibile inserire una direttiva using nel codice inline, perché tale codice viene inserito in un corpo del metodo, dove le direttive using non sono consentite.

Reference e Using gli elementi sono indipendenti dalla lingua. Le attività inline possono essere scritte in Visual Basic o C#.

Annotazioni

Gli elementi contenuti nell'elemento Task sono specifici della factory delle attività, in questo caso la factory dell'attività di codice.

Elemento di codice

L'ultimo elemento figlio da visualizzare all'interno dell'elemento Task è l'elemento Code. L'elemento Code contiene o individua il codice che si desidera compilare in un'attività. Ciò che si inserisce nell'elemento Code dipende dalla modalità di scrittura dell'attività.

L'attributo Language specifica il linguaggio in cui viene scritto il codice. I valori accettabili sono cs per C#, vb per Visual Basic.

L'attributo Type specifica il tipo di codice trovato nell'elemento Code .

  • Se il valore di Type è Class, l'elemento Code contiene il codice per una classe che deriva dall'interfaccia ITask .

  • Se il valore di Type è Method, il codice definisce una sovrascrittura del metodo Execute dell'interfaccia ITask.

  • Se il valore di Type è Fragment, il codice definisce il contenuto del Execute metodo, ma non la firma o l'istruzione return .

Il codice stesso viene in genere visualizzato tra un <![CDATA[ marcatore e un ]]> marcatore. Poiché il codice si trova in una sezione CDATA, non è necessario preoccuparsi dell'escape dei caratteri riservati, ad esempio "<" o ">".

In alternativa, è possibile usare l'attributo Source dell'elemento Code per specificare il percorso di un file che contiene il codice per l'attività. Il codice nel file di origine deve essere del tipo specificato dall'attributo Type . Se l'attributo Source è presente, il valore predefinito di Type è Class. Se Source non è presente, il valore predefinito è Fragment.

Annotazioni

Quando si definisce la classe task nel file di origine, il nome della classe deve accettare l'attributo dell'elemento TaskNameUsingTask corrispondente.

HelloWorld

Ecco un esempio di una semplice attività inlinea. L'attività HelloWorld visualizza "Hello, world!" nel dispositivo di registrazione degli errori predefinito, che in genere è la console di sistema o la finestra Output di Visual Studio.

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

È possibile salvare l'attività HelloWorld in un file denominato HelloWorld.targets e richiamarlo da un progetto come indicato di seguito.

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

Parametri di input e output

I parametri dell'attività inline sono elementi figlio dell'elemento ParameterGroup. Ogni parametro accetta il nome dell'elemento che lo definisce. Il codice seguente definisce il parametro Text.

<ParameterGroup>
  <Text />
</ParameterGroup>

I parametri possono avere uno o più di questi attributi:

  • Required è un attributo facoltativo che è false per impostazione predefinita. Se true, il parametro è obbligatorio e deve essere assegnato un valore prima di chiamare l'attività.
  • ParameterType è un attributo facoltativo che è System.String per impostazione predefinita. Può essere impostato su qualsiasi tipo completo che sia un elemento o un valore che può essere convertito in e da una stringa usando ChangeType. In altre parole, qualsiasi tipo che può essere passato a e da un'attività esterna.
  • Output è un attributo facoltativo che è false per impostazione predefinita. Se true, al parametro deve essere assegnato un valore prima di restituire dal metodo Execute.

Ad esempio:

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

definisce questi tre parametri:

  • Expression è un parametro di input obbligatorio di tipo System.String.

  • Files è un parametro di input dell'elenco di elementi obbligatorio.

  • Tally è un parametro di output di tipo System.Int32.

Se l'elemento ha l'attributo CodeType di Fragment o Method, le proprietà vengono create automaticamente per ogni parametro. In caso contrario, le proprietà devono essere dichiarate in modo esplicito nel codice sorgente dell'attività e devono corrispondere esattamente alle definizioni dei parametri.

Eseguire il debug di un'attività inline

MSBuild genera un file sorgente per l'attività inline e scrive l'output in un file di testo con un nome GUID nella cartella dei file temporanei AppData\Local\Temp\MSBuildTemp. L'output viene in genere eliminato, ma per mantenere questo file di output, è possibile impostare la variabile MSBUILDLOGCODETASKFACTORYOUTPUT di ambiente su 1.

Esempio 1

L'attività inline seguente sostituisce ogni occorrenza di un token nel file specificato con il valore specificato.

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

Esempio 2

L'attività inline seguente genera l'output serializzato. In questo esempio viene illustrato l'uso di un parametro di output e di un riferimento.

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