toge's diary

コンピュータ関連の趣味をつらつらと。

conanfile.txtで条件分岐したい場合はconanfile.pyを使う

TL;DR

conanパッケージに関して環境依存で細かい条件分岐をしたい場合はconanfile.txtではなくconanfile.pyを使いましょう。 Pythonコードでやりたい放題です。

導入というかtermuxの話

termux面白いですね。 普段持ち歩くAndroidでほぼ完璧なLinux環境が作れてしまうワクワク感がたまりません。

最近termuxで開発環境を作るのが楽しいです。 そこで困るのがtermuxの特殊なコンパイル環境です。

最近になってclang version 11が入ってほぼ最新のC++環境が手に入るのですが、微妙にx86_64とは異なる勝手があって困ることがあります。 今日困ったけれどなんとか解決できたものがあったのでメモっておきます。

足りないヘッダファイルがあってboostがコンパイルできない

私はC++のパッケージマネージャーの一つであるConanを利用しています。 conan.io Termuxに限らず、色々な環境でビルドする時にライブラリのバージョンを揃えたり、マイナーなライブラリを利用する際にとても便利です。

TermuxでもConanを使ってboostライブラリの最新版を使ってみようと思ったのですが、見事にハマりました。 strfmon() を定義している monetary.h というヘッダがないのでboost localeとboost logがビルドできないのです。

libs/locale/src/posix/numeric.cpp:26:10: fatal error: 'monetary.h' file not found
#include <monetary.h>
         ^~~~~~~~~~~~
1 error generated.

conanでパッケージオプションを指定する方法

boostCMakelists.txt側で認識してくれていないのが謎ですが monetary.h が無い環境なんてかなりマイナーなのでしょう。 私としてはとにかく今利用できないのが困ります。

幸いboost locale, boost logを使っていないプロジェクトが多いので、そういうプロジェクトだけはTermuxでビルドできるようにしてみたいと思いました。

boost locale, boost logだけをビルドの対象から外すのはconanコマンドが読み込むconanfile.txtにパッケージオプションを指定するだけです。

[requires]
boost/1.75.0

[generators]
cmake

[options]
boost.without_locale = True
boost.without_log = True

monetary.hがない時だけパッケージオプションを指定したい

上の方法でとりあえずコンパイルできるのですが monetary.hがある環境ではboost locale, boost logもビルドしてほしいのです。 そうしないと~/.conan/data/boost//1.75.0/_/_/packageの下に「全ビルド版」と「locale, log除外版」の複数のバイナリが生成されてしまってboostの性質上やたらディスク使用量が大きいのが嫌だなぁと。(貧乏性です)

こういう環境などによる条件分岐にconanfile.txtは対応していません。 幸いconangithubに同じような質問をしている人がいました。 github.com

なるほどconanfile.txtと同じようにインストールするパッケージ方法としてconanfile.pyがあるんですね。 知らなんだ。 docs.conan.io

conanfile.pyの中では普通にPythonが書けてしまうのでmonetary.hの存在チェックも可能になります。 ひとまず以下のようなconanfile.pyを書いてやりたいことを実現できるようになりました。

from conans import ConanFile
import subprocess

class ProjectConan(ConanFile):
   settings = "os", "arch", "compiler"
   generators = "cmake"

   def requirements(self):
       self.requires("boost/1.75.0")

   def config_options(self):
       compiler = str(self.settings.compiler)
       # check monetary header existance
       if self.settings.os == "Linux" and (compiler == "gcc" or compiler == "clang"):
           monetary_check = subprocess.run([compiler, "-E", "-x", "c", "-"], input="#include<monetary.h>", text=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
           if monetary_check.returncode != 0:
               self.options["boost"].without_locale = True
               self.options["boost"].without_log = True

まとめ

  • ConanでのC++パッケージ管理便利です。
  • conanfile.txtでパッケージ定義が基本ですが、条件分岐したければconanfile.pyPythonコードを書けます。