ОС обнаружение файл Makefile

Я обычно работаю на нескольких разных компьютерах и нескольких разных операционных системах, таких как Mac OS X, Linux или Solaris. Для проекта, над которым я работаю, я вытаскиваю свой код из удаленного репозитория git.

Мне нравится иметь возможность работать над моими проектами независимо от того, на каком терминале я нахожусь. До сих пор я нашел способы обойти изменения ОС, изменяя makefile каждый раз, когда я переключаю компьютеры. Однако, это утомительно и вызывает кучу головная боль.

Как я могу изменить свой файл makefile, чтобы он определял, какую ОС я использую, и соответственно изменял синтаксис?

вот 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

12 ответов


здесь уже есть много хороших ответов, но я хотел бы поделиться более полным примером того, что оба:

  • не предполагает uname существует в Windows
  • также определяет процессор

CCFLAGS, определенные здесь, не обязательно рекомендуются или идеальны; они просто то, что проект, к которому я добавлял автоматическое обнаружение OS/CPU, использовал.

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

команда uname(http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html) без параметров должен сообщить вам имя операционной системы. Я бы использовал это, а затем сделал conditionals на основе возвращаемого значения.

пример

UNAME := $(shell uname)

ifeq ($(UNAME), Linux)
# do something Linux-y
endif
ifeq ($(UNAME), Solaris)
# do something Solaris-y
endif

Если вам не нужны сложные вещи, как сделать Git LFS, вы можете обнаружить операционную систему, просто используя два простых трюка:

  • переменные среды OS
  • и затем выполнить команду uname -s
ifeq ($(OS),Windows_NT)
    detected_OS := Windows
else
    detected_OS := $(shell uname -s)
endif

или более безопасный способ, если не Windows и command uname не имеется:

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

затем вы можете выбрать соответствующий материал в зависимости от 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

см. Также это подробный ответ о важности uname -s лучше, чем uname -o.

использование OS (вместо uname -s) упростить алгоритм идентификации. Вы все еще можете использовать исключительно uname -s, но вы должны иметь дело с if/else блоки для проверки всех MinGW, Cygwin и т. д. изменения.

Примечание: переменной окружения OS всегда "Windows_NT" на любой платформе Windows (см. Переменные среды Windows в Википедии). Альтернатива OS является переменной окружения MSVC (он проверяет наличие MS Visual Studio см. пример использования Visual C++).


ниже я приведу полный пример использования make и gcc для создания общей библиотеки:*.so или *.dll в зависимости от платформы. Пример максимально прост, чтобы быть больше понятно :-)

установить make и gcc В Windows см. Cygwin или MinGW.

мой пример основан на пяти файлов

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

не забывайте: файлы Makefile отступы с использованием таблиц.

два файла 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"

чтобы узнать больше, читайте Автоматические Переменные документация как указал 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);
}

сборка

исправить copy-paste из Makefile (заменить начальные пробелы одной таблицей).

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

на make команда одинакова на обеих платформах. Данный вывод находится на 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'

запустить

приложение требует знать, где находится общая библиотека.

в Windows простым решением является копирование библиотеки, в которой находится приложение:

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

в Unix-подобных ОС вы можете использовать LD_LIBRARY_PATH переменные среды:

> export LD_LIBRARY_PATH=lib

выполните команду в Windows:

> app/app.exe
hello

выполните команду на Unix-подобные ОС:

> app/app
hello

недавно я экспериментировал, чтобы ответить на этот вопрос, который я задавал себе. Вот мои выводы:--9-->

так как в Windows вы не можете быть уверены, что можно использовать gcc -dumpmachine. Это отобразит цель компилятора.

также может возникнуть проблема при использовании uname Если вы хотите выполнить перекрестную компиляцию.

вот пример списка возможных выхода gcc -dumpmachine:

  • mingw32
  • i686 в-ПК-под Cygwin
  • x86_64-redhat-linux

вы можете проверить результат в файле makefile следующим образом:

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

