Flutter SDKの仕組み

# はじめに

本記事は、Flutterアプリの開発環境である Flutter SDK の仕組みについてまとめた記事です。この記事を読むことで、Flutter SDK がどのような仕組みで動いているのかが何となく分かると思います。

# Flutter SDKとは？

まず、Flutter SDK とは、https://github.com/flutter/flutter そのものです。Flutter アプリを Android や iOS, Web, Linux 等のターゲット環境向けにビルドしたり、デバッグしたり、実行するための機能を提供する SDK (開発環境) です。

Flutter SDK の全機能は、flutter コマンド (`$flutter run`など) で提供されます。コマンドについては、[Flutterコマンド一覧まとめ](https://qiita.com/kurun_pan/items/f9251b1827ce9dca9e14)にまとめていますので、併せて参照してください。

# Flutter SDKのアーキテクチャ

Flutter SDK の主要な機能の全体像を以下に示します。大雑把に全体像を掴むため、細かな部分は記載していません。
![スクリーンショット 2020-08-29 11.47.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259963/bbb45f65-b145-d188-181c-f4ddfe2f5d97.png)

Flutter SDK を git clone した直後の素の状態では、Flutter SDK として必要なデータの多くが含まれていません。これは、Flutter SDK のスクリプト (コマンド) が必要な時にインフラ (サーバ) から必要なデータを都度ダウンロードする仕組みになっているためです。ダウンロードされたデータは`${install先}/flutter/bin/cache`以下に置かれます。

## インフラサーバ

前述の通り、Flutter SDK を経由して[インフラサーバ](https://storage.googleapis.com/flutter_infra/flutter/)から必要なデータを取得しています。

例えば、Flutter Engine も事前にビルドされた .soファイルで Flutter SDK のスクリプトの中で自動的に今のSDKのバージョンに対応したモノをダウンロードするようになっています。例えば、`master`チャンネルであれば、https://raw.githubusercontent.com/flutter/flutter/master/bin/internal/engine.version を参照することで今の最新のバージョン (SHA) を取得可能です。

そしてその SHA をキーにして、https://storage.googleapis.com/flutter_infra/flutter/${SHA}/linux-x64/linux-x64-embedder からデータをダウンロードします。

## Flutter SDKはDart VM上で動作

Flutter SDKが提供する各種コマンドは基本的に Dart で書かれており、Flutter SDK を初回実行時にその Dart ソースコードがビルドされ、Dart VM (dart-sdk) 上で動いています。なのでちょっと遅いというデメリットがあります。

dart-sdk は、Dartのソースコードをビルドしてその環境で実行するための開発環境です。

### dart-sdkは誰がいつインストールするのか？

Flutter SDKのスクリプトが前述のインフラサーバからダウンロードしてくれます。Flutter SDK を git clone 後、何らかのFlutterコマンドを初めて実行した時に、最初は単なるシェルスクリプトで動き始め、すぐに dart-sdk を`${install先}/flutter/bin/cache/dart-sdk`にダウンロードします。

ちなみに、dart-sdk は dart-sdk 公式の[wiki](https://github.com/dart-lang/sdk/wiki/Building)にビルド手順が公開されており、誰でも簡単に自分の PC でビルドが可能です。ダウンロードするデータは dart-sdk のビルド結果そのものです。

dart-sdkダウンロード後、Flutter SDK (flutter_tools) の dart コードをビルドします。その結果は`${install先}/flutter/bin/cache/flutter_tools.snapshot`に置かれます。そのため、仮に Flutter SDK (flutter_tools) をローカルで修正し、それを動きに反映させるためには、`${install先}/flutter/bin/cache/flutter_tools.snapshot`を一度物理的に削除する必要があります。

## Flutter packages

Flutter SDKのメインのツール類は flutter/packages 以下に存在します。

| パッケージ|用途|パス|
|----|-----|-----|
|flutter|アプリ開発者が普段利用するFlutterの各種ライブラリ (Framework) 本体です|flutter/packages/flutter|
|flutter_tools|Flutter SDKの各種コマンドを実現するスクリプト本体です|flutter/packages/flutter_tools|
|flutter_driver|インテグレーションテスト用のスクリプトです|flutter/packages/flutter_driver|
|flutter_test|ユニットテスト用のスクリプトです|flutter/packages/flutter_test|

## artifacts

Flutter Engine は C++ で書かれた Flutter 独自のレンダリングエンジンです。そしてこれは事前に各種ターゲットアーキテクチャ (CPU) 毎にクロスビルドされ、.soファイルとして用意されています。それが`${install先}/flutter/bin/cache/artifacts/engine`以下にダウンロードされます。

ターゲット OS, ターゲット CPU, 動作モードの組み合わせ分データが必要になります (動作モードに依存しない共通のデータはまとめられる)。

|ディレクトリ|ターゲットOS|ターゲットCPU|モード|補足|
|-----|-----|-----|-----|-----|
|common|All|All|All|SDKパッチ用のDartビルドデータ| 
|android-arm|Android|Arm 32bit|Debug|android-arm-*で利用する共通データ含む|
|android-arm-profile|↑|↑|Profile|-|
|android-arm-release|↑|↑|Release|-|
|android-arm64|同上|Arm 64bit|Debug|android-arm64-*で利用する共通データ含む|
|android-arm64-profile|↑|↑|Profile|-|
|android-arm64-release|↑|↑|Release|-|
|android-x64|Android (基本はHost エミュレータ用)|x64|Debug|
|android-x64-profile|↑|↑|Profile|-|
|android-x64-release|↑|↑|Release|-|
|android-x86|↑|x86|Debug|android-x86-*で利用する共通データ含む|
|android-x86-jit-release|↑|↑|Release|
|darwin-x64|macOS|x64|Debug|darwin-x64-*で利用する共通データ含む|
|darwin-x64-profile|↑|↑|Profile|-|
|darwin-x64-release|↑|↑|Release|-|
|ios|iOS|Arm 64bit|Debug|ios-*で利用する共通データ含む|
|ios-profile|↑|↑|Profile|-|
|ios-release|↑|↑|Release|-|
|linux-x64|Linux|x64|Debug|linux-x64-*で利用する共通データ含む|
|linux-x64-profile|↑|↑|Profile|-|
|linux-x64-release|↑|↑|Release|-|

## Flutterアプリのビルドの流れ

`flutter run`や`flutter build`コマンドを実行することで、ターゲット向けにビルドを行います。大まかな流れは以下です。

### 1. Dartコードのビルド
Flutter アプリの Dart コード部分つまり、開発者が作成したアプリ自体の Dart ソースコードと、Flutter Framework (`${install先}/flutter/packages/flutter`) で提供されるライブラリのソースコードをビルドします。Debug/Profile モードの場合は DarVM 向けの中間言語のバイトコードにビルドされ、Release モードの場合にはマシン語のネイティブコードにビルドされます。

### 2. 実行ファイルのビルド
アプリを動かしたいターゲットの各 OS 向けの実行バイナリを作成 (ビルド) します。中核機能は Flutter Engine としてダウンロードされた .so ファイルをリンクして利用しますが、そのライブラリをリンクして実行するための各 OSのプラットフォーム向けに実行バイナリを作成する必要があります。これらのソースコードは、各 Flutter アプリのプロジェクトディレクトリ直下の `android/`, `ios/`, `macos/`, `linux/`などにあります。

これらのソースコードをビルドする時に利用する Toolchain (ビルド環境) は、AndroidStudio や XCode, Clang など、Host PC 環境にインストールされているターゲットOS毎に対応したツールが選択されます。そのため、必要なツールがインストールされていない場合はここでエラーになります。

### 3. Bundleデータの生成

その他フォントデータの作成 (変換) やその他細々した必要ファイルの生成などを経て、生成した Dart のビルドデータや実行ファイル, アプリの Assets などを Bunble データとしてまとめます。この Bundle データの形式は各ターゲットプラットフォーム毎に異なります。

## Flutter SDK (flutter_tools) のソースコード

ソースコードはhttps://github.com/flutter/flutter/tree/master/packages/flutter_tools/lib/src にあります。Flutter SDK は Google OSS とは思えないほどのベタ書きで、いわゆるスクリプトというのが実態で、追えば簡単に読めます。