🐙

タコでもわかるDartマクロ作成入門

2024/05/19に公開
2

はじめに

Google I/O 2024でFlutter発表の目玉の一つとして、Dartのマクロ(macros)機能が発表されました。
これまでコード生成に頼っていたJsonのシリアライズ・デシリアライズ、データクラス作成がより効率的に行えるということで注目されています。

より詳しい情報はDart 3.4のMedium記事をどうぞ
https://medium.com/dartlang/dart-3-4-bd8d23b4462a

マクロを自分でも作ってみたい

マクロはDartのdev版、Flutterのmaster版ですでに利用可能です。
海外のつよつよエンジニアがいくつかマクロを公開しています。

Dart公式のJsonCodableマクロ
https://pub.dev/packages/json

Remiさんのマクロ版Freezed
https://pub.dev/packages/freezed/versions/3.0.0-0.0.dev

Felangelさんのデータクラスマクロ
https://pub.dev/packages/data_class_macro

これらマクロを試す分にはいいのですが、個人的にはもっと簡単なサンプルがみたいなーと思いました。(作ってる人たちがすごすぎて実装が高度🥹)

簡単なマクロを作ってみた

そういう訳で非常にシンプルなHello World!とプリントするだけのメソッドを生やすマクロを作ってみました。

https://github.com/K9i-0/k9i_macro_sample

()
class Sample {}

とするとこういうコードが作られて

augment class Sample {
  void hello() => print("Hello, World!");
}

mainメソッドで利用できます。

main.dart
()
class Sample {}

void main() {
  final sample = Sample();
  sample.hello();
}

実行時はこのようにすれば、Hello World!と表示されます。

$ dart --enable-experiment=macros run main.dart 
$ Hello, World!

作り方

それではこのマクロの作り方です。

dev版のDartを入れる

記事執筆時点ではマクロを使うのにdev版のDartが必要です。

今回は同僚のおかやまんさんが開発しているDVM(Dart Version Management)を使います。

https://pub.dev/packages/dvmx

brewかpubでインストール
(pubの場合dvmじゃなくてdvmxなのに注意)

brewの場合
brew install blendfactory/tap/dvm
pubの場合
dart pub global activate dvmx

dev版のインストール

dvm install 3.5.0-164.0.dev

上は執筆時の最新です。以下のコマンドでリリースされているものが確認できます。

dvm list --remote -c dev

プロジェクトのセットアップ

適当にDartプロジェクトを作ります。
僕はVSCodeのコマンドパレットでDart: New Project > Dart packageで作りました。

プロジェクトルートでDVMの有効化をします。

dvm use 3.5.0-164.0.dev

パスの設定

.vscode/settings.json
{
    "dart.sdkPath": ".dvm/dart_sdk",
}

マクロの依存追加

記事執筆時点の最新のmacrosを依存に追加します。

pubspec.yaml
dependencies:
  macros: ^0.1.0-main.5

実態はここにあります。

https://github.com/dart-lang/sdk/tree/main/pkg/macros

これがdart sdkに入っている_macros 0.1.5に依存しており、0.1.5はdev版移行にしかまだないので、dev版のDartが必要です。

macrosのpubspec.yaml
name: macros
version: 0.1.0-main.5
description: >-
  This package is for macro authors, and exposes the APIs necessary to write
  a macro. It exports the APIs from the private `_macros` SDK vendored package.
repository: https://github.com/dart-lang/sdk/tree/main/pkg/macros

environment:
  sdk: ^3.4.0-256.0.dev

dependencies:
  _macros:
    sdk: dart
    version: 0.1.5

マクロの有効化

analysis_optionsを書き換えます。

analysis_options.yaml
analyzer:
  # macro有効化
  enable-experiment:
    - macros

マクロを書く

作成したパッケージのlib/src下のファイルを編集します。
パッケージ名_base.dartというファイルがあるはずです。

Hello Worldを表示したいだけならこのようにします。

lib/パッケージ名_base.dart
import 'dart:async';

import 'package:macros/macros.dart';

macro class Hello implements ClassDeclarationsMacro {
  const Hello();

  
  FutureOr<void> buildDeclarationsForClass(ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
    final methods = await builder.methodsOf(clazz);
    final hello =
        methods.where((e) => e.identifier.name == 'hello').firstOrNull;

    if (hello != null) return;

    builder.declareInType(
      DeclarationCode.fromParts(
        [
          '  void hello() => print("Hello, World!");',
        ]
      ),
    );
  }
}

ClassDeclarationsMacroインターフェースが定義するbuildDeclarationsForClassに生成処理を書きます。
builderのdeclareInTypeに生成したいhelloメソッドのコードを渡します。

使ってみる

パッケージ作成時に作られたexample/パッケージ名_example.dartで試してみましょう。
以下はk9i_macro_sampleというパッケージ名だった場合の例です。

example/パッケージ名_example.dart
import 'package:k9i_macro_sample/k9i_macro_sample.dart';

()
class Sample {}

void main() {
  final sample = Sample();
  sample.hello();
}

実行時は「簡単なマクロを作ってみた」で説明したように--enable-experiment=macrosが必要です。

dvmを使ったコマンドラインからの実行なら

dvm dart --enable-experiment=macros run example/k9i_macro_sample_example.dart

といった感じ

オーグメントクラスを見てみる

マクロで作られたコードはVS Codeの「Go to Augmentation」というCodeLensから確認できます。

こんな感じのコードが生成されているのが見えると思います。

augment library 'file:///略/k9i_macro_sample/example/k9i_macro_sample_example.dart';

augment class Sample {
  void hello() => print("Hello, World!");
}

このオーグメントクラス(augment class)がマクロによって生成されたコードです。
オーグメントクラスはマクロ本体を書き換えた時もマクロ利用コードを書き換えたときもリアルタイムで変更されるのが非常に快適に思えました。

まとめ

非常にシンプルなマクロの作り方紹介でした。
以下のリポジトリでコードを公開しているのでスターもらえると嬉しいです🥳

https://github.com/K9i-0/k9i_macro_sample

GitHubで編集を提案
株式会社ゆめみ

Discussion

K9i - Kota HayashiK9i - Kota Hayashi

exportされたファイルのマクロを使うとGo to Augmentationが表示されないバグはDartをアプデすれば発生しなくなったので記事を更新しました
村松さん情報ありがとうございます🙇‍♂️