パソコンでプログラミングしよう ウィンドウズC++プログラミング環境の構築
1.6.3.6(15)
boostをビルドする

boostライブラリを自分でビルドする手順を説明し、これを含めたデバッグ方法を示す。

本サイトはMSYS2boostライブラリを導入した。これは簡便であるものの必ずしも最新版が得られず、ライブラリ内部までデバッガが追跡できないというデメリットがある。本項目はboostをソースコードからビルドするが、特にデバッグバージョンでライブラリ内部までデバッガを追跡させるための方法を示す。本項目はバージョン1.75.0を前提に記述する。

ソースコード

ダウンロード

リンク先からboost_1_75_0.zipをダウンロードする。

ディレクトリ配置

ディレクトリは以下とする。異なるバージョンのソースコード展開ディレクトリはboostルートの配下に並列させる。

目的 ディレクトリ
boostルート C:\boost
ソースコードの展開 C:\boost\boost_1_75_0
ビルドの出力 C:\boost\boost_1_75_0\stage\lib

zipファイルをboostルートに置きエクスプローラー右クリック[すべて展開]で[圧縮(ZIP)形式フォルダーの展開]ダイアログ[ファイルを以下のフォルダーに展開する]をC:\boostに変更して[展開]する。デフォルトのままではboost_1_75_0ディレクトリをルートとして圧縮されているため一階層余分になる。boostライブラリはBoost.Build(B2)でビルドする。各ディレクトリに配置されるjamroot、jamfile、jamfile.v2、*.jamファイルはB2データで大雑把にはmakeメイクファイルに相当する。本項目での相対パスは展開ディレクトリ(C:\boost\boost_1_75_0)からとする。

boostは独立したライブラリの集合体で多くはヘッダーオンリーだがビルドするライブラリも存在し、そういったライブラリの一つregexを例として配置を示す。各ライブラリのファイルはインクルードを除きlibs\[ライブラリ名]ディレクトリに配置される。B2の実行ファイルはb2.exeでtools\buildディレクトリに供給されるソースコードをビルドする。b2.exeはMSYS2のmingw32/mingw64サブシステムにも存在し、こちらを用いるならビルドの必要は無い。

ディレクトリ ファイル 内容
C:\boost
└ boost_1_75_0 jamroot B2データ(メイン)
 │ ... ...
 ├ boost regex.hpp, *.hpp ライブラリインクルート
 │︙ ... ...
 │├ regex *.hpp regex用サブインクルード
 │︙ ... ...
 ├ libs
 │︙ ... ...
 │├ regex ... ...
 ││├ build Jamfile.v2 regexビルド用B2データ
 ││├ src *.cpp regexビルド用ソースコード
 ││├ example ... (サンプル用ソースコードディレクトリ)
 ││├ test ... (テスト用ソースコードディレクトリ)
 ││├ doc ... (ドキュメントディレクトリ)
 │︙︙ ... ...
 ├ tools ... ...
 │├ build bootstrap.bat b2.exe(インストールエンジン)ビルド用バッチ
 │││ Jamroot.jam b2.exeビルド用B2データ
 ││├ src ... ...
 │││├ engine *.cpp, *.h b2.exeビルド用ソースコード/インクルード
 │︙︙︙
 ├ doc (ドキュメントディレクトリ)
 ︙
覚え書き
regexライブラリはC++11からC++標準ライブラリも採用し、両者の差は少ないがboostの方が若干自由度が大きい。実用は標準ライブラリを優先すべきだがmingw-w64付属には大きなバグが残り(バージョン10.2.0確認)、現時点ではboostの方が安心だ。

ビルド

標準的な手順は展開ディレクトリHTMLスタートページ(index.html)からたどるGetting Started on Windowsの5.2 Or, Build Binaries From Sourceに従い、最初にインストールエンジンとなるb2.exeをビルドし、次に一括して全ビルドライブラリをビルドする。本サイトはmingw32/mingw64サブシステムのb2.exeを用いて、そのビルドは省略する。一括して全ライブラリをビルドするが、pythonライブラリだけは本サイト設定において特別な処理を必要とするため独立して行う。

覚え書き
標準的な手順はtools\build\bootstrap.batでb2.exeをビルドする。bootstrap.batはビルドを行うだけだが、念のためビルド以外の処理が追加されていないか内容を確認しておいた方が安心だろう。無条件にbootstrap.batを実行すればさらに安心かもしれないが、そうだとしても本サイトはmingw32/mingw64サブシステムのb2.exeを使う。なぜなら32ビット/64ビットでコンパイラおよびサブシステムを切り替える必要があり、b2.exeもそれぞれのバージョンを利用するほうがシンプルになる。なおbootstrap.batは展開ディレクトリにも存在するがマイクロソフトVisual C++(msvc)用として本サイトは関係ない。

