Visual Studio C++ Build Tools + clang-cl + CMake + Ninja で C++ バッチビルドや VSCode 連携環境を整える

## 背景

* Windows C++ アプリを MSVC or clang-cl でバッチビルドしたい(e.g. CI ビルドなど)
* VSCode + Cmake でお手軽連携したい
* WSL で Linux 環境で Windows アプリをビルドしたい

MSVC(コンパイラ関連)は Visual Studio 入れるか, C++ Build Tools でインストールできます.

## C++ Build Tools とは?

名前がころころ変わります. ~~最近は Build Tools for Visual Studio 2019 です.~~ (2022/11 時点では Microsoft C++ Build Tools or Visual Studio Build Tools(VSBT) になりました 😓)
ダウンロードページはわかりづらいところにありますし, URL もコロコロ変わるようなので, 頑張って探してください.

コンパイラは MSVC(cl.exe) が入ります. Visual Studio から IDE 部分を抜いて bin/include/libs だけの環境な感じです.
(インストーラーでは clang-cl(LLVM) もインストールすることができます)

MSVC のライセンス規約としては Visual Studio 関連と一緒に使うときのみ利用できるとありますので, VS ライセンスが必要です.
(2022/07 月の EULA 改定で, OSS のビルドには VS ライセンスなしで使えるようになりました https://qiita.com/syoyo/items/3b830194fbd119de5003 )

Microsoft C++ Build Tools (2019) not allowed for use with Visual Studio Code?
https://law.stackexchange.com/questions/49477/microsoft-c-build-tools-2019-not-allowed-for-use-with-visual-studio-code

VSCode のチュートリアルで, ライセンス規約を破ってそうな気がしますがいいのでしょうかね?
(VS が入っているマシンとは別にビルド用マシンに C++ Tools だけインストールとか OK なのかな?)

Windows SDK は, VS ライセンスは不要です. また, ネットワークドライブにファイルを置いてもよいなど, ライセンス規約は MSVC ほど厳しくありません.

### 開発環境インストール

C++ Build Tools or Visual Studio の installer で

* C++ Compiler(MSVC inc/lib)
* Windows SDK

をインストールしておきます.
(CI ビルド環境構築用に, コマンドラインでダウンロードとインストールする機能もあるようです)

clang-cl は llvm.org から prebuilt をインストールしているものとします.

cmake.exe, ninja.exe を `%PATH%` のある所にインストールしているものとします.
(ninja は WSL(Linux)版でもよいでしょう. cmake は windows バイナリでないとだめそうです)

## CMake bootstrap

CMake + Ninja + clang-cl で Windows C/C++ ビルドのメモ
https://qiita.com/syoyo/items/358fd4cf5315991ce0ae

にあるように, cmake では環境変数など見て, MSVC でビルドされているかどうか判定しているようです(`CMAKE_CXX_COMPILER=clang-cl.exe` 指定だけではうまくいかない)

`vcbarsall.bat` とかでいったん環境設定してコマンドラインに入るのもめんどいです.

llvm プロジェクトにある WinMsvc.cmake 設定を参考にします.

tinyusdz に出来上がったものがありますので参考にしてください.

https://github.com/syoyo/tinyusdz/blob/master/bootstrap-clang-cl-win64.bat

```
rmdir /s /q build
mkdir build

cd build
cmake.exe -G Ninja -DCMAKE_TOOLCHAIN_FILE="cmake/clang-cl-msvc-windows.cmake" ^
  -DHOST_ARCH=x64 ^
  -DLLVM_NATIVE_TOOLCHAIN="C:/Program Files/LLVM/" ^
  -DMSVC_BASE:FILEPATH="C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.26.28801\\" ^
  -DWINSDK_BASE="C:\\Program Files (x86)\\Windows Kits\\10\\" ^
  -DWINSDK_VER="10.0.18362.0" ^
  ..
```

* clang-cl へのパス
* MSVC SDK(STL や vc 用ランタイム inc/lib) へのパス
* Windows SDK(C runtime や windows.h などがある)へのパス
  * Windows SDK のヘッダは MSVC 依存のヘッダなどを呼んでいるので, Windows SDK 単体で C++ コンパイルは不可(STL のために MSVC SDK が必要).
  * C 言語に限れば UCRT(libc 相当)でいけるかもはしれません.

を設定します.

これで `vcbarsall.bat` などを呼ばずとも cmake で直に clang-cl でのビルドを bootstrap できます!
設定を少し変えれば MSVC cl.exe も利用できるものと思われます.

VSCode との連携もこれでやりやすくなるはずです!

### WSL でビルド

`clang-cl-msvc-windows.cmake` を多少変更すればいけます.

`LLVM_NATIVE_TOOLCHAIN` には linux version(e.g. llvm.org から落とせる Ubuntu 18.04 prebuilt) の clang を指定します.
(2022/11 時点で x86 版の prebuilt は 14.0.0 までのようです)

~~ただ, 空白のあるパスがあるとうまくいかないです(cmake の `-imsvc` のパスをうまくやればいけるかもしれません)~~ clang-cl or cmake のバージョンにもよるかも. clang-cl-14 + cmake 3.24 だと空白あつかえました.

```
cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE="cmake/clang-cl-msvc-wsl.cmake" \
-DLLVM_NATIVE_TOOLCHAIN="$HOME/local/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04/" \
-DHOST_ARCH=x64 \
-DMSVC_BASE:FILEPATH="/mnt/c/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133" \
-DWINSDK_BASE="/mnt/c/Program Files (x86)/Windows Kits/10" \
-DWINSDK_VER="10.0.19041.0" \
... 
```

SDK のバージョンやパスは適宜調整ください.

#### 空白問題対応版

もし空白のあるパスがうまく扱えないようであれば,
`/mnt/c/Program Files/...` にシンボリックリンクを貼り, 空白の無いパスにしてあげるといけます.


```
rmdir /s /q build
mkdir build

cd build
cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE="cmake/clang-cl-msvc-wsl.cmake" \
  -DHOST_ARCH=x64 \
  -DLLVM_NATIVE_TOOLCHAIN="/mnt/c/Users/syoyo/local/clang-10/" \
  -DMSVC_BASE:FILEPATH="/mnt/c/Users/syoyo/msvc/" \
  -DWINSDK_BASE="/mnt/c/Users/syoyo/winsdk" \
  -DWINSDK_VER="10.0.18362.0" \
  ..
```

注意点として, `/EHs`(C++ 例外)などを明示的に CXX_FLAGS に指定が必要です.
また, グラフィックスなど使っていたりする場合, `gdi32` など必要な Windows ライブラリも `target_link_libraries` で指定が必要です.
(リンク時に未解決シンボルエラーを検索して, どのライブラリ(.lib)とリンクすればよいか調べましょう)

#### Windows 側ファイル参照だと遅いかも

WSL 側から Windows 側(NTFS) のファイルアクセスは遅いことが知られています.
コンパイルがもっさりしていると感じたら, SDK ファイル群は WSL 側にこぴって使うのも検討してみましょう.

## CMAKE_CXX_COMPILER_ID

clang-cl では, Cmake `MSVC` が有効になります.
clang-cl か, cl.exe(MSVC) かを判定したい場合は, `CMAKE_CXX_COMPILER_ID` を使います.

clang-cl の場合は `Clang` にセットされます(C が大文字に注意)

```
if (MSVC) 
  if (CMAKE_CXX_COMPILER_ID SQTEQUAL "Clang")
    ...
  endif ()
endif ()
```

### VSCode で WSL(Linux) でクロスコンパイルし Windows バイナリをビルドする

最近(いつ?)vscode を wsl 側から起動すると, Windows 側のアプリが立ち上がって動くようになりました.
(もしくは Remote WSL 接続モード)

ファイルの編集だったりは Linux でやりたいけど, UI は Windows のほうが便利ですよね(あとは使える VScode plugin が Linux のみしかないときなど!)

このモードに上記 cross compile をやれば, Windows native アプリ開発 + compile-commands.json 使って補完もできます!

WSL2 vscode で clangd で C++ 補完するメモ(C/C++ for Visual Studio Code を disable が必要)
https://qiita.com/syoyo/items/153c232eb351e7493a9c

## TODO

* [x] WSL 環境で cmake bootstrap とビルドができるようにする
* [ ] MSVC cl.exe でビルドできる環境を整える
* [x] clang-cl で CMake `-DCMAKE_EXPORT_COMPILE_COMMANDS=1` で compile-commands.json を生成し, VSCode と clangd 連携して intellisense っぽいのを実現する.