make を使おう

註: この文書は『SoftwareDesign』2001年2月号 に掲載された記事の元となるものに手を加えたものです.

間違いを見つけたら,gotoken@notwork.org宛に御連絡くださると喜びます。

Copyright(c) 2001 by GOTO Kentaro. All rights reserved.


Cで書かれたプログラムを実行するまでには、少なくとも1度はすべてのソース をコンパイルしてリンクしなければなりませんが、一部に修正を加えた場合は 関係するプログラムだけを再コンパイル、リンクすれば十分です。このとき必 要な作業だけを行なうのがmakeの役割です。Makefileにプログラムの依存関係 を記述してやればmakeは必要なコマンドだけを実行します。こうしてmake を 使うことで開発効率が大幅に改善されるのです。


はじめに

Perlの作者である Larry Wall さんはその著書 ``Programming Perl'' [WCO00] の中で、プログラマの3大資質として lazziness(無精)、 impatience(短気)、hubris(傲慢)を挙げています。このうちもっとも重要とさ れる無精は、エネルギーの総消費量を押えるためにどんな努力もしてしまう性 分のことだと説いています(単にズボラなだけではないことに注意 :-)。実際、 ソフトウェアの開発過程は自動化できることをすべて自動化するという無精の 原則で発展してきました。系統だてられた自動化は単純なミスを防ぐ最良の方 法なのです。その最たるものが make でしょう。ちょっとの手間をかけ て Makefile というものを書いておけば、make がコンパイル作 業の多くを自動化してくれるのです。この自動化によって劇的に省略される手 間にくらべれば Makefile の書き方を覚える労力はまさに些細なものだ といって良いでしょう。

1章でみたように分割コンパイルを行なえば、コードの再利用やコンパイル時 間の節約をすることができるのですが、make の助けがなければこれら の利点を発揮するのはかえって面倒なことでしょう。そういったわけで make の使い方、いいかえれば Makefile の書き方はぜひ知って おきたいものです。

make の機能ははそれほどたくさんではありませんが、ここで紹介しき れるほど少なくもないので、本稿ではその基本的なもののうち、とくに知って おきたいところだけを取り上げます。make は Version 7 AT&T UNIX と いう昔のUNIXで初めて登場しましたが、その後いろいろな子孫が生まれ、そ れぞれに独自の進化をとげています。ここではいずれの make にも共通 することを主に取り上げますが、とくにフリーソフトウェアで重要なふたつの 分家であるBSDとGNUの make の違いもいくつか紹介します。

make ってなに?

ひとことでいえば makeMakefile に従ってコマンドを生成す るだけものです。それだけ聞くとスクリプト*1に似ていると思うかも知れませんが、実際はだいぶ違います。ま ずは簡単な例で概略をみてみましょう。

簡単な例

いま、calc というTTYベースのプログラムを作成しているとします。こ のプログラムは、calc.c, eval.c, parse.c をそれぞれコ ンパイルしてできる3つのオブジェクトファイルと libm ライブラリをリンク すれば実行できます。すべてをコマンドラインから与えるなら次の通りです。

$ cc -c calc.c
$ cc -c eval.c
$ cc -c parse.c
$ cc -o calc calc.o eval.o parse.o -lm

calc を作るには calc.o, eval.o, parse.o が必要 です。言い替えれば calccalc.o, eval.o, parse.o に依存します。このことを表す Makefile は次の通りで す。ただし、2行目の先頭はタブ文字です。

calc: calc.o eval.o parse.o
	cc -o calc calc.o eval.o parse.o -lm

この2行だけからなる Makefile を用意して make を実行すれば 次のような結果を見ることになるでしょう。

$ make calc
cc  -c calc.c
cc  -c eval.c
cc  -c parse.c
cc -o calc calc.o eval.o parse.o -lm

