LoginSignup
9
4

More than 1 year has passed since last update.

Swift Package ManagerでBuildToolPluginを利用する

Last updated at Posted at 2022-03-15

Swift Package Manager で BuildToolPlugin を利用する

  • Siwft 5.6 から Package Manager Extensible Build Tools が利用できるようになりました
  • これまで SwiftPM では、ビルド時に任意のスクリプトを実行する事ができませんでした
  • なので、例えば SwiftPM のモジュールでビルド時に SwiftGen などでコード生成するという事ができませんでした
  • しかし、Swift 5.6 から Plugin を利用する事で、ビルド時にコード生成などできるようになりました

Swift Package Manager の Plugin を作る

  • Makefile でコードを生成して、そのコードを利用する場合の Plugin を試してみました

やりたいこと

  • ビルド時に、下の Makefile の hoge を実行するようにします
  • DST にファイルを作成して、Hoge 構造体を書き込みます
  • この Hoge 構造体をモジュール内で利用します
hoge:
	echo ${DST}
	touch ${DST}
	echo "public struct Hoge {\n    public let num: Int = 12\n} " > ${DST}

Package.swift の定義

  • plugin のターゲットを作成し、Hoge 構造体を利用したいターゲットの plugins に、plugin のターゲットを追加します
let package = Package(
    name: "MyLibrary",
    products: [
        .library(name: "MyLibrary",
                 targets: ["MyLibrary"]),
    ],
    targets: [
        .plugin(name: "MyPlugin",
                capability: .buildTool()),
        .target(name: "MyLibrary",
                plugins: [.plugin(name: "MyPlugin")]),
    ]
)

Plugins/MyPlugin/MyPlugin.swift

  • Plugins/MyPlugin/MyPlugin.swift を作成し、BuildToolPlugin を継承した MyPlugin を作成します

image.png

  • context.pluginWorkDirectory に Hoge.swift を作成します
  • context.pluginWorkDirectory 以外に書き込む事はできませんでした
  • outputFilesDirectory で指定したディレクトリ以下がビルドの対象にされるようでした
import PackagePlugin

@main struct MyPlugin: BuildToolPlugin {
    func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
        return [
            .prebuildCommand(displayName: "Run MyPlugin",
                             executable: Path("/usr/bin/make"),
                             arguments: [
                                "hoge",
                                "-C", "\(context.package.directory)",
                                "DST=\(context.pluginWorkDirectory.string)/Hoge.swift",
                             ],
                             outputFilesDirectory: context.pluginWorkDirectory),
        ]
    }
}

作成したコードを利用する

  • DerivedData ↓ を見ると Hoge.swift が作成され、Hoge 構造体が書き込まれていることがわかります
    /xxx/Xcode/DerivedData/xxx-xxxxxxxxxx/SourcePackages/mylibrary/MyLibrary/Hoge.swift
  • Xcode の Navigator Area には表示されないのがむず痒いですが、Hoge 構造体が利用できるようになってます
public struct MyLibrary {
    public private(set) var text = "num: \(Hoge().num)"
    public init() {
    }
}
  • MyLibrary を表示するアプリを作ります
struct ContentView: View {
    var body: some View {
        Text(MyLibrary().text)
            .font(.system(size: 32))
            .padding()
    }
}

動作の様子

  • Makefile で書き込む Hoge 構造体の中身を変更して、再度アプリをビルドすると、変更した内容に表示が変わる事が確認できました
  • アプリを際ビルドすると、MyLibrary の plugin が実行され、そこで生成されたコードを使って、MyLibrary がちゃんとビルドし直されているようです

まとめ

  • Swift 5.6 で追加された Plugin を使って make を実行してみました
  • Xcode 13.3 では、生成したコードの Hoge の補完は効くのですが、jump to definition できなかったり、エラーがよくわからなかったり、使いにくさはありました
  • しかし、SwiftPM でモジュールを作成しているアプリでは、SwiftGen など自動生成系をビルド時に実行できるようになるメリットは大きそうだと感じました
9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4