OS がメイクファイルを検出しています
-
23-08-2019 - |
質問
私は日常的に、複数の異なるコンピュータと、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
これはその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 ですでにこれを使用している場合は、uname と sed を組み合わせて使用すると、問題を解決できます。まず、スクリプト内で次のことを実行します。
UNAME=uname
次に、これを Makefile に入れるために、次のような Makefile.in から始めます。
UNAME=@@UNAME@@
初期化。
次の sed コマンドを構成スクリプト内で使用します。 UNAME=uname
少し。
sed -e "s|@@UNAME@@|$UNAME|" < Makefile.in > Makefile
これで、メイクファイルには次の内容が含まれるはずです UNAME
必要に応じて定義します。残っているのは if/elif/else ステートメントだけです。