Descripción del proceso de compilación

por Jason Lee

En este tema se proporciona un tutorial de un proceso de implementación y compilación a escala empresarial. El enfoque descrito en este tema usa archivos de proyecto personalizados de Microsoft Build Engine (MSBuild) para proporcionar un control específico sobre todos los aspectos del proceso. Dentro de los archivos del proyecto, los destinos personalizados de MSBuild se usan para ejecutar utilidades de implementación como la Herramienta de implementación web de Internet Information Services (IIS) (MSDeploy.exe) y la utilidad de implementación de base de datos VSDBCMD.exe.

Nota:

En el tema anterior, Descripción del archivo de proyecto, se describen los componentes clave de un archivo de proyecto de MSBuild y se introduce el concepto de archivos de proyecto divididos para admitir la implementación en varios entornos de destino. Si aún no conoce estos conceptos, debe revisar Descripción del archivo de proyecto antes de trabajar con este tema.

Este tema forma parte de una serie de tutoriales basados en los requisitos de implementación empresarial de una empresa ficticia denominada Fabrikam, Inc. Esta serie de tutoriales utiliza una solución de muestra (Contact Manager) para representar una aplicación web con un nivel de complejidad realista, que incluye una aplicación ASP.NET MVC 3, un servicio Windows Communication Foundation (WCF) y un proyecto de base de datos.

El método de implementación que constituye el núcleo de estos tutoriales se basa en el enfoque del archivo de proyecto dividido descrito en Descripción del archivo del proyecto, en el que el proceso de compilación está controlado por dos archivos de proyecto: uno que contiene las instrucciones de compilación que se aplican a todos los entornos de destino y otro que contiene los ajustes de compilación e implementación específicos del entorno. En tiempo de compilación, el archivo de proyecto específico del entorno se combina en el archivo de proyecto independiente del entorno para formar un conjunto completo de instrucciones de compilación.

Introducción a Compilación e implementación

En la Solución de contacto con el administrador, tres archivos controlan el proceso de compilación e implementación:

  • Un archivo del proyecto universal (Publish.proj). Contiene instrucciones de compilación e implementación que no cambian entre entornos de destino.
  • Un archivo de proyecto específico del entorno (Env-Dev.proj). Contiene la configuración de compilación e implementación específica de un entorno de destino determinado. Por ejemplo, puede usar el archivo Env-Dev.proj para proporcionar la configuración de un entorno de desarrollo o prueba y crear un archivo alternativo denominado Env-Stage.proj para proporcionar la configuración de un entorno de ensayo.
  • Un archivo de comandos (Publish-Dev.cmd). Contiene un comando MSBuild.exe que especifica qué archivos de proyecto desea ejecutar. Puede crear un archivo de comandos para cada entorno de destino, donde cada archivo contiene un comando MSBuild.exe que especifica un archivo de proyecto específico del entorno diferente. Esto permite al desarrollador implementar en diferentes entornos con solo ejecutar el archivo de comandos adecuado.

En la solución de ejemplo, puede encontrar estos tres archivos en la carpeta Publicar solución.

In the sample solution, you can find three files in the Publish solution folder.

Antes de examinar estos archivos con más detalle, echemos un vistazo a cómo funciona el proceso de compilación general al usar este enfoque. A nivel global, el proceso de compilación e implementación tiene este aspecto:

What the build and deployment process looks like at a high level.

Lo primero que sucede es que los dos archivos de proyecto —uno que contiene instrucciones de compilación e implementación universales y otro que contiene configuraciones específicas del entorno— se combinan en un único archivo de proyecto. A continuación, MSBuild funciona a través de las instrucciones del archivo del proyecto. Compila cada uno de los proyectos de la solución mediante el archivo de proyecto para cada proyecto. A continuación, llama a otras herramientas, como Web Deploy (MSDeploy.exe) y la utilidad VSDBCMD para implementar el contenido web y las bases de datos en el entorno de destino.

