Intro
Emacs Advent Calendar 2015の第3日目の記事です。
皆さんはC++でコーディングするとき何のエディタでコーディングしていますか?Emacs Advent Calendarで愚問でしたね。答えはもちろんEmacsかと思います。Emacsが最高のエディタであることに疑問の余地はないのですが、ことC++のコーディング環境となるとVisual Studio/Eclipse/ClionなどのIDE系と比較してコードの追跡や補完・デバッグの環境で1歩遅れているのかもと感じずにはいられません。もちろん私がEmacsのポテンシャルを活かしきれてないのもありますが。。。
この記事ではLinuxでIDE系との差を埋めるためのC++開発環境を紹介したいと思います。
Tools
Emacs C++開発環境では以下のツールを使用します。
- LibClang
- CMake
- RTags
- Irony-Mode
- cmake-ide
LibClangはclangベースのツールを開発するためのCライブラリです。最近のC++関連のツールの目覚しい進歩の立役者です。
CMakeはC++用のクロスプラットフォーム・ビルド管理ツールです。ビルド管理ツールとしてはデファクト・スタンダードになりつつあるツールで、もし皆さんがC++オープン・ソースに携わっている場合はそのプロジェクトをCMakeでビルドできるようにすることを考慮してみてください。2014年のAdvent Calendarで詳しく紹介されています。
https://cmake.org/
http://qiita.com/advent-calendar/2014/cmake
RTagsはclangベースのC++ソース・コード・インデクサーです。ソース・コードを解析して、宣言、定義、参照などをデータベースに保存し検索・ジャンプ等できるようにするツールです。同様のツールではGNUGLOBAL/CSCOPE/etagsが有名です。C++は博士論文で研究されるほど文法が複雑でパースが難しい言語として悪名を欲しいままにしています。そのためインデクサーの開発は非常に困難だったのですが、clangがC++の抽象構文木の情報を提供してくれるおかげで精度の高いツールの開発が可能になりました。それがRTagsになります。
Irony-Modeはclangベースのコード補完&解析レポート ツールです。コード補完機能としては、company-mode/auto-completeのbackendとして動作します。また、解析レポートとしては、flycheckと連動して、コンパイル警告をコード中に表示してくれます。
cmake-ideはCMakeで管理されたプロジェクト用にツールの設定をしてくれる便利パッケージです。開発者がCPPCON2015でライトニング・トークをしているので興味があるかたは是非見てください。
https://github.com/atilaneves/cmake-ide
https://github.com/CppCon/CppCon2015/blob/master/Lightning%20Talks%20and%20Lunch%20Sessions/Emacs%20as%20a%20C++%20IDE/Emacs%20as%20a%20C++%20IDE%20-%20Atila%20Neves%20-%20CppCon%202015.pdf
https://www.youtube.com/watch?v=5FQwQ0QWBTU
ツールのインストール
LibClang
LibClangのビルド方法を書くにはこの記事の余白はあまりにも狭い、否、私の時間があまりに足りないので割愛します。(この時点で現在12/03(木)の0:30AM)もしリクエストが有ればあとで書きます。
CMake
プロジェクトのページからCMake 3.4.0をダウンロードしてパスを通します。
wget -qO- https://cmake.org/files/v3.4/cmake-3.4.0-Linux-x86_64.tar.gz | tar xvz &
cmake-3.4.0-Linux-x86_64/bin下のコマンドへ、パスが通ったディレクトリからシンボリック・リンクをはりましょう。
RTags
RTagsはC++で書かれたclient/serverとelispから構成されます。
RTagsのelispはEmacsのパッケージ・マネージャからインストールできます。M-x list-packagesでパッケージ一覧からrtagsを検索してインストールしてください。
続いてclient/serverをビルドしてインストールします。
# Step 1: レポジトリのクローン
git clone --recursive https://github.com/Andersbakken/rtags.git &
# Step 2: ビルド用のディレクトリを作成(プログラマたるものout-of-source-tree buildしかしちゃダメ)
mkdir build_rtags && pushd build_rtags
# Step 3: ビルド用のmakeファイルを生成
LIBCLANG_LLVM_CONFIG_EXECUTABLE=path_to_llvm-config CC=gcc CXX=g++ cmake ../rtags -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_INSTALL_PREFIX=where_to_install_rtags
# Step 4: ビルド & インストール
cmake --build ./ --target install
Step 3の補足をします。path_to_llvm-configはLibClangのインストール時にインストールされたコマンドllvm-configへのパスです。llvm-configがビルドに必要な情報を提供してくれます。
CC/CXXはrtagsをビルドするためのコンパイラです。LibClangをインストール時にclang/clang++もインストールされているかもしれませんが、私の環境ではclangでビルドしたrtagsは何故かクラッシュするので、明示的にgcc/g++を使用します。
CMAKE_EXPORT_COMPILE_COMMANDS=ONはcmakeにcompalation databaseを生成するように指示しています。compilation databaseはプロジェクトの各ソース・ファイルのコンパイル・オプションを保存したJSONファイルです。C++ではソース・ファイルがどのようにコンパイルされるか知らないと正確にコードを解析することはできません。compalation databaseはRTags/Irony-Modeなどのツールにコンパイル・オプション情報を渡すための手段になります。
where_to_install_rtagsはrtagsをインストールするディレクトリへのパスです。Step 4が成功すると、ディレクトリwhere_to_install_rtags下のbinに必要なコマンドが配置されます。またbin下のコマンドへパスを通してください。
Step 3で依存関係の解決に失敗してMakeファイルの生成に失敗した場合は、適宜必要なパッケージをインストールしてください。
Irony-Mode
Irony-ModeはRTagsと同様C++で書かれたserverとelispのパッケージから構成されます。また関連する複数のelispパッケージがあります。Emacsのパッケージ・マネージャで以下のパッケージをインストールします
irony
irony-eldoc
company-irony
flycheck-irony
Irony-Modeのserverをビルドしてインストールします。コマンドirony-install-serverを実行します。ビルド用のcmakeコマンドがmini bufferに表示されるので環境に合わせてコマンドを修正します。
LLVM_CONFIG=path_to_llvm-config cmake -DCMAKE_INSTALL_PREFIX\=where_to_install_irony path_to_irony_server_source_dir && cmake --build . --use-stderr --config Release --target install
コマンドはRTagsとほぼ同じです。llvm-config情報を渡すための変数がLLVM_CONFIGであることが注意点です。path_to_irony_server_source_dirはirony-serverのソース・ディレクトリへのパスでironyが環境に合わせて適切な値に設定しています。
コマンドが成功するとディレクトリwhere_to_install_irony下のbinにirony-serverというコマンドが配置されるのでパスを通します。
cmake-ide
最後にcmake-ideですが、cmake-ideは純粋なelispパッケージなのでパッケージ・マネージャでインストールします。
ツールの設定
インストールしたelispパッケージを設定します。initファイルに以下を追加してください。
(use-package flycheck
:config
(progn
(add-hook 'flycheck-mode-hook #'flycheck-irony-setup)
)
)
(use-package cmake-ide
:bind
(("<f9>" . cmake-ide-compile))
:config
(progn
(setq
; rdm & rcコマンドへのパス。コマンドはRTagsのインストール・ディレクトリ下。
cmake-ide-rdm-executable "path_to_rdm"
cmake-ide-rc-executable "path_to_rc"
)
)
)
(use-package rtags
:config
(progn
(rtags-enable-standard-keybindings c-mode-base-map)
; 関数cmake-ide-setupを呼ぶのはrtagsをrequireしてから。
(cmake-ide-setup)
)
)
(defun my-irony-mode-hook ()
(define-key irony-mode-map
[remap completion-at-point]
'irony-completion-at-point-async)
(define-key irony-mode-map
[remap complete-symbol]
'irony-completion-at-point-async)
)
(use-package irony
:config
(progn
; ironyのビルド&インストール時にCMAKE_INSTALL_PREFIXで指定したディレクトリへのパス。
(setq irony-server-install-prefix "where_to_install_irony")
(add-hook 'irony-mode-hook 'company-irony-setup-begin-commands)
(add-hook 'irony-mode-hook 'my-irony-mode-hook)
(add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)
(add-hook 'irony-mode-hook 'irony-eldoc)
(add-to-list 'company-backends 'company-irony)
)
)
(add-hook 'c-mode-common-hook 'flycheck-mode)
(add-hook 'c++-mode-hook 'irony-mode)
次に個別プロジェクトの設定を行います。今回は試しに先程git cloneしたRTagsプロジェクトに対して設定してみます。
まずはcmake-ideの設定です。RTagsプロジェクトのルート・ディレクトリ(.gitディレクトリを含むディレクトリ)をdiredで開きます。M-x add-dir-local-variableで変数cmake-ide-dirにcommpilation databaseのパス(上記のビルドをしたcompile_commands.jsonを含むディレクトリ)を設定します。cmake-ideは自動でRTagsの設定を行ってくれます。
次にirony-modeの設定を行います。コマンドirony-cdb-json-add-compile-commands-pathで、まずProject rootにRTagsプロジェクトのルート・ディレクトリ、Compile commandsにcompile_commands.jsonのパス(ディレクトリではなくjsonファイル自体のパス)を設定します。
最後に
これで準備完了です! Emacsを再起動してみてください。全て順調に活けば*rdm*というバッファでrdm(RTagsのサーバ)があなたのクエリを待ち構えてくれているはずです。
RTagsプロジェクトのソースを開いて、"C-c r ."rtags-find-symbol-at-pointで関数などのシンボルの宣言と定義を行き来したり、"C-c r /"rtags-find-all-references-at-pointでシンボルが参照されている箇所の一覧が見れます。また、を押すと指定したビルド・ディレクトリでビルドが開始されコードの変更はすぐにrdmに反映されます。
またcompany-modeで型やシグネチャ付きの補完が表示されるはずです。
時間がなく駆け足になってしまいましたが、これで少しはEmacsをIDEに近づけることができたでしょうか。上記のモードには、まだ便利な使い方が色々あるのでドキュメントなどを頼りにあなた好みの環境に仕上げていってください。
PS. 全然、関係ありませんが、どなたかorg-modeの記事をQiitaに投稿するのに便利な方法しりませんかね。。。