Boost.Logのサンプルコードを打ち込んでいたら、一部のコードが動作しませんでした。
ログファイルのローテーションを行うサンプルですが、ビルドすると、以下のようなリンクエラーが出ます。
Undefined symbols for architecture x86_64: "basic_formatter<char> parse_formatter<char>(char const*, char const*)", referenced from:
今回は、このリンクエラーを解消するための奮闘記です。
結論から
まず結論から書きます。
原因は、bjamの設定が間違っていて、古いGCC(Boost.Logのサポート外環境)でコンパイルされていたからでした。以下のように設定することで、解決しました。
- bjamの設定ファイルsite-config.jam
このファイルは、HOME下、もしくはBOOST_ROOT下に作成します。
using gcc : 4.7 : g++-mp-4.7 ;
MacPortsで入れたGCC 4.7を、gcc-4.7のツールセットとして認識させます。
- bjamコマンドでのBoostライブラリのビルド
./bjam toolset=gcc-4.7 --with-log link=static runtime-link=static
GCC 4.7でBoostをビルドします。
with-logで、Boost.Logに関連するライブラリだけがビルドされます。このオプションを付けなければ、Boostの全ライブラリがビルドされます。
デフォルトでなぜか動的ライブラリ(.dylib)を作られるようになっていたので、静的ライブラリ(.a)を作るようstaticを指定します。
- ビルドされたライブラリファイル
「boost_1_54_0」がBoostのルートディレクトリだった場合は、boost_1_54_0/stage/libディレクトリ以下に、ビルドされたライブラリファイル(.a)が置かれます。
- サンプルコードのビルド
Boost.Logのサンプルコードを動かす際のコマンド。
g++-mp-4.7 -o main main.cpp -L/Users/faith_and_brave/repository/Library/Boost/boost_1_54_0/stage/lib -lboost_thread -lboost_system -lboost_log -lboost_log_setup -lboost_filesystem -std=c++11 -Wall -Wextra -pedantic -pthread -I ~/repository/Library/Boost/boost_1_54_0
Boost.Logは、libboost_log.aとlibboost_log_setup.aの2つを少なくてもリンクする必要があり、そのほかに使用する関連ライブラリ(Boost.Thread、Boost.System、Boost.Filesystem等)をリンクします。
これでBoost.Logのファイルローテーションを行うサンプルが動きます。
奮闘記
では、ここからbjamの奮闘記です。
まず、サンプルコードがリンクエラーになる原因は、bjamでのビルド時に、libs/log/src/formatter_parser.cppがコンパイルエラーになり、formatter_parser.oが作成されていなかったためです。
コンパイルエラーの内容を見たら、どうやらresult_ofメタ関数の呼び出し部分でエラーになっているようです。調べたら、以下のチケットが見つかりました(このエントリを書いている時点では未解決)。
#8639 Boost.Log (trunk: r84587) fails to build with g++ 4.1
このチケットは、タイトルそのままですが、GCC 4.1でBoost.Logのビルドに失敗するというもので、Boost.Log作者のコメントで、
Sorry, this compiler is not supported. It fails to handle TR1 result_of protocol correctly, which is necessary for the part of Boost.Log that uses Boost.Phoenix.
If you have a patch and it's not overhauling the whole library, you can reopen this ticket. Closing it for now.
と書いてあり、つまりTR1のresult_ofを使ってる(Boost.Phoenixを使ってる)から、古いコンパイラでは動かないよ、ということです。
なるほど、bjamで使われているGCCのバージョンが古そうです。調べてみたら、g++コマンドでビルドされていたので、おそらく最初から入っていたGCCです。バージョンは4.2.1。result_ofとかないですね。
bjamで使用するGCCのバージョンを切り替えてみました。
// site-config.jam using gcc : 4.7 : g++-mp-4.7 ;
./bjam toolset=gcc-4.7
すると、今度は全てのBoostライブラリでリンクエラーが出るようになりました。
ld: unknown option: -R
ldコマンドには、-Rオプションなんてない、というエラーです。
Boostのメーリングリストを調べてみると、「darwinのGCCを使わないとだめだよ」とのこと。つまりbjamデフォルトのコンパイラ。
そんなわけないだろ、と思ってkikairoyaさんに手伝ってもらいながら調べて、tools/build/v2/tools/gcc.jamをいじればいけるんじゃないか、ということに。
(中略します。 https://twitter.com/cpp_akira/status/362275774625099777 )
結果的に、ldコマンドが必要になるのは、なぜか意図せずlibboost_log.dylibとかの動的ライブラリが作られているからなので、静的ライブラリを作ればいい、ということになりました。
最終的に、bjamのコマンドは以下のようになりました。
./bjam toolset=gcc-4.7 --with-log link=static runtime-link=static
静的ライブラリも作られてこれで万事解決かな、と思ったら、まだ一番最初のリンクエラーがでます(parse_formatter)。
libboost_log.a内にparse_formatter関数が含まれているのか怪しくなってきたので、.aファイル内の関数一覧を出力してみました。
.aファイルの解析にはnmコマンド、出力された関数名をデマングルするにはc++filtコマンドを使いました。
nm libboost_log.a | c++filt
これで、以下のように、.oファイルごとの関数名一覧が出力されます。
... libboost_log.a(unhandled_exception_count.o): 0000000000000018 short EH_frame1 0000000000000000 T boost::log::v2s_mt_posix::aux::unhandled_exception_count() U ___cxa_get_globals ...
しかし、libboost_log.aファイルには、formatter_parser.oが含まれていませんでした。調べたら、libboost_log_setup.aの方にありました。これら2つのライブラリをリンクしたら、リンクエラーが解消しました!
このリンクエラーは、夜22:00くらいから調べ始めて、解決したのは朝5時前くらいでした。
解決まで付き合ってくれたkikairoyaさんに感謝します!ありがとうございます!