Makefile zur Erkennung des Betriebssystems

249

Ich arbeite routinemäßig auf mehreren verschiedenen Computern und verschiedenen Betriebssystemen, nämlich Mac OS X, Linux oder Solaris. Für das Projekt, an dem ich arbeite, ziehe ich meinen Code aus einem Remote-Git-Repository.

Ich möchte in der Lage sein, an meinen Projekten zu arbeiten, unabhängig davon, an welchem ​​Terminal ich mich befinde. Bisher habe ich Wege gefunden, um die Betriebssystemänderungen zu umgehen, indem ich das Makefile jedes Mal ändere, wenn ich den Computer wechsle. Dies ist jedoch langwierig und verursacht eine Reihe von Kopfschmerzen.

Wie kann ich mein Makefile so ändern, dass es erkennt, welches Betriebssystem ich verwende, und die Syntax entsprechend ändert?

Hier ist das Makefile:

cc = gcc -g
CC = g++ -g
yacc=$(YACC)
lex=$(FLEX)

all: assembler

assembler: y.tab.o lex.yy.o
        $(CC) -o assembler y.tab.o lex.yy.o -ll -l y

assembler.o: assembler.c
        $(cc) -o assembler.o assembler.c

y.tab.o: assem.y
        $(yacc) -d assem.y
        $(CC) -c y.tab.c

lex.yy.o: assem.l
        $(lex) assem.l
        $(cc) -c lex.yy.c

clean:
        rm -f lex.yy.c y.tab.c y.tab.h assembler *.o *.tmp *.debug *.acts
Samoz
quelle

Antworten:

282

Hier gibt es bereits viele gute Antworten, aber ich wollte ein vollständigeres Beispiel nennen, das beide:

  • geht nicht davon aus, dass unamees unter Windows existiert
  • erkennt auch den Prozessor

Die hier definierten CCFLAGS sind nicht unbedingt empfohlen oder ideal. Sie sind genau das, was das Projekt, zu dem ich die automatische Erkennung von Betriebssystemen / CPUs hinzugefügt habe, zufällig verwendet hat.

ifeq ($(OS),Windows_NT)
    CCFLAGS += -D WIN32
    ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
        CCFLAGS += -D AMD64
    else
        ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
            CCFLAGS += -D AMD64
        endif
        ifeq ($(PROCESSOR_ARCHITECTURE),x86)
            CCFLAGS += -D IA32
        endif
    endif
else
    UNAME_S := $(shell uname -s)
    ifeq ($(UNAME_S),Linux)
        CCFLAGS += -D LINUX
    endif
    ifeq ($(UNAME_S),Darwin)
        CCFLAGS += -D OSX
    endif
    UNAME_P := $(shell uname -p)
    ifeq ($(UNAME_P),x86_64)
        CCFLAGS += -D AMD64
    endif
    ifneq ($(filter %86,$(UNAME_P)),)
        CCFLAGS += -D IA32
    endif
    ifneq ($(filter arm%,$(UNAME_P)),)
        CCFLAGS += -D ARM
    endif
endif
Trevor Robinson
quelle
8
Leider PROCESSOR_ARCHITECTUREscheint der envvar virtualisiert zu sein, je nachdem, ob der Prozess 32-Bit oder 64-Bit ist. Wenn Sie makealso 32-Bit sind und versuchen, eine 64-Bit-Anwendung zu erstellen, schlägt dies fehl. Die Verwendung in Kombination mit PROCESSOR_ARCHITEW6432hat für mich funktioniert (siehe dies und das )
Thomas
4
Wäre schön, wenn das makeTeam ein paar magische Variablen mit OS und Arch hinzufügen würde, wahrscheinlich zu viel Mühe.
Alex
6
@ JanusTroelsen: Es sollte keine Rolle spielen, ob OSauf Nicht-Windows-Systemen eingestellt ist. Deaktivieren Sie Leckereien genauso wie leer, was zu einem Sprung zum unameBlock auf der Basis führt. Sie müssen dort nur einen FreeBSD-Check hinzufügen.
Trevor Robinson
3
dies bricht auch auf osx. /bin/sh: -c: line 0: syntax error near unexpected token , Windows_NT '/ bin / sh: -c: Zeile 0:ifeq (,Windows_NT)' make: *** [os] Error 2
k107
1
@kristi Es hört sich so an, als hätten Sie dies als Shell-Befehle ausgeführt und nicht im Kontext der Makefile-Direktiven.
Phord
119