make の引数に与えた calcターゲットと呼ばれます。 ターゲットを省略すると Makefile のなかで一番最初に現れるターゲッ トを作ろうとします。いまの場合は Makefile の先頭のターゲットは calc でしたから、次のようにコマンドラインではターゲットを省略す ることができます。

$ make

ところで、*.c から *.o を作る方法を Makefile に記述 していなかったのに、make はきちんと cc を起動しました。あ とで触れるように、make はいくつかの作り方をあらかじめ知っている ので、とくに指定しなければその作り方を実行します。

make の本領

次に、いったん calc を作ったあとで parse.c を修正したとし ます。ふたたび make を実行してみましょう。

$ make
cc  -c parse.c
cc -o calc calc.o eval.o parse.o -lm

make は必要な作業だけを行ないました。このように修正に応じて必要 なコマンドだけを行なうことが make の最大の役割です。もし、 calc が十分に新しく、何も実行する必要がなければ make はそ のことを告げるだけです。

$ make
`calc' is up to date.

さて、いま作っている calc を利用してGUI版の calcg を作った とします。calcgcalc.c の代わりに calcg.c を使い、 さらに widget.c という部品を用意しました。また、libX11とlibXext の関数も使ったので、これらのライブラリもリンクする必要があります。この 場合は Makefile には次の3行を書き足せば良いのです。

calcg: calcg.o eval.o parse.o widget.o
	cc -o calcg \
           calc.o eval.o parse.o widgets \
	    -L/usr/X11R6/lib -lXext -lX11 -lm

そして、calcg を作るには make calcg を実行します。 calcgcalc の規則のあとに付け足したので、calcgMakefile 中の最初のターゲットではないため calcg を省略する ことはできないのです。

$ make calcg
cc  -c calcg.c
cc  -c widget.c
cc -o calcg \
        calcg.o eval.o parse.o widget.o \
        -L/usr/X11R6/lib -lXext -lX11 -lm

このように、Makefile の中には複数のターゲットの作り方を記述する ことができます。ターゲットの名前が書いてある行には依存関係が書かれてい るので依存関係行と呼びます。依存関係行のコロンの右側に羅列されて あるものはコンポーネントと呼ばれます。またコマンドが書いてある行 を(シェル)コマンド行と呼びます。

さらなる機能

Makefile ではマクロを使うこともできます。たとえば cc の代わりに gcc を使いたいとしましょう。このとき Makefile 中のcc をすべてgccに書き換えてしまうと、今度は cc を 使いたくなったときにまた書き直さなくてはならなくなります。正解は cc${CC} と書き直し、CC=gcc としておくことです。 また、すでに一度 make を実行していれば、*.o があるので、な にも起こらないはずです。手動で rm を実行してもいいのですが、消し 忘れると面倒なので、通常は clean というダミーターゲット を 用意しておきます。ついでに calccalcg の両方を作るダミー ターゲットall も用意しておきましょう。all は最初のターゲッ トにしておくと便利でしょう。結局Makefileは次のようになります。

CC = gcc

all: calc calcg

calc: calc.o eval.o parse.o
 	${CC} -o calc calc.o eval.o parse.o -lm

calcg: calcg.o eval.o parse.o widget.o
	${CC} -o calcg calc.o eval.o parse.o \
	   -L/usr/X11R6/lib -lXext -lX11 -lm

clean:
	rm -f *.o calc calcg

試しに使ってみましょう

$ make clean
rm -f *.o calc
$ make
wooky% make      
gcc  -c calc.c
gcc  -c eval.c
gcc  -c parse.c
gcc -o calc calc.o eval.o parse.o -lm
gcc  -c calcg.c
gcc  -c widget.c
gcc -o calcg calcg.o eval.o parse.o widget.o \
        -L/usr/X11R6/lib -lXext -lX11 -lm

ターゲットを複数指定した場合は左から順に作られます。よって上の2つのコ マンドは次の1つのコマンドにまとめることができます。

$ make clean all

また、gcc ではなく再び cc を使いたくなったら、次のコマンド を実行すれば良いのです。

$ make clean all CC=cc

いくつもの重要なことをまだ紹介していませんが、makeMakefile が何であるのか、またどう使うのかを示すことはできました。

makeがやること

make は起動されると次のような原理で動作します。最終的なゴールは 指定されたターゲットを最新のものにすることです。そのためには、まず指定 されたターゲットが依存するコンポーネントを最新にした後、ターゲットとコ ンポーネントの最終更新時刻をくらべ、ターゲットの方が古ければ(つまりター ゲットが最新でなければ)、コマンド行を実行します。

ターゲットの最終更新時刻とはターゲットと同名のファイルの最終更新時刻の ことで、ls -l で得られる時刻情報と同じものです。ターゲットと同名 のファイルが見つからない場合も最新ではないと判断されます。

コンポーネントを最新にする手順は指定されたターゲットを最新にするの同様 です。コンポーネントが更になにかに依存している場合は再帰的に最新にする 手続きが行なわれます。もし、この手順を進めていくうちにターゲットと 同名のファイルが見つからず、なおかつその作り方が見つからない場合は次の ようなエラーメッセージを出して作業を中止します(このメッセージは前節で calcclac と打ち間違えると見ることができます)。

make: *** No rule to make target `clac'.  Stop.

