私は、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
が存在すると想定していませんここで定義されている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
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
コマンド uname
はuname -s
と同じオプション-s
(--kernel-name
)がデフォルトです。 なぜuname -s
はuname -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 を参照してください。
├── lib
│ └── Makefile
│ └── hello.h
│ └── hello.c
└── app
└── Makefile
└── main.c
注意:Makefile
は、tabulationを使用してインデントされます。サンプルファイルの下にコピーアンドペーストする場合の注意。
Makefile
ファイル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)
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
私は自分自身に尋ねていたこの質問に答えるために最近実験していました。私の結論は次のとおりです。
Windowsでは、uname
コマンドが使用可能であることを確認できないため、gcc -dumpmachine
を使用できます。これにより、コンパイラのターゲットが表示されます。
また、uname
を使用するときに、クロスコンパイルを行う場合に問題が発生する場合があります。
以下は、gcc -dumpmachine
の可能な出力のリストの例です。
次のようにして、メイクファイルで結果を確認できます。
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を提供しています。
git makefile には、autoconf/automakeなしで管理する方法の多数の例が含まれていますが、それでも多数のunixyプラットフォームで動作します。
更新:この回答は時代遅れだと考えています。新しい完璧なソリューションをさらに下に投稿しました。
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
今日、この問題に遭遇し、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
私はついにこの問題を解決する完璧な解決策を見つけました。
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が必要です。
Windowsまたはposixライク(Linux/Unix/Cygwin/Mac)環境にいるかどうかを確認する簡単なソリューションを次に示します。
ifeq ($(Shell echo "check_quotes"),"check_quotes")
WINDOWS := yes
else
WINDOWS := no
endif
これは、posixライクな環境とWindows環境の両方にエコーが存在し、Windowsではシェルが引用符をフィルター処理しないという事実を利用しています。
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 $
これを行う別の方法は、「構成」スクリプトを使用することです。すでにメイクファイルで使用している場合は、unameと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ステートメントが残っているだけです!