プログラミングユーティリティ

付録 A System V の make


注 -

/usr/ccs/lib/svr4.make は、旧バージョン System V make を使用するメークファイルのユーザー用に提供されています。ただし、できるだけデフォルトのバージョンの make (/usr/ccs/bin/make) を使用してください。また、第 4 章「make ユーティリティ」も参照してください。

このバージョンの make を使用するには、以下のようにシステム変数 USE_SVR4_MAKE を設定します。

 	$ USE_SVR4_MAKE="" ; export USE_SVR4_MAKE	 (Bourne シェルの場合)  
 	% setenv USE_SVR4_MAKE	 (C シェルの場合)

このバージョンの make の詳細は、sysV-make(1) のマニュアルページも参照してください。


プログラムのモジュールの数が多くなると、プロジェクト管理者はその分多くのファイルを管理する必要があります。多数のファイルから最終的に実行可能な製品を生成するには、さまざまな作成手順が必要です。

make には、さまざまな方法で作成された多数のファイルで構成されるプログラムの最新バージョンを管理するための機能があります。

make を使用すると、プログラマは以下のことを考慮する必要がなくなります。

make は、記述ファイル中に書かれているファイルおよびファイル間の関係を作成するコマンドを追跡します。プログラムを構成するファイルのいずれかに変更があると、make コマンドは、その変更によって直接的または間接的な影響がある部分だけを再コンパイルして、最終的なプログラムを作成します。

make が行う基本的な処理は、以下のとおりです。

ファイル間の依存関係とコマンドについての情報が書かれている記述ファイルは、通常 makefileMakefiles.makefile、または s.Makefile という名前にします。記述ファイルの名前をこれら 4 つのいずれかにしておくと、最後に make を実行した後に編集されたファイル数とは無関係に、make コマンドを実行するだけでターゲットを再生成することができます。ほとんどの場合は、記述ファイルは簡単に記述でき、頻繁に変更する必要はありせん。編集されたファイルが 1 つだけの場合、ターゲットを再生成するコマンドをすべて入力せずに make コマンドを入力するだけで、記述通りに再生成が実行されます。

基本的な機能

make の基本的な処理は、ターゲットファイルが依存するすべてのファイルが存在しており、それらのファイルが最新であることを確認して、ターゲットファイルを更新することです。依存関係の修正後にターゲットファイルが修正されていない場合、ターゲットファイルが再生成されます。make は、依存関係のグラフを検索します。ファイルが最後に修正された日時によって、make が実行する処理が決まります。

make は、以下の情報を使用して処理を実行します。

例として、C 言語のファイルの x.cy.cz.c を数学ライブラリの libm とコンパイルして読み込むことにより作成した、prog という名前のプログラムを説明します。C 言語の規約によって、C 言語のコンパイル出力は、x.oy.oz.o という名前のファイルになります。x.c および y.cdefs.h というファイル中の宣言のいくつかを共有していて、z.c は共有していないとします。この場合は、x.c および y.c に次の行が含まれます。

#include "defs.h"

以下は、ファイルの関係と処理を記述しています。

prog : x.o y.o z.o 
      	cc x.o y.o z.o -lm -o prog 
x.o y.o : defs.h

この情報が makefile という名前のファイルに保存されている場合は、次のコマンドは、x.cy.cz.cdefs.h のいずれかが変更された後に prog を再生成するために必要な処理を実行します。

$ make

前述の例では、最初の行は prog が 3 つの .o ファイルに依存することを示しています。2 行目は、これらのオブジェクトが最新になった後に、それらのオブジェクトを読み込んで prog を作成する方法を示しています。3 番目の行は、x.o および y.o がファイル defs.h に依存することを示しています。make は、ファイルシステムを調べて、必要な .o ファイルに対応する 3 つの .c ファイルがあり、それらの .c ソースファイルからオブジェクトを生成する際に組み込み規則を使用する (つまり cc -c コマンドを実行する) ことを特定します。

このように必要な処理を自動的に特定する make 機能がないとすると、以下のような長い記述ファイルが必要です。

prog : x.o y.o z.o 
      	cc x.o y.o z.o -lm -o prog 
x.o : x.c defs.h 
      	cc -c x.c 
y.o : y.c defs.h 
      	cc -c y.c 
z.o : z.c 
      	cc -c z.c

最後に prog を作成した後に変更されたソースファイルまたはオブジェクトファイルがなく、すべてのファイルが最新である場合は、make コマンドはその旨を表示して停止します。ただし、defs.h ファイルが編集されている場合は、x.c および y.c (z.c を除く) が再コンパイルされ、新しい x.o および y.o と既存の z.o から prog が作成されます。y.c だけが変更されている場合は、y.c だけが再コンパイルされますが、この場合も prog をもう一度読み込む必要があります。make コマンド行でターゲット名が指定されていない場合は、記述ファイルで最初に記述されているターゲットが作成されます。指定されている場合は、そのターゲットが作成されます。以下のコマンドは、x.c または defs.h が変更された場合に、x.o を再生成します。

$ make x.o

プログラミングの際には、ニーモニック名を付けた規則と、そのニーモニック名とは異なる名前のファイルを生成するコマンドを記述し、それらをインクルードするという方法でプログラムを作成しておくと、make のファイル生成とマクロ置換の機能を利用することができます。 (マクロについての詳細は、「記述ファイルと置換」を参照してください)。たとえば、ファイルをコピーするには save エントリ、不要な中間ファイルを削除するには clean などのエントリを記述します。

