Conan C/C++ Package Managerを使ってみた

[Fujitsu Advent Carendar](http://qiita.com/advent-calendar/2016/fujitsu_ext) 18日目の記事です。

# はじめに
Qiitaへの初めての投稿です。
普段、主にC++で組み込みLinuxのアプリケーション開発を行っています。

あるアプリケーションやライブラリを開発する時、OSSのライブラリなどを利用することにより、その開発対象が他のライブラリに依存する、ということがよくあります。
依存するライブラリが多くなると、開発対象をbuildしたりUnitTestできるようにするための準備(開発マシンへのライブラリのインストールなど)の手番が多くなり、面倒に感じていました。
この課題を解決できそうなConanというパッケージマネージャを見つけ、実際に使ってみたところ、かなり便利だったので、Conanの紹介を兼ねて記事にすることにしました。

# Conanって何?
本家 [Conan C/C++ Package Manager](https://conan.io/) のページ中ほどにあるFEATURESのところにいろいろと説明が書いてあるので、詳細は省きますが

* プロジェクトが依存するライブラリなどを簡単にインストールできる
* 条件(OS, CPU, コンパイラなど)が合うものがあればbuild済みのバイナリパッケージのみインストールされる (なのでbuildする必要がない)
* 条件が合わない場合であっても、ソースからのbuildできる
* パッケージを自分で作成することもできる(また、それをサーバにuploadして他の人に利用してもらうこができる)

・・・ということができる開発者向けのパッケージ管理システムです。

# 使ってみる

記事が長くなりそうなので、今回はconanのインストール～既存パッケージの利用するところに絞って記載します。(新規パッケージの作成は別の記事で投稿したいと思います)

Ubuntu 16.04(VM)上にinstallし、実際に使ってみました。
C/C++ソースのbuildに必要なgcc/makeなどのツール類(Debian系であれば、`build-essential`に含まれるようなもの)予めinstallは済ませてある状態から始めてみます。

## conanのインストール
[本家(conan.io)のDownloadページ](https://conan.io/downloads)の通り、install方法はいろいろありますが、今回は最も簡単な `pip` を利用てinstall しました。

```shell-session
gasuketsu@ubuntu:~$ sudo pip install conan
```

管理者権限が無い場合は `--user` オプションをつければインストールできます。

```shell-session
gasuketsu@ubuntu:~$ pip install --user conan
```

`conan --version` でバージョン情報が出力されれば準備完了です。
`--user` を指定した場合は、 `PATH` の設定などが必要になると思います。
(↓の例では conanの初回実行のため、conan関連のconfigファイルの自動生成に関する出力も一緒に出ています。)

```shell-session
gasuketsu@ubuntu:~$ conan --version

It seems to be the first time you run conan
Auto detecting your dev setup to initialize conan.conf
Found gcc 5.4
Default conan.conf settings
	os=Linux
	arch=x86_64
	compiler=gcc
	compiler.version=5.4
	compiler.libcxx=libstdc++
	build_type=Release
*** You can change them in ~/.conan/conan.conf ***
*** Or override with -s compiler='other' -s ...s***


Conan version 0.17.1
gasuketsu@ubuntu:~$
```

## パッケージを利用する
conan.io ではパッケージのホスティングサービスも行っており、OSSであれば無料でパッケージをuploadすることができます。もちろん、upload済みのパッケージはどなたでも利用可能です。GoogleTest、Boost、Poco、ZeroMQ(libzmq)など、ポピュラーなライブラリのパッケージは既にuploadされています。

ここでは、conanのドキュメントに登場する、Pocoのパッケージを利用するexampleプロジェクト (https://github.com/memsharded/example-poco-timer.git) を使って、パッケージの利用方法を確認してみます。

```shell-session
gasuketsu@ubuntu:~$ git clone https://github.com/memsharded/example-poco-timer.git
```

このプロジェクトのソースコードは、以下の通り、Pocoを利用するごく単純なアプリケーションです。

```cpp:timer.cpp
// $Id: //poco/1.4/Foundation/samples/Timer/src/Timer.cpp#1 $
// This sample demonstrates the Timer and Stopwatch classes.
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
// SPDX-License-Identifier:     BSL-1.0

#include "Poco/Timer.h"
#include "Poco/Thread.h"
#include "Poco/Stopwatch.h"
#include <iostream>

using Poco::Timer;
using Poco::TimerCallback;
using Poco::Thread;
using Poco::Stopwatch;

class TimerExample{
public:
        TimerExample(){ _sw.start();}

        void onTimer(Timer& timer){
                std::cout << "Callback called after " << _sw.elapsed()/1000 << " milliseconds." << std::endl;
        }
private:
        Stopwatch _sw;
};

int main(int argc, char** argv){
        TimerExample example;
        Timer timer(250, 500);
        timer.start(TimerCallback<TimerExample>(example, &TimerExample::onTimer));

        Thread::sleep(5000);
        timer.stop();
        return 0;
```

### 利用するパッケージの定義

`conanfile.txt` という名前のファイルで、このプロジェクトのbuildに必要なconanパッケージに関する情報を定義しています。

```ini:conanfile.txt
[requires]
Poco/1.7.3@lasote/stable

[generators]
cmake
```

`[requires]` にはこのプロジェクトが依存するパッケージが記載されています。
`[generators]`には依存パッケージが提供するヘッダやライブラリなどを利用するために必要となるインクルードパスやライブラリパスの設定を記述したファイルをどんなフォーマットで生成するか、を指定しています。(このプロジェクトはCMakeを利用しているため、 `cmake` を指定しています)

### パッケージのインストール
`conanfile.txt`が格納されているディレクトリで 以下のコマンドを実行するだけで、必要となるパッケージが全てインストールされます。exampleの直接の依存はPocoだけでしたが、Pocoのパッケージ自体がzlibやOpenSSLなどのパッケージに依存しており、これらすべてがインストールされます。

```shell-session
gasuketsu@ubuntu:~/example-poco-timer$ conan install --build=missing
```

OSやCPUアーキテクチャ、コンパイラなど、conan.ioにupload済みのバイナリパッケージの中に条件を満たすものがあればバイナリパッケージを入手し、インストールしてくれます。条件を満たすものがなければ、ソースからのビルド/インストールを実行してくれます(ローカルの環境に合うバイナリパッケージをその場で作ってくれる、ということです。)

準備完了。あとは、このプロジェクトのbuildを行うだけです。

```shell-session
gasuketsu@ubuntu:~/example-poco-timer$ cmake .
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Current conanbuildinfo.cmake directory: /home/gasuketsu/example-poco-timer
-- Conan: Using cmake global configuration
-- Configuring done
-- Generating done
-- Build files have been written to: /home/gasuketsu/example-poco-timer
gasuketsu@ubuntu:~/example-poco-timer$ make
Scanning dependencies of target timer
[ 50%] Building CXX object CMakeFiles/timer.dir/timer.cpp.o
[100%] Linking CXX executable bin/timer
[100%] Built target timer
gasuketsu@ubuntu:~/example-poco-timer$
```

なお、`CMakeLists.txt`は↓のような内容になっています。

```cmake:CMakeLists.txt
project(FoundationTimer)
cmake_minimum_required(VERSION 2.8.12)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

add_executable(timer timer.cpp)
target_link_libraries(timer ${CONAN_LIBS})
```

ここでのポイントは、
* `include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)` と `conan_basic_setup()` で、依存パッケージをリンクするために必要な変数が設定される
* `target_link_libraries(timer ${CONAN_LIBS})` の `${CONAN_LIBS}` に依存パッケージのすべてのライブラリが含まれている
・・・というところでしょうか。

build完了後、アプリケーションを動かしてみます。

```shell-session
gasuketsu@ubuntu:~/example-poco-timer$ ./bin/timer 
Callback called after 250 milliseconds.
Callback called after 756 milliseconds.
Callback called after 1250 milliseconds.
Callback called after 1751 milliseconds.
Callback called after 2251 milliseconds.
Callback called after 2750 milliseconds.
Callback called after 3249 milliseconds.
Callback called after 3751 milliseconds.
Callback called after 4250 milliseconds.
Callback called after 4754 milliseconds.
gasuketsu@ubuntu:~/example-poco-timer$ 
```

# 最後に
pacakgeを利用するための準備もとてもシンプルで、conanを利用して開発対象であるアプリケーション/ライブラリが依存する他のライブラリなどを簡単にインストールできることが確認できました。
現時点では、認知度もパッケージ数もまだまだこれから、といったところですが、今後利用者が増えてパッケージ数が増えると、さらに価値が上がるはずなので、もっとポピュラーになってほしいなぁと思います。

新規にパッケージを作成する場合、pythonでrecipeの作成が必要ですが、本家のドキュメントや既存のパッケージのrecipeを参考にすれば簡単に作成できます。
ZeroMQのC++ bindingである[zmqpp](https://github.com/zeromq/zmqpp)のパッケージがなかったため、recipeを作成しましたが、普段pythonを利用しない私でも無事に自作のrecipeからパッケージ作成できました。↓はそのrecipeですが、こちらについては別記事として投稿する予定です。

https://github.com/gasuketsu/conan-zmqpp

自前のパッケージ管理サーバを用いたパッケージ管理を行うこともできるようですが、まだ試せていません。製品開発などで、CLOSEDなアプリケーション/ライブラリなどをconanのパッケージにする場合は必然的に自前の管理サーバでの運用になるため、このパターンも別の機会に試してみたいと思います。