make が実行しているコマンドがエラーを起こすと、やはりそこで作業 を中止します。コマンドがエラーを起こしたとは、そのコマンドの終了ステー タスが0でないことを意味します(コラム「終了ステータス」参照)。

次のように依存関係が循環している場合はエラーとなります。

a: b
b: c
c: a

この場合、いずれのターゲットを make しようとしても、次のような エラーを見ることになります*2

(GNUのmakeの場合)
    make: Circular c <- a dependency dropped.
    make: Nothing to be done for `a'.

(BSDのmakeの場合)
    Graph cycles through a

  `a' not remade because of errors.

ちなみに、依存関係のある仕事をやらなければならない順番に並べるにはトポ ロジカルソート[Oku91]というアルゴリズムが用いられます。

Makefileの書き方

これまで見たように Makefile には3種類のものがあります。すなわち、 依存関係行、依存関係行に関連づけられたシェルコマンド行、そしてマクロ定 義行です。

コメント

Makefile では # から行の終りまでがコメントとして扱われ、こ の部分はmake の動作に影響を与えません。文字通りコメント(注釈)を いれたり、あるいは行を変更する場合にもとの行をとっておくために使われる のはプログラム言語と同様です。

## Choose library
# LDFLAGS = -lcpml
LDFLAGS = -lm

行の継続と注意点

すべてに共通する規則として行の継続が挙げられます。これは、長い行を人間 が読み書きしやすくするために折り返すことができるというものです。すでに calcg の規則で見たように、行末にバックスラッシュ「\を置か れていると、次の行がその行の続きとして解釈されます。ただし、バック スラッシュと改行文字の間に空白を置いてはいけません。これはエディタ の上では発見しにくいので注意が必要です。同様に発見が難しく、またもっと もありがちな間違いは、コマンド行の先頭がタブになっていないことです。こ れらを発見する一つの方法は

$ cat -t -e Makefile

を実行することです[OT91]。このふたつのオプションをつけると cat はタブを ^I と表示し、行の終りを $ と表示します。 コラム 「間違い探し」も参照して下さい。

マクロ

Makefile でマクロを定義するには等号を用います。

COMMONOBJS = eval.o parse.o
LDFLAGS = -lm

参照は $ に続けてマクロの名前を { } で囲んだものを使います。

calc: ${COMMONOBJ} calc.o 
	${CC} -o calc ${COMMONOBJ} ${COMMONOBJ} ${LDFLAGS}

右辺が空のマクロは長さが0の文字列に展開されます。以下の規則では、 B の値は xz になります。

A =
B = x${A}z

マクロはコマンドラインからも与えることができます。

$ make A=y

空白を含む値をコマンドラインで渡すにはクオートしなければなりません。

