System operacyjny wykrywa plik makefile

250

Rutynowo pracuję na kilku różnych komputerach i kilku różnych systemach operacyjnych, którymi są Mac OS X, Linux lub Solaris. W przypadku projektu, nad którym pracuję, pobieram kod ze zdalnego repozytorium git.

Lubię pracować nad moimi projektami, niezależnie od tego, w którym terminalu jestem. Do tej pory znalazłem sposoby obejścia zmian w systemie operacyjnym, zmieniając plik makefile przy każdej zmianie komputera. Jest to jednak uciążliwe i powoduje wiele bólów głowy.

Jak mogę zmodyfikować mój plik makefile, aby wykrywał używany system operacyjny i odpowiednio modyfikował składnię?

Oto plik 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
źródło

Odpowiedzi:

283

Istnieje już wiele dobrych odpowiedzi, ale chciałem podać pełniejszy przykład, że oba:

  • nie zakłada uname, że istnieje w systemie Windows
  • wykrywa również procesor

Zdefiniowane tutaj CCFLAGS niekoniecznie są zalecane lub idealne; są właśnie tym, z czego korzystał projekt, do którego dodawałem automatyczne wykrywanie systemu operacyjnego / procesora.

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
źródło
8
Niestety środowisko PROCESSOR_ARCHITECTUREenvvar wydaje się zwirtualizowane w zależności od tego, czy proces jest 32-bitowy czy 64-bitowy. Jeśli więc masz makewersję 32-bitową i próbujesz zbudować aplikację 64-bitową, zakończy się ona niepowodzeniem. Używanie go w połączeniu z PROCESSOR_ARCHITEW6432działało dla mnie (patrz to i tamto )
Thomas
4
Byłoby miło, gdyby makezespół dodał kilka magicznych zmiennych z os i arch, prawdopodobnie zbyt duży problem.
Alex
6
@JanusTroelsen: nie powinno mieć znaczenia, czy OSjest ustawiony w systemach innych niż Windows. Spraw, aby przysmaki były takie same jak puste, co spowoduje przeskok do unamebloku opartego na podstawie. Wystarczy dodać tam kontrolę FreeBSD.
Trevor Robinson
3
to również psuje OSX. /bin/sh: -c: line 0: syntax error near unexpected token , Windows_NT '/ bin / sh: -c: line 0:ifeq (,Windows_NT)' make: *** [os] Error 2
k107
1
@kristi Wygląda na to, że uruchomiłeś to jako polecenie powłoki, a nie w kontekście dyrektyw makefile.
phord
119

Polecenie uname ( http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) bez parametrów powinno podać nazwę systemu operacyjnego. Użyłbym tego, a następnie stworzyłem warunki warunkowe na podstawie wartości zwracanej.

Przykład

UNAME := $(shell uname)

ifeq ($(UNAME), Linux)
# do something Linux-y
endif
ifeq ($(UNAME), Solaris)
# do something Solaris-y
endif
dbrown0708
źródło
Mówiąc wprost, linia ta znajduje się w twoim Makefile. Właśnie wypróbowałem ten konstrukt w Makefiles w Cygwin i OSX i działał on zgodnie z oczekiwaniami. Coś do wypróbowania: wpisz uname w wierszu polecenia. To powie ci wartość tego systemu operacyjnego. OSX prawdopodobnie będzie „Darwin”.
dbrown0708
W projekcie GnuWin32 zarówno uname, jak i Gnu udostępniają jako rodzime aplikacje Windows, dzięki czemu ta technika jest przenośna dla MingW w wierszu polecenia, a także dla Cygwin w systemie Windows.
RBerteig,
Nie działa na moim komputerze Solaris, gdy jest w pliku makefile. Polecenie uname jest dostępne w tym systemie.
samoz
4
Pamiętaj, że jeśli umieścisz to w Maketarget, nie może być wcięte.
nylund
4
Czy składnia „: =” nie jest specyficzna dla GNU Make?
Ankur Sethi,
40

Wykryj system operacyjny za pomocą dwóch prostych sztuczek:

  • Najpierw zmienna środowiskowa OS
  • Następnie unamepolecenie
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

Lub bezpieczniejszy sposób, jeśli nie w systemie Windows i unameniedostępny:

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

Ken Jackson proponuje interesującą alternatywę, jeśli chcesz rozróżnić Cygwin / MinGW / MSYS / Windows. Zobacz jego odpowiedź, która wygląda tak:

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

Następnie możesz wybrać odpowiednie rzeczy w zależności od 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