это сработало хорошо для меня, но я не уверен, что это надежный способ получить тип системы. По крайней мере, это надежно о MinGW и это все, что мне нужно, так как не требуется иметь или префикса msys в Окна.

подводя итог, uname дает вам система on который вы компилируете, и gcc -dumpmachine дает вам система на который вы компилируете.


на git makefile содержит многочисленные примеры того, как управлять без autoconf/automake, но все еще работают на множестве платформ unixy.


Update: теперь я считаю этот ответ устаревшим. Я опубликовал новое идеальное решение дальше.

Если ваш makefile может работать на не Cygwin Windows,uname может быть недоступна. Это неудобно, но это потенциальное решение. Сначала вы должны проверить Cygwin, чтобы исключить его, потому что в нем есть окна PATH переменной окружения тоже.

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

Это работа, которую GNU automake/autoconf предназначены для решения. Возможно, вы захотите исследовать их.

в качестве альтернативы вы можете установить переменные среды на разных платформах и сделать Makefile условным по отношению к ним.


я столкнулся с этой проблемой сегодня, и мне это было нужно на Solaris, поэтому вот стандартный способ POSIX сделать (что-то очень близкое) это.

#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

другой способ сделать это с помощью скрипта "configure". Если вы уже используете его с вашим makefile, вы можете использовать комбинацию uname и sed, чтобы заставить вещи работать. Во-первых, в вашем скрипте сделайте:

UNAME=uname

затем, чтобы поместить это в свой Makefile, начните с Makefile.в котором должно быть что-то вроде

UNAME=@@UNAME@@

в нем.

используйте следующую команду sed в сценарии configure после UNAME=uname немного.

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

теперь ваш makefile должен иметь UNAME определяется по желанию. Если / elif / else-это все, что осталось!


вот простое решение, которое проверяет, находитесь ли вы в среде Windows или posix (Linux/Unix/Cygwin/Mac):

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

он использует тот факт, что echo существует как в posix-подобных, так и в Windows средах, и что в Windows оболочка не фильтрует кавычки.


обратите внимание, что Makefiles чрезвычайно чувствительны к интервалу. Вот пример файла Makefile, который запускает дополнительную команду на OS X и который работает на OS X и Linux. В целом, однако, autoconf / automake-это способ пойти на что-либо нетривиальное.

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 $

Я, наконец, нашел идеальное решение, которое решает эту проблему для меня.

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

переменной команде uname устанавливается на Linux, под Cygwin, префикса msys, Windows и FreeBSD, NetBSD, так (или предположительно Солярис, Дарвин, в OpenBSD, AIX и HP и УБ), или неизвестно. Затем его можно сравнить на протяжении оставшейся части файла Makefile для разделения любых переменных и команд, чувствительных к ОС.

ключ в том, что Windows использует точки с запятой для разделения путей в переменной PATH, тогда как все остальные используют двоеточия. (Можно сделать каталог Linux с"; " в имени и добавить его в PATH, что нарушило бы это, но кто бы это сделал?) Это кажется наименее рискованным методом для обнаружения собственных окон, потому что ему не нужен вызов оболочки. Путь Cygwin и MSYS использует двоеточия so команде uname вызывается для них.

обратите внимание, что переменная среды ОС может использоваться для обнаружения окон, но не для различения Cygwin и собственных окон. Тестирование эхом котировки работают, но для этого требуется вызов shell.

к сожалению, Cygwin добавляет некоторую информацию о версии к выходу команде uname, поэтому я добавил вызовы "patsubst", чтобы изменить его на просто "Cygwin". Кроме того, uname для MSYS фактически имеет три возможных выхода, начиная с MSYS или MINGW, но я также использую patsubst для преобразования всех в просто "MSYS".

Если важно различать собственные системы Windows с некоторыми uname и без них.exe на пути, эта линия может использоваться вместо простого задания:

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

конечно во всех случаях GNU сделать требуется, или другой сделать который поддерживает используемые функции.