2010-02-11 23 views
47

Digamos que quería crear un archivo de texto estático que se incluye con cada versión. Quiero que el archivo se actualice con el número de versión de la versión (como se especifica en AssemblyInfo.cs), pero no quiero tener que hacerlo manualmente.Determinar la versión del ensamblaje durante un evento posterior a la construcción

Tenía la esperanza de que podría utilizar un evento posterior a la generación y alimentar el número de versión de un archivo por lotes como esto:

call foo.bat $(AssemblyVersion) 

sin embargo no puedo encontrar ninguna variable o macro adecuada para su uso.

¿Hay alguna manera de lograr esto que me he perdido?

+0

leer todas las respuestas para el que sea más adecuado para usted. El 'PostBuildEventDependsOn' es el más votado y el más simple. (cuidado con el truco especial de '25' Unicode al pegar ' – OzBob

Respuesta

15

Si prefiere secuencias de comandos estos métodos también podría funcionar para usted:

Si está utilizando el evento posterior a la generación, puede utilizar la herramienta Filever.exe de agarrarlo fuera del conjunto ya construido :

for /F "tokens=4" %%F in ('filever.exe /B /A /D bin\debug\myapp.exe') do (
    set VERSION=%%F 
) 
echo The version is %VERSION% 

Get Filever.exe desde aquí: http://support.microsoft.com/kb/913111

Si está utilizando el evento de pre-construcción, que puede sacarlo del archivo AssemblyInfo.cs como sigue s:

set ASMINFO=Properties\AssemblyInfo.cs 
FINDSTR /C:"[assembly: AssemblyVersion(" %ASMINFO% | sed.exe "s/\[assembly: AssemblyVersion(\"/SET CURRENT_VERSION=/g;s/\")\]//g;s/\.\*//g" >SetCurrVer.cmd 
CALL SetCurrVer.cmd 
DEL SetCurrVer.cmd 
echo Current version is %CURRENT_VERSION% 

Esto utiliza la herramienta de línea de comandos UNIX sed, que se puede descargar desde muchos lugares, como aquí: http://unxutils.sourceforge.net/ - IIRC que uno trabaja bien.

+0

Uso el evento Pre-Build. Ahora, tengo Properties \ AssemblyInfo.cs o enlace a% SolutionDir% \ GlobalAssemblyInfo.cs. ¿Cómo puedo detectar si AssemblyVersion está en Properties \ AssemblyInfo.cs o en% SolutionDir% \ GlobalAssemblyInfo.cs y luego obtiene la versión ... ?? – Kiquenet

+0

Quizás pueda usar la función "IF [NOT] EXIST filename command"? Escriba "if /?" en un símbolo del sistema para documentación y algunas muestras. – Tuinstoelen

1

Creo que lo mejor que puede hacer es mirar a MSBuild y MsBuild Extension Pack usted debe ser capaz de editar archivos que solución para que un evento posterior acumulación se produce y escribe en el archivo de prueba.

Si esto es demasiado complicado, entonces podría simplemente crear un pequeño programa que inspeccione todos los ensamblados en su directorio de salida y lo ejecute en compilación posterior, podría pasar el directorio de salida usando el nombre de la variable ... por ejemplo en posterior acumulación de eventos ...

AssemblyInspector.exe "$ (targetPath)"

class Program 
{ 
    static void Main(string[] args) 
    { 
     var assemblyFilename = args.FirstOrDefault(); 
     if(assemblyFilename != null && File.Exists(assemblyFilename)) 
     { 
      try 
      { 
       var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFilename); 
       var name = assembly.GetName(); 

       using(var file = File.AppendText("C:\\AssemblyInfo.txt")) 
       { 
        file.WriteLine("{0} - {1}", name.FullName, name.Version); 
       } 
      } 
      catch (Exception ex) 
      { 
       throw; 
      } 
     } 
    } 
} 

también podría pasar en la ubicación del archivo de texto ...

11

