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

ICUライブラリを自分でビルドする手順を説明し、データサイズの縮小方法を示す。

ICUはユニコードによる国際化ライブラリで、本サイトはboostライブラリの導入時に依存ライブラリとしてICUを導入している。標準のICUライブラリは30メガバイト近いデータ(ICUデータ)を必要とするため、機能の一部しか利用しないアプリケーションでは過大となる。これを縮小する最も簡便な方法はオプションを変更してビルドし直す事であり、またデバッグビルドすればライブラリ内部までデバッガ追跡可能となる。本項目はバージョン69.1を前提に記述する。

ソースコード

ダウンロード

リンク先からicu-release-69-1.zipをダウンロードする。

ディレクトリ配置

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

目的 ディレクトリ
ICUルート C:\ICU
ソースコードの展開 C:\ICU\icu-release-69-1
ビルドの出力 C:\ICU\icu-release-69-1\icu4c\my_builds\[ビルド名]

zipファイルをICUルートに置きエクスプローラー右クリック[すべて展開]で[圧縮(ZIP)形式フォルダーの展開]ダイアログ[ファイルを以下のフォルダーに展開する]をC:\ICUに変更して[展開]する。デフォルトのままではicu-release-69-1ディレクトリをルートとして圧縮されているため一階層余分になる。展開ディレクトリはicu4cとicu4jサブディレクトリを持ち、それぞれC言語/C++(ICU4C)とJava(ICU4J)に対応する。従って本サイト作業はicu4c配下で行い、runConfigureICUスクリプトからconfigure(コンフィグレーションスクリプト)を実行してビルドする。本項目での相対パスはicu4c(C:\ICU\icu-release-69-1\icu4c)からとする。

ディレクトリ ファイル 内容
C:\ICU
└ icu-release-69-1
 ├ icu4c
 |├ source runConfigureICU configure実行スクリプト
 │││ configure コンフィグレーションスクリプト
 │││ Makefile.in メイクファイル(Makefile)テンプレート
 │││ ... ...
 ││├ common Makefile.in 共通ライブラリメイクファイルテンプレート
 ││││ *.cpp,*.h ソースコード、インクルード
 │││└ unicode *.h ライブラリインクルード
 │︙︙ ... ...
 ├ icu4j ... ...
 ︙

ビルド

本サイトはMSYS2のmingw32/mingw64サブシステムでそれぞれ32ビット/64ビットライブラリをビルドする。ウィンドウズではあるがmingw32/mingw64のPOSIX互換ターミナルで作業しリンクのHow To Build And Install On Unixに準拠する。

runConfigureICUスクリプトで各ディレクトリのメイクファイル(Makefile)やメイク補助ファイル(*.mk)などを作成し、MSYS2のmakeコマンドツールでビルドする。runConfigureICUはプラットフォームを設定してconfigureスクリプトを実行する。

オプション設定

32ビット/64ビットの選択以外はrunConfigureICU、configureオプションで設定し、MinGWプラットフォームとして以下のように実行する。

$ [パス]/runConfigureICU [runConfigureICUオプション] MinGW [configureオプション]

本サイトのオプション設定は以下に限定するが、設定可能な全オプションはrunConfigureICU --helpおよびconfigure --helpで知ることができる。

runConfigureICUオプション 機能
--enable-debug デバッグ情報生成フラグを加える 生成する(-g) 生成しない
--disable-release 最適化フラグを加えない 最適化しない 最適化する(-O3)
configureオプション 機能
--disable-shared ダイナミックリンクライブラリをビルドしない ビルドしない ビルドする
--enable-static スタティックリンクライブラリをビルドする ビルドする ビルドしない
--with-data-packaging ICUデータパッケージの方法 後述 auto

バージョン69.1はconfigureオプション--disable-sharedかつ--enable-staticとしてスタティックリンクライブラリのみビルドすると失敗する。ライブラリビルドに続いてICUデータパッケージ構築などのツールをビルドするが、これらがビルド済みライブラリの一つであるツールユーティリティをダイナミックリンクするためである。configureが生成するファイルの一つ(my_builds\[ビルド名]\icudefs.mk)にパッチを当てれば修正できるが思わぬ副作用を招く恐れもあり、スタティックリンクビルドも必ずダイナミックリンクビルドを伴うとして--disable-sharedを使用しないと決めた方が安心だろう。本サイトは常に--enable-staticを与えてスタティックリンクとダイナミックリンク両方のライブラリをビルドする。

