miércoles, 9 de febrero de 2011

Versionado de ensamblados de .NET

Uno de los temas que menos cuidado se le suele prestar en el desarrollo de software es el del versionado de componentes. Y es que gracias a las versiones, podemos saber exactamente cuando se compiló un ensamblado (e incluso saber la etiqueta de la versión de código fuente que la generó) y si hay otros ensamblados que son compatibles.

En .NET, las versiones se establecen en el fichero AssemblyInfo.cs que se puede encontrar dentro de la carpeta de “Properties” del proyecto:

    1 using System.Reflection;

    2 using System.Runtime.InteropServices;

    3 

    4 // General Information about an assembly is controlled through the following

    5 // set of attributes. Change these attribute values to modify the information

    6 // associated with an assembly.

    7 [assembly: AssemblyTitle("Project Title")]

    8 [assembly: AssemblyDescription("Some description")]

    9 [assembly: AssemblyConfiguration("Debug")]

   10 [assembly: AssemblyCompany("My Company of Software")]

   11 [assembly: AssemblyProduct("Product Name")]

   12 [assembly: AssemblyCopyright("Copyright © My Company, 2011")]

   13 [assembly: AssemblyTrademark("")]

   14 [assembly: AssemblyCulture("")]

   15 

   16 // Setting ComVisible to false makes the types in this assembly not visible

   17 // to COM components.  If you need to access a type in this assembly from

   18 // COM, set the ComVisible attribute to true on that type.

   19 [assembly: ComVisible(false)]

   20 

   21 // The following GUID is for the ID of the typelib if this project is exposed to COM

   22 [assembly: Guid("f446dcb3-68fb-49d9-a057-fe50383ce4c3")]

   23 

   24 // Version information for an assembly consists of the following four values:

   25 //

   26 //      Major Version

   27 //      Minor Version

   28 //      Build Number

   29 //      Revision

   30 //

   31 // You can specify all the values or you can default the Build and Revision Numbers

   32 // by using the '*' as shown below:

   33 // [assembly: AssemblyVersion("1.0.*")]

   34 [assembly: AssemblyVersion("1.0.0.0")]

   35 [assembly: AssemblyFileVersion("1.0.0.0")]

Las últimas líneas del fichero están relacionadas con la versión del ensamblado y con la versión del fichero.

AssemblyFileVersion

Su principal objetivo es el de identificar de manera uniequivoca a un ensamblado. A partir de los valores de la versión podemos saber la versión de código fuente que compiló el ensamblado. Tambien nos permite reconocer si se trata de una versión de “Service Pack” o de “Hotfix”.

Las versiones de un fichero están basadas en un código de 4 números. El uso de cada uno de los valores depende de cada equipo de desarrollo. Personalmente, les doy el siguiente significado a los valores de versión:

  • Major Version: Versión principal de producto. No suele cambiar a lo largo de todo el ciclo de desarrollo salvo que el desarrollo requiera rehacer todo el código desde cero. Evidentemente no se mantiene ninguna compatibilidad con versiones anteriores.
  • Minor Version: Cambio importante en las interfaces del ensamblado que hace que se rompa la compatibilidad con versiones anteriores. Al igual que la versión principal, este valor se cambia de manera manual.
  • Build Number: Número calculado automáticamente por el proceso de compilación en orden creciente de manera que dos compilaciones seguidas no generen el mismo valor. Existen muchos esquemas de generación de este valor. Quizá el más conocido es el de basado en el número de días transcurridos desde el 1 de Enero de 2000.
  • Revision: Número tambien generado automáticamente por el sistema de compilación. Por ejemplo, si se generan varias versiones el mismo día, la versión de Build no cambia, por lo que está va incrementandose de uno en uno por cada compilación.

AssemblyVersion

Los ensamblados de .NET usan esta versión para enlazarse con otros ensamblados. Esto es, cuando por ejemplo el ensamblado A.dll referencia al ensablado B.dll con version “1.0.0.0”, si sustituimos este último ensamblado por la versión “1.0.0.1”, A.dll no será capaz de encontrarlo pues espera encontrar la versión con la que fue compilado. Por este motivo no se debe dejar a la ligera la opción de Visual Studio de autoincrementar la versión del ensamblado en cada compilación.