Como solución he escrito una aplicación de consola administrada que toma el objetivo como parámetro, y devuelve el número de versión.

Todavía estoy interesado en escuchar una solución más simple, pero estoy publicando esto en caso de que alguien más lo encuentre útil.

using System; 
using System.IO; 
using System.Diagnostics; 
using System.Reflection; 

namespace Version 
{ 
    class GetVersion 
    { 
     static void Main(string[] args) 
     { 
      if (args.Length == 0 || args.Length > 1) ShowUsage(); 

      string target = args[0]; 

      string path = Path.IsPathRooted(target) 
           ? target 
           : Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + Path.DirectorySeparatorChar + target; 

      Console.Write(Assembly.LoadFile(path).GetName().Version.ToString(2)); 
     } 

     static void ShowUsage() 
     { 
      Console.WriteLine("Usage: version.exe <target>"); 
     } 
    } 
} 
+0

Sí, encontré esto útil, gracias. No quiero usar las herramientas de Unix y no quiero usar MSBuild, así que utilicé un código similar al esto provisto aquí por Winston y Rohan. En el evento posterior a la construcción de un proyecto invoco este código con 2 argumentos, el TargetPath del ensamblado y la ruta al proyecto de instalación donde está empaquetado el ensamblado. En el evento posterior a la compilación de la configuración, Llamo a msiinfo para mover los datos de la versión al MSI para que los consumidores puedan ver la versión que están cargando. Gracias. – TonyG

+0

Probablemente esta sea la solución aceptada, ya que filever parece obtener la versión del archivo y no la versión del ensamblado interno. Estoy usando la versión de ensamblaje para mi aplicación porque es fácil de aumentar automáticamente. – transistor1

+0

Esta línea de comando es muy útil. Puedo usarla para actualizar el número de versión de un instalador para que coincida con la versión de la aplicación que está instalando. Una línea puede poner el número de versión en una variable de entorno –

1

Comencé a agregar un proyecto independiente que se compila por última vez y a agregar un evento de compilación posterior a ese proyecto que se ejecuta solo. Luego solo realizo mis pasos de creación de programas programáticamente allí.

Hace que sea mucho más fácil hacer cosas como esta. Luego puede inspeccionar los atributos de ensamblaje del ensamblaje que desee. Hasta ahora está funcionando bastante impresionante.

1

Desde que lo entiendo ...

Se necesita un generador para eventos posteriores a la construcción.

1. Paso: Escribir un generador

/* 
* Author: Amen RA 
* # Timestamp: 2013.01.24_02:08:03-UTC-ANKH 
* Licence: General Public License 
*/ 
using System; 
using System.IO; 

namespace AppCast 
{ 
    class Program 
    { 
     public static void Main(string[] args) 
     { 
      // We are using two parameters. 

      // The first one is the path of a build exe, i.e.: C:\pathto\nin\release\myapp.exe 
      string exePath = args[0]; 

      // The second one is for a file we are going to generate with that information 
      string castPath = args[1]; 

      // Now we use the methods below 
      WriteAppCastFile(castPath, VersionInfo(exePath)); 
     } 


     public static string VersionInfo(string filePath) 
     { 
      System.Diagnostics.FileVersionInfo myFileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(filePath); 
      return myFileVersionInfo.FileVersion; 
     } 


     public static void WriteAppCastFile(string castPath, string exeVersion) 
     { 
      TextWriter tw = new StreamWriter(castPath); 
      tw.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>"); 
      tw.WriteLine(@"<item>"); 
      tw.WriteLine(@"<title>MyApp - New version! Release " + exeVersion + " is available.</title>"); 
      tw.WriteLine(@"<version>" + exeVersion + "</version>"); 
      tw.WriteLine(@"<url>http://www.example.com/pathto/updates/MyApp.exe</url>"); 
      tw.WriteLine(@"<changelog>http://www.example.com/pathto/updates/MyApp_release_notes.html</changelog>"); 
      tw.WriteLine(@"</item>"); 
      tw.Close(); 
     } 
    } 
} 