Der Befehl uname ( http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) ohne Parameter sollte Ihnen den Namen des Betriebssystems mitteilen. Ich würde das verwenden und dann Bedingungen basierend auf dem Rückgabewert erstellen.

Beispiel

UNAME := $(shell uname)

ifeq ($(UNAME), Linux)
# do something Linux-y
endif
ifeq ($(UNAME), Solaris)
# do something Solaris-y
endif
dbrown0708
quelle
Um genau zu sein, diese Zeile wird in Ihr Makefile eingefügt. Ich habe dieses Konstrukt gerade in Makefiles in Cygwin und OSX ausprobiert und es hat wie erwartet funktioniert. Versuchen Sie Folgendes: Geben Sie uname in Ihre Befehlszeile ein. Das sagt Ihnen den Wert für dieses Betriebssystem. OSX wird wahrscheinlich "Darwin" sein.
dbrown0708
Im GnuWin32-Projekt sind sowohl uname als auch Gnu als native Windows-Anwendungen verfügbar, sodass diese Technik an einer Eingabeaufforderung für MingW sowie für Cygwin unter Windows portierbar ist.
RBerteig
Auf meinem Solaris-Computer schlägt dies fehl, wenn es sich im Makefile befindet. Der Befehl uname ist auf diesem System verfügbar.
Samoz
4
Beachten Sie, dass wenn Sie dies in ein Maketarget einfügen, es nicht eingerückt werden darf.
Nylund
4
Ist die Syntax ": =" nicht spezifisch für GNU Make?
Ankur Sethi
40

Erkennen Sie das Betriebssystem mit zwei einfachen Tricks:

  • Zuerst die Umgebungsvariable OS
  • Dann der unameBefehl
ifeq ($(OS),Windows_NT)     # is Windows_NT on XP, 2000, 7, Vista, 10...
    detected_OS := Windows
else
    detected_OS := $(shell uname)  # same as "uname -s"
endif

Oder sicherer, wenn nicht unter Windows und unamenicht verfügbar:

ifeq ($(OS),Windows_NT) 
    detected_OS := Windows
else
    detected_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown')
endif

Ken Jackson schlägt eine interessante Alternative vor, wenn Sie Cygwin / MinGW / MSYS / Windows unterscheiden möchten. Sehen Sie seine Antwort , die so aussieht:

ifeq '$(findstring ;,$(PATH))' ';'
    detected_OS := Windows
else
    detected_OS := $(shell uname 2>/dev/null || echo Unknown)
    detected_OS := $(patsubst CYGWIN%,Cygwin,$(detected_OS))
    detected_OS := $(patsubst MSYS%,MSYS,$(detected_OS))
    detected_OS := $(patsubst MINGW%,MSYS,$(detected_OS))
endif

Dann können Sie die relevanten Dinge auswählen, abhängig von detected_OS:

ifeq ($(detected_OS),Windows)
    CFLAGS += -D WIN32
endif
ifeq ($(detected_OS),Darwin)        # Mac OS X
    CFLAGS += -D OSX
endif
ifeq ($(detected_OS),Linux)
    CFLAGS   +=   -D LINUX
endif
ifeq ($(detected_OS),GNU)           # Debian GNU Hurd
    CFLAGS   +=   -D GNU_HURD
endif
ifeq ($(detected_OS),GNU/kFreeBSD)  # Debian kFreeBSD
    CFLAGS   +=   -D GNU_kFreeBSD
endif
ifeq ($(detected_OS),FreeBSD)
    CFLAGS   +=   -D FreeBSD
endif
ifeq ($(detected_OS),NetBSD)
    CFLAGS   +=   -D NetBSD
endif
ifeq ($(detected_OS),DragonFly)
    CFLAGS   +=   -D DragonFly
endif
ifeq ($(detected_OS),Haiku)
    CFLAGS   +=   -D Haiku
endif

