Portable Programmierung mit C und Makefiles

Vielleicht ist es ganz gut hier für das allgemeine Verständnis und äh "Archivzwecke" mal etwas in die Grundlagen auszuholen: Auch wenn C und C++ sich über die Jahre auseinanderentwickelt haben, ist das Prinzip in Sachen Toolchain immer noch weitgehend gleich. Und auch wenn es kleinere Differenzen gibt, sind die meisten real existierenden C-Programme noch immer valide C++-Programme. Wenn man C zum laufen hat, funktioniert daher meist auch C++. Und umgekehrt.

Nun braucht man in einer "gehosteten" Umgebung - das sind alle Umgebungen mit richtigem Betriebssystem und dadurch einer einer recht hohen Abstraktion von der Hardware - mindestens 3 Dinge:
  • Eine Toolchain. Die besteht mindestens aus einem Compiler, der den Quelltext über mehrere Schritte hinweg in Assembler übersetzt. Dann einem Assembler, der daraus Objektdateien erzeugt. Und schließlich den Linker, der die Objektdateien zu dem Programm zusammenfügt. Die einzelnen Schritte können beliebig implementiert sein. Es kann ein großes Programm sein, oder auch verschiedene einzelne Programme. Oft hat man einen sogenannten "Driver", das ist ein kleines Wrapper-Programm, was die einzelnen Teile der Toolchain wie benötigt aufruft. Die drei großen Implementierungen sind:
    • Die GNU Compiler Collection in Kombination mit dem GNU Binutils: Der Klassiker, gibt es schon ewig. Implementiert diverse Sprachen, bringt hochqualitative Assembler, mehrere Linker und jede Menge sonstiger Tools mit.
    • Microsoft Visual C++ aka MSVC, der Compiler selbst heißt "cl.exe": Für C etwas das ungeliebte Stiefkind, da Microsoft irgendwann in den 1990ern das Interesse an C verloren hat. Für C++ nach einem Durchhänger in den 2000ern aber wieder top. Kann allerdings nur für Windows bauen und ist zur GCC subtil inkomaptibel.
    • Clang: Auf LLVM basierend, ursprünglich vor allem von Apple aus Ärger über die geringe Kooperationsbereitschaft der GCC-Entwickler und die GPLv3 ins Leben gerufen. Am Anfang nur ein Compiler, implementiert das LLVM-Projekt inzwischen die ganze Toolchain. Clang kann sich mit GCC und MSVC kompatibel verhalten (sowohl in Sachen Code als auch Aufruf) und auf Wunsch sowohl die GNU- als auch die MSVC-Assembler und Linker benutzen.
  • Eine Bibliothek. Für C eine Implementierung der C Standardbibliothek und für C++ eine Implementierung der C++ Standardbibliothek. Sie sind meisten verknüpft, die C++ Bibiliothek braucht eine C Bibliothek. Neben den Bibliotheksfunktionen, Klassen und Datentypen implementieren die Bibliotheken meist auch Startup-Code (also grob alles, was vor dem Aufruf von main() ausgeführt wird) und im Falle von C++ weiteren Supportcode für Laufzeitfunktionalität. Die Bibliotheken sind daher gerade im Fall von C++ mit der Toolchain verknüpft und nicht ohne weiteres austauschbar.
  • Eine Systembibliothek. Das sind Funktionen, Datentypen und Interfaces um mit dem Betriebssystem zu interagieren. Unter unixoiden Systemen ist dies vor allem der unistd.h Header unter Windows der windows.h Header. Die Bibliotheken können eigenständig sein, sind aber in der Praxis meist eng mit der C Standardbibliothek verknüpft.
Die meisten modernen Compiler brauchen dazu noch eine Support-Bibliothek. Das ist für GCC die libgcc, auch Clang kann sie nutzen. Außerdem stellt LLVM mit compiler-rt eine eigene Implementierung zur Verfügung. Für MSVC ist die Support-Bibliothek nicht nicht sichtbar, sie ist wahrscheinlich mit der C/C++-Standardbibliothek integriert.