python以外のビルド

stage.batを展開ディレクトリに置きそこで実行する。コマンドプロンプト出力をログするためMSYS2のteeを利用し、その理由で環境変数LANGを設定しPATHへmsys2サブシステムを追加する。32ビット/64ビットでforループを形成しそれぞれに適切なPATHを設定する。b2.exeは--without-pythonでpythonビルドを抑止する。toolsetプロパティはgccとする。32ビット/64ビットコンパイラはPATHで切り替えるがaddress-modelプロパティも明示する。これを明示しないと64ビットコンパイラに-m32オプションを与え32ビットオブジェクト(*.o)を生成してしまう。その他のプロパティは_PROPERTIESに設定する。プロパティはマニュアル4.4.3で一覧できる。

覚え書き
ウィンドウズ実装のruntime-linkプロパティはmsvcがランタイムをダイナミック/スタティックリンクするためのものと考えられる。mingw-w64はダイナミック/スタティックにかかわらずmsvcランタイムをダイナミックリンクするためこのプロパティは実効しない。プロパティ有無によるビルド出力のファイル名は異なるもののバイト列はヘッダを除き一致する

stage.bat

@setlocal
@set LANG=ja_JP.SJIS
@set _PROPERTIES=variant=debug,release link=shared,static threading=multi
@for %%a in (32 64) do @(
echo Stage mingw%%a build ...
setlocal enabledelayedexpansion
set PATH=C:\msys64\mingw%%a\bin;C:\msys64\usr\bin;!PATH!
b2 --without-python toolset=gcc address-model=%%a %_PROPERTIES% | tee build.log
mv build.log bin.v2\build_mingw%%a.log
mv bin.v2\config.log bin.v2\config_mingw%%a.log
endlocal
)

1.75.0バグへのパッチ当て

1.75.0、32ビットのcontextライブラリはバグを含みログは以下のエラーを報告する。なおこのバグは1.78.0で解消されている事を確認している。

...
C:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/10.2.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot export _jump_fcontext: symbol not found
C:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/10.2.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot export _make_fcontext: symbol not found
C:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/10.2.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot export _ontop_fcontext: symbol not found
collect2.exe: error: ld returned 1 exit status
...
...failed updating 4 targets...
...skipped 20 targets...
...updated 2989 targets...

GNUとマイクロソフトのアセンブラ挙動の違いを混乱した開発者の一人が誤ったパッチを当てたためでいずれ元に戻される。

各ファイル最終行でエクスポートする名前先頭のアンダースコア_を削除する。stage.batを再実行する。

libs\context\src\asm\jump_i386_ms_pe_gas.asm

...
.section .drectve
/* .ascii " -export:\"_jump_fcontext\"" */
.ascii " -export:\"jump_fcontext\""

libs\context\src\asm\make_i386_ms_pe_gas.asm

...
.section .drectve
/* .ascii " -export:\"_make_fcontext\"" */
.ascii " -export:\"make_fcontext\""

libs\context\src\asm\ontop_i386_ms_pe_gas.asm

...
.section .drectve
/* .ascii " -export:\"_ontop_fcontext\"" */
.ascii " -export:\"ontop_fcontext\""

pythonのビルド

以下において大文字で始まるPythonは言語名称あるいはその実装、小文字で始まるpythonはboostライブラリの一つとする。python(Boost.Python)はC++とPythonを仲介するフレームワークである。対象とするPythonはCPythonと呼ばれる実装で、あなたの知るPythonは恐らくCPythonでありMSYS2が導入するPythonもまたCPythonである。Python(CPython)はpython.exeなど実行ファイルの他にDLLを配布し、C++はDLLエクスポート関数(Python/C API)を通じてPythonと連携する。pythonはエクスポート関数のラッパーライブラリで、実行はダイナミック/スタティックリンクに関係なくDLLを必要とし、ビルドはインクルードとインポートライブラリを必要とする。MSYS2はPythonの一部としてDLL(例えばbin\libpython3.8.dll)、インクルード(include\python3.8\*.h)、インポートライブラリ(lib\libpython3.8.dll.a)を提供する。

本サイトはMSYS2導入Pythonを利用してpythonをビルドするが、B2でデフォルトビルドするとコンパイルエラーとリンクエラーが発生する。

