¿Cuál es una buena forma de organizar proyectos con dependencias compartidas en Mercurial? [cerrado]

Actualmente, estoy pasando de un sistema de control de versiones heredado y estoy moviendo el proyecto de mi grupo a mercurial. Como ejemplo de los tipos de código que estoy moviendo, tengo una solución de Visual Studio de más de 25 proyectos, que contiene varias áreas de aplicación separadas que dependen de un código común. Mirando Stack Overflow, la pregunta más cercana que encontré fue esta, pero simplemente mencionó el control de versiones. Estoy buscando un poco más de consejo sobre técnicas de implementación específicas del uso de Mercurial para administrar estas dependencias.

Una vista simplificada de las dependencias se parece a la siguiente. (Esto es solo para ilustración y ejemplo; las dependencias reales son significativamente más complejas pero de naturaleza similar).

                     Common Lib 1
                    /      |      \
                ----       |       -----   
               /           |        \   \
            App 1    Common Lib 2    \ App 2
                       /   |   \      \
                -------    |    ------ |
               /           |          \|
             App 3       App 4      App 5

Los módulos Common Lib serían código compartido, esto sería una DLL o SO o alguna otra biblioteca que se usaría entre todas las aplicaciones simultáneamente, tanto en la compilación como en el tiempo de ejecución. De lo contrario, las aplicaciones podrían ejecutarse de forma independiente entre sí.

Tengo un par de objetivos con la configuración de mis repositorios mercuriales:

  • Dé a cada aplicación o grupo de componentes significativo su propio repositorio.
  • Haga que cada repositorio sea autónomo.
  • Haga que la suma total del proyecto sea autónoma.
  • Facilite la creación de todo el código base a la vez. (eventualmente, todos estos programas y bibliotecas terminan en un solo instalador).
  • Debe ser sencillo.

Otro punto es que tengo un servidor configurado donde tengo repositorios separados para cada uno de estos proyectos.

Veo un par de formas de diseñar estos proyectos.

1. Cree un repositorio "Shell" que contenga todo.

