Organizando repositórios Git com submódulos aninhados comuns

50

Sou um grande fã dos submódulos do Git . Eu gosto de poder rastrear uma dependência junto com sua versão, para que você possa reverter para uma versão anterior do seu projeto e ter a versão correspondente da dependência para criar de forma segura e limpa. Além disso, é mais fácil lançar nossas bibliotecas como projetos de código aberto, pois o histórico das bibliotecas é separado daquele dos aplicativos que dependem deles (e que não serão de código aberto).

Estou configurando o fluxo de trabalho para vários projetos no trabalho e fiquei imaginando como seria se adotássemos essa abordagem um pouco de extremo, em vez de ter um único projeto monolítico. Eu rapidamente percebi que não é uma lata potencial de vermes em realmente usando sub-módulos.

Supondo um par de aplicativos: studioe player, e bibliotecas dependentes core, graphe network, onde as dependências são as seguintes:

  • core é autônomo
  • graphdepende core(submódulo em ./libs/core)
  • networkdepdends on core(submódulo em ./libs/core)
  • studiodepende graphe network(sub-módulos em ./libs/graphe ./libs/network)
  • playerdepende graphe network(sub-módulos em ./libs/graphe ./libs/network)

Suponha que estamos usando o CMake e que cada um desses projetos tenha testes de unidade e todos os trabalhos. Cada projeto (incluindo studioe player) deve poder ser compilado de forma independente para executar métricas de código, testes de unidade etc.

A coisa é, uma recursiva git submodule fetch, então você obtém a seguinte estrutura de diretório:

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/

Observe que coreé clonado duas vezes no studioprojeto. Além desse desperdício de espaço em disco, tenho um problema no sistema de compilação porque estou construindo coreduas vezes e potencialmente recebo duas versões diferentes do core.

Pergunta, questão

Como organizo os submódulos para obter a dependência com versão e a construção autônoma sem obter várias cópias dos submódulos aninhados comuns?

Solução possível

Se a dependência da biblioteca é uma sugestão (por exemplo, "conhecida por funcionar com a versão X" ou "apenas a versão X é oficialmente suportada") e os aplicativos ou bibliotecas dependentes em potencial são responsáveis ​​por criar a versão que quiserem, então Eu poderia imaginar o seguinte cenário:

  • Tenha o sistema de compilação graphe networkdiga a eles onde encontrar core(por exemplo, através de um caminho de inclusão do compilador). Defina dois destinos de construção, "autônomo" e "dependência", em que "autônomo" é baseado em "dependência" e adiciona o caminho de inclusão para apontar para o coresubmódulo local .
  • Introduzir uma dependência extra: studioon core. Em seguida, studiocria core, define o caminho de inclusão para sua própria cópia do coresubmódulo e depois cria graphe networkno modo "dependência".

A estrutura de pastas resultante se parece com:

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)

No entanto, isso requer alguma mágica do sistema de compilação (estou bastante confiante de que isso pode ser feito com o CMake) e um pouco de trabalho manual por parte das atualizações de versão (a atualização graphtambém pode exigir atualização coree networkobter uma versão compatível coreem todos os projetos) .

Alguma idéia sobre isso?

André Caron
fonte
Observe que esse problema não é específico do cmake: ele existe para qualquer sistema de construção, incluindo nenhum sistema! (ou seja, quando é pretendido que a super-projeto basta adicionar as fontes da biblioteca, o que inclui bibliotecas somente cabeçalho)
MM

Respostas:

5

Estou muito atrasado para esta festa, mas sua pergunta ainda não parece ter uma resposta completa, e é um sucesso bastante proeminente do google.

Eu tenho exatamente o mesmo problema com C ++ / CMake / Git / Submodules e tenho um problema semelhante com MATLAB / Git / Submodules, que obtém alguma estranheza extra porque o MATLAB não é compilado. Me deparei com este vídeo recentemente, que parece propor uma "solução". Não gosto da solução, porque significa essencialmente jogar fora os submódulos, mas elimina o problema. É exatamente como a @errordeveloper recomenda. Cada projeto não possui submódulos. Para criar um projeto, crie um superprojeto para construí-lo e inclua-o como um irmão de suas dependências.

Portanto, seu projeto de desenvolvimento graphpode parecer com:

buildgraph/graph
buildgraph/core

e seu projeto para o estúdio pode ser:

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

Os superprojetos são apenas um CMakeLists.txtsubmodulo principal e um monte. Mas nenhum dos projetos possui submódulos.

O único custo que vejo nessa abordagem é a proliferação de "superprojetos" triviais que são dedicados apenas à construção de seus projetos reais. E se alguém se apossar de um de seus projetos, não há uma maneira fácil de saber sem encontrar também o superprojeto, quais são suas dependências. Isso pode torná-lo realmente feio no Github, por exemplo.

chadsgilbert
fonte
1