Uwagi:

  • Polecenie unamejest takie samo, uname -sponieważ opcja -s( --kernel-name) jest domyślna. Zobacz, dlaczego uname -sjest lepszy niżuname -o .

  • Zastosowanie OS(zamiast uname) upraszcza algorytm identyfikacji. Nadal możesz używać wyłącznie uname, ale musisz radzić sobie z if/elseblokami, aby sprawdzić wszystkie odmiany MinGW, Cygwin itp.

  • Zmienna środowiskowa OSjest zawsze ustawiona "Windows_NT"na różne wersje systemu Windows (patrz %OS%zmienna środowiskowa na Wikipedii ).

  • Alternatywą OSjest zmienna środowiskowa MSVC(sprawdza obecność MS Visual Studio , patrz przykład przy użyciu Visual C ++ ).


Poniżej podaję pełny przykład użycia makei gccbudowy wspólnej biblioteki: *.solub w *.dllzależności od platformy. Przykład jest możliwie najprostszy, aby był bardziej zrozumiały.

Aby zainstalować makei gccna Windowsie zobacz Cygwin lub MinGW .

Mój przykład opiera się na pięciu plikach

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

Przypomnienie: Makefile jest wcięte przy użyciu tabulacji . Uwaga podczas wklejania kopii poniżej przykładowych plików.

Dwa Makefilepliki

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"

Aby dowiedzieć się więcej, przeczytaj dokumentację zmiennych automatycznych wskazaną przez cfi .

Kod źródłowy

- 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);
}

Kompilacja

Napraw kopiuj-wklej Makefile(zamień wiodące spacje na jedną tabelę).

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

makeKomenda jest taka sama na obu platformach. Podane dane wyjściowe dotyczą systemów uniksopodobnych:

> 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'

Bieg

Aplikacja musi wiedzieć, gdzie jest biblioteka współdzielona.

W systemie Windows prostym rozwiązaniem jest skopiowanie biblioteki, w której znajduje się aplikacja:

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

W systemach uniksopodobnych możesz użyć LD_LIBRARY_PATHzmiennej środowiskowej:

> export LD_LIBRARY_PATH=lib

Uruchom polecenie w systemie Windows:

> app/app.exe
hello

Uruchom polecenie w systemach operacyjnych uniksopodobnych:

> app/app
hello
olibre
źródło
Doceniam twój wysiłek, ale głównym pytaniem było wykrycie systemu operacyjnego. Twój przykład wykrywa tylko Linuksa, a poza tym całkowicie zakłada Windows.
Shahbaz
Cześć @Shahbaz. Masz rację, moja odpowiedź nie podaje innego podejścia niż inne odpowiedzi. Co więcej, mój skrypt zakłada, że ​​platformą jest Windows, gdy unamenie jest to Linux. Podam tylko przykład, którego możesz nie potrzebować, ale może to pomóc komuś, kto szuka (w Internecie) sposobu na wdrożenie Makefileobu platform ;-) Co powinienem zmienić w swojej odpowiedzi? Na zdrowie
olibre,
spróbuj wymyślić sposoby prawidłowej identyfikacji również innych systemów operacyjnych! Celem jest znalezienie metody, która nie jest zbyt skomplikowana, ale co ważniejsze, jest kuloodporna. Oznacza to, że nie pomyliłby się bez względu na wszystko.
Shahbaz
1
@olibre Dziękuję za szczegółowy przykład, bardzo doceniam i pomaga mi szybko zacząć. Na lib/Makefileprzykład, targetstosuje się do .sovs .dll. Równoległy Przykładem tego app/makefilebyłoby użyteczne dla empty stringvs .exestosunku do aplikacji pliku. Na przykład zwykle nie widzę app.exew systemach operacyjnych typu Unix. ;-)
1
LSF? LFS? Literówka?
Franklin Yu
19

Niedawno eksperymentowałem, aby odpowiedzieć na to pytanie, które sobie zadawałem. Oto moje wnioski:

Ponieważ w systemie Windows nie możesz być pewien, że unamepolecenie jest dostępne, możesz użyć gcc -dumpmachine. Spowoduje to wyświetlenie celu kompilatora.

Może być również problem z używaniem, unamejeśli chcesz wykonać kompilację krzyżową.

Oto przykładowa lista możliwych wyników gcc -dumpmachine:

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

Możesz sprawdzić wynik w pliku makefile w następujący sposób:

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

