visual studio objetos muestra errores dinamicos clases biblioteca c# visual-studio-2008 mercurial build

studio - objetos dinamicos c#



Incrustar automáticamente información de revisión mercurial en proyectos de Visual Studio c# (7)

¿Ha considerado usar un recurso de cadena en lugar de una cadena de lenguaje C # constante? Los recursos de cadenas pueden ser editados / reemplazados en los binarios de salida postconstruidos usando herramientas destinadas a la localización.

Usted emitiría su número de versión mercurial a un archivo de texto que no es utilizado por la construcción de C #, luego, usando una operación posterior a la construcción, reemplace el recurso de versión con el valor real del archivo de texto emitido. Si usa un nombre seguro para firmar sus ensamblados, la sustitución de la cadena de recursos debería realizarse antes de la firma.

Así es como manejamos este problema en Borland hace años para los productos de Windows. El mundo se ha vuelto más complicado desde entonces, pero el principio todavía se aplica.

Problema original

Al construir nuestros proyectos, quiero que la identificación mercurial de cada repositorio esté integrada en los productos de ese repositorio (la biblioteca, la aplicación o la aplicación de prueba).

Me parece que hace que sea mucho más fácil depurar una aplicación que están siendo ejecutados por los clientes en 8 zonas horarias de distancia si sabes con precisión lo que entró en la construcción de la versión particular de la aplicación que están utilizando. Como tal, cada proyecto (aplicación o biblioteca) en nuestros sistemas implementa una forma de obtener la información de revisión asociada.

También me parece muy útil poder ver si una aplicación se ha compilado con conjuntos de cambios limpios (sin modificar) del repositorio. ''Hg id'' agrega útilmente un + al ID del conjunto de cambios cuando hay cambios no confirmados en un repositorio, por lo que nos permite ver fácilmente si las personas están ejecutando una versión limpia o una versión modificada del código.

Mi solución actual se detalla a continuación y cumple con los requisitos básicos, pero hay una serie de problemas con ella.

Solución actual

Por el momento, a todas y cada una de las soluciones de Visual Studio, agrego los siguientes comandos de "línea de comando de evento previo a la compilación":

cd $(ProjectDir) HgID

También agrego un archivo HgID.bat al directorio del proyecto:

@echo off type HgId.pre > HgId.cs For /F "delims=" %%a in (''hg id'') Do <nul >>HgID.cs set /p = @"%%a" echo ; >> HgId.cs echo } >> HgId.cs echo } >> HgId.cs

junto con un archivo HgId.pre, que se define como:

namespace My.Namespace { /// <summary> Auto generated Mercurial ID class. </summary> internal class HgID { /// <summary> Mercurial version ID [+ is modified] [Named branch]</summary> public const string Version =

Cuando construyo mi aplicación, el evento de preconstrucción se desencadena en todas las bibliotecas, creando un nuevo archivo HgId.cs (que no se mantiene bajo control de revisión) y haciendo que la biblioteca vuelva a compilarse con el nuevo ''hg id'' cadena en ''Versión''.

Problemas con la solución actual

El problema principal es que, dado que HgId.cs se vuelve a crear en cada precompilación, de modo que cada vez que tenemos que compilar algo, se vuelven a compilar todos los proyectos en la solución actual. Como queremos poder depurar fácilmente en nuestras bibliotecas, generalmente mantenemos muchas bibliotecas referenciadas en nuestra solución de aplicaciones principal. Esto puede dar como resultado tiempos de construcción que son significativamente más largos de lo que quisiera.

Idealmente, me gustaría que las bibliotecas compilen solo si el contenido del archivo HgId.cs realmente ha cambiado, en lugar de haber sido recreado con exactamente los mismos contenidos.

El segundo problema con este método es su dependencia del comportamiento específico del shell de Windows. Ya he tenido que modificar el archivo por lotes varias veces, ya que el original funcionaba con XP pero no con Vista, la siguiente versión funcionaba con Vista pero no con XP y finalmente conseguí que funcionara con ambas. Sin embargo, si funcionará con Windows 7 es algo que todos adivinan y con el paso del tiempo, veo que es más probable que los contratistas puedan construir nuestras aplicaciones en sus Windows 7 Boxen.

Finalmente, tengo un problema estético con esta solución, los archivos de proceso por lotes y los archivos de plantilla juntos parecen la forma incorrecta de hacerlo.

Mis preguntas reales

¿Cómo resolverías / cómo estás resolviendo el problema que intento resolver?

¿Qué mejores opciones existen que las que estoy haciendo actualmente?

Soluciones rechazadas a estos problemas

Antes de implementar la solución actual, analicé la extensión de palabra clave de Mercurials, ya que parecía la solución obvia. Sin embargo, cuanto más lo miraba y leía las opiniones de las personas, más llegaba a la conclusión de que no era lo correcto.

También recuerdo los problemas que me ha causado la sustitución de palabras clave en proyectos de compañías anteriores (el simple hecho de tener que utilizar Source Safe de nuevo me llena de una sensación de temor * 8 '').

Además, particularmente no quiero tener que habilitar las extensiones de Mercurial para completar la compilación. Quiero que la solución sea independiente, de modo que no sea fácil que la aplicación se compile accidentalmente sin la información de la versión incorporada solo porque una extensión no está habilitada o no se ha instalado el software auxiliar correcto.

También pensé en escribir esto en un mejor lenguaje de scripting, uno donde solo escribiría el archivo HgId.cs si el contenido realmente había cambiado, pero todas las opciones que podría pensar requerirían que mis compañeros de trabajo, contratistas y posiblemente clientes tiene que instalar un software que de otra manera no querrían (por ejemplo, cygwin).

Cualquier otra opción que las personas puedan pensar sería apreciada.

Actualizar

Solución parcial

Después de haber jugado con esto por un tiempo, he logrado obtener el archivo HgId.bat para sobrescribir el archivo HgId.cs si cambia:

@echo off type HgId.pre > HgId.cst For /F "delims=" %%a in (''hg id'') Do <nul >>HgId.cst set /p = @"%%a" echo ; >> HgId.cst echo } >> HgId.cst echo } >> HgId.cst fc HgId.cs HgId.cst >NUL if %errorlevel%==0 goto :ok copy HgId.cst HgId.cs :ok del HgId.cst

Problemas con esta solución

Aunque HgId.cs ya no se vuelve a crear cada vez, Visual Studio aún insiste en compilar todo cada vez. Intenté buscar soluciones e intenté comprobar "Solo crear proyectos de inicio y dependencias en Ejecutar" en Herramientas | Opciones | Proyectos y soluciones | Compilar y ejecutar, pero no hace la diferencia.

El segundo problema también permanece, y ahora no tengo manera de probar si funcionará con Vista, ya que ese contratista ya no está con nosotros.

  • Si alguien puede probar este archivo por lotes en un cuadro de Windows 7 y / o Vista, agradecería saber cómo fue.

Finalmente, mi problema estético con esta solución es aún más fuerte de lo que era antes, ya que el archivo de proceso por lotes es más complejo y ahora hay más que fallan.

Si puedes pensar en mejores soluciones, me encantaría saber sobre ellas.


Acabo de lanzar una pequeña tarea de MSBuild de código abierto para hacer exactamente lo que necesita:

  • Pone su número de revisión Mercurial en su versión de ensamblado .NET
  • Puede ver a partir de la versión si un conjunto ha sido compilado con cambios no confirmados
  • No causa compilaciones innecesarias si la revisión no ha cambiado
  • No depende de las secuencias de comandos de Windows
  • No hay nada que instalar: solo agrega una pequeña DLL a su solución y edita algunos archivos en su proyecto

http://versioning.codeplex.com