Suponho que, quando você integra os dois graphe os networksubmódulos studio, sempre deve ter a mesma versão coreem um dado momento da história do studio. Gostaria de vincular o studio/libs/coresubmódulo em studio/libs/{graph,network}/libs.

Atualizar:

Criei vários repositórios com as dependências que você declarou:

./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

v1e v2são duas versões diferentes do core. graphlida com a versão 2, enquanto networkprecisa de algum trabalho e está travado na versão 1. Na studio, as versões incorporadas locais de coreambos os pontos apontam v1para ter um programa de trabalho. Agora, além da perspectiva da construção, tudo funciona bem com os submódulos.

Agora posso remover o seguinte diretório:

./studio/libs/network/libs/core

E substitua-o por um link simbólico:

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

Eu comprometo localmente essa alteração e perco a capacidade de ter duas versões separadas do coreinside studio, mas eu só construo coreuma vez. Quando estiver pronto para atualizar v2, posso:

 git submodule update # (--rebase ?)

... dentro do estúdio / libs / rede.

coredump
fonte
A ideia do link simbólico passou pela minha cabeça, mas não é uma solução. Se você vincula de graph/libs/corefora, não está usando o submódulo. Se você vincular studio/libs/corea uma das bibliotecas do submódulo, qual você escolherá graphou network? Além disso, o que acontece quando tem três ou mais camadas de profundidade? Finalmente, o que se corepode ser uma série de revisões. Não é óbvio que você deseja vincular a qualquer versão do coreque graphe networkestá usando.
André Caron
"Qual você escolhe ?" : coreseria um sub-módulo buscado na corebiblioteca original , atualizado para uma versão compatível com ambos graphe network(você deve decidir qual é o melhor). Os links simbólicos seriam adicionados no local graphe nos networksubmódulos (não buscados).
Coredump #
11
Os links simbólicos que você propõe adicionar graphe networkapontariam para fora de seu próprio repositório (por exemplo, em outro lugar do studioprojeto). Como eles sabem quando usar seu próprio submódulo versus quando usar o link simbólico? Talvez você deva adicionar um exemplo para demonstrar sua linha de pensamento.
André Caron
0

Eu o aplainaria para ter profundidade de submódulo de apenas um e ter um repositório que conteria todos os módulos como submódulos e nada além do README e dos scripts de construção. Haveria um script de compilação separado para cada um dos pacotes vinculando suas dependências. Caso contrário, você pode ter um repositório separado para um pacote.

errordeveloper
fonte
11
Não tenho certeza se isso ficou claro na minha postagem, mas tenho vários aplicativos que dependem das mesmas bibliotecas e não quero duplicar os scripts de construção para bibliotecas nos aplicativos.
André Caron
3
Você deve elaborar sua resposta para demonstrar como ela lida com as diferentes questões. Não está claro para mim exatamente como você vincula dependências, pois, dependendo do contexto, as bibliotecas dependentes não estão no mesmo local.
André Caron
0

Eu não usaria submódulos.

É tentador, como costumava ser o caso do svn-externals. No entanto, você pode ter certeza de que todos os projetos vinculados ainda estão no mesmo local em um ano? E em cinco?

Portanto, estou simplesmente copiando todas as dependências necessárias no meu projeto. Isso significa que, enquanto meu repo for válido, posso verificar o estado exato.

Basicamente, eu tenho uma estrutura de pastas da seguinte maneira:

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


e.g. ext/boost, ext/cppunit

Embora isso não seja muito bom do ponto de vista do espaço em disco, valorizo ​​a garantia de que posso verificar todos os estados registrados, desde que o repo esteja disponível muito mais alto.

Além disso, há vários problemas com os submódulos, conforme descrito aqui

Wilbert
fonte
Tenho certeza de que eles estão no local certo, porque estou mantendo todos eles :-) Além disso, tenha cuidado ao copiar projetos devido às condições de redistribuição.
André Caron
OK, isso reduz o problema. E licenciamento: Sim, você precisa ter cuidado, mas esse é um problema completamente diferente.
Wilbert
0

Enfrentando exatamente o mesmo problema aqui. Uma das soluções poderia ser a de ter algum repo libsque iria realizar core, network, graphcomo submódulos e CMakeLists apenas que iria dizer cada uma das libs onde encontrar suas dependências. Agora, cada aplicativo teria libscomo submódulo e usaria apenas as bibliotecas necessárias.

O teste de cada lib pode ser configurado de 2 maneiras:

  • Tenha core_testing, graph_testing, network_testing como aplicativos separados
  • Implante libs testadas em servidores de teste e localize-as enquanto executa testes usando o cmake
Máx.
fonte
Isso não disponibiliza todas as bibliotecas para todas as outras bibliotecas?
André Caron
Por padrão, sim. Mas isso poderia ser decidido em cmakelists no nível de bibliotecas. Se graphnão precisa saber sobre network- não passam networkcoisas -relacionados para graphsubdir
Max