Think GNU 第 4 回

□GNU 史探訪 (1) と GNU コーディング規則□

CUJJ#8/'90.5.18 発売号掲載

0. はじめに

 今年前半の GNU の大きなイベントである (USENIX で開催される)GNU BOF が終わり、ようやく落ちついた。プロジェクト GNU はようやくここに来て、GNU カーネルに取り掛かろうとしている !

 今回から GNU の歴史を少しずつ散策してみたい。一般に歴史を紐解くとなると、成熟した分野に限られる。プロジェクト GNU はまだまだ成熟した分野とはいい難い。しかし、プロジェクト GNU が始まって 6 年目、ここでこれまでの歴史を、さまざまな出来事が忘れ去られる前にまとめておくことには意義があるだろう。

1.GNU 史探訪

 以前は、GNU の歴史を調べて紹介しようとは思わなかった。確かにプロジェクト GNU の歴史を調べる上で学ぶことはいくつかあるだろう。しかし、歴史をたどることは、特にコンピュータ科学のような未成熟な分野に関しては、基本的に「後ろ向きの作業」になるであろう。こういったコラム自体もそれほど生産的ではないかもしれない。クヌース先生の本★1 にも (Jeff Ullman の講演の中の引用として) 次のような格言が出ていた。

仕事ができるやつは仕事をする。仕事のできないやつが教師になる。教師にさえなれないやつは芸人になる。

 比喩としては非常にきついが、これは一面の真理をついているかもしれない。このコラムを書くことが教師としての役割も担っているとは思わないが……。

 さて、なぜ歴史を調べるようになったかというと、ボストンにいた時に「gcc(GNU C コンパイラ) がなぜ作られたのか」「どうしてこういう構造になったのか」を知りたくなったからである。

 「果たして、Richard Stallman 1 人で gcc を作成したのだろうか ?」この回答の一部は gcc のソース・コードのコメントを見れば多少わかる。さらにソース・コードと共に提供される ChangeLog というファイルを覗けばある程度、推測がつく。これには誰がどのファイルのどの関数を修正したかが記述されている。

 そのために資料を少しずつ集め、帰国してからプロジェクト GNU の紹介のたびごとにその資料をまとめてきた。プロジェクト GNU が開始される前が次に話す「プロジェクト GNU 前夜」である。

 ちなみに、この ChangeLog というファイルについて説明しておこう。これは、プロジェクト GNU で一般的に用いられる修正履歴を記録しておくファイルで、GNU のツールには全てこの ChangeLog というファイル名を統一して付け、作成している。

 このファイルの作成を支援する GNU Emacs 用のライブラリ (ライブラリを書くための Lisp に似た言語「GNU Emacs Lisp」(通称、elisp[いーりすぷ] で書かれている) が Free Software Foundation からの標準配布テープに入っている。時間と修正者名、括弧の中にはその人のログイン名と修正を行なったホスト名が入る。ここまでは GNU Emacs でサポートし、それに続く修正内容は自分で入れる。以降に示す例は GNU Emacs の C 言語のソース・ディレクトリに入っている ChangeLog である。

〈ChangeLog ファイルの例〉

Sun Aug 13 14:50:28 1989 Richard Stallman(rms at hobbes.ai.mit.edu)

* xfns.c(Fx_proc_mouse_event,Fx_get_mouse_event):
Set new var Vx_mouse_abs_pos,for sake of xmenu.c.
(syms_of_xfns):Make this a Lisp variable.

* x11fins.c(Fx_get_mouse_event):Fix like Fx_proc_mouse_event.

1-1. プロジェクト GNU 前夜

 プロジェクト GNU の特に前夜は文字通り rms の個人史である。さらにプロジェクトが立ち上がってからしばらくの間も、rms の個人史とオーバーラップする場面が多い。

 「ハッカー英語辞典」2 によると、趣味は国際フォークダンス、フライト (飛行機に乗って空を飛ぶことだろう)、料理、物理学、リコーダ、言葉遊び、SF 小説ファンとのこと。ちなみに飛行機 (シングル・エンジン) のライセンスを持っている。

プロジェクト GNU 歴史年表 * 未確認

年 代     出 来 事
1953(*)    Richard M.Stallman がニューヨークのマンハッタンに生まれる。
1970       IBM NY Research Center でアルバイトをする。
1971       MIT の研究員となる
           (〜'84 まで MIT に勤める '71〜'81 まで PDP-10 を使用していた)。
1974       ハーバード大学の物理学科を卒業。
           Teco(拡張性のあるエディタ) の改良と Emacs 開発を開始する。
1975       Emacs/ITCS(Twenex OS) の移植。
           Teco の改良をしながら Emacs を保守する。
           膝を故障した際にリコーダ演奏コースをハーバード大学で習得する。
1976       Teco を改良 (名前付き関数) する。
           Emacs の配布 (最初のバージョン) を開始する。
1977       Twenex(OS の名前) 版 Emacs を作成した。
1979       James Gosling が Unix Emacs を CMU で開発する。
           これは Gosling Emacs と呼ばれる。
           Unipress Emacs の元にもなった。
1982〜83   Symbolics マシンを調べて LMI 社へ対応する。
1983       MIT で LMI を使わなくなったことと、
           LMI が一人立ちしたことから、LMI 対応を打ち切る。
           共有精神を取り戻すべく GNU プロジェクトを思い立つ。
1984 前半  MIT を辞める。それまで使っていた MIT の部屋は
           使いたかったら使ってよい、とのことで継続して部屋を使用。

 Richard が生まれたのは 1953 年であるから、今年 37 才。高校生 (16 才) のころ、IBM の研究所でアルバイトをした時にコンピュータに初めて触れた。★3 10 年間ほど汎用計算機 (DEC PDP-10) をメインに使っていたことがわかる。◆1

 Richard が MIT の研究員時代にやった仕事で Emacs 以外に興味深い業績は、Guy Steel Jr.(おそらく Lisp の処理系を作っている Lucid 社から、コネクション・マシンを作っている Thinking Machine 社に移ったという人である)、Sussman 教授らと世界で初めての 1 チップ Lisp マシンを作ったことだ。それらは Scheme 79 と Scheme 81 という名前で、LSI のマスク・パターンの写真が Sussman 教授の MIT の部屋の前にある。それには開発に参加した時の名前が左上すみに書いてある。両方のマスク・パターンに rms の名前があった。★4

2.GNU コーディング規則

 最新の GNU ソフトウェアを置いてある MIT の prep マシンに standard.text というファイルが置いてある。これには GNU ソフトウェアを作成する上での注意点やコーディングの規則が書いてある。Unix のバグから移植性をどうとらえているかまでがわかる。その日本語訳をここに示す。◆2

2-1.GNU コーディング規則 1990 年 1 月 12 日更新

◎所有権のあるソフトウェアを参照してはならない

 GNU の仕事のために、あるいは GNU の仕事をしている間は、どのような状況でも Unix のソース・コードを参照してはならない (これはあらゆる所有権のあるプログラムに関して当てはまる)。

 Unix のプログラムの内部構造の漠然とした知識がある場合、Unix の代替品を作成できない、ということはない。全く異なるソースプログラムを作成実現して、代替品を構成すること。そうすれば最終的に、オリジナルの Unix 版とは無関係の全く異なる部分から構成されるものになる。

◎どうせ新しく作るのだから特徴のあるものを作ろう

 例えば、Unix のユーティリティは一般にメモリを極力使わないように設計されている。そうしないで処理速度を上げたいプログラムならば、それは全くその Unix ユーティリティとは異なったものになる。メインメモリ上に全ての入力ファイルを展開して、(stdio 構造を使う代わりに) そのメインメモリ上をスキャンできる。Unix プログラムよりも新しく、もっと賢いアルゴリズムを採用した方法を用いること。中間ファイルを使わないこと。2 回のパスではなく 1 回のパスでそれを実行しなさい (我々はアセンブラの中でこのことを実際に行なった)。

 また逆に、処理速度ではなく簡潔さを強調してもよい。幾つかのアプリケーションにとって、今日のコンピュータは、さらに簡潔なアルゴリズムを適用するのに十分な性能を備えている。

 あるいは一般性を追求すること。例えば、Unix プログラムでは、よく静的テーブルや決まったサイズの文字列を使っているが、これらはある上限を設けてしまう。入力ファイルに null 文字や変な文字が入ってきても扱えるように注意しなさい。拡張性を高めるためにプログラミング言語を設計して、その言語でプログラムの一部を書きなさい。

 あるいは、プログラムの一部を独立した有用なライブラリに追加しなさい。または、空きメモリを正確に管理するのではなく、簡単なガーベージ・コレクタを使ったり、obstack のような新しい GNU の機能を使いなさい。

◎寄付されたコードを無制限に使ってはならない

 作業中、そのプログラム向けのソース・コードを誰かが寄付してきた場合、採用するに当たって覚書を入手する必要がある。プログラムを寄付した人ひとりひとりに、ある覚書に署名してもらわなければならない。プログラムのタイトルを明確にするためである。代表の作成者 1 人だけでは不十分である。

 従って、他の人々から寄付されたソース・コードを我々のソース・コードに追加する前に、我々にご相談いただきたい。そうすれば、我々はその法的文書 (覚書) を入手する手配をすることができる。それから、実際に寄付されたソース・コードを使う我々が、署名入りの文書を受け取った旨をそちらに伝えるまで待ってほしい。

 この手続きは、プログラムをリリースする前でも、した後でも適用される。バグを修正するために差分ファイルを受け取り、それが重要の変更な場合には、我々はそのための法的文書が必要である。

 2〜3 行の変更ならば法的文書は不要である。というのは、著作権の目的からこれは重要ではないからである。提示された提案全てがアイデアであった場合も法的文書は不要である。例えば、ある問題を別の方法で解決しているケースである。

 このようなことは精神上良くないことはわかっている。我々にとってもつらいことである。しかし、このようにして合法文書を入手しなければ、危険な立場になる。例えば、ボランティアの雇い主が著作権放棄に署名しなかった場合はどうするのか ? おそらくもう一度ソース・コードを書き直さなければならないだろう !

 本当に最悪の事態は、共に作成したその他のボランティアのことを忘れてしまったという場合である。最終的に我々は、いつの日にか法廷で立ち往生してしまうであろう。2-

2-2. 互換性について

◎GNU ソフトウェアはどの標準 (POSIX、ANSI、BSD) に従うべきか ?

 ある例外を除いて、GNU 用のユーティリティ・プログラムやライブラリは Berkeley Unix の中のそれらと上位互換があり、ANSI C の機能を使うのならば ANSI C と上位互換を持たせるべきである。

 これらの標準が衝突する時は、各々と互換性のあるモードを作成すると便利である。

 ANSI C や POSIX はいろいろな種類の拡張を禁じている。どのような方法でも構わないから拡張は自由に行ない、-ansi や -compatible オプションで切り替えられるようにしなさい。しかし、拡張して元のプログラムやスクリプトを破壊するような重大な可能性がある場合は、その拡張は本当の上位互換ではない。そのインタフェースを再設計しなさい。

 ユーザだけ (プログラムやコマンド・ファイルとして使わない場合) が使う機能で、Unix 上でうまく実現されていない場合は、全く異なるもので相対的に何かより良く、完全にしかも自由に置き換えてみなさい (例えば、vi は Emacs に代替できる)。しかし、互換性のある機能をも備えることは良いことである (フリーな vi のクローンがあるから、我々はそれも提供している)。

 Berkeley Unix に役に立つ機能がさらにあれば好都合である。Unix にはないプログラムがさらにあれば役に立つかもしれないが、我々が最初に求めることは通常、Unix に既に備わっているものの代替品を作成することである。

2-3.GNU コーディング・スタイル (C 言語)

◎関数の書き方

 重要な点として、まず、C の関数の始まりを表す開き大括弧 { を 1 カラム目に入力し、その他の開き括弧や開き丸括弧 ( を 1 カラム目に入力しないこと。ツールによっては 1 カラム目の開き大括弧を見て C 関数の開始と判断しているものがあり、その方法で清書していないコードに対して正しく動作しないからである。

 関数定義は、1 カラム目に関数の名前を与えることも重要である。ctags や etags はこれら以外の関数定義を認識することができない。従って、適切なフォーマットはこのようになる。

static char*
concat (s1, s2)   /* Name starts in column zero here */
  char *s1, *s2;
{              /* Open brace in column zero here */
 ...
}

 これとは別に、我々は次のようなスタイルを好んで用いている。

if (x < foo (y, z))
 haha = bar[4] + 5;
else
 {
  while (z)
   {
    haha += foo (z, z);
    z--;
   }
  return ++x + bar ();
 }
◎カンマの入れ方と文の分割

 開き中括弧の前やカンマの後に空白があれば、プログラムの読みやすさを向上させることが知られている。特にカンマの後の場合は。

 式を複数行に分割する時は、演算子の後ではなく演算子の前で区切りなさい。その正しいやり方を次に示す。

if (foo_this_is_long && bar > win (x, y, z)
    && remaining_condition)

 異なる優先度の演算子を同じレベルで字下げしてはならない。例えば、このように記述してはいけない。

mode = (inmode[j] == VOIDmode
      || GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])
     ? outmode[j] : inmode[j]);

 この代わりに、字下げは入れ子を表すので余分な括弧を使うこと。

mode = ((inmode[j] == VOIDmode
      || (GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])))
     ? outmode[j] : inmode[j]);

 Emacs(訳者注 C モードという C 言語サポート・ライブラリで) はコードを適切に字下げしてしまうので、中括弧を余分に挿入しなさい。例えば、次の字下げは手操作の場合にはうまく行なえるが、Emacs では乱雑 * にしてしまう。

