Monthly Archives: 3月 2014

2014-03-01

gitをautocrlf=trueで使っているとel-getでWanderlustがインストールできない

el-getをインストールし、el-get-installでapelのインストールから先に進まない問題を解決したのですが、今度は次のようなエラーが出ました。

Generating autoloads for wanderlust/site-lisp/wl/wl-highlight.el...done
Generating autoloads for wanderlust/site-lisp/wl/wl-mailto.el...done
Generating autoloads for wanderlust/site-lisp/wl/wl-message.el...done
Generating autoloads for wanderlust/site-lisp/wl/wl-mime.el...done
wanderlust failed to install: (error Local variables entry is missing the suffix)
el-get-installation-failed: Local variables entry is missing the suffix

なんのこっちゃい。

原因

wl-mime.elの後くらいでエラーになっていたので、その後のファイルを見て原因が分かりました。

例えばwl-news.elの先頭は次のようになっています。

;;; wl-news.el --- Create notification from NEWS(.ja) for Wanderlust. -*-coding: iso-2022-jp-unix;-*-

;; Copyright (C) 2002 Yoichi NAKAYAMA

coding-systemがunix。つまり、改行コードがLFと指定されているのです。
でも私は普段Windows環境ではgitをcore.autocrlf=trueにして使用しています。
リポジトリにはLFで保存されるが、ワーキングコピーはCRLFになるようにしているのです。
つまり、このwl-news.elはCRLFになっているのです。
どうもヘッダーではunix(LF)と書いてあるのに、実際のファイルはdos(CRLF)なのでエラーが出るようなのです。

対策

普段 core.autocrlf=true にしているのには訳があります。なので、 git config --global core.autocrlf false にするのは却下です。

ワーキングコピーのローカルでgit configしようにも、el-getがこれからcloneしようとしているファイルに対しては意味がありません。

git cloneするときに、最初から git config core.autocrlf false の状態になるような方法を探したのですが、良い方法は見つかりませんでした。

ただ、 git -c core.autocrlf=false のようにオプションで指定してやれば、cloneして取り出したファイルはunix(LF)になりました。
残念ながらローカルなconfigにこのオプションは反映されません。
なので、毎回オプションをつけてgitを呼び出す必要があります。

つまり、el-getがgitを呼び出すときに、強制的にargsに("-c" "core.autocrlf=false")を付加してやれば良いわけです。

el-getでは、呼び出すコマンド列は全てリスト化されてまとめて el-get-start-process-list に渡されます(たぶん非同期対応のためだと思います)。

なので、 el-get-start-process-list に渡されるそのリスト(commands)の中に、:programがgitのものを見つけ出し、その:argsの先頭に("-c" "core.autocrlf=false")を付加します。

;;; el-getがgitを呼び出すとき、 -c core.autocrlf=false 引数を付加する。
;;; wanderlustがLF改行でなければバイトコンパイルに失敗するので。
;;;
;;; 普段autocrlf=trueでgitを使っているので、gitのglobal configを変えたくない。
;;;
;;; 注意: cloneしたワーキングコピーのconfigにこの設定は反映されない。
;;;       el-get以外から直接 ~/.emacs.d/el-get/ にあるワーキングコピーを
;;;       gitで操作しようとすると、問題が起きる場合があるので注意すること。
;;;       そのようなことをする前に git config で明示的に設定すると良いと思う。
(defadvice el-get-start-process-list (around my-el-get-start-process-list--modify-git-args activate)
  (let* ((commands (ad-get-arg 1))
         (git-executable (el-get-executable-find "git"))
         (new-commands
          (loop for c in commands collect
                (if (string= (plist-get c :program) git-executable)
                    ;; gitならargsプロパティに -c core.autocrlf=falseをつける。
                    ;; @todo 破壊的だけどOK?
                    (plist-put c :args (append '("-c" "core.autocrlf=false") (plist-get c :args)))
                  ;; gitでないならそのまま
                  c))))
    (ad-set-arg 1 new-commands)
    ad-do-it))

これで無事にWanderlustがバイトコンパイルできるようになりました。
……これだからel-getは使いたくなかったんだ。

その他の解決策

git -c core.autocrlf=false %* という内容のgit-lf.batを作って、それをel-getに使わせる(el-get-git-executableを書き換える)という方法もあります。

その他のトラブル

gitは空白が無いパスに入れた方が無難かも?(詳しく確認してないがverboseで追っているときに、checksumのところでProgram Filesがらみの変なエラーメッセージを見たことがある)

2014-03-01

Windowsのel-getで非同期インストールがエラーになる

el-getをインストールしたので、Wanderlustをel-getでインストールしようとしたらエラーになりました。

次のエラーメッセージが出ました。(gnupack emacs-24.3-20130503 / el-get version 5.1.1dce781)

el-get install apel
el-get: Package apel installed.
el-get: git submodule update ok
el-get: el-get-build apel: c:/app/emacs-24.3-20130503/bin/emacs -batch -q -no-site-file -l APEL-MK -f compile-apel prefix site-lisp site-lisp ok.
el-get: el-get-build apel: c:/app/emacs-24.3-20130503/bin/emacs -batch -q -no-site-file -l APEL-MK -f install-apel prefix site-lisp site-lisp ok.
apel failed to install: (error process byte-compile no longer connected to pipe; closed it) [2 times]
el-get-installation-failed: process byte-compile no longer connected to pipe; closed it

(el-get 'sync 'wanderlust)を評価して同期でインストールしようとするとこのエラーは出ないようなので(別のエラーは出るw)、非同期の時だけの問題のようです。

原因

結論から言うと shell-file-name が "cmdproxy.exe" になっていないことが原因です。
el-getはwindows-ntで実行しているとき、shell-file-nameを強制的に "cmdproxy.exe" に変更してからプロセス呼び出しを行うようになっています。
ただ、この処理がel-get-build関数内にしかないのが問題です。

el-get-build関数は非同期の時、ビルドの完了を待たずに終了します。el-get-build関数内ででいくら

(let* (...略...
       (shell-file-name (or (and (eq system-type 'windows-nt)
                                 (executable-find "cmdproxy.exe"))
                            shell-file-name))
       ...略...)
  ...処理の本体...

のようなコードを書いても、非同期で実行される後続のプロセス呼び出しには適用されません。

shell-file-name が"cmdproxy.exe"でないと、 shell-quote-argument がWindowsのドライブレターを含むパスを正しくクォートできません。
"C:/home/hoge.el"みたいなパスを"C\:/home/hoge.el"のようにしてしまいます。
結果、サブプロセスで呼ばれるemacsは引数で渡されたファイル名を開けずに異常終了します。
そうすると、上のエラーのように、プロセスとパイプがつながらないよ!ということになります。

対策

el-getがサブプロセスを起動するときは必ず shell-file-name が"cmdproxy.exe"になるようにします。

まず、 el-get で使用する shell-file-name を次のようにして決めます。

;;; el-getが使用するshell-file-nameを決める。
;;; el-get-build内(el-get version 5.1.1dce781)で似たような判定をしているが、
;;; その関数内だけでは不十分。
(setq my-el-get-shell-file-name
   (or (and (eq system-type 'windows-nt)
            (executable-find "cmdproxy.exe"))
       shell-file-name))

そして、 el-get-start-process-list 関数を実行するときは、必ずその決めた shell-file-name を使うようにします。

;;; プロセスを呼び出す前に my-el-get-shell-file-name を適用する。
;;;
;;; el-get-build内(el-get version 5.1.1dce781)で同じような処理をしているが、
;;; それでは不十分。非同期インストールの時はel-get-build関数は処理の完了を
;;; 待たずに終わってしまうので。
(defadvice el-get-start-process-list (around my-el-get-start-process-list--modify-shell-file-name activate)
  (let ((shell-file-name my-el-get-shell-file-name))
    ad-do-it))

el-get-start-process-list 関数内でコマンドライン引数に shell-quote-argument が適用されています。
shell-quote-argument は shell-file-name の値によって動作が変わるので、
この関数の中を実行しているときだけ shell-file-name が"cmdproxy.exe"になっていればOKです。

(setq el-get-verbose t) で詳細を表示するようにして、 :args 部分のパスに問題が無ければOKです。