293

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

どの端末にいても自分のプロジェクトに取り組めるのが好きです。これまでのところ、コンピューターを切り替えるたびにメイクファイルを変更することで、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
4

13 に答える 13

330

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

  • 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
于 2012-08-23T20:06:35.247 に答える
69

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 を区別したい場合は、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、識別アルゴリズムが簡素化されます。単独で使用することもできますが、MinGW、Cygwin などのすべてのバリエーションをチェックするには、ブロックをuname処理する必要があります。if/else

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

  • の代替OSは環境変数です( MS Visual StudioMSVCの存在をチェックします。Visual C++ を使用した例を参照してください)。


以下に、 および を使用makegccて共有ライブラリを構築するための完全な例を示します。*.soまたは*.dll、プラットフォームによって異なります。この例は、理解しやすいようにできるだけ単純にしています。

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

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

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

注意: tabulationMakefileを使用してインデントされています。以下のサンプルファイルをコピペする際の注意。

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"

詳細については、 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'

LD_LIBRARY_PATHUnix ライクな OS では、環境変数を使用できます。

> export LD_LIBRARY_PATH=lib

Windows で次のコマンドを実行します。

> app/app.exe
hello

Unix ライクな OS で次のコマンドを実行します。

> app/app
hello
于 2013-02-08T17:17:51.647 に答える
20

私は最近、自分自身に問いかけているこの質問に答えるために実験をしていました。これが私の結論です:

unameWindowsでは、コマンドが使用可能かどうかを確認できないため、を使用できます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

それは私にとってはうまくいきましたが、それがシステムタイプを取得するための信頼できる方法であるかどうかはわかりません。少なくとも、MinGWunameについては信頼性があり、WindowsにコマンドまたはMSYSパッケージが必要ないため、必要なのはそれだけです。

要約すると、あなたがコンパイルしているシステムをあなたに与え、unameあなたがあなたがコンパイルしているシステムgcc -dumpmachineあなたに与えます。

于 2012-09-10T12:01:37.610 に答える
11

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

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

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(shell uname -s)
endif
endif
于 2012-05-15T20:23:10.583 に答える
8

私は今日この問題に遭遇し、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
于 2012-04-24T19:52:03.447 に答える
8

それが、GNU のautomake / autoconfが解決するように設計されている仕事です。あなたはそれらを調査したいかもしれません。

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

于 2009-04-03T14:19:38.887 に答える
3

Makefile は間隔に非常に敏感であることに注意してください。OS X で追加のコマンドを実行し、OS X と Linux で動作する Makefile の例を次に示します。ただし、全体としては、autoconf/automake は、些細なことではありません。

UNAME := $(シェル 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

すべて: vor

掃除:
    rm -f $(OBJECTS) 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) Makefile
    $(CPP) $(CPPFLAGS) -c $
于 2009-10-01T16:12:59.397 に答える
2

これを行う別の方法は、「c​​onfigure」スクリプトを使用することです。メイクファイルで既に使用している場合は、una​​me と sed を組み合わせて使用​​することで問題を解決できます。まず、スクリプトで次のようにします。

UNAME=uname

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

UNAME=@@UNAME@@

初期化。

ビットの後に、configure スクリプトで次の sed コマンドを使用しますUNAME=uname

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

これで、makefile は必要UNAMEに応じて定義されているはずです。あとは if/elif/else 文だけ!

于 2011-08-04T00:43:13.767 に答える