Creo que tengo una respuesta para ti. Esto será un poco complicado, pero te aleja de tener que hacer cualquier archivo por lotes. Puede confiar en MSBuild y Custom Tareas para hacer esto por usted. He usado el paquete de extensión para MSBuild (disponible en CodePlex ), pero la segunda tarea que necesita es algo que podría escribir fácilmente.

Con esta solución, puede hacer clic con el botón derecho en la DLL y ver en las propiedades del archivo de donde proviene la versión Mercurial de la DLL (o EXE).

Estos son los pasos:

  1. Obtenga el CodePlex OR Write Custom Tarea para sobrescribir AssemblyInfo.cs
  2. Cree una Tarea de compilación personalizada en su propio proyecto para obtener el Mercurial Id (código a continuación).
  3. Edite los archivos del proyecto que necesitan Mercurial Id para usar la Tarea personalizada (código a continuación).

Tarea personalizada para obtener id mercurial: (Esto debería probarse bien y tal vez mejor generalizarse ...)

using System; using System.Diagnostics; using Microsoft.Build.Utilities; using Microsoft.Build.Framework; namespace BuildTasks { public class GetMercurialVersionNumber : Task { public override bool Execute() { bool bSuccess = true; try { GetMercurialVersion(); Log.LogMessage(MessageImportance.High, "Build''s Mercurial Id is {0}", MercurialId); } catch (Exception ex) { Log.LogMessage(MessageImportance.High, "Could not retrieve or convert Mercurial Id. {0}/n{1}", ex.Message, ex.StackTrace); Log.LogErrorFromException(ex); bSuccess = false; } return bSuccess; } [Output] public string MercurialId { get; set; } [Required] public string DirectoryPath { get; set; } private void GetMercurialVersion() { Process p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.CreateNoWindow = true; p.StartInfo.WorkingDirectory = DirectoryPath; p.StartInfo.FileName = "hg"; p.StartInfo.Arguments = "id"; p.Start(); string output = p.StandardOutput.ReadToEnd().Trim(); Log.LogMessage(MessageImportance.Normal, "Standard Output: " + output); string error = p.StandardError.ReadToEnd().Trim(); Log.LogMessage(MessageImportance.Normal, "Standard Error: " + error); p.WaitForExit(); Log.LogMessage(MessageImportance.Normal, "Retrieving Mercurial Version Number"); Log.LogMessage(MessageImportance.Normal, output); Log.LogMessage(MessageImportance.Normal, "DirectoryPath is {0}", DirectoryPath); MercurialId = output; } }

Y el archivo de proyecto modificado: (Los comentarios pueden ayudar)

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!--this is the import tag for the MSBuild Extension pack. See their documentation for installation instructions.--> <Import Project="C:/Program Files (x86)/MSBuild/ExtensionPack/MSBuild.ExtensionPack.tasks" /> <!--Below is the required UsingTask tag that brings in our custom task.--> <UsingTask TaskName="BuildTasks.GetMercurialVersionNumber" AssemblyFile="C:/Users/mpld81/Documents/Visual Studio 2008/Projects/LambaCrashCourseProject/BuildTasks/bin/Debug/BuildTasks.dll" /> <PropertyGroup> <Configuration Condition=" ''$(Configuration)'' == '''' ">Debug</Configuration> <Platform Condition=" ''$(Platform)'' == '''' ">AnyCPU</Platform> <ProductVersion>9.0.30729</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{D4BA6C24-EA27-474A-8444-4869D33C22A9}</ProjectGuid> <OutputType>Library</OutputType> <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>LibraryUnderHg</RootNamespace> <AssemblyName>LibraryUnderHg</AssemblyName> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> </PropertyGroup> <PropertyGroup Condition=" ''$(Configuration)|$(Platform)'' == ''Debug|AnyCPU'' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin/Debug/</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <PropertyGroup Condition=" ''$(Configuration)|$(Platform)'' == ''Release|AnyCPU'' "> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin/Release/</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="System.Core"> <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> <Reference Include="System.Xml.Linq"> <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> <Reference Include="System.Data.DataSetExtensions"> <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> <Reference Include="System.Data" /> <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> <Compile Include="Class1.cs" /> <Compile Include="Properties/AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)/Microsoft.CSharp.targets" /> <Target Name="Build" DependsOnTargets="BeforeBuild"> <!--This Item group is a list of configuration files to affect with the change. In this case, just this project''s.--> <ItemGroup> <AssemblyInfoFiles Include="$(MSBuildProjectDirectory)/Properties/AssemblyInfo.cs" /> </ItemGroup> <!--Need the extension pack to do this. I''ve put the Mercurial Id in the Product Name Attribute on the Assembly.--> <MSBuild.ExtensionPack.Framework.AssemblyInfo AssemblyInfoFiles="@(AssemblyInfoFiles)" AssemblyProduct="Hg: $(MercurialId)" /> <!--This is here as an example of messaging you can use to debug while you are setting yours up.--> <Message Text="In Default Target, File Path is: @(AssemblyInfoFiles)" Importance="normal" /> </Target> <Target Name="BeforeBuild"> <!--This is the custom build task. The Required Property in the task is set using the property name (DirectoryPath)--> <BuildTasks.GetMercurialVersionNumber DirectoryPath="$(MSBuildProjectDirectory)"> <!--This captures the output by reading the task''s MercurialId Property and assigning it to a local MSBuild Property called MercurialId - this is reference in the Build Target above.--> <Output TaskParameter="MercurialId" PropertyName="MercurialId" /> </BuildTasks.GetMercurialVersionNumber> </Target> <!--<Target Name="AfterBuild"> </Target>--> </Project>