configureの--with-data-packagingオプションはICUデータパッケージのビルド方法を指定するが、大きくはライブラリとしてメモリマップするか、データファイルとして読み込むかの二通りに分けられる。本サイト開発環境でautoはlibraryと同じで、デフォルトはダイナミックリンクライブラリとなる。

configureオプション 機能 分類
--with-data-packaging=files 複数からなる生データファイル データファイル
--with-data-packaging=archive アーカイブした単一のデータファイル
--with-data-packaging=library ダイナミックリンクライブラリ ライブラリ
--with-data-packaging=static スタティックリンクライブラリ
--with-data-packaging=auto 可能ならダイナミックリンクライブラリ(デフォルト)

データパッケージビルドは他のライブラリビルドから独立であるべきだがライブラリビルドの一部として扱っているため、異なる--with-data-packagingオプション毎に全ライブラリをビルドする事になり効率が悪い。本サイトはこれらを同一ディレクトリでビルドする。ビルド毎にrunConfigureICUは全てのメイクファイルを再作成するが、メイクは不要なコンパイルなどをスキップして重複作業のかなりの部分を省略できる。出力するデータファイルあるいはライブラリファイルが他オプションのそれを上書きする心配は無い。生データファイルは全てのオプションで中間生成されるため、archive、library、staticを順次ビルドすれば全オプションの出力ファイルを得る。

以下に各ビルドのオプションをまとめるが、本当に必要とするものだけをビルドすれば良い。ライブラリはダイナミックリンク、スタティックリンクの両方を作成する。各ビルドは--with-data-packaging=archive、library、staticで3回ビルドして全種類のデータパッケージを得る。

ビルド名 サブシステム runConfigureICUオプション configureオプション
debug32 mingw32 --enable-debug --disable-release --enable-static --with-data-packaging=[データパッケージ]
release32
debug64 mingw64 --enable-debug --disable-release
release64

ビルド

icu4cディレクトリにmy_buildsディレクトリを作成し、さらに各ビルドの出力ディレクトリを作成する。

configureの作成したMakefileはinstallフォニーターゲットを持つが、これをターゲットとしてメイクする(make install)とmingw32/mingw64サブシステムのinclude/lib/binディレクトリのICUファイルを上書きする。MSYS2はboostライブラリ導入時に依存ライブラリとしてICUを既に導入しており、この上書きは危険な結果を招きかねない。デフォルトターゲットによるメイク(make)でカレント配下への出力に限定してMSYS2導入や他バージョンとの共存を図る。

覚え書き
configureの--prefixオプションなどを適切に設定すればmake installはMSYS2導入や他バージョンと干渉しない。ICUのデフォルトメイク(make)はクライアントに公開するファイルの配置が単純でなく、特にライブラリインクルードは複数ディレクトリに分散している。このため--prefixしてmake installするほうが使い勝手は良くなると思うが、とあるライブラリビルドのミスオペレーションで環境破壊した苦い経験からサイト作成者はmake installを二度と使用しない。

debug64を例にビルド方法を示す。runConfigureICUに適切なrunConfigureICUオプションとconfigureオプションを与えて実行し、デフォルトターゲットでメイクする。これを--with-data-packaging=archive、library、staticで繰り返すがビルドを3回待つのは苦痛で、その度にオプションをコマンドライン指定しなければならないので、bashスクリプトmy_buildをdebug64ディレクトリに作成して実行する。

  1. mingw64ターミナルを起動する。
  2. my_buildsディレクトリへ移動する。my_buildsディレクトリが無い場合は作成する。my_buildsディレクトリにdebug64ディレクトリ(出力ディレクトリ)を作成して移動する。
  3. my_buildスクリプトファイルをdebug64ディレクトリに作成する。conficu_opシェル変数にはrunConfigureICUオプションとしてデバッグ用を設定するが、リリースビルドなら空白とする。
  4. filters.jsonファイルをdebug64ディレクトリに作成する。これは空のJSONファイルである。
  5. my_buildスクリプトを実行する。
    user@THINKPAD-L430 MINGW64 /c/ICU/icu-release-69-1/icu4c/my_builds/debug64
    $ ./my_build
    ...

