web-dev-qa-db-fra.com

Système d'exploitation détectant le makefile

Je travaille régulièrement sur plusieurs ordinateurs et sur différents systèmes d'exploitation, à savoir Mac OS X, Linux ou Solaris. Pour le projet sur lequel je travaille, je extrait mon code d'un référentiel git distant.

J'aime pouvoir travailler sur mes projets quel que soit le terminal où je me trouve. Jusqu'à présent, j'ai trouvé un moyen de contourner les modifications du système d'exploitation en modifiant le fichier Make à chaque fois que je change d'ordinateur. Cependant, cela est fastidieux et provoque beaucoup de maux de tête.

Comment puis-je modifier mon fichier Make pour qu'il détecte le système d'exploitation que 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
230
samoz

Il y a déjà beaucoup de bonnes réponses ici, mais je voulais partager un exemple plus complet, à la fois:

  • ne suppose pas que uname existe sous Windows
  • détecte également le processeur

Les CCFLAGS définis ici ne sont pas nécessairement recommandés ou idéaux; ce sont exactement ce que le projet auquel j'ajoutais la détection automatique OS/CPU utilisait.

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
259
Trevor Robinson

La commande uname ( http://developer.Apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) sans paramètre doit vous indiquer le nom du système d'exploitation. J'utiliserais cela, puis ferais des conditions basées 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
117
dbrown0708

Détectez le système d'exploitation à l'aide de deux astuces simples:

  • D'abord la variable d'environnement OS
  • Puis la commande uname
_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
_

Ou un moyen plus sûr, sinon sous Windows et uname non disponible:

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

Ken Jackson propose une alternative intéressante si vous souhaitez distinguer Cygwin/MinGW/MSYS/Windows. Voir sa réponse ça ressemble à ça:

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

Ensuite, 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
_

Remarques:

  • La commande uname est identique à uname -s parce que l'option -s (--kernel-name) est la valeur par défaut. Voir pourquoi uname -s est meilleur que uname -o .

  • L'utilisation de OS (au lieu de uname) simplifie l'algorithme d'identification. Vous pouvez toujours utiliser uniquement uname, mais vous devez utiliser des blocs _if/else_ pour vérifier toutes les variations de MinGW, Cygwin, etc.

  • La variable d'environnement OS est toujours définie sur _"Windows_NT"_ sur différentes versions de Windows (voir __ (variable d'environnement _%OS%_ sur Wikipedia) ).

  • Une alternative à OS est la variable d'environnement MSVC (elle vérifie la présence de MS Visual Studio , voir exemple avec Visual C++ ).


Ci-dessous, je donne un exemple complet en utilisant make et gcc pour créer une bibliothèque partagée: _*.so_ ou _*.dll_ en fonction de la plate-forme. L'exemple est aussi simple que possible pour être plus compréhensible.

Pour installer make et gcc sous Windows, voir Cygwin ou MinGW .

Mon exemple est basé sur cinq fichiers

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

Rappel: Makefile est mis en retrait à l'aide de la tabulation . Attention lors du copier-coller sous des exemples de fichiers.

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, lisez Documentation automatique sur les variables comme indiqué par cfi .

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

La construction

Corrigez le copier-coller de Makefile (remplacez les espaces de début par une tabulation).

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

La commande make est la même sur les deux plates-formes. La sortie donnée est sur les systèmes d'exploitation 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 nécessite de savoir où se trouve la bibliothèque partagée.

Sous Windows, une solution simple consiste à copier la bibliothèque où se trouve l’application:

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

Sur les systèmes d'exploitation de type Unix, vous pouvez utiliser la variable d'environnement _LD_LIBRARY_PATH_:

_> export LD_LIBRARY_PATH=lib
_

Exécutez la commande sous Windows:

_> app/app.exe
hello
_

Exécutez la commande sur les systèmes d'exploitation de type Unix:

_> app/app
hello
_
34
olibre

Je faisais récemment des expériences pour répondre à la question que je me posais. Voici mes conclusions:

Étant donné que dans Windows, vous ne pouvez pas être sûr que la commande uname est disponible, vous pouvez utiliser gcc -dumpmachine. Ceci affichera la cible du compilateur.

Il peut également y avoir un problème lors de l’utilisation de uname si vous souhaitez effectuer une compilation croisée.