De principio a fin, el proceso de compilación e implementación realiza estas tareas:

  1. Elimina el contenido del directorio de salida, en preparación para una compilación nueva.

  2. Compila cada proyecto en la solución:

    1. En el caso de los proyectos web (en este caso, una aplicación web de ASP.NET MVC y un servicio web WCF) el proceso de compilación crea un paquete de implementación web para cada proyecto.
    2. En el caso de los proyectos de base de datos, el proceso de compilación crea un manifiesto de implementación (archivo .deploymanifest) para cada proyecto.
  3. Usa la utilidad VSDBCMD.exe para implementar cada proyecto de base de datos en la solución y emplea varias propiedades de los archivos del proyecto (un cadena de conexión de destino y un nombre de base de datos) junto con el archivo .deploymanifest.

  4. Usa la utilidad MSDeploy.exe para implementar cada proyecto web en la solución y emplea varias propiedades de los archivos del proyecto para controlar el proceso de implementación.

Puede usar la solución de ejemplo para seguir este proceso con más detalle.

Nota:

Si necesita instrucciones sobre cómo personalizar los archivos del proyecto específicos del entorno para entornos de servidor propios, vea Configuración de propiedades de implementación para un entorno de destino.

Invocar el proceso de Compilación e implementación

Para implementar la solución Contact Manager en un entorno de prueba para desarrolladores, el desarrollador ejecuta el archivo de comandos Publish-Dev.cmd. Esto invoca MSBuild.exe, especificando Publish.proj como archivo de proyecto para ejecutar y Env-Dev.proj como valor de parámetro.

msbuild.exe Publish.proj /fl /p:TargetEnvPropsFile=EnvConfig\Env-Dev.proj

Nota:

El modificador /fl (abreviatura de /fileLogger) registra la salida de compilación en un archivo denominado msbuild.log en el directorio actual. Para obtener más información, vea Referencia de la línea de comandos de MSBuild.

En esta parte del proceso, MSBuild comienza a ejecutarse, carga el archivo Publish.proj e inicia el procesamiento de las instrucciones que contiene. La primera instrucción indica a MSBuild que importe el archivo de proyecto que especifica el parámetro TargetEnvPropsFile.

<Import Project="$(TargetEnvPropsFile)" />

El parámetro TargetEnvPropsFile especifica el archivo Env-Dev.proj, por lo que MSBuild combina el contenido del archivo Env-Dev.proj en el archivo Publish.proj.

Los siguientes elementos que MSBuild encuentra en el archivo de proyecto combinado son grupos de propiedades. Las propiedades se procesan en el orden en que aparecen en el archivo. MSBuild crea un par clave-valor para cada propiedad, siempre que se cumplan las condiciones especificadas. Las propiedades que se definan en el archivo más adelante sobrescribirán las propiedades con el mismo nombre que se hubieran definido anteriormente en el archivo. Por ejemplo, considere las propiedades OutputRoot.

<OutputRoot Condition=" '$(OutputRoot)'=='' ">..\Publish\Out\</OutputRoot>
<OutputRoot Condition=" '$(BuildingInTeamBuild)'=='true' ">$(OutDir)</OutputRoot>

Cuando MSBuild procesa el primer elemento OutputRoot (siempre que no se haya proporcionado un parámetro con nombre similar) establece el valor de la propiedad OutputRoot en ..\Publish\Out. Cuando encuentra el segundo elemento OutputRoot, si la condición se evalúa como true, sobrescribirá el valor de la propiedad OutputRoot con el valor del parámetro OutDir.

El siguiente elemento que encuentra MSBuild es un único grupo de elementos, que contiene un elemento denominado ProjectsToBuild.

<ItemGroup>
   <ProjectsToBuild Include="$(SourceRoot)ContactManager-WCF.sln"/>
</ItemGroup>

MSBuild procesa esta instrucción mediante la compilación de una lista de elementos denominada ProjectsToBuild. En este caso, la lista de elementos contiene un único valor: la ruta de acceso y el nombre del archivo de solución.

En este momento, los elementos restantes son destinos. Los destinos se procesan de forma diferente a las propiedades y los elementos. Básicamente, los destinos no se procesan a menos que el usuario los especifique explícitamente o los invoque otra construcción dentro del archivo de proyecto. Recuerde que la etiqueta Project de apertura incluye un atributo DefaultTargets.

<Project ToolsVersion="4.0" 
         DefaultTargets="FullPublish" 
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

Esto indica a MSBuild que invoque el destino FullPublish, si no se especifican destinos cuando se invoca MSBuild.exe. El destino FullPublish no contiene ninguna tarea; en su lugar, simplemente especifica una lista de dependencias.