v = rup->ru_utime.tv_sec * 1000
         + rup->ru_utime.tv_usec / 1000
         + rup->ru_stime.tv_sec * 1000
         + rup->ru_stime.tv_usec / 1000;

 しかし、一組の中括弧を追加すれば、この問題を (次のように) 解決することができる。

v = (rup->ru_utime.tv_sec * 1000
          + rup->ru_utime.tv_usec / 1000
          + rup->ru_stime.tv_sec * 1000
          + rup->ru_stime.tv_usec / 1000);
◎do-while 文とフォーム・フィード (^L)
do
   {
    a = foo (a);
   }
while (a > 0);

 (関数内ではない) 論理的な場所で、プログラムを数ページに分割する場合に、フォーム・フィード (^L) を使いなさい。何ページになろうともこの方法で区切れば、プリンタ出力されたページに合わせる必要はなくなる。フォーム・フィードは 1 行に 1 個だけ与えること。

* 訳者注 これを Emacs で入力すると次のようになって見づらくなるということ。

  v = rup->ru_utime.tv_sec * 1000
   + rup->ru_utime.tv_usec / 1000
   + rup->ru_stime.tv_sec * 1000
   + rup->ru_stime.tv_usec / 1000;

2-4. 標準注釈作法 (C 言語)

 どのプログラムも、それが何のためのプログラムであるかを簡単に述べた (英語) 注釈で始まること。例えば"fmt--filter for simple filling of text" など。

 関数ごとに次の点を記述しなさい。何を行なうか、どのような種類の引数か、可能な限りの引数の値の意味と使用方法について。C 言語の引数の宣言の意味を別な言葉で繰り返す必要はない。使い方について何か標準的でないものがある (例えば、タイプ char * の引数が実際には、文字列の 1 番目ではなく 2 番目の文字のアドレスである) 場合や、例外事項として動作しない可能性のある値 (例えば、改行を含む文字列) の場合は、その旨を注釈しなさい。

 また、戻り値があればその意味を説明しなさい。

