나는 여러 다른 컴퓨터와 여러 다른 운영 체제 (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