<PropertyGroup>   
  <FullPublishDependsOn>
     Clean;
     BuildProjects;      
     GatherPackagesForPublishing;
     PublishDbPackages;
     PublishWebPackages;
  </FullPublishDependsOn>
</PropertyGroup>
<Target Name="FullPublish" DependsOnTargets="$(FullPublishDependsOn)" />

Esta dependencia indica a MSBuild que para ejecutar el destino FullPublish, debe invocar esta lista de destinos en el orden proporcionado:

  1. Debe invocar el destino Clean.
  2. Debe invocar el destino BuildProjects.
  3. Debe invocar el destino GatherPackagesForPublishing.
  4. Debe invocar el destino PublishDbPackages.
  5. Debe invocar el destino PublishWebPackages.

Destino Clean

El destino Clean básicamente elimina el directorio de salida y todo su contenido, como preparación para una compilación nueva.

<Target Name="Clean" Condition=" '$(BuildingInTeamBuild)'!='true' ">
  <Message Text="Cleaning up the output directory [$(OutputRoot)]"/>
  <ItemGroup>
     <_FilesToDelete Include="$(OutputRoot)**\*"/>
  </ItemGroup>
  <Delete Files="@(_FilesToDelete)"/>
  <RemoveDir Directories="$(OutputRoot)"/>
</Target>

Observe que el destino incluye un elemento ItemGroup. Al definir propiedades o elementos dentro de un elemento Target, va a crear propiedades y elementos dinámicos. Es decir, las propiedades o los elementos no se procesan hasta que se ejecuta el destino. Es posible que el directorio de salida no exista o contenga archivos hasta que comience el proceso de compilación, por lo que no se puede compilar la lista de _FilesToDelete como un elemento estático; debe esperar hasta que la ejecución esté en curso. Por lo tanto, se compila la lista como un elemento dinámico dentro del destino.

Nota:

En este caso, dado que el destino Clean es el primero en ejecutarse, no es necesario usar un grupo de elementos dinámicos. Sin embargo, se recomienda usar propiedades y elementos dinámicos en este tipo de escenario, ya que es posible que quiera ejecutar destinos en un orden diferente en algún momento.
También debe evitar declarar elementos que nunca se vayan a usar. Si tiene elementos que solo usará un destino específico, puede colocarlos dentro del destino para quitar cualquier sobrecarga innecesaria en el proceso de compilación.

Sin considerar los elementos dinámicos, el destino Clean es bastante sencillo y usa las tareas integradas Message, Delete y RemoveDir para:

  1. Enviar un mensaje a un registrador.
  2. Crear una lista de archivos que se van a eliminar.
  3. Elimine los archivos.
  4. Quitar el directorio de salida.

Destino BuildProjects

El destino BuildProjects compila todos los proyectos de la solución de ejemplo.

<Target Name="BuildProjects" Condition=" '$(BuildingInTeamBuild)'!='true' ">
   <MSBuild Projects="@(ProjectsToBuild)"
            Properties="OutDir=$(OutputRoot);
                        Configuration=$(Configuration);
                        DeployOnBuild=true;
                        DeployTarget=Package"
            Targets="Build" />
  </Target>

Este destino se describió con detalle en el tema anterior, Descripción del archivo de proyecto, para ilustrar cómo las tareas y los destinos hacen referencia a propiedades y elementos. Ahora nos interesa principalmente la tarea de MSBuild. Puede usar esta tarea para compilar varios proyectos. La tarea no crea una nueva instancia de MSBuild.exe, sino que usa la instancia en ejecución actual para compilar cada proyecto. Los puntos clave de interés en este ejemplo son las propiedades de implementación:

  • La propiedad DeployOnBuild indica a MSBuild que ejecute las instrucciones de implementación en la configuración del proyecto cuando se complete la compilación de cada proyecto.
  • La propiedad DeployTarget identifica el destino que desea invocar después de compilar el proyecto. En este caso, el destino Package compila la salida del proyecto en un paquete web que se puede implementar.

Nota:

El destino Package invoca la canalización de publicación web (WPP), que proporciona integración entre MSBuild y Web Deploy. Si desea echar un vistazo a los destinos integrados que proporciona WPP, consulte el archivo Microsoft.Web.Publishing.targets en la carpeta %PROGRAMFILES(x86)%\MSBuild\Microsoft\VisualStudio\v10.0\Web.

