Portable Programmierung mit C und Makefiles

rubricanis

Homo ludens
I ch nehme mal das Thema vom vorangegangen thread auf:
Ach, ausnahmslos alle Build-Systeme sind warzig... Nach Jahren der Erfahrung würde ich, wenn nichts anderes durch die Sprache, die Umgebung oder das Projekt vorgegeben ist, immer GNU Make nehmen. Ja, GNU Make hat wie alle Make-Dialekte eine recht eklige Syntax und einige Dinge sind nur mit dem Kopf durch den Hintern möglich. Und auch wenn GNU Make eine der schnelleren Make-Implementierungen ist, ist es im Vergleich zu neueren Systemen wie Ninja einfach lahm. Aber: GNU Make gibt es auf praktisch allen Plattformen, die einen C-Compiler haben. Und ein sauber geschriebenes GNU Makefile funktioniert auf all diesen Plattformen einfach. Im Gegensatz zu all den hippen Lösungen, die oft schon auseinanderfallen, wenn der Entwickler Debian nutzt und man es auf CentOS bauen will. Außerdem ist GNU Make über die Jahre sehr stabil, ein Makefile von 1998 ist heute noch valide und wird es 2038 wahrscheinlich auch noch sein. Bei der Konkurrenz muss man oft schon nach 3 Jahren und 5 Unterversionen nachfrickeln, weil sich irgendwas am Syntax geändert hat oder es sich dann doch irgendwo subtil anders verhält.

Windows mit mingw-64 ist ist ein Härtetest für portable makefiles. Ich habe jetzt versucht drei mittelgroße Projekte bei denen ich an den libs interessiert bin damit zu kompilieren, und keines hat funktioniert, das obwohl angeblich beide angepaßt sind. Jetzt könnte ich mich natürlich daran machen mir die makefiles im Detail anzusehen und zu debuggen. Das erscheint mir auf Dauer aber wenig aussichtsreich denn wie es scheint hat jeder der Autoren andere Vorstellungen darüber wie ein Makefile aussehen sollte und wie Compiler und Umgebung konfiguriert sind womit man sich bei jedem weiteren Projekt befassen müßte.

Aussichtsreicher erscheint mir nur die *.c und *.h zu benutzen, in ein von mir selbst entwickeltes file layout [src/<projec>t, inc/<project>] einzufügen und dann ein eigenes Makefile dafür zu schreiben, - für was gibt es schließlich open source? Das ist zwar auch einiges an Arbeit aber besser an den eigenen Fehlern zu scheitern, die zu korrigieren und daran zu lernen als an denen anderer rumzufrickeln! Und schließlich hat man ja noch dir orig. makefiles zum studieren. Was man damit natürlich aufgibt ist die mehr oder minder automatische Aktualisierung durch git. Man muss die jeweiligen Projekte daher im Blick behalten und ggf. selbst aktualisieren. Das ist jetzt mein Plan, was denkt ihr dazu?

Was ich in der kurzen Zeit in der ich mich damit beschäftige gelernt habe ist, dass es sich lohnt einen wirklich tiefen Blick in die Dokumentation von gcc und (g)make zu werfen, da gibt es doch einiges das man vorteilhaft verwenden kann. Leider fehlt es dan an Beispielen und manches ist wirklich schwer verständlich wenn man noch am Anfang ist. Wir werden sehen und ich werde berichten...
 
Projekte, die ich auf mehreren Systemen halbwegs sturzfrei bauen lassen möchte und bei denen es nicht mit einem cc -o foo main.c getan ist. "wrappe" ich derzeit in CMake. Die Buildfilesyntax ist teilweise zum Speien, aber wenigstens kennt CMake ausreichend viele Compiler. Nur schade, dass es unter Windows ausschließlich Visual Studio benutzen kann, Clang lässt sich nicht einstellen. (Einer der Gründe, wieso ich auch Alternativen suche.)

