make - ビルド作業を自動化するツールの使い方

make

makeは、makefileというファイルを作成し、makeコマンドを実行することで自動的にファイルを生成できます。makeは主にプログラムのビルド作業に利用されます。また、この記事で利用しているmakeはGNU makeになります。

makefileでは、生成するファイル(target)と生成するのに前提となるファイル(prerequisites)の関係を記述し、その後に生成のためのコマンド(recipe)を記述します。

makeは、targetのファイルが存在しない場合又は、targetのファイルとprerequisitesのファイルの更新日時を確認し、prerequisitesのファイルの方が新しい場合、targetのファイルを生成します。この機能により、例えば、プログラムをビルドする場合、必要なプログラムのみをビルドできます。

スポンサーリンク

makefileの構文

makefileのルールの基本的な定義

target(ターゲット):生成するファイル

prerequisites(前提条件):ファイルを生成するのに前提となるファイル(依存ファイル)

recipe:ファイル生成で実行するコマンド

recipeの行はすべてタブ文字から始まる必要があります。
行頭に#がある場合、makefileでのコメント行になります。

makeコマンドのオプションについて

makeコマンドのオプションについては別記事で紹介しています。

makeコマンドのオプションの一部のまとめになり、コマンド例とその実行結果を紹介しています。また、ここでのオプションはGNU...

簡単なmakeの例

ここでは、単純なテキストファイルを用いてmakeを利用します。

makefileを作成し、makeコマンドを実行するとカレントディレクトリにあるmakefileを読み取り処理が行われます。

ここでの処理は、依存ファイルprerequisites.txtからターゲットファイルtarget.txtを作成します。この生成は単純にcatコマンドを利用して、prerequisites.txtの内容をtarget.txtへリダイレクトします。
以下はmakefileとprerequisites.txtの内容、そして、コマンド例と実行結果になります。

makefile

prerequisites.txt

コマンド例と実行結果

ここでもう一度、makeコマンドを実行すると以下のようになります。

これは、target.txtのタイムスタンプがprerequisites.txtのタイムスタンプより新しいため何も処理が行われません。また、タイムスタンプは以下のような状態になっています。

ここで、touchコマンドを用いて、prerequistes.txtのタイムスタンプを更新し、prerequisites.txtのタイムスタンプがtarget.txtのタイムスタンプより新しい場合にmakeコマンドを実行すると、makeによってtarget.txtが再生成できます。

makeコマンドを引数なしで実行した場合、一番上のターゲットから始まります。このmakeが処理を始めるターゲットをデフォルトゴール(default goal)といいます。
デフォルトゴール以外のターゲットから始めたい場合は、makeコマンドの引数にターゲットを指定します。

ゴールになっているターゲットの処理が終了したら、makeの処理は終了します。なので、cleanがターゲットになっているルールは処理されません。

makefile

コマンド例と実行結果

makeのターゲットについて

Phony Target(偽のターゲット)

makefileのターゲットはファイル名ではない名前を利用することができます。このようなターゲットはPhony Targetといいます。

Phony Targetは組み込みターゲット名.PHONYを利用します。.PHONYで宣言したPhony Targetは同じ名前のファイルが存在しても実行することができます。

makefile

コマンド例と実行結果

他の組み込みのターゲット名は例えば、GNU makeの場合、GNU make: Special Targetsで確認できます。
また、よく使うPhony Targetは、Arguments to Specify the Goalsにリストされています。

ターゲットの依存ファイルの処理について

makeの処理は基本的にデフォルトゴールから始まります。
ゴールに指定されたターゲットに依存ファイルが存在するとき、makeは基本的にファイルの更新日時のチェックを行います。

しかし、その依存ファイルをターゲットとしたルールが存在する場合は、まずそのルールを処理していきます。

依存ファイルをターゲットとしたルールの処理が終了した後に、依存ファイルと元のターゲットファイルの更新日時をチェックを行いルールを適用します。
また、依存ファイルをターゲットとしてルールの処理を行った結果、そのターゲット(依存ファイル)が作成されなかった場合、元のターゲットのルールのコマンドは実行されます。

以下はmakefileのみを作成し、実行した結果になります。

