Golangで作成されたVimプラグインの配布方法について調べた

# まえがき
Golangのクロスコンパイルは非常に魅力的だと思うのですが、Vimのプラグインとして配布する場合に少し悩んだので、調査した内容を残しておきます。

## 配布方法1: ユーザーにビルドしてもらうパターン
ユーザーにGoのビルドをしてもらうケース。

基本的にREADME.mdのInstallationの項目に

```shell
$ cd /ビルド可能なパス && go get -d && go build
```

と書いておけばすむので、用意する側はかなり気楽です。

ユーザーに`go build`してもらえれば必ずその環境にあったバイナリが出来上がるのでこの点はメリットになりうるのですが、ユーザー側にGoの環境がなければいけないため、もしGoの環境がなければ使う側は少しハードルが高くなるかもしれません。

[参考: Go で Vim プラグインを書く - http://haya14busa.com/vim-go-client/](http://haya14busa.com/vim-go-client/)

## 配布方法2: バイナリを配布するパターン
バイナリ配布したいですよね。でも、インストール周りどうしましょう？

ここで個人的に大好きな[fzf先生(Golang製)](https://github.com/junegunn/fzf)を参考にしてみました。
fzfにはインストールスクリプトがあり、shell scriptでアーキテクチャを判定してバイナリをダウンロードしているようです。

fzfのインストールスクリプトの実際のアーキテクチャを判定している箇所はこちらになります。

```shell
# Try to download binary executable
archi=$(uname -sm)
binary_available=1
binary_error=""
case "$archi" in
  Darwin\ *64)   download fzf-$version-darwin_${binary_arch:-amd64}.tgz  ;;
  Darwin\ *86)   download fzf-$version-darwin_${binary_arch:-386}.tgz    ;;
  Linux\ armv5*) download fzf-$version-linux_${binary_arch:-arm5}.tgz    ;;
  Linux\ armv6*) download fzf-$version-linux_${binary_arch:-arm6}.tgz    ;;
  Linux\ armv7*) download fzf-$version-linux_${binary_arch:-arm7}.tgz    ;;
  Linux\ armv8*) download fzf-$version-linux_${binary_arch:-arm8}.tgz    ;;
  Linux\ aarch64*) download fzf-$version-linux_${binary_arch:-arm8}.tgz  ;;
  Linux\ *64)    download fzf-$version-linux_${binary_arch:-amd64}.tgz   ;;
  Linux\ *86)    download fzf-$version-linux_${binary_arch:-386}.tgz     ;;
  FreeBSD\ *64)  download fzf-$version-freebsd_${binary_arch:-amd64}.tgz ;;
  FreeBSD\ *86)  download fzf-$version-freebsd_${binary_arch:-386}.tgz   ;;
  OpenBSD\ *64)  download fzf-$version-openbsd_${binary_arch:-amd64}.tgz ;;
  OpenBSD\ *86)  download fzf-$version-openbsd_${binary_arch:-386}.tgz   ;;
  CYGWIN*\ *64)  download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
  MINGW*\ *86)   download fzf-$version-windows_${binary_arch:-386}.zip   ;;
  MINGW*\ *64)   download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
  MSYS*\ *86)    download fzf-$version-windows_${binary_arch:-386}.zip   ;;
  MSYS*\ *64)    download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
  Windows*\ *86) download fzf-$version-windows_${binary_arch:-386}.zip   ;;
  Windows*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
  *)             binary_available=0 binary_error=1 ;;
esac
```
https://github.com/junegunn/fzf/blob/373c6d8d55066251dc7af58598de6af4ff4570cf/install#L176-L202

`download fzf-$version ~~~`となっている箇所はバイナリをダウンロードするスクリプト内の関数を呼び出しています。
こんなイメージで、初回起動時やプラグインマネージャでインストールスクリプトを叩いてあげれば、適切なバイナリを落としてくることができるようになりそうです。

## 配布方法3: 少し横着な方法
2つ目の方法で少し横着するとすれば、バイナリサイズが小さいうちはVimプラグインリポジトリにアーキテクチャごとのバイナリファイルをpushして、実行時に適切なバイナリが選ばれるようにすることもできるかと思います。
（無駄にファイルをダウンロードすることになるので怒られそうな気もしなくもないですが...）

Vim script側

```vimscript
let s:go_bin_path = v:null
let s:ext = ''

" windowsで極力外部コマンドを実行したくないのでここで判定できればここで
if has('win32')
    let s:go_bin_path = 'win-386'
    let s:ext = '.exe'
elseif has('win64')
    let s:go_bin_path = 'win-amd64'
    let s:ext = '.exe'
else
    " 正しいパスが得られているかチェックはしたほうが良いかも
    let s:go_bin_path = trim(system('path/to/archi_info.sh'))
endif

if l:go_bin_path != v:null
    let s:result = system('"path/to/plugin/' . s:go_bin_path . '/goで実行するバイナリファイル"' . s:ext . '')
endif
```

アーキテクチャ判定スクリプト

```shell:archi_info.sh
#!/usr/bin/env bash

set -u

# Echo binary path executable to stdout.
archi=$(uname -sm)
case "$archi" in
  Darwin\ *64)     echo darwin-amd64  ;;
  Darwin\ *86)     echo darwin-386    ;;
  Linux\ armv5*)   echo linux-arm5    ;;
  Linux\ armv6*)   echo linux-arm6    ;;
  Linux\ armv7*)   echo linux-arm7    ;;
  Linux\ armv8*)   echo linux-amd64   ;;
  Linux\ aarch64*) echo linux-amd64   ;;
  Linux\ *64)      echo linux-amd64   ;;
  Linux\ *86)      echo linux-386     ;;
  FreeBSD\ *64)    echo freebsd-amd64 ;;
  FreeBSD\ *86)    echo freebsd-386   ;;
  OpenBSD\ *64)    echo openbsd-amd64 ;;
  OpenBSD\ *86)    echo openbsd-386   ;;
  CYGWIN*\ *64)    echo windows-amd64 ;;
  MINGW*\ *86)     echo windows-386   ;;
  MINGW*\ *64)     echo windows-amd64 ;;
  MSYS*\ *86)      echo windows-386   ;;
  MSYS*\ *64)      echo windows-amd64 ;;
  Windows*\ *86)   echo windows-386   ;;
  Windows*\ *64)   echo windows-amd64 ;;
  *)               ;;
esac
```

Vim scriptからアーキテクチャ判定用のshell scriptを実行し、結果をもとにVim scriptから適切なGoのバイナリを実行します。

各アーキテクチャ用のビルドは、下記のような内容をMakefileに書いて`make all`でコマンドで一気に作成されるようにします。
（パスは適宜読み替えてください）

```makefile
mac:
	GOOS=darwin GOARCH=amd64 go build -o ../bin/darwin-amd64/go_bin ./go_file.go
	GOOS=darwin GOARCH=386 go build -o ../bin/darwin-386/go_bin ./go_file.go
all:
	GOOS=linux GOARCH=arm GOARM=5 go build -o ../bin/linux-arm5/go_bin ./go_file.go
	GOOS=linux GOARCH=arm GOARM=6 go build -o ../bin/linux-arm6/go_bin ./go_file.go
	GOOS=linux GOARCH=arm GOARM=7 go build -o ../bin/linux-arm7/go_bin ./go_file.go
	GOOS=linux GOARCH=amd64 go build -o ../bin/linux-amd64/go_bin ./go_file.go
	GOOS=linux GOARCH=386 go build -o ../bin/linux-386/go_bin ./go_file.go

	GOOS=freebsd GOARCH=amd64 go build -o ../bin/freebsd-amd64/go_bin ./go_file.go
	GOOS=freebsd GOARCH=386 go build -o ../bin/freebsd-386/go_bin ./go_file.go

	GOOS=openbsd GOARCH=amd64 go build -o ../bin/openbsd-amd64/go_bin ./go_file.go
	GOOS=openbsd GOARCH=386 go build -o ../bin/openbsd-386/go_bin ./go_file.go

	GOOS=windows GOARCH=amd64 go build -o ../bin/win-amd64/go_bin.exe ./go_file.go
	GOOS=windows GOARCH=386 go build -o ../bin/win-386/go_bin.exe ./go_file.go
	make mac
```

3つ目の方法のメリットとしてはVimプラグインのバージョンに対して正しいバイナリが落とされているか気にする必要がないため、用意する側も少し気が楽になりそうです。

## まとめ
Golangで作成されたVim プラグインの配布方法の３つをまとめました。

実際に自分もVimプラグインでGoのバイナリを配布する必要があったのですが、現在は3つ目の方法で暫定的に用意しています。

ちなみに、今回作ったプラグインはこちらです。
Qiita: [Neovimでpopup-menuを実装できるプラグイン](https://qiita.com/kamykn/items/eb20977cb9b26a760652)
Github: https://github.com/kamykn/popup-menu.nvim
（もしも、需要が高まれば2つ目の方の方法に移行する、というのも考えられるかと思います。）

色々方法はありましたが、そのプロジェクトに応じて適切な方法を選択できればいいかなと思いました👍