Makefileメモ:参考文献と注記

他人にとっては以下の資料の方が役に立つと思われるので,参考文献として先頭に持ってくる.


以下のメモは,品質については保証しかねる.思考のログとして書いているので,間違っていても修正しない可能性が高い(別物として別の場所に書き直す可能性が高い).

Makefileメモ:理念編

なぜMakefileを使うかといえば,「コンパイルを簡単にするため」であり,もうちょい突き詰めると「コーディング→動作チェックまでの時間を短くするため」とも言える.Makefileを使えば「必要な部分のみコンパイル」ができるので,コンパイルにかける時間が短縮でき,作成開始から完成までのトータルの時間も短くすることが出来る.


にも関わらず,自分があんまりMakefileを使わなかったのは以下の理由が大きい.

  • 書き方わかんね
  • 依存関係を書くのめんどくせ
  • ファイルを追加するたびにMakefileまで書き直すのはめんどくせ

逆に言えば,これらの問題さえ解決されれば,ちょー便利ということになる.で,今回これらが解決したので,そのメモ.また問題が出てきたら文章化→ググるで.

書き方わかんね

「書き方がわからない」というのはどういう状況か.Makefileプログラミング言語と捉えることが出来るので,その方向から考えると以下の要素が挙げられる(もっと綺麗に書ける気もするが今回は気にしない).

  • 何が出来るのかわからない
  • 文法がわからない
何が出来るのかわからない

Makefileは「コンパイルが必要な部分だけコンパイルすること」が可能.しかし,ここで問題になっているのはそういうことではなく,もう一段階下の「〜が更新されていたら〜を実行する」というような知識について.他の例で言えば,「ファイルの読み書きが可能」というのに対する「file.ReadLine(line)でlineに一行分の文字列が入る」というような感じ.
要は「具体的に」何が出来るのかわからない,ということ.


ということで,以下に現在知っている「何が出来るか」を列挙する.

  • 「Aが更新されている場合,Bも更新が必要であり,そのためにCを実行する」というのが書ける
    • おそらく一番重要な「依存関係」と「生成コマンド」の設定.
  • 「使い回す値」や「長い設定」は格納しておける
    • 「使用するコンパイラ」や「指定するオプション」などを変数に格納することで(厳密にはマクロを使うことで),コンパイラの変更を容易にしたり,一行の文字数を減らして見やすくすることができる.(見やすくするのは「意味的な分割」で云々というのもあるが)
  • 「何を実行するか」の部分を一般化できる.
    • 「CPPからOBJを生成する場合,〜を実行する」という風に,ファイル名に依存せずに「〜を実行する」という「生成コマンド」の部分をまとめることができる.ただし,「CPPが更新されたらOBJも更新する」という風に「依存関係」はまとめることができないっぽい.これは自動で計算すればよし.
  • ファイル名などはマクロで参照できる
    • 「CPPからOBJを生成する場合,〜を実行する」という風にファイル名に依存せずに一般化して設定した場合,この設定には肝心のファイル名が含まれていないのでコンパイルの対象が設定できない.その場合でも,マクロを使えばファイル名を持ってこれる.
  • lsとかのコマンドが使える
    • lsやfindを使うことで,「CPPファイルを列挙する」ということが自動でできるため,Makefileを書き直す必要がなくなる.
  • コメントが書ける
    • まぁ一応.あるのとないのとじゃ結構違うし.
文法がわからない

「文法がわからない」とはつまり,「何が出来るか」はわかったけど「それをどう書けばいいか」がわからない状態といえる.要は,上で列挙した「何が出来るか」の書き方がわかんねーぜ問題.ということで,上に列挙したやつの書き方を書いてみる.

  • 「Aが更新されている場合,Bも更新が必要であり,そのためにCを実行する」というのが書ける
    • 「A1・A2が更新されている場合,Bも更新が必要であり,そのためにCとDを実行する」なら以下のように書く.A1とA2の間にはスペースを.CとDの先頭にはタブを.
B:A1 A2
	C
	D
  • 「使い回す値」や「長い設定」は格納しておける
    • HOGE=〜」とすれば「HOGE」に「〜」を格納でき,「$(HOGE)」と書けば「〜」と同じように扱える.
  • 「何を実行するか」の部分を一般化できる.
    • 以下のように書けば,「CPPからOBJを生成する場合,Cを実行する」となる.「CPPが変更されたらOBJを変更する」という意味ではないので注意(「生成コマンド」のまとめであって「依存関係」のまとめではない).また,前述の「依存関係の書き方」とは向きが異なるので注意(B←AとCPP→OBJ).