Die Herausforderung ist es, ein Projekt so einzurichten, dass es BWL-Kasper Müller mit seinem Visual Studio 2017, aber ohne Peilung, ebenso kompilieren kann wie 1337 h4X0r Schmidt mit seinem, sagen wir, NixOS mit GCC 12-Alpha. Ich verstehe schon, dass das nicht gerade trivial ist...
 
Dann spende ich noch mal ein paar Codeschnippsel:


Betriebssystemerkennung

Der Code deckt alle gängigen Systeme ab, inklusive Windows. Wenn man sehr ausgefallene Systeme will, müsste man die noch nachtragen. Ich hatte da einfach nie Bedarf dran.
Code:
# Detect the OS    
ifdef SystemRoot    
OSTYPE := Windows     
else     
OSTYPE ?= $(shell uname -s)     
endif    

# Special case for MinGW    
ifneq (,$(findstring MINGW,$(YQ2_OSTYPE)))    
OSTYPE := Windows    
endif


Architekturerkennung

Die Architektur zu erkennen ist fummelig, weil es x Varianten der jeweiligen Architecture Strings gibt. Das hier ist genug um unter Windows i386 und amd64 zu unterscheiden. Auf Linux und allen BSDs erkennt es i386, amd64, arm, ppc und sparc. Es deckt keine seltenen Fälle wie Windows auf arm ab, ich habe sie nie gebraucht.

Code:
# Detect the architecture    
ifeq ($(OSTYPE), Windows)    
ifdef PROCESSOR_ARCHITEW6432    
# 64 bit Windows    
ARCH ?= $(PROCESSOR_ARCHITEW6432)    
else    
# 32 bit Windows    
ARCH ?= $(PROCESSOR_ARCHITECTURE)    
endif    
else    
# Normalize some abiguous ARCH strings    
ARCH ?= $(shell uname -m | sed -e 's/i.86/i386/' -e 's/amd64/x86_64/' -e 's/^arm.*/arm/')    
endif


Compilererkennung

Der Code erkennt Clang und GCC. Weitere Compiler lassen sich leicht hinzufügen, man braucht nur eine Regex, die die entsprechenden Informationen herauspickt. Auch fragt er nur CC ab und nicht CXX.

