Windows 用 Swift コンパイラをビルドする

最近 Windows でも Swift コンパイラ, libdispatch, foundation がビルドできるようなり、簡単なプログラムを動かせるようになってきたのでその方法を紹介します。

# 環境構築

- OS: Windows 10 1809
- PC: 速いやつ
    - ビルドには結構時間がかかります。下のスペックのマシンで 1 時間以上かかります。
        - Core i7 8700K (6コア, 12 スレッド, 全コア 4.3GHz にオーバークロック), メモリ 48GB, SATA SSD
- ストレージの空き: 40GB

## 開発者モードを有効にする

「Windows の設定」から「更新とセキュリティ」の「開発者向け」を開き、「開発者モード」を有効にします。
<img width="1204" alt="devmode.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/110491/eda2134e-aa3f-24cd-bb10-a5cacfdcbb18.png">

## Visual Studio をインストールする

https://visualstudio.microsoft.com/vs/community/ から Visual Studio 2017 Community Edition をダウンロードしてインストールします。インストール時のオプションは「C++ によるデスクトップ開発」と「ユニバーサル Windows プラットフォーム開発」を有効にしてください。
<img width="1290" alt="vsinstall.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/110491/3fbf25b7-87e2-e16e-3eda-3ffa0f7753a2.png">

## Git for Windows をインストールする

https://gitforwindows.org/ から Windows 用の Git をダウンロードしてインストールします。オプションは全てデフォルトのままにしてください。

## Python をインストールする

https://www.python.org/downloads/windows/ からバージョン 2.7 系の「Windows x86-64 MSI installer」をダウンロードしてインストールします。インストール時のオプションは「Add python.exe to Path」を有効にしてください。
<img width="495" alt="python.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/110491/407232c7-f489-d15a-b4e6-f71b48ad6205.png">
インストールしたら一度再起動します。

# ソースコードのチェックアウト

Swift や llvm、そのほかのライブラリのソースコードをチェックアウトします。ここでは全て C:\swift に置くことにします。コマンドプロンプトを開いて次のように入力してください

```
C:
cd \
mkdir swift
cd swift
git clone https://github.com/apple/swift-cmark cmark
git clone https://github.com/apple/swift-clang clang
git clone https://github.com/apple/swift-llvm llvm
git clone https://github.com/apple/swift-compiler-rt compiler-rt
git clone https://github.com/apple/swift
git clone https://github.com/apple/swift-corelibs-libdispatch
git clone https://github.com/apple/swift-corelibs-foundation
git clone https://github.com/apple/swift-corelibs-xctest
git clone https://github.com/apple/swift-lldb lldb
git clone https://github.com/curl/curl.git curl
git clone https://gitlab.gnome.org/GNOME/libxml2.git libxml2
git clone https://compnerd@dev.azure.com/compnerd/windows-swift/_git/windows-swift
powershell -Command "Invoke-WebRequest -Uri http://download.icu-project.org/files/icu4c/63.1/icu4c-63_1-Win64-MSVC2017.zip -Outfile %TEMP%\icu.zip; Expand-Archive -Path %TEMP%\icu.zip"
```

チェックアウトしたタイミングによってはビルドできないことも多いので、初めは次のようにして私が試したコミットと合わせておくのが無難でしょう。

```
git -C clang checkout b1f4baeb56
git -C cmark checkout 32fa496
git -C compiler-rt checkout 166077175
git -C curl checkout 4331a3b8f
git -C libxml2 checkout 8161b463
git -C lldb checkout e4b0ffcff6
git -C llvm checkout ab94816b593
git -C swift checkout 8fcd4e6ee3
git -C swift-corelibs-foundation checkout a733b6b5
git -C swift-corelibs-libdispatch checkout 9d485ca
git -C swift-corelibs-xctest checkout 10140f4
git -C windows-swift checkout c45ba86
```

# ビルド

## シンボリックリンクの作成

