読者です 読者をやめる 読者になる 読者になる

きくらげ観察日記

好きなことを、適当に。

Gaucheでソースコードのあるパスを取得

きちんと ./configure && make && make install するようなプログラムでもない限り、ソースコードを適当にどこかから引っ張ってきて、そこから

$ gosh ./main.scm

するだけでとりあえず動くようにできたほうが楽な場合はよくあります。
しかし、Gaucheでそれをやろうとした場合は色々な問題が……。

ソースコードからの相対パスでload

こちらの問題は簡単に解決します。

(load "./relative/module.scm")

これで現在のソースコードのある位置からの相対パスでロードすることができます。
しかし、上のコードでいう ./relative/module.scm がモジュールになっていた場合

(load "./relative/module.scm")
(import relative.module)

というように書かないといけなくなるため、少々不格好になってしまいます。素直にuseさせてくれ……。

そこで作られたのが、add-load-pathの:relativeオプションです。これを使うと、goshのカレントディレクトリではなくソースコードからの相対パスでload-pathを追加してくれます。

(add-load-path "." :relative)
(use relative.module) ;; (このコードの位置)/relative/module.scmがuseされる

実は、この機能は以下のような作者とユーザーとのやりとりによって生まれたもののようです。

togetter.com

WikiTwitterで要望を言うと、内容によってはすぐに反映させてくれるのがGaucheのいいところかもしれません。


ソースコードからの相対パスにあるファイルを開く

これは少し難しい問題となり、いくつかのパターンがあります。

ソースコードがmain関数のある場所である場合、(car args)がソースコード自体のパスとなるため、それを使用することができます。

(use file.util)
(define (main args)
  (define resource-path (build-path (sys-dirname (car args))
                                    "relative/resource.dat"))
  (call-with-input-file resource-path
    (^(resource)
      ...)))

しかし、このパターンが使えない場面もあります。main関数のあるファイルがPATHの通されている場所であり、コマンド名で呼ばれている場合や、main関数のあるファイルではないモジュールから相対パスでファイルを開く場合です。

このような場合には(current-load-path)が使えます。この関数は、ファイルのload中である場合、そのファイルのあるディレクトリを返す関数です。

;; path.scm

(define src-path
  (current-load-path))

(print src-path)

これをgoshで読み込むと、以下のようになります。

gosh> (load "./path.scm")
./path.scm
#t

しかし、(current-load-path)はload中に評価された場合のみ効果を持つため、以下のような場合にはうまく動きません。以下のように書き換えてみましょう。

;; path.scm

(define (get-src-path)
  (current-load-path))

(print src-path)

これをloadしてみると、以下のように表示されます。

gosh> (load "./path.scm")
./path.scm
#t

先ほどと同じ結果が得られたかのように見えますが、実はこれはうまくいっていません。
(get-src-path)をもう一度実行してみましょう。

gosh> (get-src-path)
#f

今度は#fが返ってきました。なぜなら現在はpath.scmのload中ではないからです。
(current-load-path)を使う場合は、確実にload中に評価されるように注意しなければなりません。

まとめ

  1. 相対パスでプログラムをloadしたい場合は(add-load-path "." :relative)
  2. main関数の中なら(car args)が実行ファイルのパスになる
  3. (current-load-path)はファイルのある位置を返すが、必ずload中に評価されなければならない