質問

私は日常的に、複数の異なるコンピュータと、Mac OS X、Linux、Solaris などの異なるオペレーティング システムを使用して作業しています。私が取り組んでいるプロジェクトでは、リモート git リポジトリからコードをプルします。

どの端末を使用しているかに関係なく、プロジェクトに取り組むことができるのが好きです。これまでのところ、コンピューターを切り替えるたびにメイクファイルを変更することで、OS の変更を回避する方法を見つけてきました。ただし、これは面倒な作業であり、多くの頭痛の種を引き起こします。

使用している OS を検出し、それに応じて構文を変更するようにメイクファイルを変更するにはどうすればよいですか?

メイクファイルは次のとおりです。

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

次の 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

ケン・ジャクソン 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% ウィキペディアの環境変数).

  • の代替案 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 (先頭のスペースを 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

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

Windows では、 uname コマンドが利用可能なので、使用できます gcc -dumpmachine. 。これにより、コンパイラのターゲットが表示されます。

使用時に問題が発生する可能性もあります uname クロスコンパイルを実行したい場合。

以下は、考えられる出力のリストの例です。 gcc -dumpmachine:

  • みんぐ32
  • 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 必要ないので必要なのはこれだけです uname コマンドまたは MSYS Windows のパッケージ。

総括する、 uname システムを提供します の上 あなたがコンパイルしているもの、そして gcc -dumpmachine システムを提供します のために あなたがコンパイルしているものです。

git メイクファイル autoconf/automake を使用せずに管理する方法の例が多数含まれていますが、多数の Unixy プラットフォームでも動作します。

アップデート:私は今、この答えは時代遅れであると考えています。私はさらに下の新しい完璧なソリューションを掲示しています。

あなたのメイクファイルは、非CygwinのWindows上で実行されている場合、

unameが使用できない場合があります。これは厄介だが、これは潜在的なソリューションです。それはあまりにもそのPATH環境変数でWindowsを持っているので、あなたは、それを除外するために最初はCygwinをチェックする必要があります。

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(shell uname -s)
endif
endif
// WWW:

これはそのGNUの automakeをする / <のhref = "HTTPの仕事です.gnu.org /ソフトウェア/ autoconfの/」のrel = "noreferrer">解決するために設計されているにはautoconf。あなたはそれらを調査することをお勧めします。

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

私は今日、この問題に走ったと私は(に非常に近いもの)これを行うにはPOSIX標準的な方法であるので、ここでのSolaris上で、それを必要としていました。

#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の、ダーウィン、OpenBSDの、AIX、HP-UX)、または不明に設定されています。その後、任意のOS感受性変数とコマンドを分離するためのMakefileの残りの部分の全体にわたって比較することができる。

キーは、Windowsが誰もがコロンを使用するのに対し、PATH変数にパスを区切るためにセミコロンを使用していることです。これがため、ネイティブのWindowsを検出するために、少なくとも危険な方法であると思われる。(名前にし、PATHに追加し、これを破ることになるが、誰がそんなことをするだろう「」これは、とLinuxのディレクトリを作成することも可能です?)シェルの呼び出しを必要としません。 CygwinとMSYS PATH利用コロンので、のはuname の彼らのために呼び出されます。

OS環境変数がWindowsを検出するために使用することができますが、CygwinとネイティブのWindowsを区別しないことに注意してください。引用符のエコーのためのテストは動作しますが、それはシェルの呼び出しを必要とします。

残念ながら、Cygwinは、のはuname のの出力にいくつかのバージョン情報を追加するので、私は「patsubstはただ「Cygwinの」にそれを変更するために呼び出す追加しました。また、MSYSのためのunameは、実際にはMSYSまたはMINGWで始まる三つの可能な出力を備えていますが、私は単に「MSYS」にすべてを変換するためにpatsubstも使用します。

それはとして、パス上のいくつかのuname.exeせずにネイティブのWindowsシステムを区別することが重要です。

場合は、この行ではなく、単純な割り当てを使用することができます:

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

もちろん、すべての場合にはGNU の作成が必要とされる、または別ののものを用いた機能をサポートして行います。

の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 $(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 $

これを行うもう 1 つの方法は、「構成」スクリプトを使用することです。Makefile ですでにこれを使用している場合は、una​​me と sed を組み合わせて使用​​すると、問題を解決できます。まず、スクリプト内で次のことを実行します。

UNAME=uname

次に、これを Makefile に入れるために、次のような Makefile.in から始めます。

UNAME=@@UNAME@@

初期化。

次の sed コマンドを構成スクリプト内で使用します。 UNAME=uname 少し。

sed -e "s|@@UNAME@@|$UNAME|" < Makefile.in > Makefile

これで、メイクファイルには次の内容が含まれるはずです UNAME 必要に応じて定義します。残っているのは if/elif/else ステートメントだけです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top