...
...failed gcc.compile.c++ bin.v2\libs\python\build\gcc-10.2.0\debug\python-3.8\threading-multi\visibility-hidden\list.o...
gcc.compile.c++ bin.v2\libs\python\build\gcc-10.2.0\debug\python-3.8\threading-multi\visibility-hidden\long.o
In file included from ./boost/python/detail/prefix.hpp:13,
from ./boost/python/long.hpp:8,
from libs\python\src\long.cpp:5:
./boost/python/detail/wrap_python.hpp:57:11: fatal error: pyconfig.h: No such file or directory
57 | # include <pyconfig.h>
| ^~~~~~~~~~~~
compilation terminated.
...
...failed updating 108 targets...
...skipped 22 targets...
...updated 32 targets...

B2はlibs\python\build\Jamfileに従いPythonインストールを探索し必要な情報を推定してビルドを試みるが、インクルードパスに誤りコンパイルに失敗する。user-config.jamでインストール情報を明示して再ビルドする。

...
gcc.link.dll.mingw bin.v2\libs\python\build\gcc-10.2.0\debug\python-3.8\threading-multi\visibility-hidden\libboost_python38-mgw10-mt-d-x32-1_75.dll.a
C:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/10.2.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lpython38
collect2.exe: error: ld returned 1 exit status
...
...failed updating 4 targets...
...skipped 8 targets...
...updated 118 targets...

インポートライブラリ名をpython38(ファイル名ならlibpython38.a)と誤り、MSYS2導入はpython3.8.dll(libpython3.8.dll.a)なのでリンクに失敗する。B2側で修正するのが本筋だが適当な方法が見当たらず、乱暴だがlibpython3.8.dll.aを一時的にlibpython38.aへコピーする

結論として、user-config.jam、stage_python.batを展開ディレクトリに配置してそこでstage_python.batを実行する。user-config.jamのPythonインストール情報はmingw32/mingw64サブシステムで異なるためaddress-modelプロパティで切り替える。stage_python.batはuser-config.jamの位置をB2に伝えるため環境変数BOOST_BUILD_PATHを設定する。さらにインポートライブラリを一時的にコピーしてビルド終了後に消去する。Pythonバージョンは_PYVER、_PYVER_NODOTで指定しファイル名一部に利用する。b2.exeは--with-pythonでpythonのみビルドする。

user-config.jam

# ---------------------
# Python configuration.
# ---------------------
# using python : [version] : [command-or-prefix] : [includes] : [libraries] : [conditions] : [extension-suffix] ;
using python
: 3.8 # version
: C:\\msys64\\mingw32\\bin\\python.exe # command-or-prefix
: C:\\msys64\\mingw32\\include\\python3.8 # includes
: C:\\msys64\\mingw32\\lib # libraries
: <address-model>32 # conditions
;
using python
: 3.8 # version
: C:\\msys64\\mingw64\\bin\\python.exe # command-or-prefix
: C:\\msys64\\mingw64\\include\\python3.8 # includes
: C:\\msys64\\mingw64\\lib # libraries
: <address-model>64 # conditions
;

stage_python.bat

@setlocal
@set LANG=ja_JP.SJIS
@set BOOST_BUILD_PATH=%~dp0
@set _PYVER=3.8
@set _PYVER_NODOT=38
@set _PROPERTIES=variant=debug,release link=shared,static threading=multi
@for %%a in (32 64) do @(
echo Stage mingw%%a build for python ...
setlocal enabledelayedexpansion
set PATH=C:\msys64\mingw%%a\bin;C:\msys64\usr\bin;!PATH!
set _LIB_COPYFILE=C:\msys64\mingw%%a\lib\libpython%_PYVER_NODOT%.a
if exist !_LIB_COPYFILE! (
echo Temporary !_LIB_COPYFILE! to create already exists.
echo The build is cancelled.
) else (
cp C:\msys64\mingw%%a\lib\libpython%_PYVER%.dll.a !_LIB_COPYFILE!
b2 --with-python toolset=gcc address-model=%%a %_PROPERTIES% | tee build.log
mv build.log bin.v2\build_python_mingw%%a.log
mv bin.v2\config.log bin.v2\config_python_mingw%%a.log
rm !_LIB_COPYFILE!
)
endlocal
)

ディレクトリ配置

stage.batとstage_python.batが生成するファイルの配置を示す。オブジェクトファイル(*.o)はbin.v2\libs\[ライブラリ名]\buildディレクトリのプロパティ名によるサブディレクトリ階層に出力する。デフォルトプロパティはディレクトリを維持し、それ以外は下位に[プロパティ名]ディレクトリを作るため分かりやすい階層にならない。ライブラリファイル(*.a、*.dll、*.dll.a)はstage\libに出力する。