Esto usaría subrepos basados ​​en URL (por ejemplo, en .hgsub, haría algo como App1 = https://my.server/repo/app1.) Presentado, se vería así:

+---------------------------+
| Main Repository           |
| | +---------------------+ |
| +-| Build               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 1        | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 2        | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 1               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 2               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 3               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 4               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 5               | |
|   +---------------------+ |
+---------------------------+

Cada carpeta principal del repositorio de shell contendría una subpoblación, una para cada área del proyecto. Las dependencias serían relativas: por ejemplo, dado que la aplicación 4 necesita Common Lib 2, simplemente usaría rutas relativas para hacer referencia a esa biblioteca común.

Ventajas de este enfoque:

  • Cada biblioteca se elimina una y solo una vez.
  • Los subreos de Mercurial se asegurarán de que se use la misma versión de la biblioteca en todos los proyectos automáticamente, ya que solo existe una versión de ese subrepo en el proyecto.
  • Es fácil encontrar cada recurso.

Contras de este enfoque:

  • No puedo trabajar en una aplicación de forma independiente. Por ejemplo, si trabajo en la aplicación 2 y necesita un cambio en las bibliotecas comunes, todas las demás aplicaciones tendrán que realizar esos cambios ahora mismo.
  • Si extraigo un repositorio de aplicaciones por sí solo, tengo que averiguar (o saber) qué otros repositorios dependientes requiere a mano si quiero construirlo.
  • Las dependencias no están muy separadas; sería tentador insertar una nueva función en cualquier lugar, ya que era fácil obtener todas las funciones.

2. Que los subrepos dependientes estén totalmente contenidos.

En este enfoque, cada aplicación tendría su propio repositorio (como antes) pero esta vez también contendría subrepositorios: uno para su propia fuente y otro para cada subrepositorio dependiente. Un repositorio general contendría cada uno de estos repositorios de proyectos y sabría cómo construir la solución completa. Esto tendría el siguiente aspecto:

+-----------------------------------------------------------------------+
| Main Repository                                                       |
| +--------------------+ +--------------------+ +--------------------+  |
| | Build              | | Common Lib 1       | | Common Lib 2       |  |
| +--------------------+ | | +--------------+ | | | +--------------+ |  |
|                        | +-| Lib 1 Source | | | +-| Common Lib 1 | |  |
|                        |   +--------------+ | | | +--------------+ |  |
|                        |                    | | | +--------------+ |  |
|                        |                    | | +-| Lib 2 Source | |  |
|                        |                    | |   +--------------+ |  |
|                        +--------------------+ +--------------------+  |
| +--------------------+ +--------------------+ +---------------------+ |
| | App 1              | | App 2              | |  App 3              | |
| | | +--------------+ | | | +--------------+ | |  | +--------------+ | |
| | +-| Common Lib 1 | | | +-| Common Lib 1 | | |  +-| Common Lib 2 | | |
| | | +--------------+ | | | +--------------+ | |  | +--------------+ | |
| | | +--------------+ | | | +--------------+ | |  | +--------------+ | |
| | +-| App 1 Source | | | +-| App 2 Source | | |  +-| App 3 Source | | |
| |   +--------------+ | |   +--------------+ | |    +--------------+ | |
| +--------------------+ +--------------------+ +---------------------+ |
| +--------------------+ +--------------------+                         |
| | App 4              | | App 5              |                         |
| | | +--------------+ | | | +--------------+ |                         |
| | +-| Common Lib 2 | | | +-| Common Lib 1 | |                         |
| | | +--------------+ | | | +--------------+ |                         |
| | | +--------------+ | | | +--------------+ |                         |
| | +-| App 4 Source | | | +-| Common Lib 2 | |                         |
| |   +--------------+ | | | +--------------+ |                         |
| +--------------------+ + | +--------------+ |                         |
|                        | +-| App 5 Source | |                         |
|                        |   +--------------+ |                         |
|                        +--------------------+                         |
+-----------------------------------------------------------------------+

Pros:

  • Cada aplicación se puede construir por sí misma, independientemente unas de otras.
  • Las versiones dependientes de las bibliotecas se pueden rastrear por aplicación, en lugar de hacerlo globalmente. Se necesita un acto explícito de insertar un subrepo en el proyecto para agregar una nueva dependencia.

Contras:

  • Al realizar la compilación final, es posible que cada aplicación esté usando una versión diferente de una biblioteca compartida. (es posible que necesite escribir herramientas para sincronizar los subrepos comunes de lib. Eww.)
  • Si quiero construir la fuente completa, termino eliminando bibliotecas compartidas varias veces. En el caso de Common Lib 1, tendría que tirar de él ocho (!) Veces.

3. No incluya dependencias en absoluto como subrepos; tráigalos como parte de la compilación.

Este enfoque se parecería mucho al enfoque 1, excepto que las bibliotecas comunes solo se extraerían como parte de la compilación. Cada aplicación sabría qué repositorios necesitaba y los colocaría en la ubicación común.

Pros:

  • Cada aplicación podría construirse por sí misma.
  • Las bibliotecas comunes solo deberían extraerse una vez.

Contras:

  • Tendríamos que realizar un seguimiento de las versiones de las bibliotecas que utiliza actualmente cada aplicación. Esto duplica las características de subrepo.
  • Tendríamos que construir una infraestructura para respaldar esto, lo que significa que se incluirán más cosas en los scripts de compilación. Puaj.

4. ¿Qué más?

¿Existe otra forma de manejarlo? ¿Una mejor manera? ¿De qué formas ha intentado y tenido éxito, qué formas ha intentado pero odiado? Actualmente me estoy inclinando hacia 1, pero la falta de independencia de la aplicación, cuando debería ser posible, realmente me molesta. ¿Hay alguna manera de obtener la agradable separación del método 2 sin la pesadilla masiva de extracción de código duplicado y mantenimiento de dependencias, sin tener que escribir scripts para manejarlo (como en la opción 3)?

preguntado el 16 de mayo de 11 a las 17:05

Recientemente hubo una discusión sobre este tema en la lista de correo de desarrolladores: news.gmane.org/… -

¿Qué herramienta usaste para crear estos diagramas ASCII? -

@caveman: Slick Edit. Un editor de texto realmente genial. La selección con el botón derecho en ese editor es realmente útil para mover columnas de texto donde le gusten. :) -

