Wandbox のビルドを CMake に移行した話

めるぽん
wandbox.org
Published in
13 min readSep 25, 2019

ビルド職人のめるぽんです。

今まで Wandbox のビルドは autoconf でやっていました。つまり ./configure --prefix=... してから make && make install するアレです。

これを CMake に置き換えました。なのでこれからは cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=... してから make && make install でビルドすることになります。

この記事では、何が問題だったのか、なぜ CMake に置き換えたのか、移行するために何をしたのかを書きます。

何が問題だったのか

依存ライブラリのバージョンが一切管理できていなかったことです。

Wandbox が直接利用しているライブラリとしては、BoostCppCMSCppDBcurl があり、それらのライブラリは SQLite3PCREICUOpenSSL といったいろんなライブラリを利用していて、結構な依存関係があります。

autoconf を使ったビルドでは、それらのライブラリが存在していて、必要な機能が揃っていることはチェックしていましたが、どのバージョンが入っているかは指定していませんでした。

どのバージョンをどうやって入れるかは利用者側(つまり自分)に任せていたのですが、気がついたらいろんな依存ライブラリが大分古くなっていました。

例えば今の Boost の最新バージョンは 1.71.0 ですが、実際に使っていたバージョンは 1.58.0 でした。CppCMS の最新バージョンは 1.2.1 ですが、実際に使っていたバージョンは 1.0.5 でした。しかも CppCMS に関しては、自分でパッチを当ててビルドしていたため、これらのバージョンを上げるのが割と手間になっていて、ずっとサボっている状態でした。

バージョンが古くなると、どこかで時代に付いていけなくなって死んでしまうので、定期的なバージョンアップが必要です。なので依存ライブラリも含めて管理できるようにしよう、ついでに CMake でのビルドにしよう、という感じでやり始めました。

なぜ CMake に置き換えたのか

これに関しては大した理由はなくて、最近自分が使ってる C++ のビルドツールが CMake なので慣れているというだけです。

重要なのは依存ライブラリとそのバージョンが管理できることで、どのビルドツールを使うかはあまり関係がありません。

移行するためにしたこと

依存ライブラリを自前で管理するために、自前でビルドすることにしました。環境によっては何らかのパッケージリポジトリ(Mac なら Homebrew、Ubuntu なら PPA 探して持ってくるとか?)とかがありそうですが、

  • そこにあるライブラリと無いライブラリでインストール方法を変えないといけない。
  • ビルドフラグを制御できない(ことが多い)。静的ライブラリが欲しいのに共有ライブラリしか無いとかだと困る。
  • そこに最新版があるかどうかが分からなくて、最新版が無い時にヤキモキしながら待つのがつらい。自分で追加してもいいけど、それなら自前ビルドするだけの方が楽。

というのがあるので、あまり利用していません。

自分の中で流行ってるやり方としては、./install_tools.sh で依存ライブラリの指定したバージョンをビルドして _install/ に配置し、CMake 側でそのライブラリをリンクするという方法です。grpc_sandboxayame_cpp 、それから仕事でやっている C++ のプロジェクトでもこの方法を使っています。

あとは依存ライブラリを調べてそれぞれの最新版をビルドし、Wandbox 本体を CMake 使ってビルドするようにしたら完了です。

依存ライブラリをビルドする

依存ライブラリのビルドは、ライブラリによってビルド方法が違ってたり、フラグによって依存するものが変わってきたりと大変で、いろいろと試行錯誤しながらやっています。

その辺を全部書いていくと大変なので、どのライブラリをビルドしたかと、苦労した点を列挙していくことにします。

完成品はこちらです。

zlib

zlib はビルド初心者にオススメのライブラリです。依存がほぼ無く、簡単にビルドできます。autoconf すら使ってなくて configure 手書きというライブラリ。

今まで何度もビルドしてきたライブラリなので苦労した点は特に無いです。静的ライブラリを作るなら --static を付けないといけないことに注意。

ICU

ICU は Unicode や i18n のライブラリです。CppCMS がこれか iconv のどちらかを使うように要求してきていて、以前は iconv を使っていましたが ICU の方が推奨らしいのでこちらを使うようにしました。

これも依存はほぼ無く、普通に ./configure して make すればビルドできます。特に苦労した点は無し。静的ライブラリだけ欲しいので --disable-shared --enable-static したのと、不要なのをビルドしないために --disable-tests --disable-samples したぐらい。