前述のコマンドを実行した後にファイルが存在している場合は、ファイルの最終変更時間によって先の処理が決定されます。コマンドを実行した後にファイルが存在していない場合は、現在時間によって先の処理が決定されます。

動作が実行された時間を記録するために、長さがゼロのファイルを使用することができます。この方法は、遠隔のアーカイブおよびリストを管理する際に便利です。

依存関係の行とコマンド文字列での置換を行う際には、make のマクロが使用されます。マクロは、コマンド行の引数として定義するか、記述ファイルに記述できます。いずれの場合も、マクロ名の後に = 記号を記述し、その後にマクロが表す内容を続けて記述します。マクロは、$ という記号を名前の最初に付けて呼び出します。1 文字以上の長さのマクロ名は、括弧で囲みます。以下に、有効なマクロ呼び出しの書式を示します。

$(CFLAGS) 
$2 
$(xy) 
$Z 
$(Z)

最後の 2 つは、同一の結果になります。

$*$@$?$< という特殊なマクロは、コマンドの実行中に値を変更します (これらの 4 つのマクロについては、「記述ファイルと置換」で説明しています)。以下の例は、マクロの割り当ておよび使用方法を示しています。

OBJECTS = x.o y.o z.o 
LIBES = -lm 
prog: $(OBJECTS) 
        	cc $(OBJECTS) $(LIBES) -o prog 
...
$ make LIBES="-ll -lm"

このコマンドは、lex (-ll) ライブラリと math (-lm)

ライブラリとともに 3 つのオブジェクトを読み取ります。これは、コマンド行でのマクロ定義が、記述ファイル中の定義を無効にするためです (オペレーティングシステムのコマンドでは、空白文字を含む引数は引用符で囲む必要があります)。

make の使用例として、make コマンドそのものを管理するために使用する記述ファイルを示します。make のコードは、複数の C 言語のソースファイルに対して実行され、yacc の文法を使用します。記述ファイルの内容は、以下のとおりです。

# make コマンドの処理を定義している記述ファイル 

FILES = Makefile defs.h main.c doname.c misc.c ¥ 
        	files.c dosys.c gram.y 
OBJECTS = main.o doname.o misc.o files.o dosys.o gram.o 
LIBES = 
LINT = lint -p 
CFLAGS = -O 
LP = lp 

make: $(OBJECTS) 
        	$(CC) $(CFLAGS) -o make $(OBJECTS) $(LIBES) 
        	@size make 

$(OBJECTS): defs.h 

cleanup: 
        	-rm *.o gram.c 
        	-du 

install: 
        	make 
        	@size make /usr/bin/make 
        	cp make /usr/bin/make && rm make 

lint: dosys.c doname.c files.c main.c misc.c gram.c 
        	$(LINT) dosys.c doname.c files.c main.c misc.c gram.c 

        	# print files that are out-of-date 
        	# with respect to "print" file.  

print: $(FILES) 
        	pr $? | $(LP) 
        	touch print

make プログラムは、各コマンドを実行する前にコマンドを表示します。

ソースファイルと記述ファイルだけが含まれるディレクトリで make コマンドを実行すると、以下のように出力されます。

cc -O -c main.c 
cc -O -c doname.c 
cc -O -c misc.c 
cc -O -c files.c 
cc -O -c dosys.c 
yacc gram.y 
mv y.tab.c gram.c 
cc -O -c gram.c 
cc -o make main.o doname.o misc.o files.o dosys.o gram.o ¥
 13188 + 3348 + 3044 = 19580

最後の行は、size make コマンドの結果表示されたものです。コマンド行そのものの出力は、記述ファイル中で記号 @ が記述されているため、表示されません。

記述ファイルと置換

記述ファイルでよく使用する要素について説明しています。

コメント

# 記号は、コメントの開始を示し、同一行にあるこの記号以降の文字はすべて無視されます。空白行および # で始まる行は無視されます。

継続行

コメントでない行が長すぎる場合は、¥ 記号を行の最後に挿入すると、行を継続できます。行の最後の文字が ¥ の場合は、その ¥ 記号とそれ以降の空白文字およびタブが 1 つの空白に置換されます。

マクロ定義

マクロ定義は、= という記号を続けて記述する識別子です。マクロ定義の先頭に、コロン (:) またはタブを使用することはできません。= の左側に指定したマクロ名 (文字および数字の文字列この名前の後の空白文字およびタブは削除されます) には、= の右側に指定した文字列 (この文字列の前にある空白文字およびタブは削除されます) が割り当てられます。以下に、有効なマクロ定義の例を示します。

2 = xyz 
abc = -ll -ly -lm 
LIBES =

最後の定義は、LIBES に NULL 文字列を割り当てます。明示的に値が割り当てられていないマクロは、値が NULL 文字列になります。ただし、make 規則で明示的に定義されているマクロがあります (「内部規則」を参照してください)。

一般的な形式

記述ファイルのエントリの通常の形式は、以下のとおりです。