◎注釈の文の終わりには 2 つの空白を入れよう

 Emacs の文章に対するコマンドが機能するように、注釈文の終わりの後に空白を 2 つ入れること。また、完璧な文章を書き、最初の単語は大文字で書くこと。小文字の識別子が文頭に来る場合には、それを大文字にしてはならない ! 綴りを変更することは別の識別子にすることである。小文字で文章が始まることに抵抗を覚えるのならば、分けて文章を書きなさい (例えば"The identifier lower-case is……")。

 関数の注釈の中で引数名自体を用いて説明するとわかりやすくなる。変数名自体は小文字にすべきだが、変数よりもその値について説明する場合は大文字で書きなさい。つまり、"an inode" よりも"the inode number NODE_NUM" というふうにである。

 関数名の前の注釈で関数名をもう 1 度述べるのは、読めばその場でわかるので一般的に無駄である。関数自体が画面の下から消えてしまう場合の注釈は例外かもしれないが。

 各静的変数に関する注釈も次のように書く。

    /* Nonzero means truncate lines in the display;
       zero means continue them. */

    int truncate_lines;

 それぞれの #endif に 1 つずつ注釈を付ける。ただし、入れ子になっていないような短い条件文のケース (2〜3 行程度) の場合は付けなくてよい。

 注釈は、その意味を含めて、条件部の終わりまでについて与えるべきである。#else はその条件と続くプログラムの意味の注釈を付けなさい。例えばこのようになる。

  #ifdef foo
   …
  #else /*not foo*/
   …
  #endif /*not foo*/

 また条件が逆になると、

  #ifndef foo
   …
  #else /*foo*/
   …
  #endif /*foo*/

2-5.GNU 構文論 (C 言語の書き方)

 関数に対する全ての引数を明示的に宣言しなさい。int だからという理由で引数を省いてはいけない。

 ソース・ファイルの後ろに出てくる関数や外部関数の宣言を全てファイルの冒頭付近に置くか、あるいはヘッダ・ファイルに置くこと。関数内で外部宣言をしてはならない。

 複数行にまたがる 1 つの宣言文で、複数の変数を宣言してはならない。1 行に 1 つずつ新しい宣言を記述しなさい。

  int foo,
    bar;

 例えば、上の宣言は次のようにしなさい。

  int foo, bar;

 あるいはこのように書くこともできる。

  int foo;
  int bar;

(宣言が広域変数ならば、とにかくそれぞれに注釈を付けてから宣言しなさい。)

 if 文の中に別の if-else 文がある場合は常に、if-else 文を大括弧で囲みなさい。従って、決してこのように書いてはならない。

  if (foo)
     if (bar)
      win ();
     else
      lose ();

 常に、次のように書きなさい。

     if (foo)
       {
        if (bar)
         win ();
        else
         lose ();
       }

 構造型のタグや変数、あるいは型宣言の両方に同じ名前で宣言してはならない。その代わりに、個々に構造型のタグを宣言し、それを使って変数かまたは型宣言を宣言しなさい。

