Ich bin der Autor des Zitats in der Frage, das aus einer früheren Antwort stammt .
Jason ist zu Recht misstrauisch gegenüber kurzen Aussagen wie meinen und bittet um eine Erklärung. Wenn ich alles in dieser Antwort vollständig erklärt hätte, hätte ich natürlich ein Buch geschrieben.
Mike weist auch zu Recht darauf hin, dass eines der Probleme mit einer svn:external
ähnlichen Funktion darin besteht, dass Änderungen an der Zielquelle Ihre eigene Quelle beschädigen können, insbesondere wenn sich diese Zielquelle in einem Repository befindet, das Sie nicht besitzen.
Lassen Sie mich zur weiteren Erläuterung meines Kommentars zunächst sagen, dass es "sichere" Möglichkeiten gibt, die svn:external
ähnliche Funktion zu verwenden, genau wie bei jedem anderen Tool oder Feature. Ich bezeichne es jedoch als Antimuster, da die Funktion weitaus häufiger missbraucht wird. Nach meiner Erfahrung wurde es immer missbraucht, und es ist sehr unwahrscheinlich, dass ich es jemals auf diese sichere Weise verwenden oder jemals empfehlen werde. Bitte beachten Sie weiter, dass ich KEINE Herabsetzung des Subversion-Teams meine - ich liebe Subversion, obwohl ich vorhabe, zum Basar zu wechseln.
Das Hauptproblem bei dieser Funktion besteht darin, dass sie ermutigt und normalerweise verwendet wird, um die Quelle eines Builds ("Projekt") direkt mit der Quelle eines anderen zu verknüpfen oder das Projekt mit einer Binärdatei (DLL, JAR usw.) zu verknüpfen. von denen es abhängt. Keine dieser Verwendungen ist weise und sie bilden ein Antimuster.
Wie ich in meiner anderen Antwort sagte, glaube ich, dass ein wesentliches Prinzip für Software-Builds darin besteht, dass jedes Projekt genau EIN binäres oder primäres Ergebnis erstellt. Dies kann als Anwendung des Prinzips der Trennung von Bedenken auf den Erstellungsprozess angesehen werden. Dies gilt insbesondere für ein Projekt, das direkt auf die Quelle eines anderen verweist, was ebenfalls einen Verstoß gegen das Kapselungsprinzip darstellt . Eine andere Form dieser Art von Verletzung ist der Versuch, eine Build-Hierarchie zu erstellen, um ein gesamtes System oder Subsystem durch rekursives Aufrufen von Sub-Builds zu erstellen. Maven ermutigt / erzwingt dieses Verhalten nachdrücklich, was einer der vielen Gründe ist, warum ich es nicht empfehle.
Schließlich finde ich, dass es verschiedene praktische Aspekte gibt, die diese Funktion unerwünscht machen. Zum einen svn:external
hat einige interessante Verhaltensmerkmale (aber die Details entgehen mir im Moment). Zum anderen stelle ich immer fest, dass solche Abhängigkeiten für mein Projekt explizit sichtbar sein müssen (Erstellungsprozess) und nicht als Metadaten der Quellcodeverwaltung vergraben sind.
Was ist also eine "sichere" Art, diese Funktion zu verwenden? Ich würde dies als eine Zeit betrachten, in der es nur von einer Person verwendet wird, beispielsweise um eine Arbeitsumgebung zu "konfigurieren". Ich konnte sehen, wo ein Programmierer möglicherweise einen eigenen Ordner im Repository erstellt (oder einen für jeden Programmierer), in dem er svn:external
Links zu den verschiedenen anderen Teilen des Repositorys konfiguriert, an denen er gerade arbeitet. Durch Auschecken dieses einen Ordners wird dann eine Arbeitskopie aller aktuellen Projekte erstellt. Wenn ein Projekt hinzugefügt oder beendet wird, können die svn:external
Definitionen angepasst und die Arbeitskopie entsprechend aktualisiert werden. Ich bevorzuge jedoch einen Ansatz, der nicht an ein bestimmtes Versionsverwaltungssystem gebunden ist, z. B. mit einem Skript, das die Checkouts aufruft.
Mein letztes Engagement für dieses Problem trat im Sommer 2008 bei einem Beratungskunden svn:external
auf, der in großem Umfang tätig war. ALLES wurde vernetzt, um eine einzige Master-Arbeitskopie zu erstellen. Ihre Ant & Jython-basierten (für WebLogic) Build-Skripte wurden auf dieser Master-Arbeitskopie erstellt. Das Nettoergebnis: NICHTS konnte eigenständig gebaut werden, es gab buchstäblich Dutzende von Teilprojekten, aber keines war sicher, selbst auszuchecken / daran zu arbeiten. Daher erforderte jede Arbeit an diesem System zuerst das Auschecken / Aktualisieren von mehr als 2 GB Dateien (sie legten auch Binärdateien in das Repository). Etwas zu erledigen war eine Übung der Sinnlosigkeit, und ich ging, nachdem ich es drei Monate lang versucht hatte (es waren auch viele andere Antimuster vorhanden).
BEARBEITEN: Erläutern Sie rekursive Builds -
Im Laufe der Jahre (insbesondere des letzten Jahrzehnts) habe ich massive Systeme für Fortune 500-Unternehmen und große Regierungsbehörden aufgebaut, die viele Dutzend Teilprojekte umfassen, die in Verzeichnishierarchien angeordnet sind, die viele Ebenen tief sind. Ich habe Microsoft Visual Studio-Projekte / -Lösungen verwendet, um .NET-basierte Systeme, Ant oder Maven 2 für Java-basierte Systeme zu organisieren, und ich habe begonnen, Distutils und Setuptools (easyinstall) für Python-basierte Systeme zu verwenden. Diese Systeme enthalten auch große Datenbanken, die normalerweise in Oracle oder Microsoft SQL Server enthalten sind.
Ich hatte großen Erfolg beim Entwerfen dieser massiven Builds für Benutzerfreundlichkeit und Wiederholbarkeit. Mein Designstandard ist, dass ein neuer Entwickler am ersten Tag auftauchen, eine neue Workstation (möglicherweise direkt von Dell mit nur einer typischen Betriebssysteminstallation) und ein einfaches Einrichtungsdokument (normalerweise nur eine Seite mit Installationsanweisungen) erhalten kann. und in der Lage sein, die Workstation vollständig einzurichten und das gesamte System aus dem Quellcode zu erstellen, unbeaufsichtigt, ohne Unterstützung und in einem halben Tag oder weniger. Das Aufrufen des Builds selbst umfasst das Öffnen einer Befehlsshell, das Wechseln in das Stammverzeichnis des Quellbaums und das Ausgeben eines einzeiligen Befehls zum Erstellen von ALLES.
Trotz dieses Erfolgs erfordert der Aufbau eines solch massiven Build-Systems große Sorgfalt und die strikte Einhaltung solider Designprinzipien, genau wie beim Aufbau einer massiven geschäftskritischen Anwendung / eines massiven Systems. Ich habe festgestellt, dass ein entscheidender Teil darin besteht, dass jedes Projekt (das ein einzelnes Artefakt / Ergebnis liefert) ein einzelnes Build-Skript haben muss, das eine genau definierte Schnittstelle (Befehle zum Aufrufen von Teilen des Build-Prozesses) haben muss und stehen muss allein aus allen anderen (Teil-) Projekten. Historisch gesehen ist es einfach, das gesamte System zu bauen, aber es ist schwierig / unmöglich, nur ein Stück zu bauen. Erst kürzlich habe ich gelernt, sorgfältig sicherzustellen, dass jedes Projekt wirklich für sich steht.
In der Praxis bedeutet dies, dass mindestens zwei Ebenen von Build-Skripten vorhanden sein müssen. Die unterste Ebene sind die Projekterstellungsskripte, die jedes Ergebnis / Artefakt erzeugen. Jedes dieser Skripte befindet sich im Stammverzeichnis seines Projektquellbaums (tatsächlich definiert dieses Skript seinen Projektquellbaum). Diese Skripte wissen nichts über die Quellcodeverwaltung. Sie erwarten, dass sie über die Befehlszeile ausgeführt werden. Sie verweisen auf alles im Projekt relativ Sie verweisen auf das Build-Skript und verweisen auf ihre externen Abhängigkeiten (Tools oder binäre Artefakte, keine anderen Quellprojekte), basierend auf einigen konfigurierbaren Einstellungen (Umgebungsvariablen, Konfigurationsdateien usw.).
Die zweite Ebene von Build-Skripten soll ebenfalls über die Befehlszeile aufgerufen werden, diese kennen sich jedoch mit der Quellcodeverwaltung aus. In der Tat handelt es sich bei dieser zweiten Ebene häufig um ein einzelnes Skript, das mit einem Projektnamen und einer Version aufgerufen wird. Anschließend wird die Quelle für das benannte Projekt in einem neuen temporären Verzeichnis (möglicherweise in der Befehlszeile angegeben) ausgecheckt und das Erstellungsskript aufgerufen.
Möglicherweise müssen mehr Variationen vorgenommen werden, um Continuous Integration Server, mehrere Plattformen und verschiedene Release-Szenarien zu berücksichtigen.
Manchmal ist eine dritte Schicht von Skripten erforderlich, die die zweite Schicht von Skripten (die die erste Schicht aufruft) aufruft, um bestimmte Teilmengen der gesamten Projektmenge zu erstellen. Beispielsweise kann jeder Entwickler ein eigenes Skript haben, das die Projekte erstellt, an denen er heute arbeitet. Möglicherweise gibt es ein Skript, mit dem Sie alles erstellen können, um die Masterdokumentation zu generieren oder Metriken zu berechnen.
Unabhängig davon habe ich festgestellt, dass der Versuch, das System als eine Hierarchie von Projekten zu behandeln, kontraproduktiv ist. Es bindet die Projekte so miteinander, dass sie nicht frei oder an beliebigen Orten (temporäres Verzeichnis auf dem Continuous Integration Server) oder in beliebiger Reihenfolge (vorausgesetzt, die Abhängigkeiten sind erfüllt) erstellt werden können. Der Versuch, eine Hierarchie zu erzwingen, unterbricht häufig jede mögliche IDE-Integration.
Schließlich kann der Aufbau einer massiven Hierarchie von Projekten einfach zu leistungsintensiv sein. Im Frühjahr 2007 habe ich beispielsweise versucht, eine bescheidene Quellhierarchie (Java plus Oracle) zu erstellen, die ich mit Ant erstellt habe. Dies ist schließlich fehlgeschlagen, da der Build immer mit einer Java OutOfMemoryException abgebrochen wurde. Dies war auf einer 2 GB RAM-Workstation mit 3,5 GB Swap-Speicher, für die ich die JVM so eingestellt hatte, dass sie den gesamten verfügbaren Speicher nutzen konnte. Die Anwendung / das System war in Bezug auf die Codemenge relativ trivial, aber die rekursiven Build-Aufrufe erschöpften schließlich den Speicher, egal wie viel Speicher ich ihm gab. Natürlich dauerte die Ausführung auch ewig (30-60 Minuten waren üblich, bevor sie abgebrochen wurden). Ich weiß, wie man SEHR gut abstimmt, aber letztendlich habe ich einfach die Grenzen der Tools überschritten (in diesem Fall Java / Ant).
Tun Sie sich selbst einen Gefallen, konstruieren Sie Ihren Build als eigenständige Projekte und komponieren Sie sie dann zu einem vollständigen System. Halten Sie es leicht und flexibel. Genießen.
EDIT: Mehr zu Antimustern
Genau genommen ist ein Antimuster eine gängige Lösung, die das Problem zu lösen scheint, dies aber nicht tut, entweder weil es wichtige Lücken hinterlässt oder weil es zusätzliche Probleme mit sich bringt (oft schlimmer als das ursprüngliche Problem). Eine Lösung umfasst notwendigerweise ein oder mehrere Werkzeuge sowie die Technik, um sie auf das jeweilige Problem anzuwenden. Daher ist es eine Strecke, ein Werkzeug oder ein bestimmtes Merkmal eines Werkzeugs als Antimuster zu bezeichnen, und es scheint, dass Menschen diese Strecke erkennen und darauf reagieren - fair genug.
Auf der anderen Seite, da es in unserer Branche üblich zu sein scheint, sich eher auf Werkzeuge als auf Technik zu konzentrieren, ist es das Werkzeug / Merkmal, das die Aufmerksamkeit auf sich zieht (eine gelegentliche Übersicht der Fragen hier auf StackOverflow scheint leicht zu veranschaulichen). Meine Kommentare und diese Frage selbst spiegeln diese Praxis wider.
Manchmal erscheint es jedoch besonders gerechtfertigt, diese Dehnung vorzunehmen, wie in diesem Fall. Einige Werkzeuge scheinen den Benutzer zu bestimmten Techniken zu führen, um sie anzuwenden, bis zu dem Punkt, an dem einige argumentieren, dass Werkzeuge das Denken formen (leicht umformuliert). svn:external
Vor allem in diesem Sinne schlage ich vor, dass es sich um ein Antimuster handelt.
Um das Problem genauer zu formulieren, besteht das Antimuster darin, eine Build-Lösung zu entwerfen, die das Zusammenbinden von Projekten auf Quellenebene umfasst, oder die Abhängigkeiten zwischen Projekten implizit zu versionieren oder zuzulassen, dass sich solche Abhängigkeiten implizit ändern, da jede dieser Abhängigkeiten sehr negativ ist Folgen. Die Art des svn:external
ähnlichen Merkmals macht es sehr schwierig, diese negativen Konsequenzen zu vermeiden.
Um die Abhängigkeiten zwischen Projekten richtig zu behandeln, müssen diese Dynamiken zusammen mit dem Basisproblem berücksichtigt werden, und die Tools und Techniken gehen einen anderen Weg. Ein Beispiel, das in Betracht gezogen werden sollte, ist Ivy , das auf ähnliche Weise wie Maven hilft, jedoch ohne die vielen Nachteile. Ich untersuche Ivy in Verbindung mit Ant als meine kurzfristige Lösung für das Java-Build-Problem. Langfristig möchte ich die Kernkonzepte und -funktionen in ein Open-Source-Tool integrieren, das eine plattformübergreifende Lösung ermöglicht.