ビルド中に必要になるシンボリックリンクを作成します。この操作には管理者権限が必要になります。コマンドプロンプトを右クリックして管理者権限で開いてください。
<img width="649" alt="super.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/110491/21bd2212-e3ca-0e5c-cb7a-1542e710b387.png">

ここで次のコマンドを実行します。

```
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" C:\swift\swift\stdlib\public\Platform\ucrt.modulemap
mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" C:\swift\swift\stdlib\public\Platform\winsdk.modulemap
mklink "%VCToolsInstallDir%\include\module.modulemap" C:\swift\swift\stdlib\public\Platform\visualc.modulemap
mklink "%VCToolsInstallDir%\include\visualc.apinotes" C:\swift\swift\stdlib\public\Platform\visualc.apinotes
```

これ以降管理者権限のコマンドプロンプトは使用しないので閉じて構いません。


## ビルド環境の設定

通常のコマンドプロンプトに戻りビルド中に必要になる環境変数を設定します。ビルドしたものインストール先は DIST により C:\swift\dist にしていますが、必要に応じて変更してください。

```
set SWIFT=C:\swift
set BUILD=%SWIFT%\build
set ICU=%SWIFT%\icu
set DIST=%SWIFT%\dist
set WINCMAKE=%SWIFT%\windows-swift\cmake\caches\Windows-x86_64.cmake
set CNERDCMAKE=%SWIFT%\windows-swift\cmake\caches\org.compnerd.dt.cmake
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=amd64
set PATH=%ICU%\bin64;%BUILD%\llvm\bin;%BUILD%\foundation;%BUILD%\xctest;%PATH%
```

## LLVM

```
mkdir "%BUILD%\llvm"
pushd "%BUILD%\llvm"
cmake -G Ninja^
  -DCMAKE_BUILD_TYPE=RelWithDebInfo^
  -DCMAKE_C_COMPILER=cl^
  -DCMAKE_CXX_COMPILER=cl^
  -DCMAKE_INSTALL_PREFIX="%DIST%"^
  -DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-unknown-windows-msvc^
  -DLLVM_ENABLE_ASSERTIONS=YES^
  -DLLVM_ENABLE_PDB=YES^
  -C "%WINCMAKE%"^
  -C "%CNERDCMAKE%"^
  %SWIFT%\llvm
popd
ninja -C "%BUILD%\llvm"
```

## cmark

```
mkdir "%BUILD%\cmark"
pushd "%BUILD%\cmark"
cmake -G Ninja^
  -DCMAKE_BUILD_TYPE=RelWithDebInfo^
  -DCMAKE_C_COMPILER=cl^
  -DCMAKE_CXX_COMPILER=cl^
  -DCMARK_TESTS=NO^
  -C "%WINCMAKE%"^
  "%SWIFT%\cmark"
popd
ninja -C "%BUILD%\cmark"
```

## swift

このプロセスは終盤、非常にメモリを使用します。メモリ不足でビルドが失敗する場合は ninja に -j 2 等と指定して同時実行数を減らしましょう。時間がかかるようになりますが、失敗はしなくなります。