$ make 'A=y z'

マクロをコマンドラインから与える場合、環境変数として渡すこともできます が、同名のマクロが Makefile で定義されている場合は、そちらが優先 されます。次の実行例を見て下さい。

$ cat Makefile
FOO = xxx
BAR = 
env:
	echo ${FOO},${BAR},${BAZ}
$ env FOO=foo BAR=bar BAZ=baz make
echo xxx,,baz
xxx,,baz

逆にコマンドラインから与えられたマクロが有効なとき環境変数として設定さ れます。

ところで make はこれから実行しようとするコマンドを表示してから実 行しますが、この「コマンドをあらかじめ表示する」という機能を止めたい場 合があります。たとえば上の例のように echo を使って利用者にメッセージを 表示させたい場合などです。このようなときは、コマンドの先頭に @ をおくと、そのコマンドは黙って表示されます。

$ cat Makefile
FOO = xxx
BAR = 
env:
	@ echo ${FOO},${BAR},${BAZ}
$ env FOO=foo BAR=bar BAZ=baz make
xxx,,baz

また、あらかじめ定義されているマクロもたくさんあります。これらのデフォ ルトの値を調べるには、次のようにします。

(GNUのmakeなど)
    $ make -p

(BSDのmake)
    $ make -d g1

あらかじめ定義されているマクロのほとんどは後で述べるデフォルトのサフィッ クスルールに影響を与えるマクロです。一例としては、CCCFLAGSLDFLAGSLDLIBS などがあります。

また特別な意味を持つマクロの一つに SHELL があります。このマクロ は、コマンドを実行するときに使うシェルを表すもので、デフォルトの値は処 理系によって異なります。

マクロの参照時に値の一部を書き換える方法も用意されています。たとえば、 次の例では、SRCS を使って新たなマクロ EUCSRCS を作ります。

SRCS = sora.c irono.c taxi.c
EUCSRCS = ${SRCS:.c=_euc.c}

この Makefile のなかにコマンド echo ${EUCSRC} があれば、次 のような出力をします。

sora_euc.c irono_euc.c taxi_euc.c

つまりコロンの後ろにある文字列を等号の後ろにある文字列で置き換えます。

依存関係とサフィックスルール

依存関係行は次の形をしています。

ターゲット: コンポーネント ...

ターゲットはすでに見たように make の引数として与えれるものですが、 いくつかの便利な書き方が用意されています。まず、複数のターゲットが同じ ものに依存するとき、コロンの左側に並べて書くことでこれらを束ねて書くこ とができます。

calc.o perse.o eval.o: calc.h

また、一つのターゲットに対して二つ以上の依存関係行を書くことができます (ただしコマンド行はそれらの依存関係行に対して多くても一つにしか書けません)。

calc.o: calc.h
calc.o: calc.c define.h

そして、サフィックスルールは知っておかなければ損をするたぐいのものです。 冒頭で示した calcMakefile の例で、*.c から *.o を作る方法をしていないのはデフォルトの規則が仮定されているた めだと述べましたが、これは具体的には次のような規則です。

.c.o: 
	${CC} ${CFLAGS} -c $<

これは なんとか.c から なんとか.o を作るということをサフィッ クス*3についてま とめて書いたもので、サフィックスルールと呼ばれます。

.c.o: 
	${CC} ${CFLAGS} -c $<

ここで登場する $< はターゲットの現在のソースを表す内部マクロです。 具体的に説明しましょう。次のターゲット calc を解決する場合、まず calc.oeval.oparse.o の3つを解決しようとします。

calc: calc.o eval.o parse.o

calc.o を解決する仮定では、まず Makefile 内に依存関係が記 述されていないかを探し、なければデフォルトのサフィックスルールを探しま す。そし て、サフィックスルール .c.o が見つかり、$<に

calc.cが代入され、次にcalc.cを探し見つかれば、

cc が実行されるのです。

