Badly Formed Macro Assignment Discovery

make


また、例は solaris でのみ確認済みです。




機能

概要

make は、大規模なプログラム開発において、 どの部分を再コンパイルしなければならないか、 あるいは再コンパイルの命令に関する問題を自動的に解決するユーティリティです。

make を使用するには、プログラムを構成するファイル間の依存関係を記述する makefile を作らなければなりません。
makefile は、コンパイルやリンクを指示するためのルールを記述するファイルです。

ドット (.) 以外ではじまるはじめのターゲットをデフォルト・ゴールといい、 make が最終的に到達するゴールです。

make は、まず makefile を全部読んで、インクルードを処理し、 全ての変数と値を内部値としてもち、 暗黙および明示的なルール、 およびターゲットとその必要条件の依存状態のグラフを作ります。

ルールの定義は、以下のとおりです。

以下は、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 する

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

必要条件の自動生成

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

例えば、main.c で #include "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 に置換します。

ワイルドカードの展開は、ターゲット、必要条件、 シェルが実行するコマンドで自動的におこなわれます。
ワイルドカード文字をバックスラッシュを前置することで、無効にできます。

必要条件のためのディレクトリ検索

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 はどの暗黙のルールを使うかを知っていて、 どの必要条件のファイルが存在するかを知っているため、 どれが適用されるかも知っています。

ここで、作成可能とは、ターゲットあるいは必要条件として、 明確に 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.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アセンブラのプログラムへのフラグ
CCC コンパイラ名 cc
CFLAGSC コンパイラへのフラグ
LDFLAGSリンカへのフラグ
CXXC ++ コンパイラ名 c++
CXXFLAGSC ++ コンパイラへのフラグ
GETSCCS からの展開プログラム get
GFLAGSSCCS get プログラムへのフラグ
LEXlex c generator lex
LFLAGSlex プログラムへのフラグ
YACCyacc c generator yacc
YFLAGSyacc プログラムへのフラグ

暗黙のルールの連鎖

ファイルは、暗黙のルールのシーケンスで作成される場合があります。 このようなシーケンスを連鎖といいます。 例えば、上記 .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:

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

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

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

最終ルールの定義

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

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

サフィックスルール

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

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

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

検索アルゴリズム

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

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

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




その他

make のオプション

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

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

make の主なターゲット

