ОС обнаружение файл 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
я столкнулся с этой проблемой сегодня, и мне это было нужно на 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 сделать требуется, или другой сделать который поддерживает используемые функции.