2009年12月30日水曜日

うんにゃらマクロ

「うんにゃら」をマクロにしてみました。


CL-USER> (defmacro unnyara (name-string)
(let ((name-value (gensym))
(not-used (gensym)))
`(macrolet ((,name-value (,not-used)
(find-symbol (string-upcase ,name-string))))
(,name-value t))))
UNNYARA
CL-USER> (setq x 0)
0
CL-USER> (setq y 'a)
A
CL-USER> (proclaim '(special y))
T
CL-USER> (let ((x 1)
(y 'b))
(print (unnyara "x"))
(print (unnyara "y"))
(locally (declare (special x))
(print (unnyara "x")))
(progv '(x y) '(2 c)
(print (unnyara "x"))
(print (unnyara "y"))
(locally (declare (special x))
(print (unnyara "x"))
(values))))

1
B
0
1
C
2 ; No value
CL-USER>

レキシカルにしてもダイナミックにしても、適切な束縛の値を返してくれているようです。

こつこつ。

Common Lispのレキシカルスコープを調べる

2chのLisp系のスレで変数参照の議論があることをg000001さんから教えていただきました。要約するとこんな感じです。

(setq x 1)
(うんにゃら "x")

というシーケンスがあり、2つ目の式の値を変数xの値(すなわち1)とするには、
'うんにゃら'はどう書けばいいの?

回答案として、

(symbol-value (find-symbol (string-upcase "x")))

という類のものがありました。私なりに書き直していますが趣旨は同じものです。

しかしこのやり方だと、変数xについてレキシカルな束縛が存在するところにシーケンスがある場合にダイナミック変数の方の値が取れちゃうのですね。なので私はこの'うんにゃら'が文脈によらず機能するように書くのは、「ANSI CLの範囲では無理」とg000001さんに話していました。

そこでふと疑問がわきました。いや、これ「ANSI CLの範囲」では無理、という一言で済む類のことではないのではないか、と。

問題は二段階に整理できます。

  • STEP 1: レキシカルスコープの中で、シンボルxではなく文字列"x"から変数xの束縛の値を取得できるか? ということ。例は次のとおりです。

    (setq x 0)
    (let ((x 0))
    (seq x 1)
    (うんにゃら "x")) → 1

  • STEP 2: レキシカルスコープの外で、名前で変数xの束縛の値を取得できるか?ということ。例は次のとおりです。

    (setq x 0)
    (let ((x 1))
    (かんにゃら))
    (うんにゃら "x") → 1


前者は不可能なのかどうか。そして、それ以上に、後者ができてしまったら、それはもうレキシカルスコープのレキシカルスコープたる本分を破戒していることになるのではないか。

ああ、、、これがレキシカルスコープという概念を破戒しているかどうかを判断できる程、私はCommon Lispを理解していない。。。

というのが事のはじまりです。久しぶりにCommon Lispの基礎を復習してみよう、と思い立ちました。

結論を先に言ってしまうと、

  • STEP 1: ANSI CLの範囲でもマクロを使えば可能。
  • STEP 2: ANSI CLの範囲ではやはり無理であり、参照できたとしたらレキシカルスコープの本分を破戒していると言ってよさそうだ。

ということがわかりました。この長文エントリは、その検討の際のメモをそのまま掲載するものです。結構長いので、投稿するかどうか悩みました。しかし、もしかしたら、Common Lisp初心者の方がスコープ関係や環境関係を復習するに参考になるかもしれないと考えて、今この前置きを書いている次第です。すでにLisperの方にとっては、アタリマエのことを何くどくどと書いてるんだ? という内容だと思いますので、その筋の方は読まないことをおすすめします。

なお、Emacsのorg-modeなどで読むと、色付けや階層化されて読みやすいです。



      *** 以下、検討メモ ***


* レキシカルスコープの理解を深める

- ふと、レキシカルスコープについて理解が甘いこと
に気付いた。
- 気付いたことを仮説として検証してみることにした。



** レキシカルスコープに関する仮説

- 関数呼出しの文脈にて変数を名前で参照するというこ
とがレキシカルスコープではできないということが、
「CLの仕様が限定的である」ことによるものだと思っ
ていたが、それは誤解である。(名前を使うかにかか
わらず)その文脈で「直接参照できたら」、それはも
うレキシカルスコープ「ではない」のだ。すなわち、
CLの仕様はレキシカルスコープがレキシカルスコープ
であることを維持するために、そのような機構をわざ
と持たないのだ。



** 現象論とその意味からの確認

*** レキシカルスコープの振る舞い

CL-USER> (setq x 10)
10
CL-USER> (defun hoge ()
(let ((x 0))
(piyo)))
HOGE
CL-USER> (defun piyo ()
(+ 1 x))
PIYO
CL-USER> (hoge)
11
CL-USER>

- piyoの中の'x'にて、hogeの中の'x'は参照されていな
い(できない)。
- 'x'という名前によって参照できるということが、
defunの評価時に、すなわち関数の生成時には可能だ
が、そのコードブロックを抜けると消失してしまう。
ただし、その束縛自体は、hogeが参照する関数オブジェ
クトについての全ての参照が無くなって、その関数オ
ブジェクトGCされるまでは残り続ける。これがレキシ
カルスコープの意味または定義。
- 簡潔に言えば、「有限スコープ」かつ「無限エクス
テント」。
- ちなみに一般的には、有限ではなく静的と言う。

*** ダイナミックスコープの振る舞い
- レキシカルスコープを確認した後で、proclaimでダイ
ナミックに切り換える。

CL-USER> (setq x 10)
10
CL-USER> (defun hoge ()
(let ((x 0))
(piyo)))
HOGE
CL-USER> (defun piyo ()
(+ 1 x))
PIYO
CL-USER> (hoge)
11
CL-USER> (symbol-function 'hoge)
#<Interpreted Closure HOGE>
CL-USER> (proclaim '(special x))
T
CL-USER> (hoge)
1
CL-USER> (symbol-function 'hoge)
#<Interpreted Function HOGE>
CL-USER>

- piyoの中の'x'にて、hogeの中の'x'を参照している
(できる)。
- 'x'という名前によって参照できるということが、
defunの評価時のみならず、piyoの呼出し時にも可能
であり、その参照可能状態は、hogeが参照する関数オ
ブジェクトについての全ての参照が無くなって、その
関数オブジェクトGCされるまでは残り続ける。逆に、
hogeの中で'x'と0の束縛は、そのコードブロックを
抜けると消失する。これがダイナミックスコープの
意味または定義。
- 簡潔に言えば、「無限スコープ」かつ「有限エクス
テント」。
- ちなみに一般的には、有限ではなく静的と言う。

*** 仮説の検証

- この現象論レベルの意味で言えば、コードブロックを
抜けたら名前で参照できなくなるのがレキシカルスコー
プなのだから仮説は正しい。

- ちなみに、dynamic scopeという用語はANSI CLでは定
義されていないようだ(用語集にはあるけど、本文に出て
こない)。レキシカルやダイナミックで言うと、本文で定
義されているのは次のもののようだ。

- Lexical Variables
- Dynamic Variables
- Lexical Scope
- Lexical Environment
- Dynamic Environment


** 環境(Environment)の観点で考える

- ANSI CLの意味論は、環境モデルを中心に構成されて
いる。例えばスコープという言葉もあまり出てこな
い。環境の振舞の結果としてスコープが実現される
からだろう。
- そこで、環境の観点にて仮説はどうなのか、考えてみ
る。
- このあたり、ANSI CLとCLtL2で差異がありそうなので、
ANSI CLであるかCLtL2であるかを常に明記することに
する。まず、ANSI CLをやる。余力があれば、CLtL2も
やる。

*** ANSI CL

- ANSI CLは、readerとevaluatorとcompilerとが概念分
離されている。
- それぞれで環境が使われている。
- レキスカルスコープ/ダイナミックスコープを定義し
ているのは、evaluatorの章である。
- 仮説の検証においては、evaluatorだけでも話は済む
かもしれないが、ここではせっかくなので、readerと
compilerでの環境も確認する。

**** reader

***** readerにおける環境とは?
- readerは、streamを入力として、そのstreamから得ら
れる文字の並びをパースし、lisp objects を返すも
のである。
- readerの動作は、次のものを骨格として定義されて
いる。
- the reader algorithm

これはプログラマが変更することはできないもの。
処理系実装者の世界。というかANSI CLの仕様で確
定されているもの。

ANSI CLの構文のほとんどは、このアルゴリズム
「以外」の構成物で定義されている。そしてそれら
はプログラマが変更可能である。よって、CLは構文
を変更可能というよりも「構文を持たない」という
方が実態をよく表していると思う。ANSI CLは「構
文が不確定、意味論が確定」している言語と言える。
(意味論が確定、といってもあくまでメタな確定で
あり、プログラマが変更可能である)

ただし、意味論を説明するには、何がしか構文が必
要なので、仕様化には、意味論の説明用のとりあえ
ずの構文が必要。それが日頃使われているあの構
文であり、標準構文(the standard syntax)という
名前を持つ。

- readtable

文字と構文タイプとを紐付けるテーブル。これはプ
ログラマが変更することができる。構文タイプには
次のものがある。

- constituent
- whitespace
- terminating macro char
- no-terminating macro char
- single escape
- multiple excape

精確には、constituent traits の定義も
readtableにあるがここでは割愛。また、
dispatching macro characterの表の定義も
readtableにあるがここでは割愛。

- tokens 解釈仕様

構文の基礎となるものについては、tokensの解釈
が仕様で決まっている。よって、readtableでなん
でもかんでも自由自在に定義できるわけではない
ということ。決まっているものは次のとおり。

- numbers
- the consing dot
- symbols

- reader macro function

readerがthe reader algorithmにしたがって、a
readtableを使いながら処理していくときに、
readtableにてmacro charとなっている文字を読み
込んだときに呼ばれる関数のこと。

- standard macro characters

説明のための構文のために定義されたreader
macro functionが割り当てられた文字達。

- left parenthesis
- right parenthesis
- single quote
- semicolon
- double quote
- backquote
- comma
- sharpsign

- さて、これらが骨格であるとして、その中には、プロ
グラマが変更可能というものもあった。すると「プロ
グラマが変更」とは具体的にどのようにやるのだろう
か、変更するということはどこかに格納されているは
ずではないか、ということになる。この格納先が、環
境だ。

- 違う観点でみてみよう。プログラマが変更可能なのは、
readerが使うもの達だ。なぜならreaderの挙動を変更
可能であるというとき、reader自体のソースコードを
いじるわけではないからだ。するとreaderは使うもの
達の名前を知っている、もしくはreaderは使うもの達
を「名前で」参照する、ことになる。よって、
readerが使う変数はdynamic variablesでなければな
らないし、その束縛を保持する環境はdynamic
environmentsだ(environmentsの導入はevaluator の
ところで)。実際、ANSI CLの仕様ではreaderが名前で
参照する変数の一覧が定義されており、それらは
dynamic variablesであると決められている。一覧は
次のとおり。

- *package*
- *read-default-float-format*
- *readtable*
- *readbase*
- *read-suppress*

この文書の話題で重要なのは*package*と
*readtable*である。それらについて説明する。

- *readtable*

- これにはa readtable objectが束縛される。
*readtable*に束縛されているa readtable
objectをthe current readtableと呼ぶ。

- Lisp imageが起動したときにthe current
readtable となるreadtableのことを、the
initial readtable と呼ぶ。the initial
readtableはプログラマが変更可能である。

- 処理系には、the standard readtableが埋め込ま
れており、処理系出荷時はthe standard
readtableがthe initial readtableになっている
ことが多い。しかし、(仕様的には)そうでなけれ
ばいけないわけではない。

- *package*

- a packageは名前(names)とシンボル(symbols)の
紐付けを保持するものである。ただし、印字名
(a print name)自体は、a symbol objectの中に
も保持されている。よって名前とシンボルの対応
は、packagesとsymbolsの双方に存在しているこ
とになる。この両者は役割が異なるのだ。
packagesが提供しているのは、名前空間の分離で
ある。すなわち、readerがtokens からsymbolsを
引き当てるときに、(package)修飾子なし名前に
ついてどのsymbolを選ぶかを決めているのが
package 機構である。引き当てだけでなく、新し
いsymbolの登録先の選定にも使われている。
package の基本について、これ以上の説明は割愛
して先に進む。

- *package*に束縛されたa package objectのことを、
the current packageと呼ぶ。

- readerがthe current pakcageをどのように使う
かというと、やはり次のものにすぎない。

- symbolsを表わすtokensについて、それが
package修飾子を持たない場合に、the
symbolsのintern先となる。(ちなみにreaderは
uninternはしない)

- 標準構文のためのpackagesが定義されている。そ
れを standarized packagesと言う。それは次の
3つである。
- common-lisp
- keyword
- common-lisp-user

common-lisp-userは、標準構文に関する要素は
含んでいないが、standarized packagesである
として定義されている。

- 'common-lisp' packageと'keyword' packageを
use-packageすることによって、標準構文で定義
済みの名前(defined names)を、仕様書に記載さ
れているそのままで使うことができるようになる。

***** 初めにthe global environmentありき

- さて、ではLisp imageが起動したときにどのようなa
dynamic environmentが存在するのだろうか。残念な
がらこれはANSI CL には明示的には書かれていないよ
うだ。しかし暗示的には書かれている。

- それが、the global environemt。the global
environmentが保持する束縛は、'indefinite scope'
かつ'indefinite extent'であるという。であるなら、
起動直後から存在していなければならない。そして、
この両方の特性をもっているのは、the global
environmentだけなので、起動時に存在しているもの
はこれだ(け)といってよいだろう。

- ちなみに、the global environmentは'indefinite
scope'かつ'indefinite extent'なので、レキシカル
スコープでもダイナミックスコープでもない。あく
までthe global scope。ただし、どちらかと言えば、
ダイナミックスコープよりではある。

- このthe global environmentが、いろいろなものの
束縛を保持している。
- reader macro functionsについて、名前と関数
objectsの束縛。
- *readtable*や*package*に関する束縛。
- 標準構文のための関数やマクロやコンパイラーマク
ロやタイプやクラスなどに関する束縛。

束縛だけでなく次の情報も保持している。

- proclamations(global environmentにおける宣言
情報)

***** readerは処理途中に環境を変えられるのか?

- さて、疑問がひとつでてきた。readしている段階で、
環境を変えることができるのだろうか?

- 答えは、「できる」だ。「できる」方法が2つ、いっ
けんできそうだが、実際は「できない」方法が1つあ
る。

- 「できる」方法、その1。ANSI CLではファイルに
programを格納しておき、これを読み込むことがで
きる。このとき、ファイルのtop-levelのformsは、
順次評価される。すると、先行して評価される
formsにてthe global environmentの束縛を代入に
て変更した場合、the global environment自体、
indefinite scope, indefinite extentなので、そ
れ以降のformsのread には束縛の新しい値が使われ
る。例は次のとおり。

CL-USER> (setq zvar 123)
123
CL-USER> (defparameter *my-rt* (copy-readtable))
*MY-RT*
CL-USER> (set-syntax-from-char #\z #\' *my-rt* *readtable*)
T
CL-USER> zvar
123
CL-USER> (setf *readtable* *my-rt*)
#<readtable @ #x1000e200e2>
CL-USER> zvar
VAR
CL-USER> (setf *readtable* (copy-readtable nil))
#<readtable @ #x1000d041a2>
CL-USER> zvar
123
CL-USER>

- 「できない」方法。ANSI CLの実行モデルでは、
top-levelにあるa lisp objectの評価方法は、その
lisp objectに含まれる内部構造(lisp objects)を
順番に評価していくというものだ。この評価順で先
行しているもの評価結果(または行為)は後続のもの
の評価に影響を与えうる。よって先行している部分
でreader にかかわる。dynamic variablesの束縛を
変更すれば、後続の部分はその新しい束縛をもとに
動作する。という形で変更可能におもえるが、それ
は間違い。というのはtop-levelにあるS式は、まる
ごとreadされてa lisp ojectにした上で、evalに渡
されるから。なので後続のevalの動作には影響を及
ぼしうるが、readはもう終わっているので影響しよ
うがない。例は続き。

CL-USER> zvar
123
CL-USER> (let* ((*readtable* *my-rt*))
zvar)
123
CL-USER>

- 「できる」方法、その2。できない方法の内部構造
にて'read'が存在していれば、その'read'の動作は、
内部構造で先行しているものから影響を受けること
ができる。例はつづき。

CL-USER> (let* ((*readtable* *my-rt*))
(read-from-string "zvar"))
'VAR
4
CL-USER> zvar
123
CL-USER>

**** evaluator
- ANSI CLにおいて、Evaluationとは、programの実行手
順というか実行のあり様を指す言葉である。ここでい
う'program'の形態は主に2種類あり、ひとつは
readerが返すlisp objects(これをevaluationの観点
ではformsと呼ぶ)、もうひとつは、compilerにて処理
済みのcompiled codeである。
- Common Lispの意味論は、lisp objects(forms) に対
して定義されている。それをベースにて、compiler
やcompiled codeの意味論が記述されている。
- なお、ANSI CLにはevaluatorという用語はめったに出
てこない(稀に本文内に出現する)。もっぱら
evaluationが使われている。しかし、個人的には、
reader、compiler、というように並べると、
evaluatorが自然に思える。もしかしたら、
evaluatorと言わずにevaluationと言うところに機微
があるのかもしれない。いやあるのだろう。そのこと
も今回探ってみたい。

***** そもそも環境ってどんなもの?
****** 環境と環境オブジェクト
- ANSI CLには、環境(environments)と環境オブジェク
ト(environment objects)が登場する。
- 処理系の内部では、環境は環境オブジェクトによっ
て実現されていると思うかもしれない。しかし仕様
はそこまでは求めていない。もちろん、そうしても
よいのだが。
- 環境オブジェクトはマクロ関連の関数で引数として
関数を渡す際の形態として使われるだけである。
- よって、環境という概念は「ANSI CL programの意味
を説明するための概念」にすぎない。違う言い方をす
ると、意味論の用語、である。
- なお、ANSI CLにおいて、環境オブジェクト
は、'lexical environments'を表現するものとして
定義されている。dynamic environmentsを表現でき
るかどうかについては記述はない。
****** 束縛って?
- 束縛(a binding)とは、名前(a name)と、その名前が
指すもの(that which the name denotes)とのつなが
り(an association)のこと。
- 束縛(bindings)がどこに存在するかというと、それは
a lexical environmentまたはa dynamic
environmentの中に、である。
それらenvironmentsにおいて、束縛は「確立される」
(are estabilished)と言う。
- special operators(の一部)のみが、束縛の確立を実
行できる。
****** 環境、束縛と名前空間
- 環境は、まず名前空間で区分けされる。名前空間が異
なるなら、ひとつの環境の中に同じ名前に関する束縛
が複数存在してもよい。ただし、ひとつの名前空間
でとある名前に関する束縛は単一でなければならな
い。
- 名前空間はパッケージで定義する。

***** evaluatorにおける環境の分類
****** the global environment
- "the global environment for evaluation is that
part of an environment that contains ..." すなわ
ち、環境の一部として存在するのだな。
- the global environmentにおける束縛は、'indefinite
scope'かつ'indefinite extent'。
- 主に次のものを含む。
- bindings
- dynamic variables
- constant variables
- functions
- macros
- special operators
- compiler macros
- type names
- class names
- proclamations
****** dynamic environments for evaluation
- "A dynamic environment for evaluation is that
part of an environment that contains ..." すなわ
ち、これまた環境の一部として存在するのだな。
- (ここ重要) dynamic environmentsにおける束縛の特
徴。
- 束縛を確立するlisp formを実行(execute)すると
きに、
- 束縛の開始点と終了点の間だけその束縛は存在す
る。
- 原文: "A dynamic environment for evaluation
is that part of an environment that contains
bindings whose duration is bounded by points
of establishment and desestablishment within
the execution of the form that established
the binding."
- 主に次のものを含む。
- bindings
- dynamic variables
- active catch tags
- exit points (established by unwind-protect)
- active handlers and restarts
****** lexical environments for evaluation
- "A lexical environment for evaluation at some
position in a program is that part of the
environment that contains information having
lexical scope within the forms containing that
position." レキシカルもこれまた環境の一部なのだ
が、the globalやdynamicsとはちょっと趣が違う。
- the globalやdynamicsと趣が違うのは、このレキシカ
ルの定義が束縛(bindings)という言葉を含んでいない
からだ。そしてスコープという、the globalや
dynamicsの定義に含まれていない用語を使っている。
- (ここ重要) lexical environmentsにおける束縛の特
徴。
- まず、lexical scopeの定義。
- その前にscopeの定義。a scopeとは、
- コードの文字面の領域である。
- ただし、すべての文字面の領域をa scopeと呼
ぶのではなく、
- その中で、lisp objects、bindings、exit
points、tagsへの参照が発生するようなもの
ことを呼ぶ。(参照は、通常、名前を使って
実施される)
- 前項を別の言葉で言うと、「環境が発生す
るようなもの」である。
- では、lexical scopeは?
- scopeの一種である。
- ただしその範囲は、束縛を確立するformの文
字面の範囲に限られる。
- では、lexical environmentsにおける束縛の特徴は?
と言うと、このlexical spcopeの定義がそのままあ
てはまる。
- ちょっと疑問。これはstatic scopeということは言っ
ているが、indefinite extentということは言って
いないような。それともこれだけでそれがimplyさ
れるのかなぁ。
- 主に次のものを含む。
- bindings
- lexical variables
- symbol macros
- functions
- macros
- block tags
- go tags
- declarations

******** the null lexical environment
- 'the null lexical environment'は、'the global
environment'と同義である。
- 環境を求める文脈では、'nil'はthe null lexical
environmentを指す。

***** evaluatorにおける環境の利用
- では、evaluatorによって環境はどのように利用され
ているのだろうか。それをThe Evaluation Modelに
したがって確認していこう。

***** The Evaluation Modelに沿って
- ANSI CLにて「programを実行する」とは、lisp
formsを評価することである。評価にあたっては、レ
キシカル、ダイナミックまたはグローバル環境を考
慮する(with respect to)。
- lisp formsは3つに分類できる。symbols、consesと
self-evaluating objectsである。これら毎に評価手
順が定義されている。

****** Symbols
- a symbolは、a symbol macroかa variableであると解
釈され、評価結果を返す。
- a symbol macroであるかどうかは、the current
lexical environmentにて、そのsymbolがa symbol
macroとしての束縛を持つ場合である。
- a symbol macroではない場合は、the name of a
variableと解釈されて、the value of a variableを
返す。
- ところで、variableって何だ、というと、それは束縛
のうちで"variable"名前空間に属するもののことだ。
なので、束縛の一部と考えてもよい。ただし、
"bindings of lexical variables"という表現もある
ことに注意。
- symbolsの評価の観点では、variablesは3つに分類で
きる。lexical variables 、dynamic variablesと
constant variablesだ。
******** symbol macrosの例
CL-USER> (let ((a '(1 2 3))
(sec 'hoge))
(print sec)
(symbol-macrolet ((sec (second a)))
(print sec)
(let ((x 1))
(print sec)
(values))))

HOGE
2
2 ; No value
CL-USER>

******* Lexical Variables
- 特徴を列挙する。
- 確立
- 方法は一種類。
- なんらかのspecial formによって、lexical
variablesは確立される。(ここでは、確立の役
割を担うspecial formをthe formと呼ぶ)。
- 束縛の生成
- the formは、評価されるたびに、freshな束縛
(lexical variables)を生成する。確立と併わせ
て別の言い方をすると、lexical variablesは確
立と束縛が同時に実行され、束縛は確立毎にユ
ニークである。
- a lexical variableは常に値を持つ。'unbound'
という状態は存在しない。別の言い方をすると、
束縛を生成しない限りlexical variablesは存在
しない。
- extent
- lexical variablesの定義には「lexical
bindings は indefinite extentである」ことの
記述は存在しない。それ以前に、extentに関す
る記述が存在しない。それは'Closures and
Lexical Binding'の節で説明されている。
- 参照認定
- a variableがa lexical variableであると認定
されるのは次の条件を満すときである。
- the formのlexical scopeの内側にて、a
symbol がa variableであると解釈されるとき、
the symbol のnameがthe lexical variable
nameと同じならば、the lexical variableへの
参照とみなされる。これが基本。
- shadowing
- the formのlexical scopeの内部にて、他の
form(another formと呼ぶ)によって同じnameに
ついてshadowされているということがある。こ
のときそのnameはthe formのbindingではなく、
another formのbindingを参照していると認定
される。shadowを実現するformは二種類存在
する。
- lexical variablesを確立するforms。
- locally declares the name specialする
forms。
- 環境
- lexical variablesの束縛はlexical
environments に格納される。

******** 例

CL-USER> ; lexical variablesを確立するspecial
; operatorsの例。
; No value
CL-USER> (let ((x 0))
x)
0
CL-USER> ; lexical variablesでは、毎回 freshな束縛
; が生成される例。
; No value
CL-USER> (defun hoge ()
(let ((x 0))
(values #'(lambda () x)
#'(lambda (y) (incf x y)))))
HOGE
CL-USER> (multiple-value-bind (g s)
(hoge)
(setf (symbol-function 'g1) g)
(setf (symbol-function 's1) s))
#<Interpreted Closure (:INTERNAL HOGE) @ #x1000e4fc62>
CL-USER> (multiple-value-bind (g s)
(hoge)
(setf (symbol-function 'g2) g)
(setf (symbol-function 's2) s))
#<Interpreted Closure (:INTERNAL HOGE) @ #x1000e6c1f2>
CL-USER> (g1)
0
CL-USER> (s1 3)
3
CL-USER> (g1)
3
CL-USER> (g2)
0
CL-USER> (s2 100)
100
CL-USER> (g2)
100
CL-USER> (g1)
3
CL-USER> ; dynamic variablesでは、毎回 freshな束縛
; は生成されない例。
; No value
CL-USER> (setq x 0)
0
CL-USER> (defun hoge ()
(progv '(x) '(0)
(values #'(lambda () x)
#'(lambda (y) (incf x)))))
HOGE
CL-USER> (multiple-value-bind (g s)
(hoge)
(setf (symbol-function 'g1) g)
(setf (symbol-function 's1) s))
#<Interpreted Closure (:INTERNAL HOGE) @ #x1000ea7cc2>
CL-USER> (multiple-value-bind (g s)
(hoge)
(setf (symbol-function 'g2) g)
(setf (symbol-function 's2) s))
#<Interpreted Closure (:INTERNAL HOGE) @ #x1000ec4192>
CL-USER> (g1)
0
CL-USER> (s1 1)
1
CL-USER> (g1)
1
CL-USER> (g2)
1
CL-USER>


******* Dynamic Variables
- 特徴を列挙する。
- 確立
- dynamic variablesに確立の概念は無い。
- a dynamic variableは、どのprogramからでも何
時でも参照できる。textualな限定は無いのだ。
- 束縛の生成
- なんらかのspecial formによって、dynamic
variablesの束縛は生成される。そのspecial
formをthe formと呼ぶ。
- 束縛が存在するのは、the formを評価している期
間(区間)だけである。これをdynamic extent と
呼ぶ。別の言い方をすると、あるnameに関する
dynamic bindingは、program 実行のどの時点に
おいても常にせいぜいひとつだけ存在する。
- 前項の確立と併せて別の言い方をすると、
dynamic variablesはどのprogramからでも何時で
も参照可能であるが、参照したときに束縛が存
在するとは限らない。束縛が存在しないこと
を'has no value'とか'unbound'などと言う。
- extent
- dynamic bindings は dynamic extent であるこ
とが、dynamic variablesの定義にて明示されて
いる。
- 参照認定
- a variable が a dynamic variableであると認
定されるのは次の2つの条件のいずれかが成立す
る場合である。(ここではa variableが持つ名前
をthe nameと呼ぶ)
- the nameについてa dynamic bindingを生成す
るformの内側にいる(textually within)とき。
(このformをthe formと呼ぶ)
- the nameがspecial宣言されている
- locally declare
- globally proclaim
- shadowing
- the form の内側にて、the nameについてa
lexical bindigを生成するformが存在する場合
(これをanother formと呼ぶ)、another formの
内側(textually within)においては、the
formのa dynamic bindingではなく、another
formのa lexical bindigを参照していると認
定される。

- 環境
- dynamic bindingsが発生するのは、dynamic
envrionmentsやthe global environmentである。
- the global envionmentにて束縛が発生している
とき、それを'global variables'と呼ぶことがあ
るが、それはdynamic variablesと何ら変わりな
いものである。(違う呼び方をすることがあるだ
けだよ、という意味だろう)

******** 例

CL-USER> ; locally declaredの例
; No value
CL-USER> (setq x 0)
0
CL-USER> (let ((x 1))
(print x)
(locally (declare (special x))
(print x)))

1
0 0
CL-USER> ; globally proclaimedの例
; No value
CL-USER> (setq *x* 10
x 10)
10
CL-USER> (let ((*x* 100)
(x 100))
(print (symbol-value '*x*))
(print (symbol-value 'x)))

100
10 10
CL-USER> ; special formsによって確立する例。
; No value
CL-USER> (boundp 'z)
NIL
CL-USER> (progv '(z) '(0)
(boundp 'z))
T
CL-USER> (boundp 'z)
NIL
CL-USER>

******** 疑問
- 疑問
- 'progv'はネストできる。そのときも、dynamic
bindingはひとつだけ存在すると言えるのだろう
か。

CL-USER> (progv '(z) '(1)
(print z)
(progv '(z) '(2)
(print z))
(print z))

1
2
1 1
CL-USER>

- そうか、言えるのだ。内側の'progv'の中で、外
側の'progv'の束縛を参照する方法は存在しない。
そのことと束縛はひとつしか存在しないという
ことは等価である。それがわかりやすい例。

CL-USER> (progv '(z) '(1)
(progv '(fn) (list #'(lambda () z))
(funcall fn)))
1
CL-USER> (progv '(z) '(1)
(progv '(fn) (list #'(lambda () z))
(progv '(z) '(2)
(funcall fn))))
2
CL-USER>


******* Constant Variables
- 'constant variables'と言うと、矛盾した名称と思
えるが、別の言い方をすると'named constant'のこ
とである。
- constant variablesはthe global environmentに格
納される。

******* 仮説の検証 その2
- lexical variablesの定義にて、lexical variables
はその束縛を確立したformのlexical scopeの内側で
のみ参照可能とあるので、仮説は正しい。
- これでANSI CL的には「the evaluation modelとして
も、仮説は正しい」と言ってもよいだろう。なので、
ここでやめるという手もある。しかし、まだthe
evaluation modelの理解の途中である。一通り理解
した上で判断しよう。

****** Conses
- consesは、4つに分類できる。special forms、macro
forms、function forms、そしてlambda formsだ。
- consesのcarによって、種類を判定する。判定ルール
は次のとおり(疑似コードにて表記)。

(cond ((carがsymbol)
(cond ((carがspecial operator)
(special formと認定))
((carがmacro function)
(macro formと認定))
((carがfunction)
(function formと認定))))
((carが複合form)
(cond ((carがlambda式)
(lambda formと認定))
(t エラー)))
(t エラー))

- ここで、carがa symbolのときに'carがほにゃらら'と
言う部分がどの環境のどこを見ているかという
と、'the current lexical environment' の
'Function'名前空間である。
- 4種類ひとつずつ確認していく。

******* Special Forms
- special formsはまさに環境を操作するための道具で
である。次のものがspecialformsをつくるspecial
operatorsの一覧である。環境に対する操作(参照や変
更)を明示的に含むものにチェックを入れた。
- [X] block
- [X] lexical
- [X] dynamic
- [-] catch
- [ ] lexical
- [X] dynamic
- [ ] eval-when
- [ ] lexical
- [ ] dynamic
- [-] flet
- [X] lexical
- [ ] dynamic
- [-] go
- [X] lexical
- [ ] dynamic
- [ ] if
- [ ] lexical
- [ ] dynamic
- [-] labels
- [X] lexical
- [ ] dynamic
- [X] let
- [X] lexical
- [X] dynamic
- [X] let*
- [X] lexical
- [X] dynamic
- [ ] load-time-value
- [ ] lexical
- [ ] dynamic
- [X] locally
- [X] lexical
- [X] dynamic
- [-] macrolet
- [X] lexical
- [ ] dynamic
- [ ] mutiple-value-call
- [ ] lexical
- [ ] dynamic
- [ ] multiple-value-prog1
- [ ] lexical
- [ ] dynamic
- [ ] progn
- [ ] lexical
- [ ] dynamic
- [-] progv
- [ ] lexical
- [X] dynamic
- [ ] quote
- [ ] lexical
- [ ] dynamic
- [X] return-from
- [X] lexical
- [X] dynamic
- [X] setq
- [X] lexical
- [X] dynamic
- [-] symbol-macrolet
- [X] lexical
- [ ] dynamic
- [-] tagbody
- 基本lexicalだが、特殊。'lexical scope'だ
が'dynamic extent'な振る舞いが定義されてい
るため。
- [X] lexical
- [ ] dynamic
- [ ] the
- [ ] lexical
- [ ] dynamic
- [-] unwind-protect
- [ ] lexical
- [X] dynamic
- 集計
- 環境に明示的に関連している。[18/23]
- レキシカル [11/23]
- ダイナミック [9/23]
- special operatorsの仕様はANSIで固定されている。
special operatorsをグローバル環境で変更すること
は、ANSI CLでは禁止されている。
- special operatorsは、処理系組込みで実装してもよ
いし、マクロで実装してもよい。マクロで実装する
場合は、同種の機能について別の組込み機能を実装
している場合などだろう。
- いずれにしても、special operatorsがspecial
operatorsたる所以は、その動作が関数やマクロでは
実現できないような特殊な振舞をそれぞれ固有に含
んでいることだろう。
- なので、環境との兼ね合いがどうなのかは、special
operatorsを個別に調べるしかない。
- special operatorsの振舞自体は、日頃のプログラミ
ングにて周知のことと思うのでここでは割愛。

******* Macro Forms
- macro formsについての評価手順。
- the current lexical environmentからcarの
symbolのnameに束縛されている関数を取得する。
これには'macro-function'を使う。
- そこで得られた関数は2引数であり、最初の引数は、
macro form全体を受け取り、2つめの引数は、the
current lexical environmentに対応したan
environment objectを受け取る。
- この関数のことを展開関数(the expansion
function)と言う。
- 展開関数は直接呼出されるのではなく、マクロ展
開フックという関数を経由して呼出される。
- マクロ展開フックの関数オブジェクトは、
*macroexpand-hook*に束縛されている(ゆえにプロ
グラマが変更可能)。
- マクロ展開フック(の関数オブジェクト)の引数は、
展開関数の引数に、展開関数の名前を加えたもの
であり、3つである。
- マクロ展開フックが返すのは、マクロ展開関数が
macro form全体をthe current lexical
environmentを考慮しながら変換したformである。
- そのformを再度(というか、再帰的というか再入的
というか)に評価する。
- 余談
- このMacroを'evaluator macro'と呼ぶことにする
と、'reader macro'、'evaluator macro' 、
'compiler macro'と3つ揃い踏みとなり、分かりや
すい。
******** 疑問 その1
- さて、ここで疑問がひとつ。何故、マクロの場合は、
環境オブジェクトを渡してあげるのか? 'eval'は渡
さないじゃん、という比較にて疑問がある。特
に'eval'はformをthe current dynamic environment
とthe null lexical environmentにて評価する。こ
の違いは何だろう。
- まず、'eval'がthe current lexical environment
にてformを評価するということは、そのeval form
の中ではthe current lexical environmentを名前
でいじれるということになのだが、この言明自体、
冒頭のlexicalとdynamicの例のような局面では意味
がないということだろう。例を考えてみよう。

CL-USER> (setq y 10)
10
CL-USER> (defun hoge ()
(let ((y 0))
(piyo)))
HOGE
CL-USER> (defun piyo ()
(eval '(+ 1 y)))
PIYO
CL-USER> (piyo)
11
CL-USER> (hoge)
11
CL-USER>

- 詳細は、次の項目であるfunction formsに譲るが、
(hoge)を評価するとhogeに束縛されている関数オ
ブジェクト(クロージャ)が実行されるが、そのク
ロージャが包んでいる環境のスコープは、defun
formの評価時であり、(hoge)の実行時には束縛に
対する名前での参照は消失している。よっ
て、'eval'に渡せるthe current lexical
environmentは、ここではthe null lexical
environmentただひとつに過ぎない。

- では、the current lexical environmentが存在
するコード領域にて'eval'を呼んでみよう。

CL-USER> (setq y 10)
10
CL-USER> (let ((y 0))
(eval '(+ 1 y)))
11
CL-USER>

- やはりこれもthe null lexical environmentを見
ている。これまでthe nullにするのはなぜか?
それはFunction formsをやった後に確認する。

- さて、マクロ展開関数の第二引数はどのように指
定するのだろう。というのは、ANSI CLの範囲では、
環境を返す仕組みが、special operators、
macros,functions,のいずれにしても存在しない
からだ。ANSI CLの例の抜粋は次のとおり。

CL-USER> (defmacro alpha (x y) `(beta ,x ,y))
ALPHA
CL-USER> (defmacro beta (x y) `(gamma ,x ,y))
BETA
CL-USER> (defmacro expand-1 (form &environment env)
(multiple-value-bind (expansion expanded-p)
(macroexpand-1 form env)
`(values ',expansion ',expanded-p)))
EXPAND-1
CL-USER> (macroexpand-1 '(alpha a b))
(BETA A B)
T
CL-USER> (expand-1 (alpha a b))
(BETA A B)
T
CL-USER> (macrolet ((alpha (x y) `(delta ,x ,y)))
(macroexpand-1 '(alpha a b)))
(BETA A B)
T
CL-USER> (macrolet ((alpha (x y) `(delta ,x ,y)))
(expand-1 (alpha a b)))
(DELTA A B)
T
CL-USER>

- これ、macroexpand-1もenvironmentを&optional
で引数にとるのに、何でマクロでくるんであげ
る必要があるのだろう?
- そうか、次の演繹が成り立つのだろう。
- environment objectsを取り出す方法は存在し
ない。
- それが引数として渡されるのは、マクロ展開関
数を呼出すとき(に勝手に処理系内部で行われ
る)だけである。
- macroexpand-1は関数である。ゆえに
macroexpand-1の呼出しにはマクロ展開関数呼
出し機構は関与しない。
- 関与させるには、マクロとしてラップしてあ
げればよい。
- 付随的な知見として、開発時にREPL上で
macroexpand-1でマクロを展開させているのは、
ソースの中で実際に行われる展開とは異なる文脈
(lexical scope)なので、展開結果は必ずしも一
致しないということを理解した。アタリマエ、な
んだけど、明確には捉えていなかった。

******** 疑問 その2
- この評価手順ならば、macro formsの展開は、評価時
に順次行われるということであり、macroexpand
hook がマクロ展開関数に渡す環境も評価時の
lexical environmentである。すると、いわゆる「マ
クロ展開時(macroexpansion-time)は実行時
(run-time)とは使える情報(環境)が異なるので注意が
必要」というのはThe Evaluation Modelには存在しな
いのだろうか。
- おそらく、異なるというのはcompilerにおいてなの
だろうとは思う。しかし、ANSI CLの意味論の基礎が
The Evaluation Modelにあるとしたら、それと
compiled codeに差異があっては意味が二重になって
しまうのでは?という不安がある。
- これはcompilerのところで確認しよう。

******* Function Forms
- function formsのルールは単純。
- functionの束縛はthe current lexical environment
のものを使うこと。
- 引数を先に評価すること。
- 引数は左から右に評価すること。

- 一点、処理系依存となっている曖昧さがある。それは、
functionの束縛を参照するのが、引数を評価する前
なのか後なのか、ということだ。例は次のとおり。

CL-USER> (defun foo (x) (+ x 3))
FOO
CL-USER> (defun bar () (setf (symbol-function 'foo) #'(lambda (x (+ x 4)))))
BAR
CL-USER> (foo (progn (bar) 20))
23
CL-USER>

- これは23になる処理系と24になる処理系がある(あっ
てよい)。ここではAllegro CLを使用している。

******* Lambda Forms
- lambda formのルールも単純。
- 基本的には、lambda式をa lexical closureにした後
で、funcallしていると考えればよい。その後は
function formsと同じ。(lexical closuresについて
は、'Closures and Lexical Binding'にて導入。
- lambda式をa lexical closureにするところで、
lambda-listに従って束縛が生成され、それはlambda
式の本体を評価する際のthe current lexical
environmentとなる。

****** Self-evaluating Objects
- symbolsでもconsesでも無いformはself-evaluation
objectsである。別の言い方をすると、評価対象が、
symbolsでもconsesでもself-evaluation objectsで
も無ければエラーとなる。
- まあ、これは環境とは関係ないですね。



****** Closures and Lexical Binding
- まず、a lexical closure と a closure は同義であ
る。
- では、a lexical closure とは何か。
- a lexical closure の定義
- 関数の一種である。
- 関数定義(bodyのこと)を文面上内包する
(textually include) lexical bindingsが存在する。
ここでは、そのlexical bindingsをthe bindingsと
呼ぶことにする。
- closureはthe bindingsの値を参照したり変更した
りすることができる。ここ重要。「the bindingsの
名前については言及していない」
- ANSI CLのかきぶりは、「lexical environments
が'indefinite extent'である」というものではなく、
「Closures が lexical bindings を参照/変更でき
る」というものである。
- まあ、lexical scopeの外側でlexical bindingsを参
照/変更する方法がclosuresしかないならば、意味す
るところは同じなのですが。

***** 仮説の検証 その3

- Macro formsの機構によれば、macroexpansion hookを
つかって環境をlexical scope外にひきずりだすこと
ができる。

CL-USER> (defmacro extract (form &environment env)
(setf *env* env))
EXTRACT
CL-USER> (let ((x 1))
(extract 'a))
#<:Augmentable INTERPRETER environment 1>
CL-USER>

- また、macro expanderが環境を捕捉するなら、
lexical scopeの中で'symbol-value'のかわりにマク
ロをつかって名前で値を取得でき
るのではないか。

CL-USER> (setq x 0)
0
CL-USER> (let ((x 1))
(symbol-value (find-symbol (string-upcase "x"))))
0
CL-USER> (let ((x 1))
(macrolet ((lex-var-value (not-used)
(find-symbol (string-upcase "x"))))
(lex-var-value t)))
1
CL-USER>

- お、できる。
- これはきっかけのお題の解の一部ではある。しかし、
仮説の反例にはなっていない。
- この2つを組みあわせて、lexical scopeを越えて、名
前でアクセスできるか?

CL-USER> (let ((*macroexpand-hook*
#'(lambda (expander form env)
(funcall expander form *env*))))
(macrolet ((lex-var-value (not-used)
(find-symbol (string-upcase "x"))))
(lex-var-value t)))
0
CL-USER>

- できない。。。*env*がうまくわたせているのかどう
かをAllegroの環境IF(処理系機能)で確認すると。

CL-USER> (let ((*macroexpand-hook*
#'(lambda (expander form env)
(funcall expander form *env*))))
(macrolet ((lex-var-value (not-used &environment env)
(multiple-value-bind (scope locative-cons)
(sys:variable-information 'x env)
(car locative-cons))))
(lex-var-value t)))
1
CL-USER>

- *env*に変更はできているようだ。しかしマクロ展開
関数に使われないという不思議な状況。
- ANSI CLを確認すると、'3.8.15
*macroexpansion-hook*'に次のような記述があった。

'The environment object has dynamic extent; the
consequences are undefined if the environment
object is referred to outside the dynamic
extent of the macro expansion function.'

ということは、Allegroでは、macro expansion
functionの呼出しを使って、specialたる*env*に
Environment Objectを束縛してひきづり出せたが、こ
のこと自体、ANSI CLでは保証されていないということ
だし、それをextent外で使用した場合の動作は不定
ということだろう。

- というわけで、引き続き仮説は正しい。


さて、この後、compiler、CLtL2、処理系独自対応などを調べているところで力尽きました。。。
いや、正確には、飽きました。。。

それらの話題は、またモチベーションが上がったときに書いてみたいと思います。


こつこつ。

2009年12月21日月曜日

Common LispとEmacs Lispの違い [関数のあたり]

というわけで、Prolog on Emacsの完成を目指して、Common LispとEmacs Lispのスコープ関係の違いを確認してみようと思う。
スコープ自体は、Common Lispがレキシカルとダイナミックの両用で、Emacs Lispはダイナミックのみというだけでさして難しくはないのだが、では、言語の中のいろいろな機構の振舞いはそれによってどういう違いがあるかというと、また別の話である。

まず、関数のあたりから調べてみよう。


* 関数のあたり
** symbolへの格納
- まず、symbolまわりがどうなっているかをなんとな
く。
- Emacs LispはLisp2であり、インターフェイスも
Common Lisp風。

*** Common Lisp の場合
CL-USER> (setq hoge 1)
1
CL-USER> (defun hoge (x) (+ 2 x))
HOGE
CL-USER> hoge
1
CL-USER> (hoge 3)
5
CL-USER> (symbol-value 'hoge)
1
CL-USER> (symbol-function 'hoge)
#<Interpreted Function HOGE>
CL-USER> (compile 'hoge)
HOGE
NIL
NIL
CL-USER> (symbol-function 'hoge)
#<Function HOGE>
CL-USER>

*** Emacs Lisp の場合
(setq hoge 1)
(defun hoge (x)
(+ 2 x))
hoge ; => 1
(hoge 3) ; => 5

(symbol-value 'hoge) ; => 1
(symbol-function 'hoge) ; => (lambda (x) (+ 2 x))
(byte-compile 'hoge)
(symbol-function 'hoge) ; => #[(x) T\207" [x] 1]



** 関数定義

*** 'defun'
- 名前をもつ関数を作成する。
- 名前はグローバルになる。

**** Common Lisp の場合
- マクロ。

CL-USER> (defun defun-at-global () 'global)
DEFUN-AT-GLOBAL
CL-USER> (defun-at-global)
GLOBAL
CL-USER> (let ((hoge))
(defun defun-in-let ()
'let))
DEFUN-IN-LET
CL-USER> (defun-in-let)
LET
CL-USER>

**** Emacs Lisp の場合
- special form。

(defun defun-at-global ()
'global)
(defun-at-global) ; => global
(let ((hoge))
(defun defun-in-let ()
'let))
(defun-in-let) ; => let

*** 'lambda'
**** Common Lisp の場合
- lambda formは、リストである。ただし、'lambda'を
第一要素とするなど、lambda expressionたるリスト
の構造の規約がある。この意味での'lambda'は単なる
シンボルである。
- a function name を求める場所に、lambda formを使
うことができる。(ただし、常に可能というわけでは
ない) このとき、そのlambda formをlambda
expressionと言う。lambda expressionは、関数の振
る舞いを直接表現していると言える。
- lambda formsがよく使われる場所は2つある。ひとつ
目は、通常の関数呼出し、すなわちリストの第一要
素として。例えば次のとおり。

CL-USER> ((lambda (x y) (+ x y)) 1 2)
3
CL-USER>


- ふたつ目は、'function' special operator の引数と
して。例えば次のとおり。

CL-USER> (function (lambda (x) (+ 1 x)))
#<Interpreted Function (unnamed) @ #x1000eec532>
CL-USER>

- このイディオムのためのリーダーマクロが存在する。
それが"#'"であり、後続のS式を(function )でくる
む。

CL-USER> #'(lambda (x) (+ 1 x))
#<Interpreted Function (unnamed) @ #x1000d2bb22>
CL-USER>

- また、略記のためのマクロ'lambda'がある。

CL-USER> (lambda (x) (+ 1 x))
#<Interpreted Function (unnamed) @ #x1000d5a402>
CL-USER> (macroexpand-1 '(lambda (x) (+ 1 x)))
#'(LAMBDA (X) (+ 1 X))
T
CL-USER>


**** Emacs Lisp の場合
- Emacs Lispでは、lambda expressionsは普通の
expression (式)であり、評価すると自分自身になる。
例は次のとおり。

(lambda (x y) (+ x y)) ; = > (lambda (x y) (+ x y))

- Emacs Lispでは、関数は、Cで書かれたものと、Lisp
で書かれたものがある。Lispで書かれたものは、バ
イトコンパイルされていないものと、バイトコンパ
イルされているものがある。バイトコンパイルされ
ていないLisp関数は、全てlambda expressionsで記
述されている。よって、言語仕様としては、Common
Lispと比べて、Emacs Lispの方がlambda
expressionsを関数概念の中心に置いている。多少
Schemeに近い部分がある(ただしクロージャが無い点
がずいぶん違う)。

- lambda expressions はそのままで関数定義なので、
関数が使えるところならどこでも使え
る。

((lambda (x y) (+ x y)) 1 2) ; => 3

- 'function'は、基本的には'quote'にすぎない。

(function (lambda (x y) (+ x y))) ; => (lambda (x y) (+ x y))


*** Closures
- Common Lispではクロージャを使える。Emacs Lisp で
はクロージャは使えない。
- Common Lispでクロージャを生成するには、レキシカ
ル環境を参照しつつ関数を定義する。Common Lispで
は、クロージャをレキシカルクロージャやクロージャ
オブジェクトとも呼ぶ。
- Common Lispでは、クロージャは関数の一種と位置づ
けられている。

*** fletとlablels
- Common Lispでは、fletやlabelsを用いて、局所的な
関数をつくることができる。別の言い方をすると、
関数の名前をスコーピングできる。
- Emacs Lisp自体では、このような機構は提供されて
いない。clパッケージにてマクロで提供されている。



** 'function' special operator/form
- 'function'の振る舞いは'flet'との兼ね合いが重要
である。

*** fletとの兼ね合い
**** Common Lisp の場合
- functionは、レキシカル環境で、名前に紐付けられた
関数オブジェクト(関数定義とも言う)を返すspecial
operator。文脈によって、symbol-functionとは返す
ものが異なる。

CL-USER> (defun hoge (x) (+ 2 x))
HOGE
CL-USER> (flet ((hoge (x) (+ 3 x)))
(funcall (function hoge) 1))
4
CL-USER> (flet ((hoge (x) (+ 3 x)))
(funcall (symbol-function 'hoge) 1))
3
CL-USER>

- 名前に紐付けられた関数定義が存在しない場合、
functionはエラーをあげる。

CL-USER> (function hoge)
#<Interpreted Function HOGE>
CL-USER> (function piyo)
; Evaluation aborted.
CL-USER>

**** Emacs Lisp の場合
- 一方、Emacs Lispでは、fletはマクロである。マクロ
でシミュレートするにあたって、symbolのfunctionセ
ルをつかっている。よって、functionと
symbol-functionの挙動は文脈によらず同じ。

(defun hoge (x) (+ 2 x))
(flet ((hoge (x) (+ 3 x)))
(funcall (function hoge) 1)) ; => 4
(flet ((hoge (x) (+ 3 x)))
(funcall (symbol-function 'hoge) 1)) ; => 4

- functionはspecial form。'function'は、プログラ
マにとっては'quote'と同じ。コンパイラ
は、'function'が使われているときはそれは
Function cellしか使われないことがわかるので、最
適化ができる。

- 'quote'と変わらないだけあって、関数定義が存在し
なくてもエラーにならない。

(defun hoge () nil)
(setq piyo nil)
(functionp hoge) ; => t
(functionp piyo) ; => nil
(function hoge) ; => hoge
(function piyo) ; => piyo



** funcall
*** Common Lisp の場合
- funcallは関数。引数は、'a function
designator'。'function designators'は関数を指し
示すなんらかのLisp Objectsのこと。関数なので、引
数は eager に評価される。アタリマエだが、評価さ
れた結果、function designatorsになるものならなん
でも引数にしてよい。
- function designators
- 関数オブジェクト
- この場合、'funcall'は、関数オブジェクトをそ
のまま使う。
- シンボル
- この場合、'funcall'は、global environmentを
参照して、symbolのfunction cellに格納されて
いる関数定義を利用する。

- 関数オブジェクトの例

CL-USER> (funcall #'car '(1 2 3))
1
CL-USER> (funcall #'(lambda (x y) (+ x y)) 1 2)
3
CL-USER> (funcall (lambda (x y) (+ x y)) 1 2)
3
CL-USER> (setq hoge #'(lambda (x y) (+ x y)))
#
CL-USER> (funcall hoge 1 2)
3
CL-USER>

- シンボルの例

CL-USER> (funcall 'car '(1 2 3))
1
CL-USER>


*** Emacs Lisp の場合
- funcallは関数。引数を eager に評価して、評価結
果によって振舞が違う。
- 評価結果がシンボルの場合。
- そのシンボルのfunction cellにある関数定義を
採用。
- 評価結果が関数の場合
- それを採用。関数がLisp系でもC系でも。
- 例

(setq hoge (lambda () 'fn-for-value-cell))
(defun hoge ()
'fn-for-function-cell)
(symbol-value 'hoge) ; => (lambda () 'fn-for-value-cell)
(symbol-function 'hoge) ; => (lambda () 'fn-for-function-cell)
(funcall hoge) ; => fn-for-value-cell
(funcall 'hoge) ; => fn-for-function-cell

(symbol-function 'list) ; => #<subr list>
(funcall (symbol-function 'list) 1 2) ; => (1 2)


こつこつ。

2009年12月20日日曜日

Smiley Hackathon #7 に参加

昨日Smiley Hackathon #7に参加しました。Hackathonは初めてだったので、いろいろ粗相をしてしまったような気がするのですが、暖かく接してくださった参加者のみなさんに感謝です。素敵な会場を提供してくださったgaiaxさんに感謝。そして、acotieさんを中心に運営されている方々に感謝です。

備忘録として、自分なりのトピックを。


  • 高校生にHaskellを教わる、という体験はなかなかできないので、新鮮だった。
  • 囲碁関係のお話もとてもおもしろかった。
  • いろいろな言語の人が参加していたが、やはりPerlの人が多かった 。Perlの重鎮の方もゲストで参加されていた。

  • Hackathonに慣れていなかったので、いつもと違う環境で集中するのにちょっと時間がかかった。でも3時間くらいで慣れた。
  • せっかく集っているのでいろいろお喋りしたいということと、Hackathonなので何か作りあげたいということが交錯した。どっちに軸足を置くか、あらかじめ決めておくとよいかも。初回は、せっかくなのでいろいろお喋りするのが楽しいと思います。

  • 自分としては、elisp library introspectionのmajor-modeをそこそこ書くことができた。
  • 途中で、データ構造について設計遅延させたくて、elispのprologインタプリタが欲しくなり、PAIPのCommon Lispのものを移植した。ただし、とりあえず動いているだけで、スコープ関係があやしい。それは残課題。

  • 懇親会に参加して、LispやHaskellの興味深いお話が聞けた。

  • やはりいろいろな人に出会いお話を聞くことは、刺激になった。

2009年12月8日火曜日

emacsのライブラリパッケージ取扱方針

多少調査したので、見えてきた。次の方針がよいように思う。

  • Ubuntu(Debian)のElispパッケージを採用する。(debian-emacs-policy)

    • 依存関係も管理しているので安心。
    • インストール/アンインストールも統一的。
    • ただし、パッケージをつくる手間は結構ある。

  • 補完的に、Auto Installを採用する。

    • Ubuntu(Debian)のElispで提供されていないものは、Auto InstallにてElispAreaなどから採用する。
    • パッケージ化の手間がないので、アジャイルにライブラリの導入ができる。

  • Auto Install -> Ubuntu(Debian)

    • Auto Installにて導入したライブラリが、有用であり安定しているならば、パッケージ化のプロセスに入る。

      • まず、Auto Install上での安定。依存関係があるなら、Auto Install上でbatch定義する。また、ライブラリのリビジョンを止めるために、ElispAreaではなく、ローカルに落として止めたリビジョンのみを見るなど。
      • 次に、debian-emacs-policyに準拠したdebian packageを作成する。これをプライベートリポジトリに置き運用する。
      • 最後に、debianパッケージメンテナ等に依頼して、公式リポジトリにアップロードしてもらう。



こつこつ。

debian-emacs-policyとXEmacs package systemの関係


* debian-emacs-policyとXEmacs package systemの関係
- debianのパッケージにXEmacs package systemのパッケージあり
- xemacs21-basesupport
- xemacs binary packages
- xemacs21-basesupport-el
- xemacs source packages

- ここにXEmacs package system公式提供のものはすべ
て入っているようだ。
- debian-emacs-policyに従ったpackagesと、XEmacs
package systemのpackageの両方に含まれているpackage
がけっこうある。
- どうやら、御互い無関係にやっているようだ。
- ユーザ自身が、どちらのパッケージシステムでいく
かの判断をしなければならない。


こつこつ。

XEmacs package system

XEmacs、ほぼ初めていじったといってよいのですが、このpackage systemは結構ちゃんとしていてびっくりです。依存関係管理を装備すれば、完備かと。

うーん。GNU Emacsにもこういうものが欲しい。。。


* XEmacs package system
- パッケージの置き場所
- パッケージの置き場所は、file system上のpathで
系統づけている。その系統を'パッケージ階層'と呼
ぶ。デフォルトでは3つの系統がある。
- startupの際に、パッケージ階層をXEmacsは見るこ
とができる。load-pathにする、ということではな
いようだ。M-x describe-installationでpackage
pathの値をみることができる。
- パッケージ階層 (package hierarchies)
- Local and 3rd party packages go here:
- $prefix/lib/xemacs/site-packages
- Only searched by MULE-enabled XEmacsen:
- $prefix/lib/xemacs/mule-packages
- Normal packages go here:
- $prefix/lib/xemacs/xemacs-packages
- Infodock development environments
- $prefix/lib/xemacs/infodock-packages

- $prefixはconfigure scriptに渡したパラメータ。
- 典型的には'/usr/local/'。
- ユーザのパッケージ階層
- '.xemacs/'配下
に、'site-packages'、'xemacs-packages'、
'mule-packages'ディレクトリをつくると、認識
される。
- パッケージ階層とload-path
- パッケージ階層達の優先度はXEmacsのコンパイ
ル時に指定する。
- 前項の指定順にしたがって、パッケージ階層を
調べて、load-pathを構成する。

- パッケージ
- バイナリパッケージとソースパッケージがある。
- ソースパッケージからバイナリパッケージを生成
する。'バイナリ'と言うが、実際はバイトコード
(コンパイルされたelispファイル)。

- バイナリパッケージアーカイブ
- XEmacs公式
- Official XEmacs Packages:
- XEmacs開発者によってテスト済み。
These packages have received testing from XEmacs developers and bleeding-edge users
and are available as documented in our Release Notice Archive.
- Pre-Release XEmacs Packages:
- テスト中またはテスト待ちのパッケージ。
- アーカイブではなく、パケージ個別に独立管理し
ているもの
- http://www.xemacs.org/Download/elispPackages.html
- ただし、このリストにあるもので、XEmacs
packagesに含まれることもあるとのこと。なん
のこっちゃ。
- http://ftp.xemacs.org/pub/packages/
- 130前後のパッケージあり。

- ソースパッケージリポジトリ
- XEmacs CVSリポジトリの一部。次の構造。

<repo-root>/
packages/
Makefile
iterate.rules
meta-iterate.rules
XEmacs.rules
Local.rules
Local.rules.template
Local.rules.mk
Local.rules.inc
package-compile.el
xemacs-packages/
Makefile
pkg-A-name/
pkg-B-name/
...
mule-packages/
Makefile
pkg-1-name/
pkg-2-name/
...
- 再帰的makeによって、パッケージ全体をビルドす
るようだ。

- パッケージの作法
- パッケージのflavor
- 2つのflavorがある。
- Regular Packages
- elispファイル達総体として機能をなすもの。
- 部分的に削除すると、機能しなくなる。
- Single-File Packages
- 個別elispファイルで機能するもの。そういっ
たものを共通テーマでくくったもの。
- 部分的に削除しても残りのものは機能する。
- ファイル名
- pkgname--pkg.tar.gz
- バイナリパッケージの中身の構成

_pkg.el
auto-autoloads.el
custom-load.el
ChangeLog
lib/pkgname/
lib-src/pkgname/
etc/pkgname/
info/
lisp/maybe-pkgname/
man/maybe-pkgname/
pkginfo/MANIFEST.pkgname

- _pkg.el
- パッケージ管理システム向け情報。
- auto-autoloads.el
- autoloadを中心に、pkgを使う(使えるように
する)ための設定を含む。
- custom-load.el
- customに対する設定を含む。
- pkginfo/MANIFEST.pkgname
- インストール対象ファイルの一覧を含む。

- ソースパッケージの構成
- バイナリパッケージの '_pkg.el' 、
'auto-autoloads.el'、'custom-load.el' は生
成物。そのかわりに、次のものがある。

Makefile
package-info.in

- 制御ファイル (Control Files)
- ソースとバイナリにまたがって、制御系のファ
イル達のことを制御ファイルと呼ぶ。具体的に
は、次のとおり。

Makefile
package-info.in
_pkg.el
auto-autoloads.el
custom-load.el

- インストール
- 基本的にはパッケージ階層のトップにてuntarする
だけ。
- elispによるインストールフロントエンドもあり。

- アンインストール
- 関数:package-admin-delete-binary-packageにて
削除可能。

- 依存関係
- 依存関係を扱う機構は無い。

こつこつ。

2009年12月7日月曜日

Auto Install

現在自分が使用しているRev.1.18を読んでみた。
印象は、EmacsWikiのElispAreaインターフェイス。


* Auto Install
- ソースがきれい。ちゃんとしている雰囲気。
** パッケージの取得先
- auto-install-emacswiki-base-url
- "http://www.emacswiki.org/cgi-bin/wiki/download/"
- auto-install-gist-base-url
- "http://gist.github.com/"
- auto-install-filter-url
- '(("color-grep" "http://www.bookshelf.jp/elc/"))
- auto-install-batch-list
- バッチインストールのためのパッケージ情報。
** パッケージのデフォルト保存場所
- auto-install-directory
- "~/.emacs.d/auto-install/"
- 新規に導入するelispファイルの置き場所。
** 基本的な動作
- emacswikiのelisp areaをelispの主たるアーカイブと
位置付けている。副次的なものとして、gist、
filter-urlがある。
- elisp areaから、'.el'ファイルの一覧を取得し、こ
れを取扱い可能ライブラリの名前として利用する。
- 基本動作としては、この名前でライブラリを指定し
て、ダウンロード-中身確認-採用-配置となる。
- ライブラリの指定は、mini-bufferで指定する方法と、
既存のelispをdired上で指定する方法の二種類がある。
- 配置場所も二種類ある。すでにそのライブラリが
load-path上に存在するなら、それへの上書き配置が
可能である。load-path上に存在しないなら、
auto-install-directory内に配置される。
- 依存関係を自動処理する方法は無いようだ。
- ただし、複数ファイルで構成されるライブラリについ
ては、auto-install-batch-listにて明示的にelisp
ファイルのグループをつくることによって、一括イ
ンストールが可能。

あとはXEmacsのPackage Systemだ。
こつこつ。

2009年12月5日土曜日

ELPA (Emacs Lisp Package Archive)


* ELPA (Emacs Lisp Package Archive)
- 次の2つのファイルがパッケージ一覧の基本情報。
package-list-packagesを呼ぶと、
package-refresh-contentsによってWebから取得され
る。
- http://tromey.com/elpa/archive-contents
- これがELPA提供のパッケージの一覧を含むファ
イル。依存関係も記述されている。
- 104個のパッケージを含む。
- http://tromey.com/elpa/builtin-packages
- Emacs組込パッケージと衝突しているものについ
て、整理情報を記述。22.0用と23.0用が含まれ
ている。
- バージョンを考慮した依存関係管理をしている。
- パッケージの様式
- <package-user-dir>/<NAME-VERSION>/にそれぞれのパッ
ケージをダウンロードする。
- .el
- ELPA上では'NAME-VERSION.el'というファイル名
をもつ。
- DLすると、'NAME-VERSION'というディレクト
リに'NAME.el'というファイル名で格納する。
- .tar
- "PACKAGE-pkg.el"を含まなければならない。
- このファイルの中では、define-packageが
呼ばれている。
- define-package
- パッケージを定義して、package-alistにパッケー
ジ情報を格納する関数。


こつこつ。

Emacs Lisp List


* Emacs Lisp List
- http://www.damtp.cam.ac.uk/user/sje30/emacs/ell.html
- さて、前回ell.elをローダと書いたが、ローダでは
なかった。
- これは、ell.xmlをemacsのbufferにきれいに表示す
るものだ。いくつか機能を提供している。
- 著者名によるソート機能。
- ell.xmlに更新があった場合、更新項目に"<new>"
をつける。
- すでに導入済みのライブラリについては、項目の頭
に"*"をつける。
- ちなみに、Emacsではelispを含んだファイルをお
しなべてライブラリと呼ぶ。
- avltree
- ell.xmlをパースしてavltreeにする。
- 作成したavltreeは'~/.ell-last-read'にファイル
として保持して差分確認にも利用する。


こつこつ。

2009年12月4日金曜日

Emacsのパッケージング事情

debian emacs policyを理解できてきたところで、他のパッケージ管理機構についてざっと調べてみた。結構いろいろな情報がある。

何かひとつをちゃんと知るということは、やっぱり大変なことだなぁと再認識した。


* Emacsのパッケージング事情
** ヘッダ等の慣習
*** Conventional Headers for Emacs Libraries
- http://www.gnu.org/software/emacs/manual/html_node/elisp/Library-Headers.html#Library-Headers
- GNU Emacs Lisp Referenceの一部。
- Elispファイルを書くときの慣習。
- ライブラリのメタデータ(著者等)をコメントとして
記述するための慣習。
- ライブラリの中身をコメントで構造化するための慣習。
- 例:メタデータ

;;; lisp-mnt.el --- minor mode for Emacs Lisp maintainers

;; Copyright (C) 1992 Free Software Foundation, Inc.

;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
;; Maintainer: Eric S. Raymond <esr@snark.thyrsus.com>
;; Created: 14 Jul 1992
;; Version: 1.2
;; Keywords: docs

;; This file is part of GNU Emacs.
...
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.

ここに本体コード。

;;; filename --- description

- 例:構造化コメント

;;; Commentary:
;;; Documentation:
;;; Change Log:
;;; Code:
;;; filename ends here

*** Elisp Area Conventions
- http://www.emacswiki.org/cgi-bin/wiki/ElispAreaConventions
- EmacsWikiのElisp Areaに置くelispのかきぶりの慣
習の説明。
- GNU Emacs Lispの慣習をベースにして、詳細化や拡
張を実施している。

** ヘッダツール
*** Automatic File Headers
- http://www.emacswiki.org/emacs/AutomaticFileHeaders
**** auto insert mode
- ユーザが定義したテンプレートにしたがって、ファイ
ル作成時にヘッダを自動的に挿入する。
auto-insert-alistでファイル名パターンとテンレプー
トを紐付ける。
**** header2.el
- Elisp Area Conventionsに対応したヘッダを自動挿入
や自動更新するelisp。
- そのライブラリが必要とするfeaturesを記述する欄を
設けている。(これは他のConventionsでは定義されて
いない)
- ;; Features that might be required by this library:
**** lib-requires.el
- ライブラリの依存情報を調査して、木構造をつくる
elisp。
**** elisp-depend.el
- elispファイルをparseして、そのelispで依存してい
るライブラリを探索し、それぞれについて、それが
providedならば、require式を出力し、そうでなけれ
ばautoload式を出力する。
**** checkdoc
- http://www.emacswiki.org/emacs/CheckDoc
- elispファイルのヘッダをチェックする。GNU慣習と
EmacsArea慣習の双方に対応している。

** パッケージ管理機構
*** Emacs Lisp List
- http://www.damtp.cam.ac.uk/user/sje30/emacs/ell.html
- http://www.damtp.cam.ac.uk/user/sje30/emacs/ell.xml
- http://www.damtp.cam.ac.uk/user/sje30/emacs/ell.el
- パッケージ一覧ファイル(ell.xml)とそれを参照する
ローダ(ell.el)を提供。
- ell.elではavltreeをつかってパッケージのキャッシュ
を作成し管理している。興味深い。

*** Auto Install
- http://www.emacswiki.org/emacs/AutoInstall
- フルセットのパッケージ管理が可能。
- パッケージの取得先はEmacsWiki他、いくつか指定可
能。
- パッケージの依存関係の管理などがどうなっている
のかは要調査。

*** ELPA
- http://tromey.com/elpa/
- パッケージアーカイブとそれを参照するローダを提
供。
- Web上の説明が簡素なため、理解するためには使って
みる必要がありそう。
- パッケージアーカイブの仕様を調べてみたい。

*** Install Lisp
- http://www.emacswiki.org/emacs/InstallElisp
- Auto Installの前身と考えてよさそう。

*** XEmacs package system
- http://www.xemacs.org/Documentation/packageGuide.html
- XEmacsはパッケージ管理機構を組み込みでもってい
る。専用のパッケージアーカイブも存在する。
- debian-emacs-policyとはどういう関係になるのだろ
う。
- 詳細は要調査。


こつこつ。

2009年12月2日水曜日

【maxima】ちょっと不安定?

ちょこちょこmaximaで遊んでいるが、いろいろなところで、Segmentation Faultとか、Stack Overflowとかが発生する。ちょっと不安定なような?

例えば。


Maxima 5.13.0 http://maxima.sourceforge.net
Using Lisp GNU Common Lisp (GCL) GCL 2.6.8 (aka GCL)
Distributed under the GNU Public License. See the file COPYING.
Dedicated to the memory of William Schelter.
This is a development version of Maxima. The function bug_report()
provides bug reporting information.
(%i1) eq1:[x^2+y*x-1,x*y^2-x+y];
2 2
(%o1) [x y + x - 1, x y + y - x]
(%i2) ans:algsys(eq1,[x,y]);

Unrecoverable error: invocation history stack overflow.

Process maxima aborted



Interpidのmaximaはgclを内包しているようだ。これを、allegroなり、sbclなりに変更すれば、common lispの中で状況を追えるかな? GCLだとGDBで追うことになりそうな。Maxima/GCL/C/Executableという階層でMaximaの問題をExecutableで追うのはかなりきつい。。。

2009年12月1日火曜日

debian-emacs-policyのポイント (2)

perlを思い出しつつ、スクリプトを多少調べてみた。


* emacsen-commonのスクリプト
- debian-emacs-policyにある例を理解できない。それ
を理解したい。
- 例1

1) Xemacs21 and the add-on packages tm and auctex are already installed,
and now someone installs emacs21.

In it's postinst, emacs21 would make this call:

/usr/lib/emacsen-common/emacs-install emacs21

which would result in calls to

/usr/lib/emacsen-common/packages/install/auctex emacs xemacs21
/usr/lib/emacsen-common/packages/install/auctex emacs21 xemacs21
/usr/lib/emacsen-common/packages/emacs21/install/tm emacs xemacs21
/usr/lib/emacsen-common/packages/emacs/install/tm emacs21 xemacs21

- わからないところの特定
- /usr/lib/emacsen-common/emacs-install emacs21
- これはわかる。これをpostinstで実行するとがポ
リシーだから。
- しかし、上の呼出しが、

/usr/lib/emacsen-common/packages/install/auctex emacs xemacs21
/usr/lib/emacsen-common/packages/install/auctex emacs21 xemacs21
/usr/lib/emacsen-common/packages/emacs21/install/tm emacs xemacs21
/usr/lib/emacsen-common/packages/emacs/install/tm emacs21 xemacs21

これになるのはわからない。特に、なんで'emacs
が呼ばれるの?というところ。具体的には、

/usr/lib/emacsen-common/packages/install/auctex emacs21 xemacs21
/usr/lib/emacsen-common/packages/emacs/install/tm emacs21 xemacs21

こうじゃないの? と。

- ソース解読
- 解決。私の「こうじゃないの?」で正しそう。
- 根拠。
- install済みのflavorsは、

/var/lib/emacsen-common/installed-flavors

に記載されており、emacs-install
scriptも'other-flavors'の値はこれを使用してい
る。なので、ここに何が書いてあるかが、展開し
て実行する命令群を決める。
- すると、ポイントは、meta packageである'emacs'
がここに含まれるかどうか、だ。私の環境では、

i emacs - The GNU Emacs editor (metapackage)
i emacs22 - The GNU Emacs editor (Emacs 22)

というinstall状況だが、installed-flavors
は'emacs22' のみを内容として含んでいる。
- 実証。
- 'emacs22'と'xemacs21'がinstall済みの状態で、
'emacs21'にて、emacs-install scriptをdry
runさせてみる。を入れてみよう。
- emacs-installをcopyしてemacs-install-testと
して、my $dry_run = 1;とする。

emacsen-common $ ./emacs-install-test emacs21
emacs-install emacs21
/usr/lib/emacsen-common/packages/install/apel emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/cedet-common emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/debian-el emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/dictionaries-common emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/emacs-extra emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/emacs-goodies-el emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/emacsen-common emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/global emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/latex-cjk-common emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/lookup-el emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/mdk emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/mew emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/mmm-mode emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/octave3.0-emacsen emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/paredit-el emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/prolog-el emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/ruby1.8-elisp emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/w3m-el emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/flim emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/elscreen emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/ddskk emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/speedbar emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/eieio emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/dpkg-dev-el emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/slime emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/nxml-mode emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/maxima-emacs emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/latex-cjk-thai emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/ede emacs21 emacs22 xemacs21
/usr/lib/emacsen-common/packages/install/semantic emacs21 emacs22 xemacs21
Installed flavors are now: emacs22 xemacs21 emacs21
emacsen-common $
- うむ。実証できた。

- 例2
- これは例1のremove版なのでinstallと同様の動作
であろう。内容確認は割愛。

- 例3
- an add-on packageをremoveする例。
- 'emacs22'と'xemacs21'がinstall済みとする。
- 'wget-el' add-on packageがinstall済みとする。
- 'wget-el'をremoveする。
- 例の挙動は、次のとおり。
- prermにて、emacsen-package-removeが'wget-el'を
引数にて呼ばれる。
- その結果、次の命令群が生成される。

/usr/lib/emacsen-common/packages/remove/wget-el emacs emacs22 xemacs21
/usr/lib/emacsen-common/packages/remove/wget-el emacs22 emacs22 xemacs21
/usr/lib/emacsen-common/packages/remove/wget-el xemacs21 emacs22 xemacs21

- ここでも、'emacs'が何故存在するのかがわから
ない。次のものならわかる。

/usr/lib/emacsen-common/packages/remove/wget-el emacs22 xemacs21
/usr/lib/emacsen-common/packages/remove/wget-el xemacs21 emacs22
- dry run。
- 今度は先にdry runしてみる。

emacsen-common $ ./emacs-package-test-remove wget-el
/usr/lib/emacsen-common/packages/remove/wget-el emacs emacs22 xemacs21
/usr/lib/emacsen-common/packages/remove/wget-el emacs22 emacs22 xemacs21
/usr/lib/emacsen-common/packages/remove/wget-el xemacs21 emacs22 xemacs21
emacsen-common $

- なんと。。。

- ソース解読。
- あ、

map {
my $script = $lib_dir . "/packages/$action/$_";
execute($script, 'emacs', @installed_flavors) if -e $script;
} @pkgs_to_handle;

なるものがいる。
- なので、add-on packagesについては
install/removeにおいて、'emacs'がかならず引
数に入ってくるのだな。
- そもそも、なんでothersをならべる必要がある
のか?
- packages/remove/wget-elをみてみる。

#!/bin/sh -e
# /usr/lib/emacsen-common/packages/remove/emacs-wget

FLAVOR=$1
PACKAGE=wget-el

if [ ${FLAVOR} != emacs ]; then
# if test -x /usr/sbin/install-info-altdir; then
# echo remove/${PACKAGE}: removing Info links for ${FLAVOR}
# install-info-altdir --quiet --remove --dirname=${FLAVOR} /usr/info/emacs-wget.info.gz
# fi

echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR}
rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE}
fi
- うむー。$1しか使っていない。


こつこつ。

2009年11月30日月曜日

maximaをいじる

手元で数式処理できるツールが欲しいなぁ、と、だいぶ前からMaximaに目をつけていた。

気分転換がてら、ちょこちょこいじってみようと思う。

参考書は、

はじめてのMaxima

にする。

ごりごり読むのではなくて、あくまでのんびり遊びながらつまみ食いする予定。

debian-emacs-policyのポイント

ざっとポイントをまとめた。
これ以上理解するには、perlを多少勉強せねばならない。
うーん。どうしたものか。


* debian-emacs-policyのポイント
- debian-emacs-policyでは、debian systemにてemacs
関連のpackagesを作成/管理するためのpolicyが規程
されている。
- 簡明にまとまっている文書なので、メモ化する必要は
ないのだが、確実な理解のためにあえてメモを作成す
る。
- policyの所在
- emacsen-common:
/usr/share/doc/emacsen-common/debian-emacs-policy.gz
- またはWeb。debian.org。

** policyにおける用語
- <emacs>

emacs
xemacs21
emacs21
emacs22

- <flavor>

xemacs21
emacs21
emacs22

- すなわち、<emacs> - 'emacs'。

** policyが求めるload-path
- load-path
- debian emacs policyでは、最低、次の場所が次の
順(優先度が高い順)にload-pathに含まれることを求めている。
これはどのemacsenにおいても、である。

/etc/<flavor>
/etc/emacs
/usr/local/share/emacs/<upstream>/site-lisp
/usr/local/share/emacs/site-lisp
/usr/share/emacs/<flavor>/site-lisp
/usr/share/emacs/site-lisp

- ここで、<upstream>は、'22.2'などのupstream
version number。

** emacsen-common package
- emacsen-commonパッケージは、debian emacs policy
に従ったemacs packagesの管理のためのfacilityを提
供している。
- 提供物一覧

~ $ apt-file list emacsen-common
emacsen-common: /etc/emacs/site-start.d/00debian-vars.el
emacsen-common: /etc/emacs/site-start.el
emacsen-common: /usr/lib/emacsen-common/emacs-install
emacsen-common: /usr/lib/emacsen-common/emacs-package-install
emacsen-common: /usr/lib/emacsen-common/emacs-package-remove
emacsen-common: /usr/lib/emacsen-common/emacs-remove
emacsen-common: /usr/lib/emacsen-common/generate-install-list
emacsen-common: /usr/lib/emacsen-common/packages/install/emacsen-common
emacsen-common: /usr/lib/emacsen-common/packages/remove/emacsen-common
emacsen-common: /usr/share/doc/emacsen-common/changelog.gz
emacsen-common: /usr/share/doc/emacsen-common/copyright
emacsen-common: /usr/share/doc/emacsen-common/debian-emacs-policy.gz
emacsen-common: /usr/share/emacs/site-lisp/debian-startup.el
~ $

- /etc/emacs/site-start.el
- これはpackageに含まれてはいるが、local admin
が中身を管理するもの。

*** 提供物の概要
**** /usr/lib/emacsen-common/emacs-install
- perl script。
- コメント。

# Call emacsen-common/packages/install/* install-flavor other-flavor ...
# for every installed emacs add-on package.

**** /usr/lib/emacsen-common/emacs-remove
- emacs-installの別名。

**** /usr/lib/emacsen-common/emacs-package-install
- perl script。
- コメント。

# Was this called because we're installing/removing a new emacs
# flavor? This should probably be handled with getopt and a command
# line opt, but it's freeze time, and I'm tired...

**** /usr/lib/emacsen-common/emacs-package-remove
- emacs-package-installの別名。

**** /usr/lib/emacsen-common/generate-install-list
- perl script。
- ライブラリ。
- コメント。

# depends on: dpkg, tsort, perl

# This script should be handed a list of add-on packages on stdin, and
# it will sort them according to their dependencies. It will also add
# in other add-on packages that aren't mentioned, but are needed.

# Test code
# my @input_packages = <STDIN>;
# my @result = generate_add_on_install_list(@input_packages);
# print " " . join("\n ", @result);

**** /usr/lib/emacsen-common/packages/install/emacsen-common
- shell script。
- 使い方。
- /usr/lib/emacsen-common/packages/remove/emacsen-common <flavor>
- emacsen-commonパッケージが提供しているelファイ
ルについて、flavor用バイトコンパイルを実施する。
**** /usr/lib/emacsen-common/packages/remove/emacsen-common
- shell script。
- 使い方。
- /usr/lib/emacsen-common/packages/remove/emacsen-common <flavor>
- emacsen-commonパッケージが提供しているelファイ
ルのflavor用バイトコンパイルを削除する。

** emacsen main packages
- 'emacs22'とか'xemacs21'とか、emacsen本体を提供
するパッケージのこと。
*** 必須作法
**** メンテナスクリプト
- postinst
- postinstに記述するもの。
- "/usr/lib/emacsen-common/emacs-install <flavor>"
- この記述によってemacs-installは次のものを実
行する。
- "/usr/lib/emacsen-common/packages/install/<pkg> <flavor> <others>"
- prerm
- prermに記述するもの。
- "/usr/lib/emacsen-common/emacs-remove <flavor>"
- この記述によってemacs-removeは次のものを実
行する。
- "/usr/lib/emacsen-common/packages/remove/<pkg> <flavor> <others>"
**** バイナリシムリンク
- /usr/bin/<package-name>から
/usr/bin/<emacs-binary>へのsymbolic linkが必須。
- というのはadd-on packagesがinstall/removeすると
き、/usr/bin/$ARGV[0]を使ってバイトコンパイルな
どをするから。
**** 依存関係 "Provides: emacsen"
- emacsen main packageは、debian packageとして
controlファイルにおいて、"Provides:"に
"emacsen"と記述する。
- 例:emacs22

Provides: editor, emacsen, info-browser, mail-reader, news-reader

** add-on packages
*** 基本作法
- 必須な作法は少い。
- 何故かというと、もしadd-onしようとしている機能に
ついて、バイトコンパイルが不要であったり、インストー
ル時の調整(emacs毎)も不要であったりするならば、
単にそのadd-on packageは、しかるべき場所にしか
るべきファイルを置くだけで目的をはたせるからだ。
- ここでは必須なものを含めて基本的なものを書く。
**** 設置場所
- add-on packagesが設置できる場所はつぎのとおり。
/etc/<emacs>/site-start.d
/usr/share/<emacs>/site-lisp/<package-name>
- これは必須作法である。
**** load-path
- add-on packagesはload-pathをいじってはいけない。
- これは必須作法である。
**** 依存関係
- 'emacsen-common'をdependsするべきではない。その
かわりに、'emacsen'をdependsするか、対応している
flavorsをdependsする。
**** 初期化ファイルの中身
- site-start.dなどにおく初期化ファイルでは、load
ではなくautoloadを使うべし。

*** 応用作法
- 「バイトコンパイルが必要」など、flavorに依存した
install/remove処理が必要なものの作法。

**** メンテナスクリプト
- まず、次のファイルをパッケージにて提供すべし。

/usr/lib/emacsen-common/packages/install/<pkg>
/usr/lib/emacsen-common/packages/remove/<pkg>

- サンプル
- 仮想的なfooというパッケージに対するもの。
- flavor毎にバイトコンパイルをするもの。
- install

#!/bin/sh
# /usr/lib/emacsen-common/packages/install/foo
# [ This particular script hasn't been tested, so be careful. ]
set -e

FLAVOR=$1
echo install/foo: Handling install of emacsen flavor ${FLAVOR}

byte_compile_options="-batch -f batch-byte-compile"
el_files="some-file.el some-other-file.el etc.el"
el_dir=/usr/share/emacs/site-lisp/foo/
elc_dir=/usr/share/${FLAVOR}/site-lisp/foo/

if [ ${FLAVOR} != emacs ]
then
echo install/foo: byte-compiling for ${FLAVOR}

[ -d ${elc_dir} ] || mkdir ${elc_dir}

# Copy the temp .el files
(cd ${el_dir} && cp ${el_files} ${elc_dir})

# Byte compile them
(cd ${elc_dir} \
&& ${FLAVOR} ${byte_compile_options} ${el_files} 2> /dev/null)

# remove the redundant .el files
# presumes that any .el files in the <flavor> dir are trash.
rm ${elc_dir}/*.el
fi
exit 0;

- remove

#!/bin/sh
# /usr/lib/emacsen-common/packages/remove/foo
# [ This particular script hasn't been tested either, so be careful. ]
set -e

FLAVOR=$1
elc_dir=/usr/share/${FLAVOR}/site-lisp/foo

echo remove/foo: Handling removal of emacsen flavor ${FLAVOR}

if [ ${FLAVOR} != emacs ]
then
echo emacsen-common: purging byte-compiled files for ${FLAVOR}
rm -f ${elc_dir}/*.elc
[ -d ${elc_dir} -a `ls -la ${elc_dir}|wc -l` -gt 3 ] && rmdir ${elc_dir}
fi
exit 0;

- その上で、メンテナスクリプトは次のようにすべき。
- postinst
- /usr/lib/emacsen-common/emacs-package-install <pkg>
- prerm
- /usr/lib/emacsen-common/emacs-package-remove <pkg>


こつこつ。

2009年11月29日日曜日

Ubuntu/GNU Emacs22の起動プロセスにおけるload-pathの構築と利用

ソースを探ったり、いろいろビルドしたりして、結構わかった。
完全とまではいきませんが、現状では十分。

load-pathに限らず、Emacsの起動プロセスをいろいろ調べた。結果として、Emacsの仕組みについて多少詳しくなったのも嬉しい。


* Ubuntu/GNU Emacs22の起動プロセスにおけるload-pathの構築と利用
** src/epaths.in
- epaths.hの素。
- この中のPATH_LOADSEARCHがload-pathの素。
- GNU配布状態:
#define PATH_LOADSEARCH "/usr/local/lib/emacs/lisp"

** src/epaths.h
- makeによって、epaths.inを素に生成される。
- GNU配布状態: [./configure, makeの結果]
#define PATH_LOADSEARCH "/usr/local/share/emacs/22.2/site-lisp:/usr/local/share/emacs/site-lisp:/usr/local/share/emacs/22.2/lisp:/usr/local/share/emacs/22.2/leim"
- Ubuntu: [debuildの結果]
#define PATH_LOADSEARCH "/etc/emacs22:/etc/emacs:/usr/local/share/emacs/22.2/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/22.2/site-lisp:/usr/share/emacs/site-lisp:/usr/share/emacs/22.2/leim:/usr/share/emacs/22.2/lisp:/usr/share/emacs/22.2/leim"

** src/emacs.c
- main
- init_lread関数[lread.c]の実行
- normal = PATH_LOADSEARCH;
- Vload_path = decode_env_path ("EMACSLOADPATH", normal);
- dcode_env_pathはパス達のリストを返す。
- Vtop_level変数の構成
- 起動時に評価するlispを格納。
- 値の形式は(load . (hoge))。すなわちhogeをロー
ドする。
- hogeは、
- emacsオプションで'-l'指定があればそのelファ
イル。
- そうでなければ、'loadup.el'。
- GNU:
(load "loadup.el")
- Frecursive_edit関数の実行
- editor command loop に入る。
- recursive_edit_1 [keyboard.c]
- command_loop [keyboard.c]
- internal_catch (Qtop_level, top_level_1, Qnil)
- top_level_1 [keyboad.c]
- internal_condition_case (top_level_2, Qerror, cmd_error) [eval.c]
- top_level_2 [keyboard.c]
- Feval (Vtop_level) [eval.c]
- ここにいたり、先にVtop_level
で設定した(load "loadup.el")が
評価される。

** lisp/loadup.el
- このelispに入った時点では、emacs.cのVload_path
がload-pathの値である。

*** load-path変数の調整
- bootstrappingするときだけ、load-pathを次のように
加工する。通常はこの加工はしない。

(let ((dir (car load-path)))
;; We'll probably overflow the pure space.
(setq purify-flag nil)
(setq load-path (list dir
(expand-file-name "emacs-lisp" dir)
(expand-file-name "language" dir)
(expand-file-name "international" dir)
(expand-file-name "textmodes" dir))))

*** (load "startup") [lisp/startup.el]
- (defcustom site-run-file "site-start" ...)
- (defconst debian-emacs-flavor 'emacs22 ...)
[*debian patch*]
- (setq top-level '(normal-top-level))
- normal-top-level関数を定義
- この関数の中で次の処理が定義されている。
- load-pathの拡張
- load-pathに含まれるパスを順次処理する。
- 処理は、そのパスに、subdirs.elや
leim-list.elがあれば、それらをloadするとい
うもの。
- subdirs.elの中身の典型は、次の2つ。
- normal-top-level-add-subdirs-\
to-load-path を引数無しで呼ぶ。これは
カレントディレクトリ配下のサブディレク
トリを全てload-pathに追加する。
- normal-top-level-add-subdirs-\
to-load-path を引数有で呼ぶ。これはカ
レントディレクトリ配下のサブディレクト
リについて、引数で指定されたものを
load-pathに追加する。
- leim-list.elの中身の典型は、次のよう。
- register-input-method関数の実行を列記。
- loadやautoloadをここに記述することも
ある。
- command-line関数を実行する。
- emacs-starup-hookを呼ぶ。
- term-startup-hookを呼ぶ。
- command-line関数を定義
- この関数の中で次の処理が定義されている。
- (load site-run-file t t)
- (load "debian-startup" t t nil) [*debian patch*]
- "debian-startup.el"の中身は関数群定義のみ。
- 定義される関数の一覧。
- debian-pkg-add-load-path-item
- debian-unique-strings
- debian-run-directories
- debian-startup
- (debian-startup debian-emacs-flavor)の実行 [*debian patch*]
- (let ((common-dir "/etc/emacs/site-start.d")
(flavor-dir (concat "/etc/" (symbol-name flavor) "/site-start.d")))
(debian-run-directories flavor-dir common-dir))))
- (debian-run-directories flavor-dir common-dir)
- flavor-dirやcommon-dirに入っているすべての
ディレクトリについて、NNfilename.elや
NNfilename.elcの形のファイル名のものを探索
収集し、アルファベティカルにソートして、重
複を削除したリストを作成する。このリス
トをbase-namesと呼ぶ。
- load-pathに、flavor-dirとcommon-dirを追
加する。
- base-namesの中身(file)をひとつずつloadす
る。
- 前項のloadをした結果として、load-pathが
さらに拡張されているかもしれない。そこで、
load-pathをflavor-dirとcommon-dirを追加
しただけの状態に戻す。
- (load site-run-file t t nil) [*debian patch*]

- user-init-file-1の調整
- user-init-file-1は、いわゆる'~/.emacs'となる。
- (load user-init-file-1 t t)
- otherfileの調整
- いわゆる'~/.emacs.d'の中の'init'というファイ
ル。
- (load otherfile t t)
- (load "default" t t)
- inihibit-default-initがnilのときは読み込まな
い。

*** (load "site-load" t)
- 'site-load.el'がload-pathのどこかに存在すれば
それをloadする。

- これはtemacsからemacsをdumpするときに含めたい
elispを指定するために存在している。計算機の速度
が向上しているため最近は使わない。
- そもそもの目的はdumpに含めることなのだが、起動時
に読み込ませるelispの設置場所としても使えると言
えば使える。
- ホストのelisp共通設定は、ここではなく
て'default.el'に書くべきである。そちらはユーザ
によって読み込みの制御ができるから。

*** (load "site-init" t)
- 'site-init.el'がload-pathのどこかに存在すれば
それをloadする。

- これもsite-loadと同種の目的のもの。タイミングが
違う。site-initに書いたものはdumped imageにdoc
stringsが読み込まれる。こちらに書いたものは
dumped imageには読み込まれない。
- そもそもの目的はdumpに含めることなのだが、起動時
に読み込ませるelispの設置場所としても使えると言
えば使える。
- ホストのelisp共通設定は、ここではなく
て'default.el'に書くべきである。そちらはユーザ
によって読み込みの制御ができるから。

- site-init.el [*debian patch*]
- flavor-specificなinfo dirの追加処理が記述され
ている。

*** (eval top-level)
- (eval top-level)
- normal-top-level関数[startup.el]の実行


こつこつ。

2009年11月26日木曜日

【入門DP】4.1 パッケージ作成のための基礎知識

教本で知識を確認しつつ、その知識をつかって、emacs22のパッケージをいじってみる。その様子を掲載する。


** 4 パッケージの作成と配布
*** 4.1 パッケージ作成のための基礎知識
- Ubuntu Interpidのemacs22をサンプルとしよう。

**** apt-cache showsrc emacs22
- これによって、ダウンロード前に情報をみれる。

$ apt-cache showsrc emacs22
Package: emacs22
Binary: emacs22-common, emacs22-bin-common, emacs22, emacs22-gtk, emacs22-nox, emacs22-el, emacs
Version: 22.2-0ubuntu2
Priority: optional
Section: editors
Maintainer: Ubuntu Core Developers
Original-Maintainer: Michael W. Olson (GNU address)
Build-Depends: libncurses5-dev, texinfo, liblockfile-dev, libgif-dev, libtiff-dev, xaw3dg-dev, libpng12-dev, libjpeg62-dev, libgtk2.0-dev, dpkg-dev (>> 1.10.0), quilt (>= 0.42), lsb-release, debhelper (>= 5.0.0), libxaw7-dev, libasound2-dev [!hurd-i386 !kfreebsd-i386 !kfreebsd-amd64]
Architecture: any
Standards-Version: 3.7.2
Format: 1.0
Directory: pool/main/e/emacs22
Files:
04ef020a8f1f98965a6b1f881800414b 1461 emacs22_22.2-0ubuntu2.dsc
d6ee586b8752351334ebf072904c4d51 38694318 emacs22_22.2.orig.tar.gz
2d264719b7d1aea7e446e73c5e68c654 26934 emacs22_22.2-0ubuntu2.diff.gz
Vcs-Bzr: http://code.launchpad.net/%7Eubuntu-elisp/emacs/ubuntu/
Checksums-Sha1:
7a371332cb8400d44e8eb31f19e432bbc93523a0 38694318 emacs22_22.2.orig.tar.gz
b6e448aa81a7bf5a04a754f3b7ed39e4923079ef 26934 emacs22_22.2-0ubuntu2.diff.gz
Checksums-Sha256:
216839e1fb38ca4f2ed0a07689fb47ee80d90845f34e0a56fe781d6aa462e367 38694318 emacs22_22.2.orig.tar.gz
5dcfd2a13f68972124174271d5c7f532954500f107c972df94364ef560f2b1a6 26934 emacs22_22.2-0ubuntu2.diff.gz

$

**** ソースのダウンロード
- '-d'をつけなければ、自動的にorig.tar.gzと
diff.gzが展開される。ここではあえて展開させない。

$ apt-get -d source emacs22
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
NOTICE: 'emacs22' packaging is maintained in the 'Bzr' version control system at:
http://code.launchpad.net/%7Eubuntu-elisp/emacs/ubuntu/
Please use:
bzr get http://code.launchpad.net/%7Eubuntu-elisp/emacs/ubuntu/
to retrieve the latest (possible unreleased) updates to the package.
38.7MB のソースアーカイブを取得する必要があります。
取得:1 http://jp.archive.ubuntu.com intrepid/main emacs22 22.2-0ubuntu2 (dsc) [1461B]
取得:2 http://jp.archive.ubuntu.com intrepid/main emacs22 22.2-0ubuntu2 (tar) [38.7MB]
取得:3 http://jp.archive.ubuntu.com intrepid/main emacs22 22.2-0ubuntu2 (diff) [26.9kB]
38.7MB を 7s で取得しました (5385kB/s)
ダウンロードオンリーモードでパッケージのダウンロードが完了しました
$ ls
emacs22_22.2-0ubuntu2.diff.gz emacs22_22.2-0ubuntu2.dsc emacs22_22.2.orig.tar.gz
$

- dscファイルの確認。

$ cat emacs22_22.2-0ubuntu2.dsc
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Format: 1.0
Source: emacs22
Binary: emacs22-common, emacs22-bin-common, emacs22, emacs22-gtk, emacs22-nox, emacs22-el, emacs
Architecture: any
Version: 22.2-0ubuntu2
Maintainer: Ubuntu Core Developers
Standards-Version: 3.7.2
Vcs-Bzr: http://code.launchpad.net/%7Eubuntu-elisp/emacs/ubuntu/
Build-Depends: libncurses5-dev, texinfo, liblockfile-dev, libgif-dev, libtiff-dev, xaw3dg-dev, libpng12-dev, libjpeg62-dev, libgtk2.0-dev, dpkg-dev (>> 1.10.0), quilt (>= 0.42), lsb-release, debhelper (>= 5.0.0), libxaw7-dev, libasound2-dev [!hurd-i386 !kfreebsd-i386 !kfreebsd-amd64]
Checksums-Sha1:
7a371332cb8400d44e8eb31f19e432bbc93523a0 38694318 emacs22_22.2.orig.tar.gz
b6e448aa81a7bf5a04a754f3b7ed39e4923079ef 26934 emacs22_22.2-0ubuntu2.diff.gz
Checksums-Sha256:
216839e1fb38ca4f2ed0a07689fb47ee80d90845f34e0a56fe781d6aa462e367 38694318 emacs22_22.2.orig.tar.gz
5dcfd2a13f68972124174271d5c7f532954500f107c972df94364ef560f2b1a6 26934 emacs22_22.2-0ubuntu2.diff.gz
Files:
d6ee586b8752351334ebf072904c4d51 38694318 emacs22_22.2.orig.tar.gz
2d264719b7d1aea7e446e73c5e68c654 26934 emacs22_22.2-0ubuntu2.diff.gz
Original-Maintainer: Michael W. Olson (GNU address)

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFIv/MoW0JvuRdL8BoRAt5eAJ9vlp/tLgrk4OKHKlqexuK5E2SKcQCdHPuB
5DPs97j8ZSQHIjDU74XphAM=
=MKWY
-----END PGP SIGNATURE-----
$

**** orig.tar.gzの中身を確認
- emacs-22.2として展開される。これはGNU配布版その
ままなのだろう。

$ tar zxf emacs22_22.2.orig.tar.gz
$ ls
emacs-22.2 emacs22_22.2-0ubuntu2.diff.gz emacs22_22.2-0ubuntu2.dsc emacs22_22.2.orig.tar.gz
$ cd emacs-22.2
$ ls
AUTHORS INSTALL configure lib-src mac nt vpath.sed
BUGS Makefile.in configure.in lisp make-dist oldXMenu
CONTRIBUTE README etc lispintro man site-lisp
COPYING config.bat info lispref mkinstalldirs src
ChangeLog config.guess install-sh lwlib move-if-change update-subdirs
FTP config.sub leim m4 msdos vms
$

**** diffファイルの中身を確認
- すべて新規ファイルの作成である。
- すべて、debian/配下のファイルである。

--- emacs22-22.2.orig/debian/emacs-bin-common.overrides.linda
+++ emacs22-22.2/debian/emacs-bin-common.overrides.linda
@@ -0,0 +1,2 @@
+Tag: no-manual-for-binary
+Data: (e|c)(tags|macsclient).emacs22
--- emacs22-22.2.orig/debian/emacsclient.desktop
+++ emacs22-22.2/debian/emacsclient.desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Version=1.0
[snip]

**** dpkg-sourceで展開
- ソースパッケージの展開にはdpkg-sourceを使う。
- これまでの作業で展開したものなどは念のため削除
しておく。

$ ls
emacs22_22.2-0ubuntu2.diff.gz emacs22_22.2-0ubuntu2.dsc emacs22_22.2.orig.tar.gz
$ dpkg-source -x emacs22_22.2-0ubuntu2.dsc
gpg: 2008年09月04日 23時39分36秒 JSTにDSA鍵ID 174BF01Aで施された署名
gpg: 署名を検査できません: 公開鍵が見つかりません
dpkg-source: extracting emacs22 in emacs22-22.2
dpkg-source: info: unpacking emacs22_22.2.orig.tar.gz
dpkg-source: info: applying emacs22_22.2-0ubuntu2.diff.gz
$ ls
emacs22-22.2 emacs22_22.2-0ubuntu2.dsc
emacs22_22.2-0ubuntu2.diff.gz emacs22_22.2.orig.tar.gz
$ cd emacs22-22.2/
$ ls
AUTHORS INSTALL configure leim m4 msdos vms
BUGS Makefile.in configure.in lib-src mac nt vpath.sed
CONTRIBUTE README debian lisp make-dist oldXMenu
COPYING config.bat etc lispintro man site-lisp
ChangeLog config.guess info lispref mkinstalldirs src
FTP config.sub install-sh lwlib move-if-change update-subdirs
$

- 確かに、debian/が増えているだけである。
- orig.tar.gzを展開したときはemacs-22.2だったが、
dpkg-sourceした後は、emacs22-22.2になっている。
これは、orig.tar.gzのファイル名はdebianポリシー
にあわせるが、中身は上流のままというのがポリシー
なのだろう。それを展開時に修正しているのが、

dpkg-source: extracting emacs22 in emacs22-22.2

なのだろう。

$ tar -tvf emacs22_22.2.orig.tar.gz
drwxrwxrwx cyd/cyd 0 2008-03-26 22:55 emacs-22.2/
-rw-r--r-- cyd/cyd 92389 2008-03-26 13:55 emacs-22.2/AUTHORS
-rw-r--r-- cyd/cyd 210 2007-04-15 10:34 emacs-22.2/FTP
[snip]

**** debian/の中身
***** debian/control
- このソースツリーから生成するすべてのパッケージ
のメタデータが記載されている。

Source: emacs22
Section: editors
Priority: optional
Maintainer: Ubuntu Core Developers
XSBC-Original-Maintainer: Michael W. Olson (GNU address)
Build-Depends: libncurses5-dev, texinfo, liblockfile-dev, libgif-dev, libtiff-dev, xaw3dg-dev, libpng12-dev, libjpeg62-dev, libgtk2.0-dev, dpkg-dev (>> 1.10.0), quilt (>= 0.42), lsb-release, debhelper (>= 5.0.0), libxaw7-dev, libasound2-dev [!hurd-i386 !kfreebsd-i386 !kfreebsd-amd64]
Standards-Version: 3.7.2
XS-Vcs-Bzr: http://code.launchpad.net/%7Eubuntu-elisp/emacs/ubuntu/

Package: emacs22-common
Section: editors
Priority: optional
Architecture: all
Depends: emacsen-common (>= 1.4.10), dpkg (>= 1.9.0)
Suggests: emacs22-el
Conflicts: emacs22-el (<< ${source:Version}), w3-el, dictionaries-common (<< 0.60.0), emacs22-common-non-dfsg
Replaces: emacs22-common-non-dfsg
Description: The GNU Emacs editor's common infrastructure
GNU Emacs is the extensible self-documenting text editor.
This package contains the infrastructure common between emacs22,
emacs22-gtk, and emacs22-nox.

Package: emacs22-bin-common
Section: editors
Priority: optional
Architecture: any
Depends: emacs22-common (= ${source:Version}), ${shlibs:Depends}
Description: The GNU Emacs editor's shared, architecture dependent files
GNU Emacs is the extensible self-documenting text editor.
This package contains the architecture dependent infrastructure
that's shared by emacs22, emacs22-gtk, and emacs22-nox.
[snip]

***** debian/rules
- パッケージの構築を実施するプログラム。
- rulesファイルの一行目は

#!/usr/bin/make -f

でなければならない。
- またインターフェイスとして次のターゲットは必須
である。

build [必須]
binary [必須]
binary-arch [必須]
binary-indep [必須]
clean
build-arch
build-indep
get-orig-source

- 中身を多少追ってみる。本格的に追うのはdebhelper
の節にて。

- binaryターゲットは?

binary: binary-indep binary-arch

- この前提条件達は?

binary-indep: build install
dh_testdir -i
dh_testroot -i
dh_installchangelogs -i
dh_installdocs -i
dh_link -i
dh_strip -i
dh_compress -XREADME.Debian -i
dh_fixperms -i
dh_installdeb -i
# don't install maintainer scripts in the metapackage
rm -vf debian/emacs/DEBIAN/{post,pre}{inst,rm}
dh_shlibdeps -i
dh_gencontrol -i
dh_md5sums -i
dh_builddeb -i

binary-arch: build install
dh_testdir -a
dh_testroot -a
dh_installinfo -a
dh_installman -a
dh_installchangelogs -a
dh_installdocs -a
dh_installexamples -a
dh_installmenu -a
dh_desktop -a
dh_link -a
dh_strip -a
dh_compress -a
dh_fixperms -a -X${movemail_bin}
dh_installdeb -a
dh_shlibdeps -a
dh_gencontrol -a
dh_md5sums -a
dh_builddeb -a

- buildターゲットは?

build: patch build-stamp

- patchターゲットは?

これはrulesには存在しない。おそらく、

include /usr/share/quilt/quilt.make

こちらの中にあるのでは。

- build-stampターゲットは?

build-stamp: bootstrap emacs emacs-gtk emacs-nox
dh_testdir
touch $@

- bootstrapターゲットは?

bootstrap: bootstrap-stamp
bootstrap-stamp:
dh_testdir

# Emacs must be bootstrapped at least once in order to
# generate .elc files
CFLAGS="${CFLAGS}" ./configure ${emacs_confflags}
${build_cmd} bootstrap
${build_cmd} distclean

touch $@

- ここでAutotoolsのbootstrapをやってるのだな。
- emacsターゲットは?

emacs: bootstrap emacs-stamp
emacs-stamp:
dh_testdir

$(call emacs_build,${emacs_confflags},x-emacs)
${build_cmd} distclean

touch $@

- emacs_buildマクロとは?

define emacs_build
# Build emacs
CFLAGS="${CFLAGS}" ./configure ${1}
${build_cmd}
# Check for pure space overflow
${check_pure_space}
# save binary from deletion
mv src/emacs src/${2}
endef

- ここで./configureをやる。

- build_cmdマクロとは?

define build_cmd
${MAKE} CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}"
endef

- お、ここでmakeを呼ぶのか。

- パッチあてて、./configureとmakeの流れがどう記述
さているかは理解できた。


**** ビルド環境確認
- 環境はそろっているようだ。

$ sudo apt-get -s build-dep emacs22
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。
$

スタックをかなり積んだ成果が出ている。rulesの中身など、いろいろよく分かる。

こつこつ。

GNUからDebianへ

調べてみたところ、GNU Autoconf/Automake/Libtoolは、第11章以降について、サンプルの現代化がなされていないようだ。自分で現代化していく、というのも勉強になりそうだが、Autotoolsの知識を得ることはスタックの途中にすぎないので、ちょっと重い。

そこで、この本は第10章(US第11章)でいったん区切りとする。先々、今度は主たる目的としてAutotoolsを攻めてみたい。

さて、スタックがひとつ巻き戻った。

次は、Debian Systemのパッケージ構造の基礎を再確認しよう。

教本は、

[入門] Debian パッケージ

にする。昔一度読んだことがあるので、パッケージ構造に関わる部分だけ復習する。

こつこつ。

【autotools】10(US 11) GNU Libtoolとconfigure.inおよびMakefile.am

Autotoolsの新刊がもうすぐ発売される。

AUTOTOOLS

GNU Autoconf/Automake/Libtoolは、少々古いので、それを補えるかもしれない。楽しみです。


** 10(US 11) GNU Libtoolとconfigure.inおよびMakefile.am
- この章は、Automake&Autoconfと一緒にlibtoolを使
う方法の説明。
- 一通り読み、ソースを追い、サンプルを試してみた。
- しかし、何かを「わかった」という実感はない。
- こういう内容は結局実戦で使うときに、理解が発生
するのだろう。
- 今は、触れておくことによってその準備をしている
と考えておく。
- なお、サンプルは、私の環境では、なぜか
Segmentation fault。。。

convenience-2.0 $ ./convenience
cos (0) => 1
Segmentation fault
convenience-2.0 $


こつこつ。

2009年11月25日水曜日

【autotools】9(US 10) GNU Libtool入門 (2)


** 9(US 10) GNU Libtool入門
- Libtoolを単に利用するだけではなく、中身も探って
みよう、シェルプロンプトからLibtoolを使ってみよ
う、というお話。
- ここで、ざっと読んでみるが困難を感じる。
- GNU Libtool Infoをちらっと見てみる。やはり困難
を感じる。
- Linkers & Loadersで勉強。完了。
- GNU Libtool Infoもざっとやる。問題なし。
- そしてこの章に戻ってきた。
- 内容はわかるが、libtoolの利用例が古いlibtoolの
もので、そのままでは動かない。。。
- 最新のlibtoolでの操作に置き換えてみる。

- 訳書P93
- ltconfigによるホスト用libtoolの生成方法が記載
されている。しかし、現状のlibtoolではltconfig
は廃止されており、configureにてホスト用
libtoolを生成するようである。
- Ubuntuでは、Ubuntu用のlibtoolが生成済みで配布
されており、この作業にはソースパッケージが必
要である。
- ここでは割愛する。
- 訳書P97
- ここはlibtoolのI/Fが変更になっているところ。
- 現在は次のとおり。

9-US10 $ libtool --mode=compile gcc -c hello.c
libtool: compile: gcc -c hello.c -fPIC -DPIC -o .libs/hello.o
libtool: compile: gcc -c hello.c -o hello.o >/dev/null 2>&1
9-US10 $ ls
hello.c hello.lo hello.o
9-US10 $ libtool --mode=link gcc -rpath /usr/local/lib -o libhello.la hello.lo
libtool: link: gcc -shared .libs/hello.o -Wl,-soname -Wl,libhello.so.0 -o .libs/libhello.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libhello.so.0" && ln -s "libhello.so.0.0.0" "libhello.so.0")
libtool: link: (cd ".libs" && rm -f "libhello.so" && ln -s "libhello.so.0.0.0" "libhello.so")
libtool: link: ar cru .libs/libhello.a hello.o
libtool: link: ranlib .libs/libhello.a
libtool: link: ( cd ".libs" && rm -f "libhello.la" && ln -s "../libhello.la" "libhello.la" )
9-US10 $ ls
hello.c hello.lo hello.o libhello.la
9-US10 $

- 次の部分は、本文にコンパイルとあるのでmodeを
compileにしているが、rpathスイッチがあるとい
うことはリンクすべきではないか。

9-US10 $ libtool --mode=compile c89 -rpath /usr/local/lib -c hello.c
libtool: compile: unable to infer tagged configuration
libtool: compile: specify a tag with `--tag'
9-US10 $

- 訳書P98

9-US10 $ libtool --mode=link gcc -static -o libhello.la hello.lo
libtool: link: ar cru .libs/libhello.a hello.o
libtool: link: ranlib .libs/libhello.a
libtool: link: ( cd ".libs" && rm -f "libhello.la" && ln -s "../libhello.la" "libhello.la" )
9-US10 $ ls
hello.c hello.lo hello.o libhello.la
9-US10 $

- 訳書P100

9-US10 $ libtool --mode=link gcc -o libtrim.la trim.lo
libtool: link: ar cru .libs/libtrim.a .libs/trim.o
libtool: link: ranlib .libs/libtrim.a
libtool: link: ( cd ".libs" && rm -f "libtrim.la" && ln -s "../libtrim.la" "libtrim.la" )
9-US10 $

- 訳書P100からP101へのまたぎ

9-US10 $ libtool --mode=link gcc -o libgraphics.la -lpng -ltiff -ljpeg -lz
libtool: link: ar cru .libs/libgraphics.a
libtool: link: ranlib .libs/libgraphics.a
libtool: link: ( cd ".libs" && rm -f "libgraphics.la" && ln -s "../libgraphics.la" "libgraphics.la" )
9-US10 $

- 訳書P101からP102へのまたぎ
- 実行可能ファイルのリンクをする前の仮定が省略
されているのでそこもやっておく。

hello $ ls
Makefile.am bootstrap configure.ac hello.c main.c
hello $ libtool --mode=compile gcc -c hello.c
libtool: compile: gcc -c hello.c -fPIC -DPIC -o .libs/hello.o
libtool: compile: gcc -c hello.c -o hello.o >/dev/null 2>&1
hello $ libtool --mode=link gcc -rpath /usr/local/lib -o libhello.la hello.lo
libtool: link: gcc -shared .libs/hello.o -Wl,-soname -Wl,libhello.so.0 -o .libs/libhello.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libhello.so.0" && ln -s "libhello.so.0.0.0" "libhello.so.0")
libtool: link: (cd ".libs" && rm -f "libhello.so" && ln -s "libhello.so.0.0.0" "libhello.so")
libtool: link: ar cru .libs/libhello.a hello.o
libtool: link: ranlib .libs/libhello.a
libtool: link: ( cd ".libs" && rm -f "libhello.la" && ln -s "../libhello.la" "libhello.la" )
hello $ ls
Makefile.am bootstrap configure.ac hello.c hello.lo hello.o libhello.la main.c
hello $ libtool --mode=link gcc -o hello main.c libhello.la
libtool: link: gcc -o .libs/hello main.c ./.libs/libhello.so
hello $ ls
Makefile.am configure.ac hello.c hello.o main.c
bootstrap hello hello.lo libhello.la
hello $ ./hello
Hello, World!
hello $

- 訳書P102

hello $ libtool --mode=link gcc -static -o hello main.c libhello.la
libtool: link: gcc -o hello main.c ./.libs/libhello.a
hello $

- 訳書P103

- この例は、loader.oが存在しないので実行できな
い。

- 訳書P105

intro-hello $ rm hello *.a *.o
intro-hello $ libtool --mode=compile gcc -c trim.c
libtool: compile: gcc -c trim.c -fPIC -DPIC -o .libs/trim.o
libtool: compile: gcc -c trim.c -o trim.o >/dev/null 2>&1
intro-hello $ libtool --mode=link gcc -rpath /usr/local/lib -o libtrim.la trim.lo
libtool: link: gcc -shared .libs/trim.o -Wl,-soname -Wl,libtrim.so.0 -o .libs/libtrim.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libtrim.so.0" && ln -s "libtrim.so.0.0.0" "libtrim.so.0")
libtool: link: (cd ".libs" && rm -f "libtrim.so" && ln -s "libtrim.so.0.0.0" "libtrim.so")
libtool: link: ar cru .libs/libtrim.a trim.o
libtool: link: ranlib .libs/libtrim.a
libtool: link: ( cd ".libs" && rm -f "libtrim.la" && ln -s "../libtrim.la" "libtrim.la" )
intro-hello $

- 訳書P106

intro-hello $ libtool --mode=compile gcc -c hello.c
libtool: compile: gcc -c hello.c -fPIC -DPIC -o .libs/hello.o
libtool: compile: gcc -c hello.c -o hello.o >/dev/null 2>&1
intro-hello $ libtool --mode=link gcc -rpath /usr/local/lib -o libhello.la hello.lo libtrim.la
libtool: link: gcc -shared .libs/hello.o -Wl,-rpath -Wl,/home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/.libs ./.libs/libtrim.so -Wl,-soname -Wl,libhello.so.0 -o .libs/libhello.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libhello.so.0" && ln -s "libhello.so.0.0.0" "libhello.so.0")
libtool: link: (cd ".libs" && rm -f "libhello.so" && ln -s "libhello.so.0.0.0" "libhello.so")
libtool: link: ar cru .libs/libhello.a hello.o
libtool: link: ranlib .libs/libhello.a
libtool: link: ( cd ".libs" && rm -f "libhello.la" && ln -s "../libhello.la" "libhello.la" )
intro-hello $ ls
hello.c hello.lo hello.o libhello.la libtrim.la main.c trim.c trim.lo trim.o
intro-hello $ libtool --mode=link gcc -o hello main.c libhello.la
libtool: link: gcc -o .libs/hello main.c ./.libs/libhello.so /home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/.libs/libtrim.so
main.c: In function 'main':
main.c:7: warning: incompatible implicit declaration of built-in function 'exit'
intro-hello $ ./hello
Hello, World!
intro-hello $

- 訳書P107

intro-hello $ libtool --mode=link gcc -o hello-again -static main.c libhello.la
libtool: link: gcc -o hello-again main.c ./.libs/libhello.a /home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/.libs/libtrim.a
main.c: In function 'main':
main.c:7: warning: incompatible implicit declaration of built-in function 'exit'
intro-hello $ ls
hello hello.c hello.o libtrim.la trim.c trim.o
hello-again hello.lo libhello.la main.c trim.lo
intro-hello $ ./hello-again
Hello, World!
intro-hello $

intro-hello $ libtool --mode=link gcc -o hello main.c libhello.la
libtool: link: gcc -o .libs/hello main.c ./.libs/libhello.so -Wl,-rpath -Wl,/home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/_inst
main.c: In function 'main':
main.c:7: warning: incompatible implicit declaration of built-in function 'exit'
intro-hello $ ./hello
Hello, World!
intro-hello $

- 訳書P109

intro-hello $ libtool --mode=execute gdb hello
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu"...
(gdb) run
Starting program: /home/aka/local/work/scratch/autotools/9-US10/hello-2.0/intro-hello/.libs/lt-hello
Hello, World!

Program exited normally.
(gdb)

- 訳書P109
- libhelloはインストールできるが、libtrimはイン
ストールできない。なぜか。

intro-hello $ sudo libtool --mode=install cp libtrim.la /usr/local/lib/
libtool: install: cp .libs/libtrim.lai /usr/local/lib/libtrim.la
cp: cannot stat `.libs/libtrim.lai': No such file or directory
intro-hello $ sudo libtool --mode=install install -c libtrim.la /usr/local/lib/
libtool: install: install -c .libs/libtrim.lai /usr/local/lib/libtrim.la
install: cannot stat `.libs/libtrim.lai': No such file or directory
intro-hello $ sudo libtool --mode=install install -c libhello.la /usr/local/lib/
libtool: install: install -c .libs/libhello.so.0.0.0 /usr/local/lib/libhello.so.0.0.0
libtool: install: (cd /usr/local/lib && { ln -s -f libhello.so.0.0.0 libhello.so.0 || { rm -f libhello.so.0 && ln -s libhello.so.0.0.0 libhello.so.0; }; })
libtool: install: (cd /usr/local/lib && { ln -s -f libhello.so.0.0.0 libhello.so || { rm -f libhello.so && ln -s libhello.so.0.0.0 libhello.so; }; })
libtool: install: install -c .libs/libhello.lai /usr/local/lib/libhello.la
libtool: install: install -c .libs/libhello.a /usr/local/lib/libhello.a
libtool: install: chmod 644 /usr/local/lib/libhello.a
libtool: install: ranlib /usr/local/lib/libhello.a
libtool: install: warning: remember to run `libtool --finish /home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/_inst'
intro-hello $

- そうか。コンビニエンスライブラリの流れ
で、-rpathを一度/usr/local/libから外している
から駄目なんだろうな。通常のライブラリでやり
直していれてみる。

intro-hello $ rm hello hello-again *.la *.lo *.o
intro-hello $ ls
hello.c main.c trim.c
intro-hello $ libtool --mode=compile gcc -c trim.c
libtool: compile: gcc -c trim.c -fPIC -DPIC -o .libs/trim.o
libtool: compile: gcc -c trim.c -o trim.o >/dev/null 2>&1
intro-hello $ libtool --mode=link gcc -rpath /usr/local/lib -o libtrim.la trim.lo
libtool: link: rm -fr .libs/libtrim.a .libs/libtrim.la
libtool: link: gcc -shared .libs/trim.o -Wl,-soname -Wl,libtrim.so.0 -o .libs/libtrim.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libtrim.so.0" && ln -s "libtrim.so.0.0.0" "libtrim.so.0")
libtool: link: (cd ".libs" && rm -f "libtrim.so" && ln -s "libtrim.so.0.0.0" "libtrim.so")
libtool: link: ar cru .libs/libtrim.a trim.o
libtool: link: ranlib .libs/libtrim.a
libtool: link: ( cd ".libs" && rm -f "libtrim.la" && ln -s "../libtrim.la" "libtrim.la" )
intro-hello $ libtool --mode=compile gcc -c hello.c
libtool: compile: gcc -c hello.c -fPIC -DPIC -o .libs/hello.o
libtool: compile: gcc -c hello.c -o hello.o >/dev/null 2>&1
intro-hello $ libtool --mode=link gcc -rpath /usr/local/lib -o libhello.la hello.lo libtrim.la
libtool: link: rm -fr .libs/libhello.a .libs/libhello.la .libs/libhello.lai .libs/libhello.so .libs/libhello.so.0 .libs/libhello.so.0.0.0
libtool: link: gcc -shared .libs/hello.o -Wl,-rpath -Wl,/home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/.libs ./.libs/libtrim.so -Wl,-soname -Wl,libhello.so.0 -o .libs/libhello.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libhello.so.0" && ln -s "libhello.so.0.0.0" "libhello.so.0")
libtool: link: (cd ".libs" && rm -f "libhello.so" && ln -s "libhello.so.0.0.0" "libhello.so")
libtool: link: ar cru .libs/libhello.a hello.o
libtool: link: ranlib .libs/libhello.a
libtool: link: ( cd ".libs" && rm -f "libhello.la" && ln -s "../libhello.la" "libhello.la" )
intro-hello $ libtool --mode=link gcc -o hello main.c libhello.la
libtool: link: gcc -o .libs/hello main.c ./.libs/libhello.so /home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/.libs/libtrim.so
main.c: In function 'main':
main.c:7: warning: incompatible implicit declaration of built-in function 'exit'
intro-hello $ ./hello
Hello, World!
intro-hello $ ls .libs
hello libhello.la libhello.so.0 libtrim.la libtrim.so.0 trim.o
hello.o libhello.lai libhello.so.0.0.0 libtrim.lai libtrim.so.0.0.0
libhello.a libhello.so libtrim.a libtrim.so lt-hello
intro-hello $ sudo libtool --mode=install cp libtrim.la /usr/local/lib
[sudo] password for aka:
libtool: install: cp .libs/libtrim.so.0.0.0 /usr/local/lib/libtrim.so.0.0.0
libtool: install: (cd /usr/local/lib && { ln -s -f libtrim.so.0.0.0 libtrim.so.0 || { rm -f libtrim.so.0 && ln -s libtrim.so.0.0.0 libtrim.so.0; }; })
libtool: install: (cd /usr/local/lib && { ln -s -f libtrim.so.0.0.0 libtrim.so || { rm -f libtrim.so && ln -s libtrim.so.0.0.0 libtrim.so; }; })
libtool: install: cp .libs/libtrim.lai /usr/local/lib/libtrim.la
libtool: install: cp .libs/libtrim.a /usr/local/lib/libtrim.a
libtool: install: chmod 644 /usr/local/lib/libtrim.a
libtool: install: ranlib /usr/local/lib/libtrim.a
libtool: finish: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/sbin" ldconfig -n /usr/local/lib
----------------------------------------------------------------------
Libraries have been installed in:
/usr/local/lib

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the `LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the `LD_RUN_PATH' environment variable
during linking
- use the `-Wl,-rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
intro-hello $

- 入った。

- 訳書P109

intro-hello $ sudo libtool --mode=execute cp hello /usr/local/bin
intro-hello $

- helloではなく、lt-helloとして入る。
- ライブラリパスをみるとローカルになっている。
なんでだろう。

intro-hello $ readelf -d /usr/local/bin/lt-hello

Dynamic section at offset 0xe10 contains 24 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libhello.so.0]
0x0000000000000001 (NEEDED) Shared library: [libtrim.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [/home/aka/scratch/autotools/9-US10/hello-2.0/intro-hello/.libs]
0x000000000000000c (INIT) 0x400568
0x000000000000000d (FINI) 0x4007b8
0x0000000000000004 (HASH) 0x400278
0x000000006ffffef5 (GNU_HASH) 0x4002b8
0x0000000000000005 (STRTAB) 0x4003f8
0x0000000000000006 (SYMTAB) 0x4002f0
0x000000000000000a (STRSZ) 213 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x600fe8
0x0000000000000002 (PLTRELSZ) 72 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x400520
0x0000000000000007 (RELA) 0x400508
0x0000000000000008 (RELASZ) 24 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x4004e8
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x4004ce
0x0000000000000000 (NULL) 0x0
intro-hello $


- 訳書P115
- lt-helloが謎だ。

intro-hello $ sudo libtool --mode=execute cp hello /usr/local/bin
intro-hello $ sudo libtool --mode=uninstall rm -f /usr/local/bin/hello
intro-hello $ ls /usr/local/bin
gauche-cesconv gauche-config gauche-install gauche-package gosh lt-hello
intro-hello $ sudo libtool --mode=uninstall rm -f /usr/local/lib/libhello.la
libtool: uninstall: rm -f /usr/local/lib/libhello.la /usr/local/lib/libhello.so.0.0.0 /usr/local/lib/libhello.so.0 /usr/local/lib/libhello.so /usr/local/lib/libhello.a
intro-hello $ sudo libtool --mode=uninstall rm -f /usr/local/bin/lt-hello
libtool: uninstall: rm -f /usr/local/bin/lt-hello
intro-hello $ sudo libtool --mode=uninstall rm -f /usr/local/lib/libtrim.la
libtool: uninstall: rm -f /usr/local/lib/libtrim.la /usr/local/lib/libtrim.so.0.0.0 /usr/local/lib/libtrim.so.0 /usr/local/lib/libtrim.so /usr/local/lib/libtrim.a
intro-hello $ ls /usr/local/lib
eclipse gauche libgauche.so libgauche.so.0 libgauche.so.0.8.14 python2.5 site_ruby
intro-hello $


こつこつ。