◎if 文の条件内に代入文を入れてはいけない

 if 文の条件内で代入文の使用は避けること。例えば、このように書いてはならない。

     if ((foo = (char *) malloc (sizeof *foo)) == 0)
     fatal ("virtual memory exhausted");

 その代わりに、このように書きなさい。

    foo = (char *) malloc (sizeof *foo);
    if (foo == 0)
     fatal ("virtual memory exhausted")

 lint の (エラーや警告の) 出力を減らすためにプログラム自体を見づらくしてはならない。例えば、このためにキャストを入れてはならない。null ポインタ (定数) を表す場合、キャストなしのゼロが一番良い。

2-6. 名前の付け方

 名前の付け方については、単語の区分に下線を使いなさい。そうすれば Emacs のワード・コマンドがそのような単語に対して有効となる。通常は小文字を使うこと。大文字はマクロ名や列挙型定数で使用する。接頭語の使用は統一した規則に従うこと。

 例えば、ignore_space_change_flag のような名前を使いなさい。iCantReadThis のような名前を使ってはならない。

 コマンド行オプションを指定しているかどうかを示す変数の名前は、オプションの文字で始めるのではなく、オプションの意味で始まるものにしなさい。注釈には、オプションの意味とその (オプションの) 文字の両方について書きなさい。

      /* Ignore changes in horizontal
       * whitespace(-b). */
      int ignore_space_change_flag;

 (代入を行なわない) 整数値を使った名前を定義したい場合は、#define よりも enum を使いなさい。◆3

2-7.GNU 意味論について (C 言語での意味付けの方法)

◎動的なデータ構造を使おう

 全てのデータ構造を動的に割り当てることによって、どのようなデータ構造の長さ、数に関する長さや個数にも制限を設けてはならない。これにはファイル名や行、ファイル、記号が含まれる。大方の Unix のユーティリティでは、「長い行は警告されずにそのまま切り詰められる」。このようなことは、GNU ユーティリティではあってはならない。

◎どのような文字も取りこぼしがあってはいけない

 ファイルを読み込むユーティリティは null 文字や、0177 以降のコードを使っている文字、プリンタ出力に現れない文字を取りこぼしてはならない。

 エラーを無視する場合を除いて、それぞれのシステム・コールが返すエラー・メッセージを確認すること。失敗した場合、システム・コールが返す (perror や同等のものからの) それぞれのエラー・メッセージを、ユーティリティが出力するメッセージに入れ、さらに (あれば) ファイル名やユーティリティ名をも入れなさい。"cannot open foo.c" や"stat failed" だけでは不十分である。

 malloc や realloc で、戻り値がゼロでないかどうか毎回確認しなさい。realloc でこれまで確保していた大きさより小さいサイズを指定した場合でもこのチェックを行ないなさい。あるシステムでは 2 の階乗のサイズにまるめてしまうかもしれないし、少ない領域を確保するように要求すると、違うブロックを返すかもしれないからである。

 Unix では、realloc はゼロを与えられた場合にストレージ・ブロックを破壊する可能性がある。GNU の realloc にはこのバグはない。もし失敗した場合は、元のブロックは変更されない。GNU 版ではバグが修正されたという仮定をして差し支えない。Unix 上でプログラムを実行する際、この失敗をおかしたくなければ GNU の malloc を使うこと。

 フリーにしたブロックの内容は「自由」に変更されることを予測しなければならない。ブロックから何かを取り出したい場合は、「free」を呼ぶ前にその内容を他の場所にとっておかなければならない。

◎コマンド行のオプション解析には getopt を使おう

 引数の構文が適切であれば、コマンドの引数を解析するためには getopt を使いなさい。

 静的ストレージをプログラムの実行中に変更する場合は、それを初期化するための明示的な C コードを使いなさい。変化しないデータである旨を C 言語で最初に宣言しておくこと。(ファイル・ディレクトリ、utmp、カーネル・メモリのレイアウトのような) 曖昧な Unix のデータ構造に対する低レベルのインタフェースを避けるようにしなさい。ディレクトリ内の全てのファイルを探す必要がある時は、readdir かその他の上位のインタフェースを使いなさい。GNU ではこれらと互換性を持たせるようなサポートを行なう予定である。

 GNU のシグナルの扱いは、おそらく BSD のものが強力で使いやすいので、System V のものよりも BSD に似たものになるだろう。

◎GNU ソフトウェアでは OS に対する移植性は考える必要はない

 Unix の世界における移植性と呼ばれるものの多くは、いろいろなバージョンの Unix に移植するという意味である。これは GNU ソフトウェアには関係ない。なぜなら、我々のシステムの目的はある 1 つのカーネル (GNU カーネル) 上でのみ動作することだからである。異なる CPU 上で動作する GNU システムの多種にわたる派生物は、異なる CPU 上で動作する Berkeley 4.3 システム (4.3BSD) と似たものになるだろう。

 まだ終了していないので、GNU カーネルではどのような機能を提供するのかは未定である。従って、4.3BSD にあるものは使えると仮定しなさい。ただし、より上位の方法 (readdir) がある時に準内部データベース (例えば、ディレクトリの構造) の形式を使ってはならない。

 C 言語やライブラリ、カーネルの適切な標準は自由に使うことができる。というのは、我々が既にその標準を実現しているかどうかにかかわらず、それらの機能をサポートする必要があると考えているからである。GNU カーネルや C コンパイラが、サポートしていなかった機能を満たすようになれば、その点は問題ではない。

 いろいろな種類の CPU の違い (バイトの順番や配列の制限に関する違い) については常に配慮しておくこと。GNU は 16 ビット・マシンをサポートしたくない。int が 32 ビット以下になる可能性を考慮するために時間を浪費するのは得策ではない。

◎1M バイトのメモリは自由に使ってよい

 1M バイトのメモリを使うことを前提にして構わない。そのレベルに達しない限り、メモリの消費を控えようとしてはならない。プログラムが複雑なデータ構造を生成する場合は、メインメモリ上にそれらを展開し、malloc がゼロを返す場合には致命的なエラーを表示するようにしなさい。

 複数行を扱うプログラムにおいて任意のファイルを入力として指定可能な場合、メインメモリ上には 1 行のみを展開しなさい。メインメモリ上に展開しきれないファイルを扱うことはそれほど困難ではないので、ユーザはできるはずと考えるからである。