@user593358 emacs tiene picture-mode y artist-mode. -

3 Respuestas

La gestión de dependencias es un aspecto importante de la organización de un proyecto, en mi opinión. Expuso con gran detalle varias soluciones, basadas en la función subrepos de Mercurial, y estoy de acuerdo con todos los pros / contras que dio.

Creo que los SCM no son adecuados para la gestión de dependencias. Prefiero tener una herramienta dedicada para eso (esta sería su solución n ° 3).

Mi proyecto actual está en Java. Fue construido con Hormiga Apache, y primero configuré Apache Ivy como herramienta de gestión de dependencias. Al final, la instalación consistió en algunos archivos de configuración de Ivy en un directorio compartido y un archivo XML que enumera las dependencias para cada módulo del proyecto. Ivy puede ser invocado por objetivos Ant, así que agregué dos nuevas acciones en cada módulo: "resolver dependencias" y "implementar el artefacto construido". La implementación agrega el resultado del buid (llamado artefacto) en el directorio compartido. La resolución de dependencias significa resolver transitivamente las dependencias del módulo y copiar los artefactos resueltos en la carpeta "lib" de las fuentes del módulo.

Esta solución es aplicable a un proyecto de C ++, ya que Ivy no es específico para administrar las dependencias de Java: los artefactos pueden ser cualquier cosa. En C ++, los artefactos producidos por un módulo serían:

  1. un so / dll en tiempo de ejecución
  2. los archivos de encabezado en tiempo de compilación.

Esta no es una solución perfecta: Ivy no es fácil de configurar, aún debe decirle a su script de compilación qué dependencias usar y no tiene acceso directo a las fuentes de las dependencias para fines de depuración. Pero terminas con repositorios SCM independientes.

En nuestro proyecto, cambiamos de Ant + Ivy a Apache Maven, que se encarga tanto de la construcción como de la gestión de dependencias. Los artefactos se despliegan en un Apache Archiva en lugar de una carpeta compartida. Esta es una gran mejora, pero funcionará bien solo para proyectos Java.

respondido 20 mar '13, 14:03

parece que esta es exactamente la dirección en la que vamos a ir. Gracias por tus pensamientos. :) - Robert P

Lo que quiere hacer es tener cada proyecto en su propio directorio como en (1). Luego, etiqueta las versiones de trabajo de sus dependencias y guarda la etiqueta en algún archivo para compilar como:

App1/.dependencies:
CommonLib1 tag-20100515
CommonLib2 tag-20100510

App2/.dependencies:
CommonLib1 tag-20100510
CommonLib2 tag-20100510

Luego, usa sus scripts de compilación para construir las bibliotecas basadas en la etiqueta específica e incluir esas bibliotecas compiladas como objetos derivados para sus aplicaciones. Si el tiempo de compilación es un problema, puede tener la versión etiquetada que está en uso para esas bibliotecas preconstruidas y guardadas en algún lugar.

Nota (los principios de diseño son los mismos si se diseña un esquema de base de datos, un modelo de objeto o una compilación de producto):

  • No vincular al código en otros proyectos (rompe la encapsulación)
  • No tenga varias copias de las bibliotecas en su repositorio (modularidad)

contestado el 17 de mayo de 11 a las 13:05

Resolvimos un problema similar usando la subversión.

Cada aplicación y cada Common Lib tienen sus propios repositorios.

Cada aplicación tiene un directorio Libs que contienen las DLL dependientes.

por lo que la aplicación solo recibe una actualización de Common Lib si se proporciona un nuevo conjunto de dlls.

sin embargo, la actualización de la carpeta lib no es trivial porque las subdll dependientes deben coincidir con la versión correcta.

contestado el 18 de mayo de 11 a las 16:05

Los objetos derivados como dlls no deben verificarse en el control de versiones, sino que deben ser el resultado de una compilación. - Jiri Klouda

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.