背景
- Windows で libclang/libLLVM(+ libcxx) を使いたい C++ アプリがある(e.g. C++ JIT など)
- MLIR を Windows 向けにビルドしたい
llvm-mingw がありますが, libclang.dll などは提供されていない.
Windows 環境でビルドするのはめんどいので, Linux からクロスコンパイルしてみます.
クロスコンパイル情報
オフィシャルのページはなんかわかりずらいです...
llvm-mingw のビルドスクリプトや, Docker.cross あたりの実際の例を見るのがよいでしょう.
手順概要
- A. ホスト環境で通常の gcc/clang を使って
clang-tblgen
,llvm-tblgen
をビルドする - B. clang/LLVM を llvm-mingw の linux クロスコンパイラでビルドする
- A でビルドした clang-tblgen, llvm-tblgen を使う
Ryzen5 1500X でビルドには 1 時間ちょっとかかります.
いい CPU(e.g. Threadripper 3990X)を用意するとよいでしょう.
ビルド環境
- Ubuntu 18.04
- host compiler: gcc(Ubuntu 標準) or clang-8 or later
- llvm-project
- tag
llvmorg-10.0.0
- tag
- ディスク空き容量 20 GB が理想(最低でも 10 GB ほど必要)
- llvm-mingw 20200325 prebuilt https://github.com/mstorsjo/llvm-mingw/releases
- cmake, ninja
- Windows は, x86 windows を想定
最近(?)ですと, clang/libcxx など含め, llvm-project で一式管理 & ビルドが推奨のようです.
制約事項
9.0.1(llvm-mingw 20191230) では, compiler-rt
(asan) のクロスコンパイルがうまくいきませんでした. 10.0.0(llvm-mingw 20200325) を使いましょう.
手順
libclang, libllvm をビルドするには, Host 環境に clang-tblgen, llvm-tblgen(.td からコード生成)が必要になります. llvm-tblgen は clang/llvm prebuilt バイナリ(e.g. https://github.com/llvm/llvm-project/releases/tag/llvmorg-9.0.1 から落とせるもの)や apt などで手に入りますが, clang-tblgen
はソースコードからビルドする必要があります.
(bintray とかで, どこかに prebuilt binary が転がっているといいのですが, 無さそうです)
以下の手順を reproduce するビルドスクリプトです.
llvm-project
したがって, まずは clang-tblgen をビルドする必要があります. いろいろバージョンごとでの違いを避けるためにも llvm-tblgen も同じバージョン(ソースコード)からビルドしてみます.
$ git clone https://github.com/llvm/llvm-project
$ cd llvm-project
$ git checkout llvmorg-9.0.1
tag は必要に応じて変えてください. llvmorg-x.y.z
という形式になっています.
host(native) で llvm-tblgen, clang-tblgen, llvm-config のビルド
cmake bootstrap します. -G ninja
で ninja ビルドがいいかと思いますが, make でも問題ありません.
distdir=`pwd`/dist-native
rm -rf build-native
mkdir build-native
cd build-native && cmake -G Ninja ../llvm \
-DCMAKE_INSTALL_PREFIX=$distdir \
-DLLVM_ENABLE_PROJECTS="clang" \
-DLLVM_TARGETS_TO_BUILD="host" \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DLLVM_OPTIMIZED_TABLEGEN=On \
-DLLVM_ENABLE_ASSERTIONS=ON \
LLVM_ENABLE_PROJECTS に "clang" を入れます. llvm-project repo ですと, clang フォルダ(llvm-project/clang
)は自動で見つけてくれるようです.
CMAKE_BUILD_TYPE は Release
でも 2GB ほどディスク消費します. MinSizeRel
がよいかと思います.
-DLLVM_OPTIMIZED_TABLEGEN=On
として, llvm-tblgen を最適化つきでビルドするとよいでしょう.
(ちなみに, Windows ネイティブで clang/llvm などビルドする場合は必須です. これを付けないと tblgen の処理がめちゃくちゃ遅くなります)
$ ninja && ninja install
でビルドとインストールします(ただ, インストールは必須ではない)
ビルドは, Ryzen5 1500X で 35 分ほどかかりました. Ryzen9 3950X(+ PCI Gen4 SSD) では 7 分ほどでした.
clang-tblgen
は, cmake install でインストールされないので注意です!!!
手動でコピーしましょう.
llvm-mingw で Window バイナリのクロスコンパイル
一度に clang, libcxx などビルドするとうまく行かないようなので, ここでは llvm, clang だけビルドします.
-DCMAKE_CROSSCOMPILING=True
で, クロスコンパイルすることを指定します.
どうも Host での llvm-config の設定も必要のようですので, LLVM_CONFIG_PATH
で, llvm-config のパスを設定します.
rm -rf build
mkdir build
LLVM_TBLGEN_PATH=`pwd`/dist-native/bin/llvm-tblgen
CLANG_TBLGEN_PATH=`pwd`/dist-native/bin/clang-tblgen
LLVM_CONFIG_FILENAME=`pwd`/dist-native/bin/llvm-config
cd build && cmake -G Ninja ../llvm \
-DCMAKE_CROSSCOMPILING=True \
-DCMAKE_SYSTEM_NAME=Windows \
-DLLVM_TABLEGEN=${LLVM_TBLGEN_PATH} \
-DCLANG_TABLEGEN=${CLANG_TBLGEN_PATH} \
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_FILENAME} \
-DCMAKE_C_COMPILER=/path/to/llvm-mingw-20191230-ubuntu-16.04/bin/x86_64-w64-mingw32-gcc \
-DCMAKE_CXX_COMPILER=/path/to/llvm-mingw-20191230-ubuntu-16.04/bin/x86_64-w64-mingw32-g++ \
-DCMAKE_RC_COMPILER=/path/to/llvm-mingw-20191230-ubuntu-16.04/bin/x86_64-w64-mingw32-windres \
-DLLVM_ENABLE_PROJECTS="clang" \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DLLVM_ENABLE_ASSERTIONS=ON
CMAKE_RC_COMPILER
は指定しなくてもいいかもしれません.
libLLVM.dll が欲しい場合は, -DLLVM_BUILD_LLVM_DYLIB=On
にします.
最新 master(clang/llvm 10 or later)だと, c-index-test で libxml2 で iconv.h
が見つからないエラーが出ます. -DLLVM_ENALBE_LIBXML2=Off
をつけると libxml2 をつかわないビルドになり解決します.
多少 warning が出ますが, これで cross compile できるはずです!
ちなみに -DCMAKE_BUILD_TYPE=RelWithDebInfo
など, デバッグ情報ありだと 45 GB ほどディスク領域消費しますので注意です!
(主に bin
が, static build のため一つ一つの .exe
がとても大きい)
libXXXX だけ欲しいのであれば, tool 周りはビルド不要なので, -DLLVM_BUILT_TOOLS=NO
とするのも手です.
llvm-mingw でのクロスコンパイルは, Ryzen5 1500X で 35 分ほどかかりました.
libcxx のビルド
llvm-mingw git repo の build-libcxx.sh
を参考にしてビルドしましょう.
- libunwind のクロスコンパイル
- libcxxabi のクロスコンパイル
- libcxx のクロスコンパイル
という手順になります. CMake の設定は結構ありますが, コンパイル自体はすぐに終わります.
compiler-rt
compiler-rt は optional です.
asan(address sanitizer)を使いたいときなどに必要になります.
compiler-rt は, llvm-mingw の clang クロスコンパイラだけあればビルドできます(libLLVM, libclang など他のプロジェクトには依存していない).
9.0 だとうまくいかないので, llvm-mingw 20200325(llvm 10.0.0)を使いましょう.
ビルド例は以下です.
#!/bin/bash
# --- config ---
LLVM_MINGW_DIR=/home/syoyo/local/llvm-mingw-20200325-ubuntu-18.04/
# --------------
curdir=`pwd`
distdir=`pwd`/dist-native
native_distdir=`pwd`/dist-native
builddir=`pwd`/build-compiler-rt-mingw-cross
arch=x86_64
buildarchname=x86_64
if [ -d "${builddir}" ]; then
rm -rf ${builddir}
fi
mkdir ${builddir}
# -DCMAKE_AR="${native_distdir}/bin/llvm-ar" \
# -DCMAKE_RANLIB="${native_distdir}/bin/llvm-ranlib" \
cd ${builddir} && cmake \
-G Ninja \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_INSTALL_PREFIX="${distdir}" \
-DCMAKE_C_COMPILER=${LLVM_MINGW_DIR}/bin/${arch}-w64-mingw32-clang \
-DCMAKE_CXX_COMPILER=${LLVM_MINGW_DIR}/bin/${arch}-w64-mingw32-clang++ \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_C_COMPILER_WORKS=1 \
-DCMAKE_CXX_COMPILER_WORKS=1 \
-DCMAKE_C_COMPILER_TARGET=$buildarchname-windows-gnu \
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=TRUE \
-DCOMPILER_RT_USE_BUILTINS_LIBRARY=TRUE \
../llvm-project/compiler-rt
cmake --build ${builddir} && cmake --build ${builddir} --target install
libspp(optional)
Stack smashing protector ライブラリです.
ランタイム時にスタック破壊を防ぐライブラリですかね.
ビルドは llvm-mingw repo を参照ください.
(libspp コードは gcc の一部となっています)
mingw ヘッダ/libc(?)の設定
libclang で C/C++ JIT などする場合, C のヘッダ, Windows 関連のヘッダ, C++ STL のヘッダなどが必要になります. llvm-mingw のパッケージを使えばよさそうですが, うまく行かない場合は, llvm-mingw git repo の mingw 関連のビルドスクリプトを参考にして, 追加でビルドが必要かもしれません.
libclang, libLLVM を使う C++ アプリのクロスコンパイル
.a でリンクだと指定順とかあって面倒なので, llvm/clang を SHARED でビルドしておくといいかもしれません.
(ただ, SHARED だと Bye サンプルがビルドできませんでした)
以下は shared(dll)バージョンでの解説です.
clang だけなぜか liblibclang.dll
という名前になります.
リンク用の .a
(MSVC で言う import library .lib
)は, liblibclang.dll.a
という名前になります.
/path/to/mingw-gcc file.cc -L/path/to/cross-mingw/lib -llibclang.dll
と言う感じで -l
で指定すればリンクできます! -lunwind.dll
で libunwind のリンクも必須になります.
TODO
- libcxx のビルド
- Wine で動作確認できるか試す
- target triple を指定したほうがいいか?
- 実際にビルドされた libclang, libLLVM が, C++ アプリでリンクできるかためす
- SHARED ビルド(libLLVM.so, libclang.so), STATIC ビルドを試す.
- compiler-rt が 10.0 or 最新 llvm-project repo でビルドできるか試す
- 同様にして, aarch64 向けにクロスコンパイルする