さてサフィックスルールを記述するためには make にサフィックスを追 加登録してやる必要があります。たとえば LaTeX のファイル(.tex)か ら、DVIファイル(.dvi)とPSファイル(.ps)を作るためには新しく 3つのサフィックス.tex.dvi.psを登録する必要があ ります。登録するには .SUFFIX: に続けて書きます。ここでは現在のターゲッ トを表す内部マクロ $@ を使っています。

.SUFFIX:  .tex .dvi .ps

LATEX = platex
DVI2PS = dvi2ps

.tex.dvi:
 	${LATEX} $<

.dvi.ps:
	${DVI2PS} $< > $@

make.c.o のような典型的なサフィックスをいく つもあらかじめ知っています。make が何を知っているかは、マクロの デフォルトの値を調べる方法と同じようにして知ることができます。

以上で Makefile の基本的な部分をすべて紹介しました。これだけ知っ ておけば、かなり複雑な Makefile も書くことができると思います。こ れより先は大きなプログラムに適用する場合やライブラリの作成にまつわる話 題となりますので自習にお任せします。

さらに学習するために

make に関する定評のあるテキストは『make 改訂版』[OT91]です。 この本は AT&T の System V Release 4 の make を取り上げています。 本稿ではあまり大きなソフトウェアに適用する際の問題点を挙げることができ ませんでしたが、この本では ar コマンドで作られるアーカイブファイ ルの取り扱いやカレントディレクトリ以外にあるコンポーネントやターゲット を作る方法、そしてその問題点と解決法も書かれています。現在ではさまざま な種類の make が存在しますが、この本を読めば共通する部分を学ぶこ とができるでしょう。共通しない点についての注意点や調査方法も書かれてい ます。

個別の make のコマンドラインオプションや特別な機能は man make で知ることができます。数多く存在するmake のなかでも、PC UNIX が広まった現在では、とりわけBSDとGNUの make が重要さを増し てきています。どちらも条件分岐構文や特別なマクロ代入のような拡張をそれ ぞれのやりかたで採用しています。またいずれの make も並行実行に対 応しています。これはmake の行なう処理の性格上、ディスクアクセス による待ち時間が存在するため、とくに速いCPUを積んだマシンでは効果的で す。

BSDの makepmake から派生したものです。pmake につい てはBSDではオンラインで参照できるチュートリアル[Boo]が用意されて います。その所在は man make を確認して下さい。ただし、これは1987 年頃に書かれたもので新しい機能は載っていません。FreeBSD の場合、ports という名前で知られるソフトウェアをインストールするシステムがあり大変便 利なものですが、これは make を利用して作られています。ports は数 千の Makefile とデータベースファイルからなる巨大な make シ ステムです。これはターゲットとマクロの使い方を約束することで成り立って います。

一方、GNU の make は Linux では標準ですが、他の環境では gmake という名前でインストールされていることもあります。この make は AT&T の make を含む形で大幅に拡張されています。GNU の make の詳細なマニュアル[FSF]もまたオンラインで読むこと ができます。こちらは GNU の優れた文書システムである info 形式で提供さ れています。少々古くなりましたが以前の info の翻訳は書籍として出版され ています。GNU General Public Licence (いわゆるGPL)で配布されているフリー ソフトウェアには GNU の make でなければコンパイルできないものも あります。

またUNIXのプログラミングツール全般にいえることですが、今日では、書籍や Webページのようなドキュメントを参照するほかにフリーソフトウェアで実際 に使われている Makefile を読むという勉強法もあります。ただし、あ まり大きなソフトウェアの場合は Makefile もそれなりに大きく、読む のも大変です。人の書いた Makefile を学習を目的として読むならば、 小さなソフトウェアの Makefile を参考にするのが良いでしょう。また、 具体的な問題にぶつかった時に、似たようなことをしている Makefile にあたりをつけて読むとわかることがあるかも知れません。