El destino GatherPackagesForPublishing

Si estudia el destino GatherPackagesForPublishing, verá que no contiene ninguna tarea. En su lugar, contiene un único grupo de elementos que define tres elementos dinámicos.

<Target Name="GatherPackagesForPublishing">
   <ItemGroup>
      <PublishPackages 
         Include="$(_ContactManagerDest)ContactManager.Mvc.deploy.cmd">
         <WebPackage>true</WebPackage>
         <!-- More item metadata -->  
      </PublishPackages>
      <PublishPackages 
         Include="$(_ContactManagerSvcDest)ContactManager.Service.deploy.cmd">
         <WebPackage>true</WebPackage>
         <!-- More item metadata -->
      </PublishPackages>
      <DbPublishPackages Include="$(_DbDeployManifestPath)">
         <DbPackage>true</DbPackage>
         <!-- More item metadata -->
      </DbPublishPackages>
   </ItemGroup>
</Target>

Estos elementos hacen referencia a los paquetes de implementación que se crearon cuando se ejecutó el destino BuildProjects. No se pudieron definir estos elementos estáticamente en el archivo del proyecto, ya que los archivos a los que hacen referencia los elementos no existen hasta que se ejecute el destino BuildProjects. En su lugar, los elementos deben definirse dinámicamente dentro de un destino que no se invoca hasta después de ejecutar el destino BuildProjects.

Los elementos no se usan dentro de este destino; este destino simplemente compila los elementos y los metadatos asociados a cada valor de elemento. Una vez procesados estos elementos, el elemento PublishPackages contendrá dos valores, la ruta de acceso al archivo ContactManager.Mvc.deploy.cmd y la ruta de acceso al archivo ContactManager.Service.deploy.cmd. Web Deploy crea estos archivos como parte del paquete web para cada proyecto y son los archivos que debe invocar en el servidor de destino para implementar los paquetes. Si abre uno de estos archivos, básicamente verá un comando MSDeploy.exe con varios valores de parámetro específicos de la compilación.

El elemento DbPublishPackages contendrá un valor único, la ruta de acceso al archivo ContactManager.Database.deploymanifest.

Nota:

Se genera un archivo .deploymanifest al compilar un proyecto de base de datos y usa el mismo esquema que un archivo de proyecto de MSBuild. Contiene toda la información necesaria para implementar una base de datos, incluida la ubicación del esquema de base de datos (.dbschema) y los detalles de los scripts previos a la implementación y posteriores a la implementación. Para obtener más información, consulte Introducción a Compilación e implementación de bases de datos.

Obtendrá más información sobre cómo se crean y se usan los paquetes de implementación y los manifiestos de implementación de base de datos en Compilar y empaquetar proyectos de aplicaciones web e Implementar proyectos de base de datos.

El destino PublishDbPackages

En breve, el destino PublishDbPackages invoca la utilidad VSDBCMD para implementar la base de datos ContactManager en un entorno de destino. La configuración de la implementación de bases de datos implica muchas decisiones y matices. Para obtener más información sobre esto, consulte Implementación de proyectos de base de datos y Personalización de implementaciones de base de datos para varios entornos. En este tema, nos centraremos en cómo funciona realmente este destino.

En primer lugar, observe que la etiqueta de apertura incluye un atributo Outputs.

<Target Name="PublishDbPackages" Outputs="%(DbPublishPackages.Identity)">

Este es un ejemplo de procesamiento por lotes de destino. En los archivos de proyecto de MSBuild, el procesamiento por lotes es una técnica para recorrer en iteración las colecciones. El valor del atributo Outputs, "%(DbPublishPackages.Identity)" hace referencia a la propiedad de metadatos Identity de la lista de elementos DbPublishPackages. Esta notación, Outputs=%(ItemList.ItemMetadataName), se traduce como:

  • Divida los elementos de DbPublishPackages en lotes de elementos que contienen el mismo valor de metadatos de Identity.
  • Ejecute el destino una vez por lote.

Nota:

Identity es uno de los valores de metadatos integrados que se asignan a cada elemento durante la creación. Hace referencia al valor del atributo Include en el elemento Item. Es decir, la ruta de acceso y el nombre de archivo del elemento.

