関数定義や参照へキーひとつで飛ぶことのできるタグジャンプ。いまやコードリーディングに無くてはならない機能ですが、これを可能にするのがCtagsやGNU GLOBAL (Gtags) といった「インデクサー」です。
本記事では、C/C++用の優れたインデクサーであるRtagsのセットアップの方法、そしてVimとの連携の仕方について書いていきます。
Rtagsの利点
インデクサーは、ソースコードをパースして関数や変数などのシンボル名と位置を記録したタグ(インデックス)データベースを作成するプログラムです。
Ctagsは現在最も広く使われているインデクサーの一つで、Cを始めとしてRubyやPythonなど、40あまりの言語に対応しています。ところがC++では文法の複雑さゆえに内部のパーサーが十分に対応できず、正しくシンボル名を認識できなかったりします。Gtagsも同様の問題を抱えています。
そこで最近登場したのがRtagsです。RtagsはC/C++コンパイラであるclangの構文解析インターフェースを利用しているのが特徴です。コンパイラがソースコードのパースをやるわけですからミスは起こりえませんし、非常に合理的です。
Rtagsのインストール
インストール自体はビルドが必要なだけで、特に難しくはありません。
必要なパッケージ
- libclang >= 3.2 (3.5以上推奨)
- GCC >=4.7 もしくは clang >= 3.2
- CMake >= 2.8
私のUbuntu-14.04では下記のコマンドで入りました。llvm-configでlibclangのバージョンを調べると、3.4であることが分かります。
$ sudo apt-get install clang libclang-dev cmake
$ llvm-config --version
3.4
このままでも問題ありませんが、libclangは3.5以上推奨とありますので、3.6を入れてみるのも良いでしょう。
$ sudo apt-get install libclang-3.6-dev
$ /usr/lib/llvm-3.6/bin/llvm-config --version
3.6
この場合はあとでcmakeを行う際に、下記のようにLIBCLANG_LLVM_CONFIG_EXECUTABLE
に3.6のllvm-configのパスを与えて実行する必要があります。これを忘れるとデフォルトの3.4が使われてしまいます。
$ LIBCLANG_LLVM_CONFIG_EXECUTABLE=/usr/lib/llvm-3.6/bin/llvm-config cmake ..
ビルド
レポジトリをクローンして、依存するサブモジュールを更新します。
$ git clone https://github.com/Andersbakken/rtags
$ cd rtags
$ git submodule init
$ git submodule update
cmakeでMakefileを生成しビルドします。buildディレクトリを作ってそこからout-of-sourceビルドします。
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
out-of-sourceビルドって何?という方はこちらの記事が詳しいです。
Rtagsのセットアップ
インストールが完了すると、rc
とrdm
とというコマンドが使えるようになっているはずです。Rtagsはクライアント・サーバー型のタグシステムで、以下がインデクシングのおおまかな流れになります。
-
rdm
を起動 rc
でインデクシング対象ソースファイルのコンパイルコマンドを与える-
rdm
がrc
から上記のコマンドを受け取り、インデクシングを行う
rcにコンパイルコマンドを与える方法が一番の肝です。おおまかに2種類の方法があります。
-
rc -c
コマンドで直接渡す- 1-1. 直接手打ち
- 1-2. ラッパースクリプトを使う
-
rc -J
コマンドでcompile_commands.jsonファイルで渡す- 2-1. cmake / ninjaを使う
- 2-2. bearを使う ★オススメ!
方法1: rc -c
コマンドで直接渡す
対象となるソースファイルのコンパイルコマンドを、下記のようにrc
に直接渡します。
$ rc -c g++ a.cpp b.cpp
数個のソースファイルならこれでも良いですが、大抵の場合は毎回このようにコマンドを打つのは苦痛です。そこで、作者が用意しているラッパースクリプトを使用する方法があります。以下のようにするのがおすすめです。
## 自分のホームディレクトリにbinディレクトリを作成
$ mkdir ~/bin
## サーチパスの先頭におく
$ echo 'export PATH=${HOME}/bin:${PATH}' >> .bashrc
## ラッパースクリプトをコピー
$ cp rtags/bin/gcc-rtags-wrapper.sh ~/bin/gcc
$ cp rtags/bin/gcc-rtags-wrapper.sh ~/bin/g++
このようにすると、gcc, g++コマンドを打つたびに実際にはこのラッパースクリプトが呼ばれ、内部でrc -c
を実行してくれます。コンパイルのたびにインデクシングが行われて大変便利です。もちろんgcc, g++の動作に影響は与えません。
方法2: rc -J
コマンドでcompile_commands.jsonを読み込ませる
compile_commands.jsonは、JSON Compilation Databaseというもので、コンパイルに必要な情報を記録したJSONファイルです。このファイルをrc
に読み込ませることでインデクシングを行うことができます
cmakeやninjaといった最近のビルドシステムはビルド時にJSON Compilation Databaseを出力することができますので、下記のようにすればOKです。
## cmakeを使う場合
$ cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=1
$ rc -J .
## ninjaを使う場合
$ ninja -t commands | rc -c -
小規模なプロジェクトでGNU Makeしか使っていないような場合は、Bearというツールの助けを借りることでCompilation Databaseを出力することができます。このやり方が個人的には最もオススメです。
下記のようにビルド&インストールします。
$ git clone https://github.com/rizsotto/Bear
$ cd Bear
$ mkdir build && cd build
$ cmake ..
$ make && sudo make install
使い方は簡単で、通常のmakeコマンドの前にbearと打つだけです。これでMakefileより呼ばれたコンパイルコマンドをもとにCompilation Databaseが出力されます。
$ bear make
$ rc -J .
RtagsをVimから使う
vim-rtagsというプラグインがあります。まだ開発中とのことですが、現時点でも十分な機能を備えています。
インストール・設定
Python拡張を使用していますので+pythonが必須です。NeoBundleで簡単に入ります。
NeoBundle 'lyuts/vim-rtags'
デフォルトのキーマップはストローク数が多くてちょっと打ちづらいです。plugin/rtags.vimにマッピングを行っている箇所があるので、これを参考にして別のキーに割り当てると良いでしょう。私は下記のように設定しています。
" 定義へジャンプ
nnoremap <silent> <F3> :call rtags#JumpTo()<CR>
" 参照へジャンプ
nnoremap <silent> <F4> :<C-u>Unite<Space>rtags/references<CR>
" クラス・関数定義内にいたらその先頭へジャンプ
nnoremap <silent> <F5> :call rtags#JumpToParent()<CR>
使い方
すでにcompile_commands.jsonを出力し、rc -J .
を一度は実行させているとします。rdm
が起動していなければ起動しておきましょう。
$ rdm --daemon
実際に動かしてみるとこんな感じです。
定義へのジャンプはもちろん、参照へのジャンプも自在です。参照のリストはUniteのウィンドウで開かれるので、絞り込みも簡単です。また、rdmはファイルの変更も監視していて、ソースファイルを保存するとインデックスも同時に自動的に更新されます。
RtagsをEmacsから使う
Rtagsには作者製のEmacs用のプラグインが同梱されています。こちらやこちらにすでに詳しい解説がありますので、ご参照ください。
終わりに
以上、C/C++用インデクサー "Rtags" の使い方について説明しました。テキストエディタでIDEと同等以上のコードナビゲーションが可能となり、非常に捗ります。セットアップは少し面倒ですが、C++をメイン言語としている方は導入の価値は大いにあると思います。