MSBuild: Cómo hacer una Custom Build Task

Mi problema: Al compilar, quería ver el valor de una key de un archivo .config (o sea, con la estructura de los app.config que genera el Visual Studio). En base a ese valor, decidir si copiar o no ciertas dlls.

Mi solución: Ante todo, recordemos que todo proyecto C#, VB.NET o C++ del Visual Studio es un script de MSBuild, o sea muy parecido a un .build de NAnt. Está compuesto, entre otras cosas, de Targets, que a su vez consisten en invocaciones de Tasks. En .NET, hay varias Tasks predefinidas, pero si uno quiere escribir una propia, el framework provee un mecanismo para hacerlo. Eso es lo que veremos.

Una custom task debe ser definida en una dll, la cual debe ser referenciada desde un script de MSBuild (recordemos que todo archivo de proyecto C#, VB.NET o C++ es un script de MSBuild).

Entonces, el primer paso es crear un proyecto de C#, de tipo Class Library. En dicho proyecto, debemos crear una clase que herede de Task y que tenga sus parámetros obligatorios y opcionales, y su output como properties marcadas con un atributo especial de .NET. Luego, debemos hacer un override del método Execute, y es ahí donde debemos implementar lo que hace nuestra tarea.

Poniendo como ejemplo mi caso, hice una tarea llamada ConfigRead, que recibe como parámetros el path del archivo de configuración y el nombre de la Key que se desea leer, y devuelve el valor de esa Key en el archivo dado. Se invocaría de esta forma: (ADVERTENCIA: MSBuild es case sensitive. El syntax highlighter que uso me pone todos los identificadores XML en minúscula, pero no van así. Las mayúsculas y minúsculas deben coincidir con los nombres definidos en la clase)

<ConfigRead ConfigFilePath="$(TargetDir)DRA.config" KeyName="DRA.AcquisitionPanel">
      <Output TaskParameter="KeyValue" PropertyName="ReadKeyValue" />
</ConfigRead>
<Message Text="Value Read from DRA.config: $(ReadKeyValue)" />
Y la implementación sería la siguiente:
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Configuration;
using System.Diagnostics;
namespace ConfigRead{

    public class ConfigRead : Task{

        [Required]
        public string ConfigFilePath{ get; set; }

        [Required]
        public string KeyName{ get; set; }

        [Output]
        public string KeyValue{ get; set; }

        public override bool Execute(){
            Debug.WriteLine("ConfigFilePath: {0}", ConfigFilePath);
            ExeConfigurationFileMap FileMap = new ExeConfigurationFileMap();
            FileMap.ExeConfigFilename = ConfigFilePath;
            Debug.WriteLine("Key sought: {0}", KeyName);
            Configuration ConfigurationFile =
               ConfigurationManager.OpenMappedExeConfiguration(
                   FileMap, ConfigurationUserLevel.None
               );
            KeyValue = ConfigurationFile.AppSettings.Settings[KeyName].Value;
            Debug.WriteLine("Value read: {0}", KeyValue);
            return true;
        }
    }
}

Por último, falta aclarar que en el script MSBuild donde querramos usar la Task, debemos referenciar la dll compilada, que deberá estar en algún lugar del File System. Ello se hace de esta forma:


<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Needed for using the ConfigRead Custom Task -->
  <UsingTask TaskName="ConfigRead" AssemblyFile="$(SolutionDir)\DRALibraries\CustomBuildTasks\ConfigRead.dll" />
  <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
  <!-- Cosas predefinidas por el Visual... -->
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.-->  
  <Target Name="AfterBuild">
    <ConfigRead ConfigFilePath="$(TargetDir)DRA.config" KeyName="DRA.AcquisitionPanel">
      <Output TaskParameter="KeyValue" PropertyName="ReadKeyValue" />
    </ConfigRead>
    <Message Text="Value Read from DRA.config: $(ReadKeyValue)" />
  </Target>  
</Project>

Comments

Popular posts from this blog

VB.NET: Raise base class events from a derived class

Apache Kafka - I - High level architecture and concepts

Upgrading Lodash from 3.x to 4.x