2009-09-21 11 views
5

Ich werde bald damit beginnen, eine Produktlinie zu unterhalten, die Varianten der gleichen eingebetteten Software enthält. Da ich seit einem Jahr mit Git spiele und es sehr schätze, werde ich es wahrscheinlich für die Quellcodeverwaltung verwenden.Überblick über die Quellcode-Varianten

Es gibt mehrere Optionen, die ich für die Wartung der Varianten der Firmware sehen kann, aber keine erfreut mich zu sehr. Was sind die Best Practices, die Sie für Ihre eigene Arbeit anwenden?

Alternativen ich denken kann:

  • definiert. Vorverarbeitung. Pros: alles ist immer im Quellcode vorhanden, es ist schwieriger, das Update eines der Produkte zu verpassen. Nachteile: schwerer zu lesen. Es kann in Ordnung sein, während wir nur zwei Varianten haben, wenn es vier oder mehr wird, wird es ein Schmerz sein. Außerdem scheint es schwieriger zu sein, das DRY-Prinzip anzuwenden (Do not Repeat Yourself).

  • ein Zweig pro Produktvariante. Wenn Änderungen, die für alle Produkte gelten, enthalten sind, muss die Änderung mit den anderen Produkten zusammengeführt werden. Nachteile: Wenn ein Commit sowohl Änderungen für alle Produkte als auch Änderungen für die spezifische Variante enthält, wird es Probleme geben. Natürlich können Sie sicherstellen, dass ein Commit nur eine Art von Änderung enthält: This-Product-Changes oder The-Whole-Family-Changes. Aber versuchen Sie das in einem Team zu erzwingen ?? Außerdem würde das Zusammenführen nicht funktionieren, stattdessen sollten wir Rosinen pflücken. Recht?

  • ein Kern-Repository als Submodul. Machen Sie alle Dateien, die die Kernfunktionalität enthalten, zu einem eigenständigen Repository. Alle Produkte enthalten eine Version des Kernrepositorys als Untermodul. Nachteile: Ich kann nicht sehen, dass es schließlich keine Varianten des Kern-Submoduls geben würde. Dann sind wir wieder in Schwierigkeiten, und dann benutzen wir wieder defines oder etwas Schlechtes. Ein zentrales Repository mit Niederlassungen? Dann kehren wir zur vorherigen Alternative zurück: Eine Änderung, die für alle Zweige gilt, muss zusammengeführt werden, aber die Zusammenführung umfasst auch die produktspezifischen Elemente.

  • Erstellen Sie ein Repository pro Modul. Zum Beispiel ein Repository für einen Display-Treiber, ein anderes für die Power-Management-Hardware, noch ein weiteres für die Benutzerschnittstelle, ... Pros: gute Modularität. Machen Sie ein neues Produkt, indem Sie einfach die benötigten Module als Submodule aufnehmen! Alle Submodule können Verzweigungen aufweisen, wenn beispielsweise eine Variante die Hardware auf andere Weise verwendet. Nachteile: Viele und viele Module, die jeweils ein paar Dateien (eine Include-Datei und eine Quelldatei) verfolgen. Ein Streit. Jemand macht ein wichtiges Update in einem Modul? Dann muss jemand die Änderung gegebenenfalls in die anderen Zweige dieses Moduls aufnehmen. Dann muss auch jemand das Submodul in jedem Produkt-Repository aktualisieren. Ziemlich etwas Arbeit, und wir verlieren irgendwie die Schnappschuss-Seite von Git.

Wie machen Sie es, und wie hat es funktioniert? Oder wie würdest du es tun?

Ich habe das Gefühl, dass ich mit Rosinenpicken erfahren sollte.

+0

Mit einer Verzweigung pro Produktlösung können Sie immer eine Entwicklung in einer oder mehreren Verzweigungen durchführen und diese Verzweigung dann in Verzweigungen für Varianten (pro Produkte) zusammenführen. –

Antwort

5

Ich würde versuchen, für #define s so viel wie möglich gehen. Mit einem geeigneten Code können Sie die Auswirkungen auf die Lesbarkeit und Wiederholungen minimieren.

Aber zur gleichen Zeit #define Ansatz kann sicher mit Teilen und Verzweigen kombiniert werden, deren Anwendung hängt von der Art der Codebasis.

+0

Defines sind die schlechteste mögliche Lösung. Sie machen den Quellcode, den Sie im Editor sehen, anders als den, den der Compiler sieht. – xpmatteo

+2

Und 'wenn' der Quellcode, den Sie im Editor sehen, unterscheidet sich von dem, der ausgeführt wird. Diese ganze Sache heißt Programmierung. –

+0

Der springende Punkt von Hochsprachen ist es, Menschen die Programmierung zu erleichtern. Das Interpretieren von IFs zur Kompilierzeit und auch von IFs zur Laufzeit macht es viel schwieriger und fehleranfälliger zu verstehen, was das Programm macht. – xpmatteo

