El sistema operativo detecta el archivo MAKE
-
23-08-2019 - |
Pregunta
Normalmente trabajo en varias computadoras diferentes y en varios sistemas operativos diferentes, que son Mac OS X, Linux o Solaris.Para el proyecto en el que estoy trabajando, extraigo mi código de un repositorio git remoto.
Me gusta poder trabajar en mis proyectos sin importar en qué terminal me encuentre.Hasta ahora, he encontrado formas de evitar los cambios del sistema operativo cambiando el archivo MAKE cada vez que cambio de computadora.Sin embargo, esto es tedioso y causa muchos dolores de cabeza.
¿Cómo puedo modificar mi archivo MAKE para que detecte qué sistema operativo estoy usando y modifique la sintaxis en consecuencia?
Aquí está el archivo MAKE:
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
Solución
Hay muchas buenas respuestas aquí ya, pero quería compartir un ejemplo más completo que tanto:
- no asume
uname
existe en Windows - también detecta el procesador
El CCFLAGS definido aquí no son necesariamente recomendable o ideal; que están justo lo que el proyecto al que estaba añadiendo OS / CPU detección automática pasó a utilizar.
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
Otros consejos
El comando uname ( http: // desarrollador. apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) sin ningún parámetro debe decirle el nombre del sistema operativo. Que haría uso que, a continuación, hacer condicionales en función del valor de retorno.
Ejemplo
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
# do something Linux-y
endif
ifeq ($(UNAME), Solaris)
# do something Solaris-y
endif
Detecta el sistema operativo mediante dos sencillos trucos:
- Primero la variable de entorno.
OS
- Entonces el
uname
dominio
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
O una forma más segura, si no en Windows y uname
indisponible:
ifeq ($(OS),Windows_NT)
detected_OS := Windows
else
detected_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown')
endif
Ken Jackson propone una alternativa interesante si se quiere distinguir Cygwin/MinGW/MSYS/Windows.Ver su respuesta eso se ve asi:
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
Luego puedes seleccionar las cosas relevantes dependiendo 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
Notas:
Dominio
uname
es igual queuname -s
porque opción-s
(--kernel-name
) es el valor predeterminado.Ver por quéuname -s
es mejor queuname -o
.El uso de
OS
(en lugar deuname
) simplifica el algoritmo de identificación.Todavía puedes usar únicamenteuname
, pero tienes que lidiar conif/else
bloques para verificar todos los MinGW, Cygwin, etc.variaciones.La variable de entorno
OS
siempre está configurado en"Windows_NT"
en diferentes versiones de Windows (ver%OS%
variable de entorno en Wikipedia).una alternativa de
OS
es la variable de entornoMSVC
(comprueba la presencia de MS Visual Studio, ver ejemplo usando Visual C++).
A continuación proporciono un ejemplo completo usando make
y gcc
para construir una biblioteca compartida: *.so
o *.dll
dependiendo de la plataforma.El ejemplo es lo más sencillo posible para que sea más comprensible.
Instalar make
y gcc
en Windows ver Cygwin o MinGW.
Mi ejemplo se basa en cinco archivos.
├── lib
│ └── Makefile
│ └── hello.h
│ └── hello.c
└── app
└── Makefile
└── main.c
Recordatorio: Makefile
está sangrado usando tabulación.Tenga cuidado al copiar y pegar debajo de archivos de muestra.
Los dos Makefile
archivos
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"
Para obtener más información, lea Variables automáticas documentación como lo señala cf.
El código fuente
- 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 construcción
Corregir el copiar y pegar de Makefile
(reemplace los espacios iniciales por una tabulación).
> sed 's/^ */\t/' -i */Makefile
El make
El comando es el mismo en ambas plataformas.El resultado proporcionado está en sistemas operativos tipo 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 carrera
La aplicación requiere saber dónde está la biblioteca compartida.
En Windows, una solución sencilla es copiar la biblioteca donde está la aplicación:
> cp -v lib/hello.dll app
`lib/hello.dll' -> `app/hello.dll'
En sistemas operativos tipo Unix, puede utilizar el LD_LIBRARY_PATH
Variable ambiental:
> export LD_LIBRARY_PATH=lib
Ejecute el comando en Windows:
> app/app.exe
hello
Ejecute el comando en sistemas operativos tipo Unix:
> app/app
hello
Estaba experimentando recientemente con el fin de responder a esta pregunta me preguntaba. Aquí están mis conclusiones:
Dado que en Windows, no se puede estar seguro de que el comando uname
está disponible, puede utilizar gcc -dumpmachine
. Esto mostrará el objetivo compilador.
Puede haber también un problema cuando se utiliza uname
si usted quiere hacer algo de compilación cruzada.
He aquí una lista de ejemplo de posible salida del gcc -dumpmachine
:
- mingw32
- i686-pc-cygwin
- x86_64-RedHat Linux
Puede comprobar el resultado en el archivo MAKE como esto:
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
Se trabajó bien para mí, pero no estoy seguro de que es una manera fiable de obtener el tipo de sistema. Por lo menos es fiable sobre MinGW y eso es todo lo que necesito, ya que no requiere tener el comando uname
o MSYS paquete en Windows.
Para resumir, uname
le da al sistema , que se está compilando, y gcc -dumpmachine
le da al sistema que está compilando.
El git makefile contiene numerosos ejemplos de cómo manejar sin autoconf / automake, y aún así trabajar en una multitud de plataformas unixy.
Actualización: he estimado que esta respuesta será obsoleto. He publicado una nueva solución perfecta más abajo.
Si su makefile puede ser que se ejecuta en no Cygwin de Windows, uname
puede no estar disponible. Eso es torpe, pero esto es una solución potencial. Usted tiene que comprobar para Cygwin primero en descartarlo, ya que tiene ventanas en su variable de entorno PATH
también.
ifneq (,$(findstring /cygdrive/,$(PATH)))
UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
UNAME := Windows
else
UNAME := $(shell uname -s)
endif
endif
Me encontré con este problema hoy y lo necesitaba en Solaris asi que aquí hay una forma estándar POSIX hacer (algo muy cercano a) esto.
#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
Aquí hay una solución simple que comprueba si se encuentra en un entorno Windows o POSIX-como (Linux / Unix / Cygwin / Mac):
ifeq ($(shell echo "check_quotes"),"check_quotes")
WINDOWS := yes
else
WINDOWS := no
endif
Se aprovecha del hecho de que el eco existe en ambos entornos POSIX-como y Windows, y que en Windows la cáscara no filtra las comillas.
Finalmente encontré la solución perfecta que resuelve este problema para mí.
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 se establece en Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (o presumiblemente Solaris, Darwin, OpenBSD, AIX, HP-UX), o desconocida. A continuación, puede compararse a lo largo del resto del Makefile para separar las variables y comandos del sistema operativo y minúsculas.
La clave es que Windows usa punto y coma para separar los caminos en la variable PATH mientras que los demás utilizan dos puntos. (Es posible hacer un directorio de Linux con un ';' en nombre y añadirlo al camino, lo que podría romper esto, pero ¿quién haría tal cosa?) Esto parece ser el método menos riesgoso para detectar nativo de Windows, ya que no necesita una llamada concha. Los Cygwin y Msys utilizar la ruta dos puntos por lo que uname se llama para ellos.
Tenga en cuenta que la variable de entorno del sistema operativo puede ser utilizado para detectar Windows, pero no para distinguir entre Cygwin y Windows nativo. Las pruebas para detectar el eco de las citas de obras, pero requiere una llamada concha.
Por desgracia, Cygwin añade algo de información de la versión a la salida de uname , por lo que agregó el 'patsubst' llama a cambiar a simplemente 'Cygwin'. Además, uname para MSYS realidad tiene tres salidas posibles que comienzan con MSYS o MinGW, pero yo uso también patsubst transformar todo simplemente 'MSYS'.
Si es importante distinguir entre los sistemas nativos de Windows con y sin algunos uname.exe en el camino, esta línea se puede utilizar en lugar de la asignación simple:
UNAME := $(shell uname 2>NUL || echo Windows)
Por supuesto, en todos los casos de GNU make se requiere, o en otro make , que es compatible con las funciones que se utilizan.
Tenga en cuenta que los Makefiles son extremadamente sensibles a la separación. Aquí está un ejemplo de un Makefile que se ejecuta un comando adicional en OS X y que funciona en OS X y Linux. En general, sin embargo, autoconf / automake es el camino a seguir para nada en absoluto no 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 $
Otra manera de hacer esto es mediante el uso de un script "configure". Si ya está utilizando uno con su makefile, puede utilizar una combinación de uname y sed de hacer las cosas para hacer ejercicio. En primer lugar, en su script, hacer:
UNAME=uname
A continuación, con el fin de poner esto en su Makefile, comenzar con Makefile.in que debe tener algo como
UNAME=@@UNAME@@
en ella.
Utilice el siguiente comando sed en su script de configuración después del bit UNAME=uname
.
sed -e "s|@@UNAME@@|$UNAME|" < Makefile.in > Makefile
Ahora su archivo MAKE debe tener UNAME
definido como se desee. Si / elif / else declaraciones son todo lo que queda!