```
mkdir "%BUILD%\swift"
pushd "%BUILD%\swift"
cmake -G Ninja^
  -DCMAKE_BUILD_TYPE=RelWithDebInfo^
  -DCMAKE_C_COMPILER=clang-cl^
  -DCMAKE_CXX_COMPILER=clang-cl^
  -DCMAKE_INSTALL_PREFIX="%DIST%"^
  -DClang_DIR="%BUILD%\llvm\lib\cmake\clang"^
  -DSWIFT_PATH_TO_CMARK_BUILD="%BUILD%\cmark"^
  -DSWIFT_PATH_TO_CMARK_SOURCE="%SWIFT%\cmark"^
  -DSWIFT_PATH_TO_LIBDISPATCH_SOURCE="%SWIFT%\swift-corelibs-libdispatch"^
  -DLLVM_DIR="%BUILD%\llvm\lib\cmake\llvm"^
  -DSWIFT_INCLUDE_DOCS=NO^
  -DSWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE="%ICU%/include"^
  -DSWIFT_WINDOWS_x86_64_ICU_UC="%ICU%/lib64/icuuc.lib"^
  -DSWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE="%ICU%/include"^
  -DSWIFT_WINDOWS_x86_64_ICU_I18N="%ICU%/lib64/icuin.lib"^
  -DSWIFT_BUILD_STATIC_STDLIB=NO^
  -DSWIFT_BUILD_STATIC_SDK_OVERLAY=NO^
  -DLLVM_INSTALL_TOOLCHAIN_ONLY=YES^
  -DSWIFT_BUILD_SOURCEKIT=YES^
  -DSWIFT_ENABLE_SOURCEKIT_TESTS=NO^
  -DSWIFT_INSTALL_COMPONENTS="autolink-driver;compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;editor-integration;tools;sourcekit-inproc;swift-remote-mirror;swift-remote-mirror-headers"^
  -C "%WINCMAKE%"^
  "%SWIFT%\swift"
popd
ninja -C "%BUILD%\swift"
```

## lldb

```
mkdir "%BUILD%\lldb"
pushd "%BUILD%\lldb"
cmake -G Ninja^
  -DCMAKE_BUILD_TYPE=Release^
  -DCMAKE_CXX_COMPILER=clang-cl^
  -DCMAKE_C_COMPILER=clang-cl^
  -DCMAKE_INSTALL_PREFIX="%DIST%"^
  -DLLDB_PATH_TO_CLANG_BUILD="%BUILD%\llvm"^
  -DLLDB_PATH_TO_LLVM_BUILD="%BUILD%\llvm"^
  -DLLDB_PATH_TO_SWIFT_BUILD="%BUILD%\swift"^
  -DLLDB_PATH_TO_SWIFT_SOURCE="%SWIFT%\swift"^
  -DLLVM_ENABLE_ASSERTIONS=YES^
  -C "%WINCMAKE%"^
  "%SWIFT%\lldb"
popd
ninja -C "%BUILD%\lldb"
```

## libdispatch

```
mkdir "%BUILD%\libdispatch"
pushd "%BUILD%\libdispatch"
cmake -G Ninja^
  -DCMAKE_BUILD_TYPE=Debug^
  -DCMAKE_C_COMPILER=clang-cl^
  -DCMAKE_CXX_COMPILER=clang-cl^
  -DCMAKE_SWIFT_COMPILER="%BUILD%\swift\bin\swiftc.exe"^
  -DCMAKE_INSTALL_PREFIX="%DIST%"^
  -DBUILD_SHARED_LIBS=YES^
  -DENABLE_TESTING=NO^
  -DCMAKE_C_COMPILER_TARGET=x86_64-unknown-windows-msvc^
  -DENABLE_SWIFT=YES^
  -C "%WINCMAKE%"^
  "%SWIFT%\swift-corelibs-libdispatch"
popd
ninja -C "%BUILD%\libdispatch"
```

## curl

```
pushd "%SWIFT%\curl"
call buildconf.bat
pushd winbuild
nmake /f Makefile.vc mode=static VC=15 MACHINE=x64
popd
popd
```

## libxml2

```
pushd "%SWIFT%\libxml2\win32"
cscript configure.js iconv=no
nmake /f Makefile.msvc
popd
```

## core-foundation

途中、止まっているように見えますが辛抱強く待ちましょう。