Última nota: el proyecto de tareas de compilación solo necesita ser construido una vez. No intente construirlo cada vez que haga el resto de su solución. Si lo hace, encontrará que VS2008 tiene el dll bloqueado. Aún no lo he descifrado, pero creo que lo mejor es construir el dll como lo desee, luego, distribuya SOLO el dll con su código, asegurándose de que la ubicación del dll sea fija en relación con cada proyecto que necesite usar. pulg. De esa manera, nadie tiene que instalar nada.

Buena suerte, y espero que esto ayude!

Audie


Esto es lo que hacemos aquí: no incorporamos la revisión cada vez que el proyecto se construye en las máquinas del desarrollador. En el mejor de los casos, esto causa los problemas que mencionó anteriormente y, en el peor de los casos, es engañoso porque podría haber modificado los archivos en su espacio de trabajo.

En cambio, solo incorporamos el número de revisión del control de origen cuando nuestros proyectos se crean bajo TeamCity en un servidor de compilación independiente. La revisión está integrada tanto en AssemblyVersion como en AssemblyFileVersion , dividida en las dos últimas partes.

Por defecto, la versión finaliza en 9999.9999, y dividimos la revisión de tal forma que la revisión 12345678 se convierta en 1234.5678 (no es que estemos cerca de la revisión número 12 millones ...).

Este proceso garantiza que un producto cuya versión es 2.3.1.1256 fue definitivamente una versión prístina de la revisión 11.256. Cualquier cosa que los desarrolladores creen manualmente se verá así: 2.3.9999.9999.

Los detalles exactos de cómo se logra lo anterior no son directamente relevantes ya que no estamos usando Mercurial, pero brevemente: TeamCity se encarga de revisar la revisión requerida y pasa su número a nuestro script MSBuild, que luego realiza la reescritura de AssemblyInfo.cs con la ayuda de una tarea personalizada que escribimos.

Lo que más me gusta de esto es que absolutamente ningún archivo se modifica al presionar F5 en Visual Studio; de hecho, en lo que respecta a Visual Studio, funciona con una solución simple y normal.


