この色の文字で書かれている部分は、
GNU make のみの機能です。
また、例は solaris でのみ確認済みです。
![]() |
![]() |
make は、大規模なプログラム開発において、 どの部分を再コンパイルしなければならないか、 あるいは再コンパイルの命令に関する問題を自動的に解決するユーティリティです。
make を使用するには、プログラムを構成するファイル間の依存関係を記述する
makefile を作らなければなりません。
makefile は、コンパイルやリンクを指示するためのルールを記述するファイルです。
ドット (.) 以外ではじまるはじめのターゲットをデフォルト・ゴールといい、 make が最終的に到達するゴールです。
make は、まず makefile を全部読んで、インクルードを処理し、 全ての変数と値を内部値としてもち、 暗黙および明示的なルール、 およびターゲットとその必要条件の依存状態のグラフを作ります。
ルールの定義は、以下のとおりです。
ターゲット: 前提条件 [TAB] コマンド
以下は、solaris の c に関する暗黙のルール(サフィックスルール) と変数定義部分です。
.c.o: $(COMPILE.c) $(OUTPUT_OPTION) $< .c.a: $(COMPILE.c) -o $% $< $(AR) $(ARFLAGS) $@ $% $(RM) $% .c~: $(GET) $(GFLAGS) -p $< > $*.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $*.c .c: $(LINK.c) -o $@ $< $(LDLIBS) ← ここまでが暗黙のルール COMPILE.c= $(CC) $(CFLAGS) $(CPPFLAGS) -c LINK.c= $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) ← 変数定義部
まず、簡単な例を示します。
CC = gcc OBJS = portstat.o portstat: $(OBJS) $(CC) -o portstat $(OBJS) -lsocket -lnsl portstat.o: portstat.c portstat.h clean: rm -f $(TAGS) *.o core
ozzy-45 04/24 2:03pm] make gcc -c portstat.c gcc -o portstat portstat.o -lsocket -lnsl
portstat.c から portstat.o を作る手順が書かれていませんが、 make の実行画面の1行目 (gcc -c portstat.c) は、 どのルールから導かれたのでしょうか? これは、暗黙のルールに従って作られたものです。
更に進化させて、可変部分を変数で定義しておけばいろいろな用途に再利用できます。
RM = rm -f LS = ls OS = SUN TAGS = portstat SRCS = $(TAGS).c OBJS = $(TAGS).o HDRS = CC_SUN = gcc LIBS_SUN = -lsocket -lnsl CC = $(CC_SUN) LDLIBS = $(LIBS_SUN) $(TAGS): $(OBJS): $(SRCS) $(HDRS) clean: $(RM) $(TAGS) $(OBJS) core $(LS)
ozzy-110 04/24 2:54pm] make gcc -o portstat portstat.c -lsocket -lnsl
makefile は、以下の4つの部分から構成されます。
項目 | 説 明 |
---|---|
暗黙のルール implicit rule |
ファイルの名前に基づいて、いつどのように変更するかを示すルールを作ります。 名前とは、ファイルにつける拡張子でサフィックスといい、 サフィックスの関係を記述します。 |
変数の定義 macro definition |
あとでテキストで代用できる変数のための文字列を定義します。 |
明示的ルール explicit rule |
1つ以上のファイルをいつどのように変更するかを示すルールのターゲットを作ります。
そしてターゲットが依存するファイルをリストし、 ターゲットの必要条件を呼び出します。 さらに、ターゲットを更新したり、生成する規則を指定します。 |
命令 directive |
make に実行させたいコマンド群を書きます。
|
すべてに共通することですが、# で始まる行はコメント行です。
また、バックスラッシュと改行で、複数行にわたって記述することができます。
通常 makefile で記述しなければならない多くのルールは、 いくつかのヘッダファイルに依存するオブジェクトの関係だけの場合がほとんどです。
例えば、main.c で #include "defs.h" があった場合、以下のように記述するだけです。
main.o: defs.h
defs.h が変更された場合はいつでも、 main.o は再構成されます。 それは、暗黙のルールが定義されているからです。
![]() |
![]() |
変数は、makefile の中で文字列の代理となる名前の定義です。
(make のバージョンによっては、変数はマクロと呼ばれます)
makefile 内の変数は、読み込んだ時展開されます。
変数は、いろいろなものを代用することができます。
例えば、ファイル名のリスト、コンパイラに渡すオプション、実行するプログラム、
ソースファイルを探すディレクトリ、出力するディレクトリなどです。
変数は、英文字ではじまり数字、アンダースコアを含む文字列で、 大文字、小文字を区別しますが、伝統的に大文字を使います。
いくつかの変数(通常 $ と特殊文字1つ)は、 自動変数と呼ばれ特別な用途に使われます。
変数の定義形式は以下の2とおりです。
変数名 = 定義の文字列 include マクロ定義ファイル
これらの変数は、再帰的に展開されます。
値が長い場合は、バックスラッシュを入れることで、
次行に継続でき、行数に制限はありません。
値を設定しない変数は、空の文字列が入っています。 環境変数の優先順位は、以下のとおりです。
変数の値を参照するには、ドル記号に続けて変数名を括弧または中括弧で囲みます。
例えば、変数 CC の値は、$(CC) または ${CC} です。 $ 自身は、$$ と書くことで実現できます。
ここでは、より柔軟な変数の参照法について記述します。
代用参照は、指定した変更による変数値の代用を行います。
$(VAR:a=b) の形式で、変数 VAR の値をとり、全ての語の終わりの a は b で置き換えられ、文字列が代用されます。
TAG1 = thdrun lib SRC1 = $(TAG1:=.c) OBJ1 = $(SRC1:.c=.o) echo1: @echo "SRC1 are $(SRC1)" @echo "OBJ1 are $(OBJ1)"
> make echo1 SRC1 are thdrun.c lib.c OBJ1 are thdrun.o lib.o
別の記法もあります。以下を参照ください。
SRC2 = a.c b.c c.c OBJ2 = $(SRC2:%.c=%.o) echo2: @echo "SRC2 are $(SRC2)" @echo "OBJ2 are $(OBJ2)"
> make echo2 SRC2 are a.c b.c c.c OBJ2 are a.o b.o c.o
変数は、内部的に名前の中で参照されます。 これは、計算された変数名、あるいはネストされた変数の参照と呼ばれます。
X = Y Y = Z A = $($(X)) C = $(A) C += $(X) D += $(X) echo3: @echo "A is $(A) echo4: @echo "C is $(C) @echo "D is $(D)
> make echo3 A is Z
ネストのレベルに制限はありません。
すでに定義されている変数の値にテキストを追加する場合は、 = の代わりに += を使います。
定義されていない変数への += は = とおなじです。
X = Y Y = Z A = $($(X)) C = $(A) C += $(X) D += $(X) echo4: @echo "C is $(C) @echo "D is $(D)
> make echo4 C is Z Y D is Y
環境で設定した変数は、そのまま make の中で有効です。 しかし、同じ変数を makefile 内で定義すると、その値が優先されます。 例外として、SHELL 変数は環境で設定されていても、無視されます。
makefile SHELL = /bin/csh TAG1 = thdrun lib SRC1 = $(TAG1:=.c) OBJ1 = $(SRC1:.c=.o) subdir: cd sub && $(MAKE)
sub/makefile TAG1 = thdrun lib SRC1 = $(TAG1:=.c) OBJ1 = $(SRC1:.c=.o) echo1: @echo "SRC1 are $(SRC1)" @echo "OBJ1 are $(OBJ1)" @echo "$(SHELL)" @echo "$(MAKEFLAGS)" @echo "$(CFLAGS)"
> setenv CFLAGS "-g -o" > make -e subdir cd sub && make /home06/taka/make/make/sub SRC1 are thdrun.c lib.c OBJ1 are thdrun.o lib.o /bin/sh -e -g -o
![]() |
![]() |
makefile のルールは、いつどのようにファイルを再構成するかを記述します。
ここで記述する名前をターゲットといい、 まずターゲットの必要条件となるファイルのリストを書き、 次行からターゲットを生成したり、更新するための命令を書きます。
ターゲットは複数書け、それらは独立に機能します。
make で何も指定しないで実行されるターゲットが、デフォルトゴールです。
通常それは all で定義されます。
ターゲットの依存関係を指定する部分の形式は以下のとおりです。
なおシェルコマンドは、環境変数 SHELL
に設定されているシェルに対応するものでなければなりません。
シェルコマンドは、行の最後にバックスラッシュと改行を入れることで、
次行に継続できます。
また、コマンドの先頭はタブ文字でなければなりません。
ターゲット名:ターゲット作成に必要なファイルのリストまたは別のターゲット名 [TAB] シェルコマンド・リスト [TAB] :
ターゲット名は、スペースで区切られたファイル名で、ワイルドカードも使えます。
シェルコマンド・リストの先頭は、タブ文字で始まり、
SHELL 変数で指定した、もしくはデフォルトのシェルのコマンド列を書きます。
変数の参照を開始するためにドル記号が使われるため、
ドル記号をドル記号として使う場合は $$ と書きます。
また、長い行はバックスラッシュで分割できます。
期限切れの判定は、必要条件で指定されたファイルが、 存在しないか、必要条件のいずれよりも古いことです。 これは、ターゲットファイルの内容が必要条件の情報に基づいて計算されます。 従って必要条件のいずれかに変更があった場合は、既存のターゲットは無効になります。 どのように更新するかは、シェルコマンド・リストで指定します。
ワイルドカードを使うと1つの指定で多くのファイルを指定できます。 make でつかえるワイルドカードは、* ? です。 また ~ で始まるファイル名は、~ を $HOME に置換します。
ワイルドカードの展開は、ターゲット、必要条件、
シェルが実行するコマンドで自動的におこなわれます。
ワイルドカード文字をバックスラッシュを前置することで、無効にできます。
clean: rm -f *.o
make は必要条件を見つけるためにいくつかのディレクトリを検索します。
make における環境変数 VPATH は、
make がサーチすべきすべてのファイルのディレクトリのリストを、
: で区切って指定します。
偽のターゲットは、ファイル名ではなく、明示的な要求を作るために使います。
ターゲットファイルを生成しないルールを記述した場合、
すなわち偽のターゲットを記述した場合、
ターゲットを指定するたびに実行されます。
clean などが代表的な例です。
ターゲットを偽のターゲットであると宣言するには、 .PHONY: に続けてターゲット名を書きます。
ターゲット名 | 機能 | SVR V |
---|---|---|
.PHONY: | 偽ターゲットであることを宣言する ファイルの依存性を調べずに、ただ命令だけを実行する |
× |
.SUFFIXES: | 依存関係のリストとしてサフィックス一覧で、 サフィックスルールのチェックに用いられる | ○ |
.DEFAULT: | ファイルを作成する必要はあるが、 そのためのコマンドや関連する組み込み規則がない場合、 これで定義されたコマンドがあれば実行される | |
.PRECIOUS: | このエントリに依存するものは、 kill や割り込みのシグナルを受けても、ターゲットは削除されません | |
.IGNORE: | 実行時エラーが起きても無視し、実行を継続します | |
.SILENT: | コマンドの実行をエコーしません |
複数のターゲットを持つルールは、たくさんのルールを記述することと同じです。
OBJS = a.o b.o c.o $(OBJS): ddef.h
1つのファイルが複数のルールのターゲットになる場合があります。 この場合、すべてのルールの必要条件が、 ターゲットに対する必要条件のリストにマージされます。 そして、ターゲットがルールの必要条件よりも古い場合、命令が実行されます。
必要条件だけを持つ特別なルールは、 同時に多くのファイルにいくつかの特別な必要条件を与えるのに使われます。
PROC = $(ORACLE_HOME)/bin/proc PROCFLAGS_DYN = CODE=ANSI_C COMP_CHARSET=MULTI_BYTE NLS_CHAR=EUC DYNAMIC=ANSI LDFLAGS = -o $@ LIBHOME = $(ORACLE_HOME)/lib/ PROLDLIBS = -lclntsh -lthread ansidyn1 ansidyn2: $(PROC) $(PROCFLAGS_DYN) iname=$* $(CC) $(CFLAGS) -c $*.c $(CC) $(LDFLAGS) $*.o -L$(LIBHOME) $(PROLDLIBS)
静的なパターンルールは、複数のターゲットを指定し、 ターゲット名に基づいてそれぞれのターゲットに対して、 必要条件の名前を作ります。
ターゲットは、同じ必要条件をもつ必要がないため、 複数のターゲットをもつルールより一般的です。
静的なパターンルールは、パターンルールとして定義された暗黙のルールを 一般的に多く持っています。が違いは適用されるときです。 パターンとマッチするどんなターゲットに対しても暗黙のルールは適用できます。 しかし、それはターゲットが異なる形で指定された命令を持たないときに限られます。 静的なパターンルールは、 ルール中で指定したターゲットの正確なリストに適用されますが、 他のターゲットには適用できず、指定されたターゲットのそれぞれに対して適用されます。 仮に2つの矛盾したルールが適用された場合で両方が命令をもつ場合には、 エラーとなります。
静的なパターンルールは、以下の理由で暗黙のルールより優れています。
ターゲットリスト: ターゲットパターン: 依存関係リスト コマンド・リスト
ターゲットリストは、ルールを適用するターゲットを指定します。
ターゲットパターンと依存関係リストは、
それぞれのターゲットの必要条件の計算方法を指定します。
それぞれのターゲットは、ターゲットパターンにマッチした名前の部分展開をします。
個々のパターンは、通常 % を1つ含んでいます。
OBJS = foo.o bar.o all: $(OBJS) $(OBJS): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
ダブルコロンルールは、ターゲットの名前のあとに : の代わりに :: を書いたルールです。
ダブルコロンルールの場合、そのルールのいかなる必要条件よりも、 ターゲットが古い場合に実行されます。 すなわち、最終ルールです。 同じターゲットについてのダブルコロンルールは、 あたかも異なるターゲットが処理されるように個別に処理されます。
個々のダブルコロンルールは、命令を指定しないと暗黙のルールが適用されます。
![]() |
![]() |
ルールの命令は、1行ずつ実行されるシェルコマンドから成り、
個々の行はタブで始まらなければなりません。
空行とコメントだけの行は、空の命令です。
SHELL 変数を定義しないと、/bin/sh が使われますので、 sh の形式に則ったコマンドを書く必要があります。
特殊文字 | 内 容 |
---|---|
@ | コマンドの実行を出力しない |
- | コマンドの実行でのエラーを無視して、処理を続行する .IGNORE: があっても同じです |
@-/-@ | コマンドの実行を出力せず、エラーを無視して、処理を続行する |
通常 make はコマンドの実行前に個々のコマンド行を出力します。
行が @ で始まる場合、エコーは抑制され、 コマンドがシェルに渡される前に @ は、破棄されます。
ターゲットを更新する命令を実行する場合、 新しいサブシェルを作ることでそれを処理します。
cd のような個々のプロセスにローカルな変数を設定するシェルコマンドは、 それに続く行に影響を与えないことを注意すべきです。
例えば、以下の例を実行すると、結果が全く異なります。
LOCAL = $(HOME) tst1: pwd cd $(LOCAL) pwd ls tst2: pwd;cd $(LOCAL);pwd;ls
> make tst1 pwd /home06/taka/cc/MAKE/cd cd /home06/taka pwd /home06/taka/cc/MAKE/cd ls makefile > make tst2 pwd;cd /home06/taka;pwd;ls /home06/taka/cc/MAKE/cd /home06/taka 01061123 csh jlib ora806 8163 data jode-1_1_1.jar ora816 816aix.tar dblnk ksh ora817 CC demo ksh.o ora901 CD doc ksh.oo orasrv CLOSE_WAIT driv ksh.ooo osaka CSC egg linux perl ERR egg.tar log post LINUX fig logall servlet LSOF file logall.tar snoop OS fvwm login sql
make は個々のシェルコマンドの実行結果をチェックします。
コマンドの実行結果が成功の場合、
次のコマンド行を新しいサブシェルで実行します。
最後のコマンド行を完了すると、ルールは終了します。
もしエラーが起こった場合(終了ステータスが0でない)、 make は現在のルールを諦めます。
コマンド行のエラーを無視したい場合は、コマンドの前に - をつけます。 また、 .IGNORE: を指定すると、すべてのコマンドのエラーを無視できます。
また、-k を指定した場合は、 必要に応じて保留になっているターゲットの他の必要条件を再構成しようとします。
コマンド実行時、make が致命的なシグナルを受信した場合、 コマンドで更新されるべきターゲットファイルを削除します。 これは、ターゲットファイルが make の最初のチェックから最終更新時刻が変化している場合だけです。
ターゲットの削除の目的は、 次回 make が動作する時最初から動作するようにするためです。
make の再帰的用法とは、makefile 中のコマンドとして make を使うことです。
subsystem: cd subdir && $(MAKE)
make を再帰的に使う場合は、MAKE 変数を用い、コマンド make は使いません。 この変数の値は、make コマンドのフルパス名です。
トップレベルの make の変数値は、明示的な要求でサブの make に渡されます。
make は、渡されたもしくはエクスポートされた変数の値を実行中の環境に設定します。 make は、明確な要求以外は環境の初期状態で定義される、 またはコマンド行で設定した場合だけ、変数をエクスポートします。
make のコマンド行で指定したオプションは、 MAKEFLAGS を通じてサブの make に自動的に渡され、 サブの make のオプションとなります。
makefile SHELL = /bin/csh TAG1 = thdrun lib SRC1 = $(TAG1:=.c) OBJ1 = $(SRC1:.c=.o) subdir: cd sub && $(MAKE)
sub/makefile TAG1 = thdrun lib SRC1 = $(TAG1:=.c) OBJ1 = $(SRC1:.c=.o) echo1: @echo "SRC1 are $(SRC1)" @echo "OBJ1 are $(OBJ1)" @echo "$(SHELL)" @echo "$(MAKEFLAGS)"
> make -e subdir cd sub && make /home06/taka/make/make/sub SRC1 are thdrun.c lib.c OBJ1 are thdrun.o lib.o /bin/sh -e
![]() |
![]() |
make は、コマンドを実行したディレクトリにある makefile,Makefile の順に探し、
あったものを使います。
また、-f オプションで makefile 名を指定することもできます。
ゴールとは、make が実行すべきターゲットのことです。
ターゲットはゴールの必要条件などになる場合更新されます。
デフォルトでは、ゴールは makefile の1番目のターゲットです
(ピリオドで始まるターゲットは無視されます)。
make へ複数の引数を渡すことで、複数のゴールを指定できます。 この場合指定された順にターゲットを処理します。
makefile に記述されているどのターゲットもゴールとして指定できます。
通常プログラムの一部をコンパイルしたり、
偽のターゲットを実行したい場合に指定します。
ユーザにとっての標準ターゲットは、 ここを参照ください。
=を含む式は、変数に値を指定します。
makefile で同じ変数に対して重複して値を割り当てると、
以前の値は無効になります。
これを変数のオーバライドといいます。
通常 c コンパイラに渡すオプションは CFLAGS です。 CFLAGS = -g であった場合、 もし望むなら、make を実行するたびにこの値をオーバライドすることもできます。
CFLAGS = -g cc -c $(CFLAGS) foo.c
make CFLAGS='-g -o'
X = Y Y = Z A = $($(X)) C = $(A) C += $(X) D += $(X) echo1: @echo $(C) @echo $(D)
> make D='AAA' echo1 Z Y AAA > make X='AAA' echo1 AAA AAA
makefile 内のコマンド実行中にエラーが発生した場合、 make はゼロでない終了値で実行を終了します。 しかし、エラーを無視してターゲットをすべて実行したい場合もあります。 このような場合は、.IGNORE をはじめに宣言するか、-i モードで実行すると、 エラーを無視します。
![]() |
![]() |
暗黙のルールは、make に対して習慣的な使用を伝え、 それを使う場合は詳細に指定する必要がない標準的な規則です。
組み込まれた暗黙のルールは、コマンドの中でいくつかの変数を使います。 そのため、その変数の値を変更することで、暗黙のルールの動作を変更できます。
またパターンルールを記述することで、独自の暗黙のルールを定義できます。
make のデフォルトの暗黙のルールは以下で知ることができます。
make -pf - < /dev/null
個々の暗黙のルールは、ターゲットパターンと必要条件のパターンを持ちます。 また同じターゲットパターンを持つ、多くの暗黙のルールがあります。 make はどの暗黙のルールを使うかを知っていて、 どの必要条件のファイルが存在するかを知っているため、 どれが適用されるかも知っています。
ここで、作成可能とは、ターゲットあるいは必要条件として、 明確に makefile にあるか、 作成手順が再帰的に暗黙のルールが発見できる場合です。
暗黙の必要条件が別の暗黙のルールの結果である場合、連鎖が起きているといいます。 明示的な必要条件は暗黙のルールの検索に影響しません。
make は個々のターゲットに対して暗黙のルールを検索します。 必要条件としてかかれているファイルは、 何も指定しないルールを持つターゲットとみなされ、 それに対して暗黙のルールが検索されます。
コマンドのないターゲットに対して暗黙のルールが使用されないようにするには、 セミコロンを使って空のコマンドをそのターゲットに与えます。
暗黙のルールは、 make に対して習慣的に使われるルールをあらかじめ指定するものです。 これらは、makefile で明確にオーバーライドするか、 無効にされない限り常に利用可能です。
定義された暗黙のルールの多くは make にサフィックスルールとして実装されています。 暗黙のルールの連鎖は順番に適用されます。
例えば、solaris の c プログラムに対する暗黙のルールは以下のとおりです。
.c.o: $(COMPILE.c) $(OUTPUT_OPTION) $< .c.a: $(COMPILE.c) -o $% $< $(AR) $(ARFLAGS) $@ $% $(RM) $% .c~: $(GET) $(GFLAGS) -p $< > $*.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $*.c .c: $(LINK.c) -o $@ $< $(LDLIBS) COMPILE.c= $(CC) $(CFLAGS) $(CPPFLAGS) -c LINK.c= $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
上記 ~ は、SCCS ファイルを意味します。
サフィックスが1つだけのルール (.c:) は、 いかにして x.c から x を作成するかの定義です。 つまり1つのソースファイルからプログラムを作るためのルールです。
サフィックスが2つのルール (.c.o:) は、 いかにして x.c から x.o を作成するかの定義です。
暗黙のルールは、その必要条件がサフィックスを1つ持っています。
サフィックスルールを修正する場合、
事前に定義された有効なサフィックスルールだけが、
指定したリストにあるサフィックスによって名前ずけられたルールになります。
リストにないサフィックスのルールは、無効になります。
複数のオブジェクトファイルがある場合、その1つは実行ファイル名と同じであれば、
x: y.o z.o
x.c,y.c,z.c があれば、x という実行ファイルを作ります。
より複雑な場合、 例えば実行ファイル名から派生するオブジェクトファイルがない場合は、 リンクのためのルールを記述する必要があります。
自動的に .o を作成する個々のファイルはコンパイラによって、 -c なしに自動的にリンクされます。 組み込み済みの暗黙のルールのコマンドは、 COMPILE.c や LINK.c などの変数を使います。
make はソースファイル .x をコンパイルするのに COMPILE.x のルールを使い、 実行ファイルを作るのに LINK.x を使います。 また、ファイル .x をプリプロセスするルールは PREPROCESS.x を使います。
オブジェクトファイルを作るすべてのルールは OUTPUT_OPTION を使います。 make はコンパイルのタイミングに応じて、 この変数に -o $@ を含むか空にするか定義します。
VPATH を使用する場合は、 -o で出力先を指定する必要があります。 OUTPUT_OPTION に ;mv $*.o $@ を与えます。
暗黙のルールで使用される定義済み変数は、 プログラムの名前とプログラムに渡す引数があります。 これらは、再定義することで変更できます。
変数名 | 説 明 | 既定値 |
---|---|---|
AR | アーカイブのプログラム名 | ar |
ARFLAGS | アーカイブのプログラムへのフラグ | rv |
AS | アセンブラのプログラム名 | as |
ASFLAGS | アセンブラのプログラムへのフラグ | |
CC | C コンパイラ名 | cc |
CFLAGS | C コンパイラへのフラグ | |
LDFLAGS | リンカへのフラグ | |
CXX | C ++ コンパイラ名 | c++ |
CXXFLAGS | C ++ コンパイラへのフラグ | |
GET | SCCS からの展開プログラム | get |
GFLAGS | SCCS get プログラムへのフラグ | |
LEX | lex c generator | lex |
LFLAGS | lex プログラムへのフラグ | |
YACC | yacc c generator | yacc |
YFLAGS | yacc プログラムへのフラグ |
ファイルは、暗黙のルールのシーケンスで作成される場合があります。 このようなシーケンスを連鎖といいます。 例えば、上記 .c.a: のルールでは、次のようなシーケンスで処理されます。
まず、C コンパイラで X.c から X.o を作り、 アーカイバ ar で X.o から X.a を作り、 X.o を削除します。
パターンルールの記述で、暗黙のルールを定義できます。
パターンルールは、ターゲットの中に % 文字を含んでいます。 % は、空でない部分文字列の何とでも適合します。 (最低 1 文字は必要です)。 必要条件はその名前がターゲット名とどのように関係するかを示すために使用します。
パターンルールで % を使用した展開は、 どんな変数やファンクションの展開の後でも、起こることに注意してください。
パターンルールは、ターゲットの中に % 文字を含んでいます。 そうでなければ、普通のルールのように見えます。 ターゲットは、ファイル名にマッチしたパターンで、 % は空でない部分文字列の何とでもマッチできます。
例えば、%.c は .c で終わるすべてのファイルにマッチします。
%.a: %.o $(AR) $(ARFLAGS) $@ $<
上記の例は、 XX.o から XX.a を作るルールです。
パターンルールは、複数のターゲットを持てます。 この場合、ターゲットのすべてを作成するために1回だけ実行されます。
makefile においてパターンルールの現れる順番は重要です。 なぜならそれが、パターンを考慮する順番になるからです。 等しく適用可能なルールでも、見つけられた最初のものだけが使われます。 また組み込まれているルールよりも記述したルールが優先します。
以下の例は、.c ファイルをコンパイルして .o を作るルールです。 ここで、$< はターゲットに対して必要条件のファイル名つまり c のプログラム名、 $@ は現在のターゲットの完全名つまり c をコンパイルしてできるオブジェクト ファイル名です。
%.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@
暗黙のルールは、適用されるごとにファイル名が異なるので、 コマンドにファイル名を書くことはできません。 この場合、自動変数を使用することで、ターゲットと必要条件に基づいて、 ルールが実行されるたびにこれらは計算された値を持ちます。
ターゲットの作成の規則を記述するのに便利な自動変数があります。
自動変数 | 内 容 |
---|---|
$* | 現在の依存部のターゲット名からサフィクスを削除したもの |
$@ | 現在のターゲットの完全名 |
$< | ターゲットに対して必要条件の名前 |
$? | 空白で区切られたターゲットより新しい必要条件の名前リスト 通常、アーカイブの構成メンバなど |
$% | ターゲットがアーカイブメンバの場合は、その名前。 それ以外は、空 |
ターゲットパターンは、プレフィックスとサフィックスの2つ、 あるいは両方とも空の場合も含めて % から構成されています。 ファイル名がプレフィックスで始まり、サフィックスで終わる場合だけ パターンはファイル名にマッチします。 パターンルールの必要条件は、ファイル名を % で代用することで、 実際のファイル名に変えられます。
ターゲットパターンがスラッシュを含まないときは、 ファイル名のディレクトリ名は、ファイル名から除去されます。 ディレクトリ名は、使用される暗黙のルールの発見のためにだけ無視されます。
パターンルールのターゲットが % だけであった場合、 どんなファイル名にもマッチします。 このようなルールは、何にでもマッチするルールとよばれ、 便利ですがあらゆる可能性を考慮すると、動作が遅くなってしまいます。 そこで、以下の方法で制約を適用します。
組み込み済の暗黙のルールを同じターゲットと必要条件で、 別のパターンルールを定義することで、オーバライドできます。 ただし、ルールは異なるものでなければなりません。
例えば以下の例ではアセンブラの実行をキャンセルします。
%.o: %.s
必要条件なしにターミナルに何にでもマッチするパターンを書くことで、 最後の手段ともいえる暗黙のルールを定義できます。 これは、いかなるターゲットにもマッチします。
%:: touch $@
上記の例は、すべてのファイルが存在するようになります。
サフィックスルールは、暗黙のルールを定義するための古い方法です。
概要にある solaris の暗黙のルールは、
この形式で書かれています。
ルールには、 ダブルサフィックスルールとシングルサフィックスルールの2種類あります。
ダブルサフィックスルールは、 ターゲットとソースのサフィックスのペアによるものです。 一方、シングルサフィックスルールは、 1つのサフィックスで定義されます。 それは、ソースのサフィックスで、どんなファイルともマッチし、 関連した暗黙の必要条件の名前には、ソースのサフィックスがつけられます。 シングルサフィックスルール .c: は %:%.c と同じです。
make がターゲットに対して暗黙のルールを検索する手順は、以下のとおりです。
これらについて、暗黙のルールが再帰的に作用します。 サフィックスルールは、一旦読み込まれるとパターンルールに 変換されますので、ここでは言及しません。
![]() |
![]() |
make のデフォルトの動作は、以下のとおりです。
オプション | 説 明 |
---|---|
-e | 環境変数を makefile 内で指定したものに置き換えます |
-f makefile | makefile で指定したファイルで実行します |
-i | コマンドからのエラーコードを無視します |
-k | 現エントリにエラーがあった場合、これを放棄しますが、 そのエントリに依存しない他のエントリに関する作業は続行します |
-n | 非実行モード。コマンドを出力するだけで、実行はしません。 @ で始まる行でも出力されます |
-p | マクロ定義とターゲットの記述をすべて出力します |
-r | デフォルトの組込み規則を使わない |
-s | サイレント・モード。実行前にコマンド行を出力しません |
-t | コマンドは実行しないで、ターゲット・ファイルを touch します |
ターゲット名 | 説 明 |
---|---|
all | すべての実行形式ファイルを生成する ターゲットを指定しない場合、このターゲットが対象になります |
dist | PACKAGE.VERSION.tar.gz で配布用ファイルを生成する |
clean | make で生成したファイル等を削除します |
distclean | 実行形式ファイルや Makefile 等が削除され、 配布用ファイルを展開した直後と同じになります |
install | 指定したインストール先にファイルをインストールします |
uninstall | インストール先からファイルをアンインストールします |
check | インストール前にプログラムのチェックをします |
tar | ソースファイルの tar 作成 |
distcheck | 配布用ファイルを生成した後、展開実行形式ファイルを生成し、 配布用ファイルをチェックします |
make check は、 make all の後、 make install の前に実行します。 make check では、アプリケーション開発者があらかじめ準備した、 テストプログラムを実行し、問題がないことをチェックします。
SHELL=/bin/sh make 内で使うシェルを定義 .SUFFIXES: サフィックリストをクリア .SUFFIXES: .c .o 必要なサフィックリストを導入
以下のユーティリティ以外を使用するときは、必ず変数を介して使うべきです。
cat cmp cp diff echo egrep expr false grep ln ls mkdir mv pwd rm rmdir sed sleep sort tar test touch true
環境変数名 | 機能 | 例 |
---|---|---|
VPATH | make が検索する検索パス (*.c *.h -lXXX) makefile で指定されたファイルが存在しない場合、 ディレクトリ検索が行われます。 |
VPATH = src:../headers |
SHELL | 命令部で書くコマンドのシェルを指定 デフォルトは /bin/sh で、環境から設定されない |
SHELL=/bin/csh |
MAKEFLAGS | make を起動するときに指定したフラグ | |
export 環境変数 = 値 個別の環境変数を export する export 全ての環境変数を export する
オプション | 機能 |
---|---|
-n |
make -n で実行すると、実際のコマンドを実行しないで、
コマンドの実行をトレースします。
この結果から、どこで問題が起きたかが解かるでしょう。 |
-d | make -d で実行すると、デバッグ情報を出力します。 |
HP-UX(10/11) の make には、以下のバグがありますので注意してください。
ログイン・セッションで定義している環境変数を、make ファイルの中で再定義し、 かつその中ですでに定義している環境変数を使うと、 変数の置き換えがおこなわれないという現象です。
ログイン・セッションで、以下の環境変数を定義して $ORACLE_HOME/plsql/demo で demos を make しようとすると、 以下のエラーとなります。
ORACLE_HOME ORA_NLS33 LANG
> make -f demo_plsql.mk demos make -f demo_plsql.mk demos sqlplus scott/tiger @exampbld < /dev/null SQL*Plus: Release 8.0.5.0.0 - Production on Wed Feb 21 10:24:4 2001 (c) Copyright 1998 Oracle Corporation. All rights reserved. ERROR: ORA-12705: invalid or unknown NLS parameter value specified Enter user-name: sqlplus scott/tiger @examplod < /dev/null SQL*Plus: Release 8.0.5.0.0 - Production on Wed Feb 21 10:24:5 2001 (c) Copyright 1998 Oracle Corporation. All rights reserved. ERROR: ORA-12705: invalid or unknown NLS parameter value specified Enter user-name: /usr/ccs/bin/make -f /home2/ora805/app/oracle/product/8. 0.5/plsql/demo/demo_plsql.mk build EXE=examp10 OBJS=examp10.o /home2/ora805/app/oracle/product/8.0.5/bin/proc sqlcheck=full userid=sco tt/tiger CHAR_MAP=VARCHAR2, DBMS=V7 iname=examp10.pc Pro*C/C++: Release 8.0.5.2.1 - Production on Wed Feb 21 10:24:8 2001 (c) Copyright 1998 Oracle Corporation. All rights reserved. System default option values taken from: /home2/ora805/app/oracle/product/8.0.5/ precomp/admin/pcscfg.cfg PCC-F-02104, Unable to connect to Oracle *** エラー終了コード 1 中止 *** エラー終了コード 1 中止
これは、$(ORACLE_HOME)/plsql/demo/demo_plsql.mk が include している
$(ORACLE_HOME)/plsql/lib/env_plsql.mk の
ORA_NLS33 = $(ORACLE_HOME)/ocommon/nls/admin/data/
部分の $(ORACLE_HOME) がそのまま SQL*Plus に渡されるためです。
回避策は、この部分をコメントにするか、 ORA_NLS33 または LANG を一時的に unsetenv するかします。
以下の例は、 NLS_LANG、LANG、ORA_NLS33、ORACLE_HOME、ORA_NLS、ORACLE_SID をログイン・セッションで定義している場合の現象です。
<< makefile >> SHELL = /bin/csh CC = cc CFLAGS = +DAportable +DS2.0 TAGS = get SRCS = $(TAGS).c OBJS = $(TAGS).o HDRS = ORACLE_HOME = $(ORACLE_SID)/err $(TAGS): $(OBJS): $(SRCS) $(HDRS) err: echo $(ORACLE_HOME) $(TAGS) clean: rm -f $(TAGS) $(OBJS)
<< get.c >> #include <stdio.h> #include <stdlib.h> char * list[]={"NLS_LANG","LANG","ORA_NLS33","ORACLE_HOME","ORA_NLS","ORACLE_SID"}; #define SZ_LIST (sizeof(list)/sizeof(char *)) int main(int argc,char *argv[]) { int i; char * cp; printf("\n\n==== get ====\n\n"); for (i=0; i<SZ_LIST; i++) { cp = getenv(list[i]); if (cp == NULL) cp ="NULL"; printf("%d] %s\t=%s\n",i,list[i],cp); } exit(0); }
> make cc +DAportable +DS2.0 get.c -o get > get ===== get ===== 0] NLS_LANG =Japanese_Japan.JA16EUC 1] LANG =ja 2] ORA_NLS33 =/home12/ora816/app/oracle/product/8.1.6/ocommon/nls/admin/data 3] ORACLE_HOME =/home12/ora816/app/oracle/product/8.1.6 4] ORA_NLS =NULL > make err echo ora816/err ora816/err get ===== get ===== 0] NLS_LANG =Japanese_Japan.JA16EUC 1] LANG =ja 2] ORA_NLS33 =/home12/ora816/app/oracle/product/8.1.6/ocommon/nls/admin/data 3] ORACLE_HOME =$(ORACLE_SID)/err 4] ORA_NLS =NULL
portstat は、指定されたポートが現在使用中であるかを知らせます。
> portstat 7100 Port Services tcp Status : Reason Services=/etc/services ------ ---------- -------------------------------------- 7100 fs Not available : Address already in use > portstat -u 26200 26210 Port Services udp Status : Reason Services=/etc/services ------ ---------- -------------------------------------- 26200 Available 26201 Available 26202 Available 26203 Available 26204 Available 26205 Available 26206 Available 26207 Available 26208 wnn6-ds Available 26209 Available 26210 Available
portstat.c /* Function : get port informations of TCP/IP or UDP Usage : portstat [-htu] [MIN_PORT [MAX_PORT]] OutFormat: port_number:service_name status Info : port number is 16bits info (u_short) 1 .. 255 system reserved (well-known port) 1 .. 1023 reserved for super user (4.3BSD) Warn : if you want to get informations of port 1~1023, you must be super user */ #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> extern int errno; extern int optind,opterr,optopt; extern char *optarg; void usage(); #define SZ_ADDR (sizeof(struct sockaddr_in)) #define MIN_PORT 1 #define MAX_PORT 0xffff #define FR_PORT 0 #define TO_PORT 1 char *pname; char tcpID[]="tcp",udpID[]="udp"; char sp[]=" "; int main(int argc,char *argv[]) { int i,sockfd,ret,c,protocol,sock_type; int fr_port=MIN_PORT,to_port=MAX_PORT; int tmp[2],optval=1; struct sockaddr_in addr; struct servent *sent; char *emsg,*cp,*proto; /* check input parameters */ /* . set defaults */ pname = argv[0]; proto = tcpID; protocol = IPPROTO_TCP; sock_type = SOCK_STREAM; tmp[0] = tmp[1] = 0; /* . check input options */ while ((c=getopt(argc,argv,"htu")) != EOF) switch(c) { case 't': break; case 'u': proto = udpID; protocol = IPPROTO_UDP; sock_type = SOCK_DGRAM; break; default: printf("%s : Input option error\n",pname); case 'h': usage(); exit(1); break; } /* . check input port numbers */ if ((argc >= (optind+1)) && (argc <= (optind+2))) { for (i=0; optind<argc; optind++,i++) { tmp[i] = atoi(argv[optind]); } if ((tmp[FR_PORT] > 0) && (tmp[TO_PORT]<= MAX_PORT) && (tmp[FR_PORT] <= tmp[TO_PORT])) { } else if (tmp[TO_PORT] == 0) { tmp[TO_PORT] = tmp[FR_PORT]; } else goto err_msg; } else { err_msg: #ifdef DEBUG printf("argc=%d optind=%d argv=%s tmp0=%d tmp1=%d fr=%d to=%d\n", argc,optind,argv[optind],tmp[0],tmp[1],fr_port,to_port); #endif printf("%s : Input arguments error\n",pname); usage(); exit(1); } /* get socket informations */ fr_port = tmp[FR_PORT]; to_port = tmp[TO_PORT]; printf(" Port Services %s Status : Reason Services=/etc/servi ces\n", proto); printf("------ ---------- --------------------------------------\n"); for (i=fr_port; i<=to_port; i++) { if (( sockfd = socket(AF_INET,sock_type,protocol)) == -1) { perror(pname); exit(2); } /* get service name from /etc/services */ sent = getservbyport(i,proto); #ifdef DEBUG if (sent) printf("%s\n",sent->s_name); #endif /* set addr */ bzero((char *) &addr,SZ_ADDR); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons(i); #if 0 /* solaris SO_REUSEADDR BSD SO_REUSEPORT */ ret = setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR, (void *) &optval,sizeof(optval)); if (ret < 0) { emsg = (char *)strerror(errno); printf("Not available : %s\n",emsg); } #endif ret = bind(sockfd,(struct sockaddr *) &addr,SZ_ADDR); cp = (sent ? sent->s_name : sp); printf("%6d %-10s ",i,cp); if (ret == -1) { emsg = (char *)strerror(errno); printf("Not available : %s\n",emsg); } else { printf("Available\n"); } close(sockfd); } exit(0); } void usage() { printf(" Usage: %s [-tuh] MIN_PORT MAX_PORT (1 ~ %d)\n",pname,MAX_PORT); printf(" -t : TCP/IP (default)\n"); printf(" -u : UDP\n"); printf(" -h : help"); printf(" Ex) %s 1500 1550\n",pname); }
portstat.mk #-------------------------------------------------------- # make all_sun # make all_hp # make all_lx # make all_ncr # make clean # make help #-------------------------------------------------------- CC = gcc CC_NCR = cc RM = rm -f TAGS = portstat SRCS = $(TAGS).c OBJS = $(TAGS).o HDRS = LIBS_SUN = -lsocket -lnsl LIBS = CFLAGS_GCC = -c CFLAGS_CC = -c CFLAGS_HP = +DAportable +DS2.0 -c LDFLAGS = $(LIBS) all_sun: $(SRCS) $(HDRS) $(CC) $(CFLAGS_GCC) $(SRCS) $(CC) -o $(TAGS) $(OBJS) $(LIBS_SUN) all_hp: $(SRCS) $(HDRS) $(CC) $(CFLAGS_HP) $(SRCS) $(CC) -o $(TAGS) $(OBJS) $(LDFLAGS) all_lx: $(SRCS) $(HDRS) $(CC) $(CFLAGS_GCC) $(SRCS) $(CC) -o $(TAGS) $(OBJS) $(LDFLAGS) all_ncr: $(SRCS) $(HDRS) $(CC_NCR) $(CFLAGS_CC) $(SRCS) $(CC_NCR) -o $(TAGS) $(OBJS) $(LIBS_SUN) $(TAGS): $(SRCS) $(HDRS) $(CC) $(CFLAGS) $(SRCS) $(CC) -o $(TAGS) $(OBJS) $(LDFLAGS) clean: $(RM) $(TAGS) $(OBJS) core help: @echo "make -f portstat.mk all_sun" @echo "make -f portstat.mk all_hp" @echo "make -f portstat.mk all_lx" @echo "make -f portstat.mk all_ncr" @echo "make -f portstat.mk clean"
> make -f portstat.mk all_sun
> portstat 2200 2220 Port Services tcp Status : Reason Services=/etc/services ------ ---------- -------------------------------------- 2200 Not available : Address already in use 2201 Not available : Address already in use 2202 Available 2203 Available 2204 Available 2205 Available 2206 Available 2207 Available 2208 Available 2209 Available 2210 Available 2211 Available 2212 Available 2213 Available 2214 Available 2215 Available 2216 Available 2217 Available 2218 Available 2219 Available 2220 Available
以下の例は、error.c mkdate.c getarg.c lib.h から libtaka.a を作成、保守するための makefile です。
/*--------------------------------------------------------------------------- FileName : error.c ---------------------------------------------------------------------------*/ #include "lib.h" /*----------------------------------------------------------------------- Function : print error message -----------------------------------------------------------------------*/ FILE *log_fp = NULL; static char *err_msg[] = { "", "", }; char *fatal = "FATAL ", *warn = "WARNING"; void error(char *func,int no,char *info,short status) { char *mp; if (status) mp = fatal; else mp = warn; fprintf(stderr,"%s [%s] ",mp,func);fflush(stderr); if ((no >= ERR_UTL_S) && (no <= ERR_UTL_E)) fprintf(stderr,"%s [%s] err=%d\n",err_msg[no-ERR_UTL_S],info,no); else { char *emsg; char buf[SZ_MSGBUF]; emsg = (char *) strerror(errno); if (emsg) strcpy(buf,emsg); else buf[0] = '\0'; strcat(buf," : "); strcat(buf,info); strcat(buf,"\n"); fprintf(stderr,buf); if (log_fp) { fprintf(log_fp,buf); fclose(log_fp); } } fflush(stderr); if (status) exit(no); else return; }
/*--------------------------------------------------------------------------- FileName : mkdate.c ---------------------------------------------------------------------------*/ #include "lib.h" /*--------------------------------------------------------- Function : make current time string Return : A(current time string) Format : yy/mm/dd hh:mm:ss 12345678901234567 ---------------------------------------------------------*/ char *mkdate(char *yymmdd,int size) { time_t tloc; struct tm *cur; if (size < SZ_DATE) { memset(yymmdd,' ',size); } else { time(&tloc); cur = localtime(&tloc); strftime(yymmdd,size,"%y/%m/%d %T",cur); } return yymmdd; }
/*---------------------------------------------------------- FileName : getarg.c Functions : char *get_arg(int argc,char *argv[]) void usage(char *prg,char *msg) ----------------------------------------------------------*/ #include "lib.h" extern char *optarg; extern int optind; char *u_opt = "ohf:n:"; char *pname; char *arglist = "[-o] [-n MAX_LOOP default=5] [-f VOL_NAME default=/dev/lvol52]" ; int loopcnt; int open_only = 0; /*---------------------------------------------------------- Function : analize input argumets Param : argc(I) = N(arguments) argv(I) = argumets array Return : table_no to make ----------------------------------------------------------*/ char *get_arg(int argc,char *argv[]) { int c; char *rtn = NULL; pname = argv[0]; while((c=getopt(argc,argv,u_opt)) != EOF) switch(c) { case 'n': loopcnt = atoi(optarg); if ((loopcnt <= 0) || (loopcnt >MAX_INT)) exit(20); break; case 'f': rtn = optarg; case 'o': open_only ++; break; case 'h': usage(pname,arglist); exit(SUCCESS); break; default: usage(pname,arglist); exit(ERROR); break; } return rtn; } void usage(char *prg,char *msg) { fprintf(stderr,"\nUsage : %s %s\n",prg,msg); fflush(stderr); }
/*------------------------------- FileName : lib.h -------------------------------*/ #include <stdio.h> #include <string.h> #include <time.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> char *mkdate(); char *get_arg(int ,char *[]); void usage(); void error(char *,int,char *,short); #define SUCCESS 0 #define ERROR 100 #define NOT_FOUND -1 /* ===== mkdate ===== */ #define SZ_DATE 28 /* mkdate:length of date (yy/mm/dd hh:mm:ss) */ #define SZ_MSGBUF 126 /* error: message buf size */ /* ===== get_arg ===== */ #define MAX_INT 0x7FFFFFFF /*------------------------------------------------------ Usage of die_err and warn_err Calling : die_err("LIB_NAME",ERR_CODE,"FUNCTION_NAME") Action : print error messages and exit with ERR_CODE SystemError : strerror GeneralError : put err_msg[] Calling : warn_err("LIB_NAME",ERR_CODE,"FUNCTION_NAME") Action : print error messages and return ------------------------------------------------------*/ #define DIE_ERR 1 /* fatal error */ #define WARN_ERR 0 /* warning error */ #define die_err(msg,no,info) error(msg,no,info,DIE_ERR); #define warn_err(msg,no,info) error(msg,no,info,WARN_ERR); /* definition of Error no */ /* System error : these are from 1 to ESTALE(151) /usr/include/sys/errno.h */ #define ERR_MUTEX_AINI 1 #define ERR_MUTEX_INI 2 #define ERR_MUTEX_LCK 3 #define ERR_MUTEX_ULK 4 #define ERR_SIGWAIT 11 #define ERR_SIGSEND 12 #define ERR_MALLOC 20 #define ERR_LSEEK 21 #define ERR_READ 22 #define ERR_WRITE 23 #define ERR_OPEN 24 #define ERR_PTHD_INIT 30 #define ERR_PTHD_ATTR 31 #define ERR_PTHD_CRE 32 #define ERR_PTHD_JOIN 33 #define ERR_FPRINTF 40 /* general error : these are ESTALE to 255 /usr/include/sys/errno.h */ #define ERR_INIT 200 #define ERR_END 255 #define ERR_UTL_S ERR_INIT #define ERR_UTL_E ERR_END
makefile .IGNORE: SHELL = /bin/csh CC = gcc RM = rm -f AR = ar CFLAGS = -c -g ARFLAGS = cr TAGS = libtaka DEFS = lib.h LIB1 = mkdate LIB2 = error LIB3 = getarg LIBS = $(LIB1).c $(LIB2).c $(LIB3).c SRCS = $(LIBS) OBJS = $(SRCS:.c=.o) all: $(TAGS) $(TAGS): $(OBJS) $(AR) $(ARFLAGS) $@.a $? $(OBJS): $(DEFS)
> make all gcc -c -g -c mkdate.c gcc -c -g -c error.c gcc -c -g -c getarg.c ar cr libtaka.a mkdate.o error.o getarg.o
OBJS = *.o TAGS = portstat LDFLAGS = -o LDLIBS = -lsocket -lnls $(TAGS): $(OBJS) $(CC) $(LDFLAGS) $@ $(OBJS) $(LDLIBS) clean: rm -f $(OBJS) $(TAGS)
> ls makefile portstat.c portstat.o > make cc -o portstat *.o -lsocket -lnls > make clean rm -f *.o portstat > make make: Fatal error: Don't know how to make target `*.o'
[解説] OBJS の実際の値は、ディレクトリにある *.o ですから、portstat.o です。 $(TAGS) に対するルールでは、実在する portstat.o が必要条件です。 そこで make を実行すると portstat を作ります。
しかし *.o を削除するとどうなるでしょうか? *.o をマッチするものがなくなり、"*.o" そのものが portstat と 依存関係になり、当然存在しないので、エラーとなります。
特殊文字 | 働き |
---|---|
$ | 変数の参照のとき、先頭に付け続けて括弧、または中括弧で名前を囲みます [例] $(LDFLAGS) |
* ? | ワイルドカードとして2つの文字が使用可能 |
\ | 1行を超えるコマンドまたは変数への代入を記述するための継続を表す |
@ | コマンド行の行頭にあると、コマンドの実行を出力しない |
- | コマンド行の行頭にあると、コマンドの実行でのエラーを無視して、 処理を続行する。 .IGNORE: があっても同じです |
# | コメントを表す |
% | パターンルールの中で記述可能で、 空でない部分文字列の何とでもマッチできることを表します |
TAB 文字 | コマンド行の行頭は、TAB で始まらなければならない |
エラーメッセージ | 対処法 |
---|---|
Fatal error in reader: makefile, line 13: Unexpected end of line seen |
多分 12 行目がおかしい。行頭が TAB でないとか |
make: Fatal error in reader: makefile, line 46: Badly formed macro assignment |
多分 46 行目がおかしい。行頭が TAB でないとか |
本 の 名 前 | 出版社 | 著者 | 値段 |
---|---|---|---|
GNU Make | ASCII | Richard M. Stallman | 1800 |
UNIX System V プログラマ・リファレンス・マニュアル |
AT&T | UNIX Press | 13000 |
make の達人 | トッパン | C.トンド、A.ネイサンソン、E.ヤント | 2200 |
用語 | 説 明 |
---|---|
サフィックス | ファイル名の拡張子部分:名前が X.Y であると Y の部分 |
プレフィックス | ファイル名の拡張子を除いた部分:名前が X.Y であると X の部分 |
デフォルト・ゴール |
ドット (.) 以外ではじまるはじめのターゲットをデフォルト・ゴールといい、
make が最終的に到達するゴールです。 通常それは all で定義されます。 |
ゴール | make が実行すべきターゲットのこと |
自動変数 | ルールが実行されるたびに計算された値を持つ変数 |
ターゲット | make が実行すべきコマンドの一群で、名前と:から成る |
偽のターゲット | ターゲットファイルを生成しないルールを記述したターゲット |
![]() |
![]() |