Anmerkungen:

  • Der Befehl unameist der gleiche wie, uname -sda option -s( --kernel-name) die Standardeinstellung ist. Sehen Sie, warum uname -sist besser alsuname -o .

  • Die Verwendung von OS(anstelle von uname) vereinfacht den Identifikationsalgorithmus. Sie können immer noch ausschließlich verwenden uname, müssen sich jedoch mit if/elseBlöcken befassen , um alle Variationen von MinGW, Cygwin usw. zu überprüfen.

  • Die Umgebungsvariable OSist in "Windows_NT"verschiedenen Windows-Versionen immer auf eingestellt (siehe %OS%Umgebungsvariable auf Wikipedia ).

  • Eine Alternative dazu OSist die Umgebungsvariable MSVC(sie überprüft das Vorhandensein von MS Visual Studio , siehe Beispiel mit Visual C ++ ).


Im Folgenden gebe ich ein vollständiges Beispiel für die Verwendung makeund gccErstellung einer gemeinsam genutzten Bibliothek: *.sooder *.dllje nach Plattform. Das Beispiel ist so einfach wie möglich, um verständlicher zu sein.

Informationen zur Installation makeund gccunter Windows finden Sie unter Cygwin oder MinGW .

Mein Beispiel basiert auf fünf Dateien

 ├── lib
    └── Makefile
    └── hello.h
    └── hello.c
 └── app
     └── Makefile
     └── main.c

Erinnerung: Makefile wird mithilfe einer Tabelle eingerückt . Vorsicht beim Einfügen unter Beispieldateien.

Die zwei MakefileDateien

1. lib/Makefile

ifeq ($(OS),Windows_NT)
    uname_S := Windows
else
    uname_S := $(shell uname -s)
endif

ifeq ($(uname_S), Windows)
    target = hello.dll
endif
ifeq ($(uname_S), Linux)
    target = libhello.so
endif
#ifeq ($(uname_S), .....) #See https://stackoverflow.com/a/27776822/938111
#    target = .....
#endif

%.o: %.c
    gcc  -c $<  -fPIC  -o $@
    # -c $<  => $< is first file after ':' => Compile hello.c
    # -fPIC  => Position-Independent Code (required for shared lib)
    # -o $@  => $@ is the target => Output file (-o) is hello.o

$(target): hello.o
    gcc  $^  -shared  -o $@
    # $^      => $^ expand to all prerequisites (after ':') => hello.o
    # -shared => Generate shared library
    # -o $@   => Output file (-o) is $@ (libhello.so or hello.dll)

2. app/Makefile

ifeq ($(OS),Windows_NT)
    uname_S := Windows
else
    uname_S := $(shell uname -s)
endif

ifeq ($(uname_S), Windows)
    target = app.exe
endif
ifeq ($(uname_S), Linux)
    target = app
endif
#ifeq ($(uname_S), .....) #See https://stackoverflow.com/a/27776822/938111
#    target = .....
#endif

%.o: %.c
    gcc  -c $< -I ../lib  -o $@
    # -c $<     => compile (-c) $< (first file after :) = main.c
    # -I ../lib => search headers (*.h) in directory ../lib
    # -o $@     => output file (-o) is $@ (target) = main.o

$(target): main.o
    gcc  $^  -L../lib  -lhello  -o $@
    # $^       => $^ (all files after the :) = main.o (here only one file)
    # -L../lib => look for libraries in directory ../lib
    # -lhello  => use shared library hello (libhello.so or hello.dll)
    # -o $@    => output file (-o) is $@ (target) = "app.exe" or "app"

Weitere Informationen finden Sie in der Dokumentation zu automatischen Variablen , auf die von cfi hingewiesen wird .

Der Quellcode

- - lib/hello.h

#ifndef HELLO_H_
#define HELLO_H_

const char* hello();

#endif

- - lib/hello.c

#include "hello.h"

const char* hello()
{
    return "hello";
}

- - app/main.c

#include "hello.h" //hello()
#include <stdio.h> //puts()

int main()
{
    const char* str = hello();
    puts(str);
}

Der Build

Korrigieren Sie das Kopieren und Einfügen von Makefile(ersetzen Sie führende Leerzeichen durch eine Tabelle).

> sed  's/^  */\t/'  -i  */Makefile