Voici un exemple de liste de sorties possibles 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 à propos de MinGW et c’est tout ce dont j’ai besoin, car il n’est pas nécessaire de disposer de la commande uname ou du package MSYS sous Windows.

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

18
phsym

Le git makefile contient de nombreux exemples de gestion sans autoconf/automake, tout en fonctionnant sur une multitude de plates-formes unixy.

16
JesperE

Mise à jour: Je considère maintenant que cette réponse est obsolète. J'ai posté une nouvelle solution parfaite plus bas.

Si votre fichier make est peut-être exécuté sur des fenêtres autres que Cygwin, il est possible que uname ne soit pas disponible. C'est gênant, mais c'est une solution potentielle. Vous devez d'abord vérifier si Cygwin l'exclut, car il possède également WINDOWS dans sa variable d'environnement PATH.

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(Shell uname -s)
endif
endif
11
Ken Jackson

C’est le travail que GNU automake / autoconf est conçu pour résoudre. Vous voudrez peut-être enquêter sur eux.

Vous pouvez également définir des variables d'environnement sur vos différentes plates-formes et rendre votre Makefile conditionnel à leur égard.

7
Douglas Leeder

J'ai rencontré ce problème aujourd'hui et j'en avais besoin sous Solaris. Voici donc un moyen standard POSIX de 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
Huckle

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 sur Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (ou vraisemblablement 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 au système d'exploitation.

La clé est que Windows utilise des points-virgules pour séparer les chemins dans la variable PATH alors que tout le monde utilise des deux-points. (Il est possible de créer un répertoire Linux avec le nom ';' dans le nom et de l'ajouter à PATH, ce qui résoudrait le problème, mais qui ferait une telle chose?) Cela semble être la méthode la moins risquée pour détecter Windows natif, car n'a pas besoin d'un appel Shell. Cygwin et MSYS PATH utilisent les deux points donc -name est appelé pour eux.

Notez que la variable d’environnement du système d’exploitation peut être utilisée pour détecter Windows, mais pas pour faire la distinction entre Cygwin et Windows natif. Le test de l'écho des guillemets fonctionne, mais il nécessite un appel Shell.

Malheureusement, Cygwin ajoute des informations de version à la sortie de name. J'ai donc ajouté les appels "patsubst" pour le changer en "Cygwin". En outre, uname pour MSYS a en fait trois sorties possibles commençant par MSYS ou MINGW, mais j'utilise aussi patsubst pour tout transformer en 'MSYS'.

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

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

Bien sûr, dans tous les cas, GNU make est requis, ou un autre make qui prend en charge les fonctions utilisées.

4
Ken Jackson

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

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

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

4
Samuel

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

UNAME: = $ (Shell uname -s) 
 CPP = g ++ 
 CPPFLAGS = -pthread -ansi -Wall -Wall -Werr -Werror -pedantic -O0 -g3 -I /nexopia/include
 LDFLAGS = -pthread -L/nexopia/lib -lboost_system 
 
 HEADERS = data_structures.h http_client.h chargement.h verrouillage.h rechercher.h serveur.h thread.h utilitaire.h 
 OBJECTS = http_client.o charge.o verrouille.ou recherche.o serveur.o thread.o utilité.o vor.o 
 
 Tout: vor 
 
 clean: 
 rm -f $ (OBJECTS) vor 
 
 vor: $ (OBJETS) 
 $ (CPP) $ (LDFLAGS) -o vor $ (OBJETS) 
 Ifeq ($ (UNAME), Darwin) 
 # Définir l'emplacement de la bibliothèque Boost 
 Install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor 
 endif 
 
%. o:% .cpp $ (HEADERS) Makefile 
 $ (CPP) $ (CPPFLAGS) -c $
3
ChrisInEdmonton

Une autre façon de faire consiste à utiliser un script "configure". Si vous en utilisez déjà un avec votre makefile, vous pouvez utiliser une combinaison de uname et sed pour faire avancer les choses. Tout d'abord, dans votre script, faites:

UNAME=uname

Ensuite, afin de mettre cela dans votre Makefile, commencez avec Makefile.in qui devrait avoir quelque chose comme:

UNAME=@@UNAME@@

en elle.

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

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

Maintenant, votre makefile devrait avoir UNAME défini comme vous le souhaitez. Si les déclarations/Elif/else sont tout ce qui reste!

2
Sean