ホーム < ゲームつくろー! < IKD備忘録

GCC
メイクファイルをじわーっと作ってみる

(2011. 9. 17)


 GCCでビルドする時にコマンドラインに直接ビルドしたい.cppファイルを指定すると、.cppファイルが増えた時に破綻してしまいます。それを防ぐためにメイクファイルを作ります。ここでは「メイクファイルってなんだべさぁ」という状態から始めたいと思います。



@ メイクファイルって何?

 有無を言わずメイクをまずは体験しましょう。
 Cygwin + GCCならばコマンドライン上で「make」と打ち込むとメイクが走ります。VisualStudioもメイク(nmake.exe)を持っています。場所は例えば「C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\nmake.exe」にあります。DOS窓でこの実行ファイルを直接指定して起動してみましょう:

はい、「MAKEFILEが見つかりません」と出ますね。メイクファイルが無いわけです。では次にメイクファイルを作りましょう。メイクファイルはテキストファイルです。好きなフォルダで良いのですが、説明のためC:/work/program/の下にmakefileという「拡張子の無い」空のテキストファイルを一つ作りましょう。そして次のようにそのファイルを指定します:

「nmake -f c:\work\program\makefile」です。これで実行すると、何も言われずに終わります。取りあえずメイクファイルを認識してくれたわけです。GCCの場合は「make -f /cygdrive/c/work/program/makefile」と指定します。GCCの場合はまだ警告が出てしまいます(ファイルの認識はしています)。

 これでメイクファイルを動かす基盤が出来ました。

 メイクファイルって何する物なのでしょうか?これは「あるファイルの
変更をきっかけにして別のファイルを作る」という仕組みです。それを体験します。

 programフォルダにテキストファイル(test.txt)を作りましょう。空っぽで結構です。そしてメイクファイルに次のようなテキスト文を書きます:

program/makefile
test.o: test.txt
    copy test.txt test.o

メイクファイルを作るにあたり色々注意があります。まず「test.o:」の後にはスペースを一つ入れます。2行目の頭に空白がありますが、これは絶対に「タブ」を入れて下さい!そうしないとメイクが認識できません。

 まず、「test.o:」の部分は「
ターゲット」と呼ばれます。ここには作成したいファイル名が来ます。その後にある「test.txt」はtest.oファイルを作る時にチェックするファイル名です。2つ以上ある場合はスペース区切りで続けます。何をチェックするかというと「test.txtが更新されたか」です。もしtest.txtが更新されていたら、2行目にあるコマンドラインを実行してくれます。上の例ではcopyコマンドを使ってtest.txtファイルからtest.oファイルを作っています。

 実際にこのメイクファイルでメイクしてみましょう。すると1回目は次のように出るはずです:

test.txtファイルが作成されたばかりであるため、test.oターゲットを作るコマンドが有効となり実行されました。結果として同じフォルダ内にtext.oファイルがコピーされて作成されました。では、何も考えずにもう一度メイクしてみましょう:

更新する必要がないと言われました。text.txtを変更していないので、text.oを再度コピーで作る必要がないと判断してくれたんです。素晴らしい!試しにtext.txtを適当に編集して再度メイクを走らせてみて下さい。今度はファイルのコピーが行われましたよね。素晴らしい!

 text.oファイルを作るためにtext.txtが必要、つまり依存している場合に先のようなメイクファイルがあれば間違いが無くなるわけです。さて、ではhoge.oを作るのにtext.oが必要な場合はどうなるでしょう?

 hoge.o(ターゲット)を作るためにはtext.oが必要なので、次のように記述します:

program/makefile
hoge.o: text.o
copy text.o hoge.o

text.o: text.txt
copy text.txt text.o

hoge.oを作るのにはtext.oが必要で(1行目の読み方です)、hoge.oはtext.oをコピーして作ります(2行目の読み方)。そのtext.oを作るにはtext.txtが必要で(3行目)、これもtext.txtをコピーする事で作ります。

 どうでしょうか、こうすると依存関係があるファイルのどこかが変更された時に、それを利用するファイルを再度生成し直す事ができます。では実際に簡単なプログラム構成を作ってメイクファイルを作成してみましょう。