target1 [target2 ...] :[:] [dependent1 ...] [; commands] 
[# ...] [ ¥t commands] [# ...] 
 .  .  .  

角括弧内の項目は省略可能です。ターゲットおよび依存関係は、文字、数字、ピリオド、スラッシュで構成される文字列です。*? などのシェルのメタキャラクタは、コマンドの評価時に展開されます。コマンドは、依存関係の行のセミコロンの後、または依存関係の行の直後に続く、タブ (前述の例では ¥t と表示されています) で始まる行に記述できます。コマンドは、# 以外の任意の文字 (二重引用符で囲んだ文字列では # を使用できます) で構成される文字列です。

依存関係について

依存関係の行には、単一コロンまたは二重コロンが含まれます。ターゲット名は複数の依存関係の行に記述できますが、すべての行が同一の種類 (コロンの数が同一) である必要があります。単一コロンの行では、コマンド文字列を 1 つの依存関係の行にのみ関連付けることができます。いずれかの行で記述されている依存ファイルよりもターゲットが古く、コマンド (セミコロンまたはタブの後の NULL 文字列も含む) が指定されている場合は、そのコマンドが実行されます。それ以外の場合は、デフォルトの規則を呼び出すことができます。二重コロンの行では、コマンドを複数の依存関係の行に関連付けることができます。ターゲットが特定の行で記述されている依存ファイルよりも古い場合は、対応するコマンドが実行されます。組み込み規則も実行できます。二重コロンの行は、ターゲットがアーカイブライブラリである、アーカイブ形式のファイルを更新する際に特に便利です (「アーカイブライブラリ」で例を示しています)。

実行可能なコマンド

ターゲットを作成する必要がある場合は、一連のコマンドを実行します。通常は、各コマンド行が表示され、マクロ置換の後に、コマンドごとに別々のシェル呼び出しに渡されます。サイレントモード (make コマンドの -s オプション) の場合、または記述ファイル中のコマンド行が @ 記号で始まる場合には、コマンドは表示されません。コマンドがゼロ以外のエラーコードを返してエラーを示している場合は、通常 make は停止します。make コマンドで -i フラグが指定されているか、.IGNORE というターゲット名が記述ファイルに記述されているか、記述ファイル中のコマンド文字列がハイフン (-) で始まる場合には、エラーは無視されます。プログラムが無意味な状態を返すことがわかっている場合は、そのプログラムを呼び出す行の前にハイフンを付けるのが適しています。各コマンド行は、コマンドごとに呼び出されたシェルに渡されるため、 1 つのシェルプロセスでのみ有効なコマンド (cd および shell の制御コマンドなど) を使用する場合は注意してください。これらのコマンドの結果は、次の行が実行される前に削除されます。

コマンドを実行する前に、内部的に管理されているマクロが設定されます。$@ マクロは、現在のターゲットの完全なターゲット名に設定されます。$@ マクロは、明示的に名前が指定された依存関係に対してのみ評価されます。$? マクロは、ターゲットよりも新しい名前の文字列に設定されます。$? マクロは、メークファイルに記述されている明示的な規則の評価時に評価されます。コマンドが暗黙の規則により生成された場合は、$< マクロはその動作を発生させた関連ファイルの名前になります。$* マクロは、現在のファイルと依存ファイルの名前に共通する接頭辞です。ファイルを作成する際に、明示的に指定されたコマンドや関連する組み込み規則がない場合は、.DEFAULT という名前に対応するコマンドが実行されます。.DEFAULT という名前がない場合は、make はメッセージを表示して停止します。

また、記述ファイルでも、関連するマクロ ($(@D)$(@F)$(*D)$(*F)$(<D)$(<F)) を使用できます。

$*、$@、$< の拡張

内部的に生成されるマクロである $*$@$< は、現在のターゲットおよび最新でないターゲットを示す総称として使用します。このリストには、関連マクロ ($(@D)$(@F)$(*D)$(*F)$(<D)$(<F)) が追加されています。D は、1 文字のマクロのディレクトリ部分を示します。F は、1 文字のマクロのファイル名部分を示します。これらの追加マクロは、メークファイルの階層を構築する際に便利です。これらのマクロを使用してディレクトリ名にアクセスし、シェルの cd コマンドを使用できます。コマンド例を以下に示します。

cd $(<D); $(MAKE) $(<F)

出力の変換

マクロの値は、評価時に置換されます。通常の形式を以下に示します。ここで、角括弧で囲んだ文字列は省略可能です。

$(macro[:string1=[string2]])

置換の指定がなくマクロ名が 1 文字の場合は、括弧を省略できます。置換文字列がある場合は、マクロの値は、空白文字、タブ、復帰改行で区切った複数の語が連なったものとして扱われます。したがって、語が string1 で終わる場合は、string1string2 に置換されます (string2 がない場合は NULL 文字列に置換されます)。

このような置換が行われるのは、make が接尾辞を区別するためです。この機能は、アーカイブライブラリを管理する際に便利です。最新でないメンバーを蓄積して、すべての C プログラム (接尾辞が .c のファイル) を処理することができるシェルスクリプトを記述するだけで管理を行うことができます。以下の例は、アーカイブライブラリの管理用に make の実行を最適化します。

$(LIB): $(LIB)(a.o) $(LIB)(b.o) $(LIB)(c.o) 
        	$(CC) -c $(CFLAGS) $(?:.o=.c) 
        	$(AR) $(ARFLAGS) $(LIB) $? 
        	rm $?

アーカイブライブラリを定義するソースファイルの種類 (接尾辞) ごとに、上記の形式の依存関係が必要です。これによって、make が生成するさまざまな情報をより汎用的に使用することができるようになります。

再帰的なメークファイル

make では、環境変数と再帰的呼び出しの機能があります。シェルのコマンド行で $(MAKE) が指定されている場合は、その行は -n フラグが設定されている場合でも実行されます。-n フラグは、(MAKEFLAGS 変数により) すべての make の呼び出しで適用されるため、実行されるのは make コマンドそのものだけです。この機能は、階層になったメークファイルでソフトウェアのサブシステムが記述されている場合に便利です。テストの目的で make -n を実行できます。この場合は、下位で呼び出された make の出力など、実行された処理がすべて出力されます。

接尾辞および変換の規則

make は、内部の規則テーブルを使用して、ある接尾辞を持つファイルを別の接尾辞を持つファイルに変換する方法を特定します。-r フラグが make コマンド行で使用されている場合は、内部の規則テーブルは使用されません。

接尾辞のリストは、実際には .SUFFIXES という名前の依存関係リストです。make は、リストに含まれるいずれかの接尾辞を持つファイルを検索します。ファイルが見つかった場合は、make はそのファイルを別の接尾辞のファイルに変換します。変換規則の名前は、変換前と変換後の接尾辞を連結したものになります。したがって、.r ファイルを .o ファイルに変換する規則の名前は .r.o になります。規則があり、コマンドが記述ファイルで明示的に記述されていない場合は、.r.o の規則のコマンドが使用されます。コマンドがいずれかの接尾辞の規則を使用して生成される場合は、作成されるファイル名のベース名 (ファイル名から接尾辞を除いたもの) が $* マクロに割り当てられます。また、$< マクロは、変換の原因となった依存ファイルの名前になります。

接尾辞のリストは左から右に検査されるため、リストの順序は重要です。ファイルおよび対応する規則の両方を持つ最初の名前が使用されます。新しい名前を追加する場合は、.SUFFIXES のエントリを記述ファイルに追加します。依存関係は、通常のリストに追加されます。

依存関係を指定しない .SUFFIXES 行によって、現在の接尾辞リストを削除できます。名前の順序を変更する場合は、現在のリストを削除する必要があります。

暗黙の規則

make は、接尾辞のテーブルおよび複数の変換規則を使用して、デフォルトの依存関係およびコマンドを指定します。デフォルトの接尾辞のリストを、デフォルトの順序で以下に示します。

.o

オブジェクトファイル

.c

C ソースファイル

.c‾

SCCS C ソースファイル

.y

yacc C ソースの文法

.y‾

SCCS yacc C ソースの文法

.l

lex C ソースの文法

.l〜

SCCS lex C ソースの文法

.s

アセンブラソースファイル

.s‾

SCCS アセンブラソースファイル

.sh

シェルファイル

.sh‾

SCCS シェルファイル

.h

ヘッダーファイル

.h‾

SCCS ヘッダーファイル

.f

FORTRAN ソースファイル

.f‾

SCCS FORTRAN ソースファイル

.C

C++ ソースファイル

.C‾

SCCS C++ ソースファイル

.Y

yacc C++ ソースの文法

.Y‾

SCCS yacc C++ ソースの文法

.L

lex C++ ソースの文法

.L‾

SCCS lex C++ ソースの文法

図 A-1 は、デフォルトの変換過程を示しています。同じ接尾辞に接続する過程が 2 とおりある場合、2 つの段階を経る過程は、中間ファイルがある場合または記述ファイル中に指定されている場合にのみ使用されます。

図 A-1 デフォルトの変換過程のまとめ

Graphic

ファイル x.o が必要で、x.c が記述ファイル中またはディレクトリにある場合は、x.o がコンパイルされます。x.l というファイルもある場合は、そのソースファイルは lex で処理されてからコンパイルされます。ただし、x.c はないが x.l はある場合は、図 A-1 で示すように、make は C の中間ファイルを破棄して直接リンクを使用します。

マクロ名がわかっていれば、デフォルトで使用されるコンパイラ名またはコンパイラを呼び出す際に引数として使用するフラグを変更できます。コンパイラ名を示すマクロは、ASCCC++CF77YACCLEX です。以下のコマンドは、通常の C コンパイラの代わりに newcc コマンドを使用するように指定します。

$ make CC=newcc

CFLAGSYFLAGSLFLAGSASFLAGSFFLAGSC++FLAGS の各マクロは、フラグを使用して実行できます。以下に例を示します。

$ make CFLAGS=-g

このコマンドは、cc コマンドがデバッグ情報を取り込むようにします。

アーカイブライブラリ

make プログラムには、アーカイブライブラリへのインタフェースがあります。ユーザーは、以下のようにライブラリのメンバーを指定できます。

projlib(object.o)

または

projlib((entry_pt))

2 番目の方法は、実際には、ライブラリ内のオブジェクトファイルのエントリポイントを指定します (make は、ライブラリを検索し、エントリポイントを特定し、正しいオブジェクトファイル名に変換します)。

この方法を使用してアーカイブライブラリを管理するには、以下のようなメークファイルが必要です。

projlib:: projlib(pfile1.o) 
        	$(CC) -c $(CFLAGS) pfile1.c 
        	$(AR) $(ARFLAGS) projlib pfile1.o 
        	rm pfile1.o 
projlib:: projlib(pfile2.o) 
        	$(CC) -c $(CFLAGS) pfile2.c 
        	$(AR) $(ARFLAGS) projlib pfile2.o 
        	rm pfile2.o

メークファイルでは、オブジェクトごとに projlib 行を記述する必要があります。これには手間がかかるうえ、誤りが発生しやすくなります。ほとんどの場合、C ファイルをライブラリに追加するコマンド文字列は各ファイルで共通していて、ファイル名だけが異なります。

make コマンドは、ライブラリを構築する規則も使用できます。使用するには、.a という接尾辞を使用します。たとえば、.c.a の規則は、C ソースファイルをコンパイルしてライブラリに追加し、.o ファイルを削除します。同様に、.y.a.s.a.l.a の各規則は、それぞれ yaccアセンブラlex のファイルを再構築します。内部定義されたアーカイブの規則は、.c.a.c‾.a.f.a.f‾.a.s‾.a です (チルド () については後述)。ユーザーは、記述ファイルで他の必要な規則を定義できます。

したがって、前述のメンバーが 2 つのライブラリは、以下のメークファイルで管理できます。

projlib: projlib(pfile1.o) projlib(pfile2.o) 
        	@echo projlib up-to-date.

この例では、内部の規則を使用して、前述のライブラリ管理を行います。実際の .c.a の規則は、以下のとおりです。

c.a:
        	$(CC) -c $(CFLAGS) $< 
        	$(AR) $(ARFLAGS) $@ $*.o 
        	rm -f $*.o

したがって、$@ マクロは .a ターゲット (projlib) です。$< および $* マクロは、古い C ファイルと接尾辞を除いたファイル名 (pfile1.c および pfile1) に設定されます。前述の規則での $< マクロ は、$*.c に変更できます。

make が構築を行う際の処理の詳細について説明しています。

projlib: projlib(pfile1.o) 
        	@echo projlib up-to-date

ライブラリ内のオブジェクトが pfile1.c よりも古くなっていて、pfile1.o ファイルは存在しないとします。

  1. make projlib を実行します。

  2. make projlib を使用する前に、projlib の依存関係を検査します。

  3. projlib(pfile1.o)projlib の依存関係であるので、これを生成する必要があります。

  4. projlib(pfile1.o) を生成する前に、projlib(pfile1.o) の依存関係を検査します (依存関係は存在していません)。

  5. 内部の規則を使用して、projlib(pfile1.o) を作成します (明示的な規則はありません)。projlib(pfile1.o) では、ターゲットの接尾辞を .a に指定する括弧が名前に含まれています。projlib のライブラリ名の最後には、明示的な .a がありません。括弧は、.a という接尾辞を示します。この意味で、.amake に組み込まれています。

  6. projlib(pfile.o) という名前を projlib および pfile1.o に分割します。$@(projlib) および $*(pfile1) という 2 つのマクロを定義します。

  7. .X.a という規則と、$*.X というファイルを検索します。.SUFFIXES リストにおいてこれらの条件を最初に満たす .X.c であるため、規則は .c.a、ファイルは pfile1.c になります。$<pfile1.c に設定し、規則を実行します。実際には、makepfile1.c をコンパイルする必要があります。

  8. ライブラリが更新されます。projlib: の依存関係に対応する以下のコマンドを実行します。

    @echo projlib up-to-date

pfile1.o が依存関係を持つには、以下の構文が必要です。

projlib(pfile1.o): $(INCDIR)/stdio.h pfile1.c

また、この構文を使用する際には、アーカイブのメンバー名を参照するマクロも使用できます。$% マクロは、$@ が評価されるごとに評価されます。現在のアーカイブのメンバーがない場合は、$% は NULL になります。アーカイブのメンバーがある場合は、$% は括弧内の式になります。

ソースコード管理システム (SCCS) ファイル名

make の構文では、接頭辞を直接参照できません。オペレーティングシステム上のほとんどの種類のファイルにおいて、ほぼすべてのユーザーがファイルを識別するために接尾辞を使用するため、問題はありません。SCCS ファイルは例外で、完全なパス名のファイル名部分の先頭に s. が付きます。

make が接頭辞 s. に簡単にアクセスできるようにするため、 記号が SCCS ファイルの識別子として使用されます。したがって、.c‾.o は、SCCS C ソースファイルをオブジェクトファイルに変換する規則を示します。厳密には、内部の規則は以下のようになっています。

$(GET) $(GFLAGS) $< $(CC) $(CFLAGS) -c $*.c rm -f $*.c

したがって、接尾辞に付けられた は、ドットからチルド記号の直前の文字までが示す実際の接尾辞を持つ SCCS ファイル名を検索します。

以下の SCCS 接尾辞が内部定義されています。

.c‾.sh‾.C‾
.y‾.h‾.Y‾
.l‾.f‾.L‾
.s‾

SCCS の変換について、以下の規則が内部定義されています。

.c‾:.s‾.s:.c‾:
.c‾.c:.s‾.a:.C‾.C:
.c‾.a:.s‾.o:.C‾.a:
.c‾.o:.sh‾:.C‾.o:
.y‾.c:.sh‾.sh:.Y‾.C:
.y‾.o:.h‾.h:.Y‾.o:
.y‾.y:.f‾:.Y‾.Y:
.l‾.c.f‾.f:.L‾.C:
.l‾.o:.f‾.a:.L‾.o:
.l‾.l:.f‾.o:.L‾.L:
.s,:

ユーザーは、このほかの規則および接尾辞を任意に定義できます。 は、SCCS ファイル名の形式を示します。

空接尾辞

多くのプログラムは、1 つのソースファイルで構成されます。make は、このようなプログラムを空接尾辞の規則により処理します。オペレーティングシステムのプログラムである cat を管理するため、以下の規則をメークファイルに記述する必要があります。

$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $< 

実際には、.c: という規則は、内部定義されているため、メークファイルは必要ありません。$make cat dd echo date を入力するだけで (これらはすべてオペレーティングシステムの単一ファイルのプログラムです)、.c: 規則に対応する前述のシェルコマンド行により 4 つの C ソースファイルが渡されます。内部定義されている単一の接尾辞の規則は、以下のとおりです。

.c:.sh:.f,:
.c,:.sh,:.C:
.s:.f:.C,:
.s,:

ユーザーは、このほかの規則をメークファイルに追加できます。

組み込みファイル

make には、C のプリプロセッサの #include 指令と同様の機能があります。include という文字列に続いて空白文字またはタブがメークファイルの行の最初に記述されている場合は、その行の残りの部分はファイル名とみなされます。実行中の make は、そのファイルを読み取ります。ファイル名にはマクロを使用できます。インクルードされたファイルの記述子はスタックされ、最高 16 レベルまでサポートされています。

SCCS メークファイル

make は、SCCS が管理するメークファイルにアクセスできます。つまり、s.makefile または s.Makefile がある場合に make を入力すると、make はファイルに対して get を実行し、そのファイルを読み込んで削除します。

動的な依存関係のパラメータ

動的な依存関係のパラメータは、メークファイルの依存関係の行で使用した場合にのみ有効です。$$@ は、: 記号の左側にある文字列 ($@) を示します。また、$$(@F) という形式もあり、$@ のファイル部分にアクセスできます。以下に例を示します。

cat: $$@.c

この例では、依存関係は、実行時に文字列 cat.c に変換されます。これは、ソースファイルがそれぞれ 1 つだけの実行可能ファイルを多数構築する際に便利です。たとえば、オペレーティングシステムのソフトウェアコマンドのディレクトリには、以下のようなメークファイルを作成できます。

CMDS = cat dd echo date cmp comm chown 

$(CMDS): $$@.c 
        	$(CC) $(CFLAGS) $? -o $@

これは、すべての 単一ファイルのプログラムのサブセットです。複数のファイルから成るプログラムでは、ディレクトリを割り当てて、プログラムごとにメークファイルを作成します。特別のコンパイル手順を持つファイルでは、メークファイルに特別なエントリが必要です。

もう 1 つの便利な依存関係のパラメータとして、$$(@F) があります。これは、$$@ のファイル名部分を示します。この場合も、実行時に評価されます。/usr/src/head ディレクトリにある makefile を使用して /usr/include ディレクトリを管理するなどの場合に便利です。この場合は、/usr/src/head/makefile は以下のようになります。

INCDIR = /usr/include 

INCLUDES = ¥ 
        	$(INCDIR)/stdio.h ¥ 
        	$(INCDIR)/pwd.h ¥ 
        	$(INCDIR)/dir.h ¥ 
        	$(INCDIR)/a.out.h 

$(INCLUDES): $$(@F) 
        	cp $? $@ 
        	chmod 0444 $@

このメークファイルは、/usr/src/head にある前述のファイルのいずれかが更新されるたびに、/usr/include ディレクトリを完全に管理します。

コマンドの使用法

make コマンド

make コマンドでは、引数として、マクロ定義、オプション、記述ファイル名、ターゲットファイル名を以下の形式で指定します。

$ make [ options ] [ macro definitions and targets ]

以下に、各引数の意味を説明します。

まず、すべてのマクロ定義の引数 (= 記号が組み込まれた引数) が解析され、値が割り当てられます。コマンド行のマクロは、記述ファイル中の対応する定義を無効にします。次にオプションの引数が調べられます。使用可能なオプションは、以下のとおりです。

-i

呼び出したコマンドが返すエラーコードを無視します。.IGNORE というターゲット名が記述ファイルに記述されている場合に、このモードになります。

-s

サイレントモードです。実行前にコマンド行を出力しません。.SILENT というターゲット名が記述ファイルに記述されている場合にも、このモードになります。

-r

組み込み規則を使用しません。

-n

実行を行わないモードです。コマンドは出力しますが、コマンドを実際には実行しません。@ 記号で始まる行も出力されます。

-t

通常のコマンドは実行せずに、ターゲットファイルに touch コマンドを実行します (ファイルが更新されます)。

-q

make コマンドは、ターゲットファイルが最新かどうかにより、ゼロまたはゼロ以外の状態コードを返します。

-p

すべてのマクロ定義およびターゲットの記述を出力します。

-k

障害が発生した場合に現在のエントリでの処理を中止しますが、現在のエントリに依存しない他の分岐では処理を続行します。

-e

メークファイルでのマクロの割り当てよりも環境変数を優先します。

-f

引数は、記述ファイル名とみなされます。ファイル名が - の場合は、標準入力を示します。-f に引数がない場合は、現在のディレクトリにある makefileMakefiles.makefiles.Makefile のいずれかの名前のファイルが読み込まれます。記述ファイルの内容 (ある場合) は、組み込み規則よりも優先されます。以下の 2 つのターゲット名は、フラグと同様に評価されます。

.DEFAULT

ファイルを作成する際に明示的なコマンドまたは関連する組み込み規則がない場合、.DEFAULT という名前があればそれに対応するコマンドが実行されます。

.PRECIOUS

Quit または Interrupt が実行された場合にこのターゲットの依存関係は削除されません。

その他の引数は、作成するターゲット名とみなされ、引数は左から右の順序で処理されます。他に引数がない場合は、記述ファイルに記述されている最初の文字が . 記号でない名前が使用されます。

環境変数

環境変数は、make の実行ごとに読み取られ、マクロ定義に追加されます。これを正しく実行するためには、優先度が重要です。以下では、make と環境変数との相互作用を説明しています。MAKEFLAGS というマクロは、make が管理しています。このマクロは、入力フラグ引数を (マイナス記号を除いて) 連結した文字列として定義されます。このマクロはエクスポートされるため、make の再帰的呼び出しでアクセスできます。メークファイルでのコマンド行のフラグおよび割り当てにより、MAKEFLAGS が更新されます。したがって、make による環境変数の処理を理解するには、MAKEFLAGS マクロ (環境変数) を考慮する必要があります。

make の実行時に、make は以下の順序でマクロ定義を割り当てます。

  1. MAKEFLAGS 環境変数を読み取ります。この環境変数が存在しない、または NULL の場合は、make の内部変数の MAKEFLAGS が NULL 文字列に設定されます。それ以外の場合は、MAKEFLAGS に含まれる各文字が入力フラグ引数とみなされて処理されます (ただし、-f-p-r フラグを除きます)。

  2. マクロ定義の内部リストを読み取ります。

  3. 環境変数を読み取ります。環境変数はマクロ定義として扱われ、(シェルにおいて) エクスポート済みとして処理されます。

  4. メークファイルを読み取ります。メークファイルでの割り当ては、環境変数よりも優先されます。これは、メークファイルが読み込まれて実行されたときに処理内容がわかるようにするためです。つまり、-e フラグが使用されている場合を除き、記述ファイルでの記述通りに処理が実行されます。-e は入力フラグ引数で、環境変数がメークファイルでのマクロの割り当てよりも優先されるように指定します。したがって、-e を使用すると、環境変数がメークファイルでの定義よりも優先されます。また、MAKEFLAGS が割り当てられている場合は、それが環境変数よりも優先されます。これは、現在のメークファイルから make をさらに呼び出す場合に便利です。

    以下に、割り当ての優先度を低いものから高いものの順に示します。

  1. 内部定義

  2. 環境変数

  3. メークファイル

  4. コマンド行

    -e フラグが有効な場合は、優先度は以下のようになります。

  1. 内部定義

  2. メークファイル

  3. 環境変数

  4. コマンド行

    通常はこの優先度によって、パラメータを動的に定義するメークファイルを定義することができます。

推奨および警告

最も頻繁に発生する問題は、make 固有の依存関係の意味が原因となっています。ファイル x.c に以下の行が記述されているとします。

#include "defs.h"

この場合は、オブジェクトファイル x.o は、defs.h に依存しますが、ソースファイル x.c は依存しません。defs.h が変更されると、x.c は変更されませんが、x.o は再作成する必要があります。

このとき、make が行う処理を確認するには、-n オプションが非常に便利です。

$ make -n

このコマンドは、make が実行するコマンドを、実際には実行せずに出力だけを行います。ファイルの変更の影響が少ない場合 (組み込みファイルにコメントを追加する場合など) は、-t (touch) オプションを使用すると時間を節約できます。make は、不要な再コンパイルを何度も実行する代わりに、影響のあるファイルの変更時間だけを更新します。

$ make -ts

したがって、このコマンド (警告を表示しないで touch を実行する) は、関連するファイルを最新にします。このコマンドを実行すると、make の処理が無効になり以前の関係が破棄されるため、使用する際は十分に注意してください。

内部規則

make が使用する標準の内部規則を以下に示します。

make が認識する接尾辞は、以下のとおりです。

.o.c.c‾.y.y‾.l.l‾.s.s‾.sh.sh‾
.h.h‾.f.f‾.C.C‾.Y.Y‾.L.L‾

定義済みマクロを以下に示します。

AR=ar ARFLAGS=-rv AS=as ASFLAGS= BUILD=build CC=cc CFLAGS= -0 C++C=CC C++FLAGS= -0 F77=f77 FFLAGS= -0 GET=get GFLAGS= LEX=lex LFLAGS= LD=ld LDFLAGS= MAKE=make MAKEFLAGS= YACC=yacc YFLAGS= $=$

特別な規則

この節では、接尾辞が 1 つまたは 2 つの特別な make の規則について説明しています。

markfile.o : markfile 
     A=@; echo "static char _sccsid[]=¥042`grep $$A'(#)' ¥ 
     markfile`¥042;" > markfile.c 
     $(CC) -c markfile.c 
     rm -f markfile.c

接尾辞が 1 つの規則

以下に、接尾辞が 1 つの規則を示します。

.c:
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< 
.c‾:
 	$(GET) $(GFLAGS) $<
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $* $*.c
 	rm -f $*.c 
.s:
 	$(AS) $(AFLAGS) -o $@ $< 
.s‾:
 	$(GET) $(GFLAGS) $<
 	$(AS) $(AFLAGS) -o $@ $*.s
 	rm -f $*.s 
.sh:
 	cp $< $@; chmod 0777 $@ 
.sh‾:
 $(GET) $(GFLAGS) $<
 	cp $*.sh $*; chmod 0777 $@
 	rm -f $*.sh 
.f:
 	$(F77) $(FFLAGS) $(LDFLAGS) -o $@ $< 
.f‾:
 	$(GET) $(GFLAGS) $<
 	$(F77) $(FFLAGS) -o $@ $(LDFLAGS) $*.f
 	rm -f $*.f 
.C‾:
 	$(GET) $(GFLAGS) $<
 	$(C++C) $(C++FLAGS) -o $@ $(LDFLAGS) $*.C
 	rm -f $*.C 
.C:
 	$(C++C) $(C++FLAGS) -o $@ $(LDFLAGS) $< 

接尾辞が 2 つの規則

以下に、接尾辞が 2 つの規則を示します。

.c‾.c.y‾.y.l‾.l.s‾.s.sh‾.sh
.h‾.h.f‾.f.C‾.C .Y‾.Y.L‾.L

$(GET) $(GFLAGS) $< 
.c.a:
 	$(CC) -c $(CFLAGS) $<
 	$(AR) $(ARFLAGS) $@ $*.o
 	rm -f $*.o
c.a‾:
 	$(GET) $(GFLAGS) $<
 	$(CC) -c $(CFLAGS) $*.c
 	$(AR) $(ARFLAGS) $@ $*.o
 	rm -f $*.[co] 
.c.o:
 	$(CC) $(CFLAGS) -c $<
.c‾.o:
 	$(GET) $(GFLAGS) $<
 	$(CC) $(CFLAGS) -c $*.c
 	rm -f $*.c 
.y.c:
	$(YACC) $(YFLAGS) $<
 	mv y.tab.c $@ 
.y‾.c:
	$(GET) $(GFLAGS) $<
 	$(YACC) $(YFLAGS) $*.y
 	mv y.tab.c $*.c
 	rm -f $*.y 
.y.o:
 	$(YACC) $(YFLAGS) $<
 	$(CC) $(CFLAGS) -c y.tab.c
 	rm -f y.tab.c
 	mv y.tab.o $@ 
.y‾.o:
 	$(GET) $(GFLAGS) $<
 	$(YACC) $(YFLAGS) $*.y
 	$(CC) $(CFLAGS) -c y.tab.c
 	rm -f y.tab.c $*.y
 	mv y.tab.o $*.o 
.l.c:
 	$(LEX) $(LFLAGS) $<
 	mv lex.yy.c $@ 
.l‾.c:
 	$(GET) $(GFLAGS) $<
 	$(LEX) $(LFLAGS) $*.l
 	mv lex.yy.c $@
 	rm -f $*.l 
.l.o:
 	$(LEX) $(LFLAGS) $<
 	$(CC) $(CFLAGS) -c lex.yy.c
 	rm -f lex.yy.c
 	mv lex.yy.o $@ 
.l‾.o:
 	$(GET) $(GFLAGS) $<
 	$(LEX) $(LFLAGS) $*.l
 	$(CC) $(CFLAGS) -c lex.yy.c
 	rm -f lex.yy.c $*.l
 	mv lex.yy.o $@ 
.s.a:
 	$(AS) $(ASFLAGS) -o $*.o $*.s
 	$(AR) $(ARFLAGS) $@ $*.o 
.s‾.a:
 	$(GET) $(GFLAGS) $<
 	$(AS) $(ASFLAGS) -o $*.o $*.s
 	$(AR) $(ARFLAGS) $@ $*.o
 	rm -f $*.[so] 
.s.o:
 	$(AS) $(ASFLAGS) -o $@ $< 
.s‾.o:
 	$(GET) $(GFLAGS) $<
 	$(AS) $(ASFLAGS) -o $*.o $*.s
 	rm -f $*.s 
.f.a:
 	$(F77) $(FFLAGS) -c $*.f
 	$(AR) $(ARFLAGS) $@ $*.o
 	rm -f $*.o 
.f‾.a:
 	$(GET) $(GFLAGS) $<
 	$(F77) $(FFLAGS) -c $*.f
 	$(AR) $(ARFLAGS) $@ $*.o
 	rm -f $*.[fo] 
.f.o:
 	$(F77) $(FFLAGS) -c $*.f 
.f‾.o:
 	$(GET) $(GFLAGS) $<
 	$(F77) $(FFLAGS) -c $*.f
 	rm -f $*.f 
.C.a:
 	$(C++C) $(C++FLAGS) -c $<
 	$(AR) $(ARFLAGS) $@ $*.o
 	rm -f $*.o 
.C‾.a:
 	$(GET) $(GFLAGS) $<
 	$(C++C) $(C++FLAGS) -c $*.C
 	$(AR) $(ARFLAGS) $@ $*.o
 	rm -f $*.[Co] 
.C.o:
 	$(C++C) $(C++FLAGS) -c $< 
.C‾.o:
 	$(GET) $(GFLAGS) $<
 	$(C++C) $(C++FLAGS) -c $*.C
 	rm -f $*.C 
.Y.C:
 	$(YACC) $(YFLAGS) $<
 	mv y.tab.c $@ 
.Y‾.C:
 	$(GET) $(GFLAGS) $<
 	$(YACC) $(YFLAGS) $*.Y
 	mv y.tab.c $*.C
 	rm -f $*.Y 
.Y.o
 	$(YACC) $(YFLAGS) $<
 	$(C++C) $(C++FLAGS) -c y.tab.c
 	rm -f y.tab.c
 	mv y.tab.o $@ 
.Y‾.o:
 	$(GET) $(GFLAGS) $<
 	$(YACC) $(YFLAGS) $*.Y
 	$(C++C) $(C++FLAGS) -c y.tab.c
 	rm -f y.tab.c $*.Y
 	mv y.tab.o $*.o 
.L.C:
 	$(LEX) $(LFLAGS) $<
 	mv lex.yy.c $@ 
.L‾.C:
 	$(GET) $(GFLAGS) $<
 	$(LEX) $(LFLAGS) $*.L
 	mv lex.yy.c $@
 	rm -f $*.L 
.L.o:
 	$(LEX) $(LFLAGS) $<
 	$(C++C) $(C++FLAGS) -c lex.yy.c
 	rm -f lex.yy.c
 	mv lex.yy.o $@ 
.L‾.o:
 	$(GET) $(GFLAGS) $<
 	$(LEX) $(LFLAGS) $*.L
 	$(C++C) $(C++FLAGS) -c lex.yy.c
 	rm -f lex.yy.c $*.L
 	mv lex.yy.o $@