.cpp.obj:
	C
  • ファイル名などはマクロで参照できる
    • 「$」を先頭につけたもので参照.前述の「HOGE」も実際にはマクロなので,「$」で中身が展開される.「$<」のように,ファイル名を参照するマクロなどがある(リファレンスの方を参照).
  • lsとかのコマンドが使える
    • 「$(shell ls *.cpp)」とすれば,そのディレクトリに存在するCPPファイルを列挙したものが展開される.
  • コメントが書ける
    • 「#」で始まった行はコメントになる

依存関係を書くのめんどくせ

ファイルの数が一桁ならば,まだ手書きでなんとかなるが,二桁以上になると死ねる.ということで,自動で依存関係を書き出せたらいいなぁと思ってググってたら良いのがヒットした.


http://www.phys.cs.is.nagoya-u.ac.jp/~watanabe/tips/makefile.htmlに,「make dep」で依存関係を書き出せる設定が書いてある.「g++ -MM -MG $(SRC) >makefile.depend」で「makefile.depend」に依存関係を書き出せるらしい.「g++」はコンパイラで,「$(SRC)」は関連する全てのCPPファイル.その結果を「>」を使ってファイルとして「makefile.depend」に書き出しているので,「-MM -MG」というオプションが依存関係を吐き出すための処理らしい.

ファイルを追加するたびにMakefileまで書き直すのはめんどくせ

で,上のサイトには「自動でCPPファイルを探す方法」も書いてあった(すでに「書き方わかんね」の項目でも多少書いたが).「SRC=$(shell ls *.cpp)」とすれば,そのディレクトリにあるCPPファイルを列挙できる.今の自分のやり方だと,CPPファイルは別の階層にあったりするので,「SRC=$(shell find Src -name "*.cpp")」という風にして「Srcというディレクトリ(サブディレクトリ含む)にある全てのCPPファイルを探し出す」ようにしている.


依存関係を吐き出すために,「make dep」してから「make」する必要があるが,「依存関係」の「A」の部分に「dep」を加えれば自動で実行,チェックしてくれるらしい.


これで,ファイルを追加しようが依存関係が変化しようが,Makefileへの変更は不要となる.

Makefileメモ:リファレンス編

自分が参照したいものを上の方にして列挙.

マクロ

$

「影響を与える側のファイル名」


すでに

HOGE.obj: HOGE.cpp

という風に「依存関係」が書かれていて,

.cpp.obj:
	gcc -c $<

という風に「CPPの影響でOBJを生成する場合,"gcc -c $<"を実行する」ということが書かれている場合,「$<」は「HOGE.cpp」に展開される.

$*

「拡張子を除いた名前」

.cpp.obj:
	gcc -o $*.obj -c $<

という風に,「前述のやつ」+「-o $*.obj」で書くと,「HOGE.cpp」から拡張子を除いたもの,つまり「HOGE」が「$*」の部分に展開される.
今みたいに「階層」を作って色んな場所にCPPファイルを作成しているときに,「CPPがあるディレクトリ」に「対応OBJ」を生成する場合に使用する.(「$*」によってディレクトリ名まで展開されるらしい)

一般的な書き方

他のMakefileで良く使われる手法.「書くとき」というより「読むとき」用のリファレンス.

コンパイラ指定

「どのコンパイラを使うか」の指定.他のコンパイラへの変更を容易にするため分離.


「CC」などの名前(「C Compiler」とか「Compiler Collection」とかの短縮?)で使用.

オプション指定

共通オプション.ぶっちゃけ,良くわかってない.ウィンドウを出すタイプか,とかいろいろ指定.


「OPT」「OPTS」などの名前(たぶん「OPTIONS」の短縮)で使用.

インクルードパス指定

「ヘッダファイルはどこにあるか」を指定.色んなライブラリを使う場合,それに対応するヘッダファイルのパスをここで指定.


「INC」「INDS」などの名前(たぶん「INCLUDES」の短縮)で使用.

ライブラリパス指定

「ライブラリはどこにあるか」を指定.色んなライブラリを使う場合,それらのライブラリのパスをここで指定.


