はじめに
今回の話題はRDKitを利用したC++プログラムについてCMakeを適用しようと思います。今までCMakeについて2回ブログを書きましたが、実はこの話をブログにしたいからでした。RDKitというライブラリはOpenBabelに比べてC++プログラムの作成が面倒です。OpenBabelはリンクすべきライブラリのファイルが1個だけなのに比べて、RDKitは何十個もあるためです。OpenBabelの様に1行のコマンドで簡単にビルドすることは難しいのです。そこでCMakeの力を借りて簡単にビルドしようと目論見ました。一旦CMakeLists.txtのひな型が出来れば、今後紹介するRDKit関連のC++プログラムを紹介しやすくなるので、なるべく単純な形に作成してみました。
RDKitのインストール
RDKitのインストールの方法は幾つかありますが、今回は最も簡単なMinicondaを利用する方法を紹介します。
1. Minicondaのインストール
既にMinicondaやAnacondaがインストールされている場合はこのステップは飛ばしてください。Minicondaは以下のURLからダウンロードできます。
Miniconda (https://docs.conda.io/en/latest/miniconda.html)
“Miniconda3 Linux 64-bit”をクリックして拡張子がshのファイルをダウンロードします。そして以下のコマンドでインストールします。
$ sh Miniconda3-latest-Linux-x86_64.sh
幾つか質問されるが全て”Enter”か”yes”と答えましょう。インストール終了後に以下のコマンドで仮想環境を自動で有効化されることを無効化すると良いです。
$ conda config --set auto_activate_base false
2. 仮想環境の作成
RDKit用の仮想環境を作成します。仮想環境を作成する際にPythonのバージョンも選べるのですが、今回は少し前のバージョンを指定します。
$ conda create -n rdkit python=3.9
3. RDKitのインストール
最新版のRDKitは以下のコマンドでインストールします。
$ conda install -c conda-forge rdkit
以前は”rdkit”というチャネルを利用していたのですが、古いバージョンしかインストールできないので、今回は”confa-forge”というチャネルを利用しています。
4. 確認
先ず仮想環境を有効化します。
$ conda activate rdkit
そして以下のコマンドを実行して特にエラーメッセージが出なければ成功です。
$ python -c "import rdkit"
RDKit用のCMakeLists.txt
RDKitを利用した非常に簡単なC++プログラムと、それをビルドするCMakeLists.txtを用意しました。以下のボタンをクリックしてzipファイルをダウンロードしてください。
適当なディレクトリを作成してzipファイルを解凍すると以下の表の通り、2個のファイルが出てきます。
ファイル名 | 説明 |
---|---|
CMakeLists.txt | CMakeの設定ファイル |
Test.cpp | C++プログラム |
そしてCMakeLists.txtファイルの内容は以下の通りです。
1 cmake_minimum_required (VERSION 3.0)
2 project (test)
3 set(CMAKE_VERBOSE_MAKEFILE ON)
4
5 # コンパイラ設定
6 set(CMAKE_C_COMPILER gcc)
7 set(CMAKE_CXX_COMPILER g++)
8
9 # C++17をサポートしているならば利用する。
10 include(CheckCXXCompilerFlag)
11 check_cxx_compiler_flag("-std=c++17" STD_CXX17)
12 if(STD_CXX17)
13 set(CMAKE_CXX_FLAGS "-std=c++17 ${CMAKE_CXX_FLAGS}")
14 else()
15 message(WARNING "The compiler ${CMAKE_CXX_COMPILER} has no C++17 support.")
16 endif()
17
18 # RDkit
19 set(RDKit_DIR $ENV{HOME}/miniconda3/envs/rdkit)
20 if(RDKit_DIR)
21 find_path(RDKit_INCLUDE_DIR NAMES GraphMol/RDKitBase.h PATHS ${RDKit_DIR}/include PATH_SUFFIXES rdkit NO_DEFAULT_PATH)
22 find_path(RDKit_LIBRARY_DIR NAMES libRDKitGraphMol.so PATHS ${RDKit_DIR}/lib NO_DEFAULT_PATH)
23 else()
24 find_path(RDKit_INCLUDE_DIR NAMES GraphMol/RDKitBase.h)
25 find_path(RDKit_LIBRARY_DIR NAMES libRDKitGraphMol.so)
26 endif()
27 if(RDKit_INCLUDE_DIR)
28 message(STATUS "RDKit found.")
29 link_directories(${RDKit_LIBRARY_DIR})
30 include(${RDKit_LIBRARY_DIR}/cmake/rdkit/rdkit-config-version.cmake)
31 set(RDKit_VERSION "${PACKAGE_VERSION}")
32 file(GLOB RDKit_Files ${RDKit_LIBRARY_DIR}/libRDKit*.so)
33 foreach(_variable ${RDKit_Files})
34 string(REGEX REPLACE "^..*lib(RDKit.+)\\.so$" "\\1" TMP1 ${_variable})
35 list(APPEND RDKit_LIBRARIES ${TMP1})
36 endforeach()
37 else()
38 message(FATAL_ERROR "Could not find RDKit.")
39 endif()
40 include_directories(${RDKit_INCLUDE_DIR})
41
42 # executable
43 add_executable(Test Test.cpp)
44
45 # link
46 target_link_libraries(Test ${RDKit_LIBRARIES} python3 boost_python39)
それでは解説します。先ず9~16行目ですが、この部分はC++17という2017年に標準規格化されたC++のバージョンが利用できるコンパイラかどうか判定しています。もし利用可能であれば、13行目で”-std=c++17″というコンパイルオプションを追加します。これはRDKitの最新版はC++17を要求する設計になっているための措置ですが、個人的には無理に新しい規格のC++を利用しなくても良いんじゃないかなぁと感じます。ちなみに私のGCCのバージョンは9.4です。
続く18~40行目がRDKitを検索する部分で、このブログの本題部分です。19行目でRDKitがインストールされているディレクトリを指定しています。この部分は環境によって異なるので、適切なディレクトリを記述してください。この例では「Minicondaでrdkitという仮想環境でRDKitをインストールした場合」のディレクトリとなります。20~26行目でインクルードパスとライブラリパスを取得します。27行目ではインクルードパスが取得出来たかどうか判定しています。取得できていない場合は38行目に飛んでエラー終了します。
28~36行目は少し複雑なことをしています。先ず、29行目ではリンクディレクトリを設定しています。
30~31行目ではRDKitのバージョンを取得しています。RDKitをインストールすると”rdkit-config-version.cmake”というCMake用のモジュールファイルもインストールされるので、それをインクルードすることによってバージョンを取得します。そして32行目ではRDKitに関係するライブラリを全て取得しています。Linuxにおける共有ライブラリは拡張子がsoというファイルなので、ファイル名の先頭が”libRDKit”であるsoファイルを全て取得することにしています。本来なら必要なライブラリだけをリンクすべきなのですが、RDKitの場合は何が必要なライブラリなのか調べるのが大変面倒なので、
全部リンクすることにしました。33~36行目では取得したライブラリのファイル名を正規表現を利用して加工しています。コンパイラの”-l”オプションでライブラリ名を指定する場合は、先頭の”lib”と末尾の”.so”は不要なので、この様な加工を施しています。実は無理してこのようなファイル名の加工しなくても、リンカーにフルパスを記述すれば済む話なのですが、CMakeで正規表現を利用する場合の例を示したかったので入れてみました。そして40行目でインクルードパスを設定しています。
上記のRDKit部分が実行されると、”RDKit_INCLUDE_DIR”にはインクルードパスが代入され、”RDKit_LIBRARIES”にはRDKitに関する全てのライブラリが代入され、”RDKit_VERSION”にはRDKitのバージョンが代入されます。
最後に46行目でtarget_link_librariesコマンドで必要なライブラリを全て記述するのですが、RDKit_LIBRARIESだけでなく”python3″と”boost_python39″も記述する必要があります。特にboostの方はインストールしたpythonのバージョンによって数字部分が変化するので注意が必要です。
ビルドしてみる
それではビルドしてみましょう。以下の一連のコマンドを実行します。
$ mkdir build
$ cd build
$ cmake ..
$ make
最後に”Test”という実行ファイルが出来ていれば成功です。このプログラムは”Cc1ccccc1″というトルエンのSMILESを分子として読み込んで、SD形式で標準出力するというものです。”Test”を実行すると以下のように出力されるはずです。
$ ./Test
Toluene
RDKit 2D
7 7 0 0 0 0 0 0 0 0999 V2000
3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0
2 3 2 0
3 4 1 0
4 5 2 0
5 6 1 0
6 7 2 0
7 2 1 0
M END
そもそもRDKitはPythonから利用することを想定して作られているため、こんなに面倒なのかもしれません。
Category: Linux関連, RDKit, プログラミング関連