Actuellement, je suis en train de quitter un ancien système de contrôle de version et de transférer le projet de mon groupe vers mercurial. Pour illustrer le type de code que je déplace, j'ai une solution Visual Studio de plus de 25 projets, contenant plusieurs domaines d'application distincts qui dépendent tous d'un code commun. En regardant sur Stack Overflow, la question la plus proche que j'ai trouvée est la suivante celui-ci mais il ne fait que mentionner le contrôle de version. Je cherche un conseil un peu plus poussé, sur les techniques spécifiques de mise en œuvre de l'utilisation de Mercurial pour gérer ces dépendances.
Une vue simplifiée des dépendances ressemble à ce qui suit. (Ceci n'est qu'une illustration et un exemple ; les dépendances réelles sont nettement plus complexes mais de nature similaire).
Common Lib 1
/ | \
---- | -----
/ | \ \
App 1 Common Lib 2 \ App 2
/ | \ \
------- | ------ |
/ | \|
App 3 App 4 App 5
Les modules de la bibliothèque commune seraient du code partagé - il s'agirait d'une DLL, d'une SO ou d'une autre bibliothèque utilisée simultanément par toutes les applications, tant au moment de la compilation que de l'exécution. Sinon, les applications pourraient fonctionner indépendamment les unes des autres.
J'ai quelques objectifs avec la mise en place de mes dépôts mercuriens :
- Donnez à chaque application ou groupe de composants importants son propre référentiel.
- Faites en sorte que chaque dépôt soit autonome.
- Faire en sorte que la totalité du projet soit autonome.
- Faciliter la construction de l'ensemble du code en une seule fois. (Au final, tous ces programmes et bibliothèques se retrouvent dans un seul installateur).
- Restez simple.
Un autre point est que j'ai un serveur configuré où j'ai des dépôts séparés pour chacun de ces projets.
Je vois deux façons d'organiser ces projets.
1. Créez un référentiel "Shell" qui contient tout.
Cela utiliserait des sous-références basées sur des url (par exemple, dans le .hgsub, je ferais quelque chose comme App1 = [https://my.server/repo/app1](https://my.server/repo/app1)
.) Voici comment cela se présente :
+---------------------------+
| Main Repository |
| | +---------------------+ |
| +-| Build | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 1 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 2 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 1 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 2 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 3 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 4 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 5 | |
| +---------------------+ |
+---------------------------+
Chaque dossier principal du référentiel shell contiendrait un sous-répertoire, un pour chaque zone de projet. Les dépendances seraient relatives : Par exemple, si l'application 4 a besoin de la bibliothèque commune 2, elle utilisera simplement des chemins relatifs pour référencer cette bibliothèque commune.
Les avantages de cette approche :
- Chaque bibliothèque est tirée vers le bas une fois et une seule.
- Les sous-références de Mercurial garantissent que la même version de la bibliothèque est utilisée dans tous les projets automatiquement, puisqu'une seule version de cette sous-référence existe dans le projet.
- Il est facile de trouver chaque ressource.
Inconvénients de cette approche :
- Je ne peux pas travailler sur une application indépendamment. Par exemple, si je travaille sur l'application 2, et qu'elle a besoin d'un changement dans les bibliothèques communes, toutes les autres applications devront prendre ces changements maintenant.
- Si je tire un dépôt d'applications par lui-même, je dois découvrir (ou connaître) les autres dépôts dépendants dont il a besoin si je veux le construire.
- Les dépendances ne sont pas fortement séparées - il serait tentant d'insérer une nouvelle fonctionnalité n'importe où puisqu'il est facile d'accéder à toutes les fonctionnalités.
2. Faire en sorte que les sous-prépositions dépendantes soient entièrement contenues.
Dans cette approche, chaque application aurait son propre dépôt (comme auparavant) mais cette fois-ci, il contiendrait également des sous-dépôts : un pour ses propres sources, et un pour chaque sous-dépôt dépendant. Un référentiel global contiendrait alors chacun de ces référentiels de projet, et saurait comment construire la solution entière. Cela ressemblerait à ce qui suit :
+-----------------------------------------------------------------------+
| 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 | | |
| | +--------------+ | |
| +--------------------+ |
+-----------------------------------------------------------------------+
Pour :
- Chaque application peut être construite par elle-même, indépendamment les unes des autres.
- Les versions dépendantes des bibliothèques peuvent être suivies par application, plutôt que globalement. Il faut un acte explicite d'insertion d'un subrepo dans le projet pour ajouter une nouvelle dépendance.
Cons :
- Lors de la construction finale, chaque application peut utiliser une version différente d'une bibliothèque partagée. (Il faudra peut-être écrire des outils pour synchroniser les sous-répertoires de la bibliothèque commune. Beurk).
- Si je veux construire l'ensemble des sources, je finis par tirer plusieurs fois sur les bibliothèques partagées. Dans le cas de Common Lib 1, je devrais le faire huit ( !) fois.
3. N'incluez pas du tout les dépendances en tant que sous-prépositions - apportez-les dans le cadre de la construction.
Cette approche ressemblerait beaucoup à l'approche 1, sauf que les bibliothèques communes ne seraient extraites que lors de la construction. Chaque application saurait de quels dépôts elle a besoin, et les placerait dans l'emplacement commun.
Pour :
- Chaque application pourrait se construire toute seule.
- Les bibliothèques communes n'auraient besoin d'être tirées qu'une seule fois.
Cons :
- Nous devrions garder la trace des versions des bibliothèques actuellement utilisées par chaque application. Cela fait double emploi avec les fonctionnalités du subrepo.
- Nous devrions construire une infrastructure pour supporter cela, ce qui signifie plus de choses dans les scripts de construction. Ugh.
4. Quoi d'autre ?
Y a-t-il une autre façon de procéder ? Une meilleure façon ? Quels moyens avez-vous essayés et réussis, quels moyens avez-vous essayés mais détestés ? Je penche actuellement pour la 1, mais le manque d'indépendance des applications, alors qu'elles devraient en être capables, me dérange vraiment. Y a-t-il un moyen d'obtenir la séparation agréable de la méthode 2 sans le cauchemar de la duplication massive du code et de la maintenance des dépendances, tout en n'ayant pas à écrire de scripts pour le gérer (comme dans l'option 3) ?