make

機能 概要 ファイルの構成 必要条件の自動生成
ルールの書き方
( 明示的 )
書き方 ワイルドカードの使い方 ディレクトリ検索
偽のターゲット 組込み済ターゲット名 複数のターゲット
複数のルール 静的なパターンルール ダブルコロンルール
コマンドの書き方 制御用特殊文字 エコー 実行
エラー 中断と kill 再帰的用法
変数(マクロ) 定義 参照の基礎 参照の進んだ機能
テキストの追加 環境変数の取得方法 自動変数
make の実行 makefile ゴールを指定する引数 オプション
変数のオーバライド 実行の継続
暗黙のルール 使い方 カタログ 定義済み変数
連鎖 パターンルール 最終ルールの定義
サフィックスルール 検索アルゴリズム
その他 オプション 主なターゲット make ファイルの約束事
make の環境変数 デバッグ バグ情報
FAQ 特殊文字一覧
Sun make のエラー一覧 HP make のエラー一覧 AIX make のエラー一覧

この色の文字で書かれている部分は、 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 の構成

makefile は、以下の4つの部分から構成されます。

項目 説          明
暗黙のルール
implicit rule
ファイルの名前に基づいて、いつどのように変更するかを示すルールを作ります。
名前とは、ファイルにつける拡張子でサフィックスといい、 サフィックスの関係を記述します。
変数の定義
macro definition
あとでテキストで代用できる変数のための文字列を定義します。
明示的ルール
explicit rule
1つ以上のファイルをいつどのように変更するかを示すルールのターゲットを作ります。
そしてターゲットが依存するファイルをリストし、 ターゲットの必要条件を呼び出します。
さらに、ターゲットを更新したり、生成する規則を指定します。
命令
directive
make に実行させたいコマンド群を書きます。
  1. 他の makefile を include する
  2. 変数の値によって makefile の一部分を使用する、無視するを決定します。
  3. 複数行にわたる文字列から変数を定義する

すべてに共通することですが、# で始まる行はコメント行です。
また、バックスラッシュと改行で、複数行にわたって記述することができます。

必要条件の自動生成

通常 makefile で記述しなければならない多くのルールは、 いくつかのヘッダファイルに依存するオブジェクトの関係だけの場合がほとんどです。

例えば、main.c で #include "defs.h" があった場合、以下のように記述するだけです。

main.o: defs.h

defs.h が変更された場合はいつでも、 main.o は再構成されます。 それは、暗黙のルールが定義されているからです。




変数(マクロ)

変数の定義

変数は、makefile の中で文字列の代理となる名前の定義です。
(make のバージョンによっては、変数はマクロと呼ばれます)

makefile 内の変数は、読み込んだ時展開されます。
変数は、いろいろなものを代用することができます。 例えば、ファイル名のリスト、コンパイラに渡すオプション、実行するプログラム、 ソースファイルを探すディレクトリ、出力するディレクトリなどです。

変数は、英文字ではじまり数字、アンダースコアを含む文字列で、 大文字、小文字を区別しますが、伝統的に大文字を使います。

いくつかの変数(通常 $ と特殊文字1つ)は、 自動変数と呼ばれ特別な用途に使われます。

変数の定義形式は以下の2とおりです。

  変数名 = 定義の文字列


  include マクロ定義ファイル

これらの変数は、再帰的に展開されます。
値が長い場合は、バックスラッシュを入れることで、 次行に継続でき、行数に制限はありません。

値を設定しない変数は、空の文字列が入っています。 環境変数の優先順位は、以下のとおりです。

  1. make ファイル内の設定
  2. 環境で設定したもの

変数の参照の基礎

変数の値を参照するには、ドル記号に続けて変数名を括弧または中括弧で囲みます。

例えば、変数 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




make の明示的ルール

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 を指定した場合は、 必要に応じて保留になっているターゲットの他の必要条件を再構成しようとします。

中断と kill

コマンド実行時、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

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つ、 あるいは両方とも空の場合も含めて % から構成されています。 ファイル名がプレフィックスで始まり、サフィックスで終わる場合だけ パターンはファイル名にマッチします。 パターンルールの必要条件は、ファイル名を % で代用することで、 実際のファイル名に変えられます。

ターゲットパターンがスラッシュを含まないときは、 ファイル名のディレクトリ名は、ファイル名から除去されます。 ディレクトリ名は、使用される暗黙のルールの発見のためにだけ無視されます。

何にでもマッチするパターンルール

パターンルールのターゲットが % だけであった場合、 どんなファイル名にもマッチします。 このようなルールは、何にでもマッチするルールとよばれ、 便利ですがあらゆる可能性を考慮すると、動作が遅くなってしまいます。 そこで、以下の方法で制約を適用します。

  1. ダブルコロンを使ってルールをターミナルとする
    つまりターミナルルールを超えた連鎖は許されません。
  2. 特定のファイル名を認識するためのダミーのパターンルールを作る
    [例] %.p:

暗黙のルールのキャンセル

組み込み済の暗黙のルールを同じターゲットと必要条件で、 別のパターンルールを定義することで、オーバライドできます。 ただし、ルールは異なるものでなければなりません。

例えば以下の例ではアセンブラの実行をキャンセルします。

%.o: %.s

最終ルールの定義