```
mkdir "%BUILD%\foundation"
pushd "%BUILD%\foundation"
cmake -G Ninja^
  -DCMAKE_BUILD_TYPE=Debug^
  -DCMAKE_C_COMPILER=clang-cl^
  -DCMAKE_SWIFT_COMPILER="%BUILD%\swift\bin\swiftc.exe"^
  -DCURL_LIBRARY="%SWIFT%/curl/builds/libcurl-vc15-x64-release-static-ipv6-sspi-winssl/lib/libcurl_a.lib"^
  -DCURL_INCLUDE_DIR="%SWIFT%/curl/builds/libcurl-vc15-x64-release-static-ipv6-sspi-winssl/include"^
  -DENABLE_TESTING=NO^
  -DICU_ROOT="%ICU%"^
  -DLIBXML2_LIBRARY="%SWIFT%/libxml2/win32/bin.msvc/libxml2_a.lib"^
  -DLIBXML2_INCLUDE_DIR="%SWIFT%/libxml2/include"^
  -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE="%SWIFT%\swift-corelibs-libdispatch"^
  -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD="%BUILD%\libdispatch"^
  -DCMAKE_INSTALL_PREFIX="%DIST%"^
  -C "%WINCMAKE%"^
  "%SWIFT%\swift-corelibs-foundation"
popd
ninja -C "%BUILD%\foundation"
```

## xctest

```
mkdir "%BUILD%\xctest"
pushd "%BUILD%\xctest"
cmake -G Ninja^
  -DBUILD_SHARED_LIBS=YES^
  -DCMAKE_BUILD_TYPE=Debug^
  -DCMAKE_SWIFT_COMPILER="%BUILD%\swift\bin\swiftc.exe"^
  -DXCTEST_PATH_TO_COREFOUNDATION_BUILD="%BUILD%\foundation\CoreFoundation-prefix"^
  -DXCTEST_PATH_TO_FOUNDATION_BUILD="%BUILD%\foundation"^
  -DXCTEST_PATH_TO_LIBDISPATCH_SOURCE="%SWIFT%\swift-corelibs-libdispatch"^
  -DXCTEST_PATH_TO_LIBDISPATCH_BUILD="%BUILD%\libdispatch"^
  -DLIT_COMMAND="%SWIFT%\llvm\utils\lit\lit.py"^
  -DPYTHON_EXECUTABLE="C:\Python27\python.exe"^
  -DCMAKE_INSTALL_PREFIX="%DIST%"^
  -C "%WINCMAKE%"^
  "%SWIFT%\swift-corelibs-xctest"
popd
ninja -C "%BUILD%\xctest"
```

# インストール

途中、Foundation がエラーを吐きますが、無視して構いません。

```
ninja -C "%BUILD%\llvm" install
ninja -C "%BUILD%\swift" install
ninja -C "%BUILD%\lldb" install
ninja -C "%BUILD%\libdispatch" install
ninja -C "%BUILD%\foundation" install
ninja -C "%BUILD%\xctest" install
```

# Swift プログラムのコンパイル

では Swift プログラムをコンパイルしてみましょう。まずコンパイラとコンパイルしたプログラムの実行に必要なディレクトリにパスを通します。

```
set PATH=%DIST%\bin;%DIST%\lib\swift\windows;%PATH%
```

こんなコードを用意しました。

```swift
// hello.swift
import Foundation

let f = DateFormatter()
f.dateStyle = .full
let now = Date()

print("Hello Swift!")
print("It's \(f.string(from: now))!")
```

このコードをコンパイルするには次のオプションをつけてコンパイラを起動します。

```
swiftc -Xcc -DDEPLOYMENT_TARGET_WINDOWS^
 -Xlinker /NODEFAULTLIB:libcmt^
 -Xlinker /LIBPATH:C:\swift\build\foundation^
 -Xlinker /LIBPATH:C:\swift\build\libdispatch^
 -Xlinker /LIBPATH:C:\swift\build\libdispatch\src^
 -Xlinker user32.lib^
 -o hello.exe^
 hello.swift
```

そうすればコンパイルできて次のように実行できるはずです！

<img width="659" alt="run.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/110491/a0833268-71e5-bfea-0af8-df4542f36a2d.png">

# 参考

- https://github.com/apple/swift/blob/master/docs/WindowsBuild.md
- https://dev.azure.com/compnerd/_git/windows-swift?path=%2FREADME.md&version=GBmaster