この文章は
の元原稿に少しだけ手を加えたものです。 雑誌掲載のものとは若干の差異があります。(詳細未確認)
2001年5月〜2006年11月の掲載記事を PDF で収録した MOOK 本が2007年5月に出るということなので これを機会に整理しました。
じつはね 「安く出したいので著作権料は現物支給で勘弁願いたい」 というメールが来たのだった。 原稿を書いた全員が二次使用の無報酬に同意したのかはわからないが、 「ま、たいした分量でもないのでいいか」と思ったしだい。
(原稿料の下落につながるからそんな勝手なことすんな!と いった話があるのであれば教えてくださいね)
(注) IE6 (IE7未確認) ではなく Firefox や Safari で見ると私の 意図通りに表示されているようです。 他のブラウザは未確認です。
前書き
著者の Emacs 歴は1989年の Nemacs(Emacs18.55 をベースに日本語化された Emacs) を使っていた時代まで遡る。 普段の生活はもちろん Emacs 上であり、 Emacs に棲んでいると言っても過言ではない。 login シェルが Emacs であったとしても全く困らないほどなのである。
メール環境は Gnus、Web 日記、blog などの Web 徘徊は Emacs-w3m、 日記を付けるのも hnf-mode、 原稿を書くのももちろん outline-mode、 プログラムは最近は Emacs Lisp ばかりとなってしまったが、 それでもソースを編集するのも、リビジュン管理するのも‥‥と、 このようにけっきょく一日中 Emacs の前にいるの である。
本稿ではプログラム開発の流れを解説する。見なきゃわからない、いやむしろ 見ればすぐにわかるということは多々あるものだ。他の人の操作手順を見る機 会などめったにないために、Emacs の持つ多機能のうちのほんの一部しか知ら ない、知らないくて損をしていることも多いだろう。そういった読者のために 視覚的な解説を行っていこうと思う。
図を順に見ていけば Emacs 流の開発の流れがわかるだろう。 そして文字通り
「あらゆる操作を Emacs の中だけで実行する」
と言われるその一端を、 一から十まで Emacs 上で実行するのだということを図で示しなが解説していく。
サンプルソース入手
まずはサンプルソースを準備しよう。 Cプログラム入門書を片手に始めるのも良いが、 他の人のコードを読むこともプログラミング上達の近道であるので、 ここでは GNUの「Hello World!」プログラムを取り上げる。
この「Hello World!」プログラムは、GNUコーディング規約(GNU Coding Standars) に則って書かれ、パッケージ化されている。「Hello World!」をビ ルド、インストールできれば、他の GNU ソフトウェアについても全く手順であ るので、基本として押さえておこう。たかが「Hello World!」と言うなかれ、 GNU ソフトウェアの様式美を色濃く受けついでいるので、その原典と言える GNU コーディング規約も併せて読んで見比べて欲しい。
ソースは GNU のミラーサイトから入手する。 ここでももちろん Emacs からそのままアクセスを行う。
C-x C-f /anonymous@ftp.ring.gr.jp:/pub/GNU/ RET
で図のように dired(ディレクトリエディタ)で、あたかもローカルディスクであ るかのごとく開くことができる。hello-2.1.1.tar.gz上にカーソルを置いて、 C を押下して、ローカルディレクトリ、 ここでは _~/sources_ の下にコピーしよう。
図1. dired でftp サーバ上のファイルコピー
(クリックで拡大/縮小)
ソースの展開
diredから ! (dired-do-shell-command)キー押下 で展開することもできるが、 ここではわかりやすいように Emacs 上で動作するシェル(シェルモード)上で 展開を行う。 シェルモードの起動は M-x shell RET である。 カーソルのあったバッファのディレクトリをカレントディレクトリとして 図のようにシェルが起動する。
図2. シェルモードの起動
(クリックで拡大/縮小)
ここでは ~/.sources を開いているdiredバッファ上で M-x shell RET を実行しているので、 シェルのカレントディレクトリは ~/.sources となる。 このバッファはシェルを対話的に使う特別なバッファであるので 通常のXターミナル(端末) と同じようにコマンド入力を行うことができる。
tar zxf hello-2.1.1.tar.gz
で展開する。
configureとmakeの実行
GNU のソースをコンパイルする時におなじみの configure スクリプトの実行も 同じくシェルモードから実行する。
図3. シェルモード上での展開と confiugre の実行
(クリックで拡大/縮小)
ソースをビルド(make を実行する)し、できあがった hello コマンドを実行してみよう。
図4. shell-mode 上でビルドを行い、hello コマンド実行
(クリックで拡大/縮小)
GNU ソフトウェアでは通常、これに引き続いて make install を行うことで実 行ファイル、オンラインマニュアルやInfoが適切な位置にインストールされる。 インストール先は configure スクリプトを実行する時に指定しないデフォルト では /usr/local の下となる。これは Linux のディストリビューションのインス トール先とは別のローカルな場所であるので別々に管理するのに都合が良い。 root になって make install を実行するか、sudo make install によってインス トールを行う。
なお本稿の範囲では「Hello World!」プログラムを Emacs から実行しているだ けであるので make install まで行う必要はないだろう。もちろんインストール してみるのはかまわない。
speedbarを使ったソースファイル徘徊
ではソースファイル群を眺めてみよう。ここではソースファイルナビゲータで ある speedbar を紹介する。M-x speedbar RETで起動すると 図のようなフレームが新しく開く。 右側のフレームが speedbar と呼ばれるもので「+」の部分を ダブルクリックするとファイル中の関数表示となる。 「-」の部分をダブルクリックすると関数を隠すことができる。 表示されている関数名、ここでは main をダブルクリックしたところを図に示す。 もう一方のフレームに hello.c ファイルが表示され、 関数 main の所にカーソルが移動しているのがわかるだろう。 単純な構成のファイルを眺めるにのには十分役立つだろう。
図5. speedbar によるファイル名、関数の tree 表示
(クリックで拡大/縮小)
Emacs を複数起動して同じファイルを同時に編集して警告を出されたことは ないだろうか? Emacs では動作の実体はひとつだが、表示フレームを複数 にするということが可能であるのだから、複数の Emacs を起動するという 愚かな行為はやめよう。
使い方はいたって簡単なので、実際に次のキー操作を行ってみよう。
C-x 5 2 もうひとつフレームを開く
C-x 5 0 カレントフームを閉じる
C-x 5 1 カレントフレームをだけを残し、他の(複数の)フレームを閉じる
C-x 2、C-x 0、C-x 1というバッファ操作と同じような意味なので、Emacs 流の直感的なキーバインドになっている。
すでに起動している Emacs に外部からファイルを開かせることができる。 まず下準備として、起動中の Emacs 上で M-x server-start RET とす るか、もしくは .emacs に (server-start) と記述してEmacs起動時から サーバ機能を有効にしておく。
Xターミナル等のコマンドラインから
emacsclient filename
を実行することにより、既に起動中の Emacs でファイルを開くことができる。
この機能を使うことにより、crontab の編集や、cvs のログの記入用のエディタ も「既に起動している Emacs で編集すること」ができる。やり方は簡単で、環 境変数 EDITOR と CVSEDITOR を emacsclient にしておくだけだ。
crontab の編集や cvs のログの記入の場合は、該当バッファを C-x # で閉じることによって完結する。 「Emacs は起動が遅い」という悪評は Emacs の使い方を知らないからであり、 こういった機能を使えば「Emacs は一回起動すればいい」のである。
grep
foge という文字列を *.c ファイル中から検索する時は M-x grep RET foge *.c RET と入力する。
図のように検索結果表示になるので、C-x ` で上から順番にタグジャンプする か、C-c C-c でカーソル行への該当箇所へのジャンプとなる。
図6. grepの実行
(クリックで拡大/縮小)
もしカーソル位置の単語を検索したい場合には、C-u M-x grep と入力 することで、ミニバッファでの foge 入力部分を補完入力するの便利な方法がある。
図7. カーソル位置の単語の検索
(クリックで拡大/縮小)
タグジャンプ
タグを使って、関数や変数の定義部分にジャンプすることができる。 下準備としてタグファイルを作る必要がある。前述したシェルモードから 次のコマンドを実行し、TAGS という名前のタグファイルを作成する。 etags はタグファイルを作成するコマンドで、後述するように _M-x man RET etags RET_ によって Emacs 上でオンラインマニュアルを参照できる。
$ cd src $ etags *.c *.h
M-. (find-tag) で、カーソル位置の関数(変数)等の定義部分にジャンプできる。 hello.c の getopt_long 上にカーソルを移動(図a)、 M-. キー押下で getopt_long 定義へジャンプ(図b)、 getoptinternal_ 上へカーソルを移動しM-.キーで getoptinternal_ 定義へジャンプ(図c)、 そこから 14行下の optarg 上へカーソルを移動して M-. キーでジャンプ(図d)と、 簡単な操作でどんどんその関数や変数を定義している箇所へジャンプして行ける。 それを逆に辿ることもできて、M-* を押せばよい。 つまり図dで、M-* キーを押せば図c に、更に M-* キーで図b、 図aと戻ることができるのだ。
(a) (b)
(c) (d)
図8. タグジャンプ
(クリックで拡大/縮小)
もし呼出し側と両方見比べたい場合は、C-x 4 . (find-tag-other-window) である。hello.c の getopt_long 上にカーソルを置き、 C-x 4 . というキー入力によって図のようになる。
図9. find-tag-other-window
(クリックで拡大/縮小)
問い合わせ型置換
同一ファイル内の問い合わせ型置換は _M-%_ (query-replace)であるが、 複数ファイルにまたがる問い合わせ型の置換も可能である。 M-x tags-query-replace foo RET bar で foo を bar に置換できる。 タグファイルに登録されたファイルが置換の対象となる。
関数名、変数名の補完入力
関数名、変数名は見ただけでその機能をわかるような名称をつけるものである。 そうすると Hello_I_Love_You() のように長くなるもので、 それを真っ正直にキー入力するのは間違いの元である。 Emacs には補完入力機能があるのだからそれを利用しない手はない。
この場合、He まで入力したら M-TAB もしくは ESC TAB と入力することで補完することができる。 もし Hello_World() のように途中まで同じ名称のものが あれば途中まで補完し、図のように *Completions* バッファに 補完候補が表示される。 次の文字をキー入力し I M-TAB と再補完するか、 マウス右クリックで選択する。 この操作はミニバッファでファイル名を補完入力する時と同様である。
なおこの場合もタグファイルに登録された名称が補完候補となる。
図10. M-TAB による補完入力
(クリックで拡大/縮小)
オンラインマニュアル
プログラム中に C の標準関数の使い方や書式がわからない場合にはすぐに オンラインマニュアルを参照しよう。 もちろん Emacs 上で。 オンラインマニュアルを参照するには M-x man RET foo RET で、 foo のオンラインマニュアルを参照できる。 カーソル位置の単語を使うので、fputs 上にカーソルを置いて M-x man RET RET で図のように fputs のオンラインマニュアルを参照できる。
マニュアルからマニュアルを参照したい場合には *Man foo* バッファ上で調べたい単語上にカーソルを置いて m で良い。 ページ番号指定は foo(5) のように括弧付きで行う。
図11. fputsのオンラインマニュアル参照
(クリックで拡大/縮小)
Infoの参照
GNU ソフトウェアのマニュアルは UNIX の伝統的なオンラインマニュアル形式で はなく、Info と呼ばれる形式となっている。コマンドラインから info foo で参 照できるのはご存知かと思うが、これももちろん Emacs から読むのがずっと使 い勝手が良い。 C-h i で起動すると全 Info のリスト表示となり、 調べたい項目をどんどん辿っていけば調べたいものに辿りつくことができるだろう。 しかしそんなまどろっこしいことをする必要ははなく、直接 Info の該当部分を表示さ せることができる。fputs 上にカーソルを置き C-h TAB または C-h C-i (info-lookup-symbol) とキー入力すると図のように新しくフレームを開き、Info の該当箇所を表示する。
図12. fputs の info (libc) の参照 ()
(クリックで拡大/縮小)
makefile には呪文のような記号が使われるが、それも同様な方法で参照するこ とができる。図は「$<」を調べている所、C-h TAB $< RET と入力したものである。
図13. makeのinfoの参照
(クリックで拡大/縮小)
info-lookup-symbol では、libc, bison, makefile, texinfo, m4, autoconf, awk, perl, Emacs Lisp 等に対応している。これらはオンラインマニュアルは 準備されていないので Info に頼らざるを得ないわけで、Emacs はここでも威力 を発揮する。コマンドラインで動作する Info など使っちゃいられないのである。
Emacs上でのコンパイル
ソースツリーはエラーなくコンパイルできる状態であるので、ここではわざと コンパイルエラーを出してみる。_src/hello.c_ を開き、適当に書換える。 図では 120 行目の progname を oprogname とした。 Emacs 上でコンパイルするには M-x compile RET。 ファイルを保存するか問われるので全て保存するとコンパイル が行われ、図のように *compilation* という名前のバッファにコンパイル結果 が表示される。 コンパイルエラー箇所へのジャンプは、grep の時と同様に C-x ` または C-cC-c で行う。 C-x ` で *compilation* バッファのエラー表示の上から 順番にコンパイルエラーを修正していき、M-x compile RET、 また修正と繰り返して行き全てのエラーがなくなるまで続ける。 なお .emacs サンプル設定では M-x compile RET を C-c c というキーにバインドする例を示した。
図14. コンパイル
(クリックで拡大/縮小)
デバッガ
デバッガの起動は M-x gdb RET gdb hello RET だ。 hello の部分は実行ファイル名を指定する。 コマンドの実行は、(gdb) プロンプトにコマンド名ではなく run と入力する。 図は実行後の画面で、「Hello, world!」と表示後正常終了している。
図15. デバッガ起動
(クリックで拡大/縮小)
次に hello.c を開いて120行目にカーソルを移動して _C-x SPC_ (gud-break) でブレークポイントをセットし、run で再実行する。 この GDB インタフェースではコマンドライン引数は run に続いて入力する。 図では --traditional という引数を与えている。
プログラムはブレークポイントで中断し、中断位置はソース表示の右側の fringe (フリンジ:ヘリ) と呼ばれる領域に三角マークで示される。 fringe とは、行の折り返しを示す矢印や、 ファイル末尾を示す印などを表示する領域で、 テキスト表示領域の外側左右にある。 fringe は Emacs21 から実装された。
図16. ブレークポイント
(クリックで拡大/縮小)
ここで、_C-x C-a C-s_ (gud-step) と入力すればソースレベルでステップ実行 できる。中断した時点の変数fooの値を調べるには *gud-hello* バッファの (gdb) プロンプトに print foo と入力する。 図では先ほどコマンドラインで与えた引数が表示されている。
図17. 変数の値を見る
(クリックで拡大/縮小)
主なキーバインド一覧を次に示す。
| C-x C-a C-p | gud-print | カーソル位置の変数表示 | ||
| C-x C-a C-r | gud-cont | プログラムの続きを実行 | ||
| C-x C-a C-n | gud-next | ステップ動作(関数を実行) | ||
| C-x C-a C-s | gud-step | ステップ動作 | ||
| C-x C-a C-d | gud-remove | ブレークポイントのクリア | ||
| C-x SPC | gud-break | ブレークポイントの設定 | ||
| C-x C-a C-b | 〃 | 〃 |
このようにソースレベルでデバッグを行うことは簡単なので 「これならプログラムしてみようか」と思ったのではないだろうか。
ChangeLog(変更履歴)を書く
ソースを変更したら必ず ChangeLog を書こう。 他の人と共同で作業する場合はもとより、 自分一人で開発している場合であっても必ず書くと良い。 先月の自分は別人であるという言葉通り忘れるものなのだから。
Emacs はもちろん ChangeLog を書くことをサポートしている。 変更を行なった箇所にカーソルを置いたまま _C-x 4 a_ (add-change-log-entry-other-window) とキー入力する。 すると図のように変更を行っている人の名前、日付、ファイル名、 関数名という必要な項目を挿入した雛型ができあがっている。 これは便利であり、これなら億劫がらずに書けるはずだ。
図18. ChangeLog モードの起動
(クリックで拡大/縮小)
たいていは、新しく追加したものか、修正か、 書き直しかの3つのタイプになるので、 New, fix, Rewrite のキワードで簡単な説明を付けると良い。 それほど詳しくなくとも後述するように、 変更前、変更後の差分(diff) 表示を色分け表示することなどお手のものなのだから 「詳しいことはソースが語ってくれるさ」というのもありだと思うのだ。 そのためにも ChangeLog に変更のキーワードを書くことは重要で、 複数のメンバーで開発している場合は尚更である。
また、ChangeLog を書いておけば後述する CVS の log にも再利用でき、 同じ内容を再入力する必要はない。
バージョン管理
CVS はバージョン管理システムで、プログラムに限らず、テキスト形式の文書 ファイル等のバージョン管理に使うことができ、複数の人での共同作業をもサ ポートする。
CVS はインターネット上のオープンな場での共同開発ツールという側面も持ち、 オープンソース文化台頭の陰の立役者であるとも言われている。
CVS の詳細は解説をした書籍があるのでそちらにゆずるとして、 本稿では Emacs から使うことのできるCVSのフロントエンドで Emacs Lisp で書かれた pcvs パッケージに絞って解説を行う。 なお、pcl-cvs という名前で別パッケージとなっていたものが Emacs21 では取り込まれ標準装備となり、別途インストールする必要はなくなった。
また、CVS というと「サーバが必要になる」、「ひとりで書いているぶんには 必要ない」、「プログラムではなく文書しか書いていないからバージョン管理 は必要ない」と思うかもしれない。しかしそんなことはない。リポジトリを自 分のPCに置いてひとりで使うこともでき、文書であってもバージョン管理は有 用で、以下の利点がある。
バージョン番号付けすることよりもむしろ変更前、変更後を保存しておける点
保存してある版と現在編集中のものとの変更箇所を視覚的に色分けして比較できること
部分的に前の版に戻すことも簡単にできる。
ここではリポジトリもあり、ワーキングディレクトリに checkout した状態から 話を始める。
$ cvs -d /home/kose/cvsrepos init
$ tar zxf hello-2.1.1.tar.gz
$ cd hello-2.1.1
$ cvs -d /home/kose/cvsrepos import -m "GNU Hello v2.1.1" hello FSF_DIST hello-2_1_1
cd /home/kose/sources
cvs -d /home/kose/cvsrepos checkout hello
ベンダー・タグ: FSF_DIST
リリース・タグ: hello-2_1_1
で、ローカルにリポジトリを作成し、checkoutしている。
M-x cvs-examine RET dir-name RET で起動すると 図19 のようになる。 変更したファイルに Modified、 リポジトリにはないがワーキングディレクトリのみに存在するファイルに Unknouwn と表示されている。
図19 cvs-examineを実行
(クリックで拡大/縮小)
M-s とキー入力することで図20のように全ファイルのリビジョンを 見ることができる。 g とキー入力することで図19に戻る。 n で下の行、p で上の行へ移動で、 ログ表示は l で、変更の差分表示は = キーで、 図21のように表示できる。
図20 ステータス表示 図21 変更差分表示
(クリックで拡大/縮小)
ワーキングファイルの変更部分と CVS リポジトリ上の特定の リビジョンとの差分を表示し、変更部分を戻すことも簡単な操作でできる。 hello.c を開いた状態で M-x ediff-revision RET RET rev1 RET rev2 とする。 もしワーキングファイルと CVS リポジトリの先端(枝でもよい)との差分表示 をしたいならば rev1 と rev2 は省略して単に RET を押す。 ediff の操作は簡単で、n で次の変更部分へ移動、p で前の変更部分へ移動、 a で Aバッファの内容を選択、b でBバッファの内容を選択、 r で変更を戻すという一連の操作で、 A と B のどちらの内容を採用するかひとつひとつ視覚的に選ぶことができる。
図22 ediffによる差分表示
(クリックで拡大/縮小)
続いて commit を行う。図24のようにChangeLogを記入し、図25のようにcommit したいファイルにmキーで「*」マークを付ける。この場合ChangeLogとhello.c を同時にcommitしている。Cキー押下で*cvs-commit*という名前のcvs log記入 用のバッファが開く。その中にはあらかじめ記載したChangeLogファイルの内 容が既に用意されているので、内容を確認して C-c C-c で実際にcommitを行 う。
図23 ediffによる差分表示 図24 ChangeLog
図25 logの記入 図26 commit後
(クリックで拡大/縮小)
もし変更部分がかちあって Conflict となってしまってもあわてる必要などない。 *cvs* バッファ上で、Conflict してしまったファイルにカーソルを移動し d E とキー入力を行えばよい。 すると図27のようになり、A と B の衝突してしまった変更のどちらの 採用するかを簡単に選べる状態となる。 操作は先程の ediff と同じで、n, p, a, b, r で良い。
図27 マージ
(クリックで拡大/縮小)
この pcvs があれば、CVS 操作も恐くはないだろう。
主なキー操作一覧を示す。キーバインドは覚えなくとも他のメジャーモードで のキーバンド一覧表示と同じで、C-h m によって見ることができる。 ここでは M-x cvs-examine RET dir RET で起動した *cvs* バッファ上で、 C-h m とキー入力すれば良い。
| h | cvs-help | ヘルプ表示 | ||
| n | cvs-mode-next-line | 下の行に移動 | ||
| p | cvs-mode-previous-line | 上の行に移動 | ||
| f | cvs-mode-find-file | カーソル行のファイルを開く | ||
| a | cvs-mode-add | cvs addを行う | ||
| i | cvs-mode-ignore | カーソル行のファイルを.cvsignoreに追加 | ||
| m | cvs-mode-mark | マークする | ||
| u | cvs-mode-unmark | マークを消す | ||
| = | cvs-mode-diff | 変更箇所を差分表示 | ||
| c | cvs-mode-commit | コミットする | ||
| g | cvs-mode-revert-buffer | 再表示 | ||
| ESC s | cvs-status | cvs status実行 | ||
| ESC u | cvs-update | cvs update実行 | ||
| d E | cvs-mode-imerge | conflictしたものをマージする |
まとめ
Emacs 流の開発の流れの一端、ほんの入口程度は解説できたと思う。 カーソル移動が C-p, C-n, C-f, C-bであったり、 M-x foo RET のような操作ばかりに目が行って、 とっつきにくいと敬遠している読者もいるでしょう。 しかしその障害など目じゃないほど Emacs は魅力、 威力を持っています。このことをおわかりいただければ幸いです。
Emacs は GNU のソフトウェアを開発するために書かれたエディタなのだから、 巨大なソースを自在に操れることを自ら証明しているエディタなのだから、 プログラム開発環境として右に出るものなどないと著者は考えています。