[makefile] 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



답변

여기에 좋은 답변이 많이 있지만, 다음과 같은 더 완전한 예제를 공유하고 싶었습니다.

  • unameWindows에 존재 한다고 가정하지 않습니다
  • 또한 프로세서를 감지

여기에 정의 된 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-nameuname -suname -o

  • OS대신 ( uname)을 사용 하면 식별 알고리즘이 간소화됩니다. 여전히 단독으로 사용할 수는 uname있지만 if/else모든 MinGW, Cygwin 등 변형을 확인하려면 블록을 처리해야합니다 .

  • 환경 변수 OS는 항상 "Windows_NT"다른 Windows 버전으로 설정됩니다 ( %OS%Wikipedia의 환경 변수 참조 ).

  • 대안은 OS환경 변수입니다 MSVC( MS Visual Studio 의 존재를 확인합니다 ( Visual C ++ 사용 예제 참조 )).


내가 사용하는 완전한 예를 제공 아래 makegcc: 공유 라이브러리를 구축 *.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을 조건부로 만들 수 있습니다.