make を使ったコンパイルをはじめたならば、あなたのソフトウェア開 発における意識的な自動化の出発点になるでしょう。自動化された手続きを採 用することの必要性と重要性は『達人プログラマー』[TH00]で鋭く言及さ れています。この本は単なる技術書ではなくプログラミングの生産性を高める ための pragmatic (現実的)な tips が満載です。

本稿が make を使うきっかけになり、あなたの無精を発揮させることが できれば幸いです :-)

参考文献

[Boo]

Adam de Boor, ``PMake - A Tutorial'', オンラインテキスト (BSDに付属 FreeBSDならば /usr/share/doc/psd/12.make/ にある)

[CSCOPE]

<URL:http://cscope.sourceforge.net>

[FSF]

``GNU Make Manual'', infoファイル (インストールされていれば、Emacsで M-x infoで見ることができる. 89年版の Richard Stallman, Roland McGrath, ``GNU Make A Program for Directing Recompiltation'', Free Software Fundation の邦訳は出版されている: 戸松豊和『GNUリファレンス マニュアル MAKE』, アディソン・ウェスレイ・パブリッシャーズ・ジャパ ン (1993))

[GLOBAL]

<URL:http://www.tamacom.com/global/>

[Oku91]

奥村晴彦, 『C言語による最新アルゴリズム事典』, 技術評論社 (1991), pp198-199

[OT91]

Andrew Oram, Steve Talbott, ``Managing Projects with make'', 2nd edition, O'Reilly (1991) (邦訳: 矢吹道郎監訳, 菊池彰訳『make 改訂版』, オライリー・ジャパン (1997))

[TH00]

David Thomas, Andrew Hunt, ``The Pragmatic Programmer'', Chapter 8. Pragmatic Project, Adison Wesley (2000) (邦訳: 村上雅章, 『達人プロ グラマー』, 第8章「達人のプロジェクト」, ピアソン・エデュケーション (2000))

[WCO00]

Larry Wall, Tom Christiansen & Jon Orwant, ``Programming Perl'', 3rd edition, O'Reilly (2000) (この版の邦訳はまだありませんが第2版はあります: 近藤嘉雪訳『プログラ ミングPerl改訂版』, オライリー・ジャパン (1997))


コラム「終了ステータス」

UNIXではコマンドは終了ステータス(exit status)というものを返します。終 了ステータスはC言語では exit の引数に与えられたものですが、一般 にはそのコマンドが成功した場合に0を返し、失敗した場合はそれ以外の値を 返すことになっています。終了ステータスはコマンドが成功したか失敗したか を知るための唯一の方法です。終了ステータスは例えば次のようなシェルのワ ンライナーで利用することができます。

$ cc -o hoge hoge.c && hoge test

この例では cc が成功したときにかぎり hoge test が実行され ます。make によるコマンド実行も同様で、失敗した時点で残りの作業 は中止されます。Makefile を書く際に問題になりやすいのは rmmkdir の終了ステータスです。rmclean ターゲッ トに書かれますが、引数で指定したファイルが存在しない場合はエラー、つま り0以外の終了ステータスが返ります。しかし、これは不便なことが多いため、 rm には必ず終了ステータスを0で終らせるためのオプション -f が用意されています。次のような実験をすればわかります。

$ rm xxx
rm: xxx: No such file or directory
$ echo $?
1
$ rm -f xxx
$ echo $?
0

ここで出てくる $? は最後に実行されたコマンドの終了ステータスを表 すシェル変数です。

いっぽう install ターゲットに書かれることの多い mkdir には rm -f のような便利なオプションはありません。このような場合のため に Makefile には「コマンドの前に - をつけるとそのコマンド が失敗しても無視して続きを実行する」という機能が用意してあります。次の 実験では mkdir のエラーが意図的に無視されています。

$ cat Makefile
xxx:
	- mkdir xxx
	touch xxx/afile
$ make xxx
mkdir xxx
touch xxx/afile
$ make xxx
mkdir: xxx: File exists
*** Error code 1 (ignored)
touch xxx

