はじめに
前回の投稿に引き続き、Conan C/C++ Package Managerを使ってできることを紹介します。
ZeroMQのC++ bindingの1つであるzmqppのpackageがなかったため、Conanで利用できるpackageを自作し、conan.ioにuploadするところまで行いました。今回はその過程をまとめてみました。
package作成までの流れ
recipeの作成
ゼロからすべてrecipeを書いてもいいのですが、Conanにはrecipeのテンプレートを準備してくれるコマンドが備わっているので利用してみます。
tharada@xubuntu:~/conan-zmqpp$ conan new zmqpp/4.1.2@gasuketsu/testing
File saved: conanfile.py
conanfile.py
という名前のpythonファイルが作られました。このファイルがrecipeになります。templateとして作られた状態のrecipeの中身はこんなかんじになっています。
from conans import ConanFile, CMake, tools
import os
class ZmqppConan(ConanFile):
name = "zmqpp"
version = "4.1.2"
license = "<Put the package license here>"
url = "<Package recipe repository url here, for issues about the package>"
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False]}
default_options = "shared=False"
generators = "cmake"
def source(self):
self.run("git clone https://github.com/memsharded/hello.git")
self.run("cd hello && git checkout static_shared")
# This small hack might be useful to guarantee proper /MT /MD linkage in MSVC
# if the packaged project doesn't have variables to set it properly
tools.replace_in_file("hello/CMakeLists.txt", "PROJECT(MyHello)", '''PROJECT(MyHello)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()''')
def build(self):
cmake = CMake(self.settings)
shared = "-DBUILD_SHARED_LIBS=ON" if self.options.shared else ""
self.run('cmake hello %s %s' % (cmake.command_line, shared))
self.run("cmake --build . %s" % cmake.build_config)
def package(self):
self.copy("*.h", dst="include", src="hello")
self.copy("*hello.lib", dst="lib", keep_path=False)
self.copy("*.dll", dst="bin", keep_path=False)
self.copy("*.so", dst="lib", keep_path=False)
self.copy("*.a", dst="lib", keep_path=False)
def package_info(self):
self.cpp_info.libs = ["hello"]
見ての通り、pythonで書かれた1つのclassという形でrecipeを定義しています。
officialのドキュメントの "Creating packages" のセクションや "Reference" にそれぞれのメソッドやメンバー変数の説明がされていますが、大まかに説明すると以下のようになると思います。
変数/メソッド | 説明 |
---|---|
name |
このpackageの名前 |
version |
packageのバージョン |
license |
packageのライセンス |
options |
このpackageをインストールする時に指定可能なオプション (packaging するプロジェクトのbuild時に指定可能なオプションに応じてここで定義すると良いと思います) |
default_options |
各オプションのデフォルト値 |
generators |
プロジェクトのbuildに必要な設定ファイルの出力形式 |
source() |
packagingするプロジェクトのソースファイルの取得方法と、build開始前にやっておくこと(もしあれば)をこのメソッドで定義します。 |
build() |
プロジェクトのbuild方法 |
package() |
ライブラリやヘッダなど、packageに含めたいファイルとその格納先 |
package_info() |
このpackageを利用する時に必要なライブラリやinclude pathなどの情報 |
zmqppの内容に合わせてrecipeを編集した結果、以下のようになりました。
from conans import ConanFile, CMake, tools
import shutil
import os
class ZmqppConan(ConanFile):
name = "zmqpp"
description = "0mq 'highlevel' C++ bindings"
version = "4.1.2"
license = "MPLv2"
url = "https://github.com/gasuketsu/conan-zmqpp"
settings = "os", "compiler", "build_type", "arch"
generators = "cmake", "txt", "env"
exports = "CMakeLists.txt"
options = {"shared": [True, False],
"zmqpp_libzmq_cmake": [True, False],
"zmqpp_build_examples": [True, False],
"zmqpp_build_client": [True, False],
"zmqpp_build_tests": [True, False]}
default_options = '''
shared=False
zmqpp_libzmq_cmake=False
zmqpp_build_examples=False
zmqpp_build_client=False
zmqpp_build_tests=False
'''
def requirements(self):
self.requires("libzmq/4.1.5@memsharded/stable")
def source(self):
self.run("git clone https://github.com/zeromq/zmqpp.git")
self.run("cd zmqpp && git checkout 4.1.2")
shutil.move("zmqpp/CMakeLists.txt", "zmqpp/CMakeListsOriginal.cmake")
shutil.copy("CMakeLists.txt", "zmqpp/CMakeLists.txt")
def build(self):
cmake = CMake(self.settings)
cmake_options = []
for option_name in self.options.values.fields:
activated = getattr(self.options, option_name)
the_option = "%s=" % option_name.upper()
if option_name == "shared":
the_option = "ZMQPP_BUILD_SHARED=ON" if activated else "ZMQPP_BUILD_SHARED=OFF"
else:
the_option += "ON" if activated else "OFF"
cmake_options.append(the_option)
options_zmqpp = " -D".join(cmake_options)
conf_command = 'cd zmqpp/cmake_build && cmake .. %s -D%s' % (cmake.command_line, options_zmqpp)
self.output.warn(conf_command)
self.run("mkdir -p zmqpp/cmake_build")
self.run(conf_command)
self.run("cd zmqpp/cmake_build && cmake --build . %s" % cmake.build_config)
def package(self):
self.copy("*.hpp", dst="include/zmqpp", src="zmqpp/src/zmqpp")
self.copy("*.lib", dst="lib", keep_path=False)
self.copy("*.dll", dst="bin", keep_path=False)
self.copy("*.so", dst="lib", keep_path=False)
self.copy("*.a", dst="lib", keep_path=False)
def package_info(self):
self.cpp_info.libs = ["libzmqpp.so"] if self.options.shared else ["libzmqpp-static.a"]
今回の変更のポイントとしては、以下の2点です
- 今回packagingするzmqppのbuildにはlibzmqが必要となるため、
requirements()
を追加し、ここでこのpackageが依存するlibzmqのpackageを定義しました。 - 依存packageのヘッダやライブラリを正しく引けるよう、zmqppのオリジナルの
CMakeLists.txt
に手を加えてconanbuildinfo.cmake
をincludeさせる必要があります。そこで、以下のCMakeLists.txt
を recipeとセットで用意するようにし、source()
で zmqppのプロジェクトのソースをclone後
- zmqppオリジナルの
CMakeLists.txt
をCMakeListOriginal.cmake
にrename - recipeとセットで用意した↓の
CMakeLists.txt
を zmqppオリジナルのCMakeLists.txt
があった場所にコピー
という細工を施すことにしました。
project(conanzmqpp)
cmake_minimum_required(VERSION 2.8)
include(${CMAKE_SOURCE_DIR}/../conanbuildinfo.cmake)
conan_basic_setup()
set(ZEROMQ_LIB_DIR ${CONAN_LIB_DIRS_LIBZMQ} CACHE PATH "libzmq path")
include("${CMAKE_SOURCE_DIR}/CMakeListsOriginal.cmake")
recipeからbuild出来ることを確認する
作成したrecipeから、Conanを利用してソース取得~buildが期待通り出来ることを確認してみます。作成したrecipe (conanfile.py
) が置いてあるディレクトリで次の3つを順に実行することで確認します。
-
conan install
でrecipeから設定ファイルを生成 -
conan source
でソース取得 (recipeで定義したsource()
の実行) -
conan build
でbuild実施 (recipeで定義したbuild()
の実行)
tharada@xubuntu:~/repos/conan-zmqpp$ conan install
Requirements
libzmq/4.1.5@memsharded/stable from conan.io
Packages
libzmq/4.1.5@memsharded/stable:81607951755e61cf17149d40bed6aba5b3a079a9
libzmq/4.1.5@memsharded/stable: Already installed!
PROJECT: Generated cmake created conanbuildinfo.cmake
PROJECT: Generated txt created conanbuildinfo.txt
PROJECT: Generated env created conanenv.txt
PROJECT: Generated conaninfo.txt
tharada@xubuntu:~/repos/conan-zmqpp$ conan source
PROJECT: Configuring sources in /home/tharada/repos/conan-zmqpp
Cloning into 'zmqpp'...
remote: Counting objects: 2761, done.
remote: Total 2761 (delta 0), reused 0 (delta 0), pack-reused 2761
Receiving objects: 100% (2761/2761), 5.54 MiB | 521.00 KiB/s, done.
Resolving deltas: 100% (1622/1622), done.
Checking connectivity... done.
Note: checking out '4.1.2'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 4784589... Merge pull request #122 from xaqq/doxygen
tharada@xubuntu:~/repos/conan-zmqpp$ conan build
Project: WARN: cd zmqpp/cmake_build && cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCONAN_COMPILER="gcc" -DCONAN_COMPILER_VERSION="5.4" -DCONAN_LIBCXX="libstdc++" -Wno-dev -DZMQPP_BUILD_SHARED=OFF -DZMQPP_BUILD_CLIENT=OFF -DZMQPP_BUILD_EXAMPLES=OFF -DZMQPP_BUILD_TESTS=OFF -DZMQPP_LIBZMQ_CMAKE=OFF
-- 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
-- Conan: Using cmake global configuration
-- Conan C++ stdlib: libstdc++
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tharada/repos/conan-zmqpp/zmqpp/cmake_build
Scanning dependencies of target zmqpp-static
[ 7%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/actor.cpp.o
[ 14%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/context.cpp.o
[ 21%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/curve.cpp.o
[ 28%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/frame.cpp.o
[ 35%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/message.cpp.o
[ 42%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/poller.cpp.o
[ 50%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/reactor.cpp.o
[ 57%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/signal.cpp.o
[ 64%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/socket.cpp.o
[ 71%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/z85.cpp.o
[ 78%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/zap_request.cpp.o
[ 85%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/auth.cpp.o
[ 92%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/zmqpp.cpp.o
[100%] Linking CXX static library lib/libzmqpp-static.a
[100%] Built target zmqpp-static
tharada@xubuntu:~/repos/conan-zmqpp$
無事にbuildできました。
packagingする
recipeからbuild出来るところまで確認ができたので、recipeをもとにpackageを作ります。
-
conan export
でrecipeをlocal cache (通常は$HOME/.conan/data
配下) にコピー -
conan install
でlocal cache上のrecipeからbuildを行いpackage作成
という流れで作ってみます。
tharada@xubuntu:~/repos/conan-zmqpp$ conan export gasuketsu
zmqpp/4.1.2@gasuketsu/testing export: Copied 1 '.txt' files: CMakeLists.txt
zmqpp/4.1.2@gasuketsu/testing: A new conanfile.py version was exported
zmqpp/4.1.2@gasuketsu/testing: Folder: /home/tharada/.conan/data/zmqpp/4.1.2/gasuketsu/testing/export
zmqpp/4.1.2@gasuketsu/testing: Package recipe modified in export, forcing source folder removal
zmqpp/4.1.2@gasuketsu/testing: Use the --keep-source, -k option to skip it
zmqpp/4.1.2@gasuketsu/testing: Removing 'source' folder, this can take a while for big packages
tharada@xubuntu:~/repos/conan-zmqpp$ cd
tharada@xubuntu:~$ conan install zmqpp/4.1.2/gasuketsu/testing --build=zmqpp
Requirements
libzmq/4.1.5@memsharded/stable from conan.io
zmqpp/4.1.2@gasuketsu/testing from conan.io
Packages
libzmq/4.1.5@memsharded/stable:81607951755e61cf17149d40bed6aba5b3a079a9
zmqpp/4.1.2@gasuketsu/testing:1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1
libzmq/4.1.5@memsharded/stable: Already installed!
zmqpp/4.1.2@gasuketsu/testing: WARN: Forced build from source
zmqpp/4.1.2@gasuketsu/testing: Building your package in /home/tharada/.conan/data/zmqpp/4.1.2/gasuketsu/testing/build/1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1
zmqpp/4.1.2@gasuketsu/testing: Configuring sources in /home/tharada/.conan/data/zmqpp/4.1.2/gasuketsu/testing/source
Cloning into 'zmqpp'...
remote: Counting objects: 2761, done.
remote: Total 2761 (delta 0), reused 0 (delta 0), pack-reused 2761
Receiving objects: 100% (2761/2761), 5.54 MiB | 1.45 MiB/s, done.
Resolving deltas: 100% (1622/1622), done.
Checking connectivity... done.
Note: checking out '4.1.2'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 4784589... Merge pull request #122 from xaqq/doxygen
zmqpp/4.1.2@gasuketsu/testing: Copying sources to build folder
zmqpp/4.1.2@gasuketsu/testing: Generated cmake created conanbuildinfo.cmake
zmqpp/4.1.2@gasuketsu/testing: Generated txt created conanbuildinfo.txt
zmqpp/4.1.2@gasuketsu/testing: Generated env created conanenv.txt
zmqpp/4.1.2@gasuketsu/testing: WARN: cd zmqpp/cmake_build && cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCONAN_COMPILER="gcc" -DCONAN_COMPILER_VERSION="5.4" -DCONAN_LIBCXX="libstdc++" -Wno-dev -DZMQPP_BUILD_SHARED=OFF -DZMQPP_BUILD_CLIENT=OFF -DZMQPP_BUILD_EXAMPLES=OFF -DZMQPP_BUILD_TESTS=OFF -DZMQPP_LIBZMQ_CMAKE=OFF
-- 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
-- Conan: Using cmake global configuration
-- Conan C++ stdlib: libstdc++
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tharada/.conan/data/zmqpp/4.1.2/gasuketsu/testing/build/1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1/zmqpp/cmake_build
Scanning dependencies of target zmqpp-static
[ 7%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/actor.cpp.o
[ 14%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/context.cpp.o
[ 21%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/curve.cpp.o
[ 28%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/frame.cpp.o
[ 35%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/message.cpp.o
[ 42%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/poller.cpp.o
[ 50%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/reactor.cpp.o
[ 57%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/signal.cpp.o
[ 64%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/socket.cpp.o
[ 71%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/z85.cpp.o
[ 78%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/zap_request.cpp.o
[ 85%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/auth.cpp.o
[ 92%] Building CXX object CMakeFiles/zmqpp-static.dir/src/zmqpp/zmqpp.cpp.o
[100%] Linking CXX static library lib/libzmqpp-static.a
[100%] Built target zmqpp-static
zmqpp/4.1.2@gasuketsu/testing: Package '1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1' built
zmqpp/4.1.2@gasuketsu/testing: Build folder /home/tharada/.conan/data/zmqpp/4.1.2/gasuketsu/testing/build/1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1
zmqpp/4.1.2@gasuketsu/testing: Generated conaninfo.txt
zmqpp/4.1.2@gasuketsu/testing: Generated conanbuildinfo.txt
zmqpp/4.1.2@gasuketsu/testing: Generated conanenv.txt
zmqpp/4.1.2@gasuketsu/testing: Generating the package
zmqpp/4.1.2@gasuketsu/testing: Package folder /home/tharada/.conan/data/zmqpp/4.1.2/gasuketsu/testing/package/1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1
zmqpp/4.1.2@gasuketsu/testing package(): Copied 1 '.a' files: libzmqpp-static.a
zmqpp/4.1.2@gasuketsu/testing package(): Copied 20 '.hpp' files
zmqpp/4.1.2@gasuketsu/testing: Package '1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1' created
tharada@xubuntu:~$
作成したpackageをuploadして公開する
今回作成したzmqppのpackageはOSSであり、publicなpackageとして他のconanユーザに公開したかったので、デフォルトのupload先であるconan.ioにuploadします。
tharada@xubuntu:~$ conan upload --all zmqpp/4.1.2@gasuketsu/testing
Uploading zmqpp/4.1.2@gasuketsu/testing
Compressing exported files... Please log in to "conan.io" to perform this action. Execute "conan user" command.
If you don't have an account sign up here: http://www.conan.io
Please enter a password for "gasuketsu" account:
Current 'conan.io' user already: gasuketsu
Uploading conanmanifest.txt
[==================================================]
Uploading conanfile.py
[==================================================]
Uploading conan_export.tgz
[==================================================]
Uploaded conan recipe 'zmqpp/4.1.2@gasuketsu/testing' to 'conan.io': https://www.conan.io/source/zmqpp/4.1.2/gasuketsu/testing
Uploading package 1/1: 1f1adf131e6c0c9fb34450c5f3e0970d57e40fc1
Package integrity OK!
Requesting upload permissions...Done!
Uploading conanmanifest.txt
[==================================================]
Uploading conaninfo.txt
[==================================================]
tharada@xubuntu:~$
さいごに
今回作成したrecipeは こちらで公開しております。
後から気づいたのですが 作成したrecipe/packageの確認手段としては conan test_package
を使ったほうが良さそうなので、test_package
用のコードを準備しておこうと思います。