build_runner再入門
Flutter開発の舞台裏!3社のエンジニアがおくるLTナイト

by Kota Hayashi

自己紹介
  • 株式会社ゆめみ
  • K9i a.k.a. たこさん
  • Kota Hayashi > Kota > Tako > たこアイコン > たこさん
  • 2023/5中途入社
  • そろそろ2年!?
  • Flutterリードエンジニア
  • 最近はほぼ案件稼働
  • かつては技術広報ぽい動きもしてたらしい
  • FlutterKaigiメンバー
  • 週末イベントします
前置き
悲報:Dart macrosの開発が中止😭

Dart

An update on Dart macros & data serialization

We have invested significant time and resources to prototype macros over the past couple years. Unfortunately, each time we solved a major…

macros: build_runnerを置き換えるのではないかと期待されていたメタプログラミングの技術
今後も付き合うことになるbuild_runnerに再入門しよう
メタプログラミング
  • コードを生成するためのコードを書くこと
  • 主なユースケース
  • Jsonシリアライズ、データクラス定義などボイラープレートが多いもの
  • ボイラープレート:殆ど、または全く変化することなく、複数の場所で繰り返される定型コードのセクションのこと。
  • Dartでは一般的にbuild_runnerを使う
  • dart:mirrorsによる実行時イントロスペクション(リフレクション)もあるが、Flutterでは使えない
  • Dart macrosはコンパイル時のイントロスペクションになる予定だった
  • build_runnerはコンパイルの前に事前にコードを生成しておくアプローチ
  • dart:mirrors、Dart macros、build_runnerは全てメタプログラミングだが実行タイミングなどアプローチが異なる
ボイラープレートがなくなるとは​
イミュータブルなデータクラスを記述するには素のDartではBeforeのような記述が必要だが、freezedを使うと簡単になる(スクショは古く、今はさらに簡潔
本編
build_runnerでのメタプログラミングを行う上で、知っておきたい知識を押さえよう
build_runnerでのコード生成における主な登場人物
  • 元となるDartコード
  • ユーザーはこれを記述する
  • 生成されたDartコード
  • 生成パッケージにより生成される
  • 生成パッケージ
  • freezed、freezed_annotationなど
  • build、source_gen、analyer、build_config等
  • 生成パッケージの開発に利用される
  • build_runner
  • buildパッケージで記述されたコードを実際に生成する
  • dart run build_runner buildなどのコマンドを提供
生成の流れ

各パッケージの記法で、元となるDartコードを記述する

以下はどのパッケージでも共通 アノテーションの追加(@freezedなど) アノテーションが定義されたライブラリのimport partディレクティブの記述(一般的には.g.dart、freezedは.freezed.dart) アノテーションの正体 アノテーションはクラスとして定義されている 生成パッケージはアノテーションがついているクラス等を生成元コードを見つける よく使う小文字で始まる@freezedは引数を何も指定しないFreezedクラスを定数として定義したもの アノテーションが定義されたライブラリのimport 生成パッケージはアノテーション等を定義したパッケージと生成ロジックが書かれたパッケージに分かれていることが多い freezedなら ロジック:freezed アノテーション:freezed_annotation json_serializableなら ロジック:json_serializable アノテーション:json_annotation ロジックのパッケージは生成時しか使わないのでdev_dependencies、アノテーションのパッケージはアプリケーションのコードにも含むのでdependenciesに記述する partディレクティブの記述 part 'person.g.dart';、part 'person.freezed.dart'; part と part of がセットで使われる 複数のファイルを同一ライブラリとして扱えるようになる。同じライブラリとすればprivateなクラスを参照したりできる freezedなら生成元コードでprivateになってる_$Personや_Personがfreezed.dartの方に生成される ちゃんと生成側はpart ofが使われていることがわかる

build_runnerのコマンド実行

build_runnerパッケージがdev_dependenciesに追加されていることでdart runコマンドが実行できる dart run build_runner build 単発ビルド dart run build_runner watch ファイルの変更を監視して、必要に応じて何度もビルド build.yaml freezedやjson_serializableはbuild_configパッケージが使われており、build.yamlで生成のオプションを設定できる よく使う設定 generate_for build.yamlがデフォルトで用意してるオプション 正規表現で生成元のDartコードを限定する ディレクトリ構成的にモデルはここにしか置かないとか分かってる場合はこれで生成速度が上がる source_gen:combining_builderのignore_for_file source_genパッケージはbuildやanalyerをまとめて使いやすくするパッケージ ignore_for_fileオプションも便利機能の一つ 生成ファイルにlintの無効化設定を追加してくれる ゆめみFlutterテンプレートプロジェクトの例https://github.com/yumemi-inc/flutter-mobile-project-template/blob/main/apps/app/build.yaml (type=lintがあるのにduplicate_ignoreをしてるのは、デフォルトでtype=lintを追加してくれるパッケージで重複が起きるため)

まとめ
  • macrosが開発中止😭
  • build_runnerのようなメタプログラミングでボイラープレートから解放される
  • 諸学者の間はおまじないのように書いてるアノテーションやpartの意味を解説
  • build.yamlの設定例を紹介
Made with