Der makeBefehl ist auf beiden Plattformen gleich. Die angegebene Ausgabe erfolgt unter Unix-ähnlichen Betriebssystemen:

> make -C lib
make: Entering directory '/tmp/lib'
gcc  -c hello.c  -fPIC  -o hello.o
# -c hello.c  => hello.c is first file after ':' => Compile hello.c
# -fPIC       => Position-Independent Code (required for shared lib)
# -o hello.o  => hello.o is the target => Output file (-o) is hello.o
gcc  hello.o  -shared  -o libhello.so
# hello.o        => hello.o is the first after ':' => Link hello.o
# -shared        => Generate shared library
# -o libhello.so => Output file (-o) is libhello.so (libhello.so or hello.dll)
make: Leaving directory '/tmp/lib'

> make -C app
make: Entering directory '/tmp/app'
gcc  -c main.c -I ../lib  -o main.o
# -c main.c => compile (-c) main.c (first file after :) = main.cpp
# -I ../lib => search headers (*.h) in directory ../lib
# -o main.o => output file (-o) is main.o (target) = main.o
gcc  main.o  -L../lib  -lhello  -o app
# main.o   => main.o (all files after the :) = main.o (here only one file)
# -L../lib => look for libraries in directory ../lib
# -lhello  => use shared library hello (libhello.so or hello.dll)
# -o app   => output file (-o) is app.exe (target) = "app.exe" or "app"
make: Leaving directory '/tmp/app'

Der Lauf

Die Anwendung muss wissen, wo sich die gemeinsam genutzte Bibliothek befindet.

Unter Windows besteht eine einfache Lösung darin, die Bibliothek zu kopieren, in der sich die Anwendung befindet:

> cp -v lib/hello.dll app
`lib/hello.dll' -> `app/hello.dll'

Unter Unix-ähnlichen Betriebssystemen können Sie die LD_LIBRARY_PATHUmgebungsvariable verwenden:

> export LD_LIBRARY_PATH=lib

Führen Sie den Befehl unter Windows aus:

> app/app.exe
hello

Führen Sie den Befehl unter Unix-ähnlichen Betriebssystemen aus:

> app/app
hello
olibre
quelle
Ich schätze Ihre Bemühungen, aber die Hauptfrage war, das Betriebssystem zu erkennen. Ihr Beispiel erkennt nur Linux und setzt ansonsten Windows direkt voraus.
Shahbaz
Hallo @ Shahbaz. Sie haben Recht, meine Antwort gibt keinen anderen Ansatz als die anderen Antworten. Außerdem geht mein Skript davon aus, dass die Plattform Windows ist, wenn unamenicht Linux. Ich gebe nur ein Beispiel, das Sie möglicherweise nicht benötigen, aber dies kann jemandem helfen, der (im Web) nach einer Möglichkeit sucht, eine Makefilefür beide Plattformen zu implementieren ;-) Was soll ich in meiner Antwort ändern? Prost
olibre
Versuchen Sie, Möglichkeiten zu finden, um auch andere Betriebssysteme korrekt zu identifizieren! Ziel ist es, eine Methode zu finden, die nicht zu kompliziert, aber vor allem kugelsicher ist. Das heißt, es würde keinen Fehler machen, egal was passiert.
Shahbaz
1
@olibre Danke für das ausführliche Beispiel, sehr geschätzt und hilft mir, schnell loszulegen. Im lib/MakefileBeispiel targetwird für .sovs verwendet .dll. Ein paralleles Beispiel für dieapp/makefile wäre nützlich für den empty stringvs- .exeVergleich für den App-Dateinamen. Zum Beispiel sehe ich normalerweise nicht app.exeauf Unix-ähnlichen Betriebssystemen. ;-)
1
LSF? LFS? Tippfehler?
Franklin Yu
19

Ich habe kürzlich experimentiert, um diese Frage zu beantworten, die ich mir gestellt habe. Hier sind meine Schlussfolgerungen:

Da Sie in Windows nicht sicher sein können, ob der unameBefehl verfügbar ist, können Sie ihn verwenden gcc -dumpmachine. Dadurch wird das Compilerziel angezeigt.

Möglicherweise liegt auch ein Problem bei der Verwendung vor, unamewenn Sie eine Cross-Kompilierung durchführen möchten.

