Système de détection de système d’exploitation

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

J’aime pouvoir travailler sur mes projets quel que soit le terminal auquel je suis. Jusqu’à présent, j’ai trouvé des moyens de contourner les modifications du système d’exploitation en modifiant le fichier makefile chaque fois que je change d’ordinateur. Cependant, cela est fastidieux et cause beaucoup de maux de tête.

Comment puis-je modifier mon fichier Make afin 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 -ly 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 

Il y a déjà beaucoup de bonnes réponses, mais je voulais vous donner un exemple plus complet:

  • ne suppose pas uname existe sous 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 l’auto-détection du système d’exploitation / processeur était utilisé.

 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 

La commande uname ( http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) sans parameters doit vous indiquer le nom du système d’exploitation. Je l’utiliserais, puis créer 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 

Si vous n’avez pas besoin de choses sophistiquées comme Git LFS , vous pouvez détecter le système d’exploitation en utilisant simplement deux astuces simples:

  • variable d’environnement OS
  • et 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 ne sont pas disponibles:

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

Ensuite, vous pouvez sélectionner les éléments pertinents en fonction de la 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 d’ OS (au lieu de uname -s ) simplifie l’algorithme d’identification. Vous pouvez toujours utiliser uniquement uname -s , mais vous devez gérer des blocs if/else pour vérifier toutes les variantes MinGW, Cygwin, etc.

Remarque: La variable d’environnement OS est toujours définie sur "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 l’ 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 fonction de la plate-forme. L’exemple est le plus simple possible pour être plus compréhensible 🙂

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

Mon exemple est basé sur cinq fichiers

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

N’oubliez pas: les fichiers Makefile sont mis en retrait à l’aide de tabulations.

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 ':' => Comstack 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 prerequirejsites (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 $< => comstack (-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 la documentation sur les variables automatiques 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  //puts() int main() { const char* str = hello(); puts(str); } 

La construction

Corrige le copier-coller de Makefile (remplace 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 ':' => Comstack 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 => comstack (-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 sur Windows:

 > app/app.exe hello 

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

 > app/app hello 

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

Puisque dans 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 é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 sortie possible de gcc -dumpmachine :

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

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

 SYS := $(shell gcc -dumpmachine) ifneq (, $(findssortingng linux, $(SYS))) # Do Linux things else ifneq(, $(findssortingng mingw, $(SYS))) # Do MinGW things else ifneq(, $(findssortingng 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 c’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 le package MSYS dans Windows.

Pour résumer, uname vous donne le système sur lequel vous comstackz et gcc -dumpmachine vous donne le système pour lequel vous comstackz.

Le makefile git contient de nombreux exemples de gestion sans autoconf / automake, mais fonctionne toujours sur une multitude de plates-formes unixy.

Si votre makefile est peut-être en cours d’exécution sur des fenêtres autres que Cygwin, uname peut ne pas être disponible. C’est gênant, mais c’est une solution potentielle. Vous devez d’abord vérifier Cygwin pour l’exclure, car WINDOWS a également sa variable d’environnement PATH .

 ifneq (,$(findssortingng /cygdrive/,$(PATH))) UNAME := Cygwin else ifneq (,$(findssortingng WINDOWS,$(PATH))) UNAME := Windows else UNAME := $(shell uname -s) endif endif 

C’est le travail que les automake / autoconf de GNU sont conçus pour résoudre. Vous pourriez vouloir les enquêter.

Vous pouvez également définir des variables d’environnement sur vos différentes plates-formes et rendre votre fichier Makefile conditionnel par rapport à celles-ci.

J’ai rencontré ce problème aujourd’hui et j’en avais besoin sur Solaris, alors voici un moyen standard de POSIX (quelque chose de très proche de cela).

 #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 

Une autre façon de faire est d’utiliser un script “configure”. Si vous en utilisez déjà un avec votre fichier makefile, vous pouvez utiliser une combinaison de uname et sed pour que tout fonctionne. Tout d’abord, dans votre script, faites:

 UNAME=uname 

Ensuite, pour le mettre dans votre Makefile, commencez par Makefile.in qui devrait avoir quelque chose comme

 UNAME=@@UNAME@@ 

dedans.

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

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

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

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 sur les environnements posix-like et Windows, et que, dans Windows, le shell ne filtre pas les guillemets.

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

  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
 OBJETS = http_client.o load.o lock.o search.o server.o thread.o utility.o vor.o

 tous: vor

 nettoyer:
     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
 fin si

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

J’ai finalement trouvé une excellente solution qui résout ce problème pour moi.

 ifeq '$(findssortingng ;,$(PATH))' ';' UNAME := Windows else UNAME := $(shell uname 2>/dev/null || echo Unknown) UNAME := $(patsubst CYGWIN%,Cygwin,$(UNAME)) # CYGWIN_NT-10.0 -> Cygwin endif 

La variable UNAME est définie sur Linux, Cygwin, Windows, FreeBSD, NetBSD (ou probablement Solaris, Darwin, OpenBSD, AIX, HP-UX) ou Inconnu. Il peut ensuite être comparé dans le rest du Makefile pour séparer les variables et 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 les deux points. (Il est possible de créer un répertoire Linux avec un ‘;’ dans le nom et de l’append à PATH, ce qui casserait ceci, mais qui ferait une telle chose?) Cela semble être la méthode la moins risquée pour détecter Windows natif n’a pas besoin d’un appel shell. Le PATH de Cygwin utilise les deux points alors uname est appelé pour cela.

Notez que la variable d’environnement du système d’exploitation peut être utilisée pour détecter Windows, mais pas pour distinguer Cygwin et Windows natif. Le test de l’écho des citations fonctionne, mais nécessite un appel shell.

Malheureusement, Cygwin ajoute des informations de version à la sortie de uname , j’ai donc ajouté l’appel “patsubst” pour le remplacer par “Cygwin”.

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 l’affectation simple:

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

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