2-8. ユーティリティのユーザ・インタフェースについて

◎実行時の名前の相違によって実行結果が違ってはならない

 ユーティリティの実行結果が、その実行に使われる名前に依存するようにしてはならない。異なる名前を使っているユーティリティにリンクするのは時に便利であるが、動作を変更してはならない。

 その代わりに、実行時のオプションか、あるいはコンパイル・スイッチを使いなさい。あるいは、それらの動作を選択できるようにしなさい。

◎GNU ドキュメンテーション・ガイド

 GNU プロジェクトのマニュアルは Texinfo で記述しなさい。GNU Emacs Info のサブシステム (C-h i、コントロール・キーを押しながら i をタイプする) のマニュアルかそのハードコピーのいずれかを使って、Texinfo マニュアルを参照のこと。

◎ドキュメンテーション作成には Texinfo システムを使おう

 例題用の GNU Texinfo ファイル (例えば、GNU Emacs 配布の man ディレクトリの下のもの) があり、それを参考にしなさい。

 上手に書きなさい。「-」で始まる全てのオプションやその動作について説明しなさい。全てのコマンドについて説明しなさい。それらの使用例を示しなさい。使い方やその条件、それぞれの機能がどのような動作をするのかというだけではなく、普通に使った場合の特徴についてユーザに説明しなさい。

3. 今月のニュース

 先月から今月にかけて次の表のような GNU 関係のソフトウェアがリリースされた。その中で、面白そうなもの 2 つ (Gnuplot と Mach) の概要をご紹介する。

 ちなみに、Mach は筆者らのヒアリングでは「マック」と発音している人が多かったが、「マーク」や「マッハ」と言っている人もいた。

ツール名    バージョン   リリース日    コメント
----------+------------+-------------+---------------
Gnuplot       2.0         1990/03/13   グラフ作成ツール、次節参照。
                                       GNU から配布されているソフト
                                       ウェアではない。
AE            2.0         1990/03/19   GCC(GNU C コンパイラ) をベース
                                       にしたソフトウェアで、実行時の
                                       動作解析ツール。GNU から配布さ
                                       れているソフトウェアではない。
flex          2.2         1990/03/21   Unix の lex(字句解析自動生成
                                       系) の GNU 版。αリリースである。
Freemacs      1.6a        1990/03/28   IBM-PC 上で動作する GNU Emacs。
                                       MicroEmacs のように機能縮小版
                                       ではなく、拡張性は GNU Emacs
                                       により近い。GNU から配布されて
                                       いるソフトウェアではない。
f2c           -           1990/04/09   Fortran から C 言語へ交換する
                                       ツール。GNU から配布されている
                                       ソフトウェアではない。
Mach/i386     2.5         1990/04/10   386 を使った IBM PC/AT 互換機
                                       用の CMU Mach カーネルのリリー
                                       ス。当然 GNU ソフトウェアでは
                                       ない。

3-1.Gnuplot とは何か

 Thomas Williams がリリースした Gnuplot の特徴について取り上げる。詳細については、回を改めてドキュメンテーション関連の話題のところでご紹介しようと思う。

 Gnuplot 2.0 なるものが 1990 年 3 月 13 日付でリリースされた。もともとは 1986 年に Colin Kelly と Thomas Williams が、いろいろな端末にさまざまな関数やデータ・ファイルをプロットできるように開発したものである。1989 年から 1990 年にかけて GNU TeX や各種 Gnuplot が 1 つに結合され、新しくできたのがこの Gnuplot 2.0 である。

 ちなみに Gnuplot には GNU という 3 文字が付いているが Free Software Foundation の GNU には無関係で、偶然の一致のネーミングだそうである。

 Gnuplot は Unix や MS-DOS や VMS 上で動作するプロット・ツール、つまりグラフィック・プログラムである。数学関数やデータをグラフなどで視覚的に理解できる点が特徴である。処理結果はグラフィックス端末ではなくても表示可能である。LaTeX のマクロ・パッケージ EEPIC を使って TeX が dvi(TeX の出力ファイル形式で出力装置に依存しない) 形式のデータを生成する。

●Gnuplot の特徴

 また、Gnuplot で作成されたデータ・ファイルを PostScript 形式のデータに変換するコマンド plot2ps(αバージョン) も 1989 年 7 月 5 日に Richard Murphey がリリースしている。

●入手条件

 plot2ps の使用条件は、Free Software Foundation の一般公的使用許諾に則ってフリーに配布することができるようになっている。plot2ps のソース・ファイルには、PostScript のハードコピーだけではなく texinfo 形式のドキュメントも含まれている。詳細は rich@rice.edu まで電子メールにて。◆4

●入手方法

a. USENET のニュース・グループ comp.sources.misc に投稿されるまで待つ。

b. duke.cs.duke.edu(128.109.140.1) マシンから pub/gnuplot.tar.Z を anonymous ftp で入手する。

c. anonymous ftp ができなければ ftp サーバ (BITETP@pucc.princeton.edu) へ、help という単語を入れた電子メールを出す。

d. a〜c で入手できなければ、フォーマット済みの FD と宛名を書いた返信用封筒と切手を次の所まで送り、いつの日か返送されてくるのを待つ。FD は 1.25M フォーマットならば 5.25 インチ 1 枚、720K か 1.44M フォーマットならば 3.5 インチ 1 枚、360K フォーマットならば 5.25 インチ 2 枚を同封すればよい。

GNUPLOT
attn: Thomas Williams
48 Lyford Drive#8
Tiburon, CA 94920
U.S.A.
●問い合わせ先

 電子メールにて、質問とコメントは pixar!info-gnuplot へ発信し、フォローは comp.sources.d へ投稿していただきたい。

3-2.Mach/i386 のリリース情報

 Intel 80386 用の CMU Mach カーネルが、プロジェクト・リーダの Richard F. Rashid 助教授から 1990 年 4 月 10 日付でリリースされたので、その様子を紹介する。

●動作環境

 現在、(IBM)AT クローンや Olivetti M386 や東芝 T5200、HP Vectra RS25C、Intel 301、Intel 302 上で動作しているが、ターゲットとなるマシン用にサポートしている / いない仕様は次の通りである。