Code:
# Detect the compiler     
ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1)     
COMPILER := clang     
COMPILERVER := $(shell $(CC)  -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/')     
else ifeq ($(shell $(CC) -v 2>&1 | grep -c -E "(gcc version|gcc-Version)"), 1)     
COMPILER := gcc     
COMPILERVER := $(shell $(CC)  -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/')     
else     
COMPILER := unknown    
endif
 
Danke Yamagi! Das erfordert aber dass man mit den unix tools (greb, sed etc) vertraut ist, was bei mir - und vermutlich manch anderem - leider nicht der Fall ist. Dann wird es schwierig wenn mal irgend etwas nicht läuft.

Ich werde es so wie Lua machen und die Parameter explizit übergeben:
Code:
make <target> platform=<os> cc=<compiler> arch=<architecture>  debug=on echo=on ...
Die Parameter werden dann an die Sub-Makefiles weiter gereicht. Das funktioniert auch prima, nur mit der Übergabe der Flags habe ich da noch Probleme, aber das muss ich mir noch genauer anschauen. Da ich vorhabe die Sub-Makefiles selbst zu entwickeln werden die Parameter auch verstanden. Ich denke dass das so ziemlich flexibel und zuverlässig ist. Klar die Parameterübergabe an die Sub-Makefiles kostet Laufzeit aber man kann eben nicht alles haben...
 
Also langsam habe ich die faxen dick! Ich habe mingw-w64 heruntergeladen und installiert. Der gcc produziert allerdings nur 32bit executables die auf meinem 64bit win10 nicht laufen. Warum eigentlich nicht? Ich dachte immer das ginge. Wie auch immer: Dann gibt es da auch ein i686-w64-mingw32-gcc. Aber wer meint damit würden 64bits funktionieren, irrt! Auch mit -m64 konnte ich keinen der beiden überreden. Warum habe ich dann mingw-w64 heruntergeladen? Oder soll ich das selbst kompilieren, und womit?

Na gut, clang könnte eine Alternative sein. Also clang installiert. Funktioniert auch, findet nur <stdlib.h> nicht. Kurze Reche: Der wird ohne headers geliefert und erwartet die offenbar vom system. Warum das? Was man allerdings machen kann ist die Header von mingw verwenden. Also -I<path to *.h> eingefügt. Das Ergebnis sind 6 Warungen und 6 Fehler. Die Warnungen könnte ich abstellen, aber auch die Fehler? Eines muss man clang zugestehen: Exakte Fehlermeldungen! Ein kurzer Blick in die Header zeigt, dass das alles mingw Macros sind die hinter den Declarationen stehen. Natürlich könnte ich die editieren, aber macht das Sinn? Und wie ist es dann mit der Portabilität?

Ich habe die Communiy editions von VC 2015 und VC 2017 installiert. Aber wo finde ich da die *.h um die clang zu übergeben. Ich finde da nichts und vermute dass sie nur in der pro-vers. öffentlich gemacht werden. Oder bin ich blind? Eine Recherche im inet hat mich leider nicht wirklich weiter gebracht.

Also folgende Fragen:
(1) Hat es schon jemand geschafft mingw für 64 bit unter win10 zu installieren, und wenn ja wie?
(2) Wie bekomme ich clang unter win10 wirklich zum laufen und brache ich dafür die Pro-Version?
(3) Kann ich ein externes make project unter Visual Studio zum laufen bingen?

Ich bin für jeden Hinweis dankbar!

Peter

NB: Windows hier im Forum? Wer das Wort Portabilität ernst nimmt, kommt eben um windows und auch den mac nicht herum! Der letztere dürfte ein geringeres Problem sein.

Nachtrag: rust war (nachdem ich die Community edition von VC15 installiert hatte) problemlos zu benutzen, - zumindest soweit das ein kurzer test zeigt. Rust portabler als c?
 
Ich habe mingw aus u.a. diesem Grund nie ernsthaft benutzt: Es ist eine ziemliche Pest, diesen emulierten Firlefanz sauber ins System zu integrieren. Verstehe nicht, wieso sich Leute das antun. Seit es Clang für Windows gibt, besteht für den Großteil der Projekte kein Anlass mehr, unbedingt GCC haben zu wollen - zumal Visual Studio selbst Clang/LLVM unterstützt und sogar mitbringt.

Ja, ich nutze für meinen Privatgebrauch eigentlich ausschließlich (das native) Clang unter Windows 10 (Home), derzeit gemeinsam mit VS 2017 Community. Ich musste allerdings nie irgendwelche Pfade irgendwo angeben.
 
Gott sei Dank bringen die meisten Open Source Projekte Support für die VS Compiler mit.
Bis auf Lua sind mir wenige Projekte im Gedächtnis geblieben die wirklich Crossplattform waren, getestet unter Windows und sich bauen ließen.
 
@Yamagi: Nachdem ich die ersten Schwierigkeiten wg. Fehler i.d. Doku überwunden habe, habe ich den mingw-w64-x86_64 toolchain installiert. An pacman muss man sich erst gewöhnen. Zwar finde ich die std*.h auch hier nicht, aber das wird wohl gcc irgendwie hinbekommen. Ob ich heute die Nerven habe das noch auszuprobieren, bezweifel ich. Wir werden sehen...

In jedem Fall vielen Dank erst einmal!
 
Seit es Clang für Windows gibt, besteht für den Großteil der Projekte kein Anlass mehr, unbedingt GCC haben zu wollen - zumal Visual Studio selbst Clang/LLVM unterstützt und sogar mitbringt.

Verstehe ich das richtig das VC-7 (community edition) clang mitbringt? Wie auch immer: Mit VC habe ich mich noch nicht befaßt und werde ggf darauf zurückkommen wenn ich Zeit (und Lust!) dazu habe.

Vielen Dank für Info!
 
Bis auf Lua sind mir wenige Projekte im Gedächtnis geblieben die wirklich Crossplattform waren, getestet unter Windows und sich bauen ließen.
Mit dem normalen mingw-64 hat das auch hier nicht funktioniert. Mal sehen ob es unter MYSY2 klappt. Mir ist das wichtig da ich Lua gerne embedded verwenden möchte.
 
Nachdem ich auch auch make installiert und .../mysys2/usr/bin zum Pfad hinzugefügt habe, funktioniert das schon einmal. Leider läuft auch wie zuvor hier die .exe nicht.
Hier mein übersimples programm...
Code:
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[])  {
    printf("Hallo World");
    return 0;
}
...und hier die cmdline.
Code:
main:
    gcc -std=c99 -m64 -c  src/main/main.c -o  bin/main.exe

Müßte das laufen oder fehlt da noch entscheidendes? Irgendwelche libs oder so?

NB: Bei solchen Fummeleien ist ein kleiner EnvironmentEditor recht brauchbar. So etwas nervt unter windows wirklich !
 
Hab hier gerade ein feines Compiler Howto gefunden:

C und Compiler howto

Sicher hilfreich für Newbees und EInsteiger, für alte Hasen sicher nicht.;) Genau gesehn ist es natürlich eher ein C HowTo.
 
