GNU make
最近では、統合開発環境(IDE - Integrated Development Environment)が発達したことにより、規模の大きいプログラムを開発する場合でも、ソースコード群への変更がビルド生成物(実行イメージなど)に漏れなく確実に反映されることが当たり前となっている。
プログラム開発では、ソースコード修正、ビルド、テストを何度も繰り返し行うことになるが、その際に上記に述べたような機能は非常に重要な要素である。
make 自体は IDE からも利用されており、上記に述べたことを実現しつつビルドを自動化するために利用される。その他に Unit テストやインストールの実行も自動化することが可能である。
作業を自動化するMakefileの記述
make コマンドを実行すると、カレントフォルダに存在する Makefile もしくは makefile の内容が実行される。
例えば、単一の C++ ソースをコンパイルするには、以下のコマンドを実行する。
$ g++ -o hellocpp hellomain.cpp
これをMakefileで記述すると以下のようになる。
- Makefile
hellocpp: hellomain.cpp g++ -o hellocpp hellomain.cpp
※注意: 2行目の先頭はタブを挿入しなければならない。スペースだと次のようなエラーになる。「Makefile:2: *** 分離記号を欠いています. 中止.」
実行例:
$ make g++ -o hellocpp hellomain.cpp
複数ソースファイルから実行ファイルを生成するMakefileの記述
プログラム開発を行う場合は、流用性や保守性を向上させたり管理しやすくするために、ソースコードを機能単位に分割することになる。しかし、ソースコードを分割することによってファイル数は増加し、流用することによってファイル間で依存関係が発生するので、修正されたファイルを漏れなく処理することや依存を考慮したビルド順序が複雑になり管理が困難になってくる。
Makefile では、機能単位にターゲットを整理しつつ、その依存ファイルを列挙することができる。make は変更されたファイルを検出すると、ターゲットの依存関係と処理コマンドの情報を利用して、自動的に最小限の関連ターゲットのみを処理する。
プログラムの規模が大きくなってくると、すべてのソースコードをビルドし直すような方法では、実行ファイルを生成するのに時間が掛かり過ぎる場合がある。C/C++ではソースコードからオブジェクトファイルを生成し、リンカでオブジェクトファイルを結合して実行ファイルとするので、変更されていないソースに関してはオブジェクトファイルの生成をスキップし、前回生成済みのオブジェクトファイルを再利用することによって、リンカによる結合だけを処理すれば良くなるのでビルド時間の大幅削減することができる。
以下に複数ソースファイルから実行ファイルをビルドする Makefile の例を示す。
- Makefile
StockPricesImport: Http.o MySQLDB.o StockPricesImportApp.o g++ -o StockPricesImport Http.o MySQLDB.o StockPricesImportApp.o Http.o: Http.cpp Http.h g++ -c Http.cpp MySQLDB.o: MySQLDB.cpp MySQLDB.h g++ -c MySQLDB.cpp StockPricesImportApp.o: StockPricesImportApp.cpp StockPricesImportApp.h g++ -c StockPricesImportApp.cpp
Makefileの意味は以下の通り。
- StockPricesImport ターゲットは、Http.o、MySQLDB.o、StockPricesImportApp.o の四つのファイルに依存している。
- ターゲットを生成するコマンドは、g++ -o StockPricesImport Http.o MySQLDB.o StockPricesImportApp.o である。
- Http.o ターゲットは、Http.cpp、Http.h の二つのファイルに依存している。
- ターゲットを生成するコマンドは、g++ -c Http.cpp である。
- MySQLDB.o ターゲットは、MySQLDB.cpp、MySQLDB.h の二つのファイルに依存している。
- ターゲットを生成するコマンドは、g++ -c MySQLDB.cpp である。
- StockPricesImportApp.o ターゲットは、StockPricesImportApp.cpp、StockPricesImportApp.h の二つのファイルに依存している。
- ターゲットを生成するコマンドは、g++ -c StockPricesImportApp.cpp である。
初回ビルド時の実行例は以下のようになる。
$ make g++ -c Http.cpp g++ -c MySQLDB.cpp g++ -c StockPricesImportApp.cpp g++ -o StockPricesImport Http.o MySQLDB.o StockPricesImportApp.o
例えば MySQLDB.cpp を修正し make を実行すると、以下のように差分のみの処理を自動的に行う。
- 依存ファイルに変更が無い限りは処理が行われないことを確認できる。
$ make g++ -c MySQLDB.cpp g++ -o StockPricesImport Http.o MySQLDB.o StockPricesImportApp.o
ソースコードに修正がない状態で make を実行した場合は以下のようになる。
$ make make: 'StockPricesImport' は更新済みです
偽りのターゲットを使ったテクニック
準備中…
参考文献
この文章を作成するに当たって、以下の文献を参考にさせて頂きました。原文の著者、翻訳者の方々に感謝いたします。
GNU make 日本語訳(Coop編)
著者: リチャード・M・ストールマン, ローランド・マグラス (GNU プロジェクト)
翻訳者: いのまた みつひろ さん/ecoop.net