ターゲット名説          明
allすべての実行形式ファイルを生成する
ターゲットを指定しない場合、このターゲットが対象になります
distPACKAGE.VERSION.tar.gz で配布用ファイルを生成する
cleanmake で生成したファイル等を削除します
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
MAKEFLAGSmake を起動するときに指定したフラグ
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 = $(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 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 MakeASCIIRichard M. Stallman1800
UNIX System V
プログラマ・リファレンス・マニュアル
AT&TUNIX Press13000
make の達人トッパンC.トンド、A.ネイサンソン、E.ヤント2200

用語集

用語説          明
サフィックスファイル名の拡張子部分:名前が X.Y であると Y の部分
プレフィックスファイル名の拡張子を除いた部分:名前が X.Y であると X の部分
デフォルト・ゴール ドット (.) 以外ではじまるはじめのターゲットをデフォルト・ゴールといい、
make が最終的に到達するゴールです。 通常それは all で定義されます。
ゴールmake が実行すべきターゲットのこと
自動変数 ルールが実行されるたびに計算された値を持つ変数
ターゲット make が実行すべきコマンドの一群で、名前と:から成る
偽のターゲット ターゲットファイルを生成しないルールを記述したターゲット

【 注意事項 】




« The Chromosome ShuffleMonday Morning Buffet »

Clint Is Dead, Long Live Clint

By Carl Zimmer | August 31, 2005 1:01 pm

Clint, the chimpanzee in this picture, died several months ago at a relatively young age of 24. But part of him lives on. Scientists chose him–or rather, his DNA–as the subject of their first attempt to sequence a complete chimpanzee genome. In the new issue of Nature, they’ve unveiled their first complete draft, and already Clint’s legacy has offered some awesome insights into our own evolution.

The editors of Nature have dedicated a sprawling space in the journal to this scientific milestone. The main paper is 18 pages long, not to mention the supplementary information kept on Nature’s web site. In addition, the journal has published three other papers that take a closer look at particularly interesting (and thorny) aspects of the chimpanzee genome, such as what it says about the different fates of the Y chromosome (the male sex chromosome) in chimpanzees and humans. Other scientists offer a series of commentaries on topics ranging from brain evolution to chimpanzee culture. The journal Science has also gotten in on the action, with a paper comparing the expression of chimp and human genes as well as comments on the importance of chimpanzee conservation and research. (Thankfully, some of this material is going to be made available online for free.)

Why all the attention to the chimpanzee genome? One important reason is that it can tell us what parts of the human genome make us uniquely human–in other words, which parts that were produced by natural selection and other evolutionary processes over the past six million years or so, since our hominid ancestors diverged from the ancestors of our closest living relatives, chimpanzees. (Bonobos, sometimes known as pygmy chimpanzees, are also our first cousins, having split off from chimpanzees 2-5 million years ago.) Until now, scientists could only compare the human genome to the genomes of more distantly related species, such as mice, chickens, and fruit flies. They learned a lot from those comparisons, but it was impossible for them to say whether the differences between humans and the other species were unique to humans, or unique to apes, or to primates, or to some broader group. Now they can pin down the evolutinary sequence much more precisely. Until scientists rebuild the Neanderthal genome–if they ever do–this is going to be the best point of comparison we will ever get. (For more of the background on all this, please check out my new book on human evolution, which will be out in November.)

The analysis that’s being published today is pretty rudimentary. It’s akin to what you’d expect from a reporter who got to spend an hour flipping through 10,000 pages of declassified government documents. But it’s still fascinating, and I’d wager that it serves as a flight plan for research on the evolution of the human genome for the next decade.

First off, scientists can get a more precise figure of how different human and chimpanzee DNA is. In places where you can line up stretches of DNA precisely, there are 35 million spots where a single "letter" of the code (a nucleotide) is different. That comes to about 1.2% of all the DNA. The scientists also found millions of other spots in the genomes where a stretch of DNA had been accidentally deleted, or copied and inserted elsewhere. This accounts for about a 3% difference. Finally, the scientists found many genes that had been duplicated after the split between humans and chimps, corresponding to 2.7% of the genome.

By studying the human genome, scientists have also gotten a better picture of the history of the genomic parasites that we carry with us. About half of the human genome consists of DNA that does not produce proteins that are useful to our well-being. All they do is make copies of themselves and reinsert those copies at other spots in the genome. Other animals have these virus-like pieces of DNA, including chimpanzees. Some of the genomic parasites we carry are also carried by chimpanzees, which means that we inherited them from our common ancestor. Many of these parasites have suffered mutations that make them unable to copy themselves any longer. But in some cases, these parasites have been replicating (and evolving) much faster in one lineage than the other. One kind of parasite, called SINES, have spread three times faster in humans than in chimps. Some 7,000 genomic parasites known as Alu repeats exist in the human genome, compared to 2,300 in the chimp genome. While a lot of these parasites have no important effect on our genome, others have. They’ve helped delete 612 genes in humans, and they’ve combined pieces of some 200 other genes, producing new ones.

In some cases, the interesting evolution has occurred in the chimpanzee lineage, not in our own ancestry. Scientists have noted for a long time that the Y chromosome has been shrinking for hundreds of millions of years. Its decline has to do with how it is copied each generation. Out of the 23 pairs of our chromosomes, 22 have the same structure, and as a result they swap some genes as they are put into sperm or egg cells. Y chromosomes do not, because their counterpart, the X, is almost completely incompatible. My Y chromosome is thus a nearly perfect clone of my father’s. Mutations can spread faster when genes are cloned than when they get mixed together during recombination. As a result, many pieces of the Y chromosome have disappeared over time, and many Y genes that once worked no longer do.

Scientists have discovered that Clint and his fellow chimpanzee males have taken a bigger hit on the Y than humans have. In the human lineage, males with mutations to the Y chromosome have tended to produce less offspring than those without them. (This is a process known as purifying selection, because it strips out variations.) But the scientists found several broken versions of these genes on the chimpanzee Y chromosome.

Why are chimpanzees suffering more genetic damage? The authors of the study suggest that it has to do with their sex life. A chimpanzee female may mate with several males when she is in oestrus, and so mutations that give one male’s sperm an edge over other males are ben strongly favored by selection. If there are harmful mutations elsewhere on that male’s Y chromosome, they may hitchhike along. We humans are not so promiscuous, and the evidence is in our Y chromosome.

As for the mutations that make us uniquely human, the researchers point out some suspects but make no arrests. The researchers found that a vast number of the differences between the genomes are inconsquential. In other words, these mutations didn’t have any appreciable effect on the structure of proteins or on the general workings of the human cell. But the scientists did identify a number of regions of the genome, and even some individual genes, where natural selection seems to have had a major impact on our own lineage. A number of these candidates support earlier studies on smaller parts of the genome that I’ve blogged about here. Some of these genes appear to have helped in our own sexual arms race; others created defenses against malaria and other diseases.

When scientists first lobbied for the money (some twenty to thirty million dollars) for the chimp genome project, they argued that the effort would yield a lot of insight into human diseases. The early signs seem to be bearing them out. In their report on the draft sequence, they show some important genetic differences between humans and chimpanzees that might have bearing on important questions such as why we get Alzheimer’s disease and chimps don’t and why chimpanzees are more vulnerable to sleeping sickness than we are, and so on.

There is also a lot of variation within our own species when it comes to disease-related genes, and here too the chimpanzee genome project can shed light. The researchers show how some versions of these genes found in humans are the ancestral form also shared by chimpanzees. New mutations have arisen in humans and spread in the recent past, possibly favored by natural selection. The ancestral form of one gene called PRSS1, for example, causes pancreatitis, while the newer form does not.

But our genetic defenses and weaknesses to diseases aren’t really what we’d like to think make us truly, uniquely human. The most profound difference between the bodies of humans and chimpanzees is the brain. Much of the evolution that’s been going on in genes expressed in the brain has been purifying. There are a lot of ways to screw up a brain, in other words. But some genes appear to have undergone strong positive selection–in other words, new mutation sequences have been favored over others. It’s possible that relatively few genes played essential roles in producing the human brain.

You can feel the excitement of discovery thrumming through these papers, but it comes with a certain sadness as well. It doesn’t come just from the fact the chimpanzee whose DNA made this all possible died before he became famous. Lots of chimpanzees are dying–so many, in fact, that conservationists worry that they may become extinct from hunting, disease, and habitat destruction. And once a species is gone, it takes a vast amount of information about evolutionary history with it.

I was reminded of this fact when I read another chimpanzee paper that appears in the same issue of Nature, reporting on the first fossil of a chimpanzee ever discovered. It may be hard to believe that no one had found a chimp fossil before. A big part of the problem, scientists thought, was that chimpanzees were restricted to rain forests and other places where fossils don’t have good odds of surviving. The fossils that have now been discovered don’t amount to much–just a few teeth–and they raise far more questions than they answer. They date back about 500,000 years, to an open woodlands in Kenya where paleoanthropologists have also found fossils of tall, big-brained hominids that may have been the direct ancestors of Homo sapiens. So apparently chimpanzees once coexisted with hominids in the open woodlands that were once thought to be off-limits to them. More chimpanzee fossils will help address this puzzle, but they may never fully resolve it.

The chimpanzees of Kenya became extinct long ago, and now other populations teeter on the brink. To make sense of Clint’s genome, scientists need to document the variations both within and between chimpanzee populations–not just genetic variations, but variations in how they eat, how they organize their societies, how they use tools, and all the other aspects of the lives. If they don’t get that chance, the chimpanzee genome may become yet another puzzling fossil.

CATEGORIZED UNDER: Evolution

One thought on “Badly Formed Macro Assignment Discovery

Leave a Reply

Your email address will not be published. Required fields are marked *