ブログトップ 記事一覧 ログイン 無料ブログ開設

翡翠はコンピュータに卵を生むか

2016-11-27

Emacs+SLIMEでCommon Lispの開発をリモートでするまとめ

ディープラーニングなどの重い処理を含むプログラムを出先から開発したいというときがよくある。

そういうときまずやるのが、SSHでGPUを積んだ開発マシンにリモートログインしてターミナル上でEmacs/Vimで開発するというやり方だが、LTEで速くなったとはいえ入力の反映がもたつくことがよくあり、非常にストレスが溜まる。moshも使ってみたが大して差は感じられなかった。その他にもCtrlとMeta以外のモディファイアキーを使いたいなど、やっぱりGUI版のEmacsで開発したいと思うことがよくある。

SLIMEはLisp処理系側で動くSwankと呼ばれるサーバと、それに接続するEmacs側フロントエンドの機能で構成されている。このサーバとクライアントの通信はローカルネットワークに限らないので、リモートからLisp処理系に接続することもできるのだ。知らないけど同じSwankクライアントであるslimv.vimやatom-slimeでも原理的にはできるはずである。

1. SSHでSwankのポートをポートフォワーディングして開発マシンに接続する

ホストの設定は~/.ssh/configでしておく。

Host myhost
    HostName xxx.yyy.com
    Port 22
    User hoge
    identityfile ~/.ssh/myhost_rsa

開発マシンの4005番をローカル(出先マシン)の4005番に繋げるには、

hoge@local$ ssh -L 4005:localhost:4005 myhost

2. 開発マシンでSwankサーバを立ち上げる

接続が切れても再開できるようにscreen上でLisp処理系を立ち上げる。

hoge@remote$ screen -S swank ros -Q -L sbcl-bin dynamic-space-size=4096

3. Swankサーバを起動する

(require :swank)
(swank:create-server :port 4005)

これでサーバの4005番をSSH経由でローカルの4005番に繋げられた。もちろんポート番号は自由に設定できる。

4. ポートフォワーディングしたローカルのポートに対してslime-connectする。

SLIMEをインストール済のEmacsを起動して、 M-x slime-connect RET RET RET でlocalhost:4005に接続して開発マシンのREPLがローカルのEmacs上に現われる。この時、「ローカルのSLIMEとリモートのSwankサーバのバージョンが違うけど大丈夫?」というプロンプトが出ることがある。これが邪魔なときは.emacsに以下のように書いておく。

(setq slime-protocol-version 'ignore)

一連のコマンドをまとめる

ややこしいので2、3のコマンドで済むようにしておく。まずローカルの~/.bashrcなどにエイリアスを書いておく。

alias ssh-swank='ssh -L 4005:localhost:4005 myhost'

次に3.のSwankサーバを起動するLispコードをファイルに保存して開発マシンの~/swank-start.lispに置いておく。それから開発マシンの~/.bashrcなどに処理系を起動してSwank起動コードを読み込むためのエイリアスを書く。

alias swank-start="screen -S swank ros -Q -L sbcl-bin dynamic-space-size=4096 run -l $HOME/swank-start.lisp"

なお、sshで接続すると~/.bashrcを読まないらしいので、~/.bash_profile に~/.bashrcを読むように書いておく。

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

これでターミナルからssh-swank、パスフレーズ入力、swank-start、Emacs起動、M-x slime-connect RET RET RETでREPLが立ち上がる。

TRAMPでリモートのファイルを編集する

リモートのファイルを編集するにはEmacsに標準搭載されているTRAMPが使える。 ~/.emacs にこう書いておく。

(require 'tramp)
(setq tramp-default-method "scp")
(setq tramp-auto-save-directory "/tmp")

オートセーブの度にリモートと通信しようとしてEmacsが止まるので、オートセーブファイルはローカルの/tmpに保存するようにしておく。これで単にC-x C-f /ssh:myhost:~/file.lisp RET などとすることによりリモートのファイルを開ける。

magitも動く!

驚くべきことにtrampでリモートのファイルを開いた状態でM-x magit-statusすると普通にmagitが使えるし、コミットもプッシュもできることに気付く。ただし通信が発生するので多少待たされはする。

まとめ

  • サーバ側でSwankサーバを立てておくことにより出先からEmacs+SLIMEでリモート開発できる
    • 出先のノートPCで重い処理を回してもCPU使用率が最低レベルなのでバッテリーも長持ち
    • ローカル/リモートに関わらずLisp式単位で開発マシンのREPLで評価できる
  • リモートのファイルの編集はEmacsの機能TRAMPによりできる
    • magitもリモートでできる
    • ローカルのEmacsの編集機能がそのまま使えるためにキビキビ動く。低速回線でもストレスがない。

2011-02-11

SLIMEでClojureを使う

Clojureのバージョンアップのせいか、どうも前に他のサイトで見た方法がうまくいかなくなってしまった。

試行錯誤してみた結果とりあえずちゃんと動くようになったので、色々とアヤシイところはあるもののまとめてみる。

SLIMEのインストール

Emacs Lispは~/elisp以下にインストールするものとする。まずはGitを利用して最新版をダウンロードする。87MBもあるのでけっこう時間かかる。SBCLは既に入っているものとする。

$ cd ~/elisp
$ git clone git://git.boinkor.net/slime.git

~/.emacsに以下のように書く

;; 処理系の指定
(setq inferior-lisp-program "sbcl")
;; SLIMEへのロードパスを通す
(add-to-list 'load-path "~/elisp/slime/")
(add-to-list 'load-path "~/elisp/slime/contrib/")
(require 'slime)
(slime-setup '(slime-repl)) ; slime-scratch slime-fancy slime-asdf
(add-hook 'slime-mode-hook
	  (lambda ()
	    ;; outline-minor-modeを有効化
	    (outline-minor-mode t)
	    ;; 各種キーバインド
	    (define-key slime-mode-map "\C-c\C-i" 'comment-region)
	    (define-key slime-mode-map "\C-c\C-o" 'uncomment-region)
	    (define-key slime-mode-map "\C-c\C-c" 'slime-hyperspec-lookup)
	    (define-key slime-mode-map "\M-n" 'scroll-up-one-line)
	    (define-key slime-mode-map "\M-p" 'scroll-down-one-line)
	    ))
(setq slime-net-coding-system 'utf-8-unix) ; 文字コードの指定
(setq slime-startup-animation nil)
(setq slime-truncate-lines nil)

再インストールの際などはコンパイラのバージョンが違っていたりするので、以前のコンパイル済みファイルは消しておいた方がいいかも。~/.slimeにあるので rm -r ~/.slime などとしておく。

Emacsを起動して M-x slime で関連ファイルのコンパイルが始まり、SLIMEのプロンプトが出てきたら成功。

Clojureのインストール

公式のダウンロードページから安定版のclojure-1.2.0とclojure-contrib-1.2.0をダウンロードしてくる。

clojure-contribの方はjarファイルが入っていないのでビルドする必要がある。ビルドのためのソフトがantからMavenっていうのに変わったらしい。/usr/ports/devel/maven2からMaven2を入れて、READMEを読みつつclojure-contribをビルド。

できたjarファイルは ~/.clojure というディレクトリを作ってそこに置いておく。

clojure-modeのインストール

$ cd ~/elisp
$ git clone git://github.com/jochu/clojure-mode.git 

~/.emacsに以下のように書く

(add-to-list 'load-path "~/elisp/clojure-mode")
(require 'clojure-mode)
(autoload 'clojure-mode "clojure-mode" "A major mode for Clojure" t)

Leiningenのインストール

id:t2ruさんの記事参照。基本的にはシェルスクリプトを落としてきて実行権限つけてパスの通ったところに置くだけ。

swank-clojureのインストール

$ cd ~/elisp
$ git clone git://github.com/jochu/clojure-mode.git

ソースをダウンロードしたはいいが、swank-clojureのビルドがよく分からないのでLeiningenでインストールしてjarファイルを~/.clojureにコピーする。

$ lein install swank-clojure 1.3.0-SNAPSHOT
$ cp ~/.m2/repository/swank-clojure/swank-clojure/1.3.0-SNAPSHOT/swank-clojure-1.3.0-SNAPSHOT.jar ~/.clojure/swank-clojure.jar

~/.emacsに以下のように書く

(add-to-list 'load-path "~/elisp/swank-clojure")
(require 'swank-clojure)
(require 'assoc)
;; /.clojure 内の各jarファイルにクラスパスを通す
(setq swank-clojure-jar-home "~/.clojure")
;; slime-lisp-implementationsにclojureの呼び出しコマンドを追加する
(swank-clojure-reset-implementation)

最後に各処理系別にコマンドを用意。

(add-to-list 'slime-lisp-implementations '(sbcl ("sbcl")))
(defun slime-clojure () (interactive) (slime 'clojure))
(defun slime-sbcl () (interactive) (slime 'sbcl))

M-x slime-clojureで起動を確認して終了。これでSBCLとClojureの使い分けができるようになった。

追記

上のように設定してClojure+SLIMEの起動はうまくいったわけだが、デバッガから抜けるときに"error in process filter: Wrong number of arguments: nil,0"というエラーメッセージがミニバッファに毎回出るようになってしまった。しかもその度に数秒止まるのでなんとかしたい。https://github.com/technomancy/swank-clojure/issues/issue/31 によると、SLIMEがELPAからインストールしたものでないとこれが出るらしい。

git cloneで取ってきた最新版でやるときは、多少強引だが、SLIMEの展開先にあるslime.elの中のslime-dispatch-eventの定義を以下のように変更してしまう。

(defun slime-dispatch-event (event &optional process)
  (let ((slime-dispatching-connection (or process (slime-connection))))
    (or (run-hook-with-args-until-success 'slime-event-hooks event)
        (destructure-case event
          ((:emacs-rex form package thread continuation)
           (when (and (slime-use-sigint-for-interrupt) (slime-busy-p))
             (slime-display-oneliner "; pipelined request... %S" form))
           (let ((id (incf (slime-continuation-counter))))
             (slime-send `(:emacs-rex ,form ,package ,thread ,id))
             (push (cons id continuation) (slime-rex-continuations))
             (slime-recompute-modelines)))
          ((:return value id)
           (let ((rec (assq id (slime-rex-continuations))))
             (cond (rec (setf (slime-rex-continuations)
                              (remove rec (slime-rex-continuations)))
                        (slime-recompute-modelines)
-			(funcall (cdr rec) value))
+			(ignore-errors (funcall (cdr rec) value))) ; ここでエラーが起きているのでignore-errorsで黙らせる
                   (t
                    (error "Unexpected reply: %S %S" id value)))))
          ((:debug-activate thread level &optional select)
           (assert thread)
           (sldb-activate thread level select))

いいのかなぁ。これ・・・ しかしとりあえずはちゃんと動くようになったのよしとする。

追記2 クラスパスの指定

変数swank-clojure-classpathにクラスパスがリストとして入ってるので、追加したいパスをpushする。

例えば、プログラミングClojureのサンプルコードを利用したいなら、

$ cd ~/.clojure
$ git clone git://github.com/stuarthalloway/programming-clojure.git

でソースコードを取得して、.emacsに以下を追加する。

(push "~/.clojure/programming-clojure" swank-clojure-classpath)

2007-03-07

howm

噂のメモ用ツールhowmを使ってみた.

一緒にorg-mode.elも試してみたんだけどドキュメントを書くわけでなければhowmの方がいいのかもしれない.

outline-minor-mode

IDEなんかでは関数定義を折り畳めるようになってるのが多くて,エディタ派としては微妙にうらやましかったのだけど,LISPで関数定義の中身を隠すために何かいいのはないかなと思って2chのLISPスレで質問したらoutline-minor-modeを使うといいよというアドバイスがあった.

LISPのファイルを開いたら自動で起動するようにslime-mode-hookに追加しておく.

(add-hook 'slime-mode-hook
   (outline-minor-mode t))

ちょっと使ってみたけど,これは実に良い! 全体の見通しが良くなるから別の関数をちょっと参照したときにわざわざC-sで探すこともない.思考が途切れないので集中力を保てる.

2006-07-15

Xのネットワーク透過性を使ってマルチディスプレイを実現する。

要するに他のPCへX-Windowを転送して、さらにそこを転送元から操作しようということ。

まず、転送先PCのxhostで転送元のアドレスを登録しておく。

転送元PCにx2xをインストールする。これで転送元のキーボードで転送先のX-windowを操作できるようになる。

$ x2x -to 転送先のホスト名:0.0

これで転送元にx2xのウインドウが出る。転送元でこのウインドウをアクティブにすると転送先のX-windowを転送元から操作できるようになる。

次にEmacsから

M-x make-frame-on-display
転送先のホスト名:0.0

とすると転送先に転送元のEmacsの別フレームが行く。これで一つのEmacsのプロセスを二つの画面、一つのキーボードで使える。

二枚のディスプレイで同じEmacsのプロセスを動かしている図。右はWindows2000上のCygwinでX-windowを動かしている。

f:id:masatoi:20060716010421j:image

2005-12-18

Gaucheのinfo参照について

http://www.soraneko.com/~nel/wiliki.cgi?NelDiary%3A2004-10-20

で紹介されているGaucheのinfoを参照するelispを多少書き直して,エラーを補足するようにしたり初回起動のときの動作がおかしいのとかを改善しました.

(defun gauche-show-info ()
  "Show gauche reference manual. this function assumes gauche-refj.info is located on info path"
  (interactive)
  (let ((key-symbol (current-word)))
    (unless (get-buffer "*gauche-info*")
      (info "gauche-refj")
      (rename-buffer "*gauche-info*"))
    (with-current-buffer (get-buffer "*gauche-info*")
    (if (or (null key-symbol) (string= key-symbol ""))
	(call-interactively 'Info-index)
	(condition-case err
	    (progn 
	      (Info-index key-symbol)
	      (switch-to-buffer "*gauche-info*"))      
	  (quit (print err)))))))

(add-hook 'scheme-mode-hook
	  '(lambda ()
	     (define-key scheme-mode-map "\C-c\C-d" 'gauche-show-info)))