●サポートしているもの
●サポートしていないもの
●入手方法

 4.3BSD や AT&T の (System V)3.2 のソース・コード・ライセンスが必要とのこと。商用ライセンスが必要な NFS は配布されていない。また、ライセンスとハードウェアの要求事項を満たせば、Mach 2.5 i386 インストレーション・マニュアルを anonymous ftp で wbl.cs.cmu.edu から入手することができる。ファイルは /usr/mach/public/doc にある。

 現在、(CMU の) 資源が限られているので、このシステムを広く配布するために他のサイトの募集をしている。CMU の活動負荷をかけないために、tcp/ip を使って Internet のアクセスが可能なサイトに限る。

●将来は ?

 CMU で使っている VGA 上の X Window System がじきに配布可能となりそうである。

●問い合わせ先

 配布サイトの申し出やその他の問い合わせは、次の所まで電子メールか手紙で連絡していただきたい。

電子メールの場合 mach@wbl.cs.cmu.edu

(これを読むのはプロジェクト管理者と配布担当者だけである。)

郵便の場合

  Mach Project
  c/o Richard F. Rashid
  School of Computer Science
  Carnegie Mellon University
  Pittsburgh, PA 15213-3890
  U.S.A.

4. おわりに

 プロジェクト GNU 前夜とコーディング規則を紹介した。コーディング規則はおそらく随時更新されていくだろう。GNU ソフトウェアのソース・コードを見て、あるいは電子掲示板の情報を見て、自分なりにカスタマイズしたスタンダードを作っていけば、プログラマの貴重な財産になるはずである。

参考文献

★1

Donald E. Knuth, Tracy Larrabee and Paul M. Roberts,MATHEMATICAL WRITING,The Mathematical Association of America, 1988(邦訳、有澤誠訳「クヌース先生のドキュメント纂法」共立出版、1989)

★2

Guy L. Steele Jr., Donald R. Woods, Raphael A. Finkel, Mark R.Crispin, Richard M. Stallman and Geoffrey S. Goodfellow, 犬伏重之訳「ハッカー英語辞典」自然社、1989(英語版 1983)

★3

「座談会 リチャード・ストールマン氏を囲んで」「bit」1987 年 8 月号、共立出版

★4

坂村健「I Love COMPUTER 9, Lisp マシンのアーキテクチャ」「bit」1981 年 3 月号、共立出版

〈補遺〉GNU コーディング規則最新版より (1992/9)

1. 変更点の記述について

ChangeLog ファイルに変更点を記述しなさい

 このファイルがあれば、ある問題を発見した場合、どの変更点で問題を引き起こしたのかを調べることができる。最後に行なった修正が原因で新しい問題が発生することが多い。変更点をソース・コードと共に配布することにより、新たな修正、追加、改良がこれまでの変更と矛盾なく行なわれるようになる。

 Emacs を使えば、M-x(ESC キーを押してから x キーを与える、あるいはメタキーと x キーを同時に押す)add-change コマンドを使って自動的に新しい項目を入力することができる。それぞれの項目には、

* 変更を施したファイル名 (変更した関数名、あるいは変数名): 変更についての記述。

のように書く。空白行で区切ったものを 1 つの項目とする。同じ修正が複数個所におよんでも項目分けはしない。同じファイル内の修正ならその前の * も省略する。

 変更の目的やなぜそのような修正方法にしたかについての、理由を記述する必要はない。

 関数の呼び出しプロトコルを変更した場合は、呼び出し側の全てを記述するのではなく、

「全ての呼び出し側を変更」

と書く。

 ドキュメントや注釈の変更に関しては、ファイル名のみでよく、

「ドキュメントの修正」

と書く。

 このようにドキュメントについてシンプルなのは、発生した問題点の影響を受けにくく、修正も容易だからである。さらにこれまでの修正との一貫性を保つ必要がなく、誤りを修正するのに過去の履歴を知る必要もない。

2.Makefile の書き方について

 Makefile には次のターゲットを入れなさい。

all

全てのプログラムをコンパイルする。

install

プログラムをコンパイルして、実行形式やライブラリをコピーし、必要に応じてファイル名を変更する。簡単なテストがあれば行なう。

clean

そのディレクトリ内で、プログラム作成途中で生成された全てのファイルを消去する。コンフィギュレーションの記録を消してはいけない。プログラム作成途中で生成されたファイルは配布テープに入れてはいけないが、消さないでおく。

distclean

コンフィギュレーションやプログラムを作成する上で生成されたファイル全てを消去する。配布テープ用のファイルのみを残す。

mostlyclean

clean と同じ動作を行なう。その際、再コンパイルしたくないファイルはそのまま残しておく。例えば gcc の mostyclean というターゲットでは、libgcc.a を消さない。再コンパイルはほとんど必要ないし、行なうとすると時間がかかるからである。

realclean

必要ないもの、もう一度作り直せるもの全てを消去する。distclean で消すファイル以外のファイルをも消去する。Bison が生成した C ソース・ファイル、タグテーブルなど。

TAGS

プログラムのタグテーブルを更新する。

dist

tar ファイルの名前はサブディレクトリ名で始める。サブディレクトリ名は配布するパッケージの名前であること。例えば、gcc バージョン 1.40 の tar ファイルを展開すると、gcc-1.40 というディレクトリが生成され、その下に必要なファイルが展開される。最も簡単な方法は、任意のディレクトリ名にコピー(cp) するかリンク (ln) をはることである。dist ターゲットには全ての「非ソース・ファイル」を明示的に指定すること。これにより配布イメージを確実に最新のものとすることができる (「5. リリースの方法」を参照のこと)。

check

必要に応じて、セルフテストを行なう。テストする前に必ずプログラムが作成されていなければならないが、インストールしておく必要はない。

Makefile には、

    SHELL= /bin/sh

も入れておくこと。環境変数 SHELL を継承して発生する問題を事前に防止することができる。コマンドやオプションなどは、Makefile 内で直接記述するのではなく、変数で定義してから使うこと。特にユーティリティ・プログラムにおいてはそのようにしなさい。Bison を使う場合、BISON という変数名を用いて、必要になった時点で $(BISON) というふうにして Bison を参照する。

 それぞれのコマンドに使うオプション用の変数名には、そのコマンド名を含んでいること。コマンド名に FLAGS という文字列を付ける。例えば、BISONFLAGS のように (CFLAGS は例外である。これが標準だからである)。

 システム・ファイルのインストールに使うコマンドに対しては、INSTALL という変数名を用いる。

 変数 INSTALL_PROGRAM と INSTALL_DATA を用いる (既定値では $(INSTALL) とする)。実行形式、非実行形式をインストールする場合にそれぞれ使用する。

