Lxrでソースコードを快適ブラウジング

ブラウザ上でソースコードをお気軽に追跡できるよう Linux Cross Referencer (Lxr) をインストールしてみた。そして、大脳新皮質のアルゴリズムを研究しているOSSプロジェクト Nupic.core を表示させてみた。

Ansible-lxr-nupic.core 公開(2014/10/19)

この記事にある内容を、Ansible Playbook にして、GitHubで公開しました。
CentOS6.5とCentOS7での動作を確認。
group_vars/lxr-server を編集して、任意のソースツリーに使えます。

1. はじめに: Lxrとは?

ブラウザ上にソースコードを表示し、簡単に定義にジャンプできたり、全文検索できるツール。
利用例はこんな感じ
開発は1994年には始まっており、2014年現在も継続している(20年!)。
2014年8月時点の最新バージョンは 2.0.4。
昔は満足なドキュメントもなく、Lxrのインストールはハックに近かったが、最近見てみたら 200+ ページのユーザマニュアルを発見。かなり発展している、さすが20年といったところ。

1.1. 対応環境

WebサーバやDBなどの対応状況は以下のテーブルの通り。
環境 サポート
Webサーバ Apache, Lighttpd, nginx, Cherokee, thttpd
DB SQLite, MySQL, PostgreSQL, Oracle
レポジトリ(VCS) VCSなし, CVS, GIT, SVN, Mercurial, BitKeeper
検索エンジン glimpse, Swish-e
今回は、お手軽さ重視で太字の構成で利用。

1.2. 全文検索したいならVCSなしで!

GitやSVNなどのVCS (Version Control System) を利用すると、タグの自動抽出やコミット情報の表示などできる。色々便利なのだが、現状、全文検索ができなくなる模様。
今回は VCS を使わず、Gitの一つのコミットを、単純コピーして使うことに。

1.3. Projectサイト&ドキュメント

2. System構成

以下の構成にする。
ポイントは以下。
  • Root権限をなるべく不要とするよう、一般ユーザ “lxruser” を作成し、基本的にその中に閉じた設定を行う。
  • URLから、AliasMatchを利用してマッピングされる ~lxruser/lxr-2.0.2/source というPerlスクリプトが、ブラウザ上にソースコードを表示するエンジンとなるCGI。
  • 全文検索エンジン glimpseは、lxruserのホームディレクトリ内の ~/local/bin/ にインストールする。
  • Lxr自体のsqliteデータベース、Lxrがブラウザ上に表示するnupic.coreのソースコード、glimpseのインデックス情報は、すべて ~/local/share/ に配置する。
  • このように、CGI部分とデータ部分を分離することで、Lxrの新バージョンがリリースされた際に、入れ替えを単純化できる。

3. Rootユーザでの作業

Root権限が必要な作業を列挙する。

3.1. SELinux 停止

rootでログインし、/etc/selinux/config を書き換える。
必須ではないが、httpd向けのchcon設定が複雑になるので、今回は disable に。
# cp -a /etc/selinux/config /etc/selinux/config.org
# sed -ie '/^SELINUX=/s/=.*/=disabled/' /etc/selinux/config
# grep '^SELINUX=' /etc/selinux/config
SELINUX=disabled
# shutdown -r now
再起動が完了したら、再度 root でログインして、SELinuxの停止を確認。
# getenforce
Disabled

3.2. Firewall 停止

CentOS 6.5は、既定のFirewallでport 80へのパケットを捨てる設定になっている。
今回は、単純に Firewall を停止する。
# service iptables stop
# chkconfig --del iptables

3.3. Lxruser ユーザの作成

