Organización de repositorios Git con submódulos anidados comunes

50

Soy un gran admirador de los submódulos Git . Me gusta poder rastrear una dependencia junto con su versión, para que pueda retroceder a una versión anterior de su proyecto y tener la versión correspondiente de la dependencia para construir de forma segura y limpia. Además, es más fácil lanzar nuestras bibliotecas como proyectos de código abierto, ya que el historial de las bibliotecas es independiente del de las aplicaciones que dependen de ellas (y que no serán de código abierto).

Estoy configurando el flujo de trabajo para múltiples proyectos en el trabajo, y me preguntaba cómo sería si tomáramos este enfoque un poco extremo en lugar de tener un solo proyecto monolítico. Rápidamente me di cuenta de que es una lata potencial de gusanos en realidad el uso de sub-módulos.

Supongamos un par de aplicaciones: studioy player, y bibliotecas dependientes core, graphy network, donde las dependencias son las siguientes:

  • core es independiente
  • graphdepende de core(submódulo en ./libs/core)
  • networkdepende de core(submódulo en ./libs/core)
  • studiodepende de graphy network(submódulos en ./libs/graphy ./libs/network)
  • playerdepende de graphy network(submódulos en ./libs/graphy ./libs/network)

Supongamos que estamos usando CMake y que cada uno de estos proyectos tiene pruebas unitarias y todos los trabajos. Cada proyecto (incluido studioy player) debe poder compilarse de forma independiente para realizar métricas de código, pruebas unitarias, etc.

La cosa es que, de forma recursiva git submodule fetch, obtienes la siguiente estructura de directorios:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/graph/
studio/libs/graph/libs/         (sub-module depth: 2)
studio/libs/graph/libs/core/
studio/libs/network/
studio/libs/network/libs/       (sub-module depth: 2)
studio/libs/network/libs/core/

Tenga en cuenta que corese clona dos veces en el studioproyecto. Además de este desperdicio de espacio en disco, tengo un problema de sistema de compilación porque estoy compilando coredos veces y potencialmente obtengo dos versiones diferentes de core.

Pregunta

¿Cómo organizo los submódulos para obtener la dependencia versionada y la compilación independiente sin obtener múltiples copias de submódulos anidados comunes?

Solución posible

Si la dependencia de la biblioteca es algo así como una sugerencia (es decir, en una forma "conocida por funcionar con la versión X" o "solo la versión X es oficialmente compatible") y las aplicaciones o bibliotecas dependientes potenciales son responsables de construir con la versión que deseen, entonces Me imagino el siguiente escenario:

  • Tenga el sistema de compilación graphy networkdígales dónde encontrar core(por ejemplo, a través de una ruta de inclusión del compilador). Defina dos objetivos de compilación, "independiente" y "dependencia", donde "independiente" se basa en "dependencia" y agrega la ruta de inclusión para apuntar al coresubmódulo local .
  • Introducir una dependencia adicional: studioon core. Luego, studiocompila core, establece la ruta de inclusión a su propia copia del coresubmódulo, luego construye graphy networken modo de "dependencia".

La estructura de carpetas resultante se ve así:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/core/
studio/libs/graph/
studio/libs/graph/libs/         (empty folder, sub-modules not fetched)
studio/libs/network/
studio/libs/network/libs/       (empty folder, sub-modules not fetched)

Sin embargo, esto requiere un poco de magia del sistema de compilación (estoy bastante seguro de que esto se puede hacer con CMake) y un poco de trabajo manual por parte de las actualizaciones de la versión (la actualización graphtambién puede requerir la actualización corey networkobtener una versión compatible coreen todos los proyectos) .

Tiene alguna idea sobre esto?

André Caron
fuente
Tenga en cuenta que este problema no es específico de cmake: existe para cualquier sistema de compilación, ¡incluido ningún sistema! (es decir, cuando se pretende que el superproyecto solo agregue las fuentes de la biblioteca; que incluye bibliotecas de solo encabezado)
MM

Respuestas:

5

Llego muy tarde a esta fiesta, pero su pregunta aún no parece tener una respuesta completa, y es un éxito bastante destacado de Google.

Tengo exactamente el mismo problema con C ++ / CMake / Git / Submodules y tengo un problema similar con MATLAB / Git / Submodules, lo que tiene una rareza adicional porque MATLAB no está compilado. Me encontré con este video recientemente, que parece proponer una "solución". No me gusta la solución, porque esencialmente significa tirar submódulos, pero elimina el problema. Es tal como lo recomienda @errordeveloper. Cada proyecto no tiene submódulos. Para construir un proyecto, cree un superproyecto para construirlo e inclúyalo como hermano de sus dependencias.

Entonces su proyecto para el desarrollo graphpodría verse así:

buildgraph/graph
buildgraph/core

y luego tu proyecto para estudio podría ser:

buildstudio/studio
buildstudio/graph
buildstudio/network
buildstudio/core

Los superproyectos son solo un CMakeLists.txtsubmódulo principal y un montón de submódulos. Pero ninguno de los proyectos tiene submódulos.

El único costo que veo para este enfoque es la proliferación de "superproyectos" triviales que solo se dedican a construir sus proyectos reales. Y si alguien obtiene uno de sus proyectos, no hay una manera fácil de saber sin encontrar también el superproyecto, cuáles son sus dependencias. Eso podría hacer que se sienta realmente feo en Github, por ejemplo.

chadsgilbert
fuente
1

Supongo que cuando integra ambos graphy networksubmódulos studio, siempre debe tener la misma versión de coreen un momento dado en la historia de studio. Me gustaría vincular el studio/libs/coresubmódulo en studio/libs/{graph,network}/libs.