ディレクトリ ファイル 内容
C:\boost
└ boost_1_75_0 stage.bat, stage_python.bat ビルド用バッチ
 │ user-config.jam B2コンフィグ
 │ ... ...
 ├ boost ... ...
 │︙ ... ...
 ├ libs
 │︙ ... ...
 ├ tools ... ...
 │︙ ... ...
 ├ bin.v2 *.log ログ
 │├ libs
 ││︙ ... ...
 ││├ regex ... ...
 │││└ build
 │││ └ gcc-10.2.0
 │││  ├ debug
 │││  │├ address-model-64
 │││  ││︙ *.o オブジェクト
 │││  │├ link-static
 │││  ││︙ *.o オブジェクト
 │││  │└ threading-multi
 │││  │ └ visibility-hidden *.o オブジェクト
 │││  └ release
 │︙︙   ︙ *.o オブジェクト
 ├ stage
 │└ lib *.a スタティックリンクライブラリ
 │ *.dll, *.dll.a DLL、インポートライブラリ
 ︙

ライブラリファイルは同一ディレクトリに32ビット/64ビットやデバッグ/リリースが混在するためファイル名で区別する。DLLも同じディレクトリに出力し実行や配布に注意する。

ファイル名 説明
libboost_[ライブラリ名]-mgw10-mt-d-x32-1_75.a 32ビットデバッグスタティックリンクライブラリ
libboost_[ライブラリ名]-mgw10-mt-d-x32-1_75.dll 32ビットデバッグDLL
libboost_[ライブラリ名]-mgw10-mt-d-x32-1_75.dll.a 32ビットデバッグインポートライブラリ
libboost_[ライブラリ名]-mgw10-mt-x32-1_75.a 32ビットリリーススタティックリンクライブラリ
libboost_[ライブラリ名]-mgw10-mt-x32-1_75.dll 32ビットリリースDLL
libboost_[ライブラリ名]-mgw10-mt-x32-1_75.dll.a 32ビットリリースインポートライブラリ
libboost_[ライブラリ名]-mgw10-mt-d-x64-1_75.a 64ビットデバッグスタティックリンクライブラリ
libboost_[ライブラリ名]-mgw10-mt-d-x64-1_75.dll 64ビットデバッグDLL
libboost_[ライブラリ名]-mgw10-mt-d-x64-1_75.dll.a 64ビットデバッグインポートライブラリ
libboost_[ライブラリ名]-mgw10-mt-x64-1_75.a 64ビットリリーススタティックリンクライブラリ
libboost_[ライブラリ名]-mgw10-mt-x64-1_75.dll 64ビットリリースDLL
libboost_[ライブラリ名]-mgw10-mt-x64-1_75.dll.a 64ビットリリースインポートライブラリ

ビルドしたboostをクライアントプロジェクトが利用するにはコンパイラ/リンカ探索にMSYS2導入より優先してディレクトリを追加する。

種類 MSYS2より前置するディレクトリ
インクルード C:\boost\boost_1_75_0
ライブラリ C:\boost\boost_1_75_0\stage\lib

DLLを利用するにはプログラムと同じディレクトリにコピーするか、あるいは実行パス(環境変数PATH)を通す。

種類 MSYS2より前置するディレクトリ
実行パス(PATH) C:\boost\boost_1_75_0\stage\lib

テスト

各ライブラリはlibs\[ライブラリ名]\testディレクトリにテストプロジェクトを用意する。例えばregexライブラリ64ビットをテストする。libs\regex\testディレクトリでコマンドプロンプトを開きmingw64サブシステムにパスを通しB2を実行する。B2はディレクトリ/サブディレクトリ各ターゲットをビルドして実行を確認する。regexはlibs\regex\exampleにサンプルプロジェクトを持つが、テストプロジェクトはこれも同時にビルド実行する。

C:\boost\boost_1_75_0\libs\regex\test>set PATH=C:\msys64\mingw64\bin;%PATH%
C:\boost\boost_1_75_0\libs\regex\test>b2 toolset=gcc address-model=64
...
...failed updating 1 target...
...skipped 1 target...
...updated 525 targets...

