LoginSignup
2
2

C++のためのConan2とCMake(Linux)

Last updated at Posted at 2024-03-03

はじめに

 本記事はライブラリを簡単に使用するため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によりインストールします。

main.cpp
#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の最下部辺りに書かれています。

conanfile.txt
[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を作成します。以下はその例です。

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の公式が出しているシェルスクリプトを紹介しましょう。

run.sh
#!/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の隣に並べて配置して、ただ実行するだけでビルドが出来るようになるのですごく便利です。

参考

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2