filters.jsonはデータパッケージに含むデータを指定する。ここでは空としてデフォルトの全データを含ませるが、常にデフォルトデータのままで良ければ不要でmy_buildのexport行も削除する。なお、filters.jsonによるデータパッケージサイズの削減方法を後述する。

my_build

#!/bin/bash
export ICU_DATA_FILTER_FILE=$PWD/filters.json
conficu_op="--enable-debug --disable-release"
for icudata in archive library static
do
../../source/runConfigureICU ${conficu_op} MinGW \
--enable-static --with-data-packaging=${icudata}
make
done

filters.json(全データ)

{
}

ディレクトリ配置

runConfigureICUおよびmakeが出力ディレクトリ(my_builds\debug64)に生成するファイルの配置を示す。runConfigureICUはsourceディレクトリとほぼ同じディレクトリ階層をdebug64ディレクトリに作成し、各ディレクトリにメイクファイルを配置する。ディレクトリのいくつかはメイクファイルの補助ファイルを持つ。例えば...\data\rules.mkは...\filters.jsonから作成したデータパッケージビルド用の補助ファイルで、...\data\Makefileはこれをインクルードする。makeはメイクファイル(my_builds\debug\Makefile)を実行するが、これは主にディレクトリ階層を巡回して各メイクファイルを実行する。

ディレクトリ ファイル 内容
C:\ICU
└ icu-release-69-1
 ├ icu4c
 |├ source runConfigureICU configure実行スクリプト
 ││︙ ... ...
 |├ my_builds
 ││├ debug64 my_build 本サイトが追加したスクリプト
 ││││ filters.json 本サイトが追加したデータパッケージ設定
 ││││ Makefile メイクファイル
 ││││ ... ...
 │││├ config ... (コンフィグディレクトリ)
 │││├ common Makefile 共通ライブラリメイクファイル
 ││││ *.d, *.o, *.ao 依存関係、オブジェクト
 │││├ i18n ... (i18nライブラリビルドディレクトリ)
 │││├ layoutex ... (レイアウト拡張ライブラリビルドディレクトリ)
 │││├ io ... (ユニコードstdioライブラリビルドディレクトリ)
 │││︙
 │││├ tools Makefile サブディレクトリ巡回メイクファイル
 ││││├ makeconv Makefile makeconv(コード変換データ作成)メイクファイル
 │││││ *.d, *.o, 依存関係、オブジェクト
 ││││︙ ... ...
 ││││└ toolutils ... (ツールユーティリティライブラリビルドディレクトリ)
 │││├ data Makefile データパッケージメイクファイル
 │││││ rules.mk データパッケージメイク補助、filters.jsonから作成
 │││││ ... ...
 ││││└ out icudt69l.dat アーカイブデータ
 ││││ ├ build
 ││││ │└ icudt69l ... 生データディレクトリ
 ││││ ︙ ︙ ... ...
 │││├ bin *.exe ツール実行、makeconv.exeなど
 │││├ lib *.dll ダイナミックリンクライブラリ、icuucd69.dllなど
 ││││ *.dll.a インポートライブラリ、libicuucd.dll.aなど
 ││││ *.a スタティックリンクライブラリ、libsicuucd.aなど
 ││││ icudtd69.dll データダイナミックリンクライブラリ
 ││││ libicudtd.dll.a データインポートライブラリ
 ││││ libicudtd.a, libsicudtd.a データスタティックリンクライブラリ
 │││├ stubdata Makefile スタブデータライブラリメイクファイル
 ││││ icudtd69.dll スタブデータダイナミックリンクライブラリ
 ││││ libicudtd.dll.a スタブデータインポートライブラリ
 ││││ libsicudtd.a スタブデータスタティックリンクライブラリ
 ││││ ... ...
 │││├ samples ... (サンプル用ビルドディレクトリ)
 │││└ test ... (テスト用ビルドディレクトリ)
 ││︙ ...
 ︙︙