なお終了ステータスは、main 関数で return を呼ぶのは exit を呼ぶのと全く同じことです。mainreturn を場 合、コンパイラに怒られないために main の型は int としてお くのが良いでしょう。一方、main から exit を呼んで終る場合 も main の型を void にすればコンパイル時に起こられるので int にします。


コラム 「間違い探し」

タブと空白と改行前の空白はエディタの上で見分けが付かないことが多いのですが、不幸なこ とに make は継続行の終りとコマンド行の先頭でこれらを区別すること を要求するため Makefile の典型的な間違いの原因になります。本文で 述べたように cat を使って調べるの一つの方法ですが、あまり見やす くはありません。次のRubyプログラムはxtermやktermの上で実行すると、これ らの間違いの候補を目立つように表示するものです。単に実行すれば怪しい空白を 赤く表示し、ページャにパイプすれば太い「#」で置き換えたものを表示しま す。

# mkmfview.rb 
# - enphasize wrong spaces in Makefile

ARGV << "Makefile" if ARGV.empty?

def redspace(n)
  "\e[41m#{' '*n}\e[m"
end

def boldhash(n)
  "#\b#" * n
end

if STDOUT.tty?
  alias em redspace
else
  alias em boldhash
end

ARGV.each do |file|
  IO.foreach(file) do |l|
    l.sub!(/^( +)/){em($1.size)}
    l.sub!(/\\( +)$/){"\\"+em($1.size)}
    print l
  end
end

コラム 「Makefile を作るための道具」

もしあなたが X Window アプリケーションのプログラミングに make を 使いたいと思うなら imake のことを学ぶべきです。imakeIMakefile というひな型から Makefile を作るもので、ローカル の設定に合ったコンパイラのオプションを調べる手間が省けるだけでなく、他 の環境でもコンパイルできる Makefile が書けるという利点があります。 なお通常は imake をコマンドラインから入力することはなく、 xmkmf というコマンドを通じて起動されます。

Makefile を作るための imake は本来汎用ですが、X Window 以 外に適用するのはあまり易しくありません。現在では本特集の3章で取り上げ られる GNU の autoconf を使うのが一般的です。移植を考えている場 合は是非とも知っておきましょう。Makefile の作成を助けてくれるツー ルに makedepend があります。これも X のために作られたものですが、 imake と違いずっと広く使えます。これは

$ makedepend *.[ch]

のようにソースファイルを引数に与えて実行すると、インクルードに関する依 存関係を Makefile に追加します。#ifdef のような部分も理解 するので、環境にあった依存関係が生成されるのが特長です。ただしある程度 大規模なソフトウェアでないとうまみはないかも知れません。

もし人が書いたプログラムや、すでにあるプログラムの Makefile を書 かなければならなくなったときは、どの関数がどのファイルで定義されている かを詳しく知る必要があります。とりあえず grep を使ってみるのも手 です。GNU の grep-l-L オプションはそれぞれ指 定されたパターンを含むファイルと含まないファイルの名前だけを表示します。 プログラムの内部を細かく追跡する必要がある場合は、ctags や GNU の etags といったツールが有効です。これらはC言語の構文を知ってお り、どのシンボルがどこで定義されたかを調べ、viemacs か らブラウズすることを可能にします。もっと手っ取り早くブラウズするには、 cscopehtags といったツールが良いでしょう。 cscope*4 はcursesベー スのCプログラムブラウザです。指定されたシンボルが定義されている場所や 使われている場所をサーチし、そのファイルを見ることができます。 Global*5 の一部である htags はそれを HTML で実現するもので、HTMLブラウザを使ってCのコー ドを探検することができます。


*1 コマンドを羅列した実行可能 ファイル
*2 古い make だともっとわかりにくい エラーになるようです
*3 文字列の終りの部分、つまり .c.o
*4<URL:http://cscope.sourceforge.net>
*5<URL:http://www.tamacom.com/global/>