Hier ist eine Beispielliste möglicher Ausgaben von gcc -dumpmachine:

  • mingw32
  • i686-pc-cygwin
  • x86_64-redhat-linux

Sie können das Ergebnis im Makefile folgendermaßen überprüfen:

SYS := $(shell gcc -dumpmachine)
ifneq (, $(findstring linux, $(SYS)))
 # Do Linux things
else ifneq(, $(findstring mingw, $(SYS)))
 # Do MinGW things
else ifneq(, $(findstring cygwin, $(SYS)))
 # Do Cygwin things
else
 # Do things for others
endif

Es hat bei mir gut funktioniert, aber ich bin mir nicht sicher, ob es ein zuverlässiger Weg ist, den Systemtyp zu ermitteln. Zumindest ist MinGW zuverlässig und das ist alles, was ich brauche, da der unameBefehl oder das MSYS- Paket in Windows nicht erforderlich sind .

Zusammenfassend unamegibt es Ihnen das System, auf dem Sie kompilieren, und gcc -dumpmachinedas System für das Sie kompilieren.

phsym
quelle
Das ist ein guter Punkt. Kommt aber sowieso nicht unamemit MinGW? Trotzdem ist der zusätzliche Hinweis zur Cross-Compilation großartig.
Shahbaz
2
Das @ Shahbaz MinGW-Setup kann MSYS (das uname enthält) installieren, ist jedoch optional. Es ist immer noch möglich, Systeme mit nur MinGW gcc-Tools zu finden
phsym
1
Das funktioniert nirgendwo. Clang ist der Standard-Compiler wie OS X und FreeBSD.
MarcusJ
@SebastianGodelet @MarcusJ Eine einfache Lösung ist $(shell $(CC) -dumpmachine). Ab OS X Sierra funktioniert der Befehl -dumpmachine unter Clang.
Vortico
Unter OS X 10.12.5 es ist x86_64-apple-darwin16.6.0und das funktioniert , ob Sie es nennen gcc, ccoder clangaber nichtcl
MarcusJ
17

Das Git-Makefile enthält zahlreiche Beispiele für die Verwaltung ohne Autoconf / Automake und funktioniert dennoch auf einer Vielzahl von Unixy-Plattformen.

JesperE
quelle
13
Wenn ich weiß, dass Git die Autofools nicht benutzt, fühle ich mich in meiner Abneigung gegen sie gerechtfertigt ...
Dan Moulding
11
"Autofools"? War das ein absichtlicher Tippfehler? :)
JesperE
6
Es war. Aber beim zweiten Gedanken denke ich, dass ich "Autostools" noch besser mag. : D
Dan Moulding
Übrigens, wen meintest du mit "ihnen"? Die Leute von Git oder Autotools? : D
JesperE
8
Englisch ist so eine ungenaue Sprache. Wie wäre es damit: if (!usesAutotools(git)) aversionTo(autotools) = justified;Ich werde auch klarstellen, dass es nur die Werkzeuge sind, denen ich abgeneigt bin. Ich bin sicher, die Autotools-Leute sind nette Leute.
Dan Moulding
11

Update: Ich halte diese Antwort jetzt für veraltet. Ich habe weiter unten eine neue perfekte Lösung veröffentlicht.

