Swiftコンパイラ開発環境構築

Swift がオープンソースになってしばらく経ちます。
コンパイラや標準ライブラリの開発に手を出してみたいけど、リポジトリを落とした後どうしていいかわからない！という方のために、まずは開発環境の構築方法をご紹介します。基本的には[README](https://github.com/apple/swift#getting-started) に書いてある内容ですが、あまり実践的ではないので、僕はこうしています的な。

# 必要な環境

メモリ容量: ちゃんと調べてないですが、リリースビルドで 2GB ほど消費するそうです。
ディスク容量: 10GB程度あれば大丈夫かと思います。
リポジトリも大きく、ビルドも含めると結構ディスク消費します。

### macOS

Xcode: 9.0 beta最新版
OS: macOS 10.12 最新版以降

macOS で開発する場合は、 Xcode のコマンドラインツールは Xcode 9.0 の最新版の物にしておく必要があります。そうしないと、Apple の最新の SDK に対応した Swiftオーバーレイがビルド出来ないためです。Swiftコンパイラのビルドを行う際には `xcode-select` などで最新版の Xcode が選択されていることを確認してください。

### Linux

Clang: 3.5 以降
CMake: 3.4.3 以降

CMake 3.4.3 以降が必要なので、公式パッケージで 3.5 がインストールできる Ubuntu 16.04 以降がオススメです。Ubuntu 以外のディストリビューションは試していないです。

READMEの通り依存パッケージをインストールしておいてください。

```shell-session:依存パッケージインストール
$ sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config libblocksruntime-dev libcurl4-openssl-dev autoconf libtool systemtap-sdt-dev tzdata
```

# リポジトリの clone

まずは README に書かれている通り [`apple/swift`](https://github.com/apple/swift) からリポジトリを clone していきます。適当なディレクトリ(`~/Document`など)に移動して、作業ディレクトリを作るところから始まります。

```shell-session:作業ディレクトリ作成
$ cd ~/Document
$ mkdir swift-source
$ cd swift-source
```

Swift.org のプロジェクトは、一つのリポジトリで完結しているわけではなくて、 [`apple/swift-llvm`](https://github.com/apple/swift-llvm) など複数のリポジトリに依存しています。最初に `apple/swift` を clone して、その中にあるスクリプトを使用してその他のリポジトリを clone します。

```shell-session:リポジトリのclone
$ git clone https://github.com/apple/swift.git
Cloning into 'swift'...

...ひたすら待つ

$ ./swift/utils/update-checkout --clone
Skipping clone of 'swift', directory already exists
Running ``obtain_additional_swift_sources`` with up to 8 processes.

...ひたすら待つ

```

ファイル数もコミット数も非常に多いリポジトリなので、時間帯によってはかなり時間がかかりますが、最初だけなので我慢。完了したらこんな感じのディレクトリ構成になっているはずです。

```
swift-source
 ├── clang
 ├── cmark
 ├── compiler-rt
 ├── llbuild
 ├── lldb
 ├── llvm
 ├── ninja
 ├── swift
 ├── swift-corelibs-foundation
 ├── swift-corelibs-libdispatch
 ├── swift-corelibs-xctest
 ├── swift-integration-test
 ├── swift-xcode-playground-support
 └── swiftpm
```

ちなみにファイル容量は、

```shell-session
$ du -s -h .
1.6G    .
```

# CMake と Ninja

Swiftコンパイラの標準のビルドシステムは [CMake](https://cmake.org/) および [Ninja](https://ninja-build.org/) を利用します。CMakeがコンフィギュレーションをして、Ninjaが実際のビルドをします。

Linux の方は上でインストールしているのでOKとして、macOSの場合。CMake は公式からバイナリパッケージ落としてきてもいいのですが、僕は homebrew でインストールしています。

```shell-session:CMakeインストール(macOSのみ)
$ brew install cmake
```

あとNinjaですが、 README にはリポジトリ clone するとか homebrew でインストールするとか書いてありますが、実は `utils/update-checkout` コマンドが既に clone してくれていますので、必要ありません。既に homebrew などでインストールされている場合は自動的にそれが使用されますし、未インストールの場合には、clone されたリポジトリから自動的にビルドされ、それが使用されます。

# とりあえずビルドしてみる

ビルド環境が整ったので、早速ビルドしてみましょう。`utils/build-script` というスクリプトでビルドします。

```shell-session:初回ビルド
$ cd swift
$ utils/build-script -Rt
... 30分程度から数時間
```

ここで使用しているオプションは `-R` が Release ビルド、これをつけないとデバッグ版のビルドになってしまい、コンパイルが非常に遅くなるので必ずつけます。 `-t` はテストも実行という意味で、省略して `-Rt` です。他にもいろいろあるのですが、僕は普段はほとんどこれだけで作業します（考えるのがめんどくさいので。）

ビルドはソースツリーの外で行われるようなっていて、成果物はすべて `swift-source` ディレクトリ内の `build/Ninja-ReleaseAssert` にあります。このディレクトリ名は `build-script` に与えたオプションによって変化します。明示的に指定することも出来ますが、とりあえず必要ないでしょう。

ちなみにこのビルドに必要なディスク容量は 3GB 程度です。

ビルドとテストが完了したら、早速ビルドされた Swift コンパイラを実行してみましょう。

```shell-session:ビルド済みswift実行
$ # swift ディレクトリ内で
$ ../build/Ninja-ReleaseAssert/swift-macosx-x86_64/bin/swift -version
Swift version 4.0-dev (LLVM 2b37177ffc, Clang cc5d6deb6e, Swift 2fe2328436)
Target: x86_64-apple-macosx10.9

$ echo 'print("Hello Swift!")' | ../build/Ninja-ReleaseAssert/swift-macosx-x86_64/bin/swift
Hello Swift!
```

とりあえず正常にビルド出来ていることが確認できました。
なお、この状態では、 swift-lldb や swiftpm など、その他 swift のビルドに依存するものがビルドされていないので、通常の REPL などは使えません。あくまでコンパイラ本体と macOS用の stdlib がビルドできたという状態です。また、iOS向けの stdlib などもコンパイルされていませんので、このビルドはXcodeのツールチェーンとしてインストールすることは出来ません。とはいえ、それら全てをビルド・テストするには非常に時間がかかるので、通常はこの状態でコンパイラの開発を行っていきます。

# 最新版へのアップデート

一度環境構築した後に、最新版を追っていく方法です。`utils/update-checkout` コマンドをオプション無しで使用します。

```shell-session:ソースアップデート
$ cd ~/Document/swift-source/swift
$ utils/update-checkout
```

`utils/update-checkout` には他にも Swift4 のリリースブランチ群をチェックアウトするなど、いろいろな機能があるのですが、僕は使ったことありません。興味ある方は `--help` オプションでヘルプを見てみてください。

ソースを最新にしたら、ビルドも更新します。

```shell-session:最新版ビルド
$ utils/build-script -Rt
```

もうすでに一度ビルドしているので、差分ビルドになります。これがデフォルトです。ただ、 `swift-llvm` などの `swift` が依存しているリポジトリに更新が入ると、ほぼ全てビルドし直しなのでそれなりに時間かかります。しょうがないですね。

また、クリーンビルドしたい時は、`--clean` オプションを同時に指定します。

```shell-session:クリーンビルド
$ utils/build-script -Rt --clean
```

とはいえ、`--clean` がやっていることはビルドディレクトリを削除してからビルドしているだけなので、 `rm -rf ../build/Ninja-ReleaseAssert` で事前に削除しておくだけで十分ではあります。ごくまれに、差分ビルドで対応できない変更が入ってビルド出来なくなることがあるので、何かおかしくなったらクリーンビルドしてみるのが良いかもしれません。

あと一応、 `update-checkout` も `build-script` も、どのディレクトリから実行しても問題無い作りになっています。つまり、カレントディレクトリに依存しませんので、 `swift-source` から `swift/utils/update-checkout` でも上記のように呼び出しても一緒です。


# Xcode プロジェクトの作成

コーディングは全部 vim でやるぜ、みたいな人には不要ですが、 Xcode プロジェクトがあると何かと便利です。ただし Xcode の IDE でビルドするのは時間がかかることもあり、やったことありません。Xcodeでコーディングして、コマンドラインの `build-script -Rt` でビルド・テストするのがオススメ構成。

Xcodeプロジェクトは、 `build-script` を通して CMake に作成してもらう形になります。

```shell-session:Xcodeプロジェクト作成
$ utils/build-script -x --skip-build
```

オプションの `-x` は CMake を Xcode ジェネレータで実行するオプションです。 Xcode では実際にはビルドしないので `--skip-build` オプションをつけて実行します(とはいっても llvm の一部だけビルドされます)。完了したら `../build/Xcode-DebugAssert` ディレクトリ内に Xcode プロジェクトが生成されているはずです。

```shell-session:Xcodeプロジェクトオープン
$ open ../build/Xcode-DebugAssert/swift-macosx-x86_64/Swift.xcodeproj
```

この xcodeproj には Scheme が設定されていないので、開くとこのようなダイアログが表示されます。

<img width="1072" alt="Screen Shot 2017-06-30 at 1.45.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/55874/9ae82ded-ca52-5560-17ff-aebb97f6397a.png">

Xcodeはエディタとして使用したいだけなので何でもいいのですが、オート実行すると膨大な数の Scheme が生成されてめんどくさいので、 「Manually Manage Schemes」 を選び、左下の 「＋」ボタンを押してターゲットをデフォルトのまま ALL_BUILD で 「OK」を押下し、スキームの作成を完了させます。

<img width="1072" alt="Screen Shot 2017-06-30 at 1.45.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/55874/82cd4944-54cf-d552-6d52-a285aebd6105.png">

で、生成された xcodeproj ですが、このグループツリー構造がよくわかりません。ターゲット毎か何かになっていて、ファイルシステムの構成と違うので、ファイル探すのに苦労します。取り急ぎコンパイラのヘッダ (`include/swift/`) とソース (`lib/`) は xcodeproj では `Sources/Swift libraries` グループ下にあります。

<img width="1075" alt="Screen Shot 2017-06-30 at 2.00.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/55874/b318502e-8662-c408-003c-284ccb1a1016.png">

インデックスに結構な時間がかかります。気長に待ちましょう。

# ツールチェーンビルド

`build-script -R` だと、前述したとおり Xcode にインストールできるパッケージにはなりません。 Xcode にインストールするには、ツールチェーンというものをビルドしなければならないのですが、一番簡単な方法は、 `utils/build-toolchain` スクリプトを使用する事です。

注意: 非常に時間がかかるので覚悟してください。ディスク容量も 40GB くらい消費するので特別な理由が無い限りオススメしません。最新版追うだけなら [Swift.org のダウンロードページ](https://swift.org/download/#snapshots)からスナップショット落としてきた方が全然早いです。自前でビルドするのは、ローカルでの修正を Xcode での動きを含めてチェックをしたい場合に限られると思います。僕自身は必要性を感じたことはありません。

```shell-session:ツールチェーンビルド
$ cd ~/Document/swift-source/swift
$ utils/build-toolchain local.swift

数時間...
```

`local.swift` はパッケージの Bundle Identifier （のprefix）になります。単なる名前なので、他と被らなければ何でもいいです。

これが完了すると、 `swift` ディレクトリに下記のファイルが出来ているはずです。（ファイル名に日付が入っているのでその日によって当然違います。）

```shell-session
$ ls -l
...
-rw-r--r-- ... swift-LOCAL-2017-07-05-a-osx-symbols.tar.gz
-rw-r--r-- ... swift-LOCAL-2017-07-05-a-osx.tar.gz
drwxr-xr-x ... swift-nightly-install
drwxr-xr-x ... swift-nightly-symroot
...
```

これをインストールして、Xcodeから使ってみましょう。アーカイブの内容は `/` からの相対パスで作られているので、ルートから `sudo` でインストールします。

```shell-session:
$ cd /
$ sudo tar zxvf ~/Document/swift-source/swift/swift-LOCAL-2017-07-05-a-osx.tar.gz
```

`/Library/Developer/Toolchains` に `swift-LOCAL-{yyyy}-{MM}-{dd}-a.toolchain` いうディレクトリが作成されているはずです。 Xcode の Preferences > Components > Toolchains から見えるようになっているはずなので、それを選択して試すことができます。

<img width="912" alt="Screen Shot 2017-07-05 at 18.30.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/55874/013ccd45-4cfd-4b6d-9e68-77e253f8ef19.png">

不要になったらこの画面で該当行をマウスオーバーすると出てくる :gear: アイコンをクリックして、メニューから、 "Move To Trash" を選択すればアンインストールできます。

注意： `build-toolchain` は `swift` のワーキングディレクトリ に成果物を作ってしまうため、あまり行儀がよくありません。終わったら `git status` で状況見て、削除しておきましょう。

# フォークの作成とリモート登録

今のうちに自分のフォークも作っちゃいましょう。
普段通り github でフォーク作り、それをローカルのリポジトリに remote として追加します。

```shell-session:remoteリポジトリ追加
$ cd ~/Document/swift-source/swift
$ git remote add private git@github.com:{フォーク名}/swift.git
```

remote 名は何でもいいですが、 `private` にしておきました。
僕は直接 push する可能性のあるリポジトリは ssh で、それ以外は https で clone するポリシーで運用しているので、こっちは ssh です。間違ってアップストリームに直接 push する心配が無くて良いです。

また、フォークしたものを clone する、つまり origin を 自分のフォークにするのもやったことないです。 `utils/update-checkout` がそれを想定しているかどうかもわからないので、 origin は `apple/swift` にしておいた方が安全と思います。

# swiftリポジトリのディレクトリ構成

[apple/swift](https://github.com/apple/swift) のリポジトリがどういうディレクトリ構成になっているのかざっくりと。

| ディレクトリ | |
|:--|---|
| `docs/` | ドキュメントです。Swift は公式の設計ドキュメントのようなものが少なくて、ドキュメントというよりはメモ書きレベルの物もいっぱい。別記事でまとめたい。 |
| `cmake/` | CMake の設定ファイルである `CMakeLists.txt` から読み込まれる CMake 関連のモジュール群 |
| `include/swift/` | コンパイラのヘッダファイル群 |
| `lib/` | コンパイラのソース。実際にはこの中のディレクトリごとに `.a` としてコンパイルされ、 `tools/` ディレクトリの各実行ファイルとリンクされます。 |
| `tools/driver/` | `swift` コマンド本体 |
| `tools/SourceKit/` | `SourceKit` のソース |
| `tools/その他` | その他テストツールなどの実行ファイルとしてコンパイルされるツール群 |
| `test/` | テスト。テストについては別記事で解説します。 |
| `validation-test/` | バリデーションテスト。普段は実行する必要ないけど、たまには実行したほうが良いテスト群。CIでは必ず実行されます。 |
| `unittest/` | `lib/` の各ライブラリの API レベルでのユニットテスト群。所詮パブリックなAPIではないので、あまり更新されません。 |
| `stdlib/public/runtime/` | Swiftのランタイムライブラリ |
| `stdlib/public/core/` | 標準ライブラリ |
| `stdlib/public/Platform/` | `Darwin` もしくは `Glibc` ライブラリ |
| `stdlib/public/SDK/` | `Foundation` や `UIKit` など Apple の各SDKのSwiftオーバーレイ |
| `stdlib/private/` | 標準ライブラリをテストするためのライブラリなど |
| `utils/` | ビルドスクリプトやその他諸々、Swiftの開発者が使用している雑多なユーティリティが納められています |

---


いかがだったでしょうか？

Swift は新規コントリビュータ向けの文書や案内が充実しているとは言いがたいです。また、今現在既にコントリビュートしている人やアップルの中の人はそれなりに分かってしまっているので、何がひっかかるポイントなのかもわかってない、もしくは文書化する暇もない状態なのだと思います。


手を出してみたいけどココが分からない。というのがあったら是非コメントで教えてください！