$ make_ がもつidea

この版
http://member.nifty.ne.jp/Static/product/tosync/IwiM.html
http://member.nifty.ne.jp/Static/product/tosync/make.tex.lzh
最新版
http://member.nifty.ne.jp/Static/product/
以前の版
なし
著者
.exe (Static) <gStatic@infoseek.jp>

Copyright(C)Static 1998-2004, All Rights Reserved.

この文書は、makeがshellにおけるリダイレクトを代表とする様な基本的かつ固有の概念を持ちつつもツールとして存在している、UNIXシステムにおいては、風変わりなコマンドである、と結論しています。


目次

  1. はじめに
  2. 生い立ち
  3. 全体的な動作
  4. 要素となる規則
    1. 依存規則
    2. 生成規則
    3. 推論規則
  5. まとめ
  6. さいごに

付記

  1. 参考文献
  2. 謝辞
  3. 改訂履歴

はじめに

標準的なUNIXシステムであればどの環境においても使用することができるコマンド、make。 現在では、その有用性のため各種のOSに移植されており、本来の「効率的なプログラムの構築」という用途以外にも幅広く使われている。 その有用性とは何だろうか。また、逆に限界はどういったところにあるのだろうか。 それらを通してmakeが持つ基本的な概念について考察する。

生い立ち

最初のmakeはAT&TにおいてStuart I. Feldmanによって制作された[Tondo]。 そのmakeが広く知れ渡ることになったのが、UNIXのSystem V Release 2でのことである。 その後、UNIXシステムのSystem VとBSD(Berkeley Software Distribution)への分裂によりmakeが持つ機能もそれと共に分化した。 現在では、それらのmakeは基本的なところでは一致しているが細かな機能ではかなり異なったものとなっている。

また、標準のmakeを改良・発展させたものも数多く存在している。 有名なものだけをあげても、

などがある[Oram pp.149-162]。これらの事実はmakeが十分に改良、または革新されるべき点を有していることを示している。

全体的な動作

makeを起動させると、

    $ make

makeは現在のディレクトリから"makefile"というファイルタイトルをもつファイルを検索し、それを読み込む。