Wenn Ihr Makefile möglicherweise unter Nicht-Cygwin-Windows ausgeführt wird, ist es unamemöglicherweise nicht verfügbar. Das ist umständlich, aber dies ist eine mögliche Lösung. Sie müssen zuerst nach Cygwin suchen, um dies auszuschließen, da die PATHUmgebungsvariable auch WINDOWS enthält .

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(shell uname -s)
endif
endif
Ken Jackson
quelle
Das ist gut bis jetzt! Kannst du mir aber eins sagen? Ich benutze Cygwin nicht, aber ich habe MinGW mit seinem Bin-Pfad in PATH installiert. Wenn ich ein Problem mit unameeinem normalen Cmd-Terminal habe, erhalte ich MINGW. Was ich meine ist, ich habe immer noch unameohne Cygwin. Ich habe auch Git Bash, aber ich habe nicht versucht, Uname darauf zu machen (im Moment bin ich in Linux). Können Sie mir sagen, wie diese beiden in Ihren Code aufgenommen werden können?
Shahbaz
Wenn Sie sicher sind, dass uname verfügbar ist, ist dies die beste Lösung. Aber in meiner Umgebung, jeder ist mit Windows und nur wenige Menschen haben entweder Cygwin oder MinGW installiert, so dass ich keine Garantie, dass alles auch als Standard wie uname arbeitet. Ich habe derzeit einige Probleme mit dem obigen Code, der make.exe in einer cmd-Shell ausführt. Windows ist eine sehr frustrierende Plattform, mit der man arbeiten kann.
Ken Jackson
Ich meine, bevor Sie die Existenz von WINDOWS in PATH testen, stellen Sie sicher, dass Sie nicht mit Cygwin zu tun haben. Wie können Sie sicherstellen, dass Sie nicht mit MinGW zu tun haben? Ist es beispielsweise in Makefile möglich zu testen, ob ein Befehl ausgeführt werden kann, und wenn er unamenicht ausgeführt werden kann, würden wir verstehen, dass wir uns in Windows befinden?
Shahbaz
Ich habe Probleme, auch für dieses Mingw / cygwin / shell-or-cmd / Linux eine saubere Lösung zu finden. Am Ende des Tages scheint so etwas wie Premake oder Cmake die beste Idee zu sein.
Isaac Nequittepas
Dies ist nicht mehr die beste Lösung. Die neue Lösung, die ich veröffentlicht habe, unterscheidet natives Windows durch die Suche nach ';' in der PATH-Variablen ohne Shell-Aufruf.
Ken Jackson
7

Das ist der Job, den GNUs Automake / Autoconf macht lösen soll. Vielleicht möchten Sie sie untersuchen.

Alternativ können Sie Umgebungsvariablen auf Ihren verschiedenen Plattformen festlegen und Makefile davon abhängig machen.

Douglas Leeder
quelle
11
Ich rate dringend davon ab, automake / autoconf zu verwenden. Sie sind mühsam zu verwenden, fügen Ihren Dateien viel Overhead hinzu und erhöhen Ihre Erstellungszeit. Sie erhöhen einfach die Komplexität für normalerweise sehr geringe Auswirkungen (immer noch keine Portabilität zwischen Systemen).
Johannes Overmann
1
Ich habe gerade ein paar Tage damit verbracht zu lernen, makewas ich will. Möchte ich jetzt auch in automake / autoconf einsteigen? - NEIN. Was im Makefile getan werden kann , sollte auf jeden Fall im Makefile getan werden, schon allein deshalb, damit ich nicht jedes Mal mehrere Zwischenstopps habe, wenn ich compile & link ändern möchte.
Ingenieur
Wie viele Plattformen unterstützen Ihre Makefiles? automake und autoconf kommen wirklich zur Geltung, wenn Sie auf viele Plattformen portierbar sein möchten.
Douglas Leeder
2
Ich werde keine nutzlosen Abhängigkeiten benötigen und mein gesamtes Build-System ändern, um herauszufinden, für welches Betriebssystem es kompiliert wird.
MarcusJ
7

Ich habe endlich die perfekte Lösung gefunden, die dieses Problem für mich löst.

ifeq '$(findstring ;,$(PATH))' ';'
    UNAME := Windows
else
    UNAME := $(shell uname 2>/dev/null || echo Unknown)
    UNAME := $(patsubst CYGWIN%,Cygwin,$(UNAME))
    UNAME := $(patsubst MSYS%,MSYS,$(UNAME))
    UNAME := $(patsubst MINGW%,MSYS,$(UNAME))
endif

Die UNAME-Variable ist auf Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (oder vermutlich Solaris, Darwin, OpenBSD, AIX, HP-UX) oder Unbekannt eingestellt. Es kann dann im Rest des Makefiles verglichen werden, um alle betriebssystemsensitiven Variablen und Befehle zu trennen.

Der Schlüssel ist, dass Windows Semikolons verwendet, um Pfade in der PATH-Variablen zu trennen, während alle anderen Doppelpunkte verwenden. (Es ist möglich, ein Linux-Verzeichnis mit einem ';' im Namen zu erstellen und es PATH hinzuzufügen, was dies beschädigen würde, aber wer würde so etwas tun?) Dies scheint die am wenigsten riskante Methode zu sein, um natives Windows zu erkennen, weil es benötigt keinen Shell-Aufruf. Cygwin und MSYS PATH verwenden Doppelpunkte, die so uname sind für sie aufgerufen wird.