「LIB」「LIBS」などの名前(たぶん「LIBRARIES」の短縮)で使用.

ターゲット指定

最終的に生成されるもの.「〜.exe」とか「a.out」とか.


「TARGET」などの名前で使用.

ヘッダファイル用マクロ

全てのヘッダファイル.使ってるのを見かけない.普通は「CPP→関連ヘッダ」なので,使う意味がないのかもしれない.


「HEADER」「HED」などの名前で使用.

ソースファイル用マクロ

全てのソース(ここではCPP)ファイル.「依存関係の計算」に利用したり,「オブジェクトファイルの名前を求める」のに使われたりする模様.


SRC」「SOURCE」などの名前で使用.

オブジェクトファイル用マクロ

全てのオブジェクトファイル(OとかOBJとか).上のソース用マクロから「OBJ=$(SRC:.cc=.o)」とか「OBJ = $(SRC:%.c=%.o)」で,拡張子を変更することで求めるのが一般的っぽい.オブジェクトファイルが一つでも変われば,exeファイルも更新が必要になるので,ターゲットの依存部分として指定されることが多い.


「OBJ」「OBJS」などの名前で使用.

未分類の雑記

・睡眠時間8時間半.眠いよぅ.睡眠時間が「長い→短い→長い」で振動を起こしている.なんとか,どっかに収束させたいところ.


Makefileのメモに関して,先に進むための「思考パターン」と,実際に書くための「参照データ」は別々にした方が良いと判断し,分けて書くことにした.
セルクマしてMakefileタグでひっかかるようにした方が良いかなぁと思いつつ,保留.


Makefileのメモは2時間半程度かかった.店が開くまでの時間つぶしみたいなつもりだったのに.もう昼だよ.腹減ったよ.
でも,「オプションが何かわかってない」みたいに「どこがわかってないかがわかった」ので良しとするか.
改めて読み返すと長いな.


・やっぱりMakefileのサンプルも載っけたいなぁと思いつつも,参考文献のやつのlsをfindにして,ODEのMakefileのオプションとかを持ってきただけだし,別にいいか?そうでもないか?とか考える.


・ケガ:見た感じ,昨日と変わりないが,なんだかケガの下端のカサブタが浮き上がってきてる気がする.


・で,boostを使う.ODEのMakefileの設定をそのまま使うと,「-fno-exceptions -fno-rtti」あたりで困る.具体的には「cannot use typeid with -fno-rtti」とか「undefined reference to 'boost::throw_exception(std::exception const&)'」とか言われる(日本語に訳すと「-fno-rttiを使うなんて許さないんだから!」とか「boost::throw_exception(std::exception const&)を定義してくれないと,わたし...わたし...」あたり).
エラー表示でもっと萌えられればなぁ(上記の通り,あまり上手く脳内変換されないのだ).
こうして,少しずつオプションの意味を理解していくのであった.


・エラーの脳内変換はどの方向が良いのだろう.ベタなところでは「ツンデレ」の「ツン」の部分か.上手くいったときに「デレ」が連想できればさらに良いけれど.
他には「どじっ娘」の「どじ」の部分?「ヤンデレ」は知らない.


・そろそろ「Effective C++」を有効活用できるレベルのようです.もう一度読み直そうかな.


・この「仮コーディングを書き換えるよりも,1から作ったほうが実は早い」というのは何という病気なのだろうか.コードを目の前にすると,上手く設計・書き換えができない.「このプログラムに必要」というのと「このクラスに必要」というのを混同してるのだろうか.
「書き換えのコスト」の問題かな.あるいは「鳥瞰」と「虫瞰」の違いか(虫瞰って最近の造語?).「目の前のコード」にひっぱられて,うまく「鳥瞰」が出来ない感じ.
やっぱり「脳内設計」とか「紙に書く」とかした方が効率的か?平均以上の記憶力があれば「脳内設計」でも十分なのかなぁと思いつつ,「紙に書く」方を選ぶ.


・そうだよね.普通,ビームとレーザーの違いなんて知らないよね.ということをなんか再確認.
とは言え,ビームもまた見てからよけられるかというとビミョーか.
てか,どれくらい速ければビームと呼べるのだろう.


・環境設定に時間がかかり,あんま本体の方は進まんかった.でも今度の3連休で基本的な部分はできあがるか.


・このデザインだと上のMakefileのメモが読みづらいな.変えるか(もう変えるのか).