A テストケース

 まず、適当な場所に作業用のフォルダを作りましょう。今はc:\work\program\testというフォルダを作ることにします。次にこのフォルダの下にcodeというコードを納めるフォードを追加します。codeフォルダ内に次の5つのコードファイルを作ります:

code/main.cpp
#include <stdio.h>
#include "myclass.h"

int main() {
    MyClass obj;
    printf("val = %d\n", obj.get());
    return 0;
}
code/myclass.h
#ifndef MYCLASS
#define MYCLASS

#include "hoge.h"

class MyClass {
public:
    int get();
};

#endif

code/myclass.cpp
#include "myclass.h"
#include "hoge.h"

int MyClass::get() {
    return Hoge().get();
}
code/hoge.h
#ifndef HOGE_H
#define HOGE_H

class Hoge {
public:
    int get();
};

#endif
code/hoge.cpp
#include "hoge.h"

int Hoge::get() {
    return 100;
}

これはmain.cpp内のメイン関数内でMyClassクラスのobjオブジェクトを一つ作り、MyClass::getメソッドが返す値をprintfするという単純なコードです。MyClass::getメソッド内ではHogeクラスのオブジェクトを作り、そのgetメソッドを読んでいます。Hoge::getメソッドは100を返しています。

 このコードをVisualStudioのコンパイラを使ってDOS窓からビルドするには次のようにします。VisualStudioのコンパイラは「C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\cl.exe」です。DOS窓を開いてこの実行ファイルの後ろにコンパイルしたい.cppファイルを列挙するだけなのですが、実はそのままだと実行時エラーになってしまいます。VisualStudioのビルド環境を先に構築する必要があります。難しいことは無くて、「C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat」というバッチファイルをDOS窓上で実行しておくだけです。これでビルド環境が整い、どこからでもcl.exeやnmake.exeが呼べるようになります。

 一つの.cppファイルをコンパイルするには、cl.exeの後に「/c」オプションを付け、その後にコンパイルしたい.cppファイルを記述します。例えばmain.cppをコンパイルするには:

とするとtestフォルダに「main.obj」というオブジェクトファイルが出来上がります。オブジェクトファイルは.cppファイルをコンパイルしてできたバイナリコードです。同様にしてmyclass.cpp、hoge.cppもコンパイルすると、testフォルダにはmain.obj、myclass.obj、hoge.objという3つのオブジェクトファイルが出来上がります:

次にこれらのオブジェクトファイル同士をくっつけます。つまり「リンク」です。リンク作業は「link.exe」という実行ファイルが受け持ってくれます。この実行ファイルはcl.exeと同じフォルダにあります。先のビルド環境を整えておけば、どこからでも呼べます。リンクするにはオブジェクトファイルをlink.exeの後に列挙するだけです:

これでtestフォルダに「main.exe」という実行ファイルが出来ました!わ〜い。

 
 
さて、たった3つのファイルでもこれらコンパイルコマンドとリンクコマンドを毎回打つのは大変ですし、毎回全コードをビルドしていては今後.cppが何十と増えていくと時間的な破綻を招くわけで、是非メイクファイルにこれらのコンパイル/リンク作業を管理してもらわなければならないわけです。

 まず.cppの依存関係を整理しましょう。main.cpp内ではMyClassを使っているため、myclass.cppに依存しています。これをメイクファイルで記述すると:

test/makefile
main.obj: code\main.cpp code\myclass.cpp
    cl.exe /c code\main.cpp

となります。main.cppが変更された時はもちろん、myclass.cppが変更された時もmain.cppはコンパイルし直さないといけないので、こうなりますよね。myclass.cppはhoge.cppに依存しているので同じように書けます。hoge.cppは依存ファイルはありませんが、自分自身が変更された場合はコンパイルしなおしてhoge.objを作る必要があります。よって、メイクファイルはこうなるわけです:

test/makefile
main.obj: code\main.cpp code\myclass.cpp
    cl.exe /c code\main.cpp

