OS detecting makefile

je travaille régulièrement sur plusieurs ordinateurs différents et plusieurs systèmes d'exploitation différents, qui sont Mac OS X, Linux, ou Solaris. Pour le projet sur lequel je travaille, je sors mon code d'un dépôt Git distant.

j'aime pouvoir travailler sur mes projets quel que soit le terminal où je suis. Jusqu'à présent, j'ai trouvé des moyens de contourner les changements OS en changeant le makefile chaque fois que je change d'ordinateur. Cependant, c'est fastidieux et provoque un tas de maux de tête.

Comment puis-je modifier mon makefile pour qu'il détecte quel OS j'utilise et modifie la syntaxe en conséquence?

voici le 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
214
demandé sur samoz 2009-04-03 18:16:36

12 réponses

il y a déjà beaucoup de bonnes réponses ici, mais je voulais partager un exemple plus complet que les deux:

  • n'assume pas uname existe sur Windows
  • détecte également le processeur

les CCFLAGS définis ici ne sont pas nécessairement recommandés ou idéaux; ils sont exactement ce que le projet auquel j'ajoutais OS/CPU auto-detection s'est avéré utiliser.

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
236
répondu Trevor Robinson 2018-05-02 16:26:10

la commande uname ( http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) sans paramètres devrait vous indiquer le nom du système d'exploitation. J'utiliserais ça, puis je ferais des conditionnels basés sur la valeur de retour.

exemple

UNAME := $(shell uname)

ifeq ($(UNAME), Linux)
# do something Linux-y
endif
ifeq ($(UNAME), Solaris)
# do something Solaris-y
endif
113
répondu dbrown0708 2009-04-03 14:27:51

si vous n'avez pas besoin de choses sophistiquées comme fait par Git LFS , vous pouvez détecter le système d'exploitation en utilisant juste deux astuces simples:

  • variable d'environnement OS
  • puis la commande uname -s
ifeq ($(OS),Windows_NT)
    detected_OS := Windows
else
    detected_OS := $(shell uname -s)
endif

ou un moyen plus sûr, sinon Windows et la commande uname n'est pas disponible:

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

alors vous pouvez sélectionner les éléments pertinents en fonction de 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

s'il vous Plaît voir aussi cette réponse détaillée sur l'importance de uname -s mieux que uname -o .

L'utilisation de OS (au lieu de uname -s ) simplifie l'algorithme d'identification. Vous pouvez toujours utiliser uniquement uname -s , mais vous devez traiter avec if/else blocs pour vérifier tous les MinGW, Cygwin, etc. variation.

Note: la variable d'environnement OS est toujours définie à "Windows_NT" sur toute plate-forme Windows (voir Variables D'environnement Windows sur Wikipedia ). Une alternative à OS est la variable d'environnement MSVC (elle vérifie la présence de MS Visual Studio , voir exemple utilisant Visual C++ ).


ci-dessous, je fournis un exemple complet en utilisant make et gcc pour construire une bibliothèque partagée: *.so ou *.dll selon la plate-forme. L'exemple est aussi simple que possible pour être plus compréhensible: -)

pour installer make et gcc sur Windows voir Cygwin ou MinGW .

mon exemple est basé sur cinq fichiers

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

Ne pas oublier: les fichiers Makefile sont en retrait à l'aide de tableaux.

les deux fichiers Makefile

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"

pour en savoir plus, lire Variables automatiques documentation comme indiqué par fci .

le code source

- 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);
}
"1519870920 de" construction

fixer le copier-coller de Makefile (remplacer les espaces avant par un tableau).

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

la commande make est la même sur les deux plateformes. La sortie donnée est sur des os de type Unix:

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

La course

l'application exige de savoir où est la bibliothèque partagée.

sur Windows, une solution simple est de copier la bibliothèque où l'application est:

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

sur les os de type Unix, vous pouvez utiliser le LD_LIBRARY_PATH variable d'environnement:

> export LD_LIBRARY_PATH=lib

exécuter la commande sur Windows:

> app/app.exe
hello

exécutez la commande sur des os de type Unix:

> app/app
hello
30
répondu olibre 2018-08-22 16:49:08

j'expérimentais récemment pour répondre à cette question que je me posais. Voici mes conclusions:

puisque sous Windows, vous ne pouvez pas être sûr que la commande uname est disponible, vous pouvez utiliser gcc -dumpmachine . Cela affichera la cible du compilateur.

il peut y avoir aussi un problème lorsque vous utilisez uname si vous voulez faire une compilation croisée.

voici un exemple de liste de sortie possible de gcc -dumpmachine :

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

vous pouvez vérifier le résultat dans le makefile comme ceci:

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

cela a bien fonctionné pour moi, mais je ne suis pas sûr que ce soit un moyen fiable d'obtenir le type de système. Au moins, il est fiable sur MinGW et c'est tout ce dont j'ai besoin car il ne nécessite pas d'avoir la commande uname ou MSYS dans Windows.