Nachdem ich auch auch make installiert und .../mysys2/usr/bin zum Pfad hinzugefügt habe
Wieso nimmst du nicht die MSYS2-Shell? :) Also ..\mingw32.exe und ..\mingw64.exe, je nachdem ob du 32- oder 64-Bit möchtest. Dann hast du fertig aufgebaute Pfade mit allem drin und dran.

Müßte das laufen oder fehlt da noch entscheidendes? Irgendwelche libs oder so?
Was funktioniert denn nicht? Crasht die Executable? Auf jeden Fall würde ich aber noch ein -static-libgcc mitgeben, damit du nicht die libgcc.dll zusammen mit der Binary ausliefern musst.
 
Wieso nimmst du nicht die MSYS2-Shell? :) Also ..\mingw32.exe und ..\mingw64.exe, je nachdem ob du 32- oder 64-Bit möchtest. Dann hast du fertig aufgebaute Pfade mit allem drin und dran.

Weil ich damit nicht aus dem Mysys2-Ordner (habe ich lokal installiert) heraus und in meinen Projekt-Ordner komme. Die scheinen zu meinen sie wären das OS. ;-) Und seit wann ist es von der Shell abhängig ob etwas läuft oder nicht? Die soll machen was ich sage, nicht was sie meint was richtig wäre! Also benutze ich cmd.exe.

Auch -static-libgcc ändert nichts. Hier die Fehlermeldung beim starten der executable:

"Die Version von C:\...\main.exe ist mit der ausgeführten Windows-Version nicht kompatibel. Überprüfen Sie die Systeminformationen des Computers, und wenden Sie sich anschließend an den Herausgeber der Software."

Der Herausgeber der Software ist ratlos! ;-)

Hier die Ausgabe von gcc -v, vielleicht kann jemand damit etwas anfangen:

Using built-in specs.
Target: x86_64-w64-mingw32
Configured with: ../gcc-8.2.0/configure --prefix=/mingw64 --with-local-prefix=/mingw64/local --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --with-native-system-header-dir=/mingw64/x86_64-w64-mingw32/include --libexecdir=/mingw64/lib --enable-bootstrap --with-arch=x86-64 --with-tune=generic --enable-languages=ada,c,lto,c++,objc,obj-c++,fortran --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-filesystem-ts=yes --enable-libstdcxx-time=yes --disable-libstdcxx-pch --disable-libstdcxx-debug --disable-isl-version-check --enable-lto --enable-libgomp --disable-multilib --enable-checking=release --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-libiconv --with-system-zlib --with-gmp=/mingw64 --with-mpfr=/mingw64 --with-mpc=/mingw64 --with-isl=/mingw64 --with-pkgversion='Rev1, Built by MSYS2 project' --with-bugurl=https://sourceforge.net/projects/msys2 --with-gnu-as --with-gnu-ld
Thread model: posix
gcc version 8.2.0 (Rev1, Built by MSYS2 project)


