staging.inyokaproject.org

Wie crossplattform fähiges Makefile mit GNU Make mit Dateinamenserweiterung für Linuxbinary?

Status: Gelöst | Ubuntu-Version: Kubuntu 21.04 (Hirsute Hippo)
Antworten |

Cordess

Anmeldungsdatum:
14. Mai 2006

Beiträge: Zähle...

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.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 10978

Reicht da nicht eine einfache Fallunterscheidung?

1
2
3
4
5
6
7
ifeq ($(OS),Windows_NT)     # is Windows_NT on XP, 2000, 7, Vista, 10...
    EXT := .exe
    RMCMD := del
else
    EXT := .bin
    RMCMD := rm
endif

Cordess

(Themenstarter)

Anmeldungsdatum:
14. Mai 2006

Beiträge: Zähle...

seahawk1986 schrieb:

Reicht da nicht eine einfache Fallunterscheidung?

1
2
3
4
5
6
7
ifeq ($(OS),Windows_NT)     # is Windows_NT on XP, 2000, 7, Vista, 10...
    EXT := .exe
    RMCMD := del
else
    EXT := .bin
    RMCMD := rm
endif

Danke, das hat super geklappt.

Ich habe deinen Code an einer beliebigen Stelle im Makefall reinkopiert, das all Target auf executable angepasst und schon hat alles funkioniert.

Ich frage mich gerade, ab wann diese Fallunterscheidung ausgewertet wird?

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 10978

Soweit ich das verstehe wird das Makefile wird immer zuerst vollständig geparsed (dahier spielt die Position von Anweisungen in der Datei keine große Rolle, solange keine Werte überschrieben werden bzw. es um das Standard-Target geht) und dann weird das ausgewählte Target unter Auflösung der Abhängigkeiten abgearbeitet.

Cordess

(Themenstarter)

Anmeldungsdatum:
14. Mai 2006

Beiträge: 462

Danke.

Jetzt habe ich aber noch ein kleines weiteres Problem.

Ich habe das clean target etwas angepasst. Das sieht jetzt ungefähr so aus, hat allerdings noch mehr Dateien:

1
2
3
clean:
	$(RMCMD) prog_a$(EXT) prog_b$(EXT)
	$(RMCMD) prog_a.o prog_b.o

Wenn man mit

make all

alles baut und anschließend

make clean

ausführt, funktioniert alles wie gewünscht.

Wenn man aber nur ein Target baut. Z.B. prog_b

make prog_b

Dann werden davon zwar die *.c Dateien gelöscht, aber weil es keine *.c Datei von prog_a gibt, bricht make mit einer Fehlermeldung ab und die Objektdatei von prog_b bleibt übrig.

Wie kann ich das lösen? Oder anders gefragt, wie verhindere ich, dass make eine Fehlermeldung für Dateien meldet, die nicht da sind, aber bei denen rm versucht, diese zu löschen? Am besten wäre es also, wenn rm nur die Dateien zu löschen versucht, die auch existieren. Für del müsste dann das gleiche geschehen. Also so, dass der Fehlerhafte Rückgabewert vermieden wird und make mit dem löschen der Objektdateien weitermacht.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 10978

Da musst du dich mit den Besonderheiten der Löschbefehle auseinander setzen - rm kennt den Schalter -f, um nicht existierende Pfade zu ignorieren und bei del kann man vorher prüfen, ob die Datei vorhanden ist: https://www.askingbox.de/frage/batch-skript-datei-loeschen-falls-sie-existiert oder eine Schleife (vgl. z.B. https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490909(v=technet.10)) nutzen:

1
for %i in (*.o *.exe *.bin) do del %i

Cordess

(Themenstarter)

Anmeldungsdatum:
14. Mai 2006

Beiträge: 462

Besten Dank.

Mit rm -f klappt es.

Die for Schleife werde ich noch unter Windows testen.

Antworten |