Boost

Boost は C++ 界隈で有名なライブラリです。

依存している内容によっては更に依存ライブラリが必要だったりしますが、今回ビルドが必要だったのは Boost.ProgramOptions だけで、他はヘッダーファイルだけで完結できるライブラリだったため、追加の依存ライブラリは不要でした。

b2 というほぼ Boost 専用のビルドツールを使ってるので、そこら辺がちょっと面倒です。が、今まで何度もビルドしてきたので特に問題なくビルドできました。

SQLite3

SQLite を操作するためのライブラリ。

fossil という珍しいソースコード管理ツールを使ってたり、Amalgamation によって単一のソースコードにして高速化したり利便性を向上させたりしているという、面白いライブラリです。

ビルドする側にとっては autoconf で生成された configure 付きソースがあるので、単にそれをビルドするだけです。

PCRE

PCRE は正規表現ライブラリです。8 系と 10 系(PCRE2)があり、CppCMS は 8 系を使っていたので、これの最新版をビルドしました。

これは CMake でビルドが可能だったので CMake でビルドしています。

依存ライブラリは、PCRE の CMakeLists.txt を見てみると、pcregrep や pcretest のバイナリを作る時に、bzip2 や readline といったライブラリがあったら利用するという感じになっていました。

PCRE ライブラリが欲しいだけでバイナリは不要なので、 -DPCRE_BUILD_PCREGREP=OFF -DPCRE_BUILD_TESTS=OFF を指定してビルドしました。

BoringSSL

Google によって fork された OpenSSL のライブラリです。OpenSSL と互換性はあるものの、中身はすごい魔改造されてて、完全に別物と言っていいレベルになってるっぽい。

CppCMS や curl が crypto ライブラリを要求していて、OpenSSL を使ってもいいけど、どうせなら BoringSSL の方が面白そうだしこっちを使うかみたいな感じで利用しました。

このライブラリは以下の点で苦労しました。

  • CMake でのビルドが可能だったのでそれでビルドしたのですが、 make install しても -DCMAKE_INSTALL_PREFIX=... した場所にインストールされませんでした。
  • ビルドには Go と Perl のバイナリが必要でした。ドキュメントを見ると “A recent version of Perl is required” “The most recent stable version of Go is required” と書いているので最新版を入れる必要があります。

最初の問題は、手動でインストール先にコピーすることで解決しました。普通に生成されたライブラリとヘッダーを cp コマンドでコピーするだけです。これで普通に動いてるので多分大丈夫でしょう。

二番目の問題は、Go に関しては自分でビルドするようにしましたが、Perl は諦めてシステムに入っている Perl を利用することにしました。Go が依存するライブラリはほぼ無くて、簡単にビルドできるはずだからです。Perl はビルドや実行のためにいくつか追加でライブラリが必要になりそうな気がしたので諦めました。Perl は最新版ではないですが、一応 BoringSSL をビルドできてるので大丈夫そうです。

Go

Go は Google によって開発されたプログラミング言語です。スタンドアローン力が高く、実行バイナリが C ランタイムにすら依存しない、ビルド職人的にはとてもありがたい言語です。

Go は Go を使ってビルドする必要があるので、ブートストラップ問題を解決する必要があります。幸いなことに Go がブートストラップ用のソース(Cで書かれてる)を提供してくれているので、それをビルドしてGoをビルドする必要があります。

そこら辺で少し手間取りましたが、ドキュメントを読めばすぐ分かったので難しくなかったです。

curl

curl は HTTP クライアントのライブラリです(HTTP 以外にも大量にサポートしてるけど)。Wandbox のコードで HTTP クライアントが欲しかったので、curl を利用しています。

これのビルドは少し手間取りました。HTTPS を扱うなら間違いなく SSL が必要なのと、gzip 圧縮された HTTP のリクエストやレスポンスを扱うなら zlib が必要なのは分かっていたので ./configure --with-ssl=/path/to/boringssl --with-zlib=/path/to/zlib を指定していて、ビルドも出来ていました。

しかし最終的に Wandbox のバイナリを生成する段階で、LDAP と RTMP のライブラリが無いと怒られてしまいました。Wandbox でそんなプロトコルは不要なので ./configure --help を読みつつ --disable-ldap --without-librtmp することで回避しました。