オブジェクトファイル*.oはダイナミックリンクライブラリビルド用、*.aoはスタティックリンクライブラリビルド用であるが、本サイト環境では両者に差異は無い。データスタティックリンクライブラリlibicudtd.aとlibsicudtd.aも同様で、それぞれダイナミックリンクライブラリあるいはスタティックリンクライブラリのクライアント用であるが、これも差異は無い。デバッグビルドはファイル本体名末尾(バージョン番号を含む場合はバージョン番号の前)に"d"を挿入するが、リリースビルドは挿入しない。mingw32サブシステムビルドはデータスタティックリンクライブラリのファイル名が何らかの不具合でliblibicudtd.aまたはliblibsicudtd.aとなるが、スタブデータのそれはlibsicudtd.aのままなので注意する。生データディレクトリ名末尾、およびアーカイブデータのファイル名末尾の"l"はデータがリトルエンディアンである事を示す。

スタブデータスタティックリンクライブラリはデータを全く含まないデータパッケージである。データパッケージをデータファイルとする場合、データライブラリは不要だが無いとリンクエラーでビルドできないため、代わりにスタブデータライブラリを用いる。なお両者でインポートライブラリlibicudtd.dll.aに差異は無い。

データパッケージを除くライブラリファイルをまとめる。以降、デバッグビルドが追加する"d"は(d)と表記する。これら全て...\libに配置される。

ライブラリ ダイナミックリンク インポート スタティックリンク 備考
共通 icuuc(d)69.dll libicuuc(d).dll.a libsicuuc(d).a
i18n icuin(d)69.dll libicuin(d).dll.a libsicuin(d).a
レイアウト拡張 iculx(d)69.dll libiculx(d).dll.a libsiculx(d).a
ユニコードstdio icuio(d)69.dll libicuio(d).dll.a libsicuio(d).a
ツールユーティリティ icutu(d)69.dll libicutu(d).dll.a libsicutu(d).a makeconvなどのツールが利用

データパッケージファイルをライブラリ、スタブライブラリも含めてまとめる。

種類 データパッケージ ファイル 配置
ライブラリ ダイナミックリンク icudt(d)69.dll ...\lib
インポート libicudt(d).dll.a
スタティック mingw32 liblibicudt(d).a, liblibsicudt(d).a
mingw64 libicudt(d).a, libsicudt(d).a
スタブライブラリ ダイナミックリンク icudt(d)69.dll ...\stubdata
インポート libicudt(d).dll.a
スタティック libsicudt(d).a
データ 生データディレクトリ icudt69l ...\data\out\build
アーカイブ icudt69l.dat ...\data\out

クライアントプロジェクトがビルドしたICUを利用するにはコンパイラ/リンカ探索にMSYS2導入より優先してディレクトリを追加する。本サイトはデフォルトターゲットにとどめmake installしないためライブラリインクルードは展開時の配置を参照しなければならず、ICUではライブラリ毎のディレクトリに分散する。データパッケージをデータファイルとする場合はスタブデータライブラリをリンクする。

種類 MSYS2より前置するディレクトリ
インクルード C:\ICU\icu-release-69-1\icu4c\source\common
C:\ICU\icu-release-69-1\icu4c\source\i18n
C:\ICU\icu-release-69-1\icu4c\source\layoutex
C:\ICU\icu-release-69-1\icu4c\source\io
ライブラリ データライブラリ利用 C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\lib
データファイル利用 C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\stubdata
C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\lib

ダイナミックリンクライブラリ(DLL)は...\lib(スタブデータライブラリは...\stubdata)に出力する。これを利用するにはDLLをプログラムと同じディレクトリにコピーするか、あるいは実行パス(環境変数PATH)を通す。

種類 MSYS2より前置するディレクトリ
実行パス(PATH) データライブラリ利用 C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\lib
データファイル利用 C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\stubdata
C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\lib

ICUデータパッケージ

