2010-03-18 13 views
11

Me gustaría crear un proyecto de MSBuild que refleje las dependencias del proyecto en una solución y envuelva los proyectos VS dentro de los objetivos reutilizables.Reemplazar .sln con MSBuild y envolver los proyectos contenidos en los objetivos

El problema que me gusta resolver al hacer esto es svn-export, build y deploy un ensamblado específico (y sus dependencias) en una aplicación de BizTalk.

Mi pregunta es: ¿Cómo puedo hacer que los objetivos para exportar svn, construir y desplegar sean reutilizables y también reutilizar los proyectos envueltos cuando se crean para dependencias diferentes?

Sé que sería más sencillo crear la solución y desplegar solo los ensamblajes necesarios, pero me gustaría reutilizar los objetivos tanto como sea posible.

Las partes

El proyecto me gusta para desplegar

<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    <PropertyGroup> 
     <ExportRoot Condition="'$(Export)'==''">Export</ExportRoot> 
    </PropertyGroup> 

    <Target Name="Clean_Export"> 
     <RemoveDir Directories="$(ExportRoot)\My.Project.Dir" /> 
    </Target> 

    <Target Name="Export_MyProject"> 
     <Exec Command="svn export svn://xxx/trunk/Biztalk2009/MyProject.btproj --force" WorkingDirectory="$(ExportRoot)" /> 
    </Target> 

    <Target Name="Build_MyProject" DependsOnTargets="Export_MyProject"> 
     <MSBuild Projects="$(ExportRoot)\My.Project.Dir\MyProject.btproj" Targets="Build" Properties="Configuration=Release"></MSBuild> 
    </Target> 

    <Target Name="Deploy_MyProject" DependsOnTargets="Build_MyProject"> 
     <Exec Command="BTSTask AddResource -ApplicationName:CORE -Source:MyProject.dll" /> 
    </Target> 
</Project> 

Los proyectos de los que depende mirada casi exactamente como esta (otra .btproj y .csproj).

Respuesta

16

Vaya, esta es una pregunta cargada para una publicación en el foro. Escribí unas 20 páginas sobre la creación de archivos .targets reutilizables en mi book, pero aquí comenzaré con los conceptos básicos. Creo que la clave para la creación de scripts de construcción reutilizables (es decir .targets archivos) es de tres elementos:

  • Lugar comportamiento (es decir, objetivos ) en archivos separados
  • datos Place (es decir, propiedades y objetos, éstos son llamados archivos .proj) en sus propios archivos
  • extensibilidad
  • .targets archivos debe validar las hipótesis

La idea es que desee colocar todos sus objetivos en archivos separados y luego estos archivos serán importados por los archivos que conducirán el proceso de compilación. Estos son los archivos que contienen los datos. Como importas los archivos .targets, obtienes todos los objetivos como si se hubieran definido en línea. Habrá un contrato silencioso entre los archivos .proj y .targets. Este contrato se define en propiedades y elementos que ambos usan. Esto es lo que necesita ser validado.

La idea aquí no es nueva. Este patrón es seguido por .csproj (y otros proyectos generados por Visual Studio). Si echas un vistazo a tu archivo .csproj, no encontrarás un solo objetivo, solo propiedades y elementos. Luego, hacia la parte inferior del archivo, importa Microsoft.csharp.targets (puede variar según el tipo de proyecto). Este archivo de proyecto (junto con otros que importa) contiene todos los objetivos que realmente realizan la compilación.

Así que está organizada así:

  • SharedBuild.targets
  • MyProduct.proj

Dónde MyProdcut.proj podría ser:

<?xml version="1.0" encoding="utf-8"?> 
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    <!-- This uses a .targets file to off load performing the build --> 
    <PropertyGroup> 
    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration> 
    <OutputPath Condition=" '$(OutputPath)'=='' ">$(MSBuildProjectDirectory)\BuildArtifacts\bin\</OutputPath> 
    </PropertyGroup> 

    <ItemGroup> 
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary1\ClassLibrary1.csproj"/> 
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary2\ClassLibrary2.csproj"/> 
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary3\ClassLibrary3.csproj"/> 
    <Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj"/> 
    </ItemGroup> 

    <Import Project="SharedBuild.targets"/> 
</Project> 