makefile

コマンド例と実行結果

コマンドにある#はmakeのコメントではなく、シェルのコメント行になります。

Phony Targetを依存ファイルとしている場合、上の例で2回目のmakeを行うとき、依存ファイルをターゲットとしたルールを適用した結果、ターゲットファイルが存在している場合でも、そのターゲットに対するルールのコマンドは実行されていきます(file2.txt : lastの行)。その結果、makeのためのすべてのコマンドが実行されてしまいます。

しかし、ターゲットファイルが存在するときに、ターゲットへのコマンドは実行したくないけれど、依存ファイルのルールは処理したい場合はorder-only prerequisitesが利用できます。order-only prerequisitesは通常のprerequisitesの後に'|'を利用します。

makefile

コマンド例と実行結果

複数のターゲットがあるルール

以下のような複数のターゲットがあるルールは

単純に、

と同じ意味になります。

複数のターゲットがあるルールがデフォルトゴールに設定されると、最初のターゲットがデフォルトゴールとして扱われます。
[email protected]はターゲット名を表すmakeの変数になります。

一つのターゲットに対しての複数のルールの設定について

一つのターゲットに対して、複数のルールを設定することで、依存ファイルを追加することができます。

makefile

コマンド例と実行結果

$^は全ての依存ファイルを列挙するmakeの変数になります。

しかし、ターゲットに対して実行されるコマンド(recipes)は一つになります。複数のルールに対して、それぞれ違うrecipesを設定するとエラーメッセージを表示しています。

makefile

コマンド例と実行結果

ここでは、下に記述している方のコマンドが前のレシピを上書きしています。

複数のルールに対して、それぞれ違うrecipesを設定したい場合は、'::'を使用します。

makefile

コマンド例と実行結果

暗黙的なルール

拡張子によって、処理が決まっている場合などは暗黙的なルール(implicit rules)を設定することで、makefileの記述を簡潔にすることができます。

暗黙的なルールの種類として、パターンルールとサフィックスルールがあります。

パターンルール(pattern rules)

ターゲットや依存ファイルに対して、%の文字を利用することで、パターンルールによる暗黙的なルールを設定できます。

makefile

コマンド例と実行結果

上の例では、.oのファイルを作成するために.cのファイルを作成するような、暗黙的なルールの後に暗黙的なルールを処理する部分があります。これは暗黙的なルールの連鎖(chains of implicit rules)になり、実行結果の最後にmakefileで記述のないファイルの削除が行われます。

削除されたファイルは中間ファイル(intermediate file)といい、この場合では.cのファイルが当てはまります。中間ファイルが削除される条件は、始めに中間ファイルが存在せず、暗黙的なルールの連鎖でmakeがその中間ファイルを作成したときになります。

また、より限定的なパターンルールとして利用できるルールとして、静的パターンルール(static pattern rules)もあります。静的パターンルールは暗黙的なルールではありませんが、特定なファイルに対して、暗黙的なルールを上書きするようなルールを記述したい場合に利用できます。

これは、パターンルールの始めにターゲットを記述し、その記述したターゲットの中でのパターンルールを適用できます。

makefile

コマンド例と実行結果

サフィックスルール(suffix rules)

サフィックスルールは暗黙的なルールの古い形式になります。サフィックスルールには2種類あり、double-suffixとsingle-suffixになります。

また、サフィックスルールを使用するには、組み込みのターゲット名.SUFFIXESに使用する接尾辞を追加する必要があります。

double-suffixは、接尾辞を2つ指定し並べたものになります。例えば、

はパターンルールの

と同じになります。

makefile

コマンド例と実行結果

single-suffixは、接尾辞を1つだけ指定したものになります。例えば、

はパターンルールの

と同じになります。

makefile

コマンド例と実行結果

サフィックスルールについて、makeはデフォルトのサフィックスルールが設定されている場合があります。意図しない接尾辞が処理されないようにするには

のように一度デフォルトの接尾辞を削除した後に、.txtのような必要な接尾辞を設定します。

デフォルトのサフィックスルールを確認するには、makefileが存在しないディレクトで、

