Compartir a través de


Tareas en línea de MSBuild

Normalmente, las tareas de MSBuild se crean mediante la compilación de una clase que implementa la ITask interfaz . Para obtener más información, vea Tasks.

Si desea evitar la sobrecarga de crear una tarea compilada, puede crear una tarea insertada en el archivo del proyecto o en un archivo importado. No es necesario crear un ensamblado independiente para hospedar la tarea. El uso de una tarea insertada facilita el seguimiento del código fuente y facilita la implementación de la tarea. El código fuente se integra en el archivo de proyecto de MSBuild o en el archivo importado, normalmente un .targets archivo.

Cree una tarea insertada mediante un generador de tareas de código. Para el desarrollo actual, asegúrese de usar RoslynCodeTaskFactory, no CodeTaskFactory. CodeTaskFactory solo admite versiones de C# de hasta 4.0.

Las tareas insertadas están pensadas como una comodidad para tareas pequeñas que no requieren dependencias complicadas. La compatibilidad de depuración con tareas insertadas es limitada. Se recomienda crear una tarea compilada en lugar de una tarea insertada cuando quiera escribir código más complejo, hacer referencia a un paquete NuGet, ejecutar herramientas externas o realizar operaciones que podrían producir condiciones de error. Además, las tareas insertadas se compilan cada vez que se compilan, por lo que puede haber un impacto notable en el rendimiento de la compilación.

Estructura de una tarea insertada

Una tarea insertada está contenida en un elemento UsingTask . La tarea insertada y el UsingTask elemento que lo contiene normalmente se incluyen en un .targets archivo y se importan en otros archivos de proyecto según sea necesario. Esta es una tarea básica insertada que no hace nada, pero ilustra la sintaxis:

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

