문제

저는 일상적으로 여러 컴퓨터와 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

그게 GNU의 직업입니다 Automake/autoconf 해결하도록 설계되었습니다. 당신은 그것들을 조사하고 싶을 수도 있습니다.

또는 다른 플랫폼에서 환경 변수를 설정하고 조건부에 대해 조건부 변수를 만들 수 있습니다.

나는 오늘이 문제를 일으켰고 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 진술은 남은 전부입니다!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top