OS 감지 메이크파일
-
23-08-2019 - |
문제
저는 일상적으로 여러 컴퓨터와 Mac OS X, Linux, Solaris 등 여러 운영 체제에서 작업합니다.제가 작업 중인 프로젝트의 경우 원격 Git 저장소에서 코드를 가져옵니다.
나는 어느 터미널에 있든 상관없이 내 프로젝트에 참여할 수 있는 것을 좋아합니다.지금까지 컴퓨터를 전환할 때마다 makefile을 변경하여 OS 변경을 피하는 방법을 찾았습니다.그러나 이것은 지루하고 많은 두통을 유발합니다.
내가 사용하고 있는 OS를 감지하고 그에 따라 구문을 수정하도록 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
창에 존재합니다 - 프로세서도 감지합니다
여기에 정의 된 CCFLAG는 반드시 권장되거나 이상적 일 필요는 없습니다. 그것들은 내가 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를 구별하려는 경우 흥미로운 대안을 제안합니다.보다 그의 대답 그것은 다음과 같습니다:
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 C++를 사용한 예).
아래에서는 다음을 사용하여 완전한 예를 제공합니다. make
그리고 gcc
공유 라이브러리를 구축하려면: *.so
또는 *.dll
플랫폼에 따라.예제는 이해하기 쉽도록 최대한 간단합니다.
설치하기 위해서 make
그리고 gcc
Windows에서는 참조 시그윈 또는 MinGW.
내 예는 5개의 파일을 기반으로 합니다.
├── 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);
}
빌드
복사 붙여넣기 수정 Makefile
(선행 공백을 하나의 표로 대체)
> sed 's/^ */\t/' -i */Makefile
그만큼 make
명령은 두 플랫폼 모두에서 동일합니다.주어진 출력은 Unix 계열 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'
Unix 계열 OS에서는 다음을 사용할 수 있습니다. LD_LIBRARY_PATH
환경 변수:
> export LD_LIBRARY_PATH=lib
Windows에서 다음 명령을 실행합니다.
> app/app.exe
hello
Unix 계열 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 그리고 그게 내가 가질 필요가 없기 때문에 필요한 전부입니다. uname
명령 또는 MSYS Windows의 패키지.
요약하기 위해 uname
시스템을 제공합니다 ~에 당신이 컴파일하고 있습니다 gcc -dumpmachine
시스템을 제공합니다 ~을 위한 당신이 컴파일하는 것.
그만큼 git makefile AutoConf/Automake없이 관리하는 방법에 대한 수많은 예가 포함되어 있지만 여전히 수많은 Unixy 플랫폼에서 작동합니다.
업데이트 : 이제이 답변은 쓸모 없다고 생각합니다. 새로운 완벽한 솔루션을 더 게시했습니다.
Makefile이 아닌 Windows에서 실행중인 경우 uname
사용할 수 없습니다. 어색하지만 이것은 잠재적 인 해결책입니다. Cygwin을 먼저 확인하려면 Windows에 Windows가 있기 때문에 배제하려면 먼저 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
다음은 Windows 또는 Posix-Like (Linux/Unix/Cygwin/Mac) 환경에 있는지 확인하는 간단한 솔루션입니다.
ifeq ($(shell echo "check_quotes"),"check_quotes")
WINDOWS := yes
else
WINDOWS := no
endif
Echo가 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는 콜론을 사용하므로 이름을 밝히다 그들을 위해 부름 받았습니다.
OS 환경 변수는 Windows를 감지하는 데 사용할 수 있지만 Cygwin과 기본 Windows를 구별하는 데는 사용할 수 없습니다.따옴표 반향 테스트는 작동하지만 셸 호출이 필요합니다.
불행하게도 Cygwin은 출력에 일부 버전 정보를 추가합니다. 이름을 밝히다, 그래서 'patsubst' 호출을 추가하여 'Cygwin'으로 변경했습니다.또한 MSYS의 uname에는 실제로 MSYS 또는 MINGW로 시작하는 세 가지 가능한 출력이 있지만 patsubst도 사용하여 모두 'MSYS'로 변환합니다.
경로에 uname.exe가 있거나 없는 기본 Windows 시스템을 구별하는 것이 중요한 경우 간단한 할당 대신 다음 줄을 사용할 수 있습니다.
UNAME := $(shell uname 2>NUL || echo Windows)
물론 모든 경우에 GNU 만들다 필수이거나 다른 만들다 사용되는 기능을 지원합니다.
Makefiles는 간격에 매우 민감합니다. 다음은 OS X에서 추가 명령을 실행하고 OS X 및 Linux에서 작동하는 MakeFile의 예입니다. 그러나 전반적으로 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 $
이를 수행하는 또 다른 방법은 "구성"스크립트를 사용하는 것입니다. 이미 Makefile과 함께 하나를 사용하고 있다면 Uname과 Sed의 조합을 사용하여 일을 해결할 수 있습니다. 먼저, 스크립트에서 다음을 수행하십시오.
UNAME=uname
그런 다음 이것을 makefile에 넣으려면 makefile.in으로 시작하십시오.
UNAME=@@UNAME@@
그것에.
다음 SED 명령을 사용하여 다음 SED 명령을 사용하여 다음 스크립트를 사용합니다. UNAME=uname
조금.
sed -e "s|@@UNAME@@|$UNAME|" < Makefile.in > Makefile
이제 당신의 makefile은 가져야합니다 UNAME
원하는대로 정의됩니다. if/elif/else 진술은 남은 전부입니다!