VimのHEADをビルドしてインストールする方法(Mac)

## はじめに

以下の記事のVim版です。

https://qiita.com/uhooi/items/95ffbfc1270df79f1fbd

Homebrewで `brew install vim` を実行してVimをインストールすると、Vimの安定版がインストールされます。

しかしそれだと(ry

`master` ブランチのHEADをビルドして、文字通り最先端のVimをインストールする方法を紹介します。

## 環境

- OS：macOS Sonoma 14.0（23A344）
- Homebrew：4.1.15-36-gc0c8a4d
- Vim：9.0

## 初回セットアップ

最初のみ必要なセットアップを紹介します。

基本的には以下の記事通りに行っています。

https://vim-jp.org/docs/build_linux.html

HomebrewのFormulaも参考にしています。

https://github.com/Homebrew/homebrew-core/blob/a24f6cc2bf5fffbd656b3bfdad127327a620b0fe/Formula/v/vim.rb

### Homebrewのインストール（任意）

以下のページを参考にHomebrewをインストールします。

https://brew.sh/

### 依存関係のインストール（任意）

Vimのビルドに必要な依存関係をインストールします。

```shell-session
$ brew install gettext libsodium lua ncurses perl python python3 ruby
```

各依存関係を紹介します。

|依存関係|説明|
|:--|:--|
| `gettext` |日本語化しない場合は不要|
| `libsodium` |暗号化ライブラリ。必要なのかわかっていない|
| `lua` | `lua` を `+` にしない場合は不要|
| `ncurses` |TUI作成ライブラリ。必要なのかわかっていない|
| `perl` | `perl` を `+` にしない場合は不要|
| `python` | `python` を `+` にしない場合は不要|
| `python3` | `python3` を `+` にしない場合は不要|
| `ruby` | `ruby` を `+` にしない場合は不要|

依存関係はHomebrew以外でインストールしていても問題ありません。
例えばPythonを `pyenv` で管理している場合、Homebrewで `python3` をインストールする必要はありません。
`ruby` も同様で、すでに `rbenv` で管理している場合はインストール不要です。

### Vimのパスを通す（任意）

必要に応じてVimのパスを通します。
私は `$HOME/.local/vim` にVimをインストールしています。

```diff_shell:.bash_profile
+ # Vim
+ export VIM_HOME=$HOME/.local/vim
+ if [ -d "${VIM_HOME}" ]; then
+   export PATH="${VIM_HOME}/bin:$PATH"
+ fi
```

### ncursesのパスを通す（任意）

必要なのかわかっていませんが、 `ncurses` のパスを通します。

```diff_shell:.bash_profile
HOMEBREW_HOME=/opt/homebrew
# ...
+ # ncurses
+ export PATH="${HOMEBREW_HOME}/opt/ncurses/bin:$PATH"
```

### 既存のVimのアンインストール（任意）

必要に応じてすでにインストールされているVimをアンインストールします。
私はHomebrewでインストールしていたので、以下のコマンドを実行してアンインストールしました。

```shell-session
$ brew uninstall vim
```

### リポジトリのクローン

Vimのリポジトリをクローンします。

```shell-session
$ mkdir vim
$ cd vim
$ git clone https://github.com/vim/vim
$ cd vim
```

## Vimのビルドとインストール

初回セットアップが完了したら、Vimをビルドしてインストールします。

### リモートリポジトリから最新を取得

リモートリポジトリから最新の変更を取得します。

```shell-session
$ git fetch --prune origin
$ git rebase origin/master
```

### キャッシュの削除

前回ビルドしたキャッシュを削除します。

```shell-session
$ make distclean
```

### Vimのビルド設定

`./configure` でビルド設定を行います。

```shell-session
$ ./configure \
  --enable-cscope \
  --enable-fail-if-missing \
  --enable-luainterp=yes \
  --with-lua-prefix=/opt/homebrew/Cellar/lua/5.4.6 \
  --enable-perlinterp=yes \
  --enable-python3interp=yes \
  --with-python3-command=python \
  --enable-rubyinterp=yes \
  --prefix=${HOME}/.local/vim \
  --with-compiledby=uhooi \
  --with-tlib=ncurses \
  CFLAGS='-I/opt/homebrew/include'
```

よく使うオプションを紹介します。

|オプション|説明|
|:--|:--|
| `--enable-cscope` | `cscope` を有効にします。|
| `--enable-fail-if-missing` |追加機能の依存関係が見つからない場合に失敗させます。失敗すると `./configure` の標準出力が止まるので、最後に出力された内容がエラーになっているとわかります。|
| `--enable-luainterp=yes` | `lua` を `+` にします。|
| `--with-lua-prefix` |Luaのインストール先を指定します。Homebrewでインストールしている場合は `/opt/homebrew/Cellar/lua/{バージョン}` です。|
| `--with-luajit` |Luaの代わりにLuaJITとリンクします。私は指定していません。|
| `--enable-perlinterp=yes` | `perl` を `+` にします。|
| `--enable-pythoninterp=yes` | `python` を `+` にします。私の環境はPython 2をセットアップしておらず、エラーになったので指定していません。|
| `--enable-python3interp=yes` | `python3` を `+` にします。|
| `--with-python3-command={コマンド}` |Python 3のコマンドを指定します。私は `pyenv` でPythonを管理しているため、 `python` を指定しています。|
| `--enable-rubyinterp=yes` | `ruby` を `+` にします。|
| `--prefix={パス}` |インストール先を指定します。|
| `--with-compiledby={文字列}` | `vim --version` の実行時に出力されるコンパイル者の名前を指定します。|
| `--with-tlib={ライブラリ名}` |ターミナルのライブラリを指定します。私はHomebrewのFormulaに合わせて `ncurses` を指定しています。|

詳細は `./configure --help` をご参照ください。

### Vimのビルド

Vimをビルドします。

```shell-session
$ make
```

### Vimのインストール

Vimをインストールします。

```shell-session
$ make install prefix=${HOME}/.local/vim
```

`prefix` でインストール先を指定します。
`./configure` の `--prefix` で指定するだけでは十分でないので注意です。
デフォルトでは `/usr/local/bin/vim` にインストールされます。

### バージョンの確認

Vimのバージョンを確認します。
`compiled` の日時が最新で、 `Compiled by` が `--with-compiledby` で指定した名前になっていて、 `./configure` で指定した機能が `+` になっていれば成功です。

もしなっていなければ `./configure` に失敗している可能性が高いので、 `./configure` の標準出力や `src/auto/config.log` を確認しましょう。

```shell
$ vim --version
VIM - Vi IMproved 9.0 (2022 Jun 28, compiled Oct 18 2023 13:47:41)
macOS version - arm64
Included patches: 1-2043
Compiled by uhooi
Huge version without GUI.  Features included (+) or not (-):
+acl               +file_in_path      +mouse_urxvt       -tag_any_white
+arabic            +find_in_path      +mouse_xterm       -tcl
+autocmd           +float             +multi_byte        +termguicolors
+autochdir         +folding           +multi_lang        +terminal
-autoservername    -footer            -mzscheme          +terminfo
-balloon_eval      +fork()            +netbeans_intg     +termresponse
+balloon_eval_term -gettext           +num64             +textobjects
-browse            -hangul_input      +packages          +textprop
++builtin_terms    +iconv             +path_extra        +timers
+byte_offset       +insert_expand     +perl              +title
+channel           +ipv6              +persistent_undo   -toolbar
+cindent           +job               +popupwin          +user_commands
-clientserver      +jumplist          +postscript        +vartabs
+clipboard         +keymap            +printer           +vertsplit
+cmdline_compl     +lambda            +profile           +vim9script
+cmdline_hist      +langmap           -python            +viminfo
+cmdline_info      +libcall           +python3           +virtualedit
+comments          +linebreak         +quickfix          +visual
+conceal           +lispindent        +reltime           +visualextra
+cryptv            +listcmds          +rightleft         +vreplace
+cscope            +localmap          +ruby              +wildignore
+cursorbind        +lua               +scrollbind        +wildmenu
+cursorshape       +menu              +signs             +windows
+dialog_con        +mksession         +smartindent       +writebackup
+diff              +modify_fname      +sodium            -X11
+digraphs          +mouse             +sound             -xattr
-dnd               -mouseshape        +spell             -xfontset
-ebcdic            +mouse_dec         +startuptime       -xim
+emacs_tags        -mouse_gpm         +statusline        -xpm
+eval              -mouse_jsbterm     -sun_workshop      -xsmp
+ex_extra          +mouse_netterm     +syntax            -xterm_clipboard
+extra_search      +mouse_sgr         +tag_binary        -xterm_save
-farsi             -mouse_sysmouse    -tag_old_static
   system vimrc file: "$VIM/vimrc"
     user vimrc file: "$HOME/.vimrc"
 2nd user vimrc file: "~/.vim/vimrc"
      user exrc file: "$HOME/.exrc"
       defaults file: "$VIMRUNTIME/defaults.vim"
  fall-back for $VIM: "/Users/uhooi/.local/vim/share/vim"
Compilation: gcc -c -I. -Iproto -DHAVE_CONFIG_H -DMACOS_X -DMACOS_X_DARWIN -I/opt/homebrew/include -I/opt/homebrew/Cellar/libsodium/1.0.18_1/include -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
Linking: gcc -L/usr/local/lib -o vim -lm -lncurses -L/opt/homebrew/Cellar/libsodium/1.0.18_1/lib -lsodium -liconv -framework AppKit -L/opt/homebrew/Cellar/lua/5.4.6/lib -llua5.4 -mmacosx-version-min=13.5 -fstack-protector-strong -L/opt/homebrew/opt/perl/lib/perl5/5.38/darwin-thread-multi-2level/CORE -lperl -L/Users/uhooi/.pyenv/versions/3.12.0/lib/python3.12/config-3.12-darwin -lpython3.12 -lintl -L/opt/homebrew/lib -Wl,-rpath,/opt/homebrew/lib -framework CoreFoundation -lruby.3.2 -L/Users/uhooi/.rbenv/versions/3.2.2/lib
```

## トラブルシューティング

トラブルシューティングです。

上記に書いた通り、ほとんどの失敗は `./configure` なので、そのログを見ればわかります。
ログは `src/auto/config.log` に出力されます。

### cscopeが+にならない

`cscope` が `-` になる場合、以下の原因が考えられます。

- `./configure` で `--enable-cscope` を指定していない
- `./configure` が途中で失敗している

### gettextが+にならない

`gettext` が `-` になる場合の解決策は私もわかっていません。
わかる方がいたらコメントなどで教えていただけると嬉しいです。

[こちらの記事](https://qiita.com/zurazurataicho/items/491a460a1842037db13c#ハマり処-nlsgettext) と同様、 `libintl.h` ファイルが見つかりませんでした。

```:src/auto/config.log
configure:15758: checking --disable-nls argument
configure:15770: result: no
configure:15780: checking for msgfmt
configure:15801: found /opt/homebrew/bin/msgfmt
configure:15812: result: msgfmt
configure:15820: checking for NLS
configure:15838: gcc -o conftest -g -O2 -I/opt/homebrew/Cellar/libsodium/1.0.18_1/include  -DMACOS_X -DMACOS_X_DARWIN  -L/usr/local/lib conftest.c  >&5
conftest.c:165:10: fatal error: 'libintl.h' file not found
#include <libintl.h>
         ^~~~~~~~~~~
1 error generated.
```

私はHomebrewを使っているので、 `CFLAGS='-I/opt/local/include'` でなく `CFLAGS='-I/opt/homebrew/include'` を指定してみます。
上記のパスに `libintl.h` が存在することも確認しました。

```diff_shell
$ ./configure \
  # ... \
+ CFLAGS='-I/opt/homebrew/include'
```

これで `libintl.h` は見つかるようになりましたが、 `intl` が見つかりませんでした。

<details><summary>config.log</summary>

```
configure:15758: checking --disable-nls argument
configure:15770: result: no
configure:15780: checking for msgfmt
configure:15801: found /opt/homebrew/bin/msgfmt
configure:15812: result: msgfmt
configure:15820: checking for NLS
configure:15838: gcc -o conftest -I/opt/homebrew/include -I/opt/homebrew/Cellar/libsodium/1.0.18_1/include  -DMACOS_X -DMACOS_X_DARWIN  -L/usr/local/lib conftest.c  >&5
ld: Undefined symbols:
  _libintl_gettext, referenced from:
      _main in conftest-afe59f.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)
configure:15838: $? = 1
configure: failed program was:
| /* confdefs.h */
| #define PACKAGE_NAME ""
| #define PACKAGE_TARNAME ""
| #define PACKAGE_VERSION ""
| #define PACKAGE_STRING ""
| #define PACKAGE_BUGREPORT ""
| #define PACKAGE_URL ""
| #define UNIX 1
| #define HAVE_SYS_WAIT_H 1
| #define HAVE_UNSIGNED_LONG_LONG_INT 1
| #define HAVE_LONG_LONG_INT 1
| #define HAVE_TIMER_CREATE 1
| #define HAVE_STDIO_H 1
| #define HAVE_STDLIB_H 1
| #define HAVE_STRING_H 1
| #define HAVE_INTTYPES_H 1
| #define HAVE_STDINT_H 1
| #define HAVE_STRINGS_H 1
| #define HAVE_SYS_STAT_H 1
| #define HAVE_SYS_TYPES_H 1
| #define HAVE_UNISTD_H 1
| #define HAVE_SYS_TIME_H 1
| #define HAVE_SYS_SELECT_H 1
| #define HAVE_SYS_SOCKET_H 1
| #define STDC_HEADERS 1
| #define HAVE_AVAILABILITYMACROS_H 1
| #define HAVE_DISPATCH_DISPATCH_H 1
| #define HAVE_XATTR 1
| #define FEAT_HUGE 1
| #define USE_XSMP_INTERACT 1
| #define FEAT_DIFF 1
| #define FEAT_LUA 1
| #define FEAT_PERL 1
| #define FEAT_PYTHON3 1
| #define FEAT_RUBY 1
| #define FEAT_CSCOPE 1
| #define FEAT_IPV6 1
| #define HAVE_INET_NTOP 1
| #define FEAT_NETBEANS_INTG 1
| #define FEAT_JOB_CHANNEL 1
| #define FEAT_TERMINAL 1
| #define HAVE_DATE_TIME 1
| #define HAVE_ATTRIBUTE_UNUSED 1
| #define HAVE_DIRENT_H 1
| #define HAVE_STDINT_H 1
| #define HAVE_STDLIB_H 1
| #define HAVE_STRING_H 1
| #define HAVE_SYS_SELECT_H 1
| #define HAVE_SYS_UTSNAME_H 1
| #define HAVE_TERMCAP_H 1
| #define HAVE_FCNTL_H 1
| #define HAVE_SGTTY_H 1
| #define HAVE_SYS_IOCTL_H 1
| #define HAVE_SYS_TIME_H 1
| #define HAVE_SYS_TYPES_H 1
| #define HAVE_ICONV_H 1
| #define HAVE_INTTYPES_H 1
| #define HAVE_LANGINFO_H 1
| #define HAVE_MATH_H 1
| #define HAVE_UNISTD_H 1
| #define HAVE_ERRNO_H 1
| #define HAVE_SYS_RESOURCE_H 1
| #define HAVE_LOCALE_H 1
| #define HAVE_TERMIOS_H 1
| #define HAVE_LIBC_H 1
| #define HAVE_POLL_H 1
| #define HAVE_SYS_POLL_H 1
| #define HAVE_PWD_H 1
| #define HAVE_UTIME_H 1
| #define HAVE_SYS_PARAM_H 1
| #define HAVE_LIBINTL_H 1
| #define HAVE_LIBGEN_H 1
| #define HAVE_SYS_ACL_H 1
| #define HAVE_WCHAR_H 1
| #define HAVE_WCTYPE_H 1
| #define HAVE_SYS_SYSCTL_H 1
| #define HAVE_STRINGS_H 1
| #define NO_STRINGS_WITH_STRING_H 1
| #define TERMINFO 1
| #define HAVE_OSPEED 1
| #define HAVE_UP_BC_PC 1
| #define HAVE_DEL_CURTERM 1
| #define SYS_SELECT_WITH_SYS_TIME 1
| #define HAVE_SVR4_PTYS 1
| #define PTYRANGE0 "pqrstuvw"
| #define PTYRANGE1 "0123456789abcdef"
| #define HAVE_SIGCONTEXT 1
| #define HAVE_FCHDIR 1
| #define HAVE_FCHOWN 1
| #define HAVE_FCHMOD 1
| #define HAVE_FSYNC 1
| #define HAVE_GETCWD 1
| #define HAVE_GETPWENT 1
| #define HAVE_GETPWNAM 1
| #define HAVE_GETPWUID 1
| #define HAVE_GETRLIMIT 1
| #define HAVE_GETTIMEOFDAY 1
| #define HAVE_LOCALTIME_R 1
| #define HAVE_LSTAT 1
| #define HAVE_MEMSET 1
| #define HAVE_MKDTEMP 1
| #define HAVE_NANOSLEEP 1
| #define HAVE_OPENDIR 1
| #define HAVE_PUTENV 1
| #define HAVE_QSORT 1
| #define HAVE_READLINK 1
| #define HAVE_SELECT 1
| #define HAVE_SETENV 1
| #define HAVE_GETPGID 1
| #define HAVE_SETPGID 1
| #define HAVE_SETSID 1
| #define HAVE_SIGALTSTACK 1
| #define HAVE_SIGSET 1
| #define HAVE_SIGSETJMP 1
| #define HAVE_SIGACTION 1
| #define HAVE_SIGPROCMASK 1
| #define HAVE_SIGVEC 1
| #define HAVE_STRCASECMP 1
| #define HAVE_STRCOLL 1
| #define HAVE_STRERROR 1
| #define HAVE_STRFTIME 1
| #define HAVE_STRNCASECMP 1
| #define HAVE_STRPBRK 1
| #define HAVE_STRPTIME 1
| #define HAVE_STRTOL 1
| #define HAVE_TGETENT 1
| #define HAVE_TOWLOWER 1
| #define HAVE_TOWUPPER 1
| #define HAVE_ISWUPPER 1
| #define HAVE_TZSET 1
| #define HAVE_USLEEP 1
| #define HAVE_UTIME 1
| #define HAVE_UTIMES 1
| #define HAVE_MBLEN 1
| #define HAVE_FTRUNCATE 1
| #define HAVE_UNSETENV 1
| #define HAVE_POSIX_OPENPT 1
| #define HAVE_CLOCK_GETTIME 1
| #define SELECT_TYPE_ARG1 int
| #define SELECT_TYPE_ARG234 (fd_set *)
| #define SELECT_TYPE_ARG5 (struct timeval *)
| #define HAVE_FSEEKO 1
| #define HAVE_SODIUM 1
| #define HAVE_ST_BLKSIZE 1
| #define ST_MTIM_NSEC st_mtimespec.tv_nsec
| #define HAVE_ICONV 1
| #define HAVE_NL_LANGINFO_CODESET 1
| #define HAVE_LIBM 1
| #define HAVE_ISINF 1
| #define HAVE_ISNAN 1
| #define HAVE_POSIX_ACL 1
| #define HAVE_FD_CLOEXEC 1
| #define HAVE_RENAME 1
| #define HAVE_DIRFD 1
| #define HAVE_FLOCK 1
| #define HAVE_SYSCTL 1
| #define HAVE_SYSCONF 1
| #define SIZEOF_INT 4
| #define SIZEOF_LONG 8
| #define SIZEOF_TIME_T 8
| #define SIZEOF_OFF_T 8
| #define VIM_SIZEOF_INT 4
| #define VIM_SIZEOF_LONG 8
| #define USEMEMMOVE 1
| /* end confdefs.h.  */
| #include <libintl.h>
| int
| main (void)
| {
| gettext("Test");
|   ;
|   return 0;
| }
configure:15855: gcc -o conftest -I/opt/homebrew/include -I/opt/homebrew/Cellar/libsodium/1.0.18_1/include  -DMACOS_X -DMACOS_X_DARWIN  -L/usr/local/lib conftest.c -lintl >&5
ld: library 'intl' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```

</details>

こちらの解決策はまだわかりません。
`intltool` をインストールしても変わりませんでした。
`CFLAGS='-I/opt/homebrew/Cellar/gettext/0.22.3/include` も試しましたが変わりませんでした。

### luaが+にならない

`lua` が `-` になる場合、以下の原因が考えられます。

- `./configure` の `--with-lua-prefix` で指定したパスに `include/lua{バージョン}/lua.h` が存在しない
- `./configure` が途中で失敗している

## おまけ

おまけです。

### ビルドスクリプトの作成

私はVimのビルドとインストールを頻繁に行うので、ビルドスクリプトを作成してdotfilesリポジトリへコミットしています。

https://github.com/uhooi/dotfiles/blob/27b15fb7d4654b0866f914496b67ef5a89316750/make-vim.sh

## おわりに

これでVimのHEADをビルドしてインストールできました。
毎朝温かみのある手動ビルドをして、常に最新のVimを使っていきましょう :relaxed:

## 参考リンク

- [vim-jp » Linuxでのビルド方法](https://vim-jp.org/docs/build_linux.html)
- https://github.com/vim/vim/blob/5a679b2263f597950f99c60a99d4d1a192e9f639/src/INSTALL
- https://github.com/Homebrew/homebrew-core/blob/a24f6cc2bf5fffbd656b3bfdad127327a620b0fe/Formula/v/vim.rb
- [homebrew - Using the brew install of lua, make file can't find lua.h - Stack Overflow](https://stackoverflow.com/questions/74501445/using-the-brew-install-of-lua-make-file-cant-find-lua-h)
- [macOSでvim8.1をlua付きでソースからビルドする #Mac - Qiita](https://qiita.com/zurazurataicho/items/491a460a1842037db13c)
- [jedisct1/libsodium: A modern, portable, easy to use crypto library.](https://github.com/jedisct1/libsodium)
- [ncurses - Wikipedia](https://ja.wikipedia.org/wiki/Ncurses)
- [第 8 章 cscope: 対話的な C プログラムの検査 (Sun Studio 12: C ユーザーズガイド)](https://docs.oracle.com/cd/E19205-01/820-1209/bjanq/index.html)