この章では、make ユーティリティに関する以下の項目を説明しています。
隠れた依存関係の検査
コマンド依存関係の検査
パターンマッチングの規則
SCCS ファイルの自動取り出し
このバージョンの make ユーティリティは、旧バージョンの make (System V make) 用に記述されたメークファイルを使用して実行できます。ただし、メークファイルが拡張機能に依存している場合には、他のバージョンで使用できないことがあります (旧バージョンの make についての詳細は、付録 A 「System V の make」 を参照してください)。拡張機能および互換性の概要については、「make の拡張機能のまとめ」を参照してください。
make は、オブジェクトファイルおよび実行可能プログラムの生成および保守を簡素化します。make によって、プログラムを一貫してコンパイルすることが容易になります。また、ソースコードの変更の影響を受けないモジュールに対して、不要な再コンパイルを実行しません。make には、コンパイルを簡単にする機能が提供されています。対話式で行わない複雑な処理や繰り返し処理を自動化することができます。また、オブジェクトライブラリの更新および保守、テストの実行、ファイルシステムまたはテープへのファイルのインストールを行うことができます。make を SCCS とともに使用すると、ソースファイルの階層全体の任意のバージョンから、大規模なソフトウェアプロジェクトを構築することができます。
make は、ユーザーが作成したメークファイルというファイルを読み取ります。メークファイルには、構築するファイルとその構築方法が指定されています。ユーザーは、メークファイルを記述してそれをテストした後は、処理の詳細を記憶しておく必要はありません。処理はすべて make が行います。
単純な処理の場合には、ソフトウェアの構築にシェルスクリプトを使用して整合性を保つことができますが、ソフトウェアプロジェクトの構築では多くの場合スクリプトは適していません。プログラムまたはオブジェクトモジュールのうちの 1 つだけが変更されている場合に、単純なスクリプトを使用してすべてのプログラムまたはオブジェクトモジュールをコンパイルするのは効率がよくありません。また、スクリプトを繰り返し編集しているうちに、整合性を保つことができない場合があります。再コンパイルが必要なモジュールだけを再コンパイルするスクリプトを記述することはできますが、それには make を使用する方が適しています。
make を使用すると、構築するファイルとその構築方法を簡潔かつ構造的に記述することができます。make は、依存関係の検査という仕組みを使用して、各モジュールとその派生元のソースファイルまたは中間ファイルを比較します。make は、モジュールが最後に構築された後にこれらの派生元のファイル (依存ファイルと呼びます) が変更されている場合にのみ、モジュールを再構築します。
派生したファイルがソースファイルよりも古いものかどうかを確認するため、make は (既存の) モジュールおよびその依存ファイルが修正された時間を比較します。モジュールがない、あるいはモジュールが依存ファイルよりも古い場合は、make はそのモジュールが最新でないとみなし、再構築に必要なコマンドを発行します。再構築に使用されたコマンドが変更されている場合に、モジュールが最新でないとみなされることがあります。
make は依存関係を完全に検査するため、1 つのソースファイルへの変更が任意の数の中間ファイルまたは処理過程に一貫して反映されます。このため、上位から下位へという階層で処理を指定することができます。
make は、メークファイルを読み取り、実行する必要のある処理を特定し、モジュールを完成させるために必要な処理のみを実行します。構築する各ファイルや実行するそれぞれの処理 (手順) をターゲットと呼びます。メークファイル中に記述するターゲットのエントリには、ターゲットの名前、そのターゲットが依存するターゲットのリスト、ターゲットを構築するためのコマンドのリストが記述されています。
コマンドのリストを規則と呼びます。make は、依存関係を必須ターゲットとして扱い、依存関係を必要に応じて更新してから現在のターゲットを処理します。ターゲットの規則によって生成されたファイルをターゲットファイルと呼びます。ターゲットの名前は、生成されるターゲットファイルの名前と対応付けられています。ターゲットの規則がファイルを生成しない場合もあります。ターゲットの派生元のファイル (ターゲットが依存しているファイルなど) を依存ファイルと呼びます。
ターゲットの規則がそのターゲットの名前のファイルを生成しない場合は、make は規則を実行し、それ以降の make の実行ではターゲットが最新のものであるとみなします。
make は、現在の実行で処理されているファイルに対しては、make だけが変更を行うことを前提としています。make の実行中に他の処理がソースファイルを変更すると、make が生成するファイルが不整合になる場合があります。
メークファイルのターゲットのエントリの基本的な書式は、以下のとおりです。
表 4-1 メークファイルのターゲットのエントリ形式
target . . . : [ dependency . . . ] [ command ] . . . |
最初の行では、ターゲット名のリストの後にコロンを付けます。依存関係がある場合にはコロンの後にそのリストを記述します。複数のターゲットが記述されている場合は、指定された規則を使用して各ターゲットが独立して構築されます。
タブで始まる次の行に示されているように、行はターゲットの規則を示すコマンド行になります。タブの代わりに空白文字を使用することはできません。
# で始まる行は、次の (エスケープされていない) 復帰改行 (NEWLINE) までがコメントとして扱われ、ターゲットのエントリとして処理されません。次にタブと # 以外の文字で始まる空白でない行の直前まで、あるいはファイルの最後までが、ターゲットのエントリとみなされます。
メークファイルは、以下の例のように 1 つのターゲットだけで構成される簡単なものもあります。
表 4-2 簡単なメークファイル
test: ls test touch test |
引数を指定せずに make を実行すると、make はまず makefile という名前のファイルを検索し、見つからない場合は Makefile という名前のファイルを検索します。いずれかの名前のメークファイルが SCCS 管理されている場合、make は SCCS の履歴ファイルを調べ、メークファイルが最新でない場合は、最新のバージョンを抽出します。
次に、make はメークファイル内の最初のターゲットのエントリについて依存関係の検査を開始します。メークファイルがない場合は、構築するターゲットのリストをコマンド行で引数として指定する必要があります。ターゲットの構築中には、make が実行中のコマンドが表示されます。
$ make ls test test not found touch test ls test test
この例では、test というファイルが存在しない (つまり最新の状態でない) ため、make はターゲットのエントリに指定されている規則を実行しています。make をもう一度実行すると、ターゲットは最新になっているというメッセージが表示され、規則は実行されません。
$ make `test' is up to date.
セミコロン (;)、切り替え記号 (<、>、>>、|)、置換記号 (*、?、[]、$、=)、引用符、エスケープ文字、コメント ("、'、`、¥、# など、:) などの、シェルのメタキャラクタがコマンド行に含まれている場合は、make は Bourne シェルを起動してコマンド行を処理します。シェルによるコマンド行の構文解析が必要ない場合は、make はコマンドを直接実行します。
各コマンド行は独立したプロセスまたはシェルによって実行されるため、規則内での行の区切りには重要な意味があります。
以下の規則を例にして説明します。
test: cd /tmp pwd
この規則の実行結果は、以下のようになります。
$ make test cd /tmp pwd /usr/tutorial/waite/arcana/minor/pentangles
一連のコマンドをセミコロンで区切って指定すると、シェルを 1 回呼び出すだけですべてのコマンドが実行されます。
test: cd /tmp ; pwd
また、復帰改行をバックスラッシュ (¥) でエスケープすると、2 行に渡って記述された入力コマンドが、1 行で続けて記述された場合と同等に扱われます。make は、エスケープされた復帰改行を空白文字として処理します。
バックスラッシュは、行の最後に記述する必要があります。バックスラッシュの前には、シェルが処理するためのセミコロンが必要です。
test: cd /tmp ; ¥ pwd
ターゲットに規則が指定されていない場合は、make は暗黙の規則を使用してターゲットを構築します。ターゲットが属するファイルのクラスに対する規則が make によって検出された場合は、暗黙の規則のターゲットエントリで定義されている規則が適用されます。
make は、ユーザーが指定したメークファイルの他に、デフォルトのメークファイル (/usr/share/lib/make/make.rules) も読み取ります。このデフォルトのメークファイルには、多数の暗黙の規則のためのターゲットエントリなどの情報が記述されています。
System V make では、暗黙の規則は make プログラム中に定義 (ハードコード) されています。
暗黙の規則には、接尾辞の規則とパターンマッチングの規則の 2 種類があります。接尾辞の規則は、ある接尾辞を持つファイルを、ベース名は同じで接尾辞が異なる別のファイルから構築するためのコマンドを指定します。パターンマッチングの規則は、ワイルドカードのパターンに一致するターゲットおよび依存関係に基づいて規則を選択します。デフォルトの暗黙の規則は、接尾辞の規則です。
接尾辞の規則を使用すると、メークファイルを記述する必要がなくなる場合があります。たとえば、以下のように make コマンドを実行して、functions.c という 1 つの C ソースファイルから functions.o という名前のオブジェクトファイルを構築することができます。
$ make functions.o cc -c functions.c -o functions.o
makeは、nonesuch.c というソースファイルから nonesuch.o というオブジェクトファイルを構築する場合でも同様に機能します。
functions.c から functions という名前の (接尾辞なし) 実行可能ファイルを構築するには、以下のように make コマンドを実行します。
$ make functions cc -o functions functions.c
.c ファイルから .o という接尾辞のファイルを構築する際の規則を、.c.o 接尾辞の規則と呼びます。.c ファイルから実行可能プログラムを構築する際の規則を、.c 規則と呼びます。デフォルトの接尾辞のすべての規則は表 4-8 を参照してください。
make の開始後、make は最初の依存関係を検査し、ターゲットが検出されるとそのターゲットが処理されます。以下のメークファイルを例として説明します。
batch: a b touch batch b: touch b a: touch a c: echo "you won't see me"
make は、batch というターゲットから処理を開始します。batch には検査されていない依存関係、つまり a と b があるため、make は a と b の依存関係の検査が終了するまで batch を実行しません。
a には依存関係がないため、make は a を処理します。ファイルがない場合は、make はターゲットのエントリの規則を実行します。
$ make touch a ...
次に、make は親のターゲットである batch に戻ります。まだ検査されていない依存関係 b があるため、make は b に移って検査を行います。
b にも依存関係がないため、make はその規則を実行します。
... touch b ...
これで batch のすべての依存関係が検査され (必要であれば) 構築されたため、最後に make は batch を検査します。
make が batch の依存関係のうち少なくとも 1 つを再構築したため、make は batch が最新ではないものとみなして再構築します。つまり make はタイムスタンプを比較する検査を行うので、a または b が、この make の実行では構築されていないが、ディレクトリ内には存在していて batch より新しい場合にも、 batch が再構築されます。
... touch batch
依存関係の検査で検出されないターゲットのエントリは処理されません。c のターゲットエントリがメークファイル中にありますが、batch の依存関係の検査において c のエントリは検出されていないため、c の規則は実行されません。make コマンドの引数として入力すると、c を開始ターゲットとして指定することができます。
次の例では、batch のターゲットはファイルを生成せず、複数のターゲットを 1 つのグループとして示すラベルとして使用されます。
batch: a b c a: a1 a2 touch a b: touch b c: touch c a1: touch a1 a2: touch a2
この場合は、下図のようにターゲットが検査および処理されます。
要約すると、make は以下のような処理を実行します。
batch の依存関係を検査して依存関係を 3 つ検出するので、batch の処理はまだ実行しません。
最初の依存関係である a を検査します。依存関係を 2 つ検出します。同様に、make は次の処理を実行します。
a1 を検査し、必要であればそれを再構築します。
a2 を検査し、必要であればそれを再構築します。
a を再構築するかどうかを決定します。
b を検査し、必要であればそれを再構築します。
c を検査し、必要であれば それを再構築します。
make は、依存関係のツリー構造をすべて処理した後に、最上位のターゲットである batch の検査および処理を行います。batch に規則が含まれている場合は、make はその規則を実行します。この例では、batch には規則が含まれていないため、make は規則を実行せずにその batch を再構築します。batch に依存するすべてのターゲットも再構築されます。
ターゲットに規則が含まれていない場合は、make は暗黙の規則を選択してターゲットを構築します。適切な暗黙の規則を make が検出できず、規則を取り出すための SCCS 履歴がない場合は、make はターゲットに対応するファイルがないものとし、規則が空であるとみなします。
規則が空である依存関係を使用して、ターゲットの規則を強制的に実行することができます。たとえば、FORCE という規則は空のターゲットを定義します。
以下のメークファイルを例として説明します。
haste: FORCE echo "haste makes waste" FORCE:
make は、haste という名前のファイルが最新である場合でも、haste を作成するための規則を実行します。
$ touch haste $ make haste echo "haste makes waste" haste makes waste
make には、特別な機能を実行するための特殊ターゲットが組み込まれています。たとえば、.PRECIOUS という特殊ターゲットは、make が割り込まれたときにライブラリファイルを保存するように make に指示します。
特殊ターゲットには、以下のような特徴があります。
名前がピリオド (.) で始まる
依存関係がない
メークファイルの任意の場所に記述できる
表 4-3 に、特殊ターゲットの一覧を示しています。
ターゲットの名前がコマンド行またはメークファイル中の依存関係リストで指定されており、かつ以下のいずれかに該当する場合には、make は処理を停止してエラーメッセージを表示します。
作業中のディレクトリにファイルがない
ターゲットまたは依存関係のエントリがない
暗黙の規則のファイル名接尾辞の定義に一致しない
SCCS 履歴ファイルがない
.DEFAULT という特殊ターゲット用に指定された規則がない
$ make believe make: Fatal error: Don't know how to make target `believe'.
ただし、-k オプションが有効である場合は、make はエラーが発生したターゲットに依存しない他のターゲットに対して処理を続行します。
メークファイル中で同じ名前のターゲットを複数回記述することができます。次に例を示します。
foo: dep_1 foo: dep_2 foo: touch foo
上記の例は、次の例と同じ結果になります。
foo: dep_1 dep_2 touch foo
ただし通常は、メークファイルをわかりやすくするために、ターゲットは 1 回だけ記述することをお勧めします。
以下の表に示す語は、make によって予約されています。
表 4-3 make の予約語
.BUILT_LAST_MAKE_RUN |
.DEFAULT |
.DERIVED_SRC |
.DONE |
.IGNORE |
.INIT |
.KEEP_STATE |
.MAKE_VERSION |
.NO_PARALLEL |
.PRECIOUS |
.RECURSIVE |
.SCCS_GET |
.SILENT |
.SUFFIXES |
.WAIT |
FORCE |
HOST_ARCH |
HOST_MACH |
KEEP_STATE |
MAKE |
MAKEFLAGS |
MFLAGS |
TARGET_ARCH |
TARGET_MACH |
VERSION_1.0 |
VIRTUAL_ROOT |
VPATH |
規則内のコマンド行の最初に @ を挿入すると、実行中にその行を表示しないようにできます。次に例を示します。
quiet: @echo you only see me once
結果は、以下のようになります。
$ make quiet you only see me once
make の実行時にコマンドを表示しないように指定する場合は、-s オプションを使用します。すべての実行でコマンド行の表示を禁止する場合は、.SILENT という特殊ターゲットをメークファイルに追加します。
.SILENT: quiet: echo you only see me once
特殊な機能を持つターゲットは、名前の最初がドット (.) になっています。ドットで始まるターゲット名は、コマンド行の引数として明示的に指定した場合を除き、開始ターゲットとしては使用されません。コマンドがゼロ以外の終了コードを返した場合は、make は通常はエラーメッセージを出力して停止します。たとえば、次のようなターゲットがある場合を考えます。
rmxyz: rm xyz
この例で、xyz という名前のファイルがない場合は、make は rm が終了ステータスを返した後に停止します。
$ ls xyz xyz not found $ make rmxyz rm xyz rm: xyz: No such file or directory *** Error code 1 make: Fatal error: Command failed for target `rmxyz'
- と @ の両方を組み合わせて記述することも可能です。
コマンドの終了コードに関係なく処理を続行するには、タブの後に記述する最初の文字としてハイフン (-) を使用します。
rmxyz: -rm xyz
この例では、make が受け取った終了コードを示す警告メッセージが表示されます。
$ make rmxyz rm xyz rm: xyz: No such file or directory *** Error code 1 (ignored)
メークファイルをテストしている場合を除き、通常はゼロ以外のエラーコードをすべて無視することは避けてください。
-i オプションを使用すると、make がすべてのエラーコードを無視するように指定できます。ただし、通常はこのオプションは使用しないでください。また、.IGNORE という特殊ターゲットを記述すると、メークファイルの処理時に make が終了コードを無視するように指定できます。このオプションも、通常は使用しないでください。
ターゲットのリストを処理している際にゼロ以外のリターンコードが返されたときに、処理全体を停止するのではなく後に make が次のターゲットを続けて処理するように指定するには、-k オプションを使用します。
依存関係リスト中にソースファイルの名前が指定されている場合は、make はそのソースファイルを他のターゲットと同様に処理します。この場合は、ソースファイルはディレクトリ内にあることが前提になっているため、ソースファイルのエントリをメークファイルに追加する必要はありません。
ターゲットに依存関係がなく、ターゲットがディレクトリ内にある場合は、make はファイルを最新のものとみなします。ただしソースファイルが SCCS 管理されている場合は、make はさらにいくつかの検査を行い、ソースファイルが最新であることを確認します。ファイルがないあるいは履歴ファイルの方が新しい場合は、make は自動的に以下のコマンドを実行して、最新のバージョンを取り出します。
sccs get -s filename -Gfilename
他のバージョンの make では、SCCS ファイルの自動取り出しは特定の暗黙の規則だけで使用できる機能です。また旧バージョンとは異なり、このバージョンの make は履歴ファイルを sccs ディレクトリ内でのみ検索します。作業中のディレクトリ内の履歴ファイルは無視されます。
ただしソースファイルが書き込み可能な場合は、make は新しいバージョンを取り出しません。
$ ls SCCS/* SCCS/s.functions.c $ rm -f functions.c $ make functions sccs get -s functions.c -Gfunctions.c cc -o functions functions.c
make は、取り出したバージョンのタイムスタンプと履歴ファイルのタイムスタンプを調べます。ディレクトリ内のバージョンが最後にチェックインされた最新バージョンかどうかは調べません。したがって、他のユーザーが日付を指定してファイルを取り出した (sccs get -c) 場合でも、make はそれが最新のバージョンであるかどうかを調べないので、最新でないバージョンのプログラムまたはオブジェクトファイルが構築されることがあります。確実に最新バージョンを構築するには、sccs get SCCS または sccs clean のいずれかのコマンドを実行してから make を実行します。
SCCS ファイルの取り出しコマンドは、デフォルトのメークファイル (/usr/share/lib/make/make.rule) の .SCCS_GET という特殊ターゲット用の規則で指定されています。SCCS ファイルの自動取り出しを抑止するには、メークファイルでこのターゲットのエントリに空の規則を指定します。
# Suppress sccs retrieval. .SCCS_GET:
make のマクロ置換は、メークファイル内でパラメータをコマンド行に渡す際に便利です。cc -O オプションを使用して、program という最適化されたプログラムを、コンパイルする場合を例に挙げます。メークファイルに以下の例のようなマクロ参照を functions のターゲットに追加して、パラメータをコマンド行に渡すことができます。
functions: functions.c cc $(CFLAGS) -o functions functions.c
マクロ参照は、メークファイル内あるいは make コマンドの引数でユーザーが定義した値に対応する可変部分として機能します。make で CFLAGS マクロの値を定義すると、make はマクロ参照を指定された値に置換します。
$ rm functions $ make functions "CFLAGS= -O" cc -O -o functions functions.c
CFLAGS マクロへの参照は、.c と .c.o の両方の暗黙の規則に含まれています。
コマンド行で指定できる引数は 1 つだけです。この例では、二重引用符で囲んである部分が引数定義を示しています。
マクロが未定義の場合は、make はマクロの参照先を空白の文字列とみなします。
メークファイル中にマクロを定義することもできます。デフォルトで最適化されたオブジェクトコードを make が生成するように、CFLAGS を -O に設定するなどの用途があります。
CFLAGS= -O functions: functions.c cc $(CFLAGS) -o functions functions.c
make のコマンド行の引数として指定されたマクロ定義は、条件付きマクロ定義を除き、メークファイル内の他の定義よりも優先されます。
たとえば、dbx または dbxtool を使用してデバッグするために functions をコンパイルするには、コマンド行で CFLAGS の値を -g として定義します。
$ rm functions $ make CFLAGS=-g cc -g -o functions functions.c
gprof を使用してプロファイル用に functions をコンパイルするには、-O および -pg の両方を CFLAGS の値として指定します。
マクロ参照は、マクロ名が 2 文字以上の長さの場合は、括弧で囲む必要があります。マクロ名が 1 文字だけの場合は、括弧を省略できます。括弧の代わりに中括弧 { と } を使用することもできます。たとえば、'$X'、'$(X)'、'${X}' はすべて同じ意味です。
通常の依存関係の検査に加えて、特殊ターゲットの .KEEP_STATE を使用してコマンドの依存関係を検査することができます。この検査を実行すると、make は各ターゲットファイルと依存ファイルとを検査するだけでなく、規則中の各コマンド行と、ターゲットを最後に構築したときに実行したコマンド行の比較も行います。比較の結果は、現在のディレクトリの .make.state ファイルに保存されます (「状態ファイル」を参照してください)。
以下のメークファイルを例として説明します。
CFLAGS= -O .KEEP_STATE: functions: functions.c cc -o functions functions.c
以下に、上記のメークファイルで make コマンドを実行した結果を示します。
$ make cc -O -o functions functions.c $ make CFLAGS=-g cc -g -o functions functions.c $ make "CFLAGS= -O -pg" cc -O -pg -o functions functions.c
プログラムのコンパイル時には、ユーザーが指定したオプションが優先されて使用されます。
.KEEP_STATE を有効にしてから最初に make を実行したときには、必要な情報を収集して記録するため、すべてのターゲットが再コンパイルされます。KEEP_STATE 変数は、環境変数から取り込んだ場合には、.KEEP_STATE ターゲットと同様に機能します。
指定したコマンド行のコマンドの依存関係の検査を抑止するには、タブの後の最初の文字として疑問符を挿入します。
動的なマクロである $? を含む行では、コマンドの依存関係の検査が自動的に抑止されます。このマクロは、現在のターゲットよりも新しい依存関係のリストです。このリストの内容は、make の実行ごとに異なる場合があります。
このマクロを含む行に対してコマンドの依存関係の検査を強制的に実行するには、そのコマンド行の (タブの後) の最初の文字として、! を挿入します。
.KEEP_STATE が有効な場合は、make は .make.state という名前の状態ファイルを現在のディレクトリに生成します。このファイルには、.KEEP_STATE が有効になっている間に処理されたすべてのターゲットおよびそのターゲットを構築する規則が、メークファイルの書式で記述されます。この状態ファイルの整合性を保つために、.KEEP_STATE をメークファイルに追加した後は、内容を変更しないでください。
このターゲットは旧バージョンの make では無視されるため、互換性の問題は生じません。このターゲットは、依存するターゲットがなく、規則が空 (NULL) で、それ自身の依存関係がないので、旧バージョンでは余分なターゲットとして処理されます。名前の先頭にドットがあるため、開始ターゲットして使用されません。
ヘッダーを挿入するための #include 指令が C のソースファイルに記述されている場合は、ターゲットはヘッダーファイルとソースファイルの両方に依存します。このようなヘッダーファイルは、コンパイルのコマンド行ではソースとして明示的に表示される場合があるため、これらの関係を隠れた依存関係と呼びます。.KEEP_STATE が有効の場合は、さまざまなコンパイラおよびコンパイルのプリプロセッサは、どの隠れた依存ファイルがどのターゲットに対応しているかを make に通知します。
.KEEP_STATE は、この情報を状態ファイルの依存関係のリストに追加します。以降の実行では、これらの追加された依存関係は通常の依存関係と同様に処理されます。この機能により、各ターゲットの隠れた依存関係のリストが常に正確で最新の状態で自動的に保持されます。また、完全な依存関係のリストを生成するために、旧バージョンのメークファイルでのように複雑なスキームを使用する必要がなくなります。
隠れた依存関係を持つターゲットを make が最初に処理する際には、状態ファイルにはまだそれらの記録がないため、若干不便な場合があります。ヘッダーがなく、make がその記録を持っていない場合は、ターゲットのコンパイル前に SCCS からヘッダーファイルを取り出す必要があることを make は認識できません。
SCCS の履歴ファイルがある場合でも、依存関係のリストや状態ファイルに履歴ファイルはまだ含まれていないため、現在のバージョンは取り出されません。C のプリプロセッサがヘッダーを挿入する際にはヘッダーが見つからないでのコンパイルは失敗します。
hidden.h というヘッダーを挿入する #include 指令が functions.c に追加されて、それ以降に何らかの理由で make を実行する前にファイル hidden.h が削除された場合は、make の実行結果は以下のようになります。
$ rm -f hidden.h $ make functions cc -O -o functions functions.c functions.c: 2: Can't find include file hidden.h make: Fatal error: Command failed for target `functions'
これを回避するには、新しいヘッダーが存在することを確認してから make を実行してください。またコンパイルが失敗する場合でも、SCCS がヘッダーを管理していれば、SCCS から手動でヘッダーを取り出すことができます。
$ sccs get hidden.h 1.1 10 lines $ make functions cc -O -o functions functions.c
2 回目以降に make を実行する際には隠れた依存関係として状態ファイルに記述されているため、make は構築を実行またはヘッダーを取り出すことができます。
隠れた依存関係の検査では、$?
マクロは隠れた依存関係の依存ファイル名をインクルードします。このため、$?
を使用する既存のメークファイルで予期しない動作が生じる場合があります。
前述のどちらの方法でも、他の (インクルード) ディレクトリの状態が一定でないために、ローカルディレクトリで最初に実行する make が失敗する場合があるという問題があります。このため、最初に行う構築をだれかが監視する必要があります。これを回避するには、.INIT ターゲットを使用して、既知の隠れた依存ファイルを SCCS から取り出します。.INIT は、make の実行開始時に依存関係とともに構築される特殊ターゲットです。hidden.h が必ず存在するようにするには、以下の行をメークファイルに追加します。
.INIT: hidden.h
-n オプションを使用して make を実行すると、make が実行するコマンドを実際には実行せずに表示だけを行います。このオプションは、メークファイルのマクロが意図しているとおりに展開されているかどうかを確認する際に便利です。以下のメークファイルを例に示します。
CFLAGS= -O .KEEP_STATE: functions: main.o data.o $(LINK.c) -o functions main.o data.o
make -n を実行すると、以下のように表示されます。
$ make -n cc -O -c main.c cc -O -c data.c cc -O -o functions main.o data.o
ただし、1 つ例外があります。MAKE
マクロへの参照 ($(MAKE)
や ${MAKE}
など) を含むコマンド行は、make に -n オプションを付けた場合にも実行されます。以下のような行をメークファイルに記述することは避けてください。
$(MAKE) ; rm -f *
MAKEFLAGS
という環境変数を設定すると、その値が make オプションのリストに追加されるため、処理が複雑になる場合があります。混乱を防ぐために、この変数を設定することは避けてください。
make には、make の動作やその理由を確認できるオプションが他にもいくつかあります。
ターゲットが最新でないと make が決定する条件を表示します。-n オプションとは異なり、以下の例のようにターゲットの処理は実行します。このオプションは、環境変数 (デフォルトでは NULL) から取り込んだ MAKEFLAGS
マクロの値も表示します。MAKEFLAGS
マクロについての詳細は、後述の節で説明しています。
$ make -d MAKEFLAGS value: Building main.o using suffix rule for .c.o because it is out of date relative to main.c cc -O -c main.c Building functions because it is out of date relative to main.o Building data.o using suffix rule for .c.o because it is out of date relative to data.c cc -O -c data.c Building functions because it is out of date relative to data.o cc -O -o functions main.o data.o
make が検査する、すべての依存関係 (隠れた依存関係も含む) を詳細に表示します。
メークファイルのテキストをメークファイル読み取り時に表示します。
メークファイル、デフォルトのメークファイル、状態ファイル、実行中の make における隠れた依存関係を表示します。
make が (makefile または Makefile ではなく) makefile に指定した名前のメークファイルを使用します。
複数のメークファイルを指定するには、-f オプションをその数だけ指定します。指定したメークファイルは連結されて処理されます。
makestatefile がディレクトリの場合は、make はそのディレクトリ内の .make.state ファイルに KEEP_STATE の情報を書き込みます。makestatefile がファイルの場合は、make は KEEP_STATE 情報を makestatefile に書き込みます。
すべてのマクロ定義およびターゲットのエントリを表示します。
デフォルトのターゲットまたは指定したターゲットのすべての依存関係のツリーを表示します。
-t オプションを使用すると、make の処理を省略できます。-t を使用して make を実行すると、make はターゲットを構築する規則を実行しません。その代わりに touch を使用して、依存関係の検査で検出した各ターゲットの変更時間を更新します。また、構築したターゲットに応じて状態ファイルを更新します。ターゲットのエントリに対応するファイルがない場合は、touch によってファイルが作成されます。
副作用が生じる場合があるため、make の -t (touch) オプションは通常は使用しないでください。
make -t を使用しない例を以下に示します。make によって作成されたターゲットファイルを削除してディレクトリ内を掃除する clean というターゲットがあるとします。
clean: rm functions main.o data.o
clean は、派生ファイルを削除するターゲットを示す慣用名です。clean は、新しく最初から構築を行う場合に便利です。
ここで、以下のような無意味なコマンドを実行したとします。
$ make -t clean touch clean $ make clean `clean' is up to date
この場合、ディレクトリ内を掃除するターゲットを再度機能させるには、clean というファイルを削除する必要があります。
ターゲットファイルが最新かどうかに応じて、ゼロまたはゼロ以外の終了ステータスを返します。
デフォルトのメークファイル (/usr/share/lib/make/make.rules) の読み取りを抑止します。
ゼロ以外の終了ステータスをコマンドが返したときに、処理を停止して -K オプションの効果を取り消します。
前述の例では、明示的なターゲットのエントリと暗黙の規則を使用して簡単な 1 つのソースファイルから C プログラムをコンパイルする方法を説明しました。ただし、ほとんどの C プログラムは、複数のソースファイルからコンパイルされます。また、多くのプログラムでは、標準のシステムライブラリやユーザー定義のライブラリのルーチンがインクルードされています。
cc コマンドを 1 回実行して 1 つのプログラムを再コンパイルおよびリンクする方が簡単な場合がありますが、通常は、複数のソースファイルからプログラムを何回かに分けて段階的にコンパイルする方が便利です。各ソースファイルを別々のオブジェクト (.o) ファイルにコンパイルしてから、それらのオブジェクトファイルをリンクして実行可能ファイル (a.out) を作成します。この方法では、より多くのディスク容量が必要ですが、後で (繰り返して) 再コンパイルを行う際に、ソースが変更されたオブジェクトファイルだけに対してコンパイルを実行すればよいため、コンパイルの時間を節約することができます。
単純ですが実用的なメークファイルの例を以下に示します。
表 4-4 C ソースのコンパイル用のメークファイル (すべて明示的)
この例では、make は main.o および data.o というオブジェクトファイルと、functions という実行可能ファイルを生成します。
$ make cc -o functions main.o data.o cc -O -c main.c cc -O -c data.c
次の例は、前述の例と機能は同一ですが、コンパイルコマンドの代わりに make の定義済みマクロを使用しています。定義済みマクロを使用すると、コンパイル環境が変更されたときに、メークファイルを編集する必要がありません。また、コマンド行からコンパイラのオプションを指定するための CFLAGS
マクロ (およびその他の FLAGS
マクロ) を使用できます。定義済みマクロは、make の暗黙の規則でも頻繁に使用されます。後述のメークファイルで使用されている定義済みマクロを次に示します。これらの定義済みマクロ [このバージョンの make では定義済みマクロが、旧バージョンよりも頻繁に使用されています。ここで示す定義済みマクロの一部は、旧バージョンでは使用できません。] は、C プログラムのコンパイル全般で便利です。
cc コマンド行。以下のように CC、CFLAGS、CPPFLAGS と -c オプションで構成されます。
COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -c
マクロの名前の COMPILE
という部分は、マクロがコンパイルの (オブジェクト、つまり .o ファイルを作成する) コマンド行を表すマクロであることを示しています。.c という接尾辞は、コマンド行が .c (C ソース) ファイルに適用されることを示すニーモニックです。
名前が FLAGS という文字列で終わるマクロは、関連するコンパイラコマンドのマクロにオプションを引き渡します。これらのマクロは、整合性と移植性を保つために使用します。また、メークファイル中にこれらのマクロに適したデフォルト値を記述しておくと便利です。すべての定義済みマクロのリストを、表 4-10 に示しています。
COMPILE.c などのオブジェクトファイルをリンクするための基本的な cc コマンド行です。-c オプションは使用せず、LDFLAGS
マクロへの参照を使用します。
LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
値は cc です (この値は、別の C コンパイラのパス名に再定義できます)。
cc コマンドのオプションです。デフォルトでは空です。
cpp のオプションです。デフォルトでは空です。
リンカー ld のオプションです。デフォルトでは空です。
main.o および data.o を .c ファイルからコンパイルするコマンド行は、このバージョンでは接尾辞 .c.o の規則と機能的に同一であるため、ターゲットのエントリが重複します。make は、そのようなコマンド行がメークファイルに記述されているかどうかに関係なく、同じようにコンパイルを実行します。次のメークファイルは、.c.o の規則によって各オブジェクトファイルをコンパイルし、各オブジェクト用のコマンド行を省略しています。
表 4-6 接尾辞の規則を使用して C ソースをコンパイルするメークファイル
# 接尾辞の規則を使用して 2 つの C ソースから # 1 つのプログラムをコンパイルするメークファイル CFLAGS= -O .KEEP_STATE: functions: main.o data.o $(LINK.c) -o functions main.o data.o clean: rm functions main.o data.o |
接尾辞のすべての規則は、表 4-8 を参照してください。
make が main.o および data.o という依存関係を処理する際に、これらの依存関係のターゲットのエントリがありません。make は、適切な暗黙の規則を調べて適用します。この場合は、接尾辞 .c.o の規則が選択され、名前が同じで接尾辞が .c の依存ファイルから .o ファイルが構築されます。
make は、接尾辞リスト中に記述されている順番に従って使用する依存ファイルおよび接尾辞の規則を決定します。たとえば、main.c と main.s の両方のファイルがディレクトリにある場合は、接尾辞のリストにおいて .c が .s よりも前に記述されているため、make は .c.o の規則を使用します。
まず、make は接尾辞リストにターゲットファイルの接尾辞が含まれているかどうかを調べます。main.o の場合は .o がリストに含まれています。次に、make はターゲットファイルを構築するための接尾辞の規則と、元になる依存ファイルを調べます。依存ファイルの名前は、ターゲットの名前と接尾辞を除いて同一です。この場合は、make は .c.o の規則を調べて main.c という名前の依存ファイルを検出するため、.c.o の規則を使用します。
接尾辞リストは、.SUFFIXES という名前の特殊ターゲットです。SUFFIXES
マクロの定義にはさまざまな接尾辞が含まれています。.SUFFIXES は、このマクロに依存するように指定されています。
SUFFIXES= .o .c .c‾ .cc .cc‾ .C .C‾ .y .y‾ .l .l‾ .s .s‾ .sh .sh‾ .S .S‾ .ln ¥ .h .h‾ .f .f‾ .F .F‾ .mod .mod‾ .sym .def .def‾ .p .p‾ .r .r‾ ¥ .cps .cps‾ .Y .Y‾ .L .L‾ .SUFFIXES: $(SUFFIXES) |
以下の例は、すべての実行可能プログラムをコンパイルするメークファイルです。各プログラムには、ソースファイルが 1 つずつあります。各実行可能ファイルは、ベース名が同じで接尾辞が .c のソースファイルから構築されます。たとえば、demo_1 は demo_1.c から構築されます。
clean と同様に、all はターゲットの慣用名です。all は、依存関係リストに含まれるすべてのターゲットを構築します。通常は、make と make all は同一の処理を行います。
# 1 組の C プログラムをコンパイルするメークファイル。 # 1 つのソース (.c ファイル) から 1 つのプログラムを生成。 CFLAGS= -O .KEEP_STATE: all: demo_1 demo_2 demo_3 demo_4 demo_5
この場合は、いずれのターゲットにも接尾辞がありません。そのため、make は各ターゲットに空の (NULL) 接尾辞が付いているとして処理します。次に、make は接尾辞の規則と、有効な接尾辞の付いた依存ファイルを検索します。demo_2 の場合は、demo_2.c という名前のファイルを検索します。接尾辞 .c の規則のターゲットエントリと、それに対応する .c ファイルがあるため、make は接尾辞 .c の規則を使用して demo_2.c から demo_2 を構築します。
空の接尾辞のターゲットに明示的な依存関係があるときは、あいまいさを避けるために、make は接尾辞の規則を使用したターゲットの構築は行いません。
program: zap zap:
このメークファイルを使用して make を実行しても、何も出力されません。
$ make program $
複数の依存ファイルからターゲットを構築する際には、構築のための規則が含まれている明示的なターゲットを make で指定する必要があります。1 つの依存ファイルからターゲットを構築する際には、多くの場合は暗黙の規則を使用すると便利です。
前述の例で示すように、make は簡単に 1 つのソースファイルを対応するオブジェクトファイルまたは実行可能ファイルにコンパイルします。ただし、複数のオブジェクトファイルをリンクして実行可能ファイルを作成するための情報は make には組み込まれていません。また、make は依存関係の検査で検出したオブジェクトファイルだけをコンパイルします。make は、開始点、つまりリスト内の各オブジェクトファイル (最終的には各ソースファイル) が依存するターゲットを必要とします。
したがって、複数の依存ファイルからターゲットを構築するには、照合順と、ターゲットの依存ファイルを示す依存関係リストを提供する明示的な規則が必要です。各依存ファイルが 1 つのソースから構築される場合は、暗黙の規則を使用できます。
make は、複数のマクロをターゲット単位で動的に管理します。動的なマクロは、特に暗黙の規則の定義において頻繁に使用されます。マクロの意味を理解することは重要です。
動的なマクロは、メークファイルで明示的に定義されていないため、$ 記号を接頭辞として追加して (つまり、マクロ参照であることを示して) 記述します。
動的なマクロは、以下のとおりです。
現在のターゲットの名前です。
ターゲットよりも新しい依存関係のリストです。
make が暗黙の規則を使用するときの依存ファイル名を示します。
現在のターゲットのベース名 (接尾辞を除いたターゲット名) です。
処理されるライブラリメンバーの名前を示します。詳細は、「オブジェクトライブラリの構築」を参照してください。
暗黙の規則は、ターゲット名または依存ファイル名を規則内のコマンド行に指定するために、これらの動的なマクロを使用します。以下の .c.o の規則を例として示します。
.c.o: $(COMPILE.c) $< $(OUTPUT_OPTION)
$< は、現在のターゲットの依存ファイル (この場合は .c ファイル) の名前に置換されます。
OUTPUT_OPTION
というマクロは、デフォルトでは値が空です。このマクロは、CFLAGS
と同様の機能を持っていますが、引数を -o コンパイラオプションに渡してコンパイラの出力先ファイル名を指定するための独立したマクロとして提供されています。
この .c の規則では、$@ は現在のターゲット名に置換されます。
.c: $(LINK.c) $< -o $@
$< および $* のいずれのマクロも、接尾辞のリストでの順序に応じて値が異なるため、明示的なターゲットのエントリでこれらのマクロを使用すると予期しない結果になる場合があります。関連するファイル名からファイル名を正確に取得する方法については、「マクロ参照での接尾辞の置換」を参照してください。
F および D を参照に含めることによって動的なマクロを修飾できます。処理されるターゲットがパス名で指定されている場合は、$(@F) はファイル名部分、$(@D) はディレクトリ部分をそれぞれ示します。ターゲット名に / という文字が含まれていない場合は、$(@D) にドット (.) が値として割り当てられます。たとえば、/tmp/test という名前のターゲットでは、$(@D) の値は /tmp、$(@F) の値は test になります。
動的なマクロは、すべてのどのようなターゲットを処理する際にも割り当てられます。マクロは、ターゲットの規則ではそのまま、依存関係リストでは参照の先頭に $ を追加して使用できます。$$ で始まる参照を、マクロへの遅延参照と呼びます。以下に例を示します。
x.o y.o z.o: $$@.BAK cp $@.BAK $@
このエントリは、x.o.BAK、y.o.BAK、z.o.BAK からそれぞれ x.o、y.o、z.o を取得するために使用できます。
遅延マクロ参照を使用できるのは、make が、メークファイル全体を最初に読み込む際と、ターゲットの依存関係を処理する際の合計 2 回依存関係リストを読み込むためです。make は、リストを読み込むたびにマクロ展開を実行します。動的なマクロは、マクロへの参照が 2 回目の読み取りまで遅延される場合を除き、最初の読み取りではまだ定義されていないため、空の文字列に展開されます。
$$ という文字列は、定義済みマクロの '$
' への参照です。このマクロは値が '$' になっています。make が最初の読み取りでこのマクロを展開する際には、$$@ は $@ に展開されます。依存関係の検査では、その $@
というマクロ参照に動的に値が割り当てられるときに、make は参照をその値に展開します。
make は、最初の読み取りではターゲットのターゲット名部分だけを評価します。ターゲット名として遅延マクロ参照を使用すると、正しい結果が得られません。
NONE= none all: $(NONE) $$(NONE): @: this target's name isn't `none'
このメークファイルは、以下のような結果になります。
$ make make: Fatal error: Don't know how to make target `none'
make は、規則の実行時にそのコマンドの適用ごとに 1 回だけ、ターゲットエントリの規則部分を評価します。この場合も、make への遅延マクロ参照を使用すると誤った結果になります。
接尾辞の規則では、複数の規則の関係から自動的にそれらの中間の規則を生成するということはありません。たとえば、接尾辞が .X というファイルから接尾辞が .Y というファイルを構築する接尾辞の規則と、接尾辞が .Y というファイルから接尾辞が .Z というファイルを構築する規則がある場合に、make は .X ファイルから .Z ファイルを作成するという規則を、上記 2 つの規則から生成するということは行いません。中間の処理をターゲットとして指定する必要があります。中間の処理のエントリには空の規則を使用できます。
trans.Z: trans.Y:
この例では、trans.Y が存在していれば trans.Z はそのファイルから構築されます。trans.Y がターゲットエントリとして記述されていない場合は、使用する依存ファイルがないために make が "don't know how to build" というエラーを表示して失敗する場合があります。trans.Y がターゲットエントリとして記述されている場合は、trans.Y が最新でないまたは存在しないときに、make が trans.Y を構築します。メークファイルで規則が指定されていないため、make は適切な暗黙の規則を使用します。この場合は .X.Y の規則が使用されます。trans.X が存在する (または SCCS から取り出すことができる) 場合に、make は trans.Y および trans.Z の両方を必要に応じて再構築します。
make には多数の便利な接尾辞の規則が提供されていますが、ユーザー定義の規則を追加することもできます。ただし、暗黙の規則を追加する場合は、パターンマッチングの規則が適しています (「パターンマッチングの規則: 接尾辞の規則の代替」を参照してください)。この節では、暗黙の規則をメークファイルに追加する従来の方法を説明しています。旧バージョンの make と互換性がある暗黙の規則を記述する必要がない場合をは、この節をとばして次の節に進んでも構いません。
接尾辞の規則は、2 段階の手順で行います。まず、ターゲットと依存関係の両方のファイルの接尾辞を、.SUFFIXES という特殊ターゲットの依存関係として指定し、接尾辞のリストに追加します。依存関係リストには指定した内容が蓄積されていくため、以下のようにこのターゲットのエントリを追加すると、リストに接尾辞が追加されます。
.SUFFIXES: .ms .tr
次に、接尾辞の規則を指定するターゲットエントリを追加します。
ms.tr: troff -t -ms $< > $@
これらのエントリを記述したメークファイルを使用して、ms というマクロ (.ms ファイル) が含まれている文書ソースファイルを troff 出力ファイル (.tr ファイル) にフォーマットできます。
$ make doc.tr troff -t -ms doc.ms > doc.tr
接尾辞リストのエントリは、SUFFIXES
マクロに指定されています。接尾辞をリストの最初に挿入するには、まず依存関係のない .SUFFIXES ターゲットエントリを指定して、その値を消去します。依存関係リストには蓄積されていきますが、この場合は例外的に値が上書きされます。以下のように依存関係および規則のないターゲットエントリを指定することによって、既存の定義を消去することができます。
.SUFFIXES
次に、以下のようにもう 1 つ別のエントリに新しい接尾辞、その後に SUFFIXES マクロへの参照を続けて記述して、接尾辞を追加することができます。
.SUFFIXES: .SUFFIXES: .ms .tr $(SUFFIXES)
パターンマッチングの規則は、機能的には暗黙の規則と似ています。パターンマッチングの規則では、ターゲットと依存関係のファイルの関係を、接頭辞 (パス名を含む) と接尾辞のいずれかまたは両方を基準にして指定できるため、暗黙の規則よりも記述が簡単で強力です。パターンマッチングの規則は、次の形式で記述するターゲットエントリです。
tp%ts: dp%ds rule
ここで、tp および ts は、ターゲットの名前の接頭辞および接尾辞 (省略可能) です。dp および ds は、依存関係の名前の接頭辞および接尾辞 (省略可能) です。% は、両方に共通するベース名を示すワイルドカードです。
make は、接尾辞の規則の前にパターンマッチングの規則を調べます。このため、標準の暗黙の規則を無効にすることもできますが、通常は無効にしないでください。
ターゲットを構築する規則がない場合は、make はパターンマッチングの規則を検索してから、接尾辞の規則を調べます。使用可能であれば、パターンマッチングの規則が使用されます。
パターンマッチングの規則のターゲットエントリに規則が含まれていない場合は、make は、規則のない明示的なターゲットエントリを持つものとして、ターゲットファイルを処理します。したがって、make は接尾辞の規則を検索し、SCCS からターゲットファイルのバージョンを取り出し、空の規則を持つものとして最終的にターゲットを処理します (ターゲットは、その実行においては更新されている (最新である) とみなされます)。
troff ソースファイルを troff 出力ファイルにフォーマットするパターンマッチングの規則は、以下のとおりです。
%.tr: %.ms troff -t -ms $< > $@
以下の表に、デフォルトのメークファイル (/usr/share/lib/make/make.rules) に記述されている標準の接尾辞の規則と定義済みマクロを示します。
表 4-8 標準の接尾辞の規則
用途 |
接尾辞規則の名前 |
コマンド行 |
---|---|---|
アセンブリファイル | .s.o | $(COMPILE.s) -o $@ $< |
.s | $(COMPILE.s) -o $@ $< | |
.s.a | $(COMPILE.s) -o $% $< | |
$(AR) $(ARFLAGS) $@ $% | ||
$(RM) $% | ||
.S.o | $(COMPILE.S) -o $@ $< | |
.S.a | $(COMPILE.S) -o $% $ | |
$(AR) $(ARFLAGS) $@ $% | ||
$(RM) $% | ||
C ファイル (.c の規則) | .c | $(LINK.c) -o $@ $< $(LDLIBS) |
.c.ln | $(LINT.c) $(OUTPUT_OPTION) -i $< | |
.c.o | $(COMPILE.c) $(OUTPUT_OPTION) $< | |
.c.a | $(COMPILE.c) -o $% $< | |
$(AR) $(ARFLAGS) $@ $% | ||
$(RM) $% | ||
C++ ファイル | .cc | $(LINK.cc) -o $@ $< $(LDLIBS) |
.cc.o | $(COMPILE.cc) $(OUTPUT_OPTION) $< | |
.cc.a | $(COMPILE.cc) -o $% $< | |
$(AR) $(ARFLAGS) $@ $% | ||
$(RM) $% | ||
C++ ファイル (SVR4 書式) | .C | $(LINK.C) -o $@ $< $(LDFLAGS) $*.c |
.C.o | $(COMPILE.C) $< | |
.C.a | $(COMPILE.C) $< | |
$(AR) $(ARFLAGS) $@ $*.o | ||
$(RM) -f $*.o | ||
FORTRAN 77 ファイル | .cc.o | $(LINK.f) -o $@ $< $(LDLIBS) |
.cc.a | $(COMPILE.f) $(OUTPUT_OPTION) $< | |
$(COMPILE.f) -o $% $< | ||
$(AR) $(ARFLAGS) $@ $% | ||
| $(RM) $% | |
.F | $(LINK.F) -o $@ $< $(LDLIBS) | |
.F.o | $(COMPILE.F) $(OUTPUT_OPTION) $< | |
.F.a | $(COMPILE.F) -o $% $< | |
$(AR) $(ARFLAGS) $@ $% | ||
$(RM) $% |
表 4-9 標準の接尾辞の規則
用途 |
接尾辞規則の名前 |
コマンド行 |
---|---|---|
lex ファイル | .l | $(RM) $*.c |
$(LEX.l) $< > $*.c | ||
$(LINK.c) -o $@ $*.c $(LDLIBS) | ||
$(RM) $*.c | ||
.l.c | $(RM) $@ | |
$(LEX.l) $< > $@ | ||
.l.ln | $(RM) $*.c | |
$(LEX.l) $< > $*.c | ||
$(LINT.c) -o $@ -i $*.c | ||
$(RM) $*.c | ||
.l.o | $(RM) $*.c | |
$(LEX.l) $< > $*.c | ||
$(COMPILE.c) -o $@ $*.c | ||
$(RM) $*.c | ||
.L.C | $(LEX) $(LFLAGS) $< | |
.L.o | $(LEX)(LFLAGS) $< | |
$(COMPILE.C) lex.yy.c | ||
.L.o | rm -f lex.yy.c | |
mv lex.yy.o $@ | ||
Modula 2 ファイル | .mod | $(COMPILE.mod) -o $@ -e $@ $< |
.mod.o | $(COMPILE.mod) -o $@ $< | |
.def.sym | $(COMPILE.def) -o $@ $< | |
NeWS | .cps.h | $(CPS) $(CPSFLAGS) $*.cps |
Pascal ファイル | .p | $(LINK.p) -o $@ $< $(LDLIBS) |
.p.o | $(COMPILE.p) $(OUTPUT_OPTION) $< | |
Ratfor ファイル | .r | $(LINK.r) -o $@ $< $(LDLIBS) |
.r.o | $(COMPILE.r) $(OUTPUT_OPTION) $< | |
.r.a | $(COMPILE.r) -o $% $< | |
$(AR) $(ARFLAGS) $@ $% | ||
$(RM) $% | ||
シェルスクリプト | .sh | $(RM) $@ |
cat $< >$@ | ||
chmod +x $@ | ||
yacc ファイル (.yc の規則) | .y | $(YACC.y) $< |
$(LINK.c) -o $@ y.tab.c $(LDLIBS) | ||
$(RM) y.tab.c | ||
.y.c | $(YACC.y) $< | |
mv y.tab.c $@ | ||
.y.ln | $(YACC.y) $< | |
$(LINT.c) -o $@ -i y.tab.c | ||
$(RM) y.tab.c | ||
.y.o | $(YACC.y) $< | |
$(COMPILE.c) -o $@ y.tab.c | ||
$(RM) y.tab.c | ||
yacc ファイル (SVR4) | .Y.C | $(YACC) $(YFLAGS) $< |
mv y.tab.c $@ | ||
.Y.o | $(YACC) $(YFLAGS) $< | |
$(COMPILE.c) y.tab.c | ||
rm -f y.tab.c | ||
mv y.tab.o $@ |
表 4-10 定義済みの動的マクロ
用途 |
マクロ |
デフォルト |
---|---|---|
ライブラリアーカイブ | AR | ar |
ARFLAGS | rv | |
アセンブラのコマンド | AS | as |
ASFLAGS | ||
COMPILE.s | $(AS) $(ASFLAGS) | |
COMPILE.S | $(CC) $(ASFLAGS) $(CPPFLAGS) -target -c | |
C コンパイラのコマンド | CC | cc |
CFLAGS | ||
CPPFLAGS | ||
COMPILE.c | $(CC) $(CFLAGS) $(CPPFLAGS) -c | |
LINK.c | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) | |
C++ コンパイラのコマンド1 | CCC | CC |
CCFLAGS | ||
COMPILE.cc | $(CCC) $(CCFLAGS) $(CPPFLAGS) -c | |
LINK.cc | $(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) | |
C++ SVR4 コンパイラのコマンド (SVR4) | (C++C) | CC |
(C++FLAGS) | -O | |
COMPILE.C | $(C++C) $(C++FLAGS) $(CPPFLAGS) -c | |
LINK.C | $(C++C) $(C++FLAGS) $(CPPFLAGS) $(LDFLAGS) -target | |
FORTRAN 77 コンパイラのコマンド | FC in SVr4 | f77 |
FFLAGS | ||
COMPILE.f | $(FC) $(FFLAGS) -c | |
LINK.f | $(FC) $(FFLAGS) $(LDFLAGS) | |
COMPILE.F | $(FC) $(FFLAGS) $(CPPFLAGS) -c | |
LINK.F | $(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) | |
リンカーのコマンド | LD | ld |
LDFLAGS | ||
lex のコマンド | LEX | lex |
LFLAGS | ||
LEX.l | $(LEX) $(LFLAGS) -t | |
lint のコマンド | LINT | lint |
LINTFLAGS | ||
LINT.c | $(LINT) $(LINTFLAGS) $(CPPFLAGS) | |
Modula 2 のコマンド | M2C | m2c |
M2FLAGS | ||
MODFLAGS | ||
DEFFLAGS | ||
COMPILE.def | $(M2C) $(M2FLAGS) $(DEFFLAGS) | |
COMPILE.mod | $(M2C) $(M2FLAGS) $(MODFLAGS) | |
NeWS | CPS | cps |
CPSFLAGS | ||
Pascal コンパイラのコマンド | PC | pc |
PFLAGS | ||
COMPILE.p | $(PC) $(PFLAGS) $(CPPFLAGS) -c | |
LINK.p | $(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) | |
Ratfor のコンパイルコマンド | RFLAGS | |
COMPILE.r | $(FC) $(FFLAGS) $(RFLAGS) -c | |
LINK.r | $(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) | |
rm のコマンド | RM | rm -f |
yacc のコマンド | YACC | yacc |
YFLAGS | ||
YACC.y | $(YACC) $(YFLAGS) | |
接尾辞のリスト | SUFFIXES | .o .c .c‾ .cc .cc‾ .C .C‾ .y .y‾ .l .l‾ .s .s‾ .sh .sh‾ .S .S‾ .ln .h .h‾ .f .f‾ .F .F‾ .mod .mod‾ .sym .def .def‾ .p .p‾ .r .r‾ .cps .cps‾ .Y .Y‾ .L .L‾ |
SCCS get コマンド | .SCCS_GET | sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ -G$@ |
SCCSGETFLAGS | -s |
下位互換性を持たせるため、C++ のマクロには上記の代わりに使用できる形式があります。C++C は CCC で、C++FLAGS は CCFLAGS で、COMPILE.C は COMPILE.cc で、LINK.cc は LINK.C で、それぞれ代用できます。これらの代用の形式は、将来のリリースでは削除されます。
オブジェクトライブラリは、ar ライブラリアーカイブ に含まれている複数のオブジェクトファイルの集まりです (ライブラリアーカイブファイルについての詳細は、ar(1) および lorder(1) のマニュアルページを参照してください)。多くの言語では、C のライブラリにあるような汎用ユーティリティのコンパイル済み関数をオブジェクトライブラリとして保存します。
ar は、1 つまたは複数のファイルを読み込んでライブラリを作成します。ライブラリに含める各メンバーには、ヘッダーと 1 つのファイルのテキストが含まれます。メンバーのヘッダーには、ファイルディレクトリのエントリから取得した、変更時間などの情報が含まれます。これによって、make は依存関係の検査で、ライブラリのメンバーを独立した別々の構成要素として扱うことができます。
オブジェクトライブラリの関数を使用するプログラムを (ファイル名または cc の -l オプションにより適切なライブラリを指定して) コンパイルすると、リンカーは必要なシンボルを含むライブラリのメンバーを選択してリンクします。
ar を使用して、オブジェクトファイルのライブラリ用のシンボルテーブルを生成できます。ld がライブラリ内のシンボルへアクセスできるように、つまり関数が定義されたオブジェクトファイルを ld が検出してリンクできるようにするには、このシンボルテーブルが必要です。また、先に lorder と tsort を使用して、メンバーを呼び出し順にライブラリ内でソートすることもできます (詳細は、ar(1) および lorder(1) を参照してください)。大規模なライブラリでは、これら両方の処理を行なってください。
make は、以下の形式で記述されたターゲットまたは依存関係を、ライブラリのメンバーへの参照または複数のメンバーを空白文字で区切ったリストへの参照として認識します。
lib.a (member . . . )
旧バージョンの make は、上記の書式を認識しますが括弧内の最初のメンバーだけが処理されます。
このバージョンの make では、括弧内に指定したすべてのメンバーが処理されます。たとえば次に示すターゲットエントリは、librpn.a というライブラリが、stacks.o および fifos.o というメンバーから構築されることを示しています。またパターンマッチングの規則は、各メンバーは対応するオブジェクトファイルに依存していて、オブジェクトファイルは対応するソースファイルから暗黙の規則を使用して構築されることを示しています。
librpn.a: librpn.a (stacks.o fifos.o) ar rv $@ $? $@ librpn.a (%.o): %.o @true
ライブラリのメンバーを記述する際には、動的マクロの $?
は対応するメンバーよりも新しいファイルのリストを示します。
$ make cc -c stacks.c cc -c fifos.c ar rv librpn.a stacks.o fifos.o a - stacks.o a - fifos.o
$%
動的マクロ $%
は、特にライブラリの指定に使用します。ライブラリのメンバーがターゲットの場合は、メンバーの名前が $%
マクロに割り当てられます。たとえば、libx.a(demo.o) というターゲットが指定されている場合は、$%
の値は demo.o になります。
通常は、ターゲットの処理中に make に割り込みが発生すると、ターゲットファイルが削除されます。これは、変更時間が最新になった不完全なファイルがディレクトリに残らないようにするため、1 つずつ独立したライブラリファイルの場合に適しています。しかし、複数のメンバーで構成されるライブラリの場合は、最新でないメンバーがあってもライブラリファイルを削除せずにそのままにしておくことをお勧めします。以降に make を実行すると、前回の実行で処理が中断されたオブジェクトファイルまたはメンバーから処理が継続されるため、特に大規模なライブラリの場合は、ライブラリファイルを削除しないでおくことをお勧めします。処理時間を節約することができます。
.PRECIOUS という特殊ターゲットは、割り込み時の削除から保護するファイルを指定するために使用します。make は、このターゲットの依存関係として指定されたターゲットは削除しません。
.PRECIOUS: librpn.a
たとえば、この行を前述のメークファイルに追加して make を実行すると、librpn.a の処理に割り込んだ場合は、ライブラリは削除されません。
これまでの節では、簡単なプログラムをコンパイルして簡単なライブラリを構築する際に make を利用する方法を説明しました。この節では、複雑なプログラムおよびライブラリを管理する make のより高度な機能について説明します。
マクロ定義は、メークファイルの任意の行に記述できます。マクロは、長いターゲットリストや式を短縮するために、あるいは繰り返し記述する必要のある長い文字列の代替として使用できます。
また、マクロを使用して、オブジェクトファイルのリストをソースファイルのリストから取得できます。マクロ名は、メークファイルの読み取り時に割り当てられます。マクロ参照の値は、最後に割り当てられた値によって決定されます。
マクロの評価は、実際には上述よりも複雑に行われます。「暗黙の規則と動的なマクロ」を参照してください。
条件付きマクロおよび動的マクロを除き、make はマクロの値をマクロの定義順に割り当てます。
マクロ参照は、他のマクロの参照箇所に組み込むことができます (旧バージョンの make では使用できません)。
$(CPPFLAGS$(TARGET_ARCH))
代入文の += は、指定された文字列をマクロの既存の値の後に追加します。
この場合は、マクロは内側から外側に展開されます。以下の定義例では、make は Sun-4 システム用の正しいシンボルを定義します。
CPPFLAGS-sun4 = -DSUN4 CPPFLAGS += $(CPPFLAGS-$(TARGET_ARCH))
make には、参照されるマクロの値に含まれる特定の接尾辞を置換する機能があります。慣例として、接尾辞はドット (.) で始まりますが、任意の文字列を接尾辞として指定することもできます。
$(macro:old-suffix=new-suffix)
上記の形式は、接尾辞を置換するマクロ参照の書式です。このような参照を使用することによって、以下のようにオブジェクトファイルのリストをソースファイルのリストで表わすことができます。
OBJECTS= $(SOURCES:.c=.o)
この例では、make は値に含まれる接尾辞 .c をすべて .o に置換します。指定された接尾辞が付いていない語に対しては置換は適用されません。
SOURCES= main.c data.c moon OBJECTS= $(SOURCES:.c=.o) all: @echo $(OBJECTS)
例えば、上記のメークファイルで make を実行すると次のような結果になります。
$ make main.o data.o moon
C プログラムをより容易にデバッグおよび管理するには、lint ツールを使用します。lint は、他のアーキテクチャへの移植性がない C の構造体の検査も行います。
lint は、移植性のある C プログラムを記述する際に非常に便利です。C プログラムの検査を行う lint は、発見および追跡するのが難しいバグを防ぐためのツールです。初期化されていないポインタ、関数呼び出しにおける引数の数の不一致、移植不可能な C の構造体の使用、などのバグがないかどうかを検査します。clean と同様に、lint はターゲットの慣用名です。C プログラムを構築するメークファイル中に、lint を含めることをお勧めします。lint は、cpp および lint の最初の (構文解析) 処理過程で処理された出力ファイルを生成します。出力ファイルは、接尾辞が .ln となります。また、接尾辞の置換によって、ソースファイルのリストから lint の出力ファイルを生成することもできます (旧バージョンの lint では不可能な場合があります)。
LINTFILES= $(SOURCES:.c=.ln)
lint のターゲットエントリは、以下のようになります。
lint: $(LINTFILES) $(LINT.c) $(LINTFILES) $(LINTFILES): $(LINT.c) $@ -i
各 .ln ファイルは、対応する .c ファイルから構築するという暗黙の規則があるため、.ln ファイルに対するターゲットエントリは必要ありません。ソースが変更されると、以下のように make を実行するときに .ln ファイルが更新されます。
make lint
LINT.c
という定義済みマクロに lint のオプション指定として LINTFLAGS
マクロへの参照が含まれていますが、通常は特に指定しなくてよいでしょう。lint では cpp を使用するため、コンパイルのプリプロセッサオプション (-I など) に対して、通常は CFLAGS ではなく CPPFLAGS を使用してください。また、LINT.c
マクロには CFLAGS
への参照は含まれていません。
make clean を実行すると、このターゲットによって生成された .ln ファイルが削除されるようにしたいと考えられます。clean ターゲットには、そのようなマクロ参照を簡単に追加することができます。
次の例は、画面上でのカーソル動作を制御する curses および termlib というライブラリパッケージを使用したプログラムをコンパイルするメークファイルです。
表 4-11 システムが提供するライブラリを使用した C プログラム用のメークファイル
リンカーは、未定義のシンボルを検出するとそれを解決するため、通常は、リンクするファイルのリストの最後に、ライブラリの参照を記述してください。
このメークファイルの実行結果は、以下のとおりです。
$ make cc -O -c main.c cc -O -c data.c cc -O -o functions main.o data.o -lcurses -ltermlib
デバッグ用およびプロファイル用のプログラムは同じソースコードを使用して生成しますが、構築の際に異なる C コンパイラオプションを使用します。デバッグ用のオブジェクトコードを生成するには、cc の -g オプションを使用します。プロファイル用には、-O および -pg という cc オプションを使用します。
コンパイル手順は同じなので、make のコマンド行で CFLAGS
の定義を指定できます。このコマンド行での定義は、メークファイルでの定義を無効にします。また、.KEEP_STATE により、変更の影響を受けるすべてのコマンド行が実行されます。以下に例を示します。
$ make "CFLAGS= -O -pg" cc -O -pg -c main.c cc -O -pg -c data.c cc -O -pg -o functions main.o data.o -lcurses -ltermlib
これらのオプションを暗記したり、このような複雑なコマンドを入力するのは煩雑です。その代わりに、これらの情報はメークファイルに記述できます。デバッグ用またはプロファイル用のコードの生成方法をメークファイルに記述し、make に伝える必要があります。1 つの方法として、debug および profile という名前の 2 つのターゲットエントリと、コマンド行へのそれぞれのコンパイルオプションを、メークファイルに追加します。
よりよい方法として、開始ターゲットに応じて CFLAGS
の定義を変更する規則を指定する、debug および profile というターゲットエントリを追加します。次に、各ターゲットが既存のターゲットに依存するようにすることによって、make は既存のターゲットの規則と、指定されたオプションを使用することができます。
以下に例を示します。
make "CFLAGS= -g"
前述のようにメークファイルを記述すると、上記のように make を実行する代わりに、以下のようにしてデバッグ用のコードをコンパイルすることができるようになります。
make debug
ここで、ターゲット (およびその依存関係) ごとに異なる方法でマクロを定義することを、どのように make に指示するかが問題になります。
条件付きマクロ定義は、以下のような形式で記述します。
target-list := macro = value
make が target-list に指定された名前のターゲットおよびその依存関係を処理する際に、指定されたマクロに指定された値を割り当てます。
target-list 中のそれぞれの語は、% のパターンを 1 つずつ含めることができます。定義が適用されるターゲットを make が特定する必要があるため、条件付きマクロ定義を使用してターゲット名を変更することはできません。
以下の行は、デバッグ用およびプロファイル用の各プログラムコードを処理するための適切な値を CFLAGS
に割り当てます。
debug := CFLAGS= -g profile := CFLAGS= -pg -O
条件付きマクロへの参照を依存関係リスト中で使用する際には、遅延参照を使用する (先頭に $ を 1 つ追加する) 必要があります 。遅延参照を使用しないと、正しい値が割り当てられる前に make が参照を展開してしまいます。make は、このような正しくない (可能性のある) 参照を検出すると警告を表示します。
以下のメークファイルは、指定したターゲットに応じて、最適化された C プログラム、デバッグ用の C プログラム、プロファイル用の C プログラムのいずれかの形式の C プログラム (デフォルトでは最適化されたプログラム) をコンパイルします。コマンドの依存関係が検査されるので、上記 3 つのうちで形式を切り換えると、プログラムおよびそのオブジェクトファイルが再コンパイルされます。
表 4-12 デバッグ形式またはプロファイル形式の C プログラムを生成するメークファイル
最初のターゲットエントリは、all によって 3 つのターゲットを指定しています。
通常最終的な完成プログラムには、デバッグ形式およびプロファイル形式のコードが含まれないようにします。
慣例として、メークファイルの最初のターゲットとして、代替の開始ターゲット (またはターゲットのリストを処理するターゲット) とともに all を記述します。all の依存関係は、種類を問わず、最終的に構築されるすべてのターゲットになります。この場合は、最終的には最適化されたプログラムが構築されます。ターゲットエントリは、debug および profile が functions ($(PROGRAM) の値) に依存することも示しています。
次の 2 行は、CFLAGS
の条件付きマクロ定義です。
次に、functions のターゲットエントリが記述されています。debug が functions に依存している場合は、-g オプションを使用してコンパイルされます。
次の例では、同様の手法を C のオブジェクトライブラリの管理に使用しています。
表 4-13 デバッグ形式またはプロファイル形式の C ライブラリを生成するメークファイル
前述の 2 つの例は、開発、デバッグ、プロファイルを別々に実行する場合には適しています。しかし、形式を切り替えるごとにすべてのオブジェクトファイルが再コンパイルされるので、コンパイル時間が長くなるという欠点があります。以下の 2 つの例は、3 つの形式すべてを独立した構成要素として別々に管理する方法を説明しています。
3 つの形式のオブジェクトファイルが同一のディレクトリにあることによる混乱を避けるため、デバッグ用およびプロファイル用のオブジェクトファイルと実行可能ファイルをサブディレクトリに保存できます。ただし、オブジェクトファイルのリストの各エントリに、サブディレクトリ名を接頭辞として追加する必要があります。
パターン置換マクロ参照は、形式および機能が接尾辞置換の参照と同様です。パターン置換参照を使用して、接頭辞、接尾辞のいずれかまたは両方を、マクロの値中で一致する語に追加または置換できます。
パターン置換マクロ参照は、パターンマッチングの規則と同様に、旧バージョンの make で使用することはできません。
パターン置換参照は、以下の形式で記述します。
$(macro:p%s =np%ns)
ここで、p は置換対象の既存の接頭辞 (ある場合)、s は置換対象の既存の接尾辞 (ある場合)、np および ns はそれぞれ新しい接頭辞および接尾辞、% はワイルドカードです。パターン置換は、値 p%s に一致するすべての語に適用されます。
SOURCES= old_main.c old_data.c moon OBJECTS= $(SOURCES:old_%.c=new_%.o) all: @echo $(OBJECTS)
この例は、以下のような結果になります。
$ make new_main.o new_data.o moon
= 記号の右側 (置換後の語) では、ワイルドカードの % を任意の数だけ必要に応じて使用できます。以下に例を示します。
... OBJECTS= $(SOURCES:old_%.c=%/%.o)
この置換は、以下のような結果になります。
main/main.o data/data.o moon
ただし、パターン置換マクロ参照は、パターンマッチングの規則を指定しているターゲットエントリの依存関係を示す行では使用しないでください。使用した場合は、マクロとターゲット (または依存関係) のどちらにワイルドカードが適用されるかを make が特定できないため、衝突が生じます。
OBJECT= .o x: x.Z: @echo correct %: %$(OBJECT:%o=%Z)
このメークファイルは、make が x.Z から x を構築するために記述したものです。しかし、依存関係の行に複数含まれている % のうちどれをパターンマッチングの規則で使用するかを make が特定できないため、パターンマッチングの規則は認識されません。
次に示すのは、複数の形式が別々に管理されている C プログラム用のメークファイルの例です。まず .INIT という特殊ターゲットが、debug_dir および profile_dir というサブディレクトリを (まだ作成されていなければ) 作成します。これらのサブディレクトリには、デバッグ用およびプロファイル用のオブジェクトファイルと実行可能ファイルが含まれます。
make は、メークファイルが読み込まれた後に .INIT ターゲットの規則を実行します。
実行可能ファイルは、VARIANTS.o
マクロで指定されているオブジェクトファイルに依存します。このマクロにはデフォルトでは OBJECTS の値が指定され、後に条件付きマクロ定義によって値が再度割り当てられます。このとき、debug_dir/ または profile_dir/ の接頭辞が追加されます。サブディレクトリ内の実行可能ファイルは、同じサブディレクトリ内に構築されるオブジェクトファイルに依存します。
次に、両方のサブディレクトリに含まれているオブジェクトファイルが作業中のディレクトリにあるソース (.c) ファイルに依存するように、パターンマッチングの規則が追加されます。これは、1 つのソースファイル群から 3 つの形式すべてを構築し管理するために必要です。
最後に、サブディレクトリの debug_dir と profile_dir は一時的に作成されたものであるため、clean ターゲットが更新されてこれらのサブディレクトリとその内容が再帰的に削除されます。これは、各形式用のサブディレクトリは一時的なものであるため、派生ファイルはそのソースと同一のディレクトリ内に構築するという慣例に従っています。
表 4-14 デバッグ用およびプロファイル用のプログラムを別々に扱うためのメークファイル
複数の形式があるライブラリ用のメークファイルも、同様の方法で変更します。
# 複数の形式のライブラリを別々に扱うためのメークファイル CFLAGS= -O SOURCES= main.c rest.c LIBRARY= lib.a LSOURCES= fnc.c OBJECTS= $(SOURCES:%.c=$(VARIANT)/%.o) VLIBRARY= $(LIBRARY:%.a=$(VARIANT)/%.a) LOBJECTS= $(LSOURCES:%.c=$(VARIANT)/%.o) VARIANT= . program profile debug: $$(OBJECTS) $$(VLIBRARY) $(LINK.c) -o $(VARIANT)/$@ $< lib.a debug_dir/lib.a profile_dir/lib.a: $$(LOBJECTS) ar rv $@ $? $$(VLIBRARY)($$(VARIANT)%.o): $$(VARIANT)%.o @true profile := VARIANT = profile_dir profile := CFLAGS = -O -pg debug := VARIANT = debug_dir debug := CFLAGS = -g .KEEP_STATE: profile_dir debug_dir: test -d $@ || mkdir $@ $$(VARIANT)/%.o: %.c $(COMPILE.c) $< -o $@
複数の形式を管理するこの手法は、便利ですがやや複雑です。説明を簡潔にするため、この手法は以下の例では省略しています。
ヘッダーのインクルードディレクトリを管理するためのメークファイルは、非常に簡単です。ヘッダーファイルはテキストファイルなので、記述する必要があるのは all というターゲットだけです。この all ターゲットで、ヘッダーを依存関係として指定します。その他の処理は、SCCS の自動取り出しによって行われます。ヘッダーのリストの代わりにマクロを使用すると、他のターゲットエントリで同一のリストを使用できます。
# インクルードディレクトリを管理するためのメークファイル FILES.h= calc.h map.h draw.h all: $(FILES.h) clean: rm -f $(FILES.h)
ユーザー定義のライブラリパッケージを作成する際には、各ライブラリを、ライブラリのヘッダーおよびライブラリを使用するプログラムとは別の構成要素として扱ってください。プログラム、ライブラリ、ヘッダーをそれぞれ別のディレクトリに置くと、各モジュール用のメークファイルの作成が簡単になります。また、ソフトウェアプロジェクトの構造が明確になります。
make を実行した後に、ファイルシステム上のいろいろな場所にファイルが生成されないようにしてください。
メークファイルは、作業中のディレクトリまたは一時的なサブディレクトリ内のファイルだけを構築するように記述する必要があります。make を使用して、何らかの理由で特定のファイルシステム上のディレクトリに意図的にファイルをインストールする場合を除き、他のディレクトリにファイルを作成するメークファイルは作成しない方がよいでしょう。
他のディレクトリにあるライブラリに依存するプログラムを構築する場合には、メークファイル中で修正が必要な点があります。これまでの例では、すべての必要なファイルは、同一のディレクトリ中か、または基本的には変更されない標準ディレクトリ中にあります。ただし、開発中のプロジェクトの一部であるユーザー定義のライブラリについては、場所が変更されることも考えられます。
これらのライブラリは自動的に構築されない (隠れた依存関係の検査に相当するものがライブラリにはない) ため、ライブラリのターゲットエントリを指定する必要があります。また、リンクするライブラリが最新のものであることを確認する必要があります。
また、ローカルディレクトリ内でのみファイルを管理するようにメークファイルを記述する必要があります。さらに、メークファイル中には、別のメークファイルにある内容と重複する情報を記述しないようにする必要があります。
以上の問題を解決するには、ライブラリがあるディレクトリで make コマンドを入れ子にして実行し、(そのディレクトリにあるメークファイル中のターゲットエントリに従って) ライブラリを構築します。
デフォルトでは "make" という値に設定されている MAKE
マクロは、make コマンドの -n オプションを無効にします。つまり MAKE
マクロを参照しているコマンドは、-n オプションが指定されている場合でも実行されます。ただし MAKE
マクロは make コマンドを呼び出すためにのみ使用され、このマクロによって呼び出された make は、特殊マクロ MAKEFLAGS
から -n オプションを継承します。入れ子 (階層構造) になった make はそれぞれ MAKEFLAGS
マクロによって -n オプションが指定されていることを認識していきます。このため、-n オプションを使用することで、入れ子になった make の動作を実際に実行せずに確認することができます。
# 他のディレクトリに生成されるターゲット用の最初のエントリ ../lib/libpkg.a: cd ../lib ; $(MAKE) libpkg.a
ライブラリは、現在のディレクトリからの相対パス名で指定します。プロジェクトが新しいルートディレクトリまたはマシンに移動された場合に、新しいルートディレクトリに対するディレクトリ構造がそのまま同じであれば、すべてのターゲットエントリが正しいファイルを示します。
入れ子にした make のコマンド行では、定義済みマクロ MAKE
の場合と同様に、動的マクロの修飾子の F および D が便利です。処理されるターゲットがパス名で指定されている場合は、$(@F) はファイル名部分、$(@D) はディレクトリ部分をそれぞれ示します。ターゲット名に / という文字が含まれていない場合は、$(@D) の値としてドット (.) が値として割り当てられます。
ターゲットエントリは、次のように書き換えることができます。
# 2 番目のエントリ ../lib/libpkg.a: cd $(@D); $(MAKE) $(@F)
このターゲットには依存関係がないため、../lib/libpkg.a という名前のファイルがないときにだけこのターゲットが実行されます。ファイルが .PRECIOUS により保護されたライブラリアーカイブである場合は、../lib/libpkg.a ファイルがないということはほとんどありません。make はそのファイルの依存関係を認識する必要はないため、認識しません。ファイルを構築するかどうか、およびその構築方法は、入れ子にした呼び出しによって決定されます。
つまり、ファイルシステム内にファイルがあっても、そのファイルが最新でない場合もあります。したがって、ファイルがあるかどうかに関わらず、そのファイルを規則が空白の (および既存のファイルがない) 他のターゲットに依存させることによって、入れ子にした make コマンドを強制的に実行する必要があります。
表 4-15 入れ子にした make コマンド用のターゲットエントリ
# 入れ子にした make コマンド用 # ターゲットエントリ ../lib/libpkg.a: FORCE cd $(@D); $(MAKE) $(@F) FORCE: |
この方法により、make は、正しいディレクトリ ../lib に変更し、そのディレクトリにあるメークファイルに記述された命令に従って、必要であれば libpkg.a を構築します。入れ子にした make の実行結果は次のようになります。
$ make ../lib/libpkg.a cd ../lib; make libpkg.a make libpkg.a `libpkg.a' is up to date.
以下のメークファイルは、入れ子にした make コマンドを使用して、プログラムが依存するユーザー定義のライブラリを処理します。
表 4-16 ユーザー定義のライブラリを使用した C プログラム用のメークファイル
../lib/libpkg.a が最新である場合は、このメークファイルを使用する make の実行結果は以下のようになります。
$ make cc -O -c main.c cc -O -c data.c cd ../lib; make libpkg.a `libpkg.a' is up to date. cc -O -o functions main.o data.o ../lib/libpkg.a -lcurses -l termlib
MAKEFLAGS
マクロ
MAKE
マクロと同様に、MAKEFLAGS
も特殊マクロです。
MAKEFLAGS
をメークファイル中に定義しないでください。
MAKEFLAGS
には、make コマンド用のフラグ (1 文字のオプション) が含まれています。他の FLAGS マクロとは異なり、MAKEFLAGS
の値は、フラグの冒頭に付いている - (ハイフン) を除いて連結したものになります。たとえば、eiknp という文字列は、MAKEFLAGS
の値として認識されますが、-f x.mk や macro=value は値として認識されません。
MAKEFLAGS
という環境変数が設定されている場合は、make は、コマンド行で指定されたフラグと、MAKEFLAGS
に含まれるフラグを組み合わせて実行されます。
MAKEFLAGS
の値は、環境変数で設定されているかどうかに関係なく常にエクスポートされ、MAKEFLAGS
に含まれるオプションは、($(MAKE)、make、/usr/bin/make のうちどれによって呼び出されたかに関係なく) 入れ子にした make コマンドに渡されます。これにより、親の make が呼び出された際のオプションが、入れ子にした make コマンドに渡されます。
MAKEFLAGS
を除いて、make は環境変数をインポートし、定義済みのマクロと同様に扱います。次に、make は呼び出したコマンド (入れ子になった make コマンドを含む) にそれらの環境変数およびその値を渡します。
環境変数 SHELL
は、このバージョンの make にはインポートおよびエクスポートされません。
マクロは、メークファイルと同様に、コマンド行の引数としても指定できます。このため、マクロが複数の箇所で定義されている際に名前が衝突することがあります。make には、このような衝突を回避するための非常に複雑な優先順位の規則があります。
まず、条件付きマクロの定義は、定義されているターゲット (およびその依存関係) 内で常に有効です。
マクロ定義を引数として make を呼び出すと、その定義はメークファイル内のマクロ定義または環境変数からインポートされたマクロ定義よりも優先されます (ただし、入れ子にした make コマンドではこの優先順位とは異なる場合があります)。それ以外の場合は、メークファイルでマクロを定義 (または再定義) した場合は、最新の定義が適用されます。通常は、最新の定義が環境変数の定義よりも優先されます。
最後に、マクロがデフォルトのファイルだけで定義されている場合は、その値が使用されます。
入れ子にした make コマンドでは、通常はメークファイルでの定義が環境変数よりも優先されますが、これは定義がメークファイルに記述されている場合のみ該当します。対応する環境変数はこれとは無関係に伝達されます。
コマンド行での定義は、その定義を指定した make の実行でのみ、環境変数とメークファイルの定義を無効にします。コマンド行で指定した値は、入れ子にした make コマンドにも渡されますが、入れ子にした make コマンドでの定義と、入れ子にした make コマンドによりインポートされた環境変数によって無効になります。
-e オプションの動作はさらに一貫しています。環境変数は、メークファイルでのマクロ定義を無効にします。コマンド行での定義は、メークファイルおよび 環境変数での定義よりも常に優先して使用されます。ただし、-e を使用すると、メークファイルに含まれていない情報により構築の成否が左右される可能性があります。
このような複雑さを避けるために、特定の値を make コマンドの階層全体に渡す際には、環境変数を (C シェルで) 設定して、サブシェルで make -e を実行してください。
% (unsetenv MAKEFLAGS LDFLAGS; setenv CFLAGS -g; make -e)
以下のメークファイルを使用して、さまざまな場合をテストすることができます。
# top.mk MACRO= "Correct but unexpected." top: @echo "------------------------------ top" echo $(MACRO) @echo "------------------------------" $(MAKE) -f nested.mk @echo "------------------------------ clean" clean: rm nested
# nested.mk MACRO=nested nested: @echo "------------------------------ nested" touch nested echo $(MACRO) $(MAKE) -f top.mk $(MAKE) -f top.mk clean
以下に、マクロの割り当て順序の一覧表を示します。
表 4-17 マクロの割り当て順序の一覧表
-e なし |
-e を使用 |
---|---|
最上位の make コマンド |
|
条件付き定義 |
条件付き定義 |
make のコマンド行 |
make のコマンド行 |
最新のメークファイルの定義 |
環境変数の値 |
環境変数の値 |
最新のメークファイルの定義 |
定義済みの値 (ある場合) |
定義済みの値 (ある場合) |
入れ子にした make コマンド |
|
条件付き定義 |
条件付き定義 |
make のコマンド行 |
make のコマンド行 |
最新のメークファイルの定義 |
親の make コマンド行 |
環境変数 |
環境変数 |
定義済みの値 (ある場合) |
最新のメークファイルの定義 |
親の make コマンド行 |
定義済みの値 (ある場合) |
次の例のメークファイルは、アセンブリ言語のルーチンとリンクした C プログラムを管理します。アセンブリのソースファイルには、cpp プリプロセッサ指令を含むものと含まないものの 2 種類あります。
慣例として、プリプロセッサ指令を含まないアセンブリのソースファイルには .s という接尾辞が付きます。プリプロセッサ処理が必要なアセンブリのソースには、.S という接尾辞が付きます。
ASFLAGS は、.s.o および .S.o の暗黙の規則に関するオプションを渡します。
アセンブリのソースは、C ソースをコンパイルするのと同様の方法でアセンブルされてオブジェクトファイルを形成します。そのオブジェクトファイルは、C プログラムにリンクできます。make には、.s および .S のファイルをオブジェクトファイルに変換するための暗黙の規則があるため、アセンブリのルーチンを持つ C プログラムのターゲットエントリでは、オブジェクトファイルのリンク方法を指定するだけです。アセンブラによって生成されたオブジェクトファイルをリンクするには、cc コマンドを使用できます。
表 4-18 アセンブルソースファイルから C プログラムを生成するメークファイル
CFLAGS= -O ASFLAGS= -O .KEEP_STATE: driver: c_driver.o s_routines.o S_routines.o cc -o driver c_driver.o s_routines.o S_routines.o |
.S ファイルは、cc コマンドを使用して処理されます。cc コマンドは、C のプリプロセッサ cpp およびアセンブラを呼び出します。
lex および yacc は、C ソースファイルを出力します。lex および yacc のソースファイルは、接尾辞がそれぞれ .l、.y になります。lex および yacc のソースファイルを別々にコンパイルする場合のコンパイル処理は、C ソースだけからプログラムを生成する場合と同様です。
lex または yacc のソースを .c ファイルにコンパイルする暗黙の規則があります。.c ファイルは、C ソースからオブジェクトファイルをコンパイルする暗黙の規則を使用してさらに処理されます。ソースファイルに #include 文が含まれていない場合は、.c ファイルは中間ファイルとして使用されるため、保存しておく必要はありません。この場合は、.l.o の規則または .y.o の規則を使用してオブジェクトファイルを生成し、(派生した) .c ファイルを削除できます。
以下にメークファイルの例を示します。
CFLAGS= -O .KEEP_STATE: all: scanner parser scanner: scanner.o parser: parser.o
このメークファイルの結果は、以下のようになります。
$ make -n rm -f scanner.c lex -t scanner.l > scanner.c cc -O -c -o scanner.o scanner.c rm -f scanner.c yacc parser.y cc -O -c -o parser.o y.tab.c rm -f y.tab.c
lex と yacc を組み合わせて使用する場合は、より複雑になります。オブジェクトファイルが正しく機能するには、lex の C コードに yacc が生成したヘッダーが含まれている必要があります。yacc のソースファイルが変更されたときは、lex が生成した C ソースファイルを再コンパイルする必要があります。この場合は、yacc のソースが変更されるごとに lex を実行しなくてもよいように、lex が生成した中間 (.c) ファイルと、yacc が生成した .h ファイルを保存しておきます。
yacc は、y.tab.c と y.tab.h という名前の出力ファイルを生成します。出力ファイルのベース名をソースファイルと同じにしたい場合には、出力ファイルの名前を変更してください。
以下のメークファイルは、lex のソース、yacc のソース、C ソースファイルから構築したプログラムを管理します。
CFLAGS= -O .KEEP_STATE: a2z: c_functions.o scanner.o parser.o cc -o $@ c_functions.o scanner.o parser.o scanner.c: parser.c + parser.h: parser.y yacc -d parser.y mv y.tab.c parser.c mv y.tab.h parser.h
前述のように、複数の暗黙の規則の橋渡しをする規則は生成されないので、scanner.c のターゲットエントリを指定する必要があります。このエントリは、.l.c と .c.o の暗黙の規則の橋渡しをして scanner.o の依存関係リストから scanner.l を生成します。ターゲットエントリには規則がないため、暗黙の規則 .l.c を使用して scanner.c が構築されます。
次のターゲットエントリは、yacc の中間ファイルを生成する方法を指定しています。yacc -d を使用してヘッダーと C ソースファイルの両方を生成する暗黙の規則はないため、これを行う規則を指定するターゲットを記述する必要があります。
parser.c と parser.h のターゲットエントリにおいて、ターゲット名を区切る + 記号は、そのエントリがターゲットグループのエントリであることを示します。ターゲットグループとは複数のファイルの集まりで、それらすべてのファイルは規則の実行時に生成されます。 1 つのターゲットを構成している複数のファイルを、 1 つのグループとして扱います。+ 記号がない場合は、リスト中の各項目は独立したターゲットになります。ターゲットグループを使用すると、make は各ターゲットファイルについて別々に変更日時を調べ、ターゲットの規則は、make の実行 1 回につき必要な場合に 1 度だけ実行されます。
シェルスクリプトはテキストファイルですが、実行するには実行権が必要です。SCCS 管理のファイルからは実行権が削除されるため、SCCS ではシェルスクリプトとその「ソース」という区別を設けると便利です。make には、ソースからスクリプトを取り出すという暗黙の規則があります。シェルスクリプトのソースファイルの接尾辞は .sh です。スクリプトおよび .sh ソースファイルファイルの内容は同じですが、スクリプトには実行権が設定されており、.sh ソースファイルには設定されていません。make でのスクリプト用の暗黙の規則は、ソースファイルからスクリプトを取り出し、.sh ファイルを (必要に応じて取り出してから) 複製し、そのスクリプトファイルのアクセス権を変更して実行可能にします。以下に例を示します。
$ file script.sh script.sh: ascii text $ make script cat script.sh > script chmod +x script $ file script script: commands text
シェルスクリプトは、テストの実行や、対話式の (ユーザー入力が必要な) 処理あるいは make による依存関係の検査が不要な定型作業を行う際に便利です。特にテストでは、プログラムに対して端末から特定の入力を繰り返し行う必要があります。
ライブラリの場合は、さまざまな機能を実行するプログラムのセットを C で記述し、スクリプトからの特定の入力に応じて特定の順序で実行することができます。ユーティリティプログラムの場合は、機能を実行してその速度を測定するベンチマークプログラムを作成できます。いずれの場合も、各テストを実行するコマンドをシェルスクリプトに組み込み、繰り返しユーザーが行う処理をなくしたり、管理を簡単にすることができます。
テスト用スクリプトを開発した後、そのスクリプトを実行するためのターゲットは簡単に記述できます。スクリプト内では make の依存関係の検査は不要な場合がありますが、依存関係の検査を実行すると、テスト前にプログラムまたはライブラリを更新することができます。
以下に示すテストを実行するためのターゲットエントリでは、test が lib.a に依存しています。ライブラリが最新でない場合は、make はライブラリを再構築してテストを実行します。これにより、常に最新のバージョンを使用してテストが実行されます。
# テスト中のライブラリ LIBRARY= lib.a test: $(LIBRARY) testscript set -x ; testscript > /tmp/test.¥$¥$ testscript: testscript.sh test_1 test_2 test_3 # ライブラリ構築規則 $(LIBRARY): @ echo Building $(LIBRARY) (library-building rules here) # test_1 ... test_3 で複数のライブラリ関数を検査する test_1 test_2 test_3: $$@.c $(LIBRARY) $(LINK.c) -o $@ $<
test は、testscript にも依存しています。testscript は、3 つのテストプログラムに依存しています。
これにより、テストプログラムが make がテスト処理を実行する前に更新されます。lib.a は、メークファイルに含まれるターゲットエントリに従って構築されます。testscript は、.sh の暗黙の規則を使用して構築されます。各テストプログラム用にそれぞれソースファイルが 1 つずつある場合に、最後のターゲットエントリの規則を使用してテストプログラムが構築されます (これらのプログラムは、.c ファイルのほかに、適切なライブラリにもリンクする必要があるため、.c の暗黙の規則は適用されません)。
テスト用の規則に含まれる ¥$¥$ という文字列は、make が $ 記号を解釈しないように指定しています。make は、2 つの $ 記号をそのままシェルに渡します。シェルは、$$ をシェルのプロセス ID に展開します。これよって、各テストごとに異なる名前の一時ファイルに書き込むことが可能になります。set -x コマンドは、シェルが端末上で実行するコマンドをシェルに表示させます。これにより、テスト結果が記録されているファイルの実際の名前を確認することができます。
以下の例のように、規則内でシェルコマンドの置換を指定できます。
do: @echo `cat Listfile`
マクロ中では、逆引用符で囲まれた式を指定することもできます。
DO= `cat Listfile` do: @echo $(DO)
ただし、この形式のコマンド置換は規則内でのみ使用できます。
シェルコマンドをマクロの定義として指定する例を以下に示します。
COMMAND= cat Listfile
コマンド置換マクロ参照を使用して、参照をマクロ値に含まれるコマンド出力に置換するように make に指示できます。このコマンド置換は、メークファイル中の任意の箇所に記述できます。
COMMAND= cat Listfile $(COMMAND:sh): $$(@:=.c)
この例は、他のファイルからターゲットのリストを取り込み、各ターゲットが対応する .c ファイルに依存することを示します。
シェルコマンド置換と同様に、コマンド置換参照が評価されると、コマンドの標準出力結果に置換されます。復帰改行文字は、空白文字に変換されます。コマンドは、参照が検出されると実行されます。コマンドの標準エラーは無視されます。ただし、コマンドがゼロ以外の終了ステータスを返した場合は、make はエラーを表示して停止します。
これを回避するには、コマンド行の末尾に true コマンドを追加します。
COMMAND = cat Listfile ; true
以下の形式のマクロ代入は、command の標準出力を cmd_macro に代入します。以下に例を示します。
cmd_macro:sh = command
COMMAND:sh = cat Listfile $(COMMAND): $$(@:=.c)
この例は、前述の例と同じ結果になります。ただし、この例ではコマンドは make の実行ごとに 1 回だけ実行されます。この場合も、標準出力だけが使用され、復帰改行文字は空白文字に変換されます。また、コマンドがゼロ以外の終了ステータスを返した場合は、make はエラーを表示して停止します。
コマンド置換マクロ代入は、以下の形式で記述することもできます。
macro:sh += command
macro の値にコマンドの出力を追加します。
target := macro:sh = command
target およびその依存関係を処理するときに、command の出力として条件付き macro を定義します。
target := macro:sh += command
target およびその依存関係を処理するときに、command の出力を条件付き macro の値に追加します。
make は、ソフトウェアプロジェクトがプログラムとライブラリのシステムで構成されている場合に特に便利です。入れ子にした make コマンドを使用すると、ディレクトリ階層全体に渡って、オブジェクトファイル、実行可能ファイル、ライブラリを管理できます。make を SCCS と合わせて使用すると、ソースを一貫して管理し、そのソースから整合性のとれたプログラムを構築することができます。必要に応じて、ディレクトリ階層を複製して他のプログラマに提供し、複数のプログラマが並行して開発およびテストを同時に行うことができます (ただし、考慮すべき点はあります)。
また、make を使用して、プロジェクト全体を構築し、完成したさまざまなモジュールを統合して配布するために他のファイルシステムにコピーすることができます。
前述のように、プロジェクトを整理するには、プロジェクトを主な構成要素ごとにいくつかのディレクトリに分割するのが適しています。このようにして分割したプロジェクトは、通常は 1 つのファイルシステム上またはディレクトリ階層内で管理します。ヘッダーファイル、ライブラリ、プログラムはそれぞれ別のサブディレクトリに置きます。参照マニュアルなどのドキュメントも、別のサブディレクトリに置いて管理します。
次の図に示すように、プロジェクトが 1 つの実行可能プログラム、1 つのユーザー定義ライブラリ、ライブラリのルーチン用の 1 組のヘッダー、複数のマニュアルで構成されているものとします。
各サブディレクトリにあるメークファイルは、これまでの節で説明したものを使用できますが、プロジェクト全体を管理するためのメークファイルがさらに必要です。プロジェクトのルートディレクトリにあるメークファイルには、プロジェクトを 1 つの構成要素として一括管理するためのターゲットエントリを指定します。
プロジェクトが大きくなると、簡単に使用できる整合性のあるメークファイルが必要になります。マクロおよびターゲット名は、どのメークファイルでも意味が同じである必要があります。出力形式を決定する条件付きマクロ定義およびコンパイルオプションは、プロジェクト全体で一貫している必要があります。
可能であれば、テンプレートを使用してメークファイルを記述します。テンプレートを使用して、プロジェクトがどのように構築されるかを監視します。モジュール用のディレクトリを作成し、適切なメークファイルをそのディレクトリにコピーして、数行を編集するだけで、新しい種類のモジュールを追加することができます。また、ルートのメークファイルで構築する新しいモジュールを追加する必要があります。
デフォルトのメークファイルなどで使用されるマクロおよびターゲットの名前の付け方は、プロジェクト全体で統一する必要があります。ニーモニック名とは、ターゲットの機能やマクロの値を正確に覚えていなくても、その名前から機能または値の種類を判断することができるような名前です。ニーモニックは、メークファイルを解読する際にも便利です。
一貫したコンパイル環境を保ちながら、メークファイルを簡潔にする方法として、以下のように make を使用します。
include filename
この include 指令は、filename に指定した名前のメークファイルの内容を読み取ります。指定した名前のファイルがない場合は、make は /etc/default でその名前のファイルを検査します。
たとえば、以下のようにターゲットエントリをインクルードすると、各メークファイルごとに troff ソースを処理するパターンマッチングの規則を重複して記述する必要はありません。
SOURCES= doc.ms spec.ms ... clean: $(SOURCES) include ../pm.rules.mk
この例では、make は ../pm.rules.mk ファイルの内容を読み取ります。
# pm.rules.mk # # パターンマッチング規則用の "include" makefile # %.tr: %.ms troff -t -ms $< > $@ %.nr: %.ms nroff -ms $< > $@
外部でのテストまたは通常の使用を目的として、プログラムをリリースする際には、make を使用してプログラムをインストールできます。インストールを行うための新しいターゲットおよびマクロ定義は、以下のように簡単に追加できます。
DESTDIR= /proto/project/bin install: functions -mkdir $(DESTDIR) cp functions $(DESTDIR)
ライブラリまたはヘッダーをインストールする際にも、同様のターゲットを使用できます。
ある時点のソースおよびそれによって構築されるオブジェクトファイルを、プロジェクト開発中に何度か保存する必要があります。プロジェクト全体を構築するには、make を各サブディレクトリごとに実行して、各モジュールを構築し、それをインストールします。以下の例は、入れ子にした make コマンドを使用して単純なプロジェクトを構築する方法を示しています。
プロジェクトが bin と lib という 2 つの異なるサブディレクトリに置かれていて、両方のサブディレクトリで、make を使用して、プロジェクトのデバッグ、テスト、インストールを行うものとします。
最初に、プロジェクトのルートディレクトリ (プロジェクトの一番上のディレクトリ) に、以下のようなメークファイルを置きます。
# プロジェクトのルート makefile TARGETS= debug test install SUBDIRS= bin lib all: $(TARGETS) $(TARGETS): @for i in $(SUBDIRS) ; ¥ do ¥ cd $$i ; ¥ echo "Current directory: $$i" ;¥ $(MAKE) $@ ; ¥ cd .. ; ¥ done
次に、各サブディレクトリ (この場合は bin) に、以下の一般的な形式のメークファイルを作成します。
#サブディレクトリ中の makefile debug: @echo " Building debug target" @echo test: @echo " Building test target" @echo install: @echo " Building install target" @echo
プロジェクトのルートディレクトリで make と入力すると、以下のように出力されます。
$ make Current directory: bin Building debugging target Current directory: lib Building debugging target Current directory: bin Building testing target Current directory: lib Building testing target Current directory: bin Building install target Current directory: lib Building install target $
プロジェクトの階層を拡張する場合は、各中間ディレクトリのメークファイルが、ターゲットファイルを生成するだけでなく、その各メークファイルのサブディレクトリ用に入れ子にした make コマンドを呼び出す必要があります。
現在のディレクトリのファイルは、サブディレクトリのファイルに依存する場合があります。その場合は、ターゲットエントリはサブディレクトリにある対応するターゲットエントリに依存している必要があります。
各サブディレクトリ用の入れ子にした make コマンドは、ローカルディレクトリのコマンドの前に実行する必要があります。入れ子にしたコマンドとローカルディレクトリのコマンドに対して別々にエントリを作成すると、コマンドを正しい順序で実行できます。このような新しいターゲットを、元のターゲットの依存関係リストに追加すると、オリジナルのターゲットの動作と新しいターゲットの動作の両方が実行されます。
ローカルディレクトリとサブディレクトリの両方で同一の動作を行うターゲットを、再帰的なターゲットと呼びます。
厳密には、ターゲット自身の名前を引数として指定して make を呼び出すターゲットは、すべて再帰的です。ただしここでは、入れ子になった動作とローカルの動作の両方を持つターゲットのみを「再帰的なターゲット」と呼びます。入れ子にした動作のみを持つターゲットは、「入れ子にしたターゲット」と呼びます。
再帰的なターゲットを含むメークファイルは、再帰的なメークファイルと呼びます。
以下の例の all の場合は、入れ子にした依存関係は NESTED_TARGETS、ローカルの依存関係は LOCAL_TARGETS です。
NESTED_TARGETS= debug test install SUBDIRS= bin lib LOCAL_TARGETS= functions all: $(NESTED_TARGETS) $(LOCAL_TARGETS) $(NESTED_TARGETS): @ for i in $(SUBDIRS) ; ¥ do ¥ echo "Current directory: $$i" ;¥ cd $$i ; ¥ $(MAKE) $@ ; ¥ cd .. ; ¥ done $(LOCAL_TARGETS): @ echo "Building $@ in local directory." (local directory commands)
入れ子にした make も、最下位の階層にある場合を除き、再帰的である必要があります。末端のディレクトリ (サブディレクトリを持たないディレクトリ) のメークファイルでは、ローカルターゲットのみを構築します。
大規模なライブラリは、複数の補助ライブラリに分割し、完全なパッケージを構築する際に make を使用して 1 つに結合すると、管理が簡単になる場合があります。ar を使用してライブラリを直接結合することはできませんが、以下の例に示すように、まず各補助ライブラリのメンバーファイルを取り出し、次に別の手順でそれらのファイルをアーカイブにまとめることができます。
$ ar xv libx.a x - x1.o x - x2.o x - x3.o $ ar xv liby.a x - y1.o x - y2.o $ ar rv libz.a *.o a - x1.o a - x2.o a - x3.o a - y1.o a - y2.o ar: creating libz.a
補助ライブラリは、そのライブラリを構築する (オブジェクト) ファイルとともに、そのディレクトリにあるメークファイルを使用して管理します。完成ライブラリ用のメークファイルは、各補助ライブラリアーカイブへのシンボリックリンクを作成し、アーカイブの内容を一時的なサブディレクトリに展開し、生成されたファイルをアーカイブして完全なパッケージを作成します。
次の例は、補助ライブラリを更新し、展開したファイルを保存する一時ディレクトリを作成して、補助ライブラリを展開します。一時ディレクトリでは、ワイルドカード * (シェル) を使用して、照合したファイルのリストを生成します。通常は、ファイル名にワイルドカードは使用しない方がよいですが、この例ではターゲットが構築されるたびに新しいディレクトリが作成されるため、使用が可能です。これによって、実行中の make により展開されたファイルだけが一時ディレクトリに含まれます。
通常は、メークファイル中でシェルファイル名にワイルドカードを使用することは避けてください。使用する場合は、必要なファイルを一時サブディレクトリに移動することによって対称外のファイルを除外するような処理手順にしてください。
この例は、ディレクトリの命名規約を使用しています。ディレクトリ名は、そのディレクトリにあるライブラリのベース名から決定されます。たとえば、補助ライブラリの名前が libx.a の場合は、その補助ライブラリがあるディレクトリの名前は libx になります。
この例は、動的なマクロ参照での接尾辞置換を使用して、各サブディレクトリのディレクトリ名を取り出します。各ライブラリを順次取り出すためのループとしてシェルを使用します。また、ライブラリからパッケージを作成する際に、シェルコマンド置換を使用して、オブジェクトファイルを正しいリンク順に並び替えます (lorder と tsort を利用します)。最後に、一時ディレクトリおよびその内容を削除します。
# サブディレクトリで生成されたライブラリをまとめる Makefile CFLAGS= -O .KEEP_STATE: .PRECIOUS: libz.a all: lib.a libz.a: libx.a liby.a -rm -rf tmp -mkdir tmp set -x ; for i in libx.a liby.a ; ¥ do ( cd tmp ; ar x ../$$i ) ; done ( cd tmp ; rm -f *_*_.SYMDEF ; ar cr ../$@ `lorder * | tsort` ) -rm -rf tmp libx.a liby.a libx.a liby.a: FORCE -cd $(@:.a=) ; $(MAKE) $@ -ln -s $(@:.a=)/$@ $@ FORCE:
説明を簡潔にするため、この例では他の形式のプログラムと、clean、install、test ターゲットはサポートしていません (ソースファイルがサブディレクトリにあるため、適用できません)。
インデントされた行にある rm -f *_*_.SYMDEF というコマンドは、補助ライブラリのシンボルテーブル (補助ライブラリに対して ar を実行すると生成されます) がこの完成ライブラリ libz.a にアーカイブされないようにします。
入れ子にした make コマンドは現在のライブラリを処理する前に補助ライブラリを構築するため、現在のディレクトリにある補助ライブラリとオブジェクトファイルの両方から構築されるライブラリ用にこのメークファイルを拡張できます。オブジェクトファイルのリストを、ライブラリの依存関係リストに追加する必要があります。また、そのオブジェクトファイルを、補助ライブラリから展開したオブジェクトファイルと照合するための一時サブディレクトリにコピーするためのコマンドを追加する必要があります。
# サブディレクトリで生成されたライブラリと、オブジェクトをまとめる Makefile CFLAGS= -O .KEEP_STATE: .PRECIOUS: libz. OBJECTS= map.o calc.o draw.o all: libz.a libz.a: libx.a liby.a $(OBJECTS) -rm -rf tmp -mkdir tmp -cp $(OBJECTS) tmp set -x ; for i in libx.a liby.a ; ¥ do ( cd tmp ; ar x ../$$i ) ; done ( cd tmp ; rm -f *_*_.SYMDEF ; ar cr ../$@ ¥ `lorder * | tsort` ) -rm -rf tmp lix.a liby.a libx.a liby.a: FORCE -cd $(@:.a=) ; $(MAKE) $@ -ln -s $(@:.a=)/$@ $@ FORCE:
たとえば、.so を要求して、troff ドキュメントにインクルードされているソースファイルをトレースする必要があるなどの場合は、隠れた依存関係を処理するコマンドを記述する必要があります。.KEEP_STATE が有効な場合は、make は環境変数 SUNPRO_DEPENDENCIES
を次のように設定します。
SUNPRO_DEPENDENCIES='report-file target'
コマンドが終了すると、make はファイルが作成されたかどうかを調べ、作成されていれば、そのファイルを読み取り、報告された依存関係を以下の形式で .make.state に書き込みます。
target:dependency ...
ここで、target は環境変数に指定されているものと同じです。
以下では、make に新しく追加された機能について説明しています。
make の暗黙の規則およびマクロ定義は、このバージョンでは make プログラム中に定義 (ハードコード) されずに、デフォルトのメークファイルである /usr/share/lib/make/make.rules に含まれています。ローカルディレクトリに make.rules というファイルがある場合を除き、このデフォルトのメークファイルを自動的に読み取ります。ローカルディレクトリにある make.rules ファイルを使用する際は、標準の暗黙の規則および定義済みマクロを取得するために、デフォルトの make.rules ファイルをインクルードする指示を追加する必要があります。
make は、状態ファイル .make.state も読み取ります。特殊ターゲットの .KEEP_STATE がメークファイル中で使用されている場合は、make は、隠れた依存関係のリスト (cpp などのコンパイル処理によりレポートされます) と、各ターゲットを構築するために使用された最新の規則が記述されている、各ターゲットのについての記録をこのファイルに書き込み蓄積します。状態ファイルの書式は、通常のメークファイルの書式とほぼ同じです。
make が .KEEP_STATE ターゲットにより起動された場合は、 cc、cpp、f77、ld、make、pc などのコンパイルコマンドによって報告された情報を使用して、ターゲットファイルにインクルードされたヘッダーファイル (場合によってはライブラリ) に対して依存関係の検査を実行します。これらの隠れた依存ファイルは、依存関係リストには記述されていません。多くの場合はローカルディレクトリ以外の場所に存在しています。
.KEEP_STATE が有効な場合に、ターゲットを構築するために使用されるコマンド行が以前の make 実行時から変更された場合 (メークファイルを編集したりマクロの値を変更した場合など)、ターゲットは最新でないとして扱われ、(ターゲットが依存するファイルよりもターゲットが新しい場合でも) make はターゲットを再構築します。
この節では、sccs が管理しているファイルの自動取り出しの規則を説明します。
このバージョンの make は、ターゲットファイルを構築する規則がない場合には、sccs getx を適宜自動的に実行します。接尾辞リストで接尾辞の後にチルド (‾) が付いている場合は、sccs によって依存ファイルを取り出すのが適切であることを示します。このバージョンの make は、sccs ファイルの現在のバージョンを取り出すためのコマンドを含むチルドの接尾辞の規則をサポートしていません。
現在の sccs のバージョンの自動取り出しを禁止または許可するには、.SCCS_GET という特殊ターゲットを再定義します。このターゲットの規則を空白にすると、すべてのファイルに対して自動取り出しを禁止します。
ユーザー定義の暗黙の規則を簡単にプロジェクトに追加する方法を簡素化するために、パターンマッチングの規則が追加されています。
tp%ts : dp%ds rule
この形式のターゲットエントリは、関連する依存ファイルからターゲットを構築するパターンマッチングの規則を定義します。tp はターゲット名の接頭辞、ts はその接尾辞です。dp は依存ファイル名の接頭辞、ds はその接尾辞です。% 記号は、ターゲットと依存ファイルの名前の両方に共通する、0 文字または 1 文字以上の連続した文字列を示すワイルドカードです。たとえば、以下のターゲットエントリは、接尾辞が .ms で -ms というマクロパッケージを使用するファイルから接尾辞が .tr の troff 出力ファイルを構築するパターンマッチングの規則を定義します。
%.tr: %.ms troff -t -ms $< > $@
メークファイルにこのエントリが含まれている場合に、以下のコマンドを実行します。
make doc.tr
結果は以下のようになります。
$ make doc.tr troff -t -ms doc.ms > doc.tr
doc2.ms というファイルがある場合に同じエントリを使用して、以下のコマンドを実行します。
make doc2.tr
結果は以下のようになります。
$ make doc2.tr troff -t -ms doc2.ms > doc2.tr
明示的なターゲットエントリは、ターゲットに適用されるパターンマッチングの規則よりも優先されます。パターンマッチングの規則は、通常は暗黙の規則よりも優先されます。例外的に、パターンマッチングの規則のターゲットエントリにおいて、規則の部分にコマンドが含まれていない場合は、make はターゲットを構築するための規則の検索を続行し、その依存関係として (依存関係) パターンに一致したファイルを使用します。
接尾辞の規則およびパターンマッチングの規則と同様に、指定したマクロ参照の語を変更する方法として、マクロ参照で接尾辞を置換する既存の方法よりもさらに汎用性が高い、パターン置換マクロ参照が追加されました。パターン置換マクロ参照は、以下の形式で記述します。
$(macro:p%s=np %ns)
ここで、p は既存の接頭辞 (ある場合)、s は既存の接尾辞 (ある場合)、np および ns は新しい接頭辞および接尾辞、% は 0 文字または 1 文字以上の一致文字列を示すワイルドカードです。
接頭辞および接尾辞の置換は、既存のパターンに一致するマクロの値中のすべての語に適用されます。この機能は、特にサブディレクトリの名前をそのサブディレクトリに含まれる各ファイルの接頭辞として使用する場合に便利です。以下にメークファイルの例を示します。
SOURCES= x.c y.c z.c SUBFILES.o= $(SOURCES:%.c=subdir/%.o) all: @echo $(SUBFILES.o)
結果は以下のようになります。
$ make subdir/x.o subdir/y.o subdir/z.o
= 記号の右側 (置換後の語) では、任意の数だけワイルドカードの % を必要に応じて使用できます。以下に例を示します。
... NEW_OBJS= $(SOURCES:%.c=%/%.o)
この置換は、以下のような結果になります。
... x/x.o y/y.o z/z.o
パターン置換マクロ参照は、パターンマッチングの規則が記述されているターゲットエントリの依存関係を示す行では使用しないでください。使用した場合は、予期しない結果が生じます。以下にその例を示します。
OBJECT= .o x: %: %.$(OBJECT:%o=%Z) cp $< $@
このメークファイルは、make が x.Z というファイルから x を構築しようとして記述したものです。しかし、依存関係の行に複数含まれている % のうち、どれをパターンマッチングの規則で使用し、どれをマクロ参照に適用するかを、make が特定できないため、パターンマッチングの規則は認識されません。
したがって、x.Z のターゲットエントリは実行されません。このような問題を回避するため、他の行で中間マクロを使用できます。
OBJECT= .o ZMAC= $(OBJECT:%o=%Z) x: %: %$(ZMAC) cp $< $@
新しいオプションは、以下のとおりです。
処理した各ターゲットの依存関係の検査結果を表示します。新しい依存関係、あるいはターゲットがコマンドの依存関係の結果構築されたことを示す依存関係をすべて表示します。
旧バージョンの make の -d と同一の機能を実行します。内部状態など、make のすべての実行情報を表示します。
メークファイルのテキスト (内容) を読み取り時に表示します。
メークファイルおよび使用されているデフォルトのメークファイルのテキストを表示します。
マクロ定義およびターゲットエントリを出力します。
ターゲットを再構築せずに、その依存関係をすべてレポートします。
このバージョンの make には、C++ プログラムをコンパイルするための定義済みマクロが含まれています。また、Modula-2 をコンパイルするための定義済みマクロおよび暗黙の規則も含まれています。
定義済みマクロの命名規約が合理的になりました。新しい命名規約に合わせて暗黙の規則が変更されました。マクロおよび暗黙の規則は、既存のメークファイルに対して上位互換性があります。
標準のコンパイルコマンド用のマクロを例に示します。
LINK.c
これは、実行可能ファイルを生成するための標準の cc コマンド行です。
COMPILE.c
これは、オブジェクトファイルを生成するための標準の cc コマンド行です。
メークファイルに記述されている場合は、隠れた依存関係およびコマンドの依存関係の検査を有効にします。また、make は、実行後に状態ファイル .make.state を更新します。
.KEEP_STATE ターゲットは、make の実行で使用したことがある場合は削除しないでください。
make 実行時に、最初と最後に実行するコマンドを指定します。
make が失敗したときに実行するコマンドを指定します。
ターゲットを並行処理するか逐次処理するかを指定します。
このターゲットには、sccs 履歴ファイルから現在のバージョンのファイルを取り出す規則を指定します。
このターゲットが依存関係リストに記述されている場合は、並行処理の場合でも、make は先行する依存関係の処理が終了するまでそれ以降の処理を行わずに待機します。
lint を使用した差分検査をサポートするため、暗黙の規則が追加されました。
このバージョンでは、マクロの値を任意の長さにすることができます。また、旧バージョンではマクロの値から最後の空白文字だけが削除されましたが、このバージョンでは最初と最後の両方の空白文字が削除されます。
マクロの既存の値に、先頭にスペースが付いた文字列を追加します。
ターゲットの条件付きマクロ定義を示す演算子です。
target := macro = value
このようにメークファイルで記述すると、target およびその依存関係を処理する際には、macro が value に指定された値になります。
make は、条件付きマクロ定義のターゲット部分において、ワイルドカードパターンである % を認識します。以下に例を示します。
profile_% := CFLAGS += -pg
この例は、profile_ という接頭辞を持つすべてのターゲットについて、CFLAGS
マクロを変更します。条件付き定義の値では、パターン置換を使用できます。以下に例を示します。
profile_% := OBJECTS = $(SOURCES:%.c=profile_%.o)
この例は、SOURCES 値に指定されているすべての .c ファイルのベース名の前後に、profile_ という接頭辞および .o という接尾辞を追加します。
部分文字列の置換は、参照されるマクロが展開されてから実行されます。旧バージョンの make では、置換が先に実行されるため、結果を直観的に理解しにくくなっていました。
このバージョンの make は、内側の参照を展開してから外側の参照を展開します。以下に例を示します。
CFLAGS-g = -I../include OPTION = -g $(CFLAGS$(OPTION))
この例の入れ子にした参照の値は、旧バージョンでは NULL 値になりましたが、このバージョンでは -I../include になります。
定義済みマクロの HOST_ARCH および TARGET_ARCH をクロスコンパイルで使用できます。デフォルトでは、arch マクロは、arch コマンドが返す値に設定されます。
以下の定義は、command で指定されたコマンドの標準出力を MACRO の値として設定します。
MACRO :sh = command
出力に含まれる復帰改行は、空白文字に置換されます。コマンドは、定義が読み込まれたときに 1 回だけ実行されます。標準エラー出力は無視されます。コマンドがゼロ以外の終了ステータスを返した場合は、make はエラーを表示して停止します。
$(MACRO :sh)
このマクロ参照は、参照が評価されるときに MACRO に設定されているコマンド行出力に展開されます。復帰改行は、空白文字に置換されます。標準エラー出力は無視されます。コマンドがゼロ以外の終了ステータスを返した場合は、make はエラーを表示して停止します。
make は、ar フォーマットのライブラリのメンバーを、そのメンバーと名前が同じファイルから自動的に更新します。また、このバージョンの make では、以下の形式で、依存関係の名前としてメンバーのリストを指定できます。
lib.a: lib.a(member member ...)
このバージョンでは、複数ターゲットファイルで構成されるグループを生成するように規則を指定することができます。ターゲットエントリ中で各ターゲット名が + 記号で区切られている場合は、指定したターゲットがグループを構成していることを示します。ターゲットグループの規則は、make の実行 1 回につき 1 度だけ実行されます。
この節では、以下について簡単に説明しています。
-d オプション
動的なマクロ
チルド規則
ターゲット名
-d オプションは、このバージョンでは、ターゲットが最新でないとされる理由を出力します。
動的なマクロの $< および $* は、旧バージョンでは、暗黙の規則と .DEFAULT という特殊ターゲットでのみ値が代入されると説明されていましたが、実際には、明示的なターゲットエントリにも値が代入される場合がありました。このバージョンでは、値の代入について正しく説明されています。
これらのマクロに実際に割り当てられる値は、暗黙の規則 (この規則は変更されていません) で使用されるのと同一の手順で取得されます。明示的なターゲットエントリでこれらのマクロを使用すると、予期しない結果になる場合があります。
明示的な依存関係を指定しても、make はその依存関係を使用して前述のマクロの値を取得することはせず、適切な暗黙の規則および依存ファイルを検索します。以下に例を示します。
test: test.f @echo $<
上記のように明示的なターゲットエントリを指定したときに、test.c と test.f というファイルがある場合には、$< には test.f という値ではなく test.c が割り当てられます。これは、接尾辞のリスト中で .c の方が .f よりも先にあるためです。
$ make test test.c
明示的なエントリでは、厳密な指定方法でマクロ参照および接尾辞の置換を使用して、依存関ファイルの名前を取得することをお勧めします。たとえば、$< の代わりに $@.f を使用して、依存関係の名前を取得できます。.o のターゲットファイルのベース名を取得するには、接尾辞の置換マクロ参照である $(@:.o=) を $* の代わりに使用できます。
隠れた依存関係の検査が有効である場合は、$? という動的なマクロの値に、ヘッダーファイルなどの隠れた依存関係の名前が含まれます。このため、以下のようなターゲットエントリで、x.c がヘッダーファイルを展開すると、コンパイルが失敗する場合があります。
x: x.c $(LINK.c) -o $@ $?
これを解決するには、$? の代わりに $@.< を使用します。
チルド規則はサポートされていません。このバージョンの make は、SCCS でバージョンを取り出すためのチルド規則をサポートしていません。このため、SCCS でバージョンを取り出す必要があるときに特別な処理を実行するためのチルド規則が、旧バージョンのメークファイルで再定義されている場合には、問題が生じることがあります。
./ で始まるターゲット名は、ローカルディレクトリにあるファイル名として扱われます。
./ で始まるターゲット名を make が検出した場合は、./ を削除します。以下に例を示します。
./filename
このターゲット名は、以下のように解釈されます。
filename
再帰的なターゲットで使用された場合は、無限状態になることがあります。これを回避するには、.. (親ディレクトリ) からの相対パスでターゲットを記述してください。
../dir/filename