Lxrをインストールするユーザとして “lxruser” を作成(ユーザ名は任意でOK)。
# useradd lxruser
# passwd lxruser
New password: (任意のパスワードを入力)
あとでインストールに使う lxruser のディレクトリと、実行PATHも設定。
httpdのためにサーチ権も付けておく。
# mkdir --parents ~lxruser/local/bin ~lxruser/local/share/{glimpse,lxr,sqlite}
# chown lxruser:lxruser -R ~lxruser/local/
# chmod o+x -R ~lxruser
# sed -i -e 's_^PATH=.*:$HOME/bin$_&:$HOME/local/bin_' ~lxruser/.bash_profile
設定を確認しよう。
# grep PATH ~lxruser/.bash_profile
PATH=$PATH:$HOME/bin:$HOME/local/bin
export PATH
# namei -m ~lxruser/local/bin ~lxruser/local/share/{glimpse,lxr,sqlite} | sort | uniq
 drwxr-xr-x bin
 drwxr-xr-x glimpse
 drwxr-xr-x home
 drwxr-xr-x local
 drwxr-xr-x lxr
 drwxr-xr-x share
 drwxr-xr-x sqlite
 drwx-----x lxruser
 dr-xr-xr-x /
f: /home/lxruser/local/bin
f: /home/lxruser/local/share/glimpse
f: /home/lxruser/local/share/lxr
f: /home/lxruser/local/share/sqlite

3.4. sudo設定

必須ではないが、あとの手間を省くため sudo の設定もしておく。
# echo "lxruser ALL=(ALL)  NOPASSWD:ALL" > /etc/sudoers.d/lxruser
動作の確認をしてみる。
# su - lxruser
$ whoami
lxr
$ sudo date
Wed Aug  6 23:19:06 JST 2014
$ exit
sudo使えているね。

3.5. 依存PKGのインストール

Lxrが必要とするパッケージをインストール。詳細はユーザマニュアルを参照のこと。
なお、gccとflexは、全文検索エンジン glimpse のビルドで必要となる。
# yum install -y ctags perl perl-DBI perl-DBD-SQLite sqlite httpd mod_perl flex gcc
Lxrは、PerlのFile::MMagicモジュールも利用するので、EPELから拝借。
# rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# yum install perl-File-MMagic

4. Lxrusrユーザでの設定

以降は、先ほどRoot権限で作成した lxruser にてログインし、実際に lxr をインストールしていく。

4.1. 全文検索エンジン glimpse のインストール

glimpseを導入する。
glimpse のライセンスは、OSS利用ではフリーだが、商用利用は有償となる。間違って使わないよう注意する必要あり。
$ wget http://webglimpse.net/trial/glimpse-latest.tar.gz
$ tar xfz glimpse-latest.tar.gz
$ cd glimpse-*/
$ ./configure --prefix=$HOME/local
$ make
$ make check
...
Start check suite

run test 1 [1 of 2]
index test... ok

run test 2 [2 of 2]
search test... ok

Done
$ make install
インストールされたことを確認。
$ which glimpse
~/local/bin/glimpse
Lxr 自体は glimpse なしでも動作可能なので、全文検索なくてもいいという場合は、設定ファイル lxr.conf の glimpse パスをあとで /bin/true にしておけば良い。

4.2. Lxr本体のインストール

ソースの取得と展開。
$ cd
$ wget http://downloads.sourceforge.net/project/lxr/stable/lxr-2.0.2.tgz
$ tar xfz lxr-2.0.2.tgz
$ cd lxr-*/

主な構成部品/File

以下のとおり。Perlで書かれている。
部品 説明
genxref ctagを利用してソースコードを解析し、DB上にインデックスを構築するスクリプト
source インデックスを利用し、ソースコードを美しく表示する CGI
diff ファイルの2バージョンのdiffを美しく表示する CGI
ident 識別子を検索し、結果を表示する CGI
search glimpseなど利用して、全文検索する CGI
念のための環境チェック。
$ ./genxref --checkonly
ERROR: could not open configuration file lxr.conf
[  OK  ]     Perl     version ... 5.10.1
Parameter 'ectagsbin' not defined - trying to find ctags
ctags found at /usr/bin/ctags
[  OK  ]     ctags    version ... 5.8
Parameter 'glimpsebin' not defined - trying to find glimpse
glimpse found at /home/lxruser/local/bin/glimpse
Checked:    glimpse   version ... 4.18.5
Parameter 'glimpseindex' not defined - trying to find glimpseindex
glimpseindex found at /home/lxruser/local/bin/glimpseindex
Checked: glimpseindex version ... 4.18.5
Parameter 'swishbin' not defined - trying to find swish-e
swish-e not found, `command -v swish-e` returned a null string
genxref stopped without indexing by --checkonly option
一行目の赤字で ERROR は、lxr.conf をまだ生成していないので、無視して良い。
最後の赤字の “swish-e not found” も、今回は代わりに glimpse を利用するので無視して良い。