一つの失敗(failed)が報告されるがこれはtest_warning.cppのコンパイルで、ウォーニングをエラーと見なすオプションを加えてウォーニング発生を調べるダミーターゲットであり、すなわちこの失敗は安全に無視できる。ビルド実行結果はbin.v2\libs\regex\test、bin.v2\libs\regex\test\[ターゲット名].test、bin.v2\libs\regex\example\[ターゲット名].testディレクトリのプロパティ名サブディレクトリ階層に出力する。出力結果を理解しても手間が掛かるわりに得る物が少なく、通常はB2実行の成功失敗で判断して良い。

Code::Blocksからの利用

例としてboostのregexライブラリgrepサンプルプログラム(C:\boost\boost_1_75_0\libs\regex\example\grep\grep.cpp)をCode::Blocksでコンソールアプリケーションとしてビルドする。最初にMSYS2導入boostライブラリにリンクして動作確認する。次にビルドしたboostライブラリの64ビットデバッグ版にリンクして動作確認する。最後にビルドboostライブラリでデバッグ追跡する。ライブラリは全てダイナミックリンクとする。スタティックリンクは必要ライブラリを芋づる式に記述する必要があり面倒くさい。ランタイムライブラリにその必要は無いが統一を重んじこれもダイナミックリンクとする。

MSYS2導入boostによるビルド実行

動作確認(1)を参考に32ビットコンソールアプリケーションのスケルトンを作成する。例えばC:\Users\user\MinGW\TestDrive\TestDrive.workspaceワークスペースを開き、[File|New|Project]のConsole applicationウィザードで[Project title]BoostRegexGrep、[Folder to create project in]C:\Users\user\MinGW\TestDriveとして作成する。ランタイムライブラリは動的リンクのままとする。main.cppをエディタに開き内容をC:\boost\boost_1_75_0\libs\regex\example\grep\grep.cppに置き換える。[Project|Build option]の[Project build options]ダイアログでDebug32、Release32ターゲット両方とも[Linker setting]ページ[Link libraries]リストボックスを以下に設定する。

ライブラリ
libboost_program_options-mt.dll.a
libboost_regex-mt.dll.a

[Project|Set project arguments]の[Select target]ダイアログでDebug32、Release32ターゲット両方の[Program arguments]に以下を与える。

実行時実引数
-E "^.*\bbool\b.*$" main.cpp

Debug32およびRelease32ターゲットで[Build|Run]すればmain.cppソースコードの"bool"を含む行がコマンドプロンプトに出力される。引き続き動作確認(1)を参考に64ビットターゲットを作成する。[Project|Properties]でDebug32/Relase32ターゲットをDebug64/Release64ターゲットにコピーして[Project|Build option]でコンパイラをGNU GCC Compiler (64bit)に変更する。Debug64およびRelease64ターゲットで[Build|Run]して出力を確認する。

ビルドboostによるビルド実行

ビルドしたboostライブラリの64ビットデバッグ版にリンクする。[Project|Properties]でDebug64ターゲットをDebug64_b2ターゲットにコピーし[Project|Build option]で設定を以下に変更する。

ライブラリ
libboost_program_options-mgw10-mt-d-x64-1_75.dll.a
libboost_regex-mgw10-mt-d-x64-1_75.dll.a

コンパイラ/リンカ探索はビルドboostをMSYS2導入に優先させる。[Project|Build option]でDebug64_b2ターゲット[Search directories]ページの[Compiler]ページ、[Linker]ページの探索パスを以下に変更する。

ページ 探索パス
[Compiler] C:\boost\boost_1_75_0
[Linker] C:\boost\boost_1_75_0\stage\lib

ダイナミックリンクはDLLに実行パスを通す必要があるが、Code::Blocksでの実行はPATHにライブラリディレクトリを自動追加するので省略できる。Debug64_b2ターゲットで[Build|Run]して出力を確認する。

デバッグ追跡

例えばboost::regex_search関数へ追跡する。main.cppのprocess_stream関数whileループ冒頭のboost::regex_search関数コールにテキストカーソルを置いて[Debug|Toggle breakpoint]でブレークポイントを設定する。Debug64_b2ターゲットで[Debug|Start / Continue]するとデバッガがスタートしブレークポイントで停止する。[Debug|Step into]するとboost::regex_searchへ追跡し、[Debug|Step into]や[Debug|Nest line]などで内部フローをステップ確認できる。[Debug|Start / Continue]で通常実行を再開する。ただしブレークポイントがループ内にありファイルから1行読み込む度に停止するので、必要に応じ[Debug|Toggle breakpoint]でブレークポイントを解除する。デバッガ上ではコンソールアプリケーションの終了と共にコマンドプロンプトは閉じるので、最終出力の確認が必要ならmain関数の最終行にブレークポイントを置く。