$(INSTALL_PROGRAM) foo ${bindir}/foo

$(INSTALL_DATA) libfoo.a ${libdir}/libfoo.a

(それぞれの第 2 引数にはディレクトリ名ではなくファイル名を与えること。いずれのファイルのインストールにもインストール・コマンドを使用すること)

 インストールする場所のディレクトリのパス名を変数名で与えること。標準以外の方法でインストールする際に楽になる。標準変数を次に示す。

bindir

ユーザが実行する実行形式をインストールするディレクトリ。一般に /usr/local/bin を指定し、$(prefix) を使った値にしておく。

datadir

プログラムが実行中に参照する読み込み専用のデータ・ファイルをインストールするディレクトリ。使うマシンに依存しないファイル形式とする。ネットワーク・インストールを行なった場合はマシン間で共有することが可能となる。一般に /usr/local/bin を指定し、$(prefix) を使った値にしておく。

statedir

実行中に更新するようなファイルをインストールするディレクトリ。使うマシンに依存しないファイル形式とする。ネットワーク・インストールを行なった場合はマシン間で共有することが可能となる。一般に /usr/local/lib を指定し、$(prefix) を使った値にしておく。

libdir

ユーザではなくプログラムが起動する実行形式をインストールするディレクトリ。オブジェクト・ファイルやライブラリもここに置く。マシン・アーキテクチャに依存するファイルをここに置くという案もある。一般に /usr/local/lib を指定し、$(prefix) を使った値にしておく。

includedir

ユーザ・プログラムで挿入するヘッダー・ファイル #include をインストールするディレクトリ。一般に /usr/local/include を指定し、$(prefix) を使った値にしておく。

gcc 以外のコンパイラでは、/usr/local/include をほとんど参照しない。gcc と共にこのヘッダー・ファイルをインストールしておくと便利である。gcc を想定したライブラリのみの場合には通常問題にならない。しかし、他のライブラリは別のコンパイラを想定しているので、ヘッダー・ファイルを 2 箇所にインストールしなければならない。ひとつは includedir で、もうひとつは oldincludedir である。

oldincludedir

gcc 以外のコンパイラで用いるヘッダー・ファイル #include をインストールするディレクトリ。通常は /usr/include になるだろう。make コマンドでは oldincludedir がないかどうかをまずチェックし、そこに何もなければ、oldincludedir を使ってはいけない。ヘッダー・ファイルのインストールのコマンドの 2 番目の引数で指定しないようにする (つまり oldincludedir にインストールしてはいけない、ということ)。

mandir

必要に応じて、man ページをインストールするディレクトリ。

マニュアルのセクションを拡張子とする。ユーティリティは通常、1 である。

man1dir

セクション 1 の man ページのディレクトリ。

man2dir

セクション 2 の man ページのディレクトリ。

...

セクションが複数にわたるマニュアルをインストールする場合に、mandir という名前を用いる。

GNU ソフトウェアの主要なドキュメントとして man ページで作成してはいけない。TexInfo でマニュアルを書くこと。man ページは 2 次的なものであり、GNU ソフトウェアを Unix 上で動かしている人々のためだけのものである。

manext

man ページの拡張子を指定する。ピリオドの後に数字が来る。

infodir

このパッケージ用の info ファイルが入るディレクトリ。既定値は /usr/local/info であり、$(prefix) を含む値で初期化される。

srcdir

コンパイルするソース・コードが入っているディレクトリ。configure シェル・スクリプトで設定される。

preifx

前述の変数を作成するために指定する接頭語 (つまり bindir などのパス名と最初の部分をこの変数で指定する)。

# Common prefix for installation directories.
# NOTE: This directory must exist when you start installation.
prefix= /usr/local
# Directory in which to put the executable for the command `gcc'
bindir= $(prefix)/bin
# Directory in which to put the directories used by the compiler.
libdir= $(prefix)/lib

3. コンフィギュレーションの動作

 GNU のそれぞれのパッケージには configure という名前のシェル・スクリプトが入っている。スクリプトでは複数の引数を与えて、プログラムをコンパイルしたいマシンやシステムの種類を記述する。

 configure ではコンフィギュレーションのオプションを記述しなければならない。後々のコンパイル時に必要だからである。

 config.h のような標準名と、選択したシステム向けのコンフィギュレーション・ファイルとリンクする、という方法もある。この方法を使った場合は、配布テープに config.h というファイルを入れてはいけない。最初にコンフィギュレーション・スクリプトを実行しないでプログラムが作成されることを防止するためである。configure の目的は、Makefile を修正することである。この方法を採用する場合に、Makefile という名前のファイルを配布イメージに含めてはならない。ファイル Makefile.in を用意し、修正用入力ファイルとする。もう一度繰り返す。これは、最初にコンフィギュレーション・スクリプトを実行しないでプログラムが作成されることを防止するために行なう。

 configure が Makefile を生成するようになっていた場合には、Makefile 内のターゲットには Makefile を入れ、configure を起動して最新のコンフィギュレーションの状態にする。configure の入力ファイルを Makefile のターゲットの依存リストに記述しておくこと。

 configure スクリプトが生成する全てのファイルには、注釈として、configure から自動的に生成されてある旨を記すこと。そうすればユーザがそのファイルを直接修正することはない。configure スクリプトが configure.status というファイルを生成するようにしておくこと。最後に、configure スクリプトを起動したいときのオプションを入れる。それ自体はシェル・スクリプトであり、実行すれば同じコンフィギュレーションを再生成することができる。

 configure スクリプトはオプション--srcdir=dirname を指定できるようにしておく。そのディレクトリになければ、ソース・ファイルの場所を指定することができる。このオプションによって他の場所のディレクトリでプログラムを作成できるようになる。その場合、実際のソース・ディレクトリの内容は修正しない。

 --srcdir というオプションの指定がなければ、configure は現在のディレクトリを調べて、ソース・ファイルがあるかどうかを確認しなさい。どちらかが見つかればそれをソース・ディレクトリとする。そうでなければ、ソースが見つからない旨を報告し、ゼロ以外のステータス・コードを返す。一般に--srcdir をサポートするためには、Makefile の VPATH を定義するように修正する。Makefile の中のルールのうち、ソース・ディレクトリを明確に指定する必要があるかもしれない。これに備えて、Makefile の srcdir という変数名にソース・ディレクトリを指定すること。

 configure スクリプトの引数で、作成するプログラムのシステム・タイプをも受け付けられるようにする。形式は、

