はじめに
実際のC++のプログラムはソースコードのファイルが単一である場合はほとんど無いです。少なくともプログラム本体とヘッダーファイルは分離して扱います。また、OpenBabelやRDKit等のライブラリも複数リンクすることも一般的です。というか、OpenBabelが既に複数ライブラリをリンクしているので、
自作プログラムも複数ライブラリをリンクせざるを得ないと言うべきでしょう。このような時にCMakeというツールを利用すると便利です。
昔からUnixやLinuxではGNU MakeというツールがC++のビルドに利用されていましたが、CMakeはGNU Make用のMakefileというファイルを自動生成するツールです。FortranやC/C++などのコンパイル型言語で作成されているオープンソースのソフトウェアは大体CMakeを利用しています。もちろんOpenBabelもRDKitもCMakeを利用しています。したがって、そのようなオープンソースを利用する場合にはCMakeについての知識が無いとビルドすら出来ないという事になります。
CMakeは最初からインストールされていない場合があります。以下のコマンドでインストールしてください。
Ubuntuの場合
$ sudo apt install cmake
Fedoraの場合
$ sudo dnf install cmake
簡単なビルド方法
先ず適当なC++プログラムを用意しました。以下のダウンロードボタンをクリックしてzipファイルをダウンロードしてみてください。
適当なディレクトリを作成してzipファイルを解凍すると以下の表の通り、4個のファイルが出てきます。
ファイル名 | 説明 |
---|---|
CMakeLists.txt | CMakeの本体 |
main.cpp | C++プログラムのメイン部分 |
mylib.cpp | C++プログラムのライブラリ部分 |
mylib.hpp | C++プログラムのライブラリ部分のヘッダー |
上記4個のファイルを確認出来たら、早速ビルドしてみましょう。先ずビルド用の適当なディレクトリを作成します。ここでは”build”という名前にします。
$ mkdir build
そしてそのディレクトリの中に移動します。
$ cd build
そこでcmakeコマンドを実行します。
$ cmake ..
cmakeの実行が終了したら、Makefileが作成されるのでmakeします。
$ make
makeが終了したら、”myexe”というファイルが作成されています。とりあえずここまででビルドは完了です。最後にmyexeを実行してみましょう。
$ ./myexe
ワンワン、僕はタロウだワン!
まっ、サンプルプログラムなんでね。
CMakeLists.txtの超初歩的な書き方
早速CMakeLists.txtの内容を見てみましょう。このCMakeLists.txtについては私がなるべくシンプルになるように書きました。
1 cmake_minimum_required (VERSION 3.0)
2 project (test)
3
4 # コンパイラ設定
5 set(CMAKE_C_COMPILER gcc)
6 set(CMAKE_CXX_COMPILER g++)
7
8 # library
9 add_library(mylib STATIC mylib.cpp)
10
11 # executable
12 add_executable(myexe main.cpp)
13
14 # link
15 target_link_libraries(myexe mylib)
16
17
18 # install
19 install(TARGETS myexe mylib
20 RUNTIME DESTINATION bin
21 LIBRARY DESTINATION lib
22 ARCHIVE DESTINATION lib
23 )
24 install(FILES mylib.hpp DESTINATION include)
1~2行目はCMakeの最低バージョンとプロジェクト名を設定しています。5~6行目はコンパイラを設定しています。デフォルトはgccである場合がほとんどかと思われるので、gccを利用する場合のこの記述は必要ないです。例えばclangや商用コンパイラを利用する場合はここで設定します。9行目では自作のライブラリを作成する設定です。動的ライブラリを作成する場合は”STATIC”の代わりに”SHARED”と記述します。12行目では実行ファイルを作成するためのソースファイルを指定しています。
そして15行目でライブラリmylibをリンクする設定を記述しています。実はこの例のように簡単にファイルを分割しただけの場合は、わざわざライブラリ作成しなくても以下のようにadd_executableのところで使用するファイルを並べればOKです。
add_executable(myexe main.cpp mylib.cpp)
これでも動くとは思いますが、mylibは必要なくなるのでmylibが関わっている9行目と15行目をコメントアウトして、19行目のmylibのみ削除した方が良いです。
19~24行目はインストールの設定を記述しています。これはmake install
とコマンド実行したときに何を何処にインストールするかを記述しています。この例ではmyexeという実行ファイルをbinの下に、libmylib.aというライブラリファイルをlibの下にインストールします。デフォルトのインストール先は”/usr/local”なので、実行ファイルは”/usr/local/bin”でライブラリは”/usr/local/lib”にインストールされます。”/usr/local”以外にインストールしたい場合はcmakeコマンド実行時に以下のようにディレクトリ指定します。
$ cmake -DCMAKE_INSTALL_PREFIX=$(任意のディレクトリ) ..
外部のライブラリを利用する
外部のライブラリを利用する場合はgccにインクルードのパスと、リンクするライブラリを指定します。CMakeにおいてもこの2点を指定します。インクルードパスの指定は”include_directories”を利用して設定します。リンクするライブラリは前述したCMakeLists.txtファイル中の”target_link_libraries”のカッコの中に付け足します。
外部ライブラリを利用する例としてBoostを利用した以下のようなmain.cppを少し改造したプログラムを考えてみます。
1 // C++
2 #include <iostream>
3 #include <fstream>
4 #include <string>
5
6 // BOOST
7 #include <boost/iostreams/filtering_stream.hpp>
8 #include <boost/iostreams/filter/gzip.hpp>
9
10 using namespace std;
11
12 #include "mylib.hpp"
13
14 int main(int argc, char* argv[]) {
15 Animal* x = new Dog("タロウ");
16 ofstream ofs("output");
17 boost::iostreams::filtering_ostream zout;
18 zout.push(boost::iostreams::gzip_compressor());
19 zout.push(ofs);
20 zout << x->cry() << endl;
21 boost::iostreams::close(zout);
22
23 return 1;
24 }
このプログラムは標準出力していたメッセージをgzipで圧縮して”output”というファイルに出力します。main.cppをこの様に改造した場合のCMakeLists.txtの15行目にBoostのライブラリを追加します。
15 target_link_libraries(myexe mylib /usr/lib/x86_64-linux-gnu/libboost_iostreams.so)
ただし、これは私のLinux環境の場合では正常動作しますが、他の環境(例えばFedora)ではエラーになると思います。わざわざCMakeを利用する意義は、この環境の違いを解決することにあります。
ライブラリの検索は”find_package”を利用します。boostライブラリのiostreamsを利用したい場合は以下のようにCMakeLists.txtを修正します。修正点は11~12行目と18行目です。先ず11行目ではboostライブラリを検索しています。find_packageによりboostがシステム内に発見できれば、インクルードパスとライブラリがそれぞれBoost_INCLUDE_DIRSとBoost_LIBRARIESという変数に入ります。そして18行目に検索で得られたライブラリを追加してリンクしています。
1 cmake_minimum_required (VERSION 3.0)
2 project (test)
3
4 # コンパイラ設定
5 set(CMAKE_C_COMPILER gcc)
6 set(CMAKE_CXX_COMPILER g++)
7
8 # library
9 add_library(mylib STATIC mylib.cpp)
10
11 find_package(Boost REQUIRED COMPONENTS iostreams)
12 include_directories(${Boost_INCLUDE_DIRS})
13
14 # executable
15 add_executable(myexe main.cpp)
16
17 # link
18 target_link_libraries(myexe mylib ${Boost_LIBRARIES})
19
20
21 # install
22 install(TARGETS myexe mylib
23 RUNTIME DESTINATION bin
24 LIBRARY DESTINATION lib
25 ARCHIVE DESTINATION lib
26 )
27 install(FILES mylib.hpp DESTINATION include)
検索できるライブラリのリストは以下のコマンド得られます。
$ cmake --help-module-list | grep -e '^Find'
このコマンドで得られるリストにライブラリが無い場合は、pkg_check_modulesというコマンドを利用することができます。これはlinuxの”pkg-config”コマンドを利用して得られる情報を返します。例えばEigenという線型代数ライブラリはfind_packageではサポートされていません。(cmakeのバージョン3.16の場合)このような場合にはpkg_check_modulesを利用します。例えば以下のようになります。
find_package(PkgConfig)
pkg_check_modules(Eigen3 REQUIRED eigen3)
ここで”find_package(PkgConfig)”はpkg_check_modulesを利用するために必要なコマンドで、CMakeLists.txtの最初の方に書いておくと良いでしょう。
おわりに
main.cppとCMakeLists.txtを前述した様に書き換えたら、ビルドと実行してみましょう。
$ mkdir build
$ cd build
$ cmake ..
$ ./myexe
実行すると”output”というファイルが作成されます。これはプログラムから表示れるメッセージをgzip圧縮したものです。その内容を確認する場合は以下のコマンドを実行してください。
$ gunzip -c < output
CMakeLists.txtファイルが読めるようになれば、スパコンなどの特殊な環境においてもオープンソースのソフトをビルドできるようになるかと思います。
Category: Linux関連, プログラミング関連