toge's diary

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

Conanで依存関係の衝突にあって対処した話

直近困ったのでメモ。

TL;DR

  • Conanでパッケージ衝突がおきたら、パッケージ依存グラフ機能を使おう
  • 自分でレシピを書くことがあったら、依存パッケージのバージョンはできるだけ範囲指定してほしい(願望)

OpenCVとTesseract OCRを使ってみたかった

相変わらずC++で何かをする時にはパッケージマネージャとしてConanを使っています。 OpenCVとTesseract OCRを同時に利用したい場合が出てきたんで、こんなconanfile.txtを書くことになりました。

[requires]
opencv/4.5.1
tesseract/4.1.1

[generators]
cmake

で、このconanfile.txtがあるディレクトリでconan install .すると、こんなエラーが出てしまいます。

% conan install .
Configuration:
[settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=10.2
os=Linux
os_build=Linux
[options]
[build_requires]
[env]

WARN: libtiff/4.1.0: requirement libwebp/1.1.0 overridden by leptonica/1.79.0 to libwebp/1.0.3 
ERROR: libtiff/4.1.0: Incompatible requirements obtained in different evaluations of 'requirements'
    Previous requirements: [zlib/1.2.11, xz_utils/5.2.5, libjpeg/9d, jbig/20160605, zstd/1.4.5, libwebp/1.1.0]
    New requirements: [zlib/1.2.11, xz_utils/5.2.5, libjpeg/9d, jbig/20160605, zstd/1.4.5, libwebp/1.0.3]

うーん、よく分からないですね。 OpenCVとtesseractなのに、libtiffがlibwebpのこと文句言ってるし…。

パッケージ依存グラフで原因を調べてみる

こういうのは大抵パッケージの依存パッケージの衝突が原因です。 Conanのパッケージ依存グラフ機能(experimental)を使うと図で表示されて便利だったりします。 docs.conan.io ※Graphizのインストールが必要になります。

OpenCVの依存グラフ

最初にOpenCVの依存グラフ見てみましょう。

% conan info opencv/4.5.1@ --graph=opencv.dot   
% dot -Tpng opencv.dot > opencv.png   

f:id:toge:20210116024447p:plain
OpenCVの依存グラフ

OpenCV -> libtiff -> libwebpの依存が見えますね。

Tesseract OCRの依存グラフ

次はTesseract OCRの依存グラフです。

% conan info tesseract/4.1.1@ --graph=tesseract.dot    
% dot -Tpng tesseract.dot > tesseract.png   

f:id:toge:20210116024733p:plain
Tesseract OCRの依存グラフ

webpについて2つの依存経路がありますね。

  • Tesseract OCR -> leptonica -> libtiff -> libwebp
  • Tessearct OCR -> leptonica -> libwebp

そしてwebpのバージョンが1.0.3に変化しています。

なぜこんなことが起きるかというと、leptonicaのConanレシピがwebpのバージョンを1.0.3に決め打ちしているためです。

https://conan.io/center/leptonica?version=1.79.0&tab=recipe

        if self.options.with_webp:
            self.requires("libwebp/1.0.3")

そうするとleptonicaが依存するlibtiffも空気を読んでwebpのバージョンを1.0.3に変更してしまっているんです。 一方でOpenCV側で依存しているlibtiffは特に制約がないのでレシピの通り1.1.0を使います。

https://conan.io/center/libtiff?version=4.1.0&tab=recipe

        if self.options.get_safe("webp"):
            self.requires("libwebp/1.1.0")

これで見事に「libtiffが依存するlibwebpのバージョンが衝突する」という事態が起きてしまいました。

回避策

回避策は何個かあると思います。 私が考えついたのは以下の2つ。

  • libtiffレシピのoptionでwebpを無効にする
  • leptonicaレシピのoptionでwebpを無効にする

他のプロジェクトでの汎用性を考えると、libtiffを使う場合に毎回webpを無効にするオプションをつけるのは面倒くさいので、(そうしないとlbtiffのバイナリが2バージョンできてしまう)今回はleptonicaレシピのoptionでwebpを無効にすることにします。 どうせ画像の読み書きはOpenCVでやるのでleptonicaの機能が減っても困りませんし。

% conan info tesseract/4.1.1@ -o leptonica:with_webp=False --graph=tesseract_wo_webp.dot
% dot -Tpng tesseract_wo_webp.dot > tesseract_wo_webp.png   

f:id:toge:20210116030821p:plain
webpを無効化したTesseract OCRの依存グラフ

ちゃんとlibwebpのバージョンが1.1.0になりました。 これならOpenCVと衝突しなくなります。

conanfile.txtに結果を反映

次のようなconanfile.txtにすることで、衝突なくinstallができるようになりました。

[requires]
opencv/4.5.1
tesseract/4.1.1

[generators]
cmake

[options]
leptonica:with_webp=False

補足: 本来はどうあるべきか?

汚い逃げ方をしてしまいましたが、本来はレシピがお行儀良く依存関係を書いてくれていればいいのになぁと思います。 Conanでは依存パッケージのバージョン番号を範囲で指定できるのでleptonicaもlibtiffも範囲指定してくれていれば良かったんですよね。

        if self.options.with_webp:
            self.requires("libwebp/[>=1.0.3]")

もちろんバージョンによってAPIや内部動作が変わるので、細かいバージョンの組み合わせをいちいちチェックしてられないのだろうとは思います。

そう考えると今回のようなテクニックを使う場面はまだまだありそうですね。