1. ホーム
  2. c++

[解決済み] SIMPLE C++ Makefileの作り方

2022-03-15 10:30:39

質問

私たちのプロジェクトでは、Makefile を使ってすべてをまとめることが要求されていますが、教授はその方法を教えてくれません。

しか持っていません。 ファイルを作成します。 a3driver.cpp . ドライバは、ある場所からクラスをインポートします。 "/user/cse232/Examples/example32.sequence.cpp" .

これだけです。それ以外のものはすべて .cpp .

という実行ファイルを作成する簡単なMakefileを作るにはどうしたらいいでしょうか? a3a.exe ?

解決方法は?

Unix用なので、実行ファイルには拡張子がありません。

一つ注意すべきは root-config は、正しいコンパイルとリンクのフラグ、および root に対してアプリケーションをビルドするための正しいライブラリを提供するユーティリティです。これは、このドキュメントの本来の読者に関連する詳細です。

メイクミーベイビー

or You Never Forget The First Time You Got Made(初めて作られた時を決して忘れない

makeの入門的な議論と簡単なmakefileの書き方

Makeとは?そしてなぜ私は気にする必要がありますか?

というツールは 作る は、ビルドの依存関係を管理するものです。つまり、ソースファイル、オブジェクトファイル、ライブラリ、ヘッダなどの集合体からソフトウェアプロジェクトを取り出し、最近変更されたかもしれないものを正しい最新版のプログラムにするために、どのコマンドをどの順番で実行する必要があるかを知ることを引き受けてくれるのです。

実は、Makeは他のことにも使えるのですが、その話は割愛します。

簡単なMakefile

を含むディレクトリがあるとします。 tool tool.cc tool.o support.cc support.hh および support.o に依存している root というプログラムにコンパイルされることになっています。 tool で、ソースファイルをハックしていたとします(つまり、既存の tool が古くなったので、プログラムをコンパイルしたい。

自分でこれを行うには、次のようにします。

  1. 以下のいずれかを確認します。 support.cc または support.hh よりも新しいです。 support.o のようなコマンドを実行します。

    g++ -g -c -pthread -I/sw/include/root support.cc
    
    
  2. のどちらかをチェックします。 support.hh または tool.cc よりも新しいです。 tool.o のようなコマンドを実行します。

    g++ -g  -c -pthread -I/sw/include/root tool.cc
    
    
  3. をチェックします。 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
    
    

ふぅ〜。なんて面倒なんだ! 覚えることがたくさんあるし、間違えることもある。(ちなみに、ここで紹介するコマンドラインは、使用するソフトの環境に依存します。私のパソコンではこの通りです)。

もちろん、3つのコマンドを毎回実行すればいいのです。しかし、この方法では、かなりのソフトウェア(DOGSのように、私のMacBookで一からコンパイルするのに15分以上かかるもの)に対してうまく機能しません。

その代わりに 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つのステップが自動的に実行されます。

ここでインデントされていない行は、次のような形式になっています。 target: dependencies" で、依存関係のどれかがターゲットより新しい場合、関連するコマンド(インデントされた行)を実行するよう 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 ).

この改良版のmakefileのように、ルールの中で変数を使用することができます。

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) のインスタンスをすべて置き換えます。 opatnpat をテキストで表示します。

これを利用することで、私たちは

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

の方が入力しやすく、読みやすいと思います。

に注目してください。

  1. 各オブジェクトファイルと最終的な実行ファイルの依存関係を明示的に記述しています。
  2. 両方のソースファイルについて、コンパイルルールを明示的に入力する必要がありました

暗黙のルールとパターンのルール

一般に、すべての C++ ソースファイルは同じように扱われるべきであると考えられており、Make はこれを表明するための 3 つの方法を提供しています。

  1. サフィックスルール (GNU make では時代遅れとされていますが、後方互換性のために残されています)
  2. 暗黙のルール
  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

組み込みのルールを利用することで、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

また、特殊なアクション(ソースディレクトリのクリーンアップなど)を実行する標準ターゲットもいくつか追加しました。

引数なしで起動された場合、ファイル内で最初に見つかったターゲット(この場合はall)を使用しますが、ターゲットに名前を付けて取得することも可能です。 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

注目すべきは

  1. ソースファイルの依存行がなくなった!!!?
  2. .dependとdependに関連した奇妙なマジックがあります。
  3. もし、あなたが make では ls -A という名前のファイルが表示されます。 .depend には、make の依存行のようなものが含まれています。

その他の読み物

バグを知る・歴史的なメモ

Make の入力言語は空白を気にします。特に 依存関係に続くアクションラインは、タブで始まる必要があります。 . しかし、スペースが連続しても同じに見えることがあり(実際、タブをスペースに無言で変換するエディターもあります)、その結果、Make ファイルは正しく見えても動作しないことがあるのです。これは早い段階でバグとして認識されましたが、( 話はそれから すでに10人のユーザーがいたため、修正されなかったのです。

<サブ (これは、私が物理学の大学院生向けに書いたwikiの記事からコピーしたものです)。