myclass.obj: code\myclass.obj code\hoge.cpp
    cl.exe /c code\myclass.cpp

hoge.obj: hoge.cpp
    cl.exe /c code\hoge.cpp

これで、.cppのどれかが変更されると、それに依存した他の.cppもコンパイルされオブジェクトファイルが更新されます。リンクはオブジェクトファイルのどれか一つでも更新されたら回さないといけないわけですから、リンクのメイクはこうなります:

test/makefile
main.exe: main.obj myclass.obj hoge.obj
    link.exe main.obj myclass.obj hoge.obj

main.obj: code\main.cpp code\myclass.cpp
    cl.exe /c code\main.cpp

myclass.obj: code\myclass.obj code\hoge.cpp
    cl.exe /c code\myclass.cpp

hoge.obj: hoge.cpp
    cl.exe /c code\hoge.cpp

これが先のコードをビルド(.exeまで作ることです)するためのメイクファイルです!よ〜し、実際に動かしてみましょう。カレントディレクトリをtestにした状態でnmakeです:

おりゃー、出来たぜい!ちゃんとリンカが働きました。では、testフォルダのmain.objを消してnmakeしてみましょう:

ちゃ〜んとmain.cppが再コンパイルされた後にリンカが回りましたよね(^-^)/。えと、んじゃ、myclass.cppの中を何か変更して保存して下さい。そしてnmake!:

最初にmyclass.cppがコンパイルされてmyclass.objを、次にmain.cppがコンパイルされていますね。最後にリンカがちゃんと回ります(.objが更新されているため)。条件に合わせてちゃんと正しい.cppがコンパイルされるわけです。メイク、素晴らしいでしょう!!


B GCCでメイク

 さて、先ほどと全く同じコードを今度はGCCでコンパイルしてみましょう。VisualStudioでのcl.exeはGCCでは「gcc」となります。nmake.exeは「make」、link.exeは「gcc -o」というオプションで作れます。testフォルダにmakefile_gccという新しいメイクファイルを作り、次のように記述してみましょう:

test/makefile
main_gcc.exe: main.o myclass.o hoge.o
    gcc -o main_gcc.exe main.o myclass.o hoge.o

main.o: code/main.cpp
    gcc -c code/main.cpp

myclass.o: code/myclass.cpp
    gcc -c code/myclass.cpp

hoge.o: code/hoge.cpp
    gcc -c code/hoge.cpp

ちょっと異なる所がありますので注意です。まず、GCCのオブジェクトファイルは.objではなく.oになります。また、オプションの付け方は「/c」とスラッシュではなくて「-c」とハイフンになります。もひとつ、パスの表し方ですが「\」は使えません。「/」でパスを区切ります。後はコンパイラとリンカをそれぞれ「gcc」「gcc -o」とすればメイクファイル完成です。

 Cygwinを立ち上げて、testフォルダまで移動しましょう。方法は「cd /cygdrive/c/work/program/test」とコマンドラインを打ち込みます。続いて「make -f makefile_gcc」とメイクファイルを指定します。すると、

とビルドが回り出します!そしてtestフォルダに「main_gcc.exe」という実行ファイルが出来上がりました。

 一つ大きな注意があります。Cygwinを通して作成した実行ファイルを動かすにはCygwin1.dllというDLLを同じフォルダに置いておく必要があります。このDLLはC:\cygwin\binフォルダにあります。このDLLをtestフォルダにコピーしてmain_gcc.exeをDOS窓のコマンドライン上から実行すると、同じように「val = 100」という出力が得られます。わ〜い。


 ということで、これで同じコードをVisualStudioのコンパイラでもGCCのコンパイラでもメイクを通してチェックできるようになりました。ただ、当然のことですが、VisualStudioに特有のコードはたっくさんあります。DirectXなどはその典型ですよね。そういうGCCが知らない部分はコンパイルできません。そう、このどうしてもコンパイルできない部分が「プラットフォーム依存」な部分になるんです。そして、そこは「別cpp」として分離されるわけです(ここでC++踏み込み編その15「プラットフォーム非依存なコードを作る」に繋がるのだ!)。楽しい〜。