El UsingTask elemento del ejemplo tiene tres atributos que describen la tarea y el generador de tareas insertados que lo compila.

  • El TaskName atributo asigna un nombre a la tarea, en este caso, DoNothing.

  • El TaskFactory atributo asigna un nombre a la clase que implementa el generador de tareas insertado.

  • El AssemblyFile atributo proporciona la ubicación del generador de tareas insertado. Como alternativa, puede usar el AssemblyName atributo para especificar el nombre completo de la clase de generador de tareas insertada, que normalmente se encuentra en $(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll.

Los elementos restantes de la DoNothing tarea están vacíos y se proporcionan para ilustrar el orden y la estructura de una tarea insertada. En este artículo se presenta un ejemplo completo.

  • El elemento ParameterGroup es opcional. Cuando se especifica, declara los parámetros de la tarea. Para obtener más información sobre los parámetros de entrada y salida, consulte Parámetros de entrada y salida más adelante en este artículo.

  • El Task elemento describe y contiene el código fuente de la tarea.

  • El Reference elemento especifica referencias a los ensamblados de .NET que está usando en el código. El uso de este elemento es equivalente a agregar una referencia a un proyecto en Visual Studio. El Include atributo especifica la ruta de acceso del ensamblado al que se hace referencia. Los ensamblados de mscorlib, .NET Standard, Microsoft.Build.Framework y Microsoft.Build.Utilities.Core, así como algunos ensamblados a los que se hace referencia transitivamente como dependencias, están disponibles sin Reference.

  • El Using elemento enumera los espacios de nombres a los que desea acceder. Este elemento es equivalente a la using directiva en C#. El Namespace atributo especifica el espacio de nombres que se va a incluir. No funciona para colocar una using directiva en el código insertado, porque ese código se coloca en un cuerpo del método, donde using no se permiten las directivas.

Reference y Using los elementos son independientes del lenguaje. Las tareas insertadas se pueden escribir en Visual Basic o C#.

Nota:

Los elementos contenidos por el Task elemento son específicos del generador de tareas, en este caso, el generador de tareas de código.

Elemento Code

El último elemento secundario que se va a aparecer dentro del Task elemento es el Code elemento . El Code elemento contiene o busca el código que desea compilar en una tarea. Lo que se coloca en el Code elemento depende de cómo quiera escribir la tarea.

El Language atributo especifica el idioma en el que se escribe el código. Los valores aceptables son cs para C#, vb para Visual Basic.

El Type atributo especifica el tipo de código que se encuentra en el Code elemento .

  • Si el valor de Type es Class, el Code elemento contiene código para una clase que deriva de la ITask interfaz .

  • Si el valor de Type es Method, el código define una invalidación del Execute método de la ITask interfaz.

  • Si el valor de Type es Fragment, el código define el contenido del Execute método, pero no la firma ni la return instrucción .

El propio código suele aparecer entre un <![CDATA[ marcador y un ]]> marcador. Dado que el código está en una sección de CDATA, no tiene que preocuparse por el escape de caracteres reservados, por ejemplo, "<" o ">".

Como alternativa, puede usar el Source atributo del Code elemento para especificar la ubicación de un archivo que contiene el código de la tarea. El código del archivo de origen debe ser del tipo especificado por el Type atributo . Si el Source atributo está presente, el valor predeterminado de Type es Class. Si Source no está presente, el valor predeterminado es Fragment.

Nota:

Al definir la clase de tarea en el archivo de origen, el nombre de clase debe estar de acuerdo con el TaskName atributo del elemento UsingTask correspondiente.

HelloWorld

Este es un ejemplo de una tarea insertada sencilla. La tarea HelloWorld muestra "Hello, world!" en el dispositivo de registro de errores predeterminado, que suele ser la consola del sistema o la ventana Salida de 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>

Puede guardar la tarea HelloWorld en un archivo denominado HelloWorld.targets y, a continuación, invocarlo desde un proyecto como se indica a continuación.

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

Parámetros de entrada y salida

Los parámetros de tarea insertados son elementos secundarios de un ParameterGroup elemento . Cada parámetro toma el nombre del elemento que lo define. El código siguiente define el parámetro Text.

<ParameterGroup>
  <Text />
</ParameterGroup>

Los parámetros pueden tener uno o varios de estos atributos:

  • Required es un atributo opcional que es false de forma predeterminada. Si truees , el parámetro es necesario y debe tener un valor antes de llamar a la tarea.
  • ParameterType es un atributo opcional que es System.String de forma predeterminada. Puede establecerse en cualquier tipo completo que sea un elemento o un valor que se pueda convertir a y desde una cadena mediante ChangeType. (Es decir, cualquier tipo que se pueda pasar a una tarea externa y desde ellas).
  • Output es un atributo opcional que es false de forma predeterminada. Si truees , el parámetro debe tener un valor antes de devolver desde el método Execute.

Por ejemplo

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

define estos tres parámetros:

  • Expression es un parámetro de entrada necesario de tipo System.String.

  • Files es un parámetro de entrada de lista de elementos obligatorio.

  • Tally es un parámetro de salida de tipo System.Int32.

Si el Code elemento tiene el Type atributo de Fragment o Method, las propiedades se crean automáticamente para cada parámetro. De lo contrario, las propiedades deben declararse explícitamente en el código fuente de la tarea y deben coincidir exactamente con sus definiciones de parámetros.

Depuración de una tarea insertada

MSBuild genera un archivo de origen en la tarea insertada y escribe la salida en el archivo de texto con un nombre de archivo GUID en la carpeta de archivos temporales, AppData\Local\Temp\MSBuildTemp. La salida se elimina normalmente, pero para conservar este archivo de salida, puede establecer la variable MSBUILDLOGCODETASKFACTORYOUTPUT de entorno en 1.

Ejemplo 1

La siguiente tarea insertada reemplaza cada aparición de un token en el archivo especificado por el valor especificado.

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

Ejemplo 2

La siguiente tarea insertada genera una salida serializada. En este ejemplo se muestra el uso de un parámetro de salida y una referencia.

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