Makefileを使用してプロジェクトのすべてをまとめる必要がありますが、教授はその方法を教えてくれませんでした。
ファイルは1つだけですa3driver.cpp
。ドライバーは、場所からクラスをインポートし"/user/cse232/Examples/example32.sequence.cpp"
ます。
それでおしまい。その他はすべてに含まれてい.cpp
ます。
と呼ばれる実行可能ファイルを作成する単純なMakefileを作成するにはどうすればよいa3a.exe
ですか?
Makefileを使用してプロジェクトのすべてをまとめる必要がありますが、教授はその方法を教えてくれませんでした。
ファイルは1つだけですa3driver.cpp
。ドライバーは、場所からクラスをインポートし"/user/cse232/Examples/example32.sequence.cpp"
ます。
それでおしまい。その他はすべてに含まれてい.cpp
ます。
と呼ばれる実行可能ファイルを作成する単純なMakefileを作成するにはどうすればよいa3a.exe
ですか?
回答:
これはUnix用であるため、実行可能ファイルには拡張子がありません。
注意すべきことの1つroot-config
は、正しいコンパイルとリンクのフラグを提供するユーティリティです。ルートに対してアプリケーションを構築するための適切なライブラリ。これは、このドキュメントの元の読者に関する詳細です。
またはあなたが初めて作ったときのことを決して忘れません
makeの紹介と簡単なmakefileの書き方
メイクとは?そして、なぜ私は気にする必要がありますか?
Makeと呼ばれるツールは、ビルド依存関係マネージャーです。つまり、ソースファイル、オブジェクトファイル、ライブラリ、ヘッダーなどのコレクションからソフトウェアプロジェクトを取得するために、どのコマンドをどの順序で実行する必要があるかを知ることができます---一部は変更されている可能性があります最近---そして、それらをプログラムの正しい最新バージョンに変える。
実際、Makeは他の目的にも使用できますが、それについては説明しません。
簡単なMakefile
を含むディレクトリがtool
tool.cc
tool.o
support.cc
support.hh
あり、 support.o
に依存し、とroot
呼ばれるプログラムにコンパイルされることになっているtool
とします。また、ソースファイルをハッキングしていて(既存のものtool
は現在古くなっていることを意味します)、プログラムをコンパイルします。
これを自分で行うには
support.cc
またはのどちらかsupport.hh
が新しいかどうかを確認し、新しいsupport.o
場合は次のようなコマンドを実行します。
g++ -g -c -pthread -I/sw/include/root support.cc
support.hh
またはのどちらかtool.cc
が新しいかどうかを確認し、新しいtool.o
場合は次のようなコマンドを実行します。
g++ -g -c -pthread -I/sw/include/root tool.cc
tool.o
がより新しいかどうかを確認し、新しい場合はtool
次のようなコマンドを実行します
g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
ふew!なんて面倒なことでしょう!覚えなければならないことがたくさんあり、間違いをする可能性もいくつかあります。(ところで、ここに表示されるコマンドラインの詳細は、ソフトウェア環境によって異なります。これらのコマンドラインは私のコンピューターで動作します。)
もちろん、3つのコマンドすべてを毎回実行することもできます。それは機能しますが、実質的なソフトウェアにうまく拡張できません(私のMacBookでゼロからコンパイルするのに15分以上かかるDOGSなど)。
代わりに、次のmakefile
ようなファイルを作成できます。
tool: tool.o support.o
g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
tool.o: tool.cc support.hh
g++ -g -c -pthread -I/sw/include/root tool.cc
support.o: support.hh support.cc
g++ -g -c -pthread -I/sw/include/root support.cc
make
コマンドラインで入力するだけです。上記の3つのステップが自動的に実行されます。
ここでインデントされていない行は「ターゲット:依存関係」という形式であり、依存関係のいずれかがターゲットよりも新しい場合は、関連するコマンド(インデントされた行)を実行する必要があることをMakeに伝えます。つまり、依存関係の行は、さまざまなファイルの変更に対応するために再構築する必要があるもののロジックを記述しています。support.cc
変更があった場合support.o
、再構築する必要がありますが、そのままにしておくことがtool.o
できます。support.o
変更をtool
再構築する必要がある場合。
各依存関係行に関連付けられたコマンドは、タブ(下記を参照)でオフに設定され、ターゲットを変更する必要があります(または変更時間を更新するために少なくともタッチします)。
この時点で、makefileは実行する必要のある作業を単に記憶しているだけですが、必要なコマンドをすべて完全に把握して入力する必要がありました。それはそのようである必要はありません:Makeは、変数、テキスト操作関数、およびこれをはるかに簡単にすることができる多数の組み込みルールを備えた強力な言語です。
変数を作る
make変数にアクセスするための構文は$(VAR)
です。
Make変数に割り当てる構文は次のとおりですVAR = A text value of some kind
(またはVAR := A different text value but ignore this for the moment
)。
メイクファイルの次の改良版のようなルールで変数を使用できます。
CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
-Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
-lm -ldl
tool: tool.o support.o
g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
これはもう少し読みやすいですが、それでも多くの入力が必要です
関数を作る
GNU makeは、ファイルシステムまたはシステム上の他のコマンドからの情報にアクセスするためのさまざまな機能をサポートしています。この場合、我々は、に興味のある$(shell ...)
引数(複数可)の出力に展開された、と$(subst opat,npat,text)
のすべてのインスタンスを置換するopat
とnpat
、テキストインチ
これを利用すると、次のようになります。
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
tool: $(OBJS)
g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
これは入力が簡単で、はるかに読みやすくなっています。
そのことに注意してください
暗黙的およびパターンのルール
通常、すべてのC ++ソースファイルは同じように扱われるべきであり、Makeはこれを説明する3つの方法を提供します。
暗黙のルールが組み込まれており、そのいくつかを以下で説明します。パターンルールは次のような形式で指定されます
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
これは、表示されたコマンドを実行することによってオブジェクトファイルがCソースファイルから生成されることを意味します。ここで、「自動」変数$<
は最初の依存関係の名前に展開されます。
組み込みルール
Makeには多数の組み込みルールがあります。つまり、非常に多くの場合、プロジェクトは非常に単純なmakefileでコンパイルできます。
Cソースファイル用のGNU make組み込みルールは、上記で示したものです。同様に、のようなルールでC ++ソースファイルからオブジェクトファイルを作成します$(CXX) -c $(CPPFLAGS) $(CFLAGS)
。
単一のオブジェクトファイルはを使用してリンクされますが$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
、複数のオブジェクトファイルをリンクする必要があるため、この場合は機能しません。
組み込みルールで使用される変数
組み込みのルールは一連の標準変数を使用するため、すべてのルールを書き直すことなく、ローカル環境情報(ROOTインクルードファイルの場所など)を指定できます。私たちにとって最も興味深いものは次のとおりです。
CC
-使用するCコンパイラCXX
-使用するC ++コンパイラLD
-使用するリンカーCFLAGS
-CソースファイルのコンパイルフラグCXXFLAGS
-C ++ソースファイルのコンパイルフラグCPPFLAGS
-Cプリプロセッサのフラグ(通常、コマンドラインで定義されたファイルパスとシンボルを含む)、CおよびC ++で使用LDFLAGS
-リンカーフラグLDLIBS
-リンクするライブラリ基本的なMakefile
組み込みルールを利用することにより、メイクファイルを次のように単純化できます。
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
support.o: support.hh support.cc
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) tool
特別なアクション(ソースディレクトリのクリーンアップなど)を実行するいくつかの標準ターゲットも追加しました。
makeが引数なしで呼び出されると、ファイルで見つかった最初のターゲット(この場合はすべて)が使用されますが、ターゲットに名前を付けてmake clean
、この場合オブジェクトファイルを削除することもできます。
すべての依存関係はまだハードコーディングされています。
いくつかの不可解な改善
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
depend: .depend
.depend: $(SRCS)
$(RM) ./.depend
$(CXX) $(CPPFLAGS) -MM $^>>./.depend;
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) *~ .depend
include .depend
そのことに注意してください
make
、その後ls -A
、あなたは指定されたファイルを参照.depend
メイク依存行のように見えるものが含まれていますその他の読書
バグと歴史的メモを知る
Makeの入力言語は空白を区別します。特に、依存関係に続くアクションラインはタブで始まる必要があります。しかし、一連のスペースは同じように見えます(実際、タブをスペースに、またはその逆に暗黙的に変換するエディターがあります)。その結果、Makeファイルは正しく見えても機能しません。これは早い段階でバグとして識別されましたが、(ストーリーは)すでに10人のユーザーがいるため、修正されませんでした。
(これは私が物理学の大学院生のために書いたwikiの投稿からコピーされました。)
-pthread
フラグgcc
は必要なマクロを定義させるため、-D_REENTRANT
不要です。
root-config
)。同じ機能を備えたより一般的な代替案がある場合は提案するか、単に除外する必要があります。最も頻繁に使用されるmakeマクロのリストと説明のため、私は反対票を投じませんでした。
これは詳細な例を使うと簡単に習得できるといつも思っていました。セクションごとに、インデントされていない1行があり、セクションの名前と依存関係が表示されます。依存関係は、他のセクション(現在のセクションの前に実行されます)またはファイル(更新された場合に、次に実行するときに現在のセクションが再度実行されるようにする)のいずれかですmake
。
これは簡単な例です(タブを使用する必要がある場所で4つのスペースを使用していることに注意してください。StackOverflowではタブを使用できません)。
a3driver: a3driver.o
g++ -o a3driver a3driver.o
a3driver.o: a3driver.cpp
g++ -c a3driver.cpp
と入力make
すると、最初のセクション(a3driver)が選択されます。a3driverはa3driver.oに依存しているので、そのセクションに移動します。a3driver.oはa3driver.cppに依存しているため、a3driver.cppが最後に実行されてから変更された場合にのみ実行されます。実行されている(または実行されたことがない)場合、a3driver.cppを.oファイルにコンパイルしてから、a3driverに戻って最終的な実行可能ファイルをコンパイルします。
ファイルは1つしかないため、次のように減らすこともできます。
a3driver: a3driver.cpp
g++ -o a3driver a3driver.cpp
最初の例を示したのは、メイクファイルの力を示しているからです。別のファイルをコンパイルする必要がある場合は、別のセクションを追加できます。次に、secondFile.cppを使用した例を示します(secondFile.hという名前のヘッダーに読み込まれます)。
a3driver: a3driver.o secondFile.o
g++ -o a3driver a3driver.o secondFile.o
a3driver.o: a3driver.cpp
g++ -c a3driver.cpp
secondFile.o: secondFile.cpp secondFile.h
g++ -c secondFile.cpp
このように、secondFile.cppまたはsecondFile.hで何かを変更して再コンパイルすると、secondFile.cppのみが再コンパイルされます(a3driver.cppではありません)。または、a3driver.cppで何かを変更しても、secondFile.cppは再コンパイルされません。
ご不明な点がありましたらお知らせください。
「all」という名前のセクションと「clean」という名前のセクションを含めることも伝統的です。「all」は通常、すべての実行可能ファイルをビルドし、「clean」は.oファイルや実行可能ファイルなどの「ビルドアーティファクト」を削除します。
all: a3driver ;
clean:
# -f so this will succeed even if the files don't exist
rm -f a3driver a3driver.o
編集:私はあなたがWindowsを使っていることに気づきませんでした。私は、唯一の違いは変化していると思います-o a3driver
し-o a3driver.exe
。
なぜ誰もがソースファイルをリストするのを好むのですか?簡単なfindコマンドで簡単に処理できます。
以下は、単純なC ++ Makefileの例です。.C
ファイルを含むディレクトリにドロップして、次のように入力してくださいmake
...
appname := myapp
CXX := clang++
CXXFLAGS := -std=c++11
srcfiles := $(shell find . -name "*.C")
objects := $(patsubst %.C, %.o, $(srcfiles))
all: $(appname)
$(appname): $(objects)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)
depend: .depend
.depend: $(srcfiles)
rm -f ./.depend
$(CXX) $(CXXFLAGS) -MM $^>>./.depend;
clean:
rm -f $(objects)
dist-clean: clean
rm -f *~ .depend
include .depend
2つのオプションがありました。
オプション1:最も単純なmakefile = NO MAKEFILE。
「a3driver.cpp」の名前を「a3a.cpp」に変更し、コマンドラインで次のように記述します。
nmake a3a.exe
以上です。GNU Makeを使用している場合は、「make」または「gmake」などを使用します。
オプション2:2行のmakefile。
a3a.exe: a3driver.obj
link /out:a3a.exe a3driver.obj
nmake
。link
コマンドラインも非常に少なくとも文書1で特定のコンパイラに非常に具体的に見える、とすべきです。
Makeファイルには、単一のコマンドでコンパイルしてリンクするか、コンパイル用に1つのコマンドとリンク用に1つのコマンドを使用するかに応じて、1つまたは2つの依存関係ルールがあります。
依存関係は、次のような規則のツリーです(インデントはタブでなければならないことに注意してください)。
main_target : source1 source2 etc
command to build main_target from sources
source1 : dependents for source1
command to build source1
そこなければならない目標のためのコマンドの後に空白行も、そしてそこになければならないコマンドの前に空白行も。makefileの最初のターゲットは全体的な目標であり、他のターゲットは、最初のターゲットがそれらに依存している場合にのみ構築されます。
したがって、メイクファイルは次のようになります。
a3a.exe : a3driver.obj
link /out:a3a.exe a3driver.obj
a3driver.obj : a3driver.cpp
cc a3driver.cpp
私は提案します(インデントはタブであることに注意してください):
tool: tool.o file1.o file2.o
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@
または
LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o
後者の提案は、GNU Makeの暗黙のルールを再利用するため、少し優れています。ただし、機能させるには、ソースファイルの名前が最終的な実行可能ファイルと同じである必要があります(例:tool.c
およびtool
)。
通知元を宣言する必要はありません。中間オブジェクトファイルは、暗黙のルールを使用して生成されます。その結果、これMakefile
はCおよびC ++(およびFortranなど)でも機能します。
また、デフォルトでは、Makefile $(CC)
がリンカーとして使用されています。$(CC)
C ++オブジェクトファイルのリンクでは機能しません。それLINK.o
だけのために変更します。Cコードをコンパイルする場合は、LINK.o
値を強制する必要はありません。
もちろん、コンパイルフラグを変数CFLAGS
に追加し、ライブラリをに追加することもできますLDLIBS
。例えば:
CFLAGS = -Wall
LDLIBS = -lm
ワンサイドノート:あなたは外部ライブラリを使用する必要がある場合は、私がお勧めするのpkg-config設定を使用し、正しく設定するために、CFLAGS
とLDLIBS
:
CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)
注意深い読者はMakefile
、1つのヘッダーが変更されると、これが正しく再構築されないことに気付くでしょう。次の行を追加して問題を修正します。
override CPPFLAGS += -MMD
include $(wildcard *.d)
-MMD
ヘッダーの依存関係に関するMakefileフラグメントを含む.dファイルを構築できます。2行目はそれらを使用しています。
確かに、適切に作成されたMakefileには次のルールも含まれている必要がclean
ありdistclean
ます。
clean:
$(RM) *.o *.d
distclean: clean
$(RM) tool
はと$(RM)
同等ですがrm -f
、rm
直接呼び出さないことをお勧めします。
all
ルールも理解されよう。機能するためには、ファイルの最初のルールである必要があります。
all: tool
install
ルールを追加することもできます:
PREFIX = /usr/local
install:
install -m 755 tool $(DESTDIR)$(PREFIX)/bin
DESTDIR
デフォルトでは空です。ユーザーは、別のシステムにプログラムをインストールするように設定できます(クロスコンパイルプロセスでは必須)。にパッケージPREFIX
をインストールするために、複数のディストリビューションのパッケージメンテナーも変更される場合があります/usr
。
最後に、ソースファイルをサブディレクトリに配置しないでください。本当にそうしたい場合は、これMakefile
をルートディレクトリに保存し、フルパスを使用してファイルを識別します(つまりsubdir/file.o
)。
要約すると、完全なMakefileは次のようになります。
LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)
all: tool
tool: tool.o file1.o file2.o
clean:
$(RM) *.o *.d
distclean: clean
$(RM) tool
install:
install -m 755 tool $(DESTDIR)$(PREFIX)/bin
make
私が知っている実装(GNU MakeとBSD Make)では、ルール間に空の行は必要ありません。ただし、make
独自のバグ^ W固有性を備えた実装が数多く存在します。
フライドマッドの答えを使いました。私はしばらくこれを調べました、そしてそれは始めるのに良い方法のようです。このソリューションには、コンパイラフラグを追加する明確に定義された方法もあります。私の環境、Ubuntuとg ++で動作するように変更を加えたので、もう一度答えました。より多くの実用的な例は、時には最高の教師です。
appname := myapp
CXX := g++
CXXFLAGS := -Wall -g
srcfiles := $(shell find . -maxdepth 1 -name "*.cpp")
objects := $(patsubst %.cpp, %.o, $(srcfiles))
all: $(appname)
$(appname): $(objects)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)
depend: .depend
.depend: $(srcfiles)
rm -f ./.depend
$(CXX) $(CXXFLAGS) -MM $^>>./.depend;
clean:
rm -f $(objects)
dist-clean: clean
rm -f *~ .depend
include .depend
Makefileは非常に複雑なようです。私はそれを使用していましたが、g ++ライブラリでリンクしないことに関連するエラーを生成していました。この構成はその問題を解決しました。