Unter unixoiden Systemen ist das nun alles relativ einfach. Das System selbst stellt eine C Standardbibliothek und Systembibliothek zur Verfügung, die reinen C-Compiler sind damit glücklich. Für C++ wird in der Linux-Welt meist GCCs libstdc++ genutzt, Apple und die BSDs nutzen für Clang die libc++ des LLVM-Projektes und für GCC die der jeweiligen Version zugehörige libstdc++. Unter Windows ist es aber etwas fummeliger. Windows selbst bringt lediglich eine C89-Standardbibliothek mit, die für praktisch nichts ausreichend ist. Man hat daher zwei Möglichkeiten:
  • Man nutzt die zu MSVC gehörenden Bibliotheken. Das macht logischerweise MSVC selbst, sowie optional Clang für Windows. Der Vorteil ist, dass man durch Microsofts Bibliotheken die bestmögliche Integration in Windows erreicht. Der Nachteil ist, dass man die Bibliotheken mit seiner Anwendung ausliefern muss. Das ist das berüchtigte "Visual C++ Redistributable" Paket, was zu jedem zweiten Programm installiert wird und wo von sich mit der Zeit etliche Versionen im System ansammeln.
  • MinGW in seinen diversen Geschmacksrichtungen hat einen Wrapper über Windows C89-Standardbibliothek geschrieben, auf dem eine angepasste und zu C99 kompatible C-Standardbibliothek und GCCs libstdc++ aufsetzen. GCC nutzt diese, Clang kann sie optional nutzen. Der Vorteil ist, dass man zu Linux sehr ähnliche Bibliotheken (und damit geringen Portierungsaufwand) bekommt. Außerdem muss man kein zusätzliches Paket mit Bibliotheken ausliefern. Der Nachteil ist die vergleichsweise schlechte Integration in Windows, es fühlt sich irgendwie immer wie ein Fremdkörper an.
Welchen Weg man geht, hängt auch stark davon ab, von wo man kommt. Portiert man aus dem Unix-Bereich auf Windows, ist MinGW meist der einfachste Weg. Gefolgt von Clang mit MSVCs Bibliotheken. Ist man hauptsächlich auf Windows fokussiert, ist MSVC als primäre Plattform und ein zusätzliches durchgeschlepptes GCC / Clang meist die Wahl. Will man wirklich plattformunabhängig sein, kommt man nicht drum herum, sowohl MSVC als auch GCC und Clang voll zu unterstützen.

Das ist nun alles recht oberflächlich, aber es hilft hoffentlich zum Verständnis. Es ist sehr zu empfehlen sich auch noch mal mit den unterschiedlichen Binärformaten zu beschäftigen. Das unter unixoiden inzwischen meist verwendete ELF, Apples Mach-O und Windows PE-COFF unterscheiden sich konzeptionell schon sehr. Vor allem bei Bibliotheken, eine Windows .dll ist ein komplett anderes Konzept als eine Unix .so.
 
Danke Yamagi! Ich werde mich wohl oder übel mit clang unter Windows und VC bedschäftigen (müssen). Aber jetzt liegt erst einmal make an. Die Anzahl der öffenen Fragen nimmt kein Ende... :-)
 
Eine kurze Frage zu GIT:

Ich speichere mein Projekt zwischendurch immer wieder auf github (privat). Da ich viel experimentiere entsteht da natürlich eine ellenlange history. Wie kann ich erreichen dass auf master nur noch die neueste Version ist und die history gelöscht wird. Da ein solcher Fall wohl nicht dem normalen Workflow entspricht kann ich dazu aus der Doku von git nicht recht was ableiten (rebase?).
 
Prima! Bleiben da evtl. branches erhalten ? Das reicht vermutlich wenn ich das lokal mache, oder?

Es gibt einige Leute hier die ein Bier oder mehr erwarten können wenn sie mal nach Wien oder Umgebung kommen sollten. :-) (PM!).
 
Warum benutzt du denn dann git und nicht was einfacheres wie Dropbox oder Google Drive oder von mir aus auch rsync?
Die Historie ist der Grund für git...
 
@goblin: Weil ichs habe und es sehr bequem ist. Und falls mal etwas schief geht, kann ich auf eine etwas (!) ältere version zurückgehen. Wie macht man das überigens: Eine ältere Version zum Master machen? Nein , ich habe keine Ahnung von git. ;-)
 
Code:
git checkout $COMMIT_HASH
git branch -D master
git checkout -b master

Anstatt dem hash kannst du auch eine andere Angabe verwenden, die git versteht, wie z.B. HEAD~ oder so.
 
Will man wirklich plattformunabhängig sein, kommt man nicht drum herum, sowohl MSVC als auch GCC und Clang voll zu unterstützen.

"Plattformunabhängig" wie in: "naja, die großen Desktopsysteme halt". Außerhalb derer ist das wieder so eine Sache...ich sag' jetzt nicht "Plan 9". ;)