pour résumer, uname vous donne le système sur que vous compilez, et gcc -dumpmachine vous donne le système pour que vous compilez.

18
répondu phsym 2018-08-22 16:38:28

le git makefile contient de nombreux exemples de gestion sans autoconf/automake, mais fonctionne encore sur une multitude de plateformes unixy.

15
répondu JesperE 2009-04-13 09:30:25

mise à jour: je considère maintenant cette réponse comme obsolète. J'ai posté une nouvelle solution parfaite.

si votre makefile peut être exécuté sur des fenêtres autres que Cygwin, uname peut ne pas être disponible. C'est embarrassant, mais c'est une solution potentielle. Vous devez vérifier pour Cygwin d'abord pour l'exclure, parce qu'il a des fenêtres dans sa variable d'environnement PATH trop.

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(shell uname -s)
endif
endif
11
répondu Ken Jackson 2018-09-11 19:10:27

C'est le travail que GNU automake / autoconf est conçu pour résoudre. Vous pourriez vouloir examiner.

alternativement, vous pouvez définir des variables d'environnement sur vos différentes plateformes et faire dépendre votre Makefile de ces variables.

7
répondu Douglas Leeder 2009-04-03 14:19:38

j'ai rencontré ce problème aujourd'hui et J'en avais besoin sur Solaris alors voici une méthode standard POSIX pour le faire (quelque chose de très proche).

#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
6
répondu Huckle 2012-04-24 19:52:03

une autre façon de le faire est d'utiliser un script" configure". Si vous en utilisez déjà un avec votre makefile, vous pouvez utiliser une combinaison de uname et sed pour que les choses s'arrangent. D'abord, dans votre script, faites:

UNAME=uname

ensuite, pour mettre ceci dans votre Makefile, commencez par Makefile.dans lequel devrait avoir quelque chose comme

UNAME=@@UNAME@@

dedans.

utilisez la commande sed suivante dans votre script de configuration après le UNAME=uname .

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

maintenant votre makefile devrait avoir UNAME défini comme désiré. Si/elif / else déclarations sont tout ce qui reste!

2
répondu Sean 2011-08-04 12:05:53

Voici une solution simple qui vérifie si vous êtes dans un environnement Windows ou posix-like (Linux / Unix / Cygwin / Mac):

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

Il tire avantage du fait que l'écho existe à la fois dans les environnements posix et Windows, et que dans Windows le shell ne filtre pas les citations.

2
répondu Samuel 2016-04-21 14:07:14

notez que les Makefiles sont extrêmement sensibles à l'espacement. Voici un exemple de Makefile qui exécute une commande supplémentaire sur OS X et qui fonctionne sur OS X et Linux. Dans l'ensemble, cependant, autoconf/automake est la voie à suivre pour tout ce qui n'est pas trivial.

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

all: vor

clean:
    rm -f $(OBJECTS) vor

vor: $(OBJECTS)
    $(CPP) $(LDFLAGS) -o vor $(OBJECTS)
ifeq ($(UNAME),Darwin)
    # Set the Boost library location
    install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor
endif

%.o: %.cpp $(HEADERS) Makefile
    $(CPP) $(CPPFLAGS) -c $
2
répondu ChrisInEdmonton 2018-08-22 16:31:11

j'ai finalement trouvé la solution parfaite qui résout ce problème pour moi.

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

la variable UNAME est définie à Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (ou probablement Solaris, Darwin, OpenBSD, AIX, HP-UX), ou Unknown. Il peut ensuite être comparé dans le reste du Makefile pour séparer les variables et les commandes sensibles à L'OS.

la clé est que Windows utilise des points-virgule pour séparer les chemins dans la variable PATH alors que tout le monde utilise des virgules. (Il est possible de créer un répertoire Linux avec un ';' dans le nom et de l'ajouter au chemin, ce qui briserait cela, mais qui ferait une telle chose?) Cela semble être la méthode la moins risquée pour détecter les fenêtres natives car elle n'a pas besoin d'un appel shell. Le chemin Cygwin et MSYS utilisent des colons donc uname est appelé pour eux.

notez que la variable d'environnement OS peut être utilisée pour détecter les fenêtres, mais pas pour distinguer entre Cygwin et les fenêtres natives. L'essai pour l'écho des citations fonctionne, mais il nécessite un appel shell.

malheureusement, Cygwin ajoute quelques informations de version à la sortie de uname , donc j'ai ajouté les appels 'patsubst' pour le changer en 'Cygwin'. En outre, uname for MSYS a en fait trois sorties possibles à partir de MSYS ou MINGW, mais j'utilise aussi patsubst pour transformer tout en "MSYS".

S'il est important de distinguer entre les systèmes Windows natifs avec et sans certains uname.exe sur le chemin, cette ligne peut être utilisée à la place de la simple assignation:

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

bien sûr dans tous les cas GNU make est nécessaire, ou un autre make qui supporte les fonctions utilisées.

0
répondu Ken Jackson 2018-09-11 19:04:41