ファイル中の依存規則から、最終的なターゲットを根とする依存関係のツリーを構築する。 そのツリーにおいて全てのファイル間において正当な関係が成立するように生成規則を実行する。 (ここで、2ファイルA, B(Aの方が依存関係ツリーにおいてより末端とする)間において正当な関係が成立するとは、ファイルXのタイムスタンプを取り出す関数をtime(X)とすると、

	time(A) ? time(B)

という関係が成立することである[福崎 pp.193]

生成規則にマクロが含まれていた場合は

    main.o : main.c
        $(CC) $(CFLAGS) $*.c

実行直前にそれらを解決する。

    main.o : main.c
        gcc -c main.c

"$*"はmakeに組み込みのマクロで、これはターゲットのファイルタイトルから接尾辞(surffix)(".o")を除いた部分を表す。 この様なマクロによって記述間違いを軽減できたり、推論規則の現実的な活用が可能になっている。

    .c.o :
        gcc -c $<

ここでの"$<"は現在のディレクトリにおいて、ソースコードと較べて正当でないオブジェクトコードに対応するソースファイルに展開される。

    .c.o :
        gcc -c main.c utl.c

この他にもサフィックスルールやアーカイブの制御などmakeを使う上で必須となる事項(方法)が余りあるが、本論にはそぐわないので割愛する。

要素となる規則

依存規則

ターゲットが依存しているソースを指定するというこの規則はある意味、あいまいさを有している。 なぜなら、何かに全く依存していないリソースというものは存在しないからである。 makeでは、これを「変更することによってターゲットを変更しなければならなくなるリソースをターゲットに依存するソースとする」 として、このあいまいさを排除しようとしている。つまり、以下のように"makefile"中に記述する

    main.c utl.c : stdio.h stdlib.h

この方法は確かに記述時点では非常に正確にすることができる。 しかし、この記述は未来のリソースに対する依存関係までは含んでおらず、未来の依存関係に対してはあいまいであるということができるであろう。

別のあいまいさはターゲットがソースの何に依存しているかということに起因しているである。 makeは確かに、ソースを「変更することによってターゲットを変更しなければならなくなるリソース」と定めているが、 ここでの「変更」はそのソースのタイムスタンプであって内容ではない。 極端な場合(例えば処理系での時間が逆行した場合)、makeは期待された働きをしなくなるということである。 すなわち、処理系時間の不可逆行性(最も新しいファイルは最も最近に変更されたファイルである)により、このあいまいさを排除しようとしていることがわかる。

さて、依存規則におけるmakeの最も重要な作業は「依存関係ツリーの構築」である。 これによって、makeは最終的にすべてのターゲットが正当となるような順序で生成規則を実行することができる。 この作業は、何れにも依存していないソースが見つかるまで構築を続けるという点で終端記号を認識するまで一連の構文解析を続けるコンパイラと動作がよく似ている。

生成規則

makeにおける生成規則の定義は現在のmakeたらしめた原因の1つであるといってもよいだろう。 なぜならば、生成規則という名称ながらも、必ずしもそこでの命令群がターゲットを生成しなくてもよかったからである。 これは、"makefile"がプログラム可能な言語としての条件の1つ、「分岐命令」を有していることになる。 このことによって以下のようにmakeにプログラムのインストール(UNIXシステムにおいては大抵ソースコードのコンパイルとリンクである)をさせることも可能になるわけである。

    $ make install

もし、makeが生成規則を依存規則と強く結合させたものとしたならば現在のmakeはまた違った様子を見せただろう。

推論規則

一般には、推論規則というものはある接尾辞を有するファイルから 同じファイルタイトルで異なる接尾辞を有するファイルを生成するための規則を決定するための仕組みであり、依存規則を限定的に拡張したものである。 しかし、その拡張は非常に重要な要素を含んでいる。

ここで「同じファイルタイトル」という表現を用いたが、 これはすなわちmakeはあるファイルに対する生成規則を決定するために同ディレクトリ内でのみ異なる接頭辞を有するファイルを 検索するということである。これは、ファイルの生成元と生成先が同じディレクトリ内に存在するということに他ならない。 これは一種の制約である。実際、makeはVPATH(View PATH)というマクロや再帰的な実行の概念を導入しており、異なるディレクトリ間での 操作も行えるようになっていたが、大規模なプロジェクト管理においては役不足であると指摘されることも多い。

さて、これらはmakeの内部にかかわる問題であるが、それはさておき、このmakeの推論機構が実際のUNIXシステムにおいて どのような意味合いを持つのであろうか。makeは各種多様な使い方がされているが、 詰まるところ依存関係に基づく命令生成器といってもよいだろう。なぜならば、 いかなる場合においても結局は、依存関係を正当にするための生成規則、すなわち、システムにおけるコマンドを 実行するか、しないか、のどちらかだけだからである。

そのようにmakeをみた場合、makeはUNIXシステムに存在している他のコマンドと較べると一線を画した存在であるということがわかる。 簡潔に表現するならば、通常のコマンドは多(1)対1の関係を持つ写像器であり、makeは多対多の関係を持つ写像器だからである。 具体的に考えてみよう。たとえばsedで考えた場合、通常

    $ sed -e 's/です/だ/g' old.txt > new.txt

という様に使われる。ここではリダイレクト先としてファイルを指定したが、 そうでなくてもこのコマンドは1つのファイルの内容を別の1つのファイルに写すということと等価であると考えることができる。 そして、"old.txt"の代わりに"*.txt"を用いた場合

    $ sed -e 's/です/だ/g' *.txt > new.txt

現在のディレクトリ内の接尾辞、".txt"をもつすべてのファイルが"new.txt"という一つのファイルにその結果がまとめられることになる。 これは、多くのファイルの内容を1つのファイルに写すということと等価であると考えることができる。

もう少しこれらの例を検討してみよう。sedは「Stream EDitor」というテキストを編集するEditorの一種である。 その基本的な動作は「与えられた1行のテキストを指定された操作に基づいて編集する」というものである。 確かにそれ以外の動作(例えば、指定されたファイルにテキストを出力する)もするがそれは付加的なことであり、無視することができる。 するとsed自身は1対1の写像器、具体的には編集器として動作することがわかる。そして、その対象がテキストであるということになる。 ならば、先ほどの「多くのファイルの内容を1つのファイルに写す」という多対1の関係は"*.txt"(パターン展開)と'>'(リダイレクト)により実現されていることになる。 そして、これらはそれぞれ

パターン展開1対多の写像器
リダイレクト多対1の写像器
多くのコマンド1対1の編集器(写像器)

の機能を持っていることがわかる。(これらの写像は一方向であることに注意したい)

このことはsedだけのことではなく、多くの他のコマンドに関しても適用することができる。その編集器としての対象が テキストであったり、ファイルシステムであったりするだけである。

ひるがえって、これらの視点からmakeを考えてみよう。 すると、前述のようなmakeの動作はパターン展開やリダイレクト、そして単純なコマンドではいかなる組み合わせをもってしても実現することができないことがわかる。 これは、単純に表現すればリダイレクト先にパターン展開を指定することができないことが原因となっている。 別の表現を用いれば、逆方向への写像は許されていないということである。

では、makeはどのような性質を持っているのであろうか。 "makefile"中に以下のように推論規則を用いた記述を行ったとすると、

    .c.o :
        gcc -c $<

この"makefile"はmakeに対し、現在のディレクトリ内の全てのオブジェクトファイルが正当なものになるように生成規則を実行させる。 それは同じファイルタイトルをもつファイルに対し実行される。 つまり、この"makefile"によって現在のディレクトリの全てのソースコードがオブジェクトコードと関連づけられたことになる。 この関連づけはgccという1つのコマンドによって行われたのではなくmakeによって行われたということもわかる。 すなわち、

make多対多の限定的な写像器

と対応づけることができる。

ここで、注意したいことはmakeを「限定的な」写像器と位置づけたことである。 なぜならば、写像器にしてはあまりにもその性質が限定されているからである。 その限定要素は前述の「推論規則が同じファイルタイトルを持つファイルを対象とする」ということ、 つまりその関係が平行になっていることである。 確かに、その平行を破ることも別の依存規則を用いれば十分可能であるが、それは逆に推論規則の能力を超えているということを示している。

しかしながら、すなわち、先ほどの例に関して述べるならば、推論規則におけるこの制限はUNIXシステムの大抵のコンパイラがソースコードをコンパイルした際にその結果を、 接頭辞を付け替えたファイルに保存するという暗黙の仮定において初めて実用に耐えうるものになるのであるが、 実際そうであるので、makeが使えなくなるほどの欠点ではなくなっている。

まとめ

UNIXシステムにおいて編集器をコマンド、写像器をshell固有の操作子であると考えるならば、 makeはパターン展開やリダイレクトなどのshellがもつ機能、すなわち基本機能と同一視できる要素を有している。 また、プログラム言語としての性質も有しているので従来の操作子という概念では統括することができない。 そのため、UNIXシステムにおいてはツール、すなわち、コマンドという位置づけになっているのではないかと思われるが、 しかしながら、それはmakeの、他のコマンドの面倒見の良さからすると不十分な表現であると思われる。

もう一つの重要な概念は、依存関係である。 未来の依存関係は関知しないとしながらも、 現在のネットワークがリンクにおいて成り立っていることを考えれば、 ファイルのつながりについて扱っていることは非常に意義のあることであると思われる。

最後に、makeがそれとして存在している(ideaから立ち上がれた)要因はUNIXシステムの様々な暗黙の条件を受け入れたことである[Oram pp.88]と思われる。

おわりに

パターン展開もリダイレクトもshellの機能の一部である。 shell自身もUNIXシステムのカーネルから見れば他のshell上で使用されるコマンドと本質的な違いはない。

しかしながら、shellを基礎として構築された環境から見ればファイルを写像する機能において多対多の写像を行う機能がshellに欠けているという状態は好ましくない。 なぜならば、そのような基本的な機能が基礎として用意されていなければ、結果として、そこでの活動に欠陥的な乱れが生じるからである。 具体的な資料がないので明言できないが、Stuart I. Feldmanはそのような状況を感知してmakeというツールをUNIXシステムがまだ初期のうちに制作したのではないだろうか。 逆にいえば、この機能をshellに組み込まず、ツールの扱いとしたのは、システム開発の難しさを表しているのではないだろうか。


参考文献

謝辞

まず、より幅広い意味での“free”なコードを提供しているFree Software Foundation、現在の環境に感謝したい。 そしてなにより、この文書を書くきっかけの形成を強く刺激してくれたChemist.Erに感謝したい。

改訂履歴

2001/08/03
HTML化
2001/07/22
急遽書き上げ、レポートとして提出