Meine einzige "größere" Mehrplattform-C++-Anwendung (die hier) scheint unter Windows, GNU/Linux und "den BSDs" gleichermaßen mit einem aktuellen Clang zu funktionieren. Gelegentlich mache ich einen Test mit dem Standard-MSVC-cl, aber tatsächlich nur noch selten. Da für mich als Fuzzi ohne Peilung absehbar scheint, dass Microsoft mittel- bis langfristig ohnehin Clang zum Standardcompiler von MSVC machen wird - warum sollten sie auch zwei mitschleppen? -, sieht es so aus, dass das Problem der Cross-Platform-Entwicklung in C/C++ mit Clang ein vorerst gelöstes ist und irgendwelches "den Compiler für das System, jenen für dieses" eine Verschwendung von Ressourcen ist.

Wie macht man das überigens: Eine ältere Version zum Master machen? Nein , ich habe keine Ahnung von git. ;-)

Git ist für das, was du vorhast, eindeutig zu fett. Ich empfehle für so was ja gerne mal Darcs.
Zu deiner Frage: https://stackoverflow.com/questions...y-master-to-an-older-commit-how-can-i-do-this

Nachtrag: Zu lange getippt. :rolleyes:
 
Was spricht eigentlich gegen BSD make als Build-System auf Unices? Als bmake ist es ja auch für Linux verfügbar.
 
Hallo, die hochgeladene Datei ist natürlich eine Objektdatei und keine EXE, hast du -c verwendet?
Das kann ich nicht ausschließen, da ich mich nicht mehr erinnere. Das wäre natürlich selten doof! Falls ja, mea maxima culpa! Zu meiner Entlastung kann ich nur sagen dass ich da noch ganz am Anfang war. Offenbar muss man bei Anfängern mit allem (!) rechnen...
 
Ich habe noch einmal über git nachgedacht: git rebase -i funtioniert zwar auf dem lokalen repo, auf github ändert sich aber nichts. Auch dann nicht wenn ich das mit 'git push origin master --force' mache wie github help empfielt. Andererseits ist die history auch für mich nicht unwichtig und habe mich daher nach Alternativen umgesehen.

Ich habe mal einen Blick auf Fossil geworfen dass mir interessant erscheint. Dann könne ich die datenbank in dropbox halten und wenn notwenig einen checkout davon machen und nach git pushen. Mir geht es ja nur darum dass nicht jede Kleinigkeit in der git history erscheint, sondern nur solche die Sinn machen und funktionieren. Und natürlich um die Sicherung des aktuellen standes. Hat hier jemand Erfahrung mit Fossil?
 
Ja.

Fossil (wie Darcs, nur deutlich mächtiger) ist ziemlich gut.... Was möchtest du denn wissen?
 
@CrimsonKing: Na ja, wie sich das praktisch macht. Nach einer kurzen Durchsicht der Doku erscheint es mir z.B. dass man jede Datei einzeln hinzufürgen muss was etwas umständlich wäre und nicht wie bei git durch 'git add *'. Kann man z.B. ganze Ordner auf einmal hinzufügen? Kurz: Ist es halbwegs bequem zu benutzen oder muss man da viel rummachen?
 
@derOliver: Ja, habe ich mir mal kurz angeschaut. Aber z.Z. hoffe ich eher auf Fossil da damit ein Unterschied zwischen Datensicherung bzw unfertigen/fehlerhaften code und (möglicherweise öffenlichem) code repository gemacht wird.
 
@darktrym & alle: Ich nehme alles zurück was ich bislang über gcc und -std=c99 und -std=gnu99 gesagt habe. Auch c99 funktioniert mit gcc!

Es bestätigt mal wieder eine alte Erfahrung: Ändere immer nur ein einzige Variable, und niemals mehr! Aufgefallen ist dass nicht da ich mit -c kompiliert und unter -o main.exe geschrieben habe. Ich wußte da noch nicht dass .exe automatisch angehängt wird.

Me: as blind as men can be!
 
@CrimsonKing: Habe ich nur überflogen. Ich glaube ich leide ein wenig an information-overflow. Das sind alles neue Themen für mich, ich ecke dauernd an und mir fehlt noch das Bild für die größeren Zusammenhänge, -vielleicht ein reichlich unscharfes mit allzu vielen Fehlern...

Dennoch: Es fängt an mir Spass zu machen und zu sehen warum das so ist wie es eben ist. Mein Respekt für Leute die sich da wirklich gut auskennen wächst in unermessliche.

NB: Mir fällt auf dass make insofern eine gewisse ähnlichkeit mit Lisp hat als es sich immer um das abarbeiten von Listen nach bestimmten Regeln handelt. Allerdings ist die Makrosprache beschränkt und wenig intuitiv.
 
Die meisten VCSe verhalten sich recht ähnlich. add zum Hinzufügen, rm oder forget zum Entfernen. Nur das "Einchecken" ist überall völlig verschieden.

Man kriegt dafür ein Gefühl irgendwann.
 
Zurück
Oben