はじめに
本記事はライブラリを簡単に使用するためConan2の使い方を紹介します。また、C++のコンパイルを簡単化するため、Conan2を絡めてCMakeの使い方を紹介します。
環境
・2024/02/26
・GitHub Codespaces
・CMake v3.16.3
・Conan v2.1.0
Conan2とは
Conan2はC/C++のライブラリ管理ツールです。このツールを使用するとプロジェクトに使用するさまざまなライブラリをインストールし、管理することができます。
CMakeとは
CMakeはクロスプラットフォームのビルド自動化ツールです。CMkaeはプロジェクトのソースコードを解析し、適切なビルド手順を生成するため、ビルド手順の記述が簡単であり、プラットフォーム間の移植性が高いという特徴を持ちます。
インストール方法
Conan2のインストール
Conan2をインストールするためのコマンドは以下の通りです。
sudo pip install conan
CMakeのインストール
Ubuntuを例としたCMakeインストールのためのコマンドは以下の通りです。APTはOSにより変更してください。
sudo apt update
sudo apt install cmake
デモンストレーションの準備
本記事ではConan2とCMakeの使い方を説明するため、デモンストレーションのコードを用意します。以下がそのコードです。このコードはGitHubのディスカッションから引用しています。
コードはCMakeによりビルドします。zlibはConan2によりインストールします。
#include <iostream>
#include <string.h>
#include <stdexcept>
#include <iomanip>
#include <sstream>
#include <zlib.h>
class myzlib {
public:
std::string compress(const std::string&, int);
std::string decompress(const std::string&);
};
/** Compress a STL string using zlib with given compression level and return* the binary data. */
std::string myzlib::compress(const std::string& str, int compressionlevel = Z_BEST_COMPRESSION) {
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
if (deflateInit(&zs, compressionlevel) != Z_OK)
throw(std::runtime_error("deflateInit failed while compressing."));
zs.next_in = (Bytef*)str.data();
zs.avail_in = str.size(); // set the z_stream's input
int ret;
char outbuffer[10240];
std::string outstring;
// retrieve the compressed bytes blockwise
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out) {
// append the block to the output string
outstring.append(outbuffer,
zs.total_out - outstring.size());
}
} while (ret == Z_OK);
deflateEnd(&zs);
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
std::ostringstream oss;
oss << "Exception during zlib compression: (" << ret << ") " << zs.msg;
throw(std::runtime_error(oss.str()));
}
return outstring;
}
/** Decompress an STL string using zlib and return the original data. */
std::string myzlib::decompress(const std::string& str) {
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
if (inflateInit(&zs) != Z_OK)
throw(std::runtime_error("inflateInit failed while decompressing."));
zs.next_in = (Bytef*)str.data();
zs.avail_in = str.size();
int ret;
char outbuffer[10240];
std::string outstring;
// get the decompressed bytes blockwise using repeated calls to inflate
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = inflate(&zs, 0);
if (outstring.size() < zs.total_out) {
outstring.append(outbuffer,
zs.total_out - outstring.size());
}
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
std::ostringstream oss;
oss << "Exception during zlib decompression: (" << ret << ") "
<< zs.msg;
throw(std::runtime_error(oss.str()));
}
return outstring;
}
myzlib zlib;
int main() {
std::string data = "halloweeks halloweeks halloweeks halloweeks";
std::cout << "Orignal string size: " << data.size() << "\n";
std::cout << "Original string: " << data << "\n";
std::cout << "--------------------\n";
// zlib compress data. last parameter is 6 for best compression
std::string com = zlib.compress(data, 6);
std::cout << "Compressed string size: " << com.size() << "\n";
std::cout << "Compressed string: " << com << "\n";
std::cout << "--------------------\n";
// zlib decompress data
std::string decom = zlib.decompress(com);
std::cout << "Decompressed string size: " << decom.size() << "\n";
std::cout << "Decompressed string: " << decom << "\n";
return 0;
}
Conan2の使い方
全体の流れ
説明の流れはConan2のチュートリアルに沿います。チュートリアルの流れは以下の通りです。
- conanfile.txtファイルの作成
- conanfile.txtは、プロジェクトが依存するライブラリやそのバージョン、ビルドオプションなどの情報が記述される設定ファイルです。
- Conanプロファイルの作成
- Conanプロファイルは、コンパイルの設定やビルド設定、アーキテクチャなどの情報が記述される設定ファイルです。
- ライブラリのインストール
- conanfile.txtに示したプロジェクトが依存するライブラリをインストールします。
conanfile.txtファイルの作成
conanfile.txtファイルをプロジェクトのルートで作成します。このconanfile.txtの書き方は、ほとんどのライブラリはConan Centerの最下部辺りに書かれています。
[requires]
zlib/1.3.1
[generators]
CMakeDeps
CMakeToolchain
conanfile.txtは見ての通り大きく2つのセクションに分けています。それぞれ説明します。
requires
requiresセクションは、プロジェクトで使用したいライブラリを宣言する場所です。
Conan2でインストール可能なライブラリはライブラリ一覧はConan Centerで確認できます。また、ライブラリ名を知っているのであれば、コマンドでインストール可能ライブラリを確認できます。以下はzlibを検索しているコマンドの例です。
conan search zlib
検索の結果は次のようになります。
$ conan search zlib
Found 6 pkg/version recipes matching zlib in conancenter
conancenter
zlib
zlib/1.2.8
zlib/1.2.11
zlib/1.2.12
zlib/1.2.13
zlib/1.3
zlib/1.3.1
generators
generatorsセクションは、コンパイラやビルドシステムが依存関係を見つけ、プロジェクトをビルドするために使用するファイルを生成するようにConanに指示します。この場合、私たちのプロジェクトはCMakeをベースにしているので、CMakeDepsを使用してZlibライブラリファイルがインストールされている場所に関する情報を生成し、CMakeToolchainを使用してCMakeツールチェーンファイルを使用してCMakeにビルド情報を渡します。
Conanプロファイルの作成
Conanプロファイルは、コンパイラ、ビルド設定、アーキテクチャ、共有ライブラリやスタティックライブラリなどの設定ファイル群をまとめて定義することができます。Conanはデフォルトではプロファイルを自動検出しようとしないので、プロファイルを作成する必要があります。Conanに現在OSのプロファイリングを行わせるために、以下のコマンドを実行する必要があります。
conan profile detect --force
ライブラリのインストール
conanfile.txtで依存関係を示したライブラリをインストールします。
conanfile.txtが存在するディレクトリで以下のコマンドを実行してください。
conan install . --output-folder=build --build=missing
もしくは以下のコマンドでも同様の処理が行えます。いずれのパスもconanfile.txtの存在を指し示すことができれば問題ないです。
conan install conanfile.txt --output-folder=build --build=missing
オプションについて解説します。
output-folder
output-folderは、インストールされたファイルや生成された設定ファイルを保存するディレクトリを指定します。上記のコマンドではbuildディレクトリを指定しています。
build
buildは依存関係のパッケージがビルド済みでない場合に、必要なパッケージをビルドするよう指示します。つまり、上記のコマンドではmissingを指定することで、Conanは依存関係の中でビルド済みのパッケージが見つからない場合に、そのパッケージを自動的にビルドするよう指示しています。
このオプションはbuildディレクトリを自動で作成してくれます。
CMakeの使い方
全体の流れ
CMakeを使用してプログラムをコンパイルするため、以下の流れを行う必要がある。
- CMakeLists.txtファイルの作成
- CMakeLists.txtはプロジェクトの構造やビルド手順が記述されたファイルである。
- ビルドディレクトリの作成
- プロジェクトのビルドに使用するディレクトリを作成します。通常、buildという名前のディレクトリを作成します。
- CMakeの実行
- ビルドディレクトリでCMakeを実行します。例えば、cmake /path/to/sourceというコマンドを使用します。これにより、CMakeはCMakeLists.txtファイルを読み込み、ビルドに必要なファイルや設定を生成します。
- ビルド
- ビルドディレクトリでcmake --buildというコマンドを使用し、 CMakeが生成したビルドシステム(MakefileやVisual Studioのソリューションファイルなど)を使用して、プロジェクトをビルドします。
CMakeLists.txtファイルの作成
CMakeを扱う上で最重要ともいえるCMakeLists.txtファイルの作成方法を説明します。
プロジェクトのルートディレクトリにCMakeLists.txtを作成します。以下はその例です。
cmake_minimum_required(VERSION 3.15)
project(compressor CXX)
find_package(ZLIB REQUIRED)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} ZLIB::ZLIB)
各行を説明します。
cmake_minimum_required
cmake_minimum_requiredはCMakeの最小バージョンを指定します。このプロジェクトではCMakeバージョン3.16以上が必要です。
project
projectはプロジェクト名と使用するプログラム言語を設定します。このプロジェクトではプロジェクト名はcompressorとしています。CXXはC++言語を使用することを指定しています。
find_package
find_packageはパッケージを探索します。このプロジェクトで使用するzlibですので、今回はzlibを指定しています。このzlibという語彙は必ずしもプログラムコード中で使用する語彙と同等ではなく、例えばコード中でzlibと書かれていたのが、find_packageでZLIBと大文字で書かれています。この語彙はほとんどのライブラリはconan centerの最下部に書いてあるので、好きなライブラリをインストールする際は確認するべきです。
add_executable
コンパイルするソースコードのファイル名とコンパイル後の実行可能ファイル名を指定します。
target_lik_libraries
ターゲットにConanで解決されたライブラリをリンクします。この場合はZLIB:ZLIBと書いてありますが、この語彙もfind_packageと同じくconan centerの最下部に書いてあるので、ライブラリをインストールする際は確認するべきです。
ビルドディレクトリの作成
Conan2とCMakeを統合する場合、ビルドディレクトリは普通Conan2によって建てられます。それは前述した Conan2の使い方 にある ライブラリのインストール のコマンドを実行するだけで成されます。
これまでのコマンドが正常に動作しているのなら、「build」ディレクトリが作成されているはずです。
CMakeの実行
buildディレクトリをカレントディレクトリとして作業をします。
cd build
CMakeを実行しCMakeLists.txtファイルを読み込み、ビルドに必要なファイルや設定を生成します。
CMakeを実行するため、以下のコマンドを実行します。
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmakeは、Conanが生成したCMakeツールチェーンファイルを指定しています。DCMAKE_BUILD_TYPE=Releaseは、リリース用のビルドを行うことを指定しています。
ビルド
カレントディレクトリはbuildのままです。
プロジェクトをビルドするため以下のコマンドを実行します。
cmake --build .
コマンド
これまでに出てきたコマンドはそれなりに多いのでいちいちビルドのたびに打っているのでは面倒です。そこでコマンド入力を簡単化するため、Conan2の公式が出しているシェルスクリプトを紹介しましょう。
#!/bin/bash
set -e
set -x
BASEDIR=$(dirname "$0")
pushd "$BASEDIR"
rm -rf build
conan install . --output-folder=build --build=missing
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
./compressor
プロジェクトディレクトリにmain.cppとCMakeLists.txtとconanfile.txtの隣に並べて配置して、ただ実行するだけでビルドが出来るようになるのですごく便利です。
参考