GCCSense ユーザーマニュアル

Index

[English]



1. はじめに

1.1. GCCSenseとは?

GCCSenseはGCCのコード解析器を利用したC/C++のための最も賢い開発援助ツールです。コンパイラの情報を利用するため非常に高精度なコード補完などが可能になります。独立したプログラムとして動作するので理論的にはどのエディタからも利用することができます。

1.2. 特徴

2. ダウンロード

GCCSenseのトップページからダウンロードできます。

3. インストール

インストールの前に上記のダウンロードページから適切なバージョンのGCCSenseとgcc-code-assistをダウンロードしておいてください。

3.1. 必要事項

GCCをビルドできるUNIX系システムが必須になります。現状Windows対応を行っていませんが、MinGWと組み合せれば動作するはずなので、将来的には対応する予定です。

3.2. 準備

GCCSenseの動作およびGCCのビルドに必要なパッケージをインストールしておきます。

3.2.1. Debian系システム

$ sudo apt-get install build-essential libgmp3-dev libmpfr-dev flex ruby rubygems libsqlite3-ruby

3.2.2. FreeBSD系システム

次のportsパッケージをインストールしておく必要があります。

次のようにインストールしてください。

# foreach p ( textproc/flex lang/ruby18 devel/ruby-gems databses/sqlite3 databases/rubygem-sqlite3 math/libgmp4 math/mpfr devel/gmake )
cd /usr/ports/$p
make install
end

3.2.3. Mac OS X

MacPortsをインストールしておく必要があります。

$ sudo port install gmp mpfr flex rb-rubygems
$ sudo gem install sqlite3-ruby

3.3. gcc-code-assistのインストール

gcc-code-assistはコード補完などの機能を搭載した独自のGCCです。基本的にはGCCのインストール手順に従いますが、ここではいくつかのプラットフォームに限って簡単に説明します。

手順の中で一番重要なのはインストール先とプログラムサフィックスです。インストール先は特に指定しない場合は/usr/localになりますが、一般ユーザーでインストールする場合はconfigureスクリプトに--prefix=$HOMEなどを設定します。その場合は$HOME/binPATH環境変数に設定する必要があります。

プログラムサフィックスは必須とまでは言えませんが、既存のGCCと独自のGCCが干渉しないようプログラム名を一意にしたほうが良いでしょう。GCCSenseではプログラムサフィックスとして-code-assistを推奨しています。これによりgccgcc-code-assistになり、g++g++-code-assistになります。プログラムサフィックスを指定するにはconfigureスクリプトに--program-suffix=-code-assistのように指定します。

最近のマシンなら30分ぐらいでビルドできます。

インストール後はgcc-code-assistg++-code-assistが動作することを確認してください。

$ gcc-code-assist --version
$ g++-code-assist --version

3.3.1. Debian系システム

$ tar xvjf gcc-code-assist-*.tar.bz2
$ cd gcc-code-assist-*
$ ./configure --program-suffix=-code-assist --enable-languages=c,c++ --disable-bootstrap --disable-multilib
$ make # -j2
$ sudo make install

3.3.2. FreeBSD系システム

# tar xvjf gcc-code-assist-*.tar.bz2
# cd gcc-code-assist-*
# ./configure --program-suffix=-code-assist --enable-languages=c,c++ --disable-bootstrap --disable-multilib
# gmake # -j2
# gmake install

3.3.3. Mac OS X

$ tar xvjf gcc-code-assist-*.tar.bz2
$ cd gcc-code-assist-*
$ ./configure --program-suffix=-code-assist --enable-languages=c,c++ --disable-bootstrap --disable-multilib --disable-nls --with-gmp-include=/opt/local/include --with-gmp-lib=/opt/local/lib --with-mpfr-include=/opt/local/include --with-mpfr-lib=/opt/local/lib --with-libiconv-prefix=/usr
$ make # -j2
$ sudo make install

3.4. GCCSenseのインストール

GCCSenseのインストールは単純で、binディレクトリをPATH環境変数に設定するか、binディレクトリ直下のプログラムをPATH環境変数に設定されたディレクトリにコピーするだけです。通常は後者のほうが楽でしょう。この作業は必須ではありませんが、これを行わなければエディタの設定が必要になります。