Beachten Sie, dass die Umgebungsvariable des Betriebssystems zum Erkennen von Windows verwendet werden kann, jedoch nicht zum Unterscheiden zwischen Cygwin und nativem Windows. Das Testen auf das Echo von Anführungszeichen funktioniert, erfordert jedoch einen Shell-Aufruf.

Leider fügt Cygwin der Ausgabe von uname einige Versionsinformationen hinzu, daher habe ich die Aufrufe 'patsubst' hinzugefügt, um sie in 'Cygwin' zu ändern. Außerdem hat uname für MSYS tatsächlich drei mögliche Ausgaben, beginnend mit MSYS oder MINGW, aber ich verwende auch patsubst, um alle nur in 'MSYS' umzuwandeln.

Wenn es wichtig ist, zwischen nativen Windows-Systemen mit und ohne uname.exe im Pfad zu unterscheiden, kann diese Zeile anstelle der einfachen Zuweisung verwendet werden:

UNAME := $(shell uname 2>NUL || echo Windows)

Natürlich ist in allen Fällen GNU make erforderlich oder ein anderes make, das die verwendeten Funktionen unterstützt.

Ken Jackson
quelle
6

Ich bin heute auf dieses Problem gestoßen und brauchte es unter Solaris. Hier ist eine POSIX-Standardmethode, um dies zu tun (etwas sehr Nahes).

#Detect OS
UNAME = `uname`

# Build based on OS name
DetectOS:
    -@make $(UNAME)


# OS is Linux, use GCC
Linux: program.c
    @SHELL_VARIABLE="-D_LINUX_STUFF_HERE_"
    rm -f program
    gcc $(SHELL_VARIABLE) -o program program.c

# OS is Solaris, use c99
SunOS: program.c
    @SHELL_VARIABLE="-D_SOLARIS_STUFF_HERE_"
    rm -f program
    c99 $(SHELL_VARIABLE) -o program program.c
Huckle
quelle
1
Fehler unter OSX: "Makefile: 22: *** fehlendes Trennzeichen. Stop.". In dieser Zeile: "- @ make $ (UNAME_S)".
Czarek Tomczak
OSX ist wahrscheinlich nicht konform, versuchen Sie es also in der richtigen Reihenfolge. (1) Stellen Sie sicher, dass Sie ein TAB als erstes Zeichen in der Zeile verwenden. (2) Entfernen Sie das "- @" vor dem make. (2a) Wenn 2 funktioniert hat, versuchen Sie es mit einem Zeichen und dann mit dem anderen. (3) Stellen Sie sicher, dass UNAME_S ist definiert, versuchen Sie echo $ (UNAME_S) anstelle von - @ make $ (UNAME_S)
Huckle
6

Hier ist eine einfache Lösung, die überprüft, ob Sie sich in einer Windows- oder posix-ähnlichen Umgebung (Linux / Unix / Cygwin / Mac) befinden:

ifeq ($(shell echo "check_quotes"),"check_quotes")
   WINDOWS := yes
else
   WINDOWS := no
endif

Es nutzt die Tatsache, dass Echo sowohl in posix-ähnlichen als auch in Windows-Umgebungen vorhanden ist und dass die Shell in Windows die Anführungszeichen nicht filtert.