Y SharedBuild.targets podría ser:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    <!-- This represents a re-usable build file --> 
    <Target Name="SharedBuild_Validate"> 
    <!-- See http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx for more info 
     about this validation pattern 
    --> 
    <ItemGroup> 
     <_RequiredProperties Include ="Configuration"> 
      <Value>$(Configuration)</Value> 
     </_RequiredProperties>  
     <_RequiredProperties Include ="OutputPath"> 
      <Value>$(OutputPath)</Value> 
     </_RequiredProperties> 

     <_RequiredItems Include="Projects"> 
     <RequiredValue>%(Projects.Identity)</RequiredValue> 
     <RequiredFilePath>%(Projects.Identity)</RequiredFilePath> 
     </_RequiredItems> 
    </ItemGroup> 

    <!-- Raise an error if any value in _RequiredProperties is missing --> 
    <Error Condition="'%(_RequiredProperties.Value)'==''" 
      Text="Missing required property [%(_RequiredProperties.Identity)]"/> 

    <!-- Raise an error if any value in _RequiredItems is empty --> 
    <Error Condition="'%(_RequiredItems.RequiredValue)'==''" 
      Text="Missing required item value [%(_RequiredItems.Identity)]" /> 

    <!-- Validate any file/directory that should exist --> 
    <Error Condition="'%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)')" 
      Text="Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)]" /> 
    </Target> 

    <PropertyGroup> 
    <BuildDependsOn> 
     SharedBuild_Validate; 
     BeforeBuild; 
     CoreBuild; 
     AfterBuild; 
    </BuildDependsOn> 
    </PropertyGroup> 
    <Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/> 
    <Target Name="BeforeBuild"/> 
    <Target Name="AfterBuild"/> 
    <Target Name="CoreBuild"> 
    <!-- Make sure output folder exists --> 
    <PropertyGroup> 
     <_FullOutputPath>$(OutputPath)$(Configuration)\</_FullOutputPath> 
    </PropertyGroup> 
    <MakeDir Directories="$(_FullOutputPath)"/> 
    <MSBuild Projects="@(Projects)" 
      BuildInParallel="true" 
      Properties="OutputPath=$(_FullOutputPath)"/> 
    </Target> 
</Project> 

No mirar demasiado al SharedBuild_Validate objetivo aún. Lo puse ahí para completarlo, pero no me concentre en eso. Puede encontrar más información sobre eso en mi blog al http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx.

Las partes importantes a tener en cuenta son los puntos de extensibilidad. Aunque este es un archivo muy básico, tiene todos los componentes de un archivo .targets reutilizable. Puede personalizar su comportamiento pasando diferentes propiedades y elementos para compilar. Puede extender su comportamiento reemplazando un objetivo (BeforeBuild, AfterBuild o incluso CoreBuild) y se puede inyectar sus propios objetivos en la acumulación con:

<Project ...> 
    ... 
    <Import Project="SharedBuild.targets"/> 
    <PropertyGroup> 
    <BuildDependsOn> 
     $(BuildDependsOn); 
     CustomAfterBuild 
    </BuildDependsOn> 
    </PropertyGroup> 
    <Target Name="CustomAfterBuild"> 
    <!-- Insert stuff here --> 
    </Target> 
</Project> 

En su caso me gustaría crear un archivo que utiliza el SvnExport.targets propiedades requeridas:

  • SvnExportRoot
  • SvnUrl
  • SvnWorkingDirectory que va a utilizar estas propiedades para hacer la exportación.

A continuación, cree otra para la creación e implementación de Biztalk. Podrías dividir esto en 2 si es necesario.

Luego, dentro de su archivo .proj solo importa ambos y configura los objetivos para compilarlos en el orden correcto y listo.

Esto es solo el comienzo de la creación de elementos de construcción reutilizables, pero esto debería hacer girar las ruedas en su cabeza. Voy a publicar todo esto en mi blog, así como enlaces de descarga para todos los archivos.

ACTUALIZACIÓN:

Publicado en blog en http://sedodream.com/2010/03/19/ReplacingSolutionFilesWithMSBuildFiles.aspx

+0

Gracias por esta cartilla completa sobre Objetivos reutilizables! Esto me dará un buen punto de partida. Creo que el problema con mis intentos fue que intenté mezclar el .proj que impulsa la compilación y los objetivos. Reutilizables. – Filburt

+0

¿Hay alguna manera "fácil" de forzar el elemento en todos los archivos .proj incluidos? Quiero decir, si incluye proyectos como puede incluir proyectos que no están conectados con los objetivos personalizados. OH y @filburt, deberían revisar el libro de Sayed, ¡vale cada centavo! :) – JohannesH

+0

@JohannesH Yo diría que hay una manera fácil pero es demasiado para empacar en un comentario. Tal vez revise [mi respuesta sobre Buenas prácticas: ¿Cómo volver a usar los archivos .csproj y .sln para crear su secuencia de comandos MSBuild para CI?] (Http://stackoverflow.com/a/3071804/205233) - los "Proyectos" sección. Básicamente solo incluya su importación global en el alcance más externo y agregue a esta colección. – Filburt

Cuestiones relacionadas