Sun WorkShop 入門

メークファイルの制限

複数のターゲットを同時に構築する場合に、メークファイルには多少の制限が伴います。たとえば、暗黙の順序に依存しているメークファイルは、並列構築を行うと正しく動作しない可能性があります。また同じファイルの修正を行うターゲットがあり、これらファイルが 2 つの異なるターゲットで同時に変更されると、メークファイル中のターゲットが誤動作する場合があります。この項では、こうした問題を取り上げて説明します。

依存関係リスト

ターゲットの並列構築を行う場合、まず依存関係リストが正確であることが重要なポイントになります。たとえば、 2 つの実行可能ファイルが同じオブジェクトファイルを共有しているときに、一方のファイルでしか依存関係が指定されていないと、並列構築を行なった場合はエラーになる可能性があります。次のメークファイルの断片を考えてみます。


all: prog1 prog2 
prog1: prog1.o aux.o 
    $(LINK.c) prog1.o aux.o -o prog1 
prog2: prog2.o 
    $(LINK.c) prog2.o aux.o -o prog2 

直列的に構築を実行する場合は、ターゲット aux.oprog1 の依存対象として作成されるので、prog2 の構築時には更新されています。一方、並列的に構築を実行すると、prog2 のリンクが、aux.o の作成前に設定される可能性があります。この場合は、エラーになります。make.KEEP_STATE 機能で検出できる依存関係も一部ありますが、上のような関係はその対象にはなりません。

依存関係リストの明示的な順序

次に、暗黙の順序にもとづく依存関係がある場合は、問題がさらに複雑になるという例を示します。たとえば、あるシステムのヘッダーをすべて作成してから構築を開始する場合は、すべての要素がこの構築に依存していなければなりません。これに伴いメークファイルがさらに複雑になり、新しいターゲットをメークファイルに追加した場合などにエラーが起きる可能性が高くなります。この場合は、メークファイルの中で特殊な .WAIT ターゲットを指定して、暗黙の依存関係があることを明示することができます。dmake により依存関係リストの中に .WAIT ターゲットが検出されると、これまでの依存関係の処理を一時停止してから、以降の依存関係の処理を実行するようになります。 1 つの依存関係リストに、複数の .WAIT ターゲットを入れることもできます。次に、.WAIT を使用して、まずヘッダーを作成してから、以降の処理を続行する例を示します。


all: hdrs .WAIT libs functions 

.WAIT ターゲットに適用する空の規則をメークファイルに追加すれば、旧バージョンのメークファイルとの互換性も保持されます。

ファイルの同時修正

ターゲットの構築プロセスでは、同時に同じファイルに対して修正作業を行わないように注意する必要があります。この問題は、さまざまな形式で起きる可能性があります。一時ファイルを使用する新しい接尾辞の規則を定義する場合、一時ファイルの名前は、ターゲットごとに異なっていなければなりません。この場合は、$@$* の動的マクロを利用できます。たとえば、ソースファイルをコンパイルする前に .c ファイルに多少の修正を加える場合、.c.o の規則は次のように定義できます。


.c.o:
    awk -f modify.awk $*.c > $*.mod.c
    $(COMPILE.c) $*.mod.c -o $*.o
    $(RM) $*.mod.c

ライブラリの同時更新

並列処理に伴うもう 1 つの問題は、ライブラリを作成するときのデフォルトの規則です。すなわち、ライブラリという固定ファイルが変更される場合があるということです。本来適用されるべきでない .c.a 規則によって、dmake がそれぞれのオブジェクトファイルを生成し、このオブジェクトファイルをアーカイブする可能性があります。dmake が 2 つのオブジェクトファイルを同時にアーカイブすると、並列的に更新が行われてアーカイブファイルが壊れることがあります。


.c.a:
    $(COMPILE.c) -o $% $<
    $(AR) $(ARFLAGS) $@ $%
    $(RM) $%

この場合は、各オブジェクトファイルを作成して、構築が完了した後、すべてのオブジェクトファイルをアーカイブするようにします。正しい接尾辞の規則と対応するライブラリの規則は次のとおりです。


.c.a:
    $(COMPILE.c) -o $% $< lib.a: lib.a($(OBJECTS))
    $(AR) $(ARFLAGS) $(OBJECTS)
    $(RM) $(OBJECTS)

複数のターゲット

ファイルの同時更新の問題は、同じ規則を複数のターゲットに対して定義した場合にも起きます。たとえば、lex(1) を使用してプログラムとヘッダーの両方を作成する yacc(1) プログラムがあります。1 つの規則にもとづいて複数のターゲットファイルを作成する場合は、+ 記号を使用して各ファイルを 1 つのグループとして指定する必要があります。特に、並列構築の場合に、これが重要になります。


y.tab.c y.tab.h: parser.y
    $(YACC.y) parser.y

この規則は、次の 2 つの規則と等価です。


y.tab.c: parser.y
    $(YACC.y) parser.y
y.tab.h: parser.y
    $(YACC.y) parser.y

直列の make ではまず、y.tab.c を生成する最初の規則を使用して構築を行い、次に y.tab.h がすでに最新版になっているため構築の必要がないと判断します。一方、並列構築の場合、dmakeyaccy.tab.c の構築を完了する前に y.tab.h を検索し、y.tab.h が構築の必要があることを知り、最初の yacc を並行して別の yacc を開始します。いずれの yacc の呼び出しも同じファイル (y.tab.cy.tab.h) に書き込まれるので、これらのファイルの内容が不当になる可能性があります。正しい方法は、+ 構文を使用して、両方のターゲットを同じ規則に従って同時に作成するように指示することです。たとえば次のとおりです。


y.tab.c + 
y.tab.h: parser.y
    $(YACC.y) parser.y