CppCMS

C++ の HTTP サーバのライブラリです。

CppCMS は CMake でビルド可能なで、いくつかの外部ライブラリを要求しているので、引数に上記でビルドしたライブラリを追加します。

cmake .. -DCMAKE_PREFIX_PATH="/path/to/pcre;/path/to/zlib;/path/to/icu;/path/to/boringssl" みたいな感じです。これでシステムのライブラリより指定したパスのライブラリの方が先に検索されるようになります。あとはFCGI や SCGI を使うかどうかとか、iconv を使うかどうかとかそういう CppCMS 特有の設定を入れてビルドするだけです。

CppDB

C++ の DB 操作のライブラリです。

Wandbox では SQLite を使っているので、上記でビルドした SQLite を利用するように -DCMAKE_PREFIX_PATH="/path/to/sqlite" みたいに書くだけです。

CppCMS と CppDB で苦労した点としては、これらのライブラリは常に共有ライブラリを作るようになっていて、他のライブラリが静的ライブラリなせいで、ちゃんとリンクができずにエラーになっていました。確か -fPIC を指定してなかったせいだった気がしますけど、どちらにせよ共有ライブラリが出来てる時点で意図と違うので、これを消す方法が無いか CMakeLists.txt を頑張って読むことになりました。

結果としては、CppCMS や CppDB は常に共有ライブラリを作ってしまうことが分かったので、自前でパッチを当てて共有ライブラリを作らないようにしました。

CMake

CMake はとても便利なビルドツールですが、非常に残念なことに「事前に CMake を入れないと使えない」「CMake を入れたとしてもバージョンによって動いたり動かなかったりする」という問題があります。

前者はまあ仕方ないとしても、後者はちょっと厳しいものがあります。

現在の CMake の最新バージョンは 3.15.3 で、このバージョンにはデフォルトで SQLite3 を検索する機能(3.14 で追加)や ICU を検索する機能(3.7 で追加)があります。

Ubuntu 16.04 の apt でインストールされる CMake は 3.5.1 であるため、この CMake は自前でファイルを追加しない限り SQLite3 や ICU を find_package で検索することは出来ません。あと OpenSSL の検索も 3.5.1 と 3.15.3 で微妙に変わってて、これのせいで 3.15.3 で試した時はちゃんと動いたのに 3.5.1 にしたら動かないみたいなことにもなりました。

ということで、CMake の最新版も依存ライブラリに追加することで、安定してビルドできるようにしました。

ただし CMake 本体のビルド(正確には HTTPS 通信するため)に curl と zlib が必要で、curl のために BoringSSL が必要で、BoringSSL のビルドには CMake が必要で…となると循環して泥沼化するので、ここは諦めて curl と zlib をシステムに入れてもらうことにしました。CMake のブートストラップ問題はなかなか難しい…。

Wandbox 本体をビルドする

ここまで来れば、あとは本体をビルドするだけです。Wandbox には cattleshed と kennel という2つのモジュールがあるので、2つの CMakeLists.txt を書きました。

実際に構築してる時は、ここで実行バイナリを作る時にリンクエラーが発生するので、ここで引っかかってライブラリをビルドし直すみたいなことが結構頻繁にありました。

今までビルドしたライブラリを CMake に探してもらうため、こんな感じで CMake にパラメータを渡すことになります。

-DCMAKE_PREFIX_PATH="/p/t/boost;/p/t/cppcms;/p/t/cppdb;/p/t/zlib;/p/t/curl;/p/t/sqlite3;/p/t/pcre;/p/t/icu"

今までのインストールしたライブラリの集大成みたいな感じがして楽しいですね。これでビルドして、動作確認して、念の為 ldd で意図しない共有ライブラリが含まれていないことを確認して完成です。

これで Wandbox の CMake 化&依存ライブラリのバージョン管理が出来ました。あとは定期的に依存ライブラリバージョンの値を変えてビルドし直すだけで、常に新しいライブラリを使ったアプリケーションが提供できるようになります(ただしビルド方法が変わった場合はまた頑張って調べることになる)。

これらの一連の変更は この Pull Request にあるので、どんなコードを書いているか気になったら見てみるといいでしょう。

--

--