5

Ich bin mir nicht sicher, ob das "Best Practice" ist, aber das Scintilla-Projekt verwendet seit Jahren etwas, das immer noch ziemlich überschaubar ist. Es hat nur einen Zweig, der allen Plattformen gemein ist (meistens Windows/GTK +/Mac, aber mit Varianten für VMS, Fox, etc.).

Irgendwie verwendet es die erste Option, die recht häufig ist: Es verwendet definiert, um kleine plattformspezifische Teile innerhalb der Quellen zu verwalten, wo es unpraktisch oder unmöglich ist, gemeinsamen Code zu setzen.
Beachten Sie, dass diese Option bei einigen Sprachen (z. B. Java) nicht möglich ist. Der Hauptmechanismus für die Portabilität ist die Verwendung von OO: Es abstrahiert einige Operationen (Zeichnen, Kontextmenü anzeigen usw.) und verwendet eine Plattformdatei (oder mehrere) pro Ziel, die die konkrete Implementierung bietet.
Das Makefile kompiliert nur die richtige Datei (en) und verwendet Verknüpfung, um den richtigen Code zu erhalten.

+0

OO wäre nett, aber das ist eingebetteter Code in Assembler, mit nicht einmal einem C-Compiler verfügbar. Ich verstehe, dass die Konzepte von OO trotzdem angewendet werden können. Das Zusammenstellen und Verknüpfen nur der notwendigen Dateien ist eine Option, aber ich glaube, dass zwei solche Dateien (eine pro Variante) sehr ähnlich wären und sich gegenseitig wiederholen würden. – Gauthier

+0

Kompatible Dateien, sei es eine oder eine pro einzigartige Plattform, finde ich eine gute Idee. –

+0

Die Problemumgehung für Java besteht darin, zusätzliche Plugins wie Antenna for Eclipse zu verwenden. Nicht perfekt, aber sehr hilfreich. – David

2

Ich denke, dass die angemessene Antwort teilweise davon abhängt, wie radikal die Varianten sind.

Wenn kleine Teile unterschiedlich sind, ist die Verwendung der bedingten Kompilierung für eine einzelne Quelldatei sinnvoll. Wenn die Variantenimplementierungen nur an der Aufrufschnittstelle konsistent sind, ist es möglicherweise besser, separate Dateien zu verwenden. Sie können Variantenvarianten in einer Datei mit bedingter Kompilierung radikal einbeziehen. Wie chaotisch das ist, hängt von der Menge des Variantencodes ab. Wenn es, sagen wir, vier Varianten von jeweils etwa 100 Zeilen gibt, ist vielleicht eine Datei in Ordnung. Wenn es vier Varianten von 100, 300, 500 und 900 Zeilen gibt, dann ist eine Datei wahrscheinlich eine schlechte Idee.

Sie benötigen die Varianten nicht unbedingt in getrennten Zweigen; In der Tat sollten Sie Zweige nur bei Bedarf verwenden (aber verwenden Sie sie, wenn sie notwendig sind!). Sie können die vier Dateien sagen, alle auf einem gemeinsamen Zweig, immer sichtbar. Sie können veranlassen, dass die Zusammenstellung die richtige Variante aufnimmt. Eine Möglichkeit (es gibt viele andere) ist die Erstellung einer einzigen Quelldatei, die die aktuelle Kompilierungsumgebung gegeben, die Source-Variante weiß umfassen:

#include "config.h" 
#if defined(USE_VARIANT_A) 
#include "variant_a.c" 
#elif defined(USE_VARIANT_B) 
#include "variant_b.c" 
#else 
#include "basecase.c" 
#endif 
+0

Ich würde das viel lieber im Makefile machen, ich mag es nicht, c-Dateien mit #include einzuschließen: sie würden nicht in meinem Makefile erscheinen. Ich stimme zu, dass es keine Silberkugel gibt, die Lösung hängt von der Varianz der Varianten ab :) Was ich fürchte ist, mit #defines zu beginnen und zu sehen, dass die Produktfamilie auf 10 Varianten anwächst. – Gauthier

+0

Wenn Sie Makefiles generieren (im Gegensatz zu einem versionsgesteuerten Makefile), dann gehen Sie mit dem direkten Ansatz der Auflistung der richtigen Dateien im Makefile. Dieser Ansatz funktioniert OK (nicht unbedingt empfohlen, funktioniert aber), wenn das Makefile nicht dynamisch generiert wird. Meine grundlegende Botschaft ist "eine Größe passt nicht für alle" und verschiedene Lösungen sind abhängig von den Details relevant. Wenn Sie der Meinung sind, dass Sie 10 Variantenartikel haben, dann gestalten Sie Ihr System so, dass es mit so vielen Varianten arbeitet. Sie sagen nicht, ob es 10 binäre Entscheidungen oder 1 von 10 Alternativen oder etwas anderes ist. –