Działa to dla mnie dobrze, ale nie jestem pewien, czy jest to niezawodny sposób na uzyskanie typu systemu. Przynajmniej jest niezawodny w MinGW i to wszystko, czego potrzebuję, ponieważ nie wymaga posiadania unamepolecenia lub pakietu MSYS w systemie Windows.

Podsumowując, unamedaje ci system, na którym kompilujesz, i gcc -dumpmachinesystem, na którym kompilujesz.

phsym
źródło
To dobra uwaga. W każdym razie jednak nie unamepochodzi MinGW? Niemniej jednak dodatkowa uwaga dotycząca kompilacji krzyżowej jest świetna.
Shahbaz,
2
Instalator @Shahbaz MinGW może zainstalować MSYS (który zawiera uname), ale jest opcjonalny. Nadal można znaleźć systemy tylko z narzędziami gcc MinGW
phsym,
1
To nie działa nigdzie Clang jest domyślnym kompilatorem, takim jak OS X i FreeBSD.
MarcusJ
@SebastianGodelet @MarcusJ Łatwym rozwiązaniem jest $(shell $(CC) -dumpmachine). W systemie OS X Sierra polecenie -dumpmachine działa w systemie Clang.
Vortico,
W systemie OS X 10.12.5 działa x86_64-apple-darwin16.6.0i działa, niezależnie od tego gcc, jak go nazwiesz cc, czy też clang- ale niecl
MarcusJ
17

Plik makit git zawiera wiele przykładów zarządzania bez autoconf / automake, ale wciąż działa na wielu platformach unixy.

JesperE
źródło
13
Wiedząc, że Git nie użyć Autofools jakoś czuję się uzasadnione w mojej niechęci do nich ...
Dan Moulding
11
„Autofools”? Czy to zamierzona literówka? :)
JesperE
6
To było. Ale po namyśle myślę, że lubię „Autostools” jeszcze bardziej. : D
Dan Molding
Btw, co miałeś na myśli przez „ich”? Ludzie Git czy Autotools? : D
JesperE
8
Angielski jest takim nieprecyzyjnym językiem. A co powiesz na to: if (!usesAutotools(git)) aversionTo(autotools) = justified;wyjaśnię też, że to tylko narzędzia, którym jestem niechętny. Jestem pewien, że ludzie z Autotools to mili ludzie.
Dan Molding
11

Aktualizacja: Teraz uważam tę odpowiedź za nieaktualną. W dalszej części opublikowałem nowe idealne rozwiązanie.

Jeśli plik makefile może działać w systemie Windows innym niż Cygwin, unamemoże nie być dostępny. To niezręczne, ale jest to potencjalne rozwiązanie. Musisz najpierw sprawdzić, czy Cygwin go wyklucza, ponieważ ma on także PATHzmienną środowiskową WINDOWS .

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(shell uname -s)
endif
endif
Ken Jackson
źródło
To jest na razie dobre! Czy możesz mi powiedzieć jedno? Nie używam Cygwin, ale mam zainstalowaną MinGW ze ścieżką bin w PATH. Jeśli wydaje unamez normalnego terminala cmd, daje mi MINGW. Chodzi mi o to, że nadal mam unamebez korzystania z Cygwin. Mam też git bash, ale nie próbowałem na nim odmówić (teraz jestem w Linuksie). Czy możesz mi powiedzieć, jak te dwa elementy mogą zostać włączone do twojego kodu?
Shahbaz
Jeśli masz pewność, że uname jest dostępny, to najlepsze rozwiązanie. Ale w moim środowisku wszyscy używają Windowsa i niewiele osób ma zainstalowane cygwin lub mingw , więc nie mam gwarancji, że wszystko, nawet tak standardowe jak uname, będzie działać. Mam obecnie problem z powyższym kodem uruchomionym make.exe w powłoce cmd. Windows jest bardzo frustrującą platformą do pracy.
Ken Jackson
Mam na myśli to, że przed przetestowaniem istnienia WINDOWS w ŚCIEŻCE upewniasz się, że nie masz do czynienia z cygwinem, jak możesz się upewnić, że nie masz do czynienia z MinGW? Na przykład, czy w Makefile można przetestować, czy polecenie można uruchomić, a jeśli unamenie można go uruchomić, zrozumielibyśmy, że jesteśmy w systemie Windows?
Shahbaz
Staram się też znaleźć czyste rozwiązanie dla tego Mingw / cygwin / shell-or-cmd / Linux. Pod koniec dnia najlepszym pomysłem wydaje się coś takiego jak premake lub cmake.
Isaac Nequittepas
To nie jest już najlepsze rozwiązanie. Nowe opublikowane przeze mnie rozwiązanie wyróżnia natywny system Windows, szukając „;” w zmiennej PATH, bez wywołania powłoki.
Ken Jackson
7

