HPCメモ

HPC(High Performance Computing)に関連したりしなかったりすることのメモ書き

lctagsでcall graphを書きたかった・・・

最近知ったんですが、lctagsというclangを使ったソースコードタグシステムがあるそうです。

github.com

ソースコードタグシステムというのは、変数名や関数名から定義を参照したり、関数の定義部から呼び出し元へ飛ぶような複数のファイルを跨いで内容を飛びまわるようなものを言います。メジャーどころだと、ctags, etags, GNU Globalあたりが昔から使われています。これらの昔から存在するツールは、ソースコードを読み込んで解析するパーサを自前で持っていますが、この部分を真面目に開発していくと、最終的にはコンパイラを作る羽目になることが広く知られています。*1

じゃ、実際にコンパイラを使えばいいじゃんというわけで、ClangのフロントエンドがパースしたAST木を使っているのがこちらのシステムです。

インストール方法は apt が使えるOSならmake一発でインストールしてくれるそうです。

$ make build_for_ubuntu [PROXY=http://proxy.hoge:port/]
$ sudo make install

でもってmakefile(src/makefile)を確認するとbuild_for_ubntuはありませんでしたが、build_for_aptがあったのでそっちを見てみると

  1. libclang3.8-devのインストール
  2. liblua5.3-devのインストール
  3. libssl-devのインストール
  4. swig3.0のインストール
  5. gtagsのインストール
  6. luasqlite3のインストール
  7. lctagsのビルド

と進んでいくようです。

動作を確認しているのは debian 9.1, ubuntu 17.04 の 64bit だけです。

とのことでしたが、とりあえずubuntu20で試してみましょう。

> docker run -it --rm ubuntu /bin/bash
# apt update
# apt install git make python sudo curl unzip
# git clone https://github.com/ifritJP/lctags.git
# cd lctags
# make build_for_apt
(cd src; make build_for_apt)
make[1]: Entering directory '/lctags/src'
/bin/sh: 1: lua5.3: not found
check clang-dev ...
sudo apt-get -o Acquire::http::proxy="" install libclang-3.8-dev
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package libclang-3.8-dev
E: Couldn't find any package by glob 'libclang-3.8-dev'
E: Couldn't find any package by regex 'libclang-3.8-dev'
make[1]: *** [makefile:637: build_for_apt] Error 100
make[1]: Leaving directory '/lctags/src'
make: *** [makefile:2: all] Error 2

というわけで、libclang-3.8-dev なんてパッケージは無いと言われて止まります。

試しにapt searchしてみたらversion10なんてのが見つかりました。

# apt search libclang-dev
Sorting... Done
Full Text Search... Done
libclang-dev/focal 1:10.0-50~exp1 arm64
  clang library - Development package

もうちょっと古いのは無いかなということで探してみると

# apt search libclang |grep dev     

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

libclang-10-dev/focal 1:10.0.0-4ubuntu1 arm64
libclang-11-dev/focal-updates 1:11.0.0-2~ubuntu20.04.1 arm64
libclang-6.0-dev/focal 1:6.0.1-14 arm64
libclang-7-dev/focal 1:7.0.1-12 arm64
libclang-8-dev/focal 1:8.0.1-9 arm64
libclang-9-dev/focal 1:9.0.1-12 arm64
libclang-common-10-dev/focal 1:10.0.0-4ubuntu1 arm64
  Clang library - Common development package
libclang-common-11-dev/focal-updates 1:11.0.0-2~ubuntu20.04.1 arm64
  Clang library - Common development package
libclang-common-6.0-dev/focal 1:6.0.1-14 arm64
  clang library - Common development package
libclang-common-7-dev/focal 1:7.0.1-12 arm64
  Clang library - Common development package
libclang-common-8-dev/focal 1:8.0.1-9 arm64
  Clang library - Common development package
libclang-common-9-dev/focal 1:9.0.1-12 arm64
  Clang library - Common development package
libclang-cpp10-dev/focal 1:10.0.0-4ubuntu1 arm64
libclang-cpp11-dev/focal-updates 1:11.0.0-2~ubuntu20.04.1 arm64
libclang-dev/focal 1:10.0-50~exp1 arm64
librust-clang-sys+clang-3-7-dev/focal 0.28.1-6 arm64
librust-clang-sys+clang-3-8-dev/focal 0.28.1-6 arm64
librust-clang-sys+clang-3-9-dev/focal 0.28.1-6 arm64
librust-clang-sys+clang-4-0-dev/focal 0.28.1-6 arm64
librust-clang-sys+clang-5-0-dev/focal 0.28.1-6 arm64
librust-clang-sys+clang-6-0-dev/focal 0.28.1-6 arm64
librust-clang-sys+clang-7-0-dev/focal 0.28.1-6 arm64
librust-clang-sys+clang-8-0-dev/focal 0.28.1-6 arm64
librust-clang-sys+libloading-dev/focal 0.28.1-6 arm64
librust-clang-sys-dev/focal 0.28.1-6 arm64

あら、rustバインディングは3.8が残ってますね。

じゃ、こいつの依存パッケージは何かしらというと

# apt depends  librust-clang-sys+clang-3-8-dev/focal
librust-clang-sys+clang-3-8-dev
  Depends: librust-clang-sys-dev (= 0.28.1-6)
  Depends: <librust-clang-sys+gte-clang-3-6-dev> (= 0.28.1-6)
    librust-clang-sys-dev
  Depends: <librust-clang-sys+gte-clang-3-7-dev> (= 0.28.1-6)
    librust-clang-sys-dev
  Depends: <librust-clang-sys+gte-clang-3-8-dev> (= 0.28.1-6)
    librust-clang-sys-dev
root@e0f6d7d7f2c9:/lctags# apt depends librust-clang-sys-dev
librust-clang-sys-dev
  Depends: <librust-glob-0.3+default-dev>
    librust-glob-dev
  Depends: <librust-libc-0.2-dev> (>= 0.2.39-~~)
    librust-libc-dev
  Depends: libclang-dev
  Depends: llvm
  Depends: clang
  Suggests: librust-clang-sys+clang-3-7-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+clang-3-8-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+clang-3-9-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+clang-4-0-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+clang-5-0-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+clang-6-0-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+clang-7-0-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+clang-8-0-dev (= 0.28.1-6)
  Suggests: librust-clang-sys+libloading-dev (= 0.28.1-6)

なぜかlibclangには依存していない・・・

面倒なんで、新しいのを入れて自力でビルドしましょう。

もう一度、READMEファイルに戻ってみると

必要なライブラリ等
    swig (3.0)
    lua, lua-dev(5.2 or 5.3)
    libclang-dev (r380 or r390)
    luasqlite3 (0.9.4)
    openssl

だそうなので、この辺をlibclang-devのバージョンだけ無視してインストールします。 ただし、luasqlite3はパッケージが無いようなので、lctagsのmakefileを使ってインストールします。

# apt-get install swig3.0 lua5.3 liblua5.3-dev libclang-dev  openssl
# make download_luasqlite3
(cd src; make download_luasqlite3)
make[1]: Entering directory '/lctags/src'
check luasqlite3 ...
install
mkdir -p ../external/luasqlite3
curl --proxy "" "http://lua.sqlite.org/index.cgi/zip/lsqlite3_fsl09x.zip?uuid=fsl_9x" -o ../external/luasqlite3/lsqlite3_fsl09x.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1947k  100 1947k    0     0  1399k      0  0:00:01  0:00:01 --:--:-- 1398k
(cd ../external/luasqlite3; unzip -o lsqlite3_fsl09x.zip)
Archive:  lsqlite3_fsl09x.zip
   creating: lsqlite3_fsl09x/
  inflating: lsqlite3_fsl09x/HISTORY  
  inflating: lsqlite3_fsl09x/Makefile  
  inflating: lsqlite3_fsl09x/README  
   creating: lsqlite3_fsl09x/doc/
  inflating: lsqlite3_fsl09x/doc/lsqlite3.wiki  
   creating: lsqlite3_fsl09x/examples/
  inflating: lsqlite3_fsl09x/examples/aggregate.lua  
  inflating: lsqlite3_fsl09x/examples/function.lua  
  inflating: lsqlite3_fsl09x/examples/hooks_advanced.lua  
  inflating: lsqlite3_fsl09x/examples/order.lua  
  inflating: lsqlite3_fsl09x/examples/simple.lua  
  inflating: lsqlite3_fsl09x/examples/smart.lua  
  inflating: lsqlite3_fsl09x/examples/statement.lua  
  inflating: lsqlite3_fsl09x/examples/tracing.lua  
  inflating: lsqlite3_fsl09x/examples/update_hook.lua  
   creating: lsqlite3_fsl09x/extras/
  inflating: lsqlite3_fsl09x/extras/Makefile  
  inflating: lsqlite3_fsl09x/extras/extension-functions.c  
  inflating: lsqlite3_fsl09x/extras/installpath.lua  
  inflating: lsqlite3_fsl09x/lsqlite3-0.9.4-2.rockspec  
  inflating: lsqlite3_fsl09x/lsqlite3.c  
  inflating: lsqlite3_fsl09x/lsqlite3complete-0.9.4-2.rockspec  
  inflating: lsqlite3_fsl09x/sqlite3.c  
  inflating: lsqlite3_fsl09x/sqlite3.h  
   creating: lsqlite3_fsl09x/test/
  inflating: lsqlite3_fsl09x/test/test-dyld.lua  
  inflating: lsqlite3_fsl09x/test/test.lua  
  inflating: lsqlite3_fsl09x/test/tests-sqlite3.lua  
make[1]: Leaving directory '/lctags/src'

でもって最後にmake buildすると

# make build
(cd src; make build)
make[1]: Entering directory '/lctags/src'
swig -I/usr/lib/llvm-3.8/include -o swig/libClangLuaBase_wrap.c -lua swig/libClangLuaBase.i
make[1]: swig: Command not found
make[1]: *** [makefile:162: swig/libClangLuaBase_wrap.c] Error 127
make[1]: Leaving directory '/lctags/src'
make: *** [makefile:2: all] Error 2

vi が無いので、sedmakefileを書き換えます。

# sed -i -e '/SWIG=swig/s/swig/swig3.0/' -e '/CLANG_VER=3.8/s/3.8/10/' src/makefile 
# make build
(cd src; make build)
make[1]: Entering directory '/lctags/src'
swig3.0 -I/usr/lib/llvm-10/include -o swig/libClangLuaBase_wrap.c -lua swig/libClangLuaBase.i
/usr/lib/llvm-10/include/clang-c/Platform.h:45: Error: Syntax error - possibly a missing semicolon.
make[1]: *** [makefile:162: swig/libClangLuaBase_wrap.c] Error 1
make[1]: Leaving directory '/lctags/src'
make: *** [makefile:2: all] Error 2

ぐぬぬぬ。とりあえず一番古いlibclangを入れて再トライ

# apt install libclang-6.0-dev
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  libclang-common-6.0-dev libclang1-6.0 libllvm6.0
The following NEW packages will be installed:
  libclang-6.0-dev libclang-common-6.0-dev libclang1-6.0 libllvm6.0
0 upgraded, 4 newly installed, 0 to remove and 5 not upgraded.
Need to get 42.9 MB of archives.
After this operation, 437 MB of additional disk space will be used.
・
・
・
# sed -i -e '/CLANG_VER=10/s/10/6.0/' src/makefile 
# make build
・
・
・
gcc Helper.c -std=c99 -DHELPER_STAND_ALONE -o Helper  -I/usr/include/lua5.3 -llua5.3 -lcrypto -lm -lpthread -lrt
Helper.c:4:10: fatal error: openssl/evp.h: No such file or directory
    4 | #include <openssl/evp.h>
      |          ^~~~~~~~~~~~~~~
compilation terminated.

わーい進んだ。 でも今度はopensslのヘッダが無いとか言われてます。

よくよくこの記事を遡ってみると、opensslは入れてるけど、libssl-devは入れてませんね。 追加でインストールして再ビルドしてみると

#apt install   libssl-dev
# make build
(cd src; make build)
make[1]: Entering directory '/lctags/src'
make -C lctags LUA_COMMAND=lua5.3 LUA_INC=/usr/include/lua5.3 LUA_LIB= LUA_CFLAGS="-I/usr/include/lua5.3" LUA_LDFLAGS=-llua5.3 SO=so
make[2]: Entering directory '/lctags/src/lctags'
gcc Helper.c -std=c99 -DHELPER_STAND_ALONE -o Helper  -I/usr/include/lua5.3 -llua5.3 -lcrypto -lm -lpthread -lrt
Helper.c: In function 'helper_msleep':
Helper.c:322:5: warning: implicit declaration of function 'usleep'; did you mean 'sleep'? [-Wimplicit-function-declaration]
  322 |     usleep( ( msec % 1000 ) * 1000 );
      |     ^~~~~~
      |     sleep
gcc Helper.c -std=c99 -fPIC -shared -DHELPER_STAND_ALONE -o Helper.so   -I/usr/include/lua5.3 -llua5.3 -lcrypto -lm -lpthread -lrt
Helper.c: In function 'helper_msleep':
Helper.c:322:5: warning: implicit declaration of function 'usleep'; did you mean 'sleep'? [-Wimplicit-function-declaration]
  322 |     usleep( ( msec % 1000 ) * 1000 );
      |     ^~~~~~
      |     sleep
make[2]: Leaving directory '/lctags/src/lctags'
make[1]: Leaving directory '/lctags/src'
# make install
(cd src; make install)
make[1]: Entering directory '/lctags/src'
make -C lctags LUA_COMMAND=lua5.3 LUA_INC=/usr/include/lua5.3 LUA_LIB= LUA_CFLAGS="-I/usr/include/lua5.3" LUA_LDFLAGS=-llua5.3 SO=so
make[2]: Entering directory '/lctags/src/lctags'
make[2]: Nothing to be done for 'all'.
make[2]: Leaving directory '/lctags/src/lctags'
make install-lib
make[2]: Entering directory '/lctags/src'
mkdir -p /usr/local/lib/lua/5.3
mkdir -p /usr/local/lib/lua/5.3/libclanglua
mkdir -p /usr/local/lib/lua/5.3/lctags
mkdir -p /usr/local/share/lua/5.3
mkdir -p /usr/local/share/lua/5.3/libclanglua
mkdir -p /usr/local/share/lua/5.3/lctags
/usr/bin/install -c libclanglua/core.so /usr/local/lib/lua/5.3/libclanglua
/usr/bin/install -c libclanglua/if.lua libclanglua/ifc.lua /usr/local/share/lua/5.3/libclanglua
if [ -f lsqlite3.so  ]; then /usr/bin/install -c lsqlite3.so /usr/local/lib/lua/5.3; fi
mkdir -p /usr/local/share/lua/5.3/lctags
/usr/bin/install -c lctags/_lctags.conf /usr/local/share/lua/5.3/lctags
/usr/bin/install -c lctags/*.lua /usr/local/share/lua/5.3/lctags
sed "s@.*-- replase by install@  self.clangIncPath = \"/usr/lib/llvm-6.0/lib/clang/6.0.1\"@g" lctags/config.lua > /tmp/config.lua
/usr/bin/install -c /tmp/config.lua /usr/local/share/lua/5.3/lctags
rm /tmp/config.lua
/usr/bin/install -c lctags/*.so /usr/local/lib/lua/5.3/lctags
make[2]: Leaving directory '/lctags/src'
make -C lctags install INST_DIR=/usr/local/bin \
    LUA_COMMAND=lua5.3 LUA_MOD_DIR=/usr/local/share/lua/5.3
make[2]: Entering directory '/lctags/src/lctags'
sed "s@lua5.3@lua5.3@g" lctags | \
    sed 's@${DIR}@'"/usr/local/share/lua/5.3/lctags@g" > /usr/local/bin/lctags
chmod +x /usr/local/bin/lctags
make[2]: Leaving directory '/lctags/src/lctags'
make[1]: Leaving directory '/lctags/src'

じゃ、テストしてみましょう。

#make test
(cd src; make test)
make[1]: Entering directory '/lctags/src'
make -C lctags LUA_COMMAND=lua5.3 LUA_INC=/usr/include/lua5.3 LUA_LIB= LUA_CFLAGS="-I/usr/include/lua5.3" LUA_LDFLAGS=-llua5.3 SO=so
make[2]: Entering directory '/lctags/src/lctags'
make[2]: Nothing to be done for 'all'.
make[2]: Leaving directory '/lctags/src/lctags'
lua5.3 lctags/lctags.lua init .   
/usr/include/stdio.h:33:10: fatal error: 'stddef.h' file not found
please set clang inc-path at getClangIncPath() in /lctags/src/lctags.conf, and retry init.
make[1]: *** [makefile:210: test] Error 1
make[1]: Leaving directory '/lctags/src'
make: *** [makefile:2: all] Error 2

なんでやねん。 このissueを見てると、build-essential入れとけって話のようなので、素直に入れときましょう。

github.com

でもって、再トライ

# make test
(cd src; make test)
make[1]: Entering directory '/lctags/src'
make -C lctags LUA_COMMAND=lua5.3 LUA_INC=/usr/include/lua5.3 LUA_LIB= LUA_CFLAGS="-I/usr/include/lua5.3" LUA_LDFLAGS=-llua5.3 SO=so
make[2]: Entering directory '/lctags/src/lctags'
make[2]: Nothing to be done for 'all'.
make[2]: Leaving directory '/lctags/src/lctags'
lua5.3 lctags/lctags.lua init .   
/usr/include/stdio.h:33:10: fatal error: 'stddef.h' file not found
please set clang inc-path at getClangIncPath() in /lctags/src/lctags.conf, and retry init.
make[1]: *** [makefile:210: test] Error 1
make[1]: Leaving directory '/lctags/src'
make: *** [makefile:2: all] Error 2

うーむ駄目か。

いさぎよく、build_for_aptの説明のところで動作確認したと言われている、debian9.1で再トライしてみます。 途中、何度かDo you want to continue? [Y/n]と聞かれるので、yes | make build_for_aptしといた方が無難ですね。

>docker run -it --rm debian:9.1 /bin/bash
# apt update
# apt install git make python sudo curl unzip
# git clone https://github.com/ifritJP/lctags.git
# cd lctags
# make build_for_apt
・
・
・
gcc Helper.c -std=c99 -fPIC -shared -DHELPER_STAND_ALONE -o Helper.so   -I/usr/include/lua5.3 -llua5.3 -lcrypto -lm -lpthread -lrt
Helper.c: In function 'helper_msleep':
Helper.c:322:5: warning: implicit declaration of function 'usleep' [-Wimplicit-function-declaration]
     usleep( ( msec % 1000 ) * 1000 );
     ^~~~~~
make[3]: Leaving directory '/lctags/src/lctags'
make[2]: Leaving directory '/lctags/src'
make[1]: Leaving directory '/lctags/src'

無事にインストールできました・・・orz

さっそくテストしてみましょう。

# make test
(cd src; make test)
make[1]: Entering directory '/lctags/src'
make -C lctags LUA_COMMAND=lua5.3 LUA_INC=/usr/include/lua5.3 LUA_LIB= LUA_CFLAGS="-I/usr/include/lua5.3" LUA_LDFLAGS=-llua5.3 SO=so
make[2]: Entering directory '/lctags/src/lctags'
make[2]: Nothing to be done for 'all'.
make[2]: Leaving directory '/lctags/src/lctags'
lua5.3 lctags/lctags.lua init .   
sync
rm -f 
emacs -batch -q -eval "(progn (add-to-list 'load-path \"lisp\") (load \"lctags-test.el\"))"
/bin/sh: 1: emacs: not found
makefile:210: recipe for target 'test' failed
make[1]: *** [test] Error 127
make[1]: Leaving directory '/lctags/src'
makefile:2: recipe for target 'all' failed
make: *** [all] Error 2

emacsを入れてもう一度テストすると

# apt install -y emacs
# make test
・
・
・
--- 3669,3673 ----
  make test-chg-proj
  /usr/bin/lua lctags/lctags.lua --lctags-form json set-projDir . test@test2 > /dev/null
  /usr/bin/lua lctags/lctags.lua --lctags-form json dump file | grep test/ > /dev/null && (echo ==== NG set-projDir ====; exit 1)
! makefile:476: recipe for target 'test-chg-proj' failed
  /usr/bin/lua lctags/lctags.lua --lctags-form json set-projDir . test2@test > /dev/null
makefile:210: recipe for target 'test' failed
make[1]: [test] Error 1 (ignored)
make[1]: Leaving directory '/lctags/src'

うむ。駄目っぽいです・・・o....rz

この後make cleanしてmake testすると、stddefが無いエラーが再発しました。 とりあえずlctagsコマンド自体はビルドできてるっぽいので、インストールして初期化してみると

#make install
# lctags init .
/usr/include/stdio.h:33:11: fatal error: 'stddef.h' file not found
please set clang inc-path at getClangIncPath() in /lctags/lctags.conf, and retry init.

dockerイメージだと、インストーラから入れたものと比べると大幅にパッケージが減らされているんで、たぶん何かしら不足してるパッケージがあるんだと思いますが、これ以上追求していくのも大変そうなので、今回は諦めます。

これが使えると、いろいろとはかどりそうだったんで期待してたんですが、残念です。

ifritjp.github.io

*1:たしか、GNU Globalの作者の人がGlobalの本かウェブページで書いてた記憶が・・・