4.3. Configファイルの生成

次に lxr.conf や Apache 向けの設定ファイルなどを生成する。
プロンプト(>)で必要事項を入力。大文字が既定値。入力が無いところは、ENTERでOK。
とても長いので、インラインで説明していく。
$ ./scripts/configure-lxr.pl --verbose
*** LXR configurator (version: 1.15) ***

LXR root directory is /home/lxruser/lxr-2.0.2
Configuration will be stored in custom.d/
directory custom.d created

Configure for single/multiple trees? [S/m] > m
一つの lxr スクリプトで、複数のソースツリーを扱う時は “m” を選択。
“S” を選択しても、ひとつのソースツリーの複数バージョンを扱うことは可能。
URLパスに影響してくる。
*** LXR web server configuration ***

Many different configurations are possible, they are related to the way
LXR service is accessed, i.e. to the structure of the URL.
Refer to the User's Manual for a description of the variants.

LXR can be located at the server-root (so called dedicated)
or lower in the server hierarchy (shared because there are
usually other pages or sections).
Server type? [dedicated/SHARED] >
デフォルトのSHAREDを選べば、他のWebページと共存できる。
普通は、SHAREDで良い。だから、何も入力せずENTER。
Selecting which tree to display can be done in various ways:
  1. from the host name (all names are different),
  2. from a prefix to a common host name (similar to previous)
  3. from the site section name (all different)
  4. from interpretation of a section name part (similar to previous)
  5. from the head of script arguments
Method 5 is highly recommended because it has no impact on webserver
  configuration.
Method 3 is second choice but involves manually setting up many
  symbolic links (one per source-tree).
Method 1 & 2 do not involve symbolic links but need populating webserver
  configuration with virtual hosts.
  Note that method 2 does not work well on //localhost.
Method 4 is deprecated because it has proved not easily portable
  under alternate webservers (other than Apache).

Tree designation? [ARGUMENT
/section name
/prefix in host
/hostname
/embedded in section] > embedded
ソースツリーとURLを対応付けるため、URLの形式を決める。
embedded を選択し、URL形式をhttp://hostname:port/lxr_section/tree_name/ とする。
以降の設問で、embedded形式の3つの部分: http://hostname:port/ lxr_section/ tree_name/を埋めていく。

参考

全形式の一覧は以下のとおり。”TREE01” など大文字の部分が、ツリー毎に変化。
詳しくはユーザマニュアル参照。
形式 URLの例 補足
argument http://hostname/lxr/source?tree=TREE01 ツリー名を引数で渡す。
section name http://hostname/PATH/TO/TREE01/source URLと単独ツリーで設定したLxrを、静的に紐付ける。複数ツリーにはLxrの複数インストールで対応。
prefix in host http://TREE01.hostname/lxr/source VirtualHostとツリーを紐付け。DNSも考慮要。
hostname http://TREE01/lxr/source 各ホスト名を各ツリーを紐付け。DNSも考慮要。
embedded http://hostname/lxr/TREE01/source URLパスにツリー名を埋め込む。
The computer hosting the server is described by an URL.
The form is scheme://host_name:port
where:
  - scheme is either http or https (http: can be omitted),
  - host_name can be given as an IP address such as 123.45.67.89
              or a domain name like localhost or lxr.url.example,
  - port may be omitted if standard for the scheme.
