Problem: Ich möchte mit GNU make mehrere Programme in einem Verzeichnis crossplattformfähig bauen lassen und das so, das abhängig vom Betriebssystem, die ausführbare Binärdatei eine andere Dateinamenserweiterung bekommt. Die Compilierung erfolgt dann erst auf dem jeweiligen Zielsystem, also keine Crosscompilierung.
Ein Programm soll also die Dateiendung: *.exe für ausführbare Windowsbinarys erhalten, das passiert automatisch und aber *.bin für ausführbare Linuxbinarys und das passiert nicht automatisch, denn unter Linux bekommt ein ausführbares Binary keine Dateiendung, wenn es nicht irgendwie explizit gefordert wird.
Ich habe also bspw. folgendes Makefile erstellt:
CXX = g++ CC = gcc LD = gcc CFLAGS=-Wall --pedantic -std=c17 -O -c LDFLAGS = all: linux_build clean: linuxclean win_build: EXT = .exe win_build: executable linux_build: EXT = .bin linux_build: executable winclean: EXT = ".exe" winclean: RMCMD = "del" winclean: remove linuxclean: EXT = .bin linuxclean: RMCMD = rm linuxclean: remove executable: prog_a prog_b # Object Files: prog_a.o: prog_a.c $(CC) $(CFLAGS) $< -o $@ prog_b.o: prog_b.c $(CC) $(CFLAGS) $< -o $@ # Executables: prog_a: prog_a.o $(CC) $(LDFLAGS) $^ -o $@$(EXT) prog_b: prog_b.o $(CC) $(LDFLAGS) $^ -o $@$(EXT) # Sauber machen: remove: $(RMCMD) prog_a$(EXT) prog_b$(EXT) prog_a.o prog_b.o
Das Target "all:" muss momentan noch manuell angepasst werden, je nach dem ob ich unter Windows oder Linux compilieren will. Unter Windows werden noch Anpassungen für den Compiler notwendig sein, das habe ich noch nicht getestet, momentan versuche ich aber erst einmal den Linux Binaries eine Dateiendung beizubringen.
Solange ich
1 | make all
|
ausführe, werden die Progamme wie gewünscht erstellt. D.h. die Linux binarys kriegen die Dateiendung .bin Die Variable EXT wird nämlich definiert und ausgewertet.
Wenn ich aber versuche nur eines der Programme zu erstellen. Bspw.
1 | make prog_a
|
dann funktioniert es mit obigem Makefile natürlich nicht mehr, da EXT dann nicht definiert wird und somit auch nicht verwendet werden kann.
Der Versuch $(EXT) im Target des Progammnamens zu definieren, siehe Beispiel unten, funktioniert zwar, ist aber erstens keine besonders elegante Lösung, da ich das ja dann bei jedem Target so machen müsste und viel Redunanz fehleranfällig ist und zweitens ist es dann nicht crossplattformfähig, weil ich dann ja die Einträge für Windows wieder extra anpassen müsste:
# Executables: prog_a: EXT = .bin prog_a: prog_a.o $(CC) $(LDFLAGS) $^ -o $@$(EXT)
Gibt's da also eine brauchbare bessere Lösung?
Vielleicht bspw. so eine Art Subroutine, die nachdem das Target prog_a aufgerufen wurde, diese Subroutine ausgeführt wird und danach zu prog_a wieder zurückkehrt? Aber leider ist make ja deklarativ und nicht imperativ, insofern weiß ich nicht, ob es da etwas in dieser Richtung gibt.
Also in etwa so: (nicht funktionierendes Beispiel)
# Executables: prog_a: os_select prog_a: prog_a.o $(CC) $(LDFLAGS) $^ -o $@$(EXT) os_select: # definiere alles was für Linux nötig ist und springe dann nach prog_a zurück.