Hemos resuelto este problema con otro sistema de control de fuente, subversión.

Lo que hacemos es que tenemos un archivo commonassemblyinfo.cs y conectamos el número de revisión svn con un script de msbuild.

Literalmente llamamos a svninfo y raspamos el resultado de la revisión: parte y luego lo insertamos en el archivo CommonAssemblyInfo.cs.

Cada proyecto en nuestra solución tiene un enlace al archivo común y luego se compila, lo que significa que los ensamblajes están versionados en la compilación con el número de revisión svn, también significa que todas las bibliotecas dependientes que hemos escrito también están versionadas.

Conseguimos esto bastante fácilmente usando los archivos cruisecontrol .net y msbuild.

No he usado mercurial, pero creo que podrías hacer algo similar con el comando id. pero debido al formato de la salida, necesitarás acercarte a cómo lo usaste de forma algo diferente.

Tendría un archivo xml estándar que se lee en el inicio de la aplicación y podría meter la información en eso (también funciona para los archivos de resx). Luego puede leer ese valor en el código. Un poco asqueroso, pero funciona.

Mi solución favorita es la forma en que lo hacemos, pero no se puede hacer en mercurial ya que la información del conjunto de cambios no es puramente numérica, como lo es el número de revisión svn.


Parece haber múltiples enfoques posibles para este problema. La primera y probablemente preferida solución sería instalar un servidor de compilación y solo distribuir compilaciones generadas allí a los clientes. Esto tiene la ventaja de que nunca envía cambios no confirmados. Al usar MSBuild, NAnt u otra herramienta de compilación basada en tareas, todo el proceso es muy flexible. Pude instalar TeamCity y obtener las primeras compilaciones en funcionamiento con muy poco esfuerzo, pero también hay otros buenos servidores de compilación. Esta realmente debería ser tu solución.

Si por algún motivo insiste en que está bien distribuir compilaciones de desarrollador a los clientes;) entonces necesitará una solución local.

Una solución bastante fácil sería usar el soporte integrado para auto incrementar el número de compilación de un ensamblado:

// major.minor.build.revision [assembly:AssemblyVersion("1.2.*")]

El * hace que el número de compilación aumente automáticamente cada vez que compila (y hay cambios). El número de revisión es un número aleatorio. Desde aquí, puede realizar un seguimiento de la asociación al ID de Mercurial guardando ambas partes de la información, por ejemplo, publicándola en una solución web interna o lo que se ajuste a sus necesidades particulares, o actualice el ensamblaje generado. Sospecho que podrías usar PostSharp o Mono.Cecil reescribir el ensamblaje, por ejemplo, parcheando el número de revisión para que sea el id. Si sus ensamblajes están firmados, la reescritura debe suceder antes de que los firme, lo cual es un poco molesto si no tiene un archivo de compilación. Tenga en cuenta que VS se puede configurar para compilar utilizando su archivo de compilación personalizado en lugar del procedimiento de compilación predeterminado.

Mi sugerencia final es crear un proyecto separado solo para la ID de hg, y usar el paso posterior a la construcción para fusionar los ensamblados generados en uno solo. ILMerge admite volver a firmar las asambleas firmadas y, por lo tanto, es probable que sea más fácil hacer el trabajo. El inconveniente es que la redistribución de ILMerge no está permitida (aunque el uso comercial es).

No es una solución, pero espero que sea una inspiración para empezar.


Soy mi .hgrc tengo esto:

[extensions] hgext.keyword = hgext.hgk = [keyword] ** = [keywordmaps] Id = {file|basename},v {node|short} {date|utcdate} {author|user}

Y en mi archivo fuente (Java) hago:

public static final String _rev = "$Id$";

Después de confirmar, $ Id $ se expande en algo como:

public static final String _rev = "$Id: Communication.java,v 96b741d87d07 2010/01/25 10:25:30 mig $";