データパッケージの選択優先順位は以下とされる。

  1. アプリケーションがu_setCommonData()をコールすれば実引数に与えたアドレスのデータを使用する。
  2. リンクしたデータライブラリがスタブでなければそのデータを使用する。
  3. データファイルicudt<version><flag>(例えばicudt69l)を探索、ロードして使用する。

データファイル探索のディレクトリ優先順位は以下とされるが、データライブラリを使用するなら関係ない。

  1. アプリケーションがu_setDataDirectory()をコールすれば実引数に与えたディレクトリ。
  2. ICU_DATA環境変数に与えたディレクトリ。
  3. ライブラリビルド時にICU_DATA_DIRマクロに与えたディレクトリ。

以下サンプルコードはUTF-16文字列をシフトJISに変換してコマンドプロンプト出力するもので、データライブラリをリンクすれば正常に動作する。スタブデータライブラリをリンクするとU_FILE_ACCESS_ERRORとなるが、(1)をアンコメントして生データディレクトリを参照、または(2)をアンコメントしてアーカイブデータを参照すれば正常動作する。

#include <iostream>
#include <unicode/putil.h>
#include <unicode/ucnv.h>
int main()
{
const wchar_t in[]=L"こんにちは世界!";
char out[256]={0};
UErrorCode myError=U_ZERO_ERROR;
// u_setDataDirectory("C:\\ICU\\icu-release-69-1\\icu4c\\my_builds\\debug64\\data\\out\\build"); // (1)
// u_setDataDirectory("C:\\ICU\\icu-release-69-1\\icu4c\\my_builds\\debug64\\data\\out"); // (2)
UConverter* conv=ucnv_open("shift-jis",&myError);
ucnv_fromUChars(conv,out,sizeof(out),reinterpret_cast<const UChar*>(in),-1,&myError);
ucnv_close(conv);
std::cout<<out<<std::endl;
if (U_FAILURE(myError)) {std::cout<<u_errorName(myError)<<std::endl;}
return 0;
}

データパッケージサイズの削減

runConfigureICUの実行するconfigureスクリプトはsource\pythonディレクトリのPythonモジュールicutools.databuilderを用いてICU_DATA_FILTER_FILE環境変数で指定するデータパッケージ設定ファイル(my_builds\debug64\filters.json)からmy_builds\debug64\data\rules.mkを作成する。...\data\Makefileはこれをインクルードして設定ファイル指定のデータからデータパッケージをビルドする。設定ファイルが無いか空の場合はデフォルトとして全データとなる。データソースはsource\dataに存在する。

...
PYTHONPATH="$srcdir/python" \
$PYTHON -m icutools.databuilder \
--mode gnumake \
--seqmode parallel \
--src_dir "$srcdir/data" \
--filter_file "$ICU_DATA_FILTER_FILE" \
$ICU_DATA_BUILDTOOL_OPTS \
> data/rules.mk
...
覚え書き
これを参考にすればデータパッケージを他ライブラリから独立してビルドできるが、混乱を招く可能性があるので推奨しない。
  1. PYTHONPATH環境変数にsource\pythonへのパスを設定する。
  2. icutools.databuilderを実行する。
    • $PYTHONはPython実行コマンドに置き換える。
    • $srcdir/dataはsource\dataへのパスに置き換える。
    • $ICU_DATA_FILTER_FILEはmy_builds\debug64\filters.jsonへのパスに置き換える。
    • $ICU_DATA_BUILDTOOL_OPTSはJava(ICU4J)追加オプション用なので削除する。
    • 出力先はmy_builds\debug64\data\rules.mkへのパスとする。
  3. my_builds\debug64\dataへ移動してメイク(make)する。
    • ビルドされるデータパッケージは最後に実行したrunConfigureICUの--with-data-packagingオプションに従う。

文字コード変換だけを利用するとしてデータパッケージサイズを削減する。...\filters.jsonを以下に置き換え、...\my_buildスクリプトを実行してビルドし直す。mingw64でビルドされるデータパッケージサイズを比較するがmingw32もほぼ同様となる。なおicudt69lは生データディレクトリに含まれるファイルサイズ総和を示す。