--- Host name or IP? [//localhost] >
--- Alias name or IP? > http://192.168.170.64
--- Alias name or IP? >
先の説明の通り、http://hostname:port/ 部分を入力。ひとつ以上入力し、その後にENTERで次へ進む。今回は省略形で、http://localhost:80/http://192.168.170.64:80/ を入力した。
URL section name for LXR in your server? [/lxr] >
The built-in method to manage several trees with a single instance of LXR is to include
a designation of the tree in the URL at the end of the section name.
This sequence after host name is called "virtual root".
Supposing one of your trees is to be referred as "my-tree", an URL to list the content
of the default version directory would presently be:
     //localhost/lxr/my-tree/source
with virtual root equal to /lxr/my-tree
今度はlxr_section/部分を入力。
既定値の /lxr にしたので、URLは http://192.168.170.64:80/lxr/tree_name/ となった。
Use built-in multiple trees management with tree designation at end of virtual root? [YES/no] >
ApacheのVirtual Hostの指定方法を聞いてきている。既定のYESで良い。
ここで一旦、URL系の設定は完了。tree_nameについては、後ほどツリー設定のところ(★)で聞いてくるので、それまでDB系の設定へ。
*** LXR database configuration ***

The choice of the database engine can make a difference in indexing performance,
but resource consumption is also an important factor.
  * For a small personal project, try SQLite which do not
    need a server and is free from configuration burden.
  * For medium to large projects, choice is between MySQL,
    PostgreSQL and Oracle.
    Oracle is not a free software, its interface has not been
    tested for a long time.
  * PostgreSQL databases are smaller than MySQL's
    and performance is roughly equivalent.
  * MySQL is at its best with large-sized projects
    (such as kernel cross-referencing) where it is fastest at the cost
    of bigger databases.
  * Take also in consideration the number of connected users.
Database engine? [MYSQL/oracle/postgres/sqlite] > sqlite
The safest option is to create one database per tree.
You can however create a single database for all your trees with a specific set of
tables for each tree (though this is not recommended).
How do you setup the databases? [PER TREE/global] >
All databases can be accessed with the same username and
can also be described under the same names.
Will you share database characteristics? [YES/no] >
Will you use the same username and password for all DBs? [YES/no] >
--- DB user name? [lxr] >
--- DB password ? [lxrpw] >
Will you give the same prefix to all tables? [YES/no] >
--- Common table prefix? [lxr_] >
--- Directory for glimpse databases? > /home/lxruser/local/share/glimpse
DBをsqliteとし、glipmseのインデックスを /home/lxruser/local/share/glimpse に格納するよう指示。
Is your Apache version 2.4 or higher? [YES/no] > no
file .htaccess written into LXR root directory
file apache2-require.pl written into configuration directory
file apache-lxrserver.conf written into configuration directory
file lighttpd-lxrserver.conf written into configuration directory
file nginx-lxrserver.conf written into configuration directory
file thttpd-lxrserver.conf written into configuration directory
Mercurial support files written into configuration directory
CentOS 6.5は、標準で Apache 2.2 なので、”no” を回答。
*** LXR master configuration file setup ***
    Global section part

*** Configuring auxiliary tool paths
*** Host name previously defined as http://localhost
*** Configuring HTML parameters
*** 'Buttons-and-menus' interface is recommended for the kernel
*** to avoid screen cluttering.
--- Use 'buttons-and-menus' instead of 'link' interface? [YES/no] >
*** Configuring file subsection
*** Configuring "common factors"
*** Marking tree section
buttons-and-menus は、ページ上部のボタンをリンクにするかという話。ボタンで良いのでYESのままでOK。
*** LXR master configuration file setup ***
    Tree section part
    SQL script for database initialisation

*** Configuring LXR server parameters
*** The virtual root is the fixed URL part after the hostname.
*** The tree needs to be uniquely identified as e.g. /lxr/the_tree
--- Tree designation for URL? (e.g. the_tree) > nupic.core
--- Caption in page header? (e.g. Project XYZZY displayed by LXR) > Nupic.core by Lxr
Do you want a speed switch button for this tree ? [YES/no] >
--- Short title for button? (e.g. XYZZY) [nupic.core] >
Do you need a specific encoding for this tree ? [yes/NO] >
ようやく、URLの tree_name/ の設定(★)。
nupic.core にしたので、結局、URLは http://192.168.170.64:80/lxr/nupic.core/ になった。
その他、フロントページに表示されるタイトル “Nupic.core by Lxr” なども入力している。
*** Describing tree location
How is your tree stored? [FILES/cvs/git/svn/hg/bk] >
*** A source directory contains one sub-directory for every version.
--- Source directory? (e.g. /home/myself/project-tree) > /home/lxruser/local/share/lxr/nupic.core
Name to display for the path root? (e.g. Project or $v for version) [$v] >
コンテンツとなるソースの場所を設定。
ソースは /home/lxruser/local/share/nupic.core/2014-08-07 に配置することにしたので、
2014-08-07 の前までを Source directory に書く。2014-08-07部分は、Lxrが自動で特定してくれる($vにより)。
*** Enumerating versions
Label for version selection menu?  [Version] > TAG
*** Versions can be explicitly enumerated, be read from a file or computed
*** by a function. The latter case is recommended for VCS-stored trees.
Version enumeration method? [LIST/file/function] >
--- Version name?  > 2014-08-07
--- Version name? (hit return to stop) >
*** By default, first version in list is displayed. You may also indicate
*** a prefered version.
--- Default displayed version is first in 'range'? [YES/no] >
Versionの列挙は、直書きするのでLIST。functionを選択すると、VCSでタグを自動抽出してくれる。
*** Setting directory lists
*** Some directories may contain non-public project data (binaries,
*** compilers caches, SCM control data, ...). They can be hidden from LXR.
--- Directory to ignore, e.g. CVSROOT or CVS? (hit return to stop) >
*** If your source code uses "include" statements (#include, require, ...)
*** LXR needs hints to resolve the destination file.
--- Include directory, e.g. /include? (hit return to stop) >
コンテンツとなるソースディレクトリの設定。無視するべきディレクトリなど。
*** Configuring data storage
--- Database file? (e.g. /home/myself/storage.db) > /home/lxruser/local/share/sqlite/lxr.db
Do you want to override the global 'lxr_' table prefix? [yes/NO] >
SQliteのDBファイルのパス指定。こちらは、ディレクトリではなく、ファイルのパスなので注意。
*** Configure another tree? [YES/no] > no
configuration saved in custom.d/lxr.conf
DB initialisation sript is custom.d/initdb.sh
今回は、この一つのツリーのみ設定するので、noでループを抜ける。
custom.d/ に必要なファイルが色々と生成されるので、移行の手順で、これを利用する。

4.4. Index DBの初期化

$ ./custom.d/initdb.sh
*** SQLite -  Configuring tables lxr_ in database /home/lxruser/local/share/sqlite/lxr.db
$ ls -ldh /home/lxruser/local/share/sqlite/lxr.db
-rw-r--r-- 1 lxruser lxruser 30K Aug  7 01:45 /home/lxruser/local/share/sqlite/lxr.db
できた。

4.5. Lxr.conf の有効化と環境チェック

$ cp custom.d/lxr.conf .

$ ./genxref --url=http://localhost/lxr/nupic.core --checkonly
[  OK  ]     Perl     version ... 5.10.1
[  OK  ]     ctags    version ... 5.8
Checked:    glimpse   version ... 4.18.5
Checked: glimpseindex version ... 4.18.5
Parameter 'swishbin' not defined - trying to find swish-e
swish-e not found, `command -v swish-e` returned a null string
genxref stopped without indexing by --checkonly option
問題ない。

4.6. Lxrのコンテンツとなるソースコードの取得と配置

これからLxrとglimpseの両方のインデックスを生成するが、その前に、コンテンツとなるソースコードを準備する。今回は、お題として Nupic.core を使う。
取得は簡単。git cloneし、Lxr が参照するパスへ配置。
設定ファイルに指定したとおり、このソースツリーの名称は nupic.core 、バージョン名は 2014-08-07 だったから、配置先のパスは ~/local/share/lxr/nupic.core/2014-08-07 が正解。
$ git clone https://github.com/numenta/nupic.core.git
$ find nupic.core/ -name .git* | xargs rm -rf
$ mkdir ~/local/share/lxr/nupic.core/
$ mv nupic.core/ ~/local/share/lxr/nupic.core/2014-08-07

参考:NuPICとは?

大脳新皮質の働きをアルゴリズムとして読み解き、応用しようというオープンソースプロジェクト。”Numenta Platform for Intelligent Computing” の略。詳しくは、こちら(後日)

4.7. Index生成

$  ./genxref --url=http://localhost/lxr/nupic.core --version=2014-08-07

... 延々とインデックスを生成、手元のPCで13分あまり。I/Oネックだった。 ...

*** 2014-08-07 /src/test/testeverything/
--- 2014-08-07 TestEverythingMain.cpp 1407338444-6837 9595 +++ 214/649
*** 2014-08-07 /
完了!

5. Apacheサーバの設定

Lxrが生成したApache向け設定ファイルをコピーし、ベースのhttpd.confを若干手直しして、起動する。
$ sudo cp custom.d/apache-lxrserver.conf /etc/httpd/conf.d/
$ sudo sed -i -e '/^#ServerName/a\ServerName localhost' -e '/^ServerName /d' /etc/httpd/conf/httpd.conf
$ sudo service httpd start
Starting httpd:                                            [  OK  ]
うまく起動した。
/var/log/httpd/error_log や /var/log/messages にも起動時刻にエラーはなし。
最後に動作確認として、http://192.168.170.64/lxr/nupic.core/source にアクセスしてみる。
以下の画面が表示される。
最後に、Apacheサーバの自動起動をONにしておく。
# chkconfig --add httpd
# chkconfig  httpd on
# chkconfig --list  httpd
httpd           0:off   1:off   2:on    3:on    4:on    5:on    6:off

5.1 Trouble-shooting

Lxrの設定で引っかかりそうなネタをメモ。なお、Lxrプロジェクトサイトが提供している情報はこちら。

TR1. Portを指定した場合に httpd サーバ起動でエラー

[Thu Jul 31 05:45:12 2014] [error] VirtualHost _default_:443 -- mixing * ports and non-* ports with a NameVirtualHost address is not supported, proceeding with undefined results
原因は、mod_sslの設定ファイル /etc/httpd/conf.d/ssl.conf と、lxrの設定ファイル apache-lxrserver.conf が競合しているため。どちらもポート指定だったので。
対策は、以下のようにapache-lxrserver.confを修正。再びコピー。
$ PORT=876
$ cd lxr-2.0.2
$ sed -i -e '/NameVirtualHost/s/\*$/\*:'$PORT'/' -e '/<VirtualHost/s/\*>/\*:'$PORT'>/' custom.d/apache-lxrserver.conf
$ sudo cp custom.d/apache-lxrserver.conf /etc/httpd/conf.d/
更に詳しい情報はこちら:

TR2. エラーログ出力して httpdの起動が失敗

/var/log/httpd/error_log に以下のログがあるなら
Can't locate /home/lxruser/lxr-2.0.2/custom.d/apache2-require.pl in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 /etc/httpd) at (eval 2) line 1.\n"
原因は、SELinux が有効になっており、httpd から apache2-require.pl へのアクセスが拒否されているから。
rootで直接 httpd を起動したり
# /usr/sbin/httpd -X
/etc/rc.d/init.d/httpdをコピーするとうまくいく。
# cp /etc/rc.d/init.d/httpd .
# ./httpd start
違いはこれ:
# ls --context ./httpd /etc/rc.d/init.d/httpd
-rwxr-xr-x. root root system_u:object_r:httpd_initrc_exec_t:s0 /etc/rc.d/init.d/httpd
-rwxr-xr-x. root root unconfined_u:object_r:admin_home_t:s0 ./httpd
Lxrのユーザマニュアルによると、以下の対策でOKとあるが、不足している模様。
今回は、SELinuxをDisableして対処した。
# chcon --reference /var/www/cgi-bin/ -R /home/h1suzuki/lxr-2.0.2

TR3. URLにアクセスしてもログ出力がなく、ブラウザに何も表示なし

httpdは起動しており、ネットワークポートもListenしているのに、/var/log/httpd/access_log にも /var/log/httpd/error_log にも何もログが出力されないなら、おそらく、ネットワーク的に届いていない。
考えられる原因としては、ルーティングやiptables(firewall)など。ping や traceroute コマンドを駆使して、解決する。
今日はここまで。Happy Hacking!

コメントを投稿