$ cd gccsense-*
$ sudo cp bin/* /usr/local/bin/

ここで/usr/local/binPATH環境変数に設定されていることを想定しています。特に気にしなければ/usr/binでも良いでしょう。

最後にgccrecautopchが動作することを確認してください。

$ gccrec --version
gccrec 0.0.1
$ autopch --version
autopch 0.0.1

3.5. Emacs拡張のインストール

Emacs拡張はetc/gccsense.elにあります。load-pathの通ったディレクトリにコピーするだけでインストールできます。

$ cp etc/gccsense.el ~/.emacs.d/

~/.emacsに次のコードを書いておきます。

(require 'gccsense)

3.6. Vimプラグインのインストール

Vimプラグインはetc/gccsense.vimにあります。~/.vim/pluginにコピーするだけでインストールできます。特に設定する必要はありません。

$ cp etc/gccsense.vim ~/.vim/plugin

4. 使い方

GCCSenseは次の4つの部品が協調することで動作します。読んでおくべき節を強調してあります。

それぞれの使い方について詳しく説明します。

4.1. gcc-code-assist

gcc-code-assistはコード補完などの機能を搭載した独自のGCCです。GCC 4.4がベースになっており上位互換性があります。現状利用できる拡張機能は次のようになります。

4.1.1. コード補完

-code-completion-atオプションで補完位置を指定することにより、その位置で有効な補完候補を出力することができます。オプションには<file>:<line>:<column>という形式の値を指定します。<file>とコンパイラに渡すファイル名は完全に一致している必要があります。<line>および<column>は1ベースです。例を示します。

$ nl test.cpp
     1  #include <string>
     2  int main()
     3  {
     4      std::string s;
     5      s.
     6  }
$ # test.cppの5行目7カラム目(ドットの直後)でcから始まる補完候補を出力する
$ g++-code-assist -fsyntax-only -code-completion-at=test.cpp:5:7 test.cpp 2> /dev/null | grep '^completion: c'
completion: capacity "typename _Alloc::rebind<_CharT>::other::size_type std::basic_string<_CharT, _Traits, _Alloc>::capacity() const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: clear "void std::basic_string<_CharT, _Traits, _Alloc>::clear() [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: copy "typename std::basic_string<_CharT, _Traits, _Alloc>::size_type std::basic_string<_CharT, _Traits, _Alloc>::copy(_CharT*, typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: c_str "const _CharT* std::basic_string<_CharT, _Traits, _Alloc>::c_str() const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: compare "int std::basic_string<_CharT, _Traits, _Alloc>::compare(const std::basic_string<_CharT, _Traits, _Alloc>&) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: compare "int std::basic_string<_CharT, _Traits, _Alloc>::compare(typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type, const std::basic_string<_CharT, _Traits, _Alloc>&) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: compare "int std::basic_string<_CharT, _Traits, _Alloc>::compare(typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type, const std::basic_string<_CharT, _Traits, _Alloc>&, typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: compare "int std::basic_string<_CharT, _Traits, _Alloc>::compare(const _CharT*) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: compare "int std::basic_string<_CharT, _Traits, _Alloc>::compare(typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type, const _CharT*) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"
completion: compare "int std::basic_string<_CharT, _Traits, _Alloc>::compare(typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type, const _CharT*, typename _Alloc::rebind<_CharT>::other::size_type) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]"

補完候補の出力はcompletion: <name> "<summary>"という形式になります。

現状、CとC++でコード補完を利用できます。言うまでもないですが、GCC 4.4でコンパイルできるコードなら、どんなコードでもコード補完を利用できます。Cでは.->の直後、C++ではそれに加えて::の直後でのコード補完に対応しています。

4.2. gccrec

前節のコード補完コマンドを利用すれば、エディタでのコード補完は難しい話ではないように思えるでしょう。基本的にはその通りなのですが、やっかいなのはGCCのコンパイルオプションをエディタが検知できない点です。次のコマンドを考えてみてください。

$ gcc -I../some/include/path -DSOME_SWITCH -c test.cpp

test.cpp../some/include/pathから何か重要なヘッダをインクルードしているかもしれませんし、SOME_SWITCHが有効でないとエラーが起きるかもしれません。おそらくこれらのオプションなしではコード補完を行う前にエラーで終了してしまうでしょう。しかも普通は、このようなコマンドはMakefileの中に埋もれてしまっており、有効なコンパイルオプションを取得するのが困難になっています。

gccrecはそのような問題を解決するためのGCCラッパーです。GCCラッパーとはGCCとして振る舞うが、その前後で特別な処理を行うプログラムのことを言います。gccrecはGCCに処理を移譲する前に、ソースファイルとコンパイルオプションの対応をデータベースに記録します。記録後はソースファイルを与えるだけで、データベースから有効なコンパイルオプションを取得して、記録されたコマンドと同様のコマンドを再生できます。

例として次のコマンドを記録することを考えてみましょう

$ gcc -I../some/include/path -DSOME_SWITCH -c test.c

記録するにはgccの前にgccrecを付けます。

$  gccrec gcc -I../some/include/path -DSOME_SWITCH -c test.c

gccrecはコンパイラオプションからソースファイル(ここではtest.c)取得し、そのソースファイルとコンパイルオプションの対応をデータベースに記録します。

データベースは~/.gccrecにSQLite3データベースとして記録されます。少し覗いてみましょう。

$ sqlite3 ~/.gccrec
sqlite> select file, args from gccrec;
/home/tomo/tmp/test.c|-I../some/include/path -DSOME_SWITCH -c

ファイルとコンパイルオプションの対応が記録されているのが分かります。記録したコマンドを再生するにはgccrec-rオプションとソースファイルを指定します。

$ gccrec -r test.c

-vオプションを指定することで、実際に実行されるコマンドを出力することもできます。

$ gccrec -v -r test.c
gcc -I../some/include/path -DSOME_SWITCH -c /home/tomo/tmp/test.c

もうお気付きだと思いますが、エディタはこのgccrecコマンドに編集中のファイル名を渡してコンパイルやコード補完を行います。

擬似的な例を示します。ユーザーは最初にgccrecでコンパイルオプションを記憶させておく必要があります。この際、gccg++でなくgcc-code-assistg++-code-assistで記録しておかないと、後からコード補完などが行えません。

$ gccrec g++-code-assist -c test.cpp

コード補完が要求されたら、エディタは内部で次のようなコマンドを実行します。

$ gccrec -v -r test.cpp -fsyntax-only -code-completion-at=$PWD/test.cpp:5:7
completion: ...

重要なのは最初にgccrecでコンパイルオプションを記憶させておくことです。これさえ一度やっておけば、後はプログラムが良きにはからってくれます。

4.2.1. ビルドツール対応

多くのプロジェクトではmakeなどのビルドツールを利用しているでしょう。そのようなプロジェクトで、上記のように手動でコマンドを記録していては日が暮れてしまいます。gccrecはそのような場合でもうまく対処できます。

Makefileを例にして考えます。一般的なMakefileでは次のように外部からCCCXXを与えることができます。

$ make CC='my-gcc' CXX='my-g++'

この特性を利用して全てのソースファイルのコンパイルオプションを記録することができます。

$ make clean
$ make CC='gccrec gcc-code-assist' CXX='gccrec g++-code-asssist'

記録中でも実際にコンパイルが行われますが、これは記録を極力成功させるためです。

~/.gccrecに大量のレコードが登録されると思います。コンパイルオプションが変更されたり、新しいファイルが追加されない限り、この作業は一度だけで済みます。

この手法により、WebKitMozilla FirefoxGoogle V8の全てのソースコードでコード補完ができるようになったことを確認しています。

4.3. autopch

autopchはGCCの解析速度を向上させるためのGCCラッパーです。自動的にプリコンパイルヘッダを適用するのでこのような名前になっています(AUTOmatic PreCompiled Header)。インクルードするヘッダファイルが巨大な場合などに顕著ですが、プリコンパイルヘッダを利用することでコンパイル速度をかなり改善することができます。コード補完などのレスポンス性能が求められる領域では、特にこの速度改善は重要です。

しかしGCCのプリコンパイルヘッダには「Translation unitにつき一つのプリコンパイルヘッダしかインクルードできない」という重大な制限があります。プリコンパイルヘッダの特性上、これは仕方がないのですが、通常は次のように二個以上のヘッダをインクルードしているでしょう。

test.cpp:

#include <iostream>
#include <string>
#include <vector>
int main() {...}

このような場合は、インクルード部分をstdafx.hのような特別なヘッダに括りだして、そのヘッダをプリコンパイルヘッダにするというのが常套手段になります。

stfafx.h:

#include <iostream>
#include <string>
#include <vector>

test.cpp:

#include <stdafx.h>
int main() {...}

プリコンパイル:

$ # 一度だけ
$ g++ stdafx.h

コンパイル:

$ g++ -c test.cpp

これによりコンパイル速度は向上するのですが、いかんせん手間がかかりすぎます。プリコンパイルヘッダの利用が増えないのは、この手間が一番の理由でしょう。autopchはまさにこの手間を自動化してくれるツールなのです。使い方は簡単で、gccrecと同様、gccの前にautopchを付けるだけです。なお-cオプションを渡さないと、リンクとみなされるので注意してください。

$ autopch g++ -c test.cpp

ヘッダをプリコンパイルする分、最初のコンパイルは遅くなりますが、二度目以降は速度が向上します。~/.autopchにヘッダの依存関係や実際のプリコンパイルヘッダが格納されていきます。プリコンパイルヘッダは巨大になりがちなのでautopchの多用は禁物です。

GCCSenseではコード補完などのレスポンス性能を改善するためにautopchを利用しています。

4.3.1. ベンチマーク

コード補完を想定した環境でどれだけ改善されるかの一例を示します。

$ # autopchなし
$ time g++ -fsyntax-only -c test.cpp
g++ -fsyntax-only -c test.cpp  4.49s user 0.19s system 95% cpu 4.915 tota
$ # autopchあり初回
$ time autopch g++ -fsyntax-only -c test.cpp
autopch g++ -fsyntax-only -c test.cpp  10.16s user 0.97s system 96% cpu 11.531 total
$ # autopchあり二回目
$ time autopch g++ -fsyntax-only -c test.cpp
autopch g++ -fsyntax-only -c test.cpp  0.42s user 0.06s system 94% cpu 0.504 total
$ # autopchあり三回目
time autopch g++ -fsyntax-only -c test.cpp
autopch g++ -fsyntax-only -c test.cpp  0.38s user 0.10s system 90% cpu 0.524 total

4.4. エディタ

各エディタの拡張・プラグインのインストールの仕方はインストールを参照してください。エディタからコード補完などを行うには、最初にgccrecでコンパイルオプションの記録を行っておく必要があります。記録方法についてはgccrecを参照してください。なお、コンパイルオプションなしでコンパイルできるソースファイルにおいてはその限りではありません。

4.4.1. Emacs

利用の前にまず正しくインストールされたか診断を行ってください。M-x gccsense-diagnoseで診断を開始します。問題がなければミニバッファにEverything OK!と表示されます。エラーが表示されたら、内容に従って問題を修正してください。それでも直らない場合は開発者に連絡してください。

コード補完を行うには.->の直後にM-x gccsense-completeします。なお編集中のファイルが存在しない場合、つまり一度も保存されていないバッファではコード補完できないので注意してください。

Auto Complete Modeを利用しているならM-x ac-complete-gccsenseでも補完を行えます。

これらのコマンドをC-c .などに割り当てると良いでしょう。

(add-hook 'c-mode-common-hook
          (lambda ()
            (local-set-key (kbd "C-c .") 'gccsense-complete)
            ;; あるいは
            (local-set-key (kbd "C-c .") 'ac-complete-gccsense)))

Flymakeを利用して自動構文チェックを行うこともできます。Makefileではなくgccrecを利用するようになっているので、従来より正確でかつ高速です。gccrec版Flymakeを有効にするにはM-x gccsense-flymake-setupを実行します。~/.emacsに次のように書いておくと良いでしょう。

(add-hook 'c-mode-common-hook
          (lambda ()
            (flymake-mode)
            (gccsense-flymake-setup)))

4.4.2. Vim

利用の前にまず正しくインストールされたか診断を行ってください。:call GCCSenseDiagnose()で診断を開始します。エラーが表示されたら、内容に従って問題を修正してください。それでも直らない場合は開発者に連絡してください。

コード補完を行うには.->の直後に^X ^Uします。なお編集中のファイルが存在しない場合、つまり一度も保存されていないバッファではコード補完できないので注意してください。

オムニ補完でコードを補完するにはg:gccsenseUseOmniFunc1に設定します。

let g:gccsenseUseOmniFunc = 1

これで^X ^Oや自動補完機能でコード補完を行えるようになります。

5. 制限事項

6. トラブルシューティング

6.1. コード補完できない

診断を行ってください。EmacsならM-x gccsense-diagnose、Vimなら:call GCCSenseDiagnose()です。問題なければ、コード補完を行っているファイルのコンパイラオプションをgccrecで正しく記録しているか確認してください。

$ gccrec -v -r a_problem_file.cpp

record not foundと出力される場合はgccrecでの記録を行っていないことを意味します。gccrecを参照しながら正しく記録を行ってください。

それでもコード補完できない場合は開発者に連絡してください。