cpu-company-system

で、例えば Sun-3 は、

m68k-sun-sunos4.1

というふうになる。

 configure スクリプトは、そのマシンを示すそれらしい別名をも解釈できるようにすべきである。sun3-sunos4.1 も妥当である。基本的に sun3-bsd4.2 もよいだろう。sunos は基本的に BSD であり、Sun 以外に BSD システムを使っていないからである。たいていのプログラムでは、vax-dec-ultrix の別名として、vax-dec-bsd は妥当であろう。というのは単純に BSD と Ultrix の相違はほとんどない。時にはプログラムでは区別する必要があるかもしれない。

 config.sub というシェル・スクリプトを、システム名あるいは別名が適切かどうかを調べるために用いる。

 他のオプションとして次のようなものが可能である。各オプションではマシンのハードウェアやソフトウェアの詳細を記述する。

-with-package

package がインストールされており、package を使うようにコンフィギュレーションに指定する。X や gnu-as(あるいは gas)、gnu-ld、gnu-libc、gdb など。

--nfp

浮動小数点プロセッサがないターゲット・マシンに用いる。

--gas

ターゲット・マシンのアセンブラは GAS(GNU アセンブラ) である。このオプションは古い形式なので、-with-gnu-as を用いるように。

--x

X Window System がインストールされているターゲット・マシンに用いる。このオプションも古い形式なので、--with-x を使うように。

 将来、独自のパッケージで相違が生じるかどうかにかかわらず、configure スクリプトは前述の詳細なオプション全てを受け付けるようにしなさい。特に、--with-で始まる全てのオプションも受け付けるように。そうすれば、ユーザは一連のオプションを与えるだけで、一度で全ての GNU ソース・ツリーを構成できるようになる。

 コンパイルするようなパッケージならば、クロス・コンパイルをサポートするかもしれない。その場合、プログラムのホストとターゲットのマシンが異なる。configure スクリプトでは通常、ホスト・マシンとターゲット・マシンの両方のマシン・タイプを指定する。従って、スクリプトが起動されるマシンと同じもので動作するプログラムを生成する。

 クロス・コンパイラやクロス・アセンブラなどを作成するには、configure に --host= ホストタイプ・オプションを指定する。ターゲット・システムは変更しない。ホスト・タイプの書き方は前述のものと同じである。

 クロスとしての動作が無意味なプログラムでは--host を受け付けてはいけない。クロス動作を行なわせるために全てのオペレーティング・システムを構成すること自体が無意味だからである。

 プログラムによってコンフィギュレーションを自動的に行なう。

この場合には、前述の全ての引数を無視してもよい。

4.C 言語以外の言語を使用するには

 C 言語以外の言語を使うということは標準に従っていないことになる。gcc でさえ、他の言語のフロントエンドを複数用意してサポートしている。その言語をインストールするために他の言語をインストールすることは手間がかかるので、C 言語を使うように。

 規則には次の 3 つの例外がある。

5. リリースの方法

 foo バージョン 69.69 のパッケージは、foo-69.69.tar というファイル名で配布する。展開すると、foo-69.69 というサブディレクトリの元にファイルが生成される。

 プログラムを作成しインストールする際に、配布イメージに入っているファイルを修正してはいけない。 これは、どのような場合でもプログラムは形式上「ソース・ファイル」と「非ソース・ファイル」に分類されていなければならない、という意味である。「ソース・ファイル」は人間が作成したものであり、自動的に修正されてはいけない。「非ソース・ファイル」は、「ソース・ファイル」から Makefile に従ってプログラムが生成したものである。

 もちろん、全てのソース・ファイルは配布イメージ内に入っている必要がある。「非ソース・ファイル」は入っていてもかまわないが、最新のもので、しかもマシンに依存しないものでなければならない。配布イメージを作成する際に修正しないこと。一般に我々は、Bison や Lex、TeX、Makeinfo で生成した「非ソース・ファイル」を含めている。配布イメージ内の不要な依存関係の記述を省いている。このようにして希望するものをどれでもインストールできるようにしている。

 プログラムの作成や、インストールを行なうために実際に修正が入る可能性のある「非ソース・ファイル」を、配布イメージに入れてはいけない。もし「非ソース・ファイル」を配布する時に、新たに配布イメージを作成する場合は常に最新のものになるよう注意しなさい。

 配布イメージ内には 14 文字より長い名前のファイルがない点に留意すること。同様にできあがったプログラムも 14 文字より長い名前のファイルを生成してはならない。というのは POSIX 規格を変に解釈しているシステムがあるためである。そのシステムは、長い名前のファイルを切り詰めてオープンせずに、オープンすること自体を拒否しているのである。

 MS-DOG 上でもファイル名が重複しないように考慮してみること。MS-DOG のファイル名は 9 文字からなり、さらにピリオドの後に 3 文字を追加することができる。MS-DOG は、ピリオドの前後でこの制限を越えたものは切り捨てられる。従って、foobarhacker.c と foobarhacker.o は重複しない。それぞれ foobarhac.c と foobarhac.o とに切り捨てられて区別できるからである。

 texinfo.tex のコピーを配布イメージに入れなさい。*.texinfo ファイルを出力するのに必要である。


Think GNU 連載第 4 回【脚注】

◆1

rms の今年 37 才とは 1990 年起稿時のこと。

◆2

「GNU コーディング規則」の最新版は、マニュアルとしてプリンタに出力できるきれいな体裁となっている。

 その最新 GNU コーディング規則では、

といった章が追加された。この章の最後に概略を示したので参考にしてほしい。

◆3

enum を使うと gdb で表示できるからでもある。

◆4

以前の注記でもふれたが、GNU General Public License のことを「GNU 一般公的使用許諾」ではなく、「一般公有使用許諾書」に訂正して呼んでいる。