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
¿Fue útil?

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 que uname -s porque opción -s (--kernel-name) es el valor predeterminado.Ver por qué uname -s es mejor que uname -o.

  • El uso de OS (en lugar de uname) simplifica el algoritmo de identificación.Todavía puedes usar únicamente uname, pero tienes que lidiar con if/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 entorno MSVC (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

Ese es el trabajo que de GNU automake / autoconf están diseñados para resolver. Es posible que desee para investigarlos.

Como alternativa se puede establecer variables de entorno en sus diferentes plataformas y hacer que se Makefile condicional contra ellos.

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!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top