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
Il y a déjà beaucoup de bonnes réponses ici, mais je voulais partager un exemple plus complet, à la fois:
uname
existe sous WindowsLes 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
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
OS
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
_
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 .
_ ├── 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.
Makefile
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)
_
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 .
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);
}
_
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'
_
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
_
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
:
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.
Le git makefile contient de nombreux exemples de gestion sans autoconf/automake, tout en fonctionnant sur une multitude de plates-formes unixy.
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
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
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.
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.
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 $
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!