WindowsネイティブなVimとMSYSビルドなVimで同一のvimrcを読む状況で、dein.vimを使って両環境のvimproc DLLを作るまで


## はじめに

私が扱う特殊な環境のひとつに、`mingw-w64`でビルドしたVimと、`msys2`でビルドしたVimが共存している環境がある。
どちらも`$HOME`を同じ場所にしているため、同一の`.vimrc`、`runtimepath`を見に行くようになっている。

このような環境で問題になるのは各環境用にビルドが必要な[vimproc](https://github.com/Shougo/vimproc.vim)で、以前[pathogen](https://github.com/tpope/vim-pathogen)で管理していたときはそれぞれの環境で`make`を手動実行すればよかったのだが、[dein.vim](https://github.com/Shougo/dein.vim)に乗り換えてからはそれだけでは済まなくなってしまった。
特殊な状況なので同一のことに悩まれている方は少ないかと思うが、ひとまず解決に至ったので記事にまとめることにする。

## 前提となる環境

下記の環境でVimを扱う場合を想定している。

- Windowsネイティブ環境: `mingw-w64`でビルドしたVim、または[vim-jp](http://vim-jp.org/)で配布されているVim
- msys2のシェル内: `vim`パッケージのvim
- mingw32/64のシェル内: `vim`パッケージのvim

また、`vimproc`はTOMLに以下のように書いてあると想定する。

```
[[plugins]]
repo = 'Shougo/vimproc'
hook_post_update = '''
  if dein#util#_is_windows()
    let cmd = 'tools\\update-dll-mingw'
  elseif dein#util#_is_cygwin()
    let cmd = 'make -f make_cygwin.mak'
  elseif executable('gmake')
    let cmd = 'gmake'
  else
    let cmd = 'make'
  endif
  let g:dein#plugin.build = cmd
'''
```

## 問題点

### 1. どちらかのDLLしか作られない

前提とする環境だと2種類のDLL（Windows用のDLLとCygwin用のDLL）を作らなければならないのだが、この設定だと`dein#install()`や`dein#update()`した環境のDLLしか作られない。
DLLが作られなかった環境で`dein#reinstall('vimproc')`すればよいのかとも思ったが、「そんなプラグインはねーよ！」と怒られてしまった。

### 2. mingw32/64のシェルでVimを立ち上げるとvimprocのビルドに失敗する

前提とする環境の場合、WindowsネイティブなVimではなくmingw32/64でもmsysでビルドしたVimを利用するようにしている。
`dein#install()`や`dein#update()`でvimprocのビルドが走るのだが、msysでビルドしたVimなので`dein#util#_is_cygwin()`が`1`を返すこととなり、上記の設定だと`make -f make_cygwin.mak`が走ることとなる。
ただし、シェル環境自体はmingw32/64なので、このままだとビルドに失敗してしまう。

## 解決策

**2016/10/31 Update**

以下のようにTOMLを修正することでとりあえずうまく動くようになった。

```
[[plugins]]
repo = 'Shougo/vimproc'
merged = 0
hook_add = '''
  if dein#util#_is_windows()
    let s:vimproc_dll_basename = has('win64') ?
          \ 'vimproc_win64.dll' : 'vimproc_win32.dll'
  elseif dein#util#_is_cygwin()
    let s:vimproc_dll_basename = 'vimproc_cygwin.dll'
  elseif dein#util#_is_mac()
    let s:vimproc_dll_basename = 'vimproc_mac.so'
  elseif glob('/lib*/ld-linux*64.so.2', 1) != ''
    let s:vimproc_dll_basename = 'vimproc_linux64.so'
  elseif glob('/lib*/ld-linux*.so.2', 1) != ''
    let s:vimproc_dll_basename = 'vimproc_linux32.so'
  elseif system('uname -s') =~? '^.\+BSD\n$'
    let s:vimproc_dll_basename = system(
          \ 'uname -sm | tr "[:upper:]" "[:lower:]"'
          \ .' | sed -e "s/ /_/" | xargs -I "{}" echo "vimproc_{}.so"')[0 : -2]
  else
    let s:vimproc_dll_basename = 'vimproc_unix.so'
  endif

  let s:vimproc_dll = get(g:, 'vimproc#dll_path', dein#get('vimproc').path . '/lib/'. s:vimproc_dll_basename)

  if !filereadable(s:vimproc_dll)
    call dein#call_hook('post_update')
    call dein#build('vimproc')
  endif
'''
hook_post_update = '''
  if dein#util#_is_windows()
    let cmd = 'tools\\update-dll-mingw'
  elseif dein#util#_is_cygwin()
    let cmd = 'make -f make_cygwin.mak'
    if $MSYSTEM == "MINGW32" || $MSYSTEM == "MINGW64"
      let cmd = "MSYSTEM=MSYS bash --login -c 'cd " . g:dein#plugin.path . ";" . cmd . "'"
    endif
  elseif executable('gmake')
    let cmd = 'gmake'
  else
    let cmd = 'make'
  endif
  let g:dein#plugin.build = cmd
'''
```

mingw32/64のシェルからmsysのVimが起動された場合はmsysのシェルから`make`を叩くようにしてみた。
~~さらに、`hook_add`フックで`g:vimproc#dll_path`にファイルがあるかを調べ、なければビルドして`dein#recache_runtimepath()`でキャッシュを再構築するようにしている。~~
~~このタイミングで`dein#recache_runtimepath()`して問題ないのかはよく調べていないが、今のところ問題なく動いているようだ。~~

dein.vim作者のShougo氏がコメント欄にmore betterな解決方法を教えてくださったので反映を行った。

- `merged = 0`とする
- `dein#build`を使用する

さらに上記修正を行うと、私の環境では稀に`g:vimproc#dll_path`に値がセットされる前に`hook_add`フックが呼ばれることがあったため、`hook_add`内の判定を若干修正した。

恐らく`dein#build`はこの記事で私がビルドし直そうとしていたがために実装してくれたのだろう。非常に感謝である。
本来ならば`build = "make"`としておけば解決なのだが、MinGW32/64なシェルでMSYSビルドのVimを使おうとしているために`make`だけの場合だと`make -f make_mingw32.mak`または`make -f make_mingw63.mak`が走ることになり、vimproc_cygwin.dllが作られないためにこのようなトリッキーな手法をキメなければならなくなっている。
MinGW32/64なシェルでMSYSビルドのVimを使うことがなければこのような手法は不要なので、どうか`build = make`だけ書いて、必要なときに手動で`call dein#build('vimproc')`するようにしてほしい。