Ich sträube mich ein wenig an dieser Stelle aufzugeben...
 
Weil ich damit nicht aus dem Mysys2-Ordner (habe ich lokal installiert) heraus und in meinen Projekt-Ordner komme. Die scheinen zu meinen sie wären das OS. ;-)
'cd /c/' bringt dich nach C:\, 'cd /d/' nach D:\ und so weiter. Und ja 'ls /' zeigt das nicht an...

"Die Version von C:\...\main.exe ist mit der ausgeführten Windows-Version nicht kompatibel. Überprüfen Sie die Systeminformationen des Computers, und wenden Sie sich anschließend an den Herausgeber der Software."
Das ist ... interessant. Und macht mich ein wenig ratlos. Hmm... Kann man herausfinden mit welcher Version von Windows die Executable denn kompatibel wäre, also um welchen Typ es sich handelt?
 
Das ist ... interessant. Und macht mich ein wenig ratlos. Hmm... Kann man herausfinden mit welcher Version von Windows die Executable denn kompatibel wäre, also um welchen Typ es sich handelt?
Da habe ich z.Z. keine Idee, aber vielleicht fällt mir (oder jemanden anderes) dazu noch etwas ein...

Ich frage mich allerdings ob das überhaupt der richtige Weg ist und ich mich nicht besser mit VC-7 (community) beschäftigen sollte. Seit meinem Abschied von IDEs mangelt es mir hierzu allerdings an Lust. Ich weiss allerdings nicht welche Restriktionen die Sommunity Edition einem auferlegt. Es ist wirklich fatal!

@KrimsonKing: Oder kann man das alles auch gut von der VC-shell aus regeln?
 
Ich gebe gcc auf windows erst einmal auf und werde mich ein wenig mit VisualStudio beschäftigen, - schaden kann das ja nicht! Das ist mir alles zu fragil! Und wenn man den Nachrichten von Google und Mozilla glauben kann, wird es ja eines Tages clang auch auch auf windows geben.
 
Und denk daran vorher die Batch aufzurufen die die Umgebungsvariablen setzt, sonst wird das nix, also diese vcvarsall.
Ich habe gerade auch lua mit gcc gebaut, so kaputt kann mingw nicht sein, vorausgesetzt man hat Path erweitert.
 
Ich habe gerade auch lua mit gcc gebaut, so kaputt kann mingw nicht sein, vorausgesetzt man hat Path erweitert.
Hmm, das läßt mich hoffen! Welches mingw64 hats du verwendet?

- MinGW, gcc 4.8 wenn ich mich recht erinnere.
- Mingw-w64, gcc 8.1
-
Mysys2, gcc 8.1
-
TDM-GCC, gcc 5.1

Mingw-w64 und Mysys2 hatbe ich ausprobiert aber vielleicht auch irgendeinen Fehler gemacht. Vor einigen Jahren hatte ich TDM-GCC verwendet der gut funktionierte, aber ich habe jetzt die neueren bevorzugt. Ich habe die jetzt lokal in meinem ~/Verzeichnis installiert und die Pfade auf die binaries gesetzt.

Und denk daran vorher die Batch aufzurufen die die Umgebungsvariablen setzt, sonst wird das nix, also diese vcvarsall.
Was ist vcvarsall? Gibt es bei Mysys2 nicht.
 
Und außerdem: Was hast Du denn gegen IDEs?
Cmdline und Text-Editor sind einfacher und man braucht sich nicht in die jeweilige IDE einzuarbeiten. Zur Zeit ist mit Visual-C noch sehr fremd und da ist mir zuviel magic dahinter die ich nicht verstehe. Das Thema wurde hier aber schon ausgiebig diskutiert und ist m.E. in diesem thread OT.
 
Hmm, habe gerade noch einmal Lua 5.3.5 ausprobiert und das funktioniert: Lua.exe läuft, liblua.a und lua53.dll gibt es auch. Also liegt der Fehler bei mir, - wie immer! :-)

Das muss ich mir also alles noch genauer anschauen. Es nimmt kein Ende...
 
Zurück
Oben