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

記述ファイルと置換

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

コメント

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

継続行

コメントでない行が長すぎる場合は、¥ 記号を行の最後に挿入すると、行を継続できます。行の最後の文字が ¥ の場合は、その ¥ 記号とそれ以降の空白文字およびタブが 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 ディレクトリを完全に管理します。