Takie zadanie automake / autoconf GNU ma rozwiązać. Możesz je zbadać.

Alternatywnie możesz ustawić zmienne środowiskowe na różnych platformach i uzależnić Makefile od nich.

Douglas Leeder
źródło
11
Zdecydowanie odradzam używanie automake / autoconf. Są uciążliwe w użyciu, zwiększają koszty plików i czas kompilacji. Po prostu dodają złożoności, co zwykle daje bardzo niewielki efekt (wciąż brak możliwości przenoszenia między systemami).
Johannes Overmann
1
Właśnie spędziłem kilka dni, ucząc się makerobić to, co chcę. Czy teraz chcę również przejść do automake / autoconf? - NIE Co można zrobić w pliku makefile, z pewnością należy to zrobić w pliku makefile, choćby po to, aby nie miałem kilku punktów zatrzymania za każdym razem, gdy chcę zmienić kompilację i link.
Inżynier
Ile platform obsługuje twoje pliki makefiles? Automake i autoconf naprawdę sprawdzają się, gdy chcesz mieć możliwość przenoszenia na wiele platform.
Douglas Leeder,
2
Nie będę wymagał bezużytecznych zależności i zmieniam cały system kompilacji, aby dowiedzieć się, dla jakiego systemu operacyjnego jest kompilowany.
MarcusJ
7

W końcu znalazłem idealne rozwiązanie, które rozwiązuje ten problem dla mnie.

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

Zmienna UNAME jest ustawiona na Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (lub prawdopodobnie Solaris, Darwin, OpenBSD, AIX, HP-UX) lub Nieznany. Następnie można go porównać w pozostałej części pliku Makefile, aby oddzielić zmienne i polecenia wrażliwe na system operacyjny.

Kluczem jest to, że system Windows używa średników do oddzielania ścieżek w zmiennej PATH, podczas gdy wszyscy inni używają dwukropków. (Możliwe jest utworzenie katalogu Linux z nazwą „;” i dodanie go do PATH, co by to zepsuło, ale kto by zrobiłby coś takiego?) Wydaje się, że jest to najmniej ryzykowna metoda wykrywania rodzimego systemu Windows, ponieważ nie potrzebuje wywołania powłoki. Ścieżka Cygwin i MSYS PATH używają dwukropków, dlatego wymaga się od nich nazwy uname .

Zauważ, że zmienna środowiskowa systemu operacyjnego może być używana do wykrywania systemu Windows, ale nie do rozróżniania między Cygwin a rodzimym systemem Windows. Testowanie echa cytatów działa, ale wymaga wywołania powłoki.

Niestety, Cygwin dodaje informacje wyjściowe do uname , więc dodałem wywołania „patsubst”, aby zmienić je na „Cygwin”. Ponadto, uname dla MSYS faktycznie ma trzy możliwe wyjścia, zaczynając od MSYS lub MINGW, ale używam również patsubst, aby przekształcić wszystko w tylko „MSYS”.

Jeśli ważne jest rozróżnienie między rodzimymi systemami Windows z i bez pliku uname.exe na ścieżce, można użyć tej linii zamiast prostego przypisania:

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

Oczywiście we wszystkich przypadkach wymagana jest marka GNU lub inna marka, która obsługuje używane funkcje.

Ken Jackson
źródło
6

Zetknąłem się dzisiaj z tym problemem i potrzebowałem go w systemie Solaris, więc oto standardowy sposób na to, aby to zrobić (coś bardzo zbliżonego) do POSIX.

#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
Biodro
źródło
1
Występuje błąd w systemie OSX: „Brak pliku separatora: 22: ***. Zatrzymaj.”. W tym wierszu: „- @ make $ (UNAME_S)”.
Czarek Tomczak
OSX prawdopodobnie nie jest zgodny, więc spróbuj je po kolei. (1) Upewnij się, że używasz TAB jako pierwszego znaku w linii (2) Usuń znak „- @” przed marką (2a) Jeśli 2 zadziałało, wypróbuj jeden znak, a następnie drugi (3) Upewnij się, że UNAME_S jest zdefiniowany, spróbuj echo $ (UNAME_S) zamiast - @ make $ (UNAME_S)
Huckle
6