+0

Bisher meinte ich 1 von 10 Alternativen. Gott sei Dank :) Ich verstehe nicht, warum versionierte Makefiles (eine pro Variante) diese Methode ungültig machen würde? – Gauthier

6

Sie sollten sich bemühen, so viel wie möglich, halten Sie jede benutzerdefinierte Code der Variante in eine eigene Reihe von Dateien. Dann wählt Ihr Build-System (Makefile oder was auch immer) basierend auf der von Ihnen erstellten Variante aus, welche Quellen verwendet werden sollen.

Der Vorteil ist, dass wenn Sie an einer bestimmten Variante arbeiten, Sie den gesamten Code zusammen sehen, ohne den Code anderer Varianten, um die Dinge zu verwechseln. Ablesbarkeit ist auch viel besser als die Quelle mit #ifdef Littering, #elif, #endif usw.

Branchen am besten, wenn Sie wissen, dass Sie in Zukunft alle des Codes aus der Branche fusionieren wollen in der Master-Zweig (oder andere Zweige). Es funktioniert nicht so gut nur für die Zusammenführung einige Änderungen von Zweig zu Zweig (obwohl es sicherlich getan werden kann). Getrennte Verzweigungen für jede Variante werden daher wahrscheinlich keine guten Ergebnisse liefern.

Wenn Sie den oben genannten Ansatz verwenden, müssen Sie nicht versuchen, solche Tricks in Ihrer Versionskontrolle zu verwenden, um die Organisation Ihres Codes zu unterstützen.

+0

+1 für Hinweise zur Verzweigung, nur wenn geplant ist, wieder einzubinden (obwohl es Ausnahmen gibt). Auch für das Risiko, VCS-Tricks zu verwenden, um den Code zu unterstützen. Sehen Sie meinen Kommentar zu PhiLho über DRY mit solchen benutzerdefinierten Code-Dateien. – Gauthier

+0

Wenn ich an einer bestimmten Variante arbeite, möchte ich oft den Code der anderen Varianten sehen. Dies ist eine gute Verwendung für Code-Faltung. –

1

Sie sind in einer Welt der Verletzung!

Was immer Sie tun, Sie benötigen eine automatische Build-Umgebung. Zumindest brauchen Sie einen automatischen Weg, um alle verschiedenen Versionen Ihrer Firmware zu erstellen. Ich hatte Probleme, einen Fehler in einer Version zu beheben und den Build einer anderen Version zu unterbrechen.

Idealerweise könnten Sie die verschiedenen Ziele laden und einige Rauchtests durchführen.

Wenn Sie den #define Weg zu gehen, würde ich die folgenden irgendwo setzen, wo die Variante geprüft:

#else 
    #error You MUST specify a variant! 
#endif 

Dies wird sicherstellen, alle Dateien für die gleiche Variante während des Build-Prozesses aufgebaut sind.

+0

Sicher, der # Fehler. Und sogar, wenn mehrere Varianten definiert sind, undefiniert alle außer einem (oder senden Sie einen anderen #Fehler). – Gauthier

0

Ich bin mit dem gleichen Problem konfrontiert. Ich möchte die Quelle nicht nur zwischen den Zielen teilen, sondern über Plattformen hinweg (zB Mono vs. Visual Studio). Es scheint, als ob es keine Möglichkeit gibt, dies einfach mit der Version zu tun, die das Branching/Taging von Git bietet Ich würde gerne einen gemeinsamen Code-Zweig pflegen und spezifische Verzweigungen hosten/zielen und einfach den gemeinsamen Code in diese Zweige hinein- und wieder herausführen. " Aber ich weiß nicht, wie ich das machen soll. Vielleicht eine Kombination aus Verzweigen und Markieren? ?

Offensichtlich würde ich Condition Compilation verwenden, wenn es benötigt wurde, wo ich könnte, so dass es nur eine Quelldatei unter Versionskontrolle geben würde, aber für die anderen Stuff-Dateien, die nur in einem Host/Zielzweig erscheinen sollten, oder wo der Inhalt völlig anders wäre, wird ein anderer Mechanismus benötigt.Wenn Sie OO-Techniken verwenden können, um das Problem zu umgehen, das auch wünschenswert wäre e.

Also die Antwort scheint zu sein, Sie brauchen eine Art von Konfiguration/Build-Management-Mechanismus oben auf von Git, um dies zu verwalten. Aber das würde eine Menge Komplexität hinzufügen, so dass die Rosinenpickerei in einen gemeinsamen Zweig übergeht, wäre kein schlechter Weg. Vor allem, wenn Sie andere Techniken verwenden, um die Quellvarianten auf ein Minimum zu reduzieren. (Könnte Markierung helfen, dies zu automatisieren?).

Verwandte Themen