OS 감지 makefile
나는 일상적으로 여러 다른 컴퓨터와 여러 다른 운영 체제 (Mac OS X, Linux 또는 Solaris)에서 작업합니다. 내가 작업중 인 프로젝트의 경우 원격 git 저장소에서 코드를 가져옵니다.
나는 어떤 터미널에 있는지에 관계없이 프로젝트를 수행 할 수 있기를 원합니다. 지금까지 컴퓨터를 전환 할 때마다 makefile을 변경하여 OS 변경을 피할 수있는 방법을 찾았습니다. 그러나 이것은 지루하며 두통을 유발합니다.
사용중인 OS를 감지하고 그에 따라 구문을 수정하도록 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
여기에 좋은 답변이 많이 있지만, 다음과 같은 더 완전한 예제를 공유하고 싶었습니다.
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 )은 운영 체제 이름을 알려줍니다. 그것을 사용하고 반환 값을 기준으로 조건을 만듭니다.
예
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
또는 Windows에없고 uname
사용할 수없는 경우 더 안전한 방법 :
ifeq ($(OS),Windows_NT)
detected_OS := Windows
else
detected_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown')
endif
Cygwin / MinGW / MSYS / Windows를 구별하려면 Ken Jackson 이 흥미로운 대안을 제안합니다. 다음과 같은 답변 을 보십시오 .
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
그런 다음에 따라 관련 항목을 선택할 수 있습니다 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
은 동일 합니다. 보다 나은 이유를 참조하십시오 .uname -s
-s
--kernel-name
uname -s
uname -o
OS
대신 (uname
)을 사용 하면 식별 알고리즘이 간소화됩니다. 여전히 단독으로 사용할 수는uname
있지만if/else
모든 MinGW, Cygwin 등 변형을 확인하려면 블록을 처리해야합니다 .환경 변수
OS
는 항상"Windows_NT"
다른 Windows 버전으로 설정됩니다 (%OS%
Wikipedia의 환경 변수 참조 ).대안은
OS
환경 변수입니다MSVC
( MS Visual Studio 의 존재 여부를 확인합니다 ( Visual C ++ 사용 예 참조 )).
내가 사용하는 완전한 예를 제공 아래 make
와 gcc
: 공유 라이브러리를 구축 *.so
또는 *.dll
플랫폼에 따라. 이 예는 최대한 이해하기 쉽도록 가장 단순합니다.
Windows에 설치 make
하고 설치하려면 Cygwin 또는 MinGW를gcc
참조하십시오 .
내 예는 5 개의 파일을 기반으로합니다.
├── lib
│ └── Makefile
│ └── hello.h
│ └── hello.c
└── app
└── Makefile
└── main.c
알림 : tabulation을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);
}
빌드
복사 붙여 넣기를 수정합니다 Makefile
(앞의 공백을 하나의 표로 대체).
> sed 's/^ */\t/' -i */Makefile
이 make
명령은 두 플랫폼에서 동일합니다. 주어진 출력은 유닉스 계열 OS에 있습니다.
> 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'
유닉스 계열 OS에서 LD_LIBRARY_PATH
환경 변수를 사용할 수 있습니다 .
> export LD_LIBRARY_PATH=lib
Windows에서 명령을 실행하십시오.
> app/app.exe
hello
유닉스 계열 OS에서 명령을 실행하십시오.
> app/app
hello
나는 최근 에이 질문에 대답하기 위해 스스로 실험하고있었습니다. 내 결론은 다음과 같습니다.
Windows에서는 uname
명령이 사용 가능한지 확신 할 수 없으므로을 사용할 수 있습니다 gcc -dumpmachine
. 컴파일러 대상이 표시됩니다.
uname
크로스 컴파일을 수행하려는 경우 사용할 때 문제가 발생할 수도 있습니다 .
가능한 출력 목록은 다음과 같습니다 gcc -dumpmachine
.
- mingw32
- i686-pc-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 에 대해 신뢰할 수 있으며 Windows에 uname
명령이나 MSYS 패키지 가 필요하지 않기 때문에 필요한 것 입니다.
요약하면, uname
당신에게 시스템 제공 에 당신이 컴파일중인, 그리고 gcc -dumpmachine
당신에게 시스템 제공 을 위해 당신이 컴파일되는합니다.
자식 메이크 autoconf를 / automake에, unixy 다양한 플랫폼에 아직 여전히 작업없이 관리하는 방법에 대한 많은 예제가 포함되어 있습니다.
업데이트 : 이제이 답변이 더 이상 사용되지 않는다고 생각합니다. 더 완벽한 새로운 솔루션을 게시했습니다.
Makefile이 Cygwin이 아닌 Windows에서 실행중인 uname
경우 사용하지 못할 수 있습니다. 어색하지만 이것은 잠재적 인 해결책입니다. Cygwin의 PATH
환경 변수에도 WINDOWS가 있으므로 Cygwin을 먼저 제외해야합니다 .
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
다음은 Windows 또는 posix와 같은 (Linux / Unix / Cygwin / Mac) 환경에 있는지 확인하는 간단한 솔루션입니다.
ifeq ($(shell echo "check_quotes"),"check_quotes")
WINDOWS := yes
else
WINDOWS := no
endif
에코는 posix와 같은 환경과 Windows 환경 모두에 존재하고 Windows에서는 쉘이 따옴표를 필터링하지 않는다는 사실을 이용합니다.
마침내이 문제를 해결하는 완벽한 솔루션을 찾았습니다.
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 (또는 아마도 Solaris, Darwin, OpenBSD, AIX, HP-UX) 또는 Unknown으로 설정됩니다. 그런 다음 나머지 Makefile에서 OS에 민감한 변수와 명령을 구분하기 위해 비교할 수 있습니다.
핵심은 Windows가 세미콜론을 사용하여 PATH 변수에서 경로를 구분하는 반면 다른 모든 사람은 콜론을 사용한다는 것입니다. (이름에 ';'을 사용하여 Linux 디렉토리를 만들어 PATH에 추가하면 문제가 발생하지만 누가 그런 일을 할 수 있습니까?) 이것은 기본 Windows를 감지하는 가장 위험한 방법 인 것 같습니다. 쉘 호출이 필요하지 않습니다. Cygwin 및 MSYS PATH는 콜론을 사용하므로 uname 이 필요합니다.
OS 환경 변수를 사용하여 Windows를 감지 할 수 있지만 Cygwin과 기본 Windows를 구별 할 수는 없습니다. 따옴표의 에코 테스트는 작동하지만 셸 호출이 필요합니다.
불행히도 Cygwin은 uname 의 출력에 일부 버전 정보를 추가하므로 'patsubst'호출을 추가하여 'Cygwin'으로 변경했습니다. 또한 MSYS의 uname에는 실제로 MSYS 또는 MINGW로 시작하는 세 가지 가능한 출력이 있지만 patsubst를 사용하여 모두 'MSYS'로 변환합니다.
경로에 uname.exe가 있거나없는 기본 Windows 시스템을 구별하는 것이 중요한 경우 간단한 지정 대신이 행을 사용할 수 있습니다.
UNAME := $(shell uname 2>NUL || echo Windows)
물론 모든 경우에 GNU make 가 필요하거나 사용 된 기능을 지원하는 다른 make 가 필요합니다 .
Makefile은 간격에 매우 민감합니다. 다음은 OS X에서 추가 명령을 실행하고 OS X 및 Linux에서 작동하는 Makefile의 예입니다. 그러나 전체적으로 autoconf / automake는 사소하지 않은 모든 작업을 수행하는 방법입니다.
UNAME : = $ (쉘 uname -s) CPP = g ++ CPPFLAGS = -pthread -ansi-벽-오류 -pedantic -O0 -g3 -I / nexopia / include LDFLAGS = -pthread -L / nexopia / lib -lboost_system 헤더 = data_structures.h http_client.h load.h lock.h search.h server.h thread.h utility.h 객체 = http_client.o load.o lock.o search.o server.o thread.o utility.o vor.o 모두 : vor 깨끗한: rm -f $ (개체) vor vor : $ (OBJECTS) $ (CPP) $ (LDFLAGS) -o vor $ (OBJECTS) ifeq ($ (UNAME), 다윈) # Boost 라이브러리 위치 설정 install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor 엔디 프 % .o : % .cpp $ (HEADERS) 메이크 파일 $ (CPP) $ (CPPFLAGS) -c $
이를 수행하는 다른 방법은 "configure"스크립트를 사용하는 것입니다. makefile과 함께 이미 사용중인 경우 uname과 sed를 함께 사용하여 문제를 해결할 수 있습니다. 먼저 스크립트에서 다음을 수행하십시오.
UNAME=uname
그런 다음 이것을 Makefile에 넣으려면 Makefile.in으로 시작하십시오.
UNAME=@@UNAME@@
그 안에.
UNAME=uname
비트 다음에 configure 스크립트에서 다음 sed 명령을 사용하십시오 .
sed -e "s|@@UNAME@@|$UNAME|" < Makefile.in > Makefile
이제 makefile이 UNAME
원하는대로 정의 되었 어야 합니다. If / elif / else 문만 남았습니다!
참고 URL : https://stackoverflow.com/questions/714100/os-detecting-makefile
'IT story' 카테고리의 다른 글
파이썬을 사용하여 빈 파일 만들기 (0) | 2020.04.14 |
---|---|
정렬되지 않은 목록 항목에서 들여 쓰기를 제거하는 방법? (0) | 2020.04.14 |
Java : 쉼표로 구분 된 문자열을 분할하지만 따옴표로 쉼표는 무시 (0) | 2020.04.14 |
현재 사용중인 CodeIgniter의 버전은 무엇입니까? (0) | 2020.04.14 |
경고 : CSRF 토큰 진위 여부를 확인할 수 없습니다 (0) | 2020.04.14 |