Oto proste rozwiązanie, które sprawdza, czy jesteś w środowisku Windows lub posixowym (Linux / Unix / Cygwin / Mac):

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

Wykorzystuje fakt, że echo istnieje zarówno w środowisku podobnym do POSIX-a, jak i Windows, oraz że w Windows powłoka nie filtruje cudzysłowów.

Samuel
źródło
1
Dość niebezpieczne, ponieważ $PATHmoże odnosić się do innego echo(mój robi ...)
yyny 16.04.16
@YoYoYonnY Dlaczego twoja ścieżka odnosi się do innego echa? Wydaje się to bardzo mało prawdopodobną sytuacją.
Samuel,
1
nie bardzo, git robi to, mingw robi to, cygwin robi to ... I osobiście umieszczam C: \ Windows \ System32 na dole mojej ścieżki.
yyny 20.04.16
1
To „rozwiązanie” „działa” dla wszystkich środowisk, ale chodzi mi o to, że zdecydowanie nie wykrywa ono bezpiecznie okien. Jeśli chcę, aby ustawić -mwindowsflagę lub wybrać pomiędzy .dlllub .sobyłoby to niepowodzeniem.
rr
1
@YoYoYonnY Dziękujemy za wyjaśnienie. W mojej sytuacji zależało mi tylko na tym, że pracowałem w środowisku Cygwin, Windows lub Linux, a nie w jakim systemie operacyjnym byłem, więc było to dla mnie pomocne. Wygląda na to, że twoje potrzeby są inne niż moje.
Samuel
3

Zauważ, że Makefile są wyjątkowo wrażliwe na odstępy. Oto przykład pliku Makefile, który uruchamia dodatkowe polecenie w OS X i działa w OS X i Linux. Ogólnie rzecz biorąc, autoconf / automake to sposób na wszystko, co nie jest trywialne.

UNAME: = $ (uname powłoki -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
OBIEKTY = http_klient. O obciążenie. O blokada. O wyszukiwanie. O serwer. O wątek. O narzędzie. O vor. O

wszystkie: vor

czysty:
    rm -f $ (OBIEKTY) vor

vor: $ (OBIEKTY)
    $ (CPP) $ (LDFLAGS) -o vor $ (OBIEKTY)
ifeq ($ (UNAME), Darwin)
    # Ustaw lokalizację biblioteki Boost
    narzędzie_nazwa_instalacji -zmień_system libboost.dylib /nexopia/lib/libboost_system.dylib vor
endif

% .o:% .cpp $ (HEADERS) Makefile
    $ (CPP) $ (CPPFLAGS) -c $
ChrisInEdmonton
źródło
2

Innym sposobem na to jest użycie skryptu „config”. Jeśli korzystasz już z pliku makefile, możesz użyć kombinacji uname i sed, aby wszystko działało. Najpierw w swoim skrypcie wykonaj:

UNAME=uname

Następnie, aby umieścić to w swoim Makefile, zacznij od Makefile.in, który powinien mieć coś takiego

UNAME=@@UNAME@@

w tym.

Użyj następującego polecenia sed w skrypcie konfiguracyjnym po UNAME=unamebicie.

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

Teraz twój plik makefile powinien był zostać UNAMEzdefiniowany zgodnie z potrzebami. Jeśli pozostały tylko instrukcje / elif / else!

Sean
źródło
Czy pierwszy nie powinien być taki? UNAME = $ (uname)
Ken Jackson
0

Miałem przypadek, w którym musiałem wykryć różnicę między dwiema wersjami Fedory, aby dostosować opcje wiersza polecenia dla inkscape:
- w Fedorze 31 domyślnym inkscape jest 1.0beta, który używa --export-file
- w Fedorze <31 domyślnym inkscape jest 0,92, który wykorzystuje--export-pdf

Mój plik Makefile zawiera następujące elementy

# 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)=$@

Działa to, ponieważ /etc/os-releasezawiera linię

VERSION_ID=<value>

więc polecenie powłoki w pliku Makefile zwraca ciąg znaków VERSION_ID=<value>, a następnie polecenie eval działa na to, aby ustawić zmienną Makefile VERSION_ID. Można to oczywiście poprawić w innych systemach operacyjnych, w zależności od sposobu przechowywania metadanych. Zauważ, że w Fedorze nie ma domyślnej zmiennej środowiskowej, która podaje wersję systemu operacyjnego, w przeciwnym razie użyłbym tego!

Patrick B Warren
źródło