データ icudt(d)69.dll libsicudt(d).a icudt69l icudt69l.dat
全データ 27.43MiB 27.34MiB 27.1MiB 27.34MiB
文字コード変換のみ 5.05MiB 4.96MiB 4.95MiB 4.96MiB

filters.json(文字コード変換のみ)

{
"strategy": "additive",
"featureFilters": {
"conversion_mappings": "include",
"cnvalias": "include"
}
}

Code::Blocksからの利用

Code::BlocksからMSYS2導入ICUライブラリを利用するための設定と、本項目でビルドしたICUライブラリを利用する設定を示す。サンプル実行やデバッグ確認の方法は省略する。

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

MSYS2が導入するデータパッケージは全データを含むデータライブラリに限定される。レイアウト拡張ライブラリは導入しない。インクルードファイル、ライブラリファイルは標準ディレクトリに導入されていてライブラリ指定だけを追加する。[Project|Build option]の[Project build options]ダイアログの各ターゲット[Linker setting]ページ[Link libraries]リストボックスに以下を追加するが、もちろん不要なライブラリは省ける。文字コード変換だけなら共通ライブラリとデータライブラリだけで良い。

ダイナミック スタティック 説明
libicuuc.dll.a libicuuc.a 共通
libicuin.dll.a libicuin.a i18n
libicuio.dll.a libicuio.a ユニコードstdio
libicudt.dll.a libicudt.a データライブラリ

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

本項目でビルドしたデータパッケージはデータライブラリまたはデータファイルでそれぞれ設定は異なる。最初にデータライブラリを用いる場合を説明する。ライブラリ指定として以下を[Link libraries]リストボックスに追加するが、mingw32ビルドlibsicudt(d).aは前置"lib"が重なりliblibsicudt(d).aとなる。デバッグターゲットは(d)に"d"を挿入し、リリースターゲットは挿入しない。

ダイナミック スタティック 説明
libicuuc(d).dll.a libsicuuc(d).a 共通
libicuin(d).dll.a libsicuin(d).a i18n
libicuio(d).dll.a libsicuio(d).a ユニコードstdio
libiculx(d).dll.a libsiculx(d).a レイアウト拡張
libicudt(d).dll.a libsicudt(d).a データライブラリ

コンパイラ/リンカ探索はビルドICUをMSYS2導入に優先させる。[Project|Build option]で各ターゲット[Search directories]ページの[Compiler]ページ、[Linker]ページの探索パスに以下を追加する。[Compiler]はライブラリ毎に設定する。[Linker]の"debug64"はターゲットに合わせて適切に書き換える。

ページ 探索パス 説明
[Compiler] C:\ICU\icu-release-69-1\icu4c\source\common 共通
C:\ICU\icu-release-69-1\icu4c\source\i18n i18n
C:\ICU\icu-release-69-1\icu4c\source\io ユニコードstdio
C:\ICU\icu-release-69-1\icu4c\source\layoutex レイアウト拡張
[Linker] C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\lib -

データパッケージにデータファイルを用いる場合は以下となる。こちらのmingw32ビルドlibsicudt(d).aは前置"lib"が重ならない。探索パス[Linker]トップに...\stubdataを追加してスタブデータライブラリをデータライブラリの代わりにリンクする。

ダイナミック スタティック 説明
libicuuc(d).dll.a libsicuuc(d).a 共通
libicuin(d).dll.a libsicuin(d).a i18n
libicuio(d).dll.a libsicuio(d).a ユニコードstdio
libiculx(d).dll.a libsiculx(d).a レイアウト拡張
libicudt(d).dll.a libsicudt(d).a スタブデータライブラリ
ページ 探索パス 説明
[Compiler] C:\ICU\icu-release-69-1\icu4c\source\common 共通
C:\ICU\icu-release-69-1\icu4c\source\i18n i18n
C:\ICU\icu-release-69-1\icu4c\source\io ユニコードstdio
C:\ICU\icu-release-69-1\icu4c\source\layoutex レイアウト拡張
[Linker] C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\stubdata スタブデータ
C:\ICU\icu-release-69-1\icu4c\my_builds\debug64\lib -