で確認できます。これはルールや変数の設定があるmakeのデータベースを確認するオプションになります。
使用するmakefileのmakeのデータベースだけを確認する場合は処理を実行しないdry-runモードの-nオプションをつける

や、またはコマンドを何も表示や実行しないで終了ステータスのみを返す-qオプションをつける

でmakeのデータベースを確認できます。-nオプションや-qオプションがない場合はmakeの処理が実行された後に、makeのデータベースが表示されます。
また、デフォルトのサフィックスルールのドキュメントはGNU makeの場合、Catalogue of Rulesに、デフォルトのサフィックスルールに使用する変数はImplicit Variablesになります。

中間ファイル

中間ファイルを生成したとき、暗黙的なルールの連鎖で作成した中間ファイルを削除したくない場合があります。その場合は、組み込みのターゲット名.SECONDARYを利用すると中間ファイルは削除されません。

makefile

コマンド例と実行結果

逆に中間ファイルとして削除したいファイルは組み込みのターゲット名.INTERMEDIATEが利用できます。

makefile

コマンド例と実行結果

makeでエラーが起きた時やCtrl+Cで処理を終了したときは、生成中のファイルが削除されます。ファイルを削除したくない場合は組み込みのターゲット名.PRECIOUSが利用できます。
また、.SECONDARYではなく、.PRECIOUSを利用しても中間ファイルは残ります。

変数

変数の代入

makeでの変数の主な代入演算子は、'='もしくは、':='(または'::=')があります。また、変数の呼び出しは$(var)や${var}のように呼び出すことができます。

変数に対して、'='を用いた代入は再帰的に変数を展開できます。また、代入演算子の周りにあるスペースは無視されます。

makefile

コマンド例と実行結果

makeでターゲットとコマンドの行を一行で記述したい場合は、

のように';'の記号が利用できます。

また、':='(または':==')の場合はその時点の変数を単純に展開します。

makefile

コマンド例と実行結果

代入演算子を両方利用してみると、違いがさらに分かると思います。

makefile

コマンド例と実行結果

他の代入演算子

変数が設定されていない変数のみに代入したい場合は'?='が利用できます。

makefile

コマンド例と実行結果

シェルでのコマンド結果を代入したい場合は'!='が利用できます。

makefile

コマンド例と実行結果

変数の文字列を追加したい場合は'+='が利用できます。

makefile

コマンド例と実行結果

'+='の演算子を利用した代入

に書き換えることもできます。ただし、最初に=を使用した代入の場合は、変数は再帰的な展開がされる変数として扱われます。

複数行の代入にはdefineが利用できます。defineは、defineのキーワードの後に変数と代入演算子を指定し、そのあとの行に代入したい行を記述し、最後にendefのキーワードで終了します。

makefile

コマンド例と実行結果

make特有の変数

make特有の変数は以下の表になります。主に暗黙的なルールを利用する際に便利な変数になります。

make特有の変数の表

変数意味
[email protected]ターゲットのファイル名
$%アーカイブファイルのメンバー名
$<最初の依存ファイル名
$?ターゲットより新しい全ての依存ファイル
$^全ての依存ファイル(スペース区切り)
ただし、order-only prerequisitesは除く
$+全ての依存ファイル
ただし、繰り返されるファイル名もリスト通り表示
$|全てのorder-only prerequisites(順序付けの依存関係)の名前
$*パターンルールなどで%の文字で
パターンマッチした文字列(stem,ステム)
$(@D)[email protected]でのディレクトリ部分
$(@F)[email protected]でのファイル部分
$(*D)
$(*F)
$*でのディレクトリ部分とファイル部分
$(%D)
$(%F)
$%でのディレクトリ部分とファイル部分
$(<D)
$(<F)
$<でのディレクトリ部分とファイル部分
$(^D)
$(^F)
$^でのディレクトリ部分とファイル部分
$(+D)
$(+F)
$+でのディレクトリ部分とファイル部分
$(?D)
$(?F)
$?でのディレクトリ部分とファイル部分

また、下の例は、上の変数を利用したmakeの使用例になります。

makefile

コマンド例と実行結果

アーカイブファイルを利用していないので$%は空欄になっています。

参考

GNU Make Manual