OpenGrokによるソースコード検索環境の構築

OpenGrokを用いてC/Python/JavaScriptのソースコード検索とクロスリファレンスを構築するまでの手順。


# OpenGrokとは
[{OpenGrok by OpenGrok](http://opengrok.github.io/OpenGrok/)
>OpenGrok is a fast and usable source code search and cross reference engine. 

公式GitHub repository: https://github.com/OpenGrok/OpenGrok


## 対応言語
公式ページより抜粋

- [Supported Languages and Formats ・ OpenGrok/OpenGrok Wiki](https://github.com/OpenGrok/OpenGrok/wiki/Supported-Languages-and-Formats)
 - C
 - Python
 - JavaScript
 - その他、C++/C#/Java/PHP/Perl等々

本記事ではC/Python/JavaScriptソースコードの構築を試す。


# [追記] Docker Image
- `OpenGrok` の Docker Imageを作成した [thombashi/opengrok - Docker Hub]( https://hub.docker.com/r/thombashi/opengrok/ )
    - `OpenGrok 1.0`
    - [Universal Ctags]( https://github.com/universal-ctags/ctags )
    - Auto reindexing triggered by changes of the directory with files to be indexed
        - [inotify-tools]( https://github.com/rvoicilas/inotify-tools )
    - [Git]( https://git-scm.com/ )
    - [tomcat 8.5]( http://tomcat.apache.org/ )

カスタマイズ不要な場合は↑による構築が簡単。
以降はやや古いバージョンのOpenGrok手動インストール手順。


# 環境
- debian 8.4/8.6 (jessie)


# 参考資料
- [How to install OpenGrok ・ OpenGrok/OpenGrok Wiki](https://github.com/OpenGrok/OpenGrok/wiki/How-to-install-OpenGrok)
- [OpenGrokでAndroidソースコード検索 - Qiita](http://qiita.com/sukaaaaaaaaa/items/67f613c037e6a5bb0078)


# 前準備
## OpenGrokバイナリ取得
https://github.com/OpenGrok/OpenGrok/releases

現時点での最新stable(0.12.1.6)のバイナリをダウンロードする。

- opengrok-0.12.1.6.tar.gz 

## OpenGrok 0.12.1.6
### Requirements
```rst:README
2. Requirements
---------------

    * Latest Java (At least 1.8)
      http://www.oracle.com/technetwork/java/
    * A servlet container like Tomcat (8.x or later)
      supporting Servlet 2.5 and JSP 2.1
      http://tomcat.apache.org/
    * Exuberant Ctags or Universal Ctags
      http://ctags.sourceforge.net/
      http://github.io/uctags
    * Source Code Management installation
      depending on type of repositories indexed
    * If you want to build OpenGrok:
      - Ant (1.9.4 and later)
        http://ant.apache.org/
      - JFlex
        http://www.jflex.de/
      - Netbeans (optional, at least 8.0, will need Ant 1.9.4)
        http://netbeans.org/
```

### Supported Deployment Engines
```bash:bin/OpenGrok
# Supported Deployment Engines:
#   - Tomcat 7
#   - Tomcat 6
#   - Glassfish
```

### Supported Operating Systems
```bash:bin/OpenGrok
# Supported Operating Systems:
#   - Solaris 11  (SunOS 5.11)
#   - Solaris 12  (SunOS 5.12)
#   - Debian      (Linux)
```


## Requirementsパッケージインストール
```console:install-java
$ sudo aptitude install openjdk-7-jre
(略)
$ java -version
java version "1.7.0_111"
```

```console:install-ctags
$ sudo aptitude install ctags
```

```console:install-web-server
$ sudo aptitude install tomcat7 tomcat7-admin
```

### tomcat準備
```console:起動
$ sudo service tomcat7 start
$ sudo service tomcat7 status
● tomcat7.service - LSB: Start Tomcat.
   Loaded: loaded (/etc/init.d/tomcat7)
   Active: active (exited) since 火 2016-01-05 09:52:23 JST; 11s ago
  Process: 46899 ExecStart=/etc/init.d/tomcat7 start (code=exited, status=0/SUCCESS)

 1月 05 09:52:23 debian8 tomcat7[46899]: Starting Tomcat servlet engine: tomcat7.
$
```

server.xml を修正して使用ポート番号を2424(OpenGrokのデフォルトポート番号)に変更する。

```diff:server.xml
@@ -69,7 +69,7 @@
          APR (HTTP/AJP) Connector: /docs/apr.html
          Define a non-SSL HTTP/1.1 Connector on port 8080
     -->
-    <Connector port="8080" protocol="HTTP/1.1"
+    <Connector port="2424" protocol="HTTP/1.1"
                connectionTimeout="20000"
                URIEncoding="UTF-8"
                redirectPort="8443" />
```

tomcatを再起動する。

```console
$ sudo service tomcat7 restart
$
```


# インストール手順
OpenGrokバイナリからインストールする手順を示す。

## OpenGrokインストール
```console:untar
$ tar xvf opengrok-0.12.1.6.tar.gz
opengrok-0.12.1.6/doc/CHANGES.txt
opengrok-0.12.1.6/doc/LICENSE.txt
opengrok-0.12.1.6/doc/NOTICE.txt
opengrok-0.12.1.6/doc/README.txt
opengrok-0.12.1.6/doc/logging.properties
opengrok-0.12.1.6/doc/paths.tsv
opengrok-0.12.1.6/bin/OpenGrok
opengrok-0.12.1.6/doc/EXAMPLE.txt
opengrok-0.12.1.6/lib/lib/ant.jar
opengrok-0.12.1.6/lib/lib/bcel-5.2.jar
opengrok-0.12.1.6/lib/lib/jrcs.jar
opengrok-0.12.1.6/lib/lib/lucene-analyzers-common-4.7.1.jar
opengrok-0.12.1.6/lib/lib/lucene-core-4.7.1.jar
opengrok-0.12.1.6/lib/lib/lucene-queryparser-4.7.1.jar
opengrok-0.12.1.6/lib/lib/lucene-suggest-4.7.1.jar
opengrok-0.12.1.6/lib/lib/swing-layout-0.9.jar
opengrok-0.12.1.6/lib/opengrok.jar
opengrok-0.12.1.6/lib/source.war
opengrok-0.12.1.6/man/man1/opengrok.1
$
```

```bash:パスを通す
OPENGROK_PATH=<path to OpenGrok install dir>/opengrok-0.12.1.6
export PATH=$PATH:$OPENGROK_PATH/bin
```

```console:deploy
$ sudo OpenGrok deploy
Loading the default instance configuration ...
Installing /opt/opengrok-0.12.1.6/bin/../lib/source.war to /var/lib/tomcat7/webapps ...

Start your application server (Tomcat),  if it is not already
running, or wait until it loads the just installed web  application.

OpenGrok should be available on <HOST>:<PORT>/source
  where HOST and PORT are configured in Tomcat.
```

## ソースコード取り込み
まずはLinux 3.16.1のソースを入れてみる。
デフォルトでは`/var/opengrok/src`がソース格納パスとなる。

```console:source配置
$ sudo mkdir -p /var/opengrok/src

$ tar xvf linux-3.16.1.tar.gz
(中略)
$
$ sudo mv linux-3.16.1 /var/opengrok/src
```

`OpenGrok index`を実行するとソースコードのインデックスが作成される。

```console
opengrok-0.12.1/bin$ sudo ./OpenGrok index
Loading the default instance configuration ...
WARNING: OpenGrok generated data path /var/opengrok/data doesn't exist
  Attempting to create generated data directory ...
WARNING: OpenGrok generated etc path /var/opengrok/etc  doesn't exist
  Attempting to create generated etc directory ...
  Creating default /var/opengrok/logging.properties ...
opengrok-0.12.1/bin$
```


## アクセス確認
`http://<OpenGrok server IP address>:2424/source/` にWebブラウザからアクセスする。

![top.png](https://qiita-image-store.s3.amazonaws.com/0/107537/cc9bd41c-9594-c998-cb38-5eff37def41b.png)

検索画面が表示された。
インデックスが正しく作成されていないと`In Project(s)`が表示されず、
検索するとエラーメッセージが表示される。


# 機能
OpenGrokの簡単な機能紹介。
言語を問わず共通した機能が使用できる。。

## 検索機能
全文検索/定義/シンボル等で検索可能。

`Type`を`C`にして`fork`で検索してみると、ファイルを横断しての検索結果が表示される。
ディレクトリ毎にファイルがグループ分けされて、検索該当行が表示される。
個人的にはLXRより見やすい。

![result.png](https://qiita-image-store.s3.amazonaws.com/0/107537/1efa033b-513b-29e7-e922-2b5215061177.png)

## クロスリファレンス機能
変数や関数をクリックすると、その定義について検索する。

![search_01.png](https://qiita-image-store.s3.amazonaws.com/0/107537/f1e71055-bfcf-890f-2f0b-ec1aa11a8294.png)


## ナビゲート機能
ファイルを開いて、画面上部メニューから`Navigate`を選択すると、
ファイル内の変数と関数の定義一覧が表示される。
クリックすると該当箇所にジャンプできる。

![navigate.png](https://qiita-image-store.s3.amazonaws.com/0/107537/df16ddbe-79a7-0962-6728-3f8ff89d0b87.png)

![search_02.png](https://qiita-image-store.s3.amazonaws.com/0/107537/e6688ffc-9ea6-dc16-5f5c-26cc031b61b1.png)


# ソースコード追加
新たなソースコードを追加するには、以下の手順を実施する。

1. `/var/opengrok/src`以下にソースディレクトリを追加
 - ソース格納ディレクトリで`git clone`を実行でOK
2. `OpenGrok update`を実行する
3. tomcatを再起動

例として、[sysstat](https://github.com/sysstat/sysstat)を追加してみる。

```console
$ git clone https://github.com/sysstat/sysstat.git
...
/var/opengrok/src$ ls
linux-3.16.1  sysstat
```

```console
opengrok-0.12.1/bin$ sudo ./OpenGrok update
opengrok-0.12.1/bin$ sudo service tomcat7 restart
opengrok-0.12.1/bin$ 
```

![add.png](https://qiita-image-store.s3.amazonaws.com/0/107537/e64e28c9-63f1-1b41-5ce6-69051bb90f48.png)


`In Project(s)`に`sysstat`が追加された。


# 出力例
PythonとJavaScriptを追加した結果例。

## Python
PythonソースもC言語ソースの追加と同じ方法で追加できる。
例として、[psutil](https://github.com/giampaolo/psutil)モジュールのソースを追加する。

### ソース追加
```console
/var/opengrok/src# git clone https://github.com/giampaolo/psutil.git
...
/var/opengrok/src# ls
linux-3.16.1  psutil
```

```console
opengrok-0.12.1/bin$ sudo ./OpenGrok update
opengrok-0.12.1/bin$ sudo service tomcat7 restart
opengrok-0.12.1/bin$ 
```

### 画面出力
`psutil`が追加されている。`Type`で`Python`が選択できる。
検索結果はC言語での検索と同じ形式で出力される。

![python_01.png](https://qiita-image-store.s3.amazonaws.com/0/107537/0d8b25b2-2ecb-ebbc-a591-1ba0206b5d3c.png)

`Navigate`も機能し、ファイル内の変数と関数の定義が表示できる。

![python_02.png](https://qiita-image-store.s3.amazonaws.com/0/107537/0c551971-e04c-f4bc-c2f5-af808125a683.png)


## JavaScript
JavaScriptソースも同様に追加できる。
例として、[jQuery](https://github.com/jquery/jquery)を追加してみる。

### ソース追加
```console
/var/opengrok/src$ sudo git clone https://github.com/jquery/jquery.git
...
/var/opengrok/src$ ls
jquery  linux-3.16.1  psutil
$
```

```console
opengrok-0.12.1/bin$ sudo ./OpenGrok update
opengrok-0.12.1/bin$ sudo service tomcat7 restart
opengrok-0.12.1/bin$ 
```

### 画面出力
`Type`で`JavaScript`が選択できる。
検索結果はC言語での検索と同じ形式で出力される。

![jquery_01.png](https://qiita-image-store.s3.amazonaws.com/0/107537/7da178c6-8a3b-7c3c-abfa-3e57d6a4efad.png)

JavaScriptでは`Navigate`はうまく働かないようだ。
表示が空白となっている。

![jquery_02.png](https://qiita-image-store.s3.amazonaws.com/0/107537/4f5ad8e5-1a54-1c79-fdab-53c0214851d1.png)


# Troubleshooting
## インデックス作成(OpenGrok index)で失敗
`OpenGrok index`を実行するとソースコードのインデックスが作成される。

```console:インデックス作成失敗
opengrok-0.12.1/bin$ sudo ./OpenGrok index
Loading the default instance configuration ...
WARNING: OpenGrok generated data path /var/opengrok/data doesn't exist
  Attempting to create generated data directory ...
WARNING: OpenGrok generated etc path /var/opengrok/etc  doesn't exist
  Attempting to create generated etc directory ...
  Creating default /var/opengrok/logging.properties ...
10:16:54 SEVERE: Failed to send configuration to localhost:2424 (is web application server running with opengrok deployed?)
java.net.ConnectException: 接続を拒否されました
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:579)
        at java.net.Socket.connect(Socket.java:528)
        at java.net.Socket.<init>(Socket.java:425)
        at java.net.Socket.<init>(Socket.java:241)
        at org.opensolaris.opengrok.configuration.RuntimeEnvironment.writeConfiguration(RuntimeEnvironment.java:928)
        at org.opensolaris.opengrok.index.Indexer.sendToConfigHost(Indexer.java:852)
        at org.opensolaris.opengrok.index.Indexer.main(Indexer.java:563)
opengrok-0.12.1/bin$
```

失敗した。verboseを有効にしてやってみると、
インデックスの作成までは成功して、Webサーバに通信するところで失敗している。

```console:インデックス作成失敗_w/_verbose
$ sudo OPENGROK_VERBOSE=true  ./OpenGrok index
Loading the default instance configuration ...
Logging filehandler pattern: /var/opengrok/log/opengrok%g.%u.log
10:47:34 INFO: Scanning for repositories...
10:47:35 INFO: Done scanning for repositories (0s)
10:47:35 INFO: Writing configuration to /var/opengrok/etc/configuration.xml
10:47:35 INFO: Done...
10:47:35 INFO: Generating history cache for all repositories ...
10:47:35 INFO: Creating historycache for 0 repositories
10:47:35 INFO: Done historycache for all repositories (took 2 ms)
10:47:35 INFO: Done...
10:47:35 INFO: Starting indexing
10:47:37 INFO: Done indexing data of all repositories (took 1.984 seconds)
10:47:37 INFO: Send configuration to: localhost:2424
10:47:37 SEVERE: Failed to send configuration to localhost:2424 (is web application server running with opengrok deployed?)
java.net.ConnectException: 接続を拒否されました
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:579)
        at java.net.Socket.connect(Socket.java:528)
        at java.net.Socket.<init>(Socket.java:425)
        at java.net.Socket.<init>(Socket.java:241)
        at org.opensolaris.opengrok.configuration.RuntimeEnvironment.writeConfiguration(RuntimeEnvironment.java:928)
        at org.opensolaris.opengrok.index.Indexer.sendToConfigHost(Indexer.java:852)
        at org.opensolaris.opengrok.index.Indexer.main(Indexer.java:563)
10:47:37 INFO: Configuration update routine done, check log output for errors.
10:47:37 INFO: Total time: 2.738 seconds
10:47:37 INFO: Final Memory: 2M/12M
```

```text:Errorメッセージ抽出
SEVERE: Failed to send configuration to localhost:2424 (is web application server running with opengrok deployed?)
```

tomcatは動いているが、

```
$ netstat -an | grep 2424
$
```

ポート設定がデフォルトのままだった。
前述の手順で使用ポートを8080 -> 2424に変更し、`./OpenGrok update`を実行すると成功した。

```console
opengrok-0.12.1/bin$ sudo ./OpenGrok update
Loading the default instance configuration ...
toor@debian8:/home/tmp/opengrok-0.12.1/bin$
```

>Once above command finishes without errors(e.g. SEVERE: Failed to send configuration to localhost:2424 ),

公式インストール手順にこういう記述がある。
ここではまる人が多いのかもしれない。


# 類似ツールとの比較
他の類似ツールでは[en/LXR Project](http://lxr.sourceforge.net/en/index.php)が有名。
[LXR linux](https://lxr.missinglinkelectronics.com/linux)等、Linuxカーネルで使われることが多い。
[LXR Cross Referencer を試してみた - harry’s memorandum](http://dharry.hatenablog.com/entry/20120508/1336413218)。

公式の類似ツールとの比較。
[Comparison with Similar Tools · OpenGrok/OpenGrok Wiki](https://github.com/OpenGrok/OpenGrok/wiki/Comparison-with-Similar-Tools)

これによると、OpenGrokは以下のような特徴がある。

- 優位
 - History Search
 - query syntax like AND, OR, field
 - Integration with Eclipse
- 弱点
 - Integration with emacs/vim


# Eclipse plugin
GrokClipseというEclipseとの連携プラグインが公開されている。
[GrokClipse | Eclipse Plugins, Bundles and Products - Eclipse Marketplace](https://marketplace.eclipse.org/content/grokclipse)

インストールを試みたが、失敗した。
ネット上でも情報がほとんどない。