Samuel
quelle
1
Ziemlich unsicher, da es sich $PATHmöglicherweise um einen anderen handelt echo(
meiner
@YoYoYonnY Warum bezieht sich Ihr Pfad auf ein anderes Echo? Scheint eine sehr unwahrscheinliche Situation zu sein.
Samuel
1
nicht wirklich, git macht es, mingw macht es, cygwin macht es ... Und ich persönlich setze C: \ Windows \ System32 am Ende meines Pfades.
yyny
1
Diese "Lösung" "funktioniert" für alle Umgebungen, aber mein Punkt ist, dass dies Fenster definitiv nicht sicher erkennt. Wenn ich ein -mwindowsFlag setzen oder zwischen einem .dlloder .sowählen möchte, schlägt dies fehl.
yyny
1
@YoYoYonnY Danke für die Klarstellung. In meiner Situation kümmerte es mich nur, wenn ich mich in einer Cygwin-, Windows- oder Linux-Umgebung befand und nicht in welchem ​​Betriebssystem ich mich befand. Dies war also hilfreich für mich. Klingt so, als wären deine Bedürfnisse anders als meine.
Samuel
3

Beachten Sie, dass Makefiles sehr empfindlich auf Abstände reagieren. Hier ist ein Beispiel für ein Makefile, das unter OS X einen zusätzlichen Befehl ausführt und unter OS X und Linux funktioniert. Insgesamt ist autoconf / automake jedoch der richtige Weg, um alles zu erreichen, was nicht trivial ist.

UNAME: = $ (Shell uname -s)
CPP = g ++
CPPFLAGS = -pthread -ansi -Wall -Werror -pedantic -O0 -g3 -I / nexopia / include
LDFLAGS = -pthread -L / nexopia / lib -lboost_system

HEADERS = data_structures.h http_client.h load.h lock.h search.h server.h thread.h Utility.h
OBJECTS = http_client.o load.o lock.o search.o server.o thread.o Utility.o vor.o.

alle: vor

reinigen:
    rm -f $ (OBJEKTE) vor

vor: $ (OBJEKTE)
    $ (CPP) $ (LDFLAGS) -o vor $ (OBJEKTE)
ifeq ($ (UNAME), Darwin)
    # Legen Sie den Speicherort der Boost-Bibliothek fest
    install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor
endif

% .o:% .cpp $ (HEADERS) Makefile
    $ (CPP) $ (CPPFLAGS) -c $
ChrisInEdmonton
quelle
2

Eine andere Möglichkeit, dies zu tun, ist die Verwendung eines "configure" -Skripts. Wenn Sie bereits eine mit Ihrem Makefile verwenden, können Sie eine Kombination aus uname und sed verwenden, um die Dinge zum Laufen zu bringen. Führen Sie in Ihrem Skript zunächst Folgendes aus:

UNAME=uname

Um dies in Ihr Makefile aufzunehmen, beginnen Sie mit Makefile.in, das so etwas wie haben sollte

UNAME=@@UNAME@@

drin.

Verwenden Sie nach dem UNAME=unameBit den folgenden Befehl sed in Ihrem Konfigurationsskript .

sed -e "s|@@UNAME@@|$UNAME|" < Makefile.in > Makefile

Jetzt sollte Ihr Makefile UNAMEwie gewünscht definiert sein. Wenn / elif / else-Anweisungen alles sind, was übrig bleibt!

Sean
quelle
Sollte das nicht gleich sein? UNAME = $ (uname)
Ken Jackson
0

Ich hatte einen Fall, in dem ich den Unterschied zwischen zwei Fedora-Versionen feststellen musste, um die Befehlszeilenoptionen für inkscape zu optimieren:
- In Fedora 31 ist die Standard-Inkscape 1.0beta, die verwendet wird--export-file
- in Fedora <31 ist die Standard-Inkscape 0,92 die verwendet--export-pdf

Mein Makefile enthält Folgendes

# set VERSION_ID from /etc/os-release

$(eval $(shell grep VERSION_ID /etc/os-release))

# select the inkscape export syntax

ifeq ($(VERSION_ID),31)
EXPORT = export-file
else
EXPORT = export-pdf
endif

# rule to convert inkscape SVG (drawing) to PDF

%.pdf : %.svg
    inkscape --export-area-drawing $< --$(EXPORT)=$@

Dies funktioniert, weil es /etc/os-releaseeine Zeile enthält

VERSION_ID=<value>

Der Shell-Befehl im Makefile gibt also die Zeichenfolge zurück VERSION_ID=<value>, und der Befehl eval wirkt darauf, um die Makefile-Variable festzulegen VERSION_ID. Dies kann natürlich für andere Betriebssysteme angepasst werden, je nachdem, wie die Metadaten gespeichert sind. Beachten Sie, dass es in Fedora keine Standardumgebungsvariable gibt, die die Betriebssystemversion angibt, sonst hätte ich das verwendet!

Patrick B. Warren
quelle