IT story

OS 감지 makefile

hot-time 2020. 4. 14. 19:16
반응형

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을 조건부로 만들 수 있습니다.


오늘이 문제에 부딪 쳤고 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

반응형