2. Paso: Su uso como puesto de mando construir en nuestro IDE

Después de la aplicación se está ejecutando satisfactoriamente para usted:

En su IDE de desarrollo, use la siguiente línea de comando para eventos de compilación posteriores.

C:\Projects\pathto\bin\Release\AppCast.exe "C:\Projects\pathto\bin\Release\MyApp.exe" "c:\pathto\www.example.com\root\pathto\updates\AppCast.xml" 
71

Si (1) usted no desea descargar o crear un archivo ejecutable personalizado que recupera la versión de montaje y (2) no le importa editar el archivo de proyecto de Visual Studio, entonces hay una solución simple que le permite utilizar una macro que se parece a esto:

@ (Objetivos -> '% (Versión)')

@(VersionNumber) 

para lograr esto, descargar su proyecto. Si el proyecto en algún lugar define una propiedad <PostBuildEvent>, córtela del proyecto y guárdela en otro lugar temporalmente (¿Bloc de notas?). Luego, al final del proyecto, justo antes de la etiqueta final, coloque la siguiente:

<Target Name="PostBuildMacros"> 
    <GetAssemblyIdentity AssemblyFiles="$(TargetPath)"> 
    <Output TaskParameter="Assemblies" ItemName="Targets" /> 
    </GetAssemblyIdentity> 
    <ItemGroup> 
    <VersionNumber Include="@(Targets->'%(Version)')"/> 
    </ItemGroup> 
</Target> 
<PropertyGroup> 
    <PostBuildEventDependsOn> 
    $(PostBuildEventDependsOn); 
    PostBuildMacros; 
    </PostBuildEventDependsOn>  
    <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent> 
</PropertyGroup> 

Este fragmento tiene un ejemplo <PostBuildEvent> ya en ella. No se preocupe, puede restablecerlo a su evento real posterior a la construcción después de haber vuelto a cargar el proyecto.

Ahora, como prometió, la versión de montaje está disponible para su evento acumulación post con esta macro:

@(VersionNumber) 

hecho!

+1

Emite LA VERSIÓN DE MONTAJE ES: (Versión) para mí –

+1

También lo hizo para mí. El problema fue que '25' ingresó furtivamente a través del editor de compilación posterior en Visual Studio, lo verá si edita texto csproj @ (Targets -> '% 25 (Version)'). Funciona como un amuleto cuando se elimina el 25 ... – jmelhus

+0

Excelente, después de encontrar los 25 que seguían infiltrándose (¿de todos modos, se trata de WTH?) Tengo una compilación muy bien automatizada. Ahora solo necesito hacer que se active condicionalmente solo cuando se construye en modo Release y estamos dorados. ¡Gracias! –

0

Necesitaba exactamente esto para poner automáticamente el número en el archivo Léame en la carpeta de salida. Al final, como lo demostró Winston Smith, una pequeña herramienta externa es una muy buena solución para eso, y tiene la ventaja de que puedes formatearla como quieras.

Esta aplicación envía la versión formateada a la consola. Lo usé en mis eventos posteriores a la construcción para construir el archivo Léame llamándolo con >> para redirigir su salida al archivo Léame.

public class GetVerNum 
{ 
    static void Main(String[] args) 
    { 
     if (args.Length == 0) 
      return; 
     try 
     { 
      FileVersionInfo ver = FileVersionInfo.GetVersionInfo(args[0]); 
      String version = "v" + ver.FileMajorPart.ToString() + "." + ver.FileMinorPart; 
      if (ver.FileBuildPart > 0 || ver.FilePrivatePart > 0) 
       version += "." + ver.FileBuildPart; 
      if (ver.FilePrivatePart > 0) 
       version += "." + ver.FilePrivatePart; 
      Console.Write(version); 
     } 
     catch { } 
    } 
} 

Mis posterior a la generación de eventos:

