Swift Package ManagerのBuild Tools
はじめに
Swift Pakcage Manager (以下 SwiftPM) Build Tools(Build Tool Plugin)についてハマりどころがあったので、使用方法について実例を交えて紹介します
なお、コードサンプルも用意したので適宜参考にしていただければ幸いです
また、本機能についてのRelease Noteはこちら
本記事では説明しないこと
- Swift Package Managerの使用方法
- 実例に挙げるライブラリの使用方法
環境
- OS: macOS Monterey 12.5.1
- Xcode: 14.0 (14A309)
- Swift: 5.7
例: SwiftGenを実行する
Resourceファイルを安全にアクセスするためのコードジェネレータ。タイプセーフに扱うことができるので類似のライブラリを使用、もしくは自作している人も多いんじゃないでしょうか
SwiftGen
- version: 6.6.2
- artifactbundle: github
Package.swiftの実装
Package.swiftの実装は以下のようになります
.binaryTarget(
name: "swiftgen", // ①
url: "https://github.com/SwiftGen/SwiftGen/releases/download/6.6.2/swiftgen-6.6.2.artifactbundle.zip", // ②
checksum: "7586363e24edcf18c2da3ef90f379e9559c1453f48ef5e8fbc0b818fbbc3a045" // ③
),
.plugin(
name: "SwiftGenPlugin", // ④
capability: .buildTool(),
dependencies: ["swiftgen"] // ⑤
),
このPackage.swiftの実装について詳しく解説します
- swiftgen-6.6.2.artifactbundle.zipを解凍したあとの
swiftgen
.artifactbundle
- artifactbundleのリンク
- artifactbundle.zipをダウンロード後、SwiftPakcage.swiftがあるディレクトリ配下で以下のコマンド実行結果
$ swift package compute-checksum swiftgen-6.6.2.artifactbundle.zip
7586363e24edcf18c2da3ef90f379e9559c1453f48ef5e8fbc0b818fbbc3a045
- 後述するPluginのディレクトリ名
- 1.で記載した名前
おすすめはしませんが、checksum
を空文字にして実行することで得られるエラーでchecksumの中身を確認することができます
BuildToolPluginの実装
SwiftGenで実行するコマンドを実装します。ディレクトリ名は上述したPackage.swiftのディレクトリ名を使用して下図のように追加します
import PackagePlugin
@main
struct Plugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
[
.prebuildCommand(
displayName: "Running SwiftGen",
executable: try context.tool(named: "swiftgen").path,
arguments: [
"config",
"run",
"--config", "\(context.package.directory.string)/../swiftgen.yml"
],
environment: [
"OUTPUT_DIR": context.pluginWorkDirectory.string // ①
],
outputFilesDirectory: context.pluginWorkDirectory
)
]
}
}
ここでは設定のほとんどをswiftgen.yml
に記載しています
①のようにenvironmentの中で変数を定義することで、今回であればswiftgen.ymlの中で使用することができます
swiftgen.ymlの実装
SwiftGenの設定ファイルです。細かい設定については公式を参照してください
input_dir: ./Package/Sources/App/Resources
output_dir: ${OUTPUT_DIR}
strings:
inputs: ja.lproj
filter: .+\.strings$
outputs:
templateName: structured-swift5
output: L10n-Constants.swift
上記ではBuildToolPluginで指定したOUTPUT_DIR
を使用して、コードの生成先を指定しています
以上でビルド時にSwiftGenが実行されるようになったと思います
ところで……
ライブラリを追加/更新の際に毎回artifactbundleをダウンロードして、checksumを計算してと少し手間に感じませんか?
なので、次章ではSwiftLintを例にurlを用いずにPluginを実行する方法について紹介します
例: SwiftLintを実行する
Swift用の静的解析ツール。PRのレビュー等で指摘することも減りコードの品質を保つことができるので静的解析ツールを導入する機会も多いかと思います
SwiftLint
- version: 0.49.1
- artifactbundle: github
Package.swiftの実装の前に
今回はurl/checksumを使用しない方法として、ローカルに配置されたartifactbundleを使用する方法について説明します
そのためローカルにあらかじめ上記のartifactbundle.zipをダウンロードの上解凍済みのものとします
コードサンプルではArtifacts
配下に配置しています
Package.swiftの実装
Package.swiftの実装は以下のようになります
.binaryTarget(
name: "SwiftLintBinary",
path: "./../Artifacts/SwiftLintBinary.artifactbundle"
),
.plugin(
name: "SwiftLintPlugin",
capability: .buildTool(),
dependencies: ["SwiftLintBinary"]
),
基本的にはSwiftGenの時に実装した内容と同じ内容です。異なる点としてbinaryTarget
で指定しているのがurlではなくartifactbundleまでのpathになっている点ですね
BuildToolPluginの実装
SwiftLintで実行するコマンドを実装します。ディレクトリ名は上述したPackage.swiftのディレクトリ名を使用してSwiftLintPluginという名前で作成しています
import PackagePlugin
@main
struct Plugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
[
.buildCommand(
displayName: "Running SwiftLint for \(target.name)",
executable: try context.tool(named: "swiftlint").path,
arguments: [
"--config", "\(context.package.directory.string)/../.swiftlint.yml",
"--no-cache",
"--quiet",
"--format"
],
environment: [:]
)
]
}
}
こちらもほぼSwiftGenの実装時と変わらないので説明は省略します
.swiftlint.ymlの実装
SwiftLintの設定ファイルです。細かい設定については公式を参照してください
以上でビルド時にSwiftLintが実行されるようになったと思います
おわりに
本記事では、SwiftGen, SwiftLintを例にSwiftPM Build Toolsの実装についてURL、Pathを使用して実装する方法について紹介しました
実装に少し癖のあるプラグイン機能ですが、慣れてしまえばとても便利な機能かと思います
個人的にはレビューが容易になったりと嬉しいことも多いのでぜひ使っていきたい機能ですね!
参考リンク
- https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md
- https://github.com/SwiftGen/SwiftGen
- https://github.com/realm/SwiftLint
- https://github.com/SwiftGen/SwiftGen/blob/ac34a1531d092d4637d4b79389b9c939101fbe59/Documentation/Articles/SwiftGen-Build-Tool-Package-Plugins.md
- https://qiita.com/usamik26/items/1c2cec0903fea2e03344
- https://speakerdeck.com/shimastripe/spm-swiftgen-plugin
Discussion