El significado de los valores de la versión puede ser:

  • Major Version: Se actualiza de manera manual. Normalmente significa un cambio radical en la estructura del ensamblado. Por ejemplo porque se ha vuelto a reescribir gran parte del código. Normalmente implica rompe la compatibilidad con versiones anteriores.
  • Minor Version: Se han añadido nuevas funcionalidades al ensamblado. Y por tanto, puede suponer cambios en la interfaz pero tratando de mantener la compatibilidad con versiones anteriores. Su modificación es manual.
  • Build Number: Valor calculado automatiamente y que identifica el número de compilación realizado sobre el código fuente.
  • Revision: Normalmente identifican ligeros cambios sobre el código. Por ejemplo solución de hotfixes. Aquellos ensamblados donde únicamente varíe el número de revisión son totalmente intercambiables entre sí.

AssemblyInformationalVersion

Este es otro atributo que puede ser usado dentro de AssemblyInfo. Su objetivo es el de exponer una versión del ensamblado a nivel de producto, documentación, instalador, etc. En ningún momento esta información es usada por Windows o por otros ensamblados de .NET.

Esta versión se identifica con tres valores:

  • Major Release: Normalmente asociada a la versión principal de producto y no suele variar en todo el ciclo de desarrollo del proyecto salvo que se tenga que rehacer todo el contenido del ensamblado.
  • Minor Release: Este número se asocia a incrementos en la funcionalidad del ensamblado.
  • Maintenance Release: Los incrementos en este valor suelen reflejar pequeños cambios normalmente asociados a correción de errores, rendimiento o estabilidad. Los clientes del ensamblado no aprecian un cambio en la funcionalidad importada.

2 comentarios:

  1. Hola Oscar,

    Muy buen artículo, coincido en casi todo, incluyendo la cuenta de días desde el 2000. Pero los números Build Number y Revision de AssemblyVersion no los calculo automáticamente, para que no se pierdan las dependencias existentes. Normalmente los tengo siempre a 0.0, hasta ahora nunca he necesitado cambiarlos. Donde sí los cambio automáticamente es en AssemblyFileVersion, que no afecta a la dependencia, es informativo. Por ejemplo:

    [assembly: AssemblyVersion("3.0.0.0")]
    [assembly: AssemblyFileVersion("3.0.4058.1")] //Segunda build de hoy

    Otra regla implícita es que siempre hago coincidir versiones mayor y menor de los dos atributos. Creo que es una regla de aplicación general, corrígeme si me equivoco.

    El último atributo AssemblyInformationalVersion no lo conocía y no acabo de entenderlo, creo que necesito un ejemplo.

    ResponderEliminar
  2. Hola Pablo,

    Gracias por el comentario. Efectivamente, si necesitas entregar los ensamblados a otros desarrolladores y les cambias los números de Build y Revision del AssemblyVersion, implicitamente les estás obligando a recompilar sus proyectos para que acepten las nuevas versiones. El problema puede ser mayor si además quieres instalar los ensamblados en el GAC y los tienes que firmar con un Strong Name.

    Una situación que te puede venir bien el cambio de versión de Build y Revision de forma automática es si quieres evitar que los usuarios de las aplicaciones jueguen con distintas versiones de ensamblados. Dado que cada vez que recompilas la solución se rehacen las referencias a cada versión de ensamblado.

    Tambien puedes desactivar en Visual Studio el parámetro de "Specific Version" dentro de las propiedades del ensamblado. De esta forma mientras no cambien las interfaces de los ensamblados el sistema no usará la versión de ensamblado.

    En definitiva estoy de acuerdo contigo en que hay casos en los que es mejor dejar las versiones de Build y Revision fijas. Tambien en que coincidan las Major y Minor version de ambas versiones.

    Respecto a AssemblyInformationalVersion, se centra en recoger la versión del producto. Es, digamos, la versión que les gusta poner a la gente de Marketing/Ventas o Soporte técnico a sus versiones de producto y que es independiente del contenido del ensamblado.
    Por ejemplo, imaginate que estas trabajando en la versión 2.0 de un proyecto que está compuesto de muchos ensamblados. Normalmente estos ensamblados tendrán la versión de Assembly y de FileAssembly 2.0.*.*. porque han evolucionado desde la versión 1.0 del producto. Pero además resulta que el la versión 2.0 de tu producto has añadido ensamblados nuevos con la versión 1.0.*.* de ensamblado. Sin embargo pertenecen a la versión de producto 2.0.
    Por tanto, el AssemblyInformationalVersion sirve para agruparlos por versión de producto.

    Un saludo,

    Oscar

    ResponderEliminar