これまで,みなさんが作ってきたソフトウェアは概ねひとつのソースプログラムを実行する場合が多かったのではないでしょうか.でも,世の中の多くのソフトウェアは簡単なものでも複数のファイルやデータの組み合わせから構成されます.数千〜数万のファイルやデータから構成される複雑なソフトウェアも珍しくはありません.
また,ひとつのソフトウェアが複数のモジュールやプログラムから構成される場合もあります.
このように,複数のファイルやデータを組合せて,さまざまなプログラムを構成するときに,その組合せの手順書を記述したものが Makefile
です.
Makefile
の書式は基本的には以下のような雛形に沿った記述を列挙したものです.
目的ファイル: 依存ファイル1 依存ファイル2 ...
ビルドコマンド1
ビルドコマンド2
...
この雛形は,
目的ファイル
を作成するには,いくつかのファイル(依存ファイル1
,依存ファイル2
,…)を利用すること,
目的ファイル
を作成する方法は,ビルドコマンド列(ビルドコマンド1
,ビルドコマンド2
,…)を順次実行すること,そして
目的ファイル
の作成に必要な依存ファイル群のうちひとつでも更新されたら,目的ファイル
を作り直すことが指定されています.
一旦,Makefile
を書けば,その使い方は簡単です.単に make
コマンドを実行するだけです.make
コマンドは Makefile
の記述を読み,そこに記述されたファイル群の更新日時を検査して,依存関係に沿って更新が必要なファイルを検出したら,該当するビルドコマンドを実行して,ファイルの更新を試みます.
サンプルプロジェクト lx01
に含まれる Makefile
の内容について見てみましょう.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
まず,わかりやすいところで5-6行目を見てみましょう.ここには,以下のことが記述されています.
simple
という目的ファイルが simple.c
というファイルから構成されること
simple.c
の最終更新日時が simple
の最終更新日時よりも新しいときには,simple
をビルドし直さなくてはいけないこと
simple
をビルドするためには clang -o simple simple.c
というコマンドを実行すること
clang
は C 言語のためのコンパイラの名前
clang
の -o
オプションはコンパイラの生成物のファイル名を指定します.
このコマンドは simple.c
という名前のファイルを読み込み,C 言語のための clang
コンパイラを利用して,simple
という実行可能ファイルを生成するものです.clang
について,もっと詳しい説明についてはclang
のマニュアル (man clang
) を見て下さい.
この記述があるので make simple
というように make
コマンドに目的ファイルの simple
を渡すと,make
は simple
を作成するために必要最小限の作業を実施します.現時点で simple
が存在していなかったり,その最終更新日時が simple.c
より古い場合には make simple
によって,新しい simple
がビルドされます.すでに simple
が存在していて,その最終更新日時が simple.c
の最終更新日時よりも新しい場合には,make simple
は何もしません.
simple
を作成するために simple.c
をプログラムする作業は,(1) テキストエディタ等で simple.c
を修正し,(2) make simple
を実行してビルドし,(3) simple
を実行してテストする作業の繰り返しが普通です.Makefile
を利用することで,複雑なコンパイラのオプションを毎回,記述する手間から解放されます.
8-9 行目には,同じく simple.c
という名前のファイルから simple.s
という別のファイルを作成する方法が記述されています.simple.s
は simple.c
に相当するアセンブリコードのファイルの名前です.
clang
コマンドの -O0
オプションはCコンパイラの最適化を無効化する指定です.
clang
コマンドの -S
オプションはアセンブリコードを出力する指定です.このオプションが与えられるとclang
は.s
という拡張子のファイルにアセンブリコードを出力します.
11-12 行目にはscalac
(Scala言語のコンパイラ)を利用して,Simple.class
という scala の実行可能形式のファイルを simple.scala
からコンパイルする方法が記述されている.
3 行目の記述 all: simple simple.s Simple.class
は依存性だけが列挙されており,ビルドコマンドの記述はない.この記述は all
をビルドするためには,まずは simple
と simple.s
と Simple.class
が必要だと指定している.すでに見たようにこれらのファイルにはビルドの記述があるので,make
は all
のビルドの手始めにこれら三つのファイルを作成しようとする.all
にはビルドコマンドは指定されていないので,三つのファイルができたならば,その時点で all
のビルドは成功したことになる.all
のような記述は,複数の目的ファイルをまとめて作成する規則の記述に便利である.この例の場合 make all
を実行すれば三つのファイルのビルドが実施される.
make
が引数なしで呼ばれた場合は Makefile
のなかの最初の規則を実行することになっている.このため,単に make
を実行することは上の Makefile
の場合は make all
を実行することにあたる.ここまでの規則たちは simple.c
と simple.scala
を元とするビルド規則である.すでに述べたように make
は各ファイルの最終更新日時に応じて必要最小限の作業のみを実施する.このため simple.c
の作業を実施しているときも simple.scala
の作業を実施しているときでも,単に make
コマンドを実行すれば,最後に更新したソースプログラムに依存するファイルのビルドのみが実施される.このことは大きなソフトウェア開発において重要である.なぜなら,数千にも及ぶファイルのなかでたったひとつのファイルが更新されたばかりに,すべてのプログラムの再コンパイルを実施するとしたら,コンパイル作業に膨大な時間を要し,ソフトウェア開発が滞るからだ.1
14-15 行目のclean:
で始まる規則は依存性がない.しかし,clean
という名前のファイルは存在しないので make clean
を実行すれば,そのビルドコマンドは常に実行される.clean
規則のビルドコマンド rm -f simple simple.s *.class
はファイルを削除するrm
コマンドを利用して,5-12行目の規則が生成するファイル群を削除する.それらのファイルが存在しない場合,rm
コマンドはエラーを出力するが-f
オプションによって,エラーメッセージの出力を抑制している.
他人にソフトウェアを受け渡すときは,普通は必要最小限の構成としてプログラムのみを送り,コンパイル生成物を送るべきではない.このような場合,一旦 make clean
するだけで,フォルダの内容をきれいに整えることができるので便利である.
Makefile
はプログラムの実行のためのコマンドを記述する目的でもしばしば利用される.たとえば,23-25行目には,scalac
の生成物である Simple.class
を Java のインタプリタを用いて実行する方法を記述している.このような長いコマンドを正確に記憶することも,それを入力することも困難である.このような複雑な実行文もMakefile
の規則として記述すれば単にmake run-j
を実行するだけで実行することができる.
なお,本ウェブページの作成にあたっても,Markdown形式と呼ばれる簡素な記述から,煩瑣なHTML形式にpandoc
コマンドを利用して変換しているのだが,この思いの外に複雑な作業もMakefile
を用いて管理している.本格的は Makefile
記述の実際を学ぶ一助になれば幸いである.
かつて,ソフトウェアのビルドに長大な時間がかかったときには,たとえ make
を利用してもビルド作業に何十分もかかった.ビルドの合間に剣玉をしていたために当時のIBM東京基礎研の研究員はん剣玉上手だった.↩