ICU(International Component for Unicode)をCMake+VisualStudioでビルドするためのパッチを作成しました.
- icu4c-55_1-src_cmakebuild.patch (バージョン55.1用)
- icu4c-56_1-src_cmakebuild.patch (バージョン56.1用)
一応ICUの公式の配布物にはVisualStudioのソリューションファイルが含まれているのですが, DLLとしてビルドするのが前提になっていて,スタティックリンクライブラリを簡単に 作成することができません.
マニュアルにはCygwinを入れてゴニョゴニョせよ,とは書いてますが,それだけのために Cygwinを導入するのも大変なので,前述のソリューションファイルやプロジェクト ファイルを分析するrubyスクリプトを書いてCMakeのためのファイルを生成することにしました (使いはしないと思いますがスクリプトごとパッチに入れてあります).
ICUのビルド構造
以下はICUの構造に関する個人的なメモです.
ライブラリと,そのライブラリを構成するソースファイルの関係は.vcxprojから比較的簡単に抜き出すことができますが, ICUのビルド時の構造のため少し複雑な部分があります.
ICUはいくつかのライブラリで構成されています.
ライブラリ名 | .vcxproj名 | 役割 |
---|---|---|
icudt | stubdata/makedata | 処理に使うテーブルデータ |
icuuc | common | 共通ライブラリ |
icuin | i18n | 日付時刻関連のライブラリ |
icuio | io | 入出力のためのライブラリ |
icule | layout | 複雑な言語での文字表示のためのレイアウトエンジン |
iculx | layoutex | 同上 |
ややこしいのがicudtというライブラリで,これはICU内部で使用する様々な変換テーブルなどのデータを含んでいるのですが, それらのデータはICUのツール(pkgdataなど)でバイナリファイルとして直接作成されます.
そのICUのツールは当然上記のライブラリに依存しているわけで,要するにビルド順序に循環参照が発生しているわけです.
ビルド時に小さなツールを最初に作ってそれがデータを含むソースファイルを生成する,というライブラリは結構あるのですが, データを含むバイナリを直接生成する,という構成を取っているライブラリは珍しいと思います(といってもpkgdataは.objを作ったのちlibを起動しているだけのようですが).
この循環参照を解決するために,空のデータを生成しているのがstubdataというプロジェクトです. このプロジェクトが仮のicudtライブラリを作ります.これを使ってICUのツール群をいったん作成し, それらが揃った段階でmakedataというプロジェクトが本物のicudtライブラリを作成します.
makedataプロジェクトは実態としてはMakefile(nmake)です.中身を精査してみましたが,このMakefileが 実際に使用するのはpkgdataとicupkgというツールのみのようです.他のツールを使った生成規則も含まれていますが, 配布されているソースアーカイブ(icu4c-*-src.zip)からビルドする分にはこれらの二つのツールで十分なようです.
(icu4c-*-src.zip内にはicu/source/data/in/icudt*l.dat
というファイルが含まれていますが,他の生成規則はこれを生成するためのもののようです.別の配布物であるicu4c-*-data.zipにはこの.datファイルを生成するための依存ファイルが含まれていました.)
pkgdataの謎挙動
前述の通りpkgdataツールが,libファイルやdllを生成します.出力ファイル名は,-Lオプションで指定することができるのですが
文字列icudt
を含むファイル名を指定すると出力ファイル名が強制的に変更されます.
- スタティックモード(-m static)の場合
- 出力ファイル名に
icudt
が含まれる場合,指定した文字列の先頭に強制的にs
が付く. - DLLモードの場合(-m dll)の場合
- 出力ファイル名に
icudt
が含まれる場合,出力ファイル名(インポートライブラリ名)が強制的にicudt.lib
になる.
windows版のpkgdataとlinux版のpkgdata(Debianだとicu-devtoolsに含まれる)はインターフェースが異なるらしく, このあたりの詳しいことはドキュメント化されていないようです(あるならだれかおせーて).
ソースを見る限り上記のような処理をしていました.
スタティックモードの挙動はicu/source/tools/pkgdata/pkgdata.cpp
の1772行目付近(pkg_createWindowsDLL関数内)
sprintf(staticLibFilePath, "%s%s%s%s%s", o->targetDir, PKGDATA_FILE_SEP_STRING, (strstr(o->libName, "icudt") ? "s" : ""), o->libName, LIB_EXT);
DLLモードは同ファイルの1805行目
if (strstr(o->libName, "icudt")) { uprv_strcat(libFilePath, LIB_FILE); } else { uprv_strcat(libFilePath, o->libName); uprv_strcat(libFilePath, ".lib"); } uprv_strcat(dllFilePath, o->entryName);
なぜそうなっているのかはよくわかりません.
このページに掲載したパッチではこれらをコメントアウトして-Lオプションに指定した値が無条件で反映されるように
修正しています.
あとDLLモードではdllファイルのファイル名を制御する方法がないようです.-pオプション(.obj内のシンボル名のプレフィックス)に
指定した値に.dll
をつけたファイル名になるようです.