Obwohl ich programmieren kann, habe ich noch keine Erfahrung mit der Arbeit an großen Projekten. Bisher habe ich entweder kleine Programme codiert, die in Sekundenschnelle kompiliert werden (verschiedene c / c ++ - Übungen wie Algorithmen, Programmierprinzipien, Ideen, Paradigmen oder einfach nur APIs ausprobieren ...) oder an einigen kleineren Projekten gearbeitet Erstellt in einer oder mehreren Skriptsprachen (Python, PHP, JS), in denen keine Kompilierung erforderlich ist.
Die Sache ist, wenn ich in einer Skriptsprache codiere, wann immer ich versuchen möchte, ob etwas funktioniert - ich führe einfach das Skript aus und sehe, was passiert. Wenn die Dinge nicht funktionieren, kann ich einfach den Code ändern und ihn erneut ausprobieren, indem ich das Skript erneut ausführe und so lange mache, bis ich das gewünschte Ergebnis erhalte. Mein Punkt ist, dass Sie nicht warten müssen Alles, was kompiliert werden muss, ist einfach, eine große Codebasis zu erstellen, zu ändern, etwas hinzuzufügen oder einfach damit zu spielen - Sie können die Änderungen sofort sehen.
Als Beispiel nehme ich Wordpress. Es ist ziemlich einfach, herauszufinden, wie man ein Plugin dafür erstellt. Zuerst erstellen Sie ein einfaches "Hello World" -Plugin, dann erstellen Sie eine einfache Oberfläche für das Admin-Panel, um sich mit der API vertraut zu machen. Dann bauen Sie sie auf und erstellen etwas Komplexeres. In der Zwischenzeit ändern Sie das Aussehen einiger Plugins Die Idee, nach jeder kleinen Änderung immer wieder etwas so Großes wie WP neu kompilieren zu müssen, um zu versuchen, "ob es funktioniert" und "wie es funktioniert / sich anfühlt", scheint einfach ineffizient, langsam und falsch.
Wie könnte ich das mit einem Projekt machen, das in einer kompilierten Sprache geschrieben ist? Ich möchte zu einigen Open-Source-Projekten beitragen, und diese Frage nervt mich immer wieder. Die Situation ist wahrscheinlich von Projekt zu Projekt unterschiedlich, da einige von ihnen, die mit Bedacht vorausgesehen wurden, in gewisser Weise "modular" sind, während andere nur ein einziger großer Blob sind, der immer wieder neu kompiliert werden muss.
Ich würde gerne mehr darüber erfahren, wie das richtig gemacht wird. Was sind einige gängige Praktiken, Ansätze und Projektdesigns (Muster?), Um damit umzugehen? Wie heißt diese "Modularität" in der Welt der Programmierer und worauf sollte ich googeln, um mehr darüber zu erfahren? Wachsen Projekte oft aus ihren ersten Gedankenverhältnissen heraus, was nach einer Weile problematisch wird? Gibt es eine Möglichkeit, das lange Kompilieren von nicht so gut gestalteten Projekten zu vermeiden ? Eine Möglichkeit, sie irgendwie zu modularisieren (möglicherweise nicht wichtige Teile des Programms während der Entwicklung auszuschließen (irgendwelche anderen Ideen?))?
Vielen Dank.
quelle
Antworten:
Wie bereits gesagt, kompilieren Sie das gesamte Projekt nie jedes Mal neu, wenn Sie eine kleine Änderung vornehmen. Stattdessen kompilieren Sie nur den Teil des Codes, der sich geändert hat, sowie den gesamten Code, der davon abhängt.
In C / C ++ ist das Kompilieren ziemlich einfach. Sie kompilieren jede Quelldatei in Maschinencode (wir nennen sie Objektdateien * .o) und verknüpfen dann alle Ihre Objektdateien zu einer großen ausführbaren Datei.
Wie bereits von MainMa erwähnt, sind einige Bibliotheken in separate Dateien integriert, die zur Laufzeit dynamisch mit der ausführbaren Datei verknüpft werden. Diese Bibliotheken werden unter Unix als Shared Objects (* .so) und unter Windows als Dynamically Linked Libraries (DLL) bezeichnet. Dynamische Bibliotheken haben viele Vorteile. Einer davon ist, dass Sie sie nicht kompilieren / verknüpfen müssen, es sei denn, ihr Quellcode ändert sich effektiv.
Es gibt Tools zur Build-Automatisierung, die Ihnen helfen:
Die bekanntesten (make, ant, maven, ...) können automatisch erkennen, welche Teile des Codes seit der letzten Kompilierung geändert wurden und welches Objekt / welche Binärdatei genau aktualisiert werden muss.
Dies ist jedoch mit den (relativ geringen) Kosten für das Schreiben eines "Build-Skripts" verbunden. Es handelt sich um eine Datei, die alle Informationen zu Ihrem Build enthält, z. B. das Definieren der Ziele und ihrer Abhängigkeiten, das Definieren des gewünschten Compilers und der zu verwendenden Optionen, das Definieren Ihrer Build-Umgebung, Ihrer Bibliothekspfade usw. Sie haben möglicherweise von Makefiles gehört (sehr häufig in der Unix-Welt) oder build.xml (sehr beliebt in der Java-Welt). Das machen sie.
quelle
build.xml
Datei anSie kompilieren nicht jedes Mal das gesamte Projekt neu. Wenn es sich beispielsweise um eine C / C ++ - Anwendung handelt, besteht die Möglichkeit, dass sie in Bibliotheken (DLLs in Windows) aufgeteilt wird, wobei jede Bibliothek separat kompiliert wird.
Das Projekt selbst wird in der Regel täglich auf einem dedizierten Server kompiliert: Dies sind nächtliche Builds. Dieser Prozess kann viel Zeit in Anspruch nehmen, da er nicht nur die Kompilierungszeit, sondern auch die Zeit für die Ausführung von Komponententests, anderen Tests und anderen Prozessen umfasst.
quelle
Ich denke, was alle Antworten bisher auch angedeutet haben, ist, dass große Softwareprojekte fast immer in viel kleinere Teile zerlegt werden. Jedes Stück wird normalerweise in einer eigenen Datei gespeichert.
Diese Stücke werden einzeln zusammengestellt , um Objekte zu erstellen. Die Objekte werden dann miteinander verbunden, um das Endprodukt zu bilden. [In gewisser Weise ist es so, als würde man Sachen aus Legos bauen. Du versuchst nicht, das Letzte aus einem großen Stück Plastik zu formen, sondern kombinierst ein paar kleinere Stücke, um es zu machen.]
Wenn Sie das Projekt in Teile zerlegen, die einzeln zusammengestellt werden, können einige nette Dinge passieren.
Inkrementelles Gebäude
Wenn Sie ein Teil wechseln, müssen Sie normalerweise nicht alle Teile neu kompilieren. Im Allgemeinen müssen die anderen nicht neu kompiliert werden, solange Sie nicht ändern, wie andere Teile mit Ihrem Teil interagieren.
Daraus ergibt sich die Idee des inkrementellen Bauens . Bei einem inkrementellen Build werden nur die Teile neu kompiliert, die von der Änderung betroffen waren. Dies beschleunigt die Entwicklungszeit erheblich. Zwar müssen Sie möglicherweise noch warten, bis alles neu verknüpft ist, aber es ist immer noch eine Ersparnis, wenn Sie alles neu kompilieren und neu verknüpfen müssen. (Übrigens: Einige Systeme / Sprachen unterstützen inkrementelle Verknüpfungen, sodass nur die Änderungen erneut verknüpft werden müssen. Die Kosten hierfür liegen normalerweise in einer schlechten Codeleistung und -größe.)
Unit Testing
Das zweite, was Sie mit kleinen Stücken tun können, ist, die Teile einzeln zu testen, bevor sie kombiniert werden. Dies wird als Unit Testing bezeichnet . Beim Komponententest wird jede Einheit einzeln getestet, bevor sie in den Rest des Systems integriert (kombiniert) wird. Unit-Tests werden normalerweise so geschrieben, dass sie schnell ausgeführt werden können, ohne den Rest des Systems einzubeziehen.
Der Grenzfall bei der Anwendung von Tests ist in Test Driven Development (TDD) zu sehen. In diesem Entwicklungsmodell wird kein Code geschrieben / geändert, es sei denn, es soll ein fehlgeschlagener Test behoben werden.
Einfacher machen
Das Aufteilen von Dingen scheint also gut zu sein, aber es scheint auch, dass viel Arbeit erforderlich ist, um das Projekt zu erstellen: Sie müssen herausfinden, welche Teile sich geändert haben und was von diesen Teilen abhängt, jedes Teil zusammenstellen und dann alles miteinander verknüpfen.
Glücklicherweise sind Programmierer faul * und erfinden viele Tools, um ihre Arbeit zu erleichtern. Zu diesem Zweck wurden viele Tools geschrieben, um die obige Aufgabe zu automatisieren. Die bekanntesten davon wurden bereits erwähnt (make, ant, maven). Mit diesen Tools können Sie definieren, welche Teile für Ihr endgültiges Projekt zusammengesetzt werden müssen und wie die Teile voneinander abhängen (dh wenn Sie dies ändern, muss dies neu kompiliert werden). Das Ergebnis ist, dass durch die Ausgabe nur eines Befehls herausgefunden wird, was neu kompiliert werden muss, kompiliert wird und alles neu verknüpft wird.
Aber das lässt immer noch herauszufinden, wie sich die Dinge zueinander verhalten. Das ist viel Arbeit und wie ich bereits sagte, sind Programmierer faul. Sie haben sich also eine andere Klasse von Werkzeugen ausgedacht. Diese Tools wurden geschrieben, um die Abhängigkeiten für Sie zu bestimmen! Oft sind die Tools Teil von Integrated Development Environments (IDEs) wie Eclipse und Visual Studio, aber es gibt auch einige eigenständige Tools, die sowohl für generische als auch für spezifische Anwendungen verwendet werden (makedep, QMake for Qt-Programme).
* Eigentlich sind Programmierer nicht wirklich faul, sie verbringen ihre Zeit nur gerne mit der Arbeit an Problemen und erledigen keine sich wiederholenden Aufgaben, die von einem Programm automatisiert werden können.
quelle
Hier ist meine Liste von Dingen, mit denen Sie versuchen können, C / C ++ - Builds zu beschleunigen:
quelle
Etwas Interpretiertes auszuführen ist auch sehr ineffizient und langsam und (wohl) falsch. Sie beschweren sich über die Zeitanforderungen auf dem PC des Entwicklers, aber das Nichtkompilieren verursacht Zeitanforderungen auf dem PC des Benutzers , was wahrscheinlich viel schlimmer ist.
Noch wichtiger ist, dass moderne Systeme recht fortgeschrittene inkrementelle Neuerstellungen durchführen können und es nicht üblich ist, das Ganze für geringfügige Änderungen neu zu kompilieren. Kompilierte Systeme können Skriptkomponenten enthalten, insbesondere für Dinge wie die Benutzeroberfläche.
quelle
Wenn das Projekt eine ordnungsgemäße DAG für die Kompilierungsabhängigkeit implementiert, können Sie nur die Objektdateien neu kompilieren, auf die sich Ihre Änderung auswirkt.
Unter der Annahme einer ordnungsgemäßen DAG für die Kompilierungsabhängigkeit können Sie mit mehreren Prozessen kompilieren. Ein Job pro Kern / CPU ist die Norm.
Sie können zum Testen mehrere ausführbare Dateien erstellen, die nur bestimmte Objektdateien verknüpfen.
quelle
Zusätzlich zur Antwort von MainMa haben wir gerade die Maschinen aktualisiert, an denen wir arbeiten. Einer der besten Einkäufe, die wir getätigt haben, war eine SSD, bei der Sie das gesamte Projekt neu kompilieren müssen.
Ein weiterer Vorschlag wäre, einen anderen Compiler auszuprobieren. Früher wechselten wir von Javas Compiler zu Jikes und jetzt verwenden wir den mit Eclipse gebündelten Compiler (ich weiß nicht, ob er einen Namen hat), der Multicore-Prozessoren besser nutzt.
Das Kompilieren unseres 37.000-Dateien-Projekts dauerte ungefähr 15 Minuten, bevor wir diese Änderungen vorgenommen haben. Nach Änderungen wurde es auf 2-3 Minuten gekürzt.
Natürlich ist es erwähnenswert, den Punkt von MainMa noch einmal zu erwähnen. Kompilieren Sie das gesamte Projekt nicht jedes Mal neu, wenn Sie eine Änderung sehen möchten.
quelle