En este caso, porque nunca debería haber más de un elemento con la misma ruta de acceso y nombre de archivo, básicamente estamos trabajando con tamaños de lote de uno. El destino se ejecuta una vez para cada paquete de base de datos.

Puede ver una notación similar en la propiedad _Cmd, que compila un comando VSDBCMD con los modificadores adecuados.

<_Cmd>"$(VsdbCmdExe)" 
   /a:Deploy 
   /cs:"%(DbPublishPackages.DatabaseConnectionString)" 
   /p:TargetDatabase=%(DbPublishPackages.TargetDatabase)             
   /manifest:"%(DbPublishPackages.FullPath)" 
   /script:"$(_CmDbScriptPath)" 
   $(_DbDeployOrScript)
</_Cmd>

En este caso, %(DbPublishPackages.DatabaseConnectionString), %(DbPublishPackages.TargetDatabase) y %(DbPublishPackages.FullPath) hacen referencia a los valores de metadatos de la colección de elementos DbPublishPackages. La tarea Exec usa la propiedad _Cmd, que invoca el comando.

<Exec Command="$(_Cmd)"/>

Como resultado de esta notación, la tarea Exec creará lotes basados en combinaciones únicas de los valores de metadatos DatabaseConnectionString, TargetDatabase y FullPath, y la tarea se ejecutará una vez para cada lote. Este es un ejemplo de procesamiento por lotes de destino. Sin embargo, dado que el procesamiento por lotes de nivel de destino ya ha dividido nuestra colección de elementos en lotes de un solo elemento, la tarea Exec se ejecutará una vez y solo una vez para cada iteración del destino. En otras palabras, esta tarea invoca la utilidad VSDBCMD una vez para cada paquete de base de datos de la solución.

Nota:

Para obtener más información sobre el procesamiento por lotes de tareas y de destino, vea Procesamiento por lotes de MSBuild, metadatos de elementos en procesamiento por lotes de destino y metadatos de elementos en el procesamiento por lotes de tareas.

El destino PublishWebPackages

En este punto, ha invocado el destino BuildProjects, que genera un paquete de implementación web para cada proyecto de la solución de ejemplo. Un archivo deploy.cmd acompaña a cada paquete y contiene los comandos MSDeploy.exe necesarios para implementar el paquete en el entorno de destino y un archivo SetParameters.xml, que especifica los detalles necesarios del entorno de destino. También ha invocado el destino GatherPackagesForPublishing, que genera una colección de elementos con los archivos de deploy.cmd que le interesan. Básicamente, el destino PublishWebPackages realiza estas funciones:

  • Manipula el archivo SetParameters.xml para cada paquete, a fin de que incluya los detalles correctos del entorno de destino mediante la tarea XmlPoke.
  • Invoca el archivo deploy.cmd para cada paquete mediante los modificadores adecuados.

Al igual que el destino PublishDbPackages, el destino PublishWebPackages usa el procesamiento por lotes de destino para asegurarse de que el destino se ejecuta una vez para cada paquete web.

<Target Name="PublishWebPackages" Outputs="%(PublishPackages.Identity)">

Dentro del destino, la tarea Exec se usa para ejecutar el archivo deploy.cmd para cada paquete web.

<PropertyGroup>
   <_Cmd>
      %(PublishPackages.FullPath) 
      $(_WhatifSwitch) 
      /M:$(MSDeployComputerName) 
      %(PublishPackages.AdditionalMSDeployParameters)
   </_Cmd>
</PropertyGroup>
<Exec Command="$(_Cmd)"/>

Para obtener más información sobre cómo configurar la implementación de paquetes web, vea Compilar y empaquetar proyectos de aplicaciones web.

Conclusión

En este tema se proporciona un tutorial sobre cómo se usan los archivos de proyecto divididos para controlar el proceso de compilación e implementación de principio a fin para la solución de ejemplo Contact Manager. Este enfoque permite ejecutar implementaciones complejas y a escala empresarial en un solo paso repetible, simplemente mediante la ejecución de un archivo de comandos específico del entorno.

Lecturas adicionales

Para obtener una introducción más detallada a los archivos del proyecto y el WPP, vea Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build (Dentro de Microsoft Build Engine: uso de MSBuild y Team Foundation Build) de Sayed Ibrahim Hashimi y William Bartholomew, ISBN: 978-0-7356-4524-0.