libclang/libLLVM を Windows(MinGW) 向けに llvm-mingw でクロスコンパイルするメモ(10.0.0 or later)

## 背景

* Windows で libclang/libLLVM(+ libcxx) を使いたい C++ アプリがある(e.g. C++ JIT など)
* MLIR を Windows 向けにビルドしたい

https://github.com/mstorsjo/llvm-mingw

llvm-mingw がありますが, libclang.dll などは提供されていない.

Windows 環境でビルドするのはめんどいので, Linux からクロスコンパイルしてみます.
 
## クロスコンパイル情報

https://llvm.org/docs/HowToCrossCompileLLVM.html

オフィシャルのページはなんかわかりずらいです...

https://github.com/mstorsjo/llvm-mingw

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` 
* ディスク空き容量 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 するビルドスクリプトです.

https://github.com/syoyo/llvm-project-mingw-build

### 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` にします.

https://releases.llvm.org/10.0.0/docs/CMake.html


最新 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)を使いましょう.

https://llvm.org/docs/HowToCrossCompileBuiltinsOnArm.html

ビルド例は以下です.

```
#!/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)

https://wiki.osdev.org/Stack_Smashing_Protector

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

* [x] libcxx のビルド
* [x] Wine で動作確認できるか試す 
* [ ] target triple を指定したほうがいいか?
* [ ] 実際にビルドされた libclang, libLLVM が, C++ アプリでリンクできるかためす
* [ ] SHARED ビルド(libLLVM.so, libclang.so), STATIC ビルドを試す.
* [x] compiler-rt が 10.0 or 最新 llvm-project repo でビルドできるか試す
* [ ] 同様にして, aarch64 向けにクロスコンパイルする