Actualizar:

Creé múltiples repositorios con las dependencias que usted indicó:

./core      <--- (v2)
./graph
./graph/libs
./graph/libs/core  <--- (v2)
./graph/.gitmodules
./network
./network/libs
./network/libs/core  <--- (v1)
./network/.gitmodules
./studio
./studio/libs
./studio/libs/graph
./studio/libs/graph/libs
./studio/libs/graph/libs/core <--- (v1)
./studio/libs/graph/.gitmodules
./studio/libs/network
./studio/libs/network/libs
./studio/libs/network/libs/core  <--- (v1)
./studio/libs/network/.gitmodules
./studio/studio
./studio/.gitmodules

v1y v2son dos versiones diferentes de core. graphmaneja la versión 2, mientras que networknecesita algo de trabajo y está atascado en la versión 1. En studio, las versiones locales incrustadas de coreambos puntos v1para tener un programa de trabajo. Ahora, aparte de la perspectiva de construcción, todo funciona bien con submódulos.

Ahora puedo eliminar el siguiente directorio:

./studio/libs/network/libs/core

Y reemplácelo con un enlace simbólico:

./studio/libs/network/libs/core@ -> ../../graph/libs/core/

Confirmo localmente este cambio y pierdo la capacidad de tener dos versiones separadas de coreadentro studio, pero solo construyo coreuna vez. Cuando estoy listo para actualizar v2, puedo hacer:

 git submodule update # (--rebase ?)

... dentro de studio / libs / network.

volcado de memoria
fuente
La idea del enlace simbólico cruzó por mi mente, pero no es una solución. Si vincula desde graph/libs/coreel exterior, no está utilizando el submódulo. Si vincula desde studio/libs/coreuna de las bibliotecas propias del submódulo, ¿cuál elige, grapho network? Además, ¿qué sucede cuando tiene tres o más capas de profundidad? Finalmente, ¿qué pasa si corepuede haber una variedad de revisiones? No es obvio que desea vincular a cualquiera de las versiones de coreeso graphy networkestá utilizando.
André Caron
"Cuál eliges ?" : coresería un submódulo obtenido de la corebiblioteca original , actualizado a una versión que sea compatible con ambos graphy network(debe decidir cuál es el correcto). Los enlaces simbólicos se agregarían en los submódulos locales graphy network(sin recuperar).
coredump
1
Los enlaces simbólicos que propone agregar graphy networkapuntarían fuera de su propio repositorio (por ejemplo, en otro lugar del studioproyecto). ¿Cómo saben cuándo usar su propio submódulo versus cuándo usar el enlace simbólico? Quizás debería agregar un ejemplo para demostrar su línea de pensamiento.
André Caron
0

Lo aplanaría para tener una profundidad de submódulo de solo uno y un repositorio que contendría todos los módulos como submódulos y nada más aparte de README y los scripts de compilación. Habría un script de compilación separado para cada uno de los paquetes que vinculan sus dependencias. De lo contrario, puede tener un repositorio separado para un paquete.

desarrollador de errores
fuente
1
No estoy seguro de si esto estaba claro en mi publicación, pero tengo varias aplicaciones que dependen de las mismas bibliotecas y no quiero duplicar los scripts de compilación para bibliotecas en todas las aplicaciones.
André Caron
3
Debe elaborar su respuesta para demostrar cómo aborda los diferentes problemas. No me queda claro exactamente cómo vincular dependencias dado que, según el contexto, las bibliotecas dependientes no están en la misma ubicación.
André Caron
0

No usaría submódulos.

Es tentador, igual que solía ser el caso con svn-externals. Sin embargo, ¿puede estar seguro de que todos los proyectos que vincula siguen en el mismo lugar en un año? ¿Qué tal en cinco?

Por lo tanto, simplemente estoy copiando todas las dependencias requeridas en mi proyecto. Esto significa que mientras mi repositorio sea válido, puedo verificar el estado exacto.

Básicamente, tengo una estructura de carpetas de la siguiente manera:

myproject/... [sources etc]
ext/ [third-party dependencies]


e.g. ext/boost, ext/cppunit

Si bien esto no es muy bueno desde la perspectiva del espacio en disco, valoro la garantía de que puedo verificar cada estado registrado siempre que el repositorio esté disponible mucho más alto.

Además, hay un montón de problemas con los submódulos como se describe aquí

Wilbert
fuente
Estoy seguro de que están en la ubicación correcta porque los mantengo a todos :-) Además, tenga cuidado al copiar proyectos debido a las condiciones de redistribución.
André Caron
OK, eso reduce el problema. Y licencias: sí, hay que tener cuidado, pero ese es un problema completamente diferente.
Wilbert
0

Enfrentando exactamente el mismo problema aquí. Una de las soluciones podría ser tener un poco de recompra libsque sostendría core, network, graphcomo submódulos y CMakeLists sólo que contarían cada una de las librerías donde encontrar sus dependencias. Cada aplicación ahora tendría libscomo submódulo y usaría solo las librerías necesarias.

La prueba de cada lib podría configurarse de 2 maneras:

  • Tener core_testing, graph_testing, network_testing como aplicaciones separadas
  • Implemente libs probadas en servidores de prueba y encuéntrelas mientras ejecuta pruebas usando cmake
Max
fuente
¿No hace esto que todas las bibliotecas estén disponibles para todas las otras bibliotecas?
André Caron
Por defecto sí. Pero eso podría decidirse en cmakelists de nivel libs. Si graphno necesita saber network, no pase networkcosas relacionadas con graphsubdirigir
Max