<nul set /p dummyset=My Application > "$(ProjectDir)\Readme\readme-header.txt" 
"$(ProjectDir)\Readme\GetVersionNumber.exe" "$(TargetPath)" >>"$(ProjectDir)\Readme\readme-header.txt" 
echo by Nyerguds>>"$(ProjectDir)\Readme\readme-header.txt" 
echo Build date: %date% %time% >> "$(ProjectDir)\Readme\readme-header.txt" 
echo.>>"$(ProjectDir)\Readme\readme-header.txt" 
copy /b "$(ProjectDir)\Readme\readme-header.txt" + "$(ProjectDir)\Readme\readme-body.txt" "$(TargetDir)\$(ProjectName).txt" 

pongo todo el readme generar cosas relacionadas en el \ Readme \ carpeta de mi proyecto; la aplicación que contiene el código anterior, y el "readme-body.txt" que contiene las cosas reales del léame.

  • Primera línea: cree el archivo "readme-header.txt" en la carpeta \ Readme \ de mi proyecto y ponga el nombre del programa dentro de él. (El <nul set /p dummyset= es un truco que encontré aquí: Windows batch: echo without new line). También podría almacenar esta cadena en otro archivo de texto y simplemente copiar eso en "readme-header.txt" en su lugar.
  • Segunda línea: ejecute la aplicación de recuperación del número de versión con el archivo exe recién generado como parámetro y agregue su salida al archivo de encabezado.
  • Tercera línea: agregue cualquier otra cosa (en este caso, créditos) al archivo de encabezado. Esto también agrega un salto de línea hasta el final.

Estos tres juntos le dan un archivo "readme-header.txt" con "Mi Aplicación v1.2.3 por Nyerguds", seguido de un salto de línea. Luego agrego la fecha de compilación y otra línea abierta, y copio el archivo de encabezado y el archivo de cuerpo léame juntos en un archivo en la carpeta de compilación final. Tenga en cuenta que utilizo específicamente la copia binaria, de lo contrario, da resultados extraños. Debe asegurarse de que el archivo del cuerpo no contenga una marca de orden de bytes UTF-8 al comienzo, o que obtenga bytes extraños en su archivo final.

4

Esta respuesta es una modificación menor de la respuesta de Brent Arias. Su PostBuildMacro funcionó bastante bien para mí hasta una actualización de versión de Nuget.exe.

En las versiones recientes, Nuget recorta partes no significativas del número de versión del paquete para obtener una versión semántica como "1.2.3". Por ejemplo, la versión de ensamblado "1.2.3.0" está formateada por Nuget.exe "1.2.3". Y "1.2.3.1" tiene el formato "1.2.3.1" como se esperaba.

Como necesito para inferir el nombre de archivo paquete exacta generada por Nuget.exe, que uso ahora esta macro adaptated (probado en VS2015):

<Target Name="PostBuildMacros"> 
    <GetAssemblyIdentity AssemblyFiles="$(TargetPath)"> 
    <Output TaskParameter="Assemblies" ItemName="Targets" /> 
    </GetAssemblyIdentity> 
    <ItemGroup> 
    <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" /> 
    </ItemGroup> 
</Target> 
<PropertyGroup> 
    <PostBuildEventDependsOn> 
    $(PostBuildEventDependsOn); 
    PostBuildMacros; 
    </PostBuildEventDependsOn>  
    <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent> 
</PropertyGroup> 

ACTUALIZACIÓN 05/24/2017: corregí la regex de esta manera: "1.2.0.0" se traducirá a "1.2.0" y no a "1.2" como se ha codificado previamente.


Y para responder a un comentario de Ehryk Abr, se puede adaptar la expresión regular para mantener sólo una parte del número de versión. A modo de ejemplo para mantener "MAJOR.MINOR", reemplace:

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" /> 

Por

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^([^\.]+)\.([^\.]+)(.*)$&quot;, &quot;$1.$2&quot;))" /> 
Cuestiones relacionadas