必要条件なしにターミナルに何にでもマッチするパターンを書くことで、 最後の手段ともいえる暗黙のルールを定義できます。 これは、いかなるターゲットにもマッチします。

%::
	touch $@

上記の例は、すべてのファイルが存在するようになります。

サフィックスルール

サフィックスルールは、暗黙のルールを定義するための古い方法です。
概要にある solaris の暗黙のルールは、 この形式で書かれています。

ルールには、 ダブルサフィックスルールとシングルサフィックスルールの2種類あります。

ダブルサフィックスルールは、 ターゲットとソースのサフィックスのペアによるものです。 一方、シングルサフィックスルールは、 1つのサフィックスで定義されます。 それは、ソースのサフィックスで、どんなファイルともマッチし、 関連した暗黙の必要条件の名前には、ソースのサフィックスがつけられます。 シングルサフィックスルール .c: は %:%.c と同じです。

検索アルゴリズム

make がターゲットに対して暗黙のルールを検索する手順は、以下のとおりです。

  1. コマンドなしの個々のダブルコロンルール
  2. コマンドなしの個々の通常のルールのターゲット
  3. どのルールのターゲットでもない個々の必要条件

これらについて、暗黙のルールが再帰的に作用します。 サフィックスルールは、一旦読み込まれるとパターンルールに 変換されますので、ここでは言及しません。




その他

make のオプション

make のデフォルトの動作は、以下のとおりです。

  1. makefile,Makefile の順に探します。
  2. ターゲットは、 ドット (.) 以外ではじまるはじめのターゲットを最終的なゴールとする。

オプション 説          明
-e 環境変数を makefile 内で指定したものに置き換えます
-f makefile makefile で指定したファイルで実行します
-i コマンドからのエラーコードを無視します
-k 現エントリにエラーがあった場合、これを放棄しますが、 そのエントリに依存しない他のエントリに関する作業は続行します
-n 非実行モード。コマンドを出力するだけで、実行はしません。
@ で始まる行でも出力されます
-p マクロ定義とターゲットの記述をすべて出力します
-r デフォルトの組込み規則を使わない
-s サイレント・モード。実行前にコマンド行を出力しません
-t コマンドは実行しないで、ターゲット・ファイルを touch します

make の主なターゲット

ターゲット名 説          明
all すべての実行形式ファイルを生成する
ターゲットを指定しない場合、このターゲットが対象になります
dist PACKAGE.VERSION.tar.gz で配布用ファイルを生成する
clean make で生成したファイル等を削除します
distclean 実行形式ファイルや Makefile 等が削除され、 配布用ファイルを展開した直後と同じになります
install 指定したインストール先にファイルをインストールします
uninstall インストール先からファイルをアンインストールします
check インストール前にプログラムのチェックをします
tar ソースファイルの tar 作成
distcheck 配布用ファイルを生成した後、展開実行形式ファイルを生成し、 配布用ファイルをチェックします

make check は、 make all の後、 make install の前に実行します。 make check では、アプリケーション開発者があらかじめ準備した、 テストプログラムを実行し、問題がないことをチェックします。

make ファイルの約束ごと

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

make の環境変数

環境変数名 機能
VPATH make が検索する検索パス (*.c *.h -lXXX)
makefile で指定されたファイルが存在しない場合、 ディレクトリ検索が行われます。
VPATH = src:../headers
SHELL 命令部で書くコマンドのシェルを指定
デフォルトは /bin/sh で、環境から設定されない
SHELL=/bin/csh
MAKEFLAGS make を起動するときに指定したフラグ

環境変数をサブの make に渡したい場合は、以下のようにします。

export 環境変数 = 値            個別の環境変数を export する
export                          全ての環境変数を export する

make ファイルのデバッグ

オプション 機能
-n make -n で実行すると、実際のコマンドを実行しないで、 コマンドの実行をトレースします。
この結果から、どこで問題が起きたかが解かるでしょう。
-d make -d で実行すると、デバッグ情報を出力します。

make のバグ情報 (HP-UX)

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

make の例

portstat の例

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 

make FAQ

  1. 以下のエラーはどうして起こるのでしょうか?

    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 と 依存関係になり、当然存在しないので、エラーとなります。

make で使う特殊文字一覧

特殊文字 働き
$ 変数の参照のとき、先頭に付け続けて括弧、または中括弧で名前を囲みます
[例] $(LDFLAGS)
* ? ワイルドカードとして2つの文字が使用可能
\ 1行を超えるコマンドまたは変数への代入を記述するための継続を表す
@ コマンド行の行頭にあると、コマンドの実行を出力しない
- コマンド行の行頭にあると、コマンドの実行でのエラーを無視して、 処理を続行する。 .IGNORE: があっても同じです
# コメントを表す
% パターンルールの中で記述可能で、 空でない部分文字列の何とでもマッチできることを表します
TAB 文字 コマンド行の行頭は、TAB で始まらなければならない

Sun make のエラー一覧

エラーメッセージ 対処法
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 でないとか

HP make のエラー一覧

AIX make のエラー一覧



【 関連項目 】

make(1S)

【 関連リンク 】

  1. ORACLE KROWN 検索 11550

【 参考書 】

本 の 名 前 出版社 著者 値段
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 が実行すべきコマンドの一群で、名前と:から成る
偽のターゲット ターゲットファイルを生成しないルールを記述したターゲット

【 注意事項 】