NTEmacs + cygwin or msys2 で exec-path-from-shell


exec-path-from-shell

exec-path-from-shell (https://github.com/purcell/exec-path-from-shell) は、GUI での起動時に、指定した環境変数をシェルから引き継いでくれます。楽チンです。
詳細は以下のリンクを参照
http://qiita.com/catatsuy/items/3dda714f4c60c435bb25
http://syohex.hatenablog.com/entry/20130718/1374154709

ただし、windows はサポートしてないし、そのままでは動きません。ですが、NTEmacs と cygwin/msys2 を楽して連携したいので、それを何とかしてみます。

以下の環境で検証

  • Windows 7、msys2、NTEmacs 24.5.1、exec-path-from-shell melpa の最新版

パスの形式の問題

cygwin/msys2 のシェルで扱うパスは unix 形式であり、cygwin or msys2 の環境変数もそうなっています。一方、NTEmacs は WIN32 アプリケーションのため unix 形式のパスは読み込めません。exec-path-from-shell がうまく動かない問題はこれが原因です。よって、 unix 形式のパス → windows 形式のパスのように変換できればいいわけです。

cygpath

cygwin/msys2 には unix 形式のパス と windows 形式のパスを相互に変換する cygpath コマンドがあります(詳細は以下のリンク参照)。
http://takuya-1st.hatenablog.jp/entry/20110910/1315644196

このリンク先には紹介されていませんが、-p オプションを使うと、環境変数で使われるパスのリストを一気に変換してくれます。

$ cygpath -amp "/bin:/usr/bin" # unix 形式のパスリスト
D:/msys64/usr/bin;D:/msys64/usr/bin # windows 形式のパスリスト

$ cygpath -aup "D:/msys64/usr/bin;D:/msys64/usr/bin" # windows 形式のパスリスト
/usr/bin:/usr/bin # unix 形式のパスリスト

よって cygpath を NTemacs から実行すればうまくいきそうです。

NTEmacs の設定

以上を踏まえて、init.el あたりに以下を設定すればいいようです。
注1) bash と cygpath のフルパスは各自の環境にあわせて変更してください
注2) cygwin でもおそらく動きますが、msys2 の設定部分はコメントアウトなりしてください
注3) NTEmacs 24.3 以前を使用している場合 新 advice は動作しないので 旧 advice に書き直しが必要。

init.el
(setq shell-command-switch "-c")

;; シェルからの起動では exec-path-from-shell を実行しない
(let ((shell-level (getenv "SHLVL")))
  (when (or (not shell-level) (string= "0" shell-level))
    (when (eq system-type 'windows-nt)
      ;; bash のフルパスを指定
      (setq shell-file-name "d:/msys64/usr/bin/bash")
      (defvar explicit-shell-file-name)
      (setq explicit-shell-file-name shell-file-name)
      (setenv "SHELL" shell-file-name)

      ;; msys2 のみの設定 mingw64 とパスを共有
      (setenv "MSYSTEM" "MINGW64")

      ;; 環境変数 PATH のときのみパスの変換を実行
      (defun ad-exec-path-from-shell-setenv (orig-fun &rest args)
        (when (string=  (car args) "PATH")
          (setf (nth 1 args)
                (with-temp-buffer
                  (call-process "d:/msys64/usr/bin/cygpath" nil '(t nil) nil "-amp" (nth 1 args))
                  (unless (bobp)
                    (goto-char (point-min))
                    (buffer-substring-no-properties (point) (line-end-position))))))
        (apply orig-fun args))
      (advice-add 'exec-path-from-shell-setenv :around 'ad-exec-path-from-shell-setenv))

    ;; 引き継ぎたい環境変数を列挙
    (exec-path-from-shell-copy-envs '("PATH" "MANPATH" "PKG_CONFIG_PATH"))))