LinuxとWindowsでそれぞれmakeとnmakeを介してコンパイルしたい単純なCプログラム(1つのソースファイル)があります。単一のメイクファイルでこれを達成する可能性はありますか?
私は次のようなことを考えました
ifeq($(MAKE), nmake)
// nmake code here
else
// make code here
endif
残念ながら、nmakeはifeq
を理解していないようですので、それを使用することはできません。私は動作するメイクファイルを持っていますが、それは非常に醜い結果を生み出します:
hello: hello.c
$(CC) hello.c
これは両方のシステムで機能します。問題は、結果がそれぞれのコンパイラのデフォルトの動作に依存することです。 Linuxでは、「hello」ではなく「a.out」という名前の実行可能ファイルを取得します。 Windowsでは「hello.exe」を取得しますが、「hello.obj」も必要ありません。
別の方法はありますか?それとも私が試みていることは絶対に不可能ですか?
おそらく不可能ではありませんが、とにかく2つのmakefileを作成する方が簡単なほど難しいでしょう。
ただし、GNU make(Linuxで使用)とnmakeの両方にincludeディレクティブがあるため、メインのmakefileに含まれる共通のmakefileにいくつかの一般的なものを入れることができます。
これには CMake の使用を検討する必要があります。 1つのソースファイルで、それは非常に簡単なはずです!
簡単なプロジェクトを設定する方法は次のとおりです。
cmake_minimum_required(VERSION 2.8)
project(Simple)
include_directories("${PROJECT_BINARY_DIR}")
add_executable(Simple simple.cpp)
単純なプロジェクトをビルドするには、次のようにします(これは、ソースファイルとCMakeLists.txtファイルが~/src/simple
にあることを前提としています。
foo@bar~/src/simple$ mkdir build
foo@bar~/src/simple$ cd build
foo@bar~/src/simple$ cmake ..
foo@bar~/src/simple$ make
MakeとNMAKEで使用するのと同じmakefileインクルードを使用したかったのです。 makeはコメント行の行継続を認識しますが、NMAKEは認識しないため、これは、MakeとNMAKEに対して別々の命令を持つことができることを意味します。例えば:
# \
!ifndef 0 # \
# NMAKE code here \
MV=move # \
RM=del # \
CP=copy # \
!else
# Make code here
MV=mv -f
RM=rm -f
CP=cp -f
# \
!endif
NMAKE固有のコードが# \
で終了していることを確認する必要があります。
GNU MakeとMicrosoftNMAKEの両方で機能する共通のmakefileを使用する方法を見つけることができません。主な理由は、「include」および/または「if」ディレクティブの構文に互換性がないためです。 。MicrosoftNMAKEでは、ディレクティブに!プレフィックスを使用する必要があります。たとえば、!if、!includeなど...
ただし、個別のマクロを持つことが許可されている場合は、だまされる可能性があります。ここでは、以下を観察することにより、makefileをGNU MakeとMicrosoftNMAKEの両方と互換性を持たせるためにこれまでに見つけた最良の方法を示します。
注:以下は、Microsoft Visual Studio2015およびMINGW32を使用してテストされています。
ステップ1:次のDOSバッチファイルを作成し、CMDプロンプトが呼び出されるたびに実行します。
set MAKEFILES=TOOLS.gcc
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
手順2:次のように作業ディレクトリの下にTOOLS.iniファイルを作成します(このファイルは、ライブラリを除いて、プロジェクトの依存関係から独立しています)
[NMAKE]
LDLIBS =
CDEBUG = /Zi
LDEBUG = /debug:full
WDFLAGS = /wd4996 /wd4774 /wd4018 /wd4710 /wd4820
CFLAGS = /nologo $(CDEBUG) /EHsc /Wall $(WDFLAGS)
LDFLAGS = /nologo $(LDEBUG)
RM = del /F /Q
LINK = "$(VCINSTALLDIR)bin\link" $(LDFLAGS)
CP = copy
CC = cl
CPP = $(CC) /P
X = .exe
O = .obj
.obj.exe:
$(LINK) $** $(LOADLIBES) $(LDLIBS) /Out:$@
ステップ3:以下のように作業ディレクトリの下にTOOLS.gccを作成します:(このファイルは、ライブラリを除いてプロジェクトの依存関係から独立しています)
LD_LIBS =
LDLIBS =
CDEBUG = -g
LDEBUG = -g
CFLAGS = $(CDEBUG)
LDFLAGS = $(LDEBUG)
RM = rm -f
LINK = gcc $(LDFLAGS)
CP = cp
CC = gcc
CPP = $(CC) -E
X =
O = .o
%: %.o
$(LINK) $^ $(LOADLIBES) $(LDLIBS) -o $@
ステップ4:依存関係のみが指定されている以下のようにメイクファイルを編集します($(X)と$(O)に注意してください)。
Shell = /usr/bin/sh
app: app1$(X) app2$(X)
app1$(X): app1$(O)
app2$(X): app2$(O)
clean:
$(RM) *.exe *.o *.obj *.ilk *.pdb *.tmp *.i *~
ステップ5:同じmakefileでGNU MakeとMicrosoftNMAKEを楽しむ
$ nmake
$ make clean
$ nmake clean
$ make
私の解決策は、2つの異なるファイル名を使用することです。 (異なるOSでのMakefile名検索の優先順位は同じではないため)
Windowsの場合、通常の「Makefile」を使用します。
Linuxの場合、 この記事 に従って特別な「GNUmakefile」を使用します。
Nmake(Win)が「Makefile」を検索し、make(Linux)が「GNUmakefile」を検索するようにします。
まったく違うことを考えただけです。
非常に単純なMakefileに固執する場合、それは機能し、「標準」変数CCとCFLAGSをそれぞれの環境に配置するだけです。
export CC=gcc
それぞれ
set CC=CL.EXE
そして
export CFLAGS=-o myexecutable
それぞれ
set CFLAGS=/out:myexecutable.exe
それはうまくいくかもしれません。
注意してください、私は使用する正確なオプションに固執していません、あなたはあなた自身でそれらを理解しなければならないでしょう。ただし、AFAIKはどちらも、バリアントに同じフラグのセットを認識させます。それらをそれぞれのコマンドラインで設定することもできます(ただし、NMAKEは異なる「ifeq」構文を使用するため、makefileでは設定できません...)
はい、単一のMakefileでこれを行うことができます。この資料の最良の情報源は、O'Reillyの本です。
GNU Make、Third Edition By Robert Mecklenburgでプロジェクトを管理する
第7章:ポータブルMakefileを参照してください。
要約すると、この手法は、Windowsコマンドインタープリターが存在するかどうかを示す環境変数ComSpecをテストすることです。
ifdef COMSPEC
MV ?= move
RM ?= del
else
MV ?= mv -f
RM ?= rm -f
endif
これを、sedを使用してNmakeのmakefileを編集するポータブルシェルスクリプトまたはGNU make .. ..
最近、Cプリプロセッサを使用して、プリプロセッサシンボルを含むテンプレートMakefile.ccからポータブルMakefileを生成する実験を行いました。これまでのところ、驚くほどうまく機能しています。最初の観察は、NMAKEがTools.iniファイルをプレスキャンすることです。これは私が同じディレクトリに提供します。
[NMAKE]
MAKECONFIG=-D_NMAKE
次に、その隣に「true」のMakefileがあります。これは、GNU MakeとNMAKEの共通のサブ言語のみで記述されています。
MAKEFILE=Makefile.mk
TEMPLATE=Makefile.cc
all: $(MAKEFILE)
$(MAKE) -f $(MAKEFILE)
clean: $(MAKEFILE)
$(MAKE) -f $(MAKEFILE) clean
$(MAKEFILE): $(TEMPLATE)
$(CXX) $(MAKECONFIG) -E $(TEMPLATE) > $(MAKEFILE)
-Eスイッチは、ファイルを前処理するためだけのコンパイラー(少なくとも、私が使用している大きな3つ:GCC、Clang、およびCL)ではかなり一般的であることに注意してください。 With GNU $(MAKECONFIG)を何にも展開しないようにしますが、NMAKEでは、それ自体を宣言するプリプロセッサ変数を提供します。テンプレートMakefile.ccは、#ifdefでチェックできるため、コンパイラがそれ自体を宣言する一般的な変数であるため、Makefile.mkを、「make」プログラム、オペレーティングシステム、および使用しているコンパイラの両方に合わせてかなりカスタマイズできます。
'make'がある場合は、おそらくすでにCコンパイラも持っています。 CMakeやautotoolsのような追加のソフトウェアをインストールする必要はありません。古いメカニズムを使用しているため、多くの環境で機能する可能性があります。そして、私がこれまでに言うことができたことから、それは本当に速いです。少なくともautotoolsで設定ステップを実行するよりも高速です。私が直面した唯一の欠点は、プリプロセッサがコードのインデントを変更するため、Makeルールのスタイルが同じ行に制限されることです。また、プリプロセッサは#タグ付きの行を吐き出しますが、これらはMakefileでコメントを開始するため、とにかく無視されます。
次のスニペットのようなMakefile.ccを持つやや小さなC++プロジェクトがあります。 GNU MakeまたはNMAKEでGCC、Clang、またはCLのいずれかを使用し、WindowsまたはPOSIX環境でコンパイルします。BSDMakeをサポートしたり、他のコンパイラをテストしたりすることはまだありません。
// Make Version
#ifdef _NMAKE
# define ifdef !ifdef
# define ifndef !ifndef
# define else !else
# define endif !endif
# define err(x) !error x
# define cat(x, y) x=$(x) y
#else // GNU Make
# define err(x) $(error x)
# define cat(x, y) x += y
#endif
// System Commands
ifdef Shell
RM=rm -f
else
ifdef COMSPEC
RM=del /f
else
err("Cannot determine your system commands.")
endif // COMSPEC
endif // Shell
// Project Variables
STD=c++17
SRC=test.cpp dbg.cpp dir.cpp dll.cpp env.cpp err.cpp fifo.cpp file.cpp shm.cpp sig.cpp socket.cpp sys.cpp xdg.cpp
BIN=test
.SUFFIXES: .cpp .hpp .o .d .obj .pdb .lib .exp .ilk .log .i .db
// Operating system
#ifdef _WIN32
cat(CFLAGS, -D_WIN32)
EXE=$(BIN).exe
#else
cat(CFLAGS, -D_POSIX_C_SOURCE)
cat(LDFLAGS, -ldl -lrt -lpthread)
EXE=$(BIN)
#endif
// Make Targets
all: $(EXE)
clean: ; $(RM) $(EXE) *.o *.d *.obj *.pdb *.lib *.exp *.ilk *.log *.i
// Compiler Options
#ifdef _MSC_VER
cat(CFLAGS, -nologo -std:$(STD) -W4 -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -EHsc -permissive-)
ifndef NDEBUG
cat(CFLAGS, -Zi)
endif
cat(LDFLAGS, -nologo)
OBJ=$(SRC:.cpp=.obj)
$(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -Fe$@
.cpp.obj: ; $(CXX) $(CFLAGS) -c $<
#Elif defined(__GNUC__) || defined(__llvm__) || defined(__clang__)
cat(CFLAGS, -std=$(STD) -Wall -Wextra -Wpedantic -MP -MMD)
ifndef NDEBUG
cat(CFALGS, -g)
endif
cat(LDFLAGS, -rdynamic)
OBJ=$(SRC:.cpp=.o)
$(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -o $@
.cpp.o: ; $(CXX) $(CFLAGS) -c $<
# ifndef _NMAKE
-include $(SRC:.cpp=.d)
# endif
#else
# error "Cannot determine your compiler."
#endif