web-dev-qa-db-ja.com

OSがメイクファイルを検出

私は、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
228
samoz

ここにはすでに多くの良い答えがありますが、私は両方のより完全な例を共有したいと思いました。

  • windowsにunameが存在すると想定していません
  • プロセッサも検出します

ここで定義されている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
257
Trevor Robinson

パラメータなしの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
116
dbrown0708

2つの簡単なトリックを使用してオペレーティングシステムを検出します。

  • 最初に環境変数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

Ken Jackson 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

ノート:

  • コマンド unameuname -sと同じオプション-s--kernel-name)がデフォルトです。 なぜuname -suname -o よりも優れている。

  • OSの代わりに)unameを使用すると、識別アルゴリズムが簡素化されます。それでもunameのみを使用できますが、if/elseブロックを処理して、MinGW、Cygwinなどのバリエーションをすべてチェックする必要があります。

  • 環境変数OSは、異なるWindowsバージョンでは常に"Windows_NT"に設定されます(Wikipediaの %OS%環境変数 を参照)。

  • OSの代替は、環境変数MSVCです(MS Visual Studioの存在をチェックします。 Visual C++を使用した例 を参照)。


以下に、makeおよびgccを使用して共有ライブラリを構築する完全な例を示します。プラットフォームに応じて、*.soまたは*.dllです。この例は、できる限り簡単に理解できるようにします。

makeおよびgccをWindowsにインストールするには、 Cygwin または MinGW を参照してください。

私の例は5つのファイルに基づいています

 ├── lib
 │   └── Makefile
 │   └── hello.h
 │   └── hello.c
 └── app
     └── Makefile
     └── main.c

注意:Makefileは、tabulationを使用してインデントされます。サンプルファイルの下にコピーアンドペーストする場合の注意。

2つの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"

詳細については、 Automatic Variablesdocumentationcfi で指摘されているとおりに読んでください。

ソースコード

-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のコピーと貼り付けを修正します(先頭のスペースを1つの表に置き換えます)。

> 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
34
olibre

私は自分自身に尋ねていたこの質問に答えるために最近実験していました。私の結論は次のとおりです。

Windowsでは、unameコマンドが使用可能であることを確認できないため、gcc -dumpmachineを使用できます。これにより、コンパイラのターゲットが表示されます。

また、unameを使用するときに、クロスコンパイルを行う場合に問題が発生する場合があります。

以下は、gcc -dumpmachineの可能な出力のリストの例です。

  • mingw32
  • i686-pc-cygwin
  • x86_64-redhat-linux

次のようにして、メイクファイルで結果を確認できます。

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はシステムonを提供し、gcc -dumpmachineはシステムforを提供しています。

18
phsym

git makefile には、autoconf/automakeなしで管理する方法の多数の例が含まれていますが、それでも多数のunixyプラットフォームで動作します。

16
JesperE

更新:この回答は時代遅れだと考えています。新しい完璧なソリューションをさらに下に投稿しました。

Makefileが非Cygwin Windowsで実行されている場合、unameが利用できない場合があります。それは厄介ですが、これは潜在的な解決策です。除外するには、まずCygwinをチェックする必要があります。これは、PATH環境変数にもWINDOWSが含まれているためです。

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(Shell uname -s)
endif
endif
11
Ken Jackson

それが、GNUの automake / autoconf が解決するように設計されている仕事です。それらを調査することをお勧めします。

あるいは、異なるプラットフォームで環境変数を設定し、それらに対してMakefileを条件付きにすることができます。

7
Douglas Leeder

今日、この問題に遭遇し、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
6
Huckle

私はついにこの問題を解決する完璧な解決策を見つけました。

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に追加すると、これが壊れますが、だれがそのようなことをするのでしょうか?) Shell呼び出しは必要ありません。 CygwinおよびMSYS PATHはコロンを使用するため、nameが呼び出されます。

OS環境変数を使用してWindowsを検出できますが、CygwinとネイティブWindowsを区別することはできません。引用符のエコーのテストは機能しますが、シェル呼び出しが必要です。

残念ながら、Cygwinはnameの出力にバージョン情報を追加するため、 'patsubst'呼び出しを追加して、それを単に 'Cygwin'に変更します。また、MSYSのunameには、実際にはMSYSまたはMINGWで始まる3つの出力がありますが、patsubstを使用してすべてを「MSYS」に変換することもできます。

パスにuname.exeがある場合とない場合のネイティブWindowsシステムを区別することが重要な場合は、単純な割り当ての代わりにこの行を使用できます。

UNAME := $(Shell uname 2>NUL || echo Windows)

もちろん、すべての場合でGNU makeが必要です。または、使用される機能をサポートする別のmakeが必要です。

4
Ken Jackson

Windowsまたはposixライク(Linux/Unix/Cygwin/Mac)環境にいるかどうかを確認する簡単なソリューションを次に示します。

ifeq ($(Shell echo "check_quotes"),"check_quotes")
   WINDOWS := yes
else
   WINDOWS := no
endif

これは、posixライクな環境とWindows環境の両方にエコーが存在し、Windowsではシェルが引用符をフィルター処理しないという事実を利用しています。

4
Samuel

Makefileは間隔に非常に敏感であることに注意してください。 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 $ (オブジェクト)
 ifeq($(UNAME)、Darwin)
#Boostライブラリの場所を設定
 install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor 
 endif 
 
%。o:%.cpp $(HEADERS)Makefile 
 $(CPP)$(CPPFLAGS)-c $
3
ChrisInEdmonton

これを行う別の方法は、「構成」スクリプトを使用することです。すでにメイクファイルで使用している場合は、una​​meと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ステートメントが残っているだけです!

2
Sean