プロジェクトに SPM 製の Command Line Tool を追加して Build Phase で実行する

2 min read読了の目安(約2100字

Swift Package Manager で Command Line Tool を作る

コマンドライン引数で渡されたファイルの行数を出力するコマンドを例とする

  1. GitHub でリポジトリを新規に作って、ローカルにcloneする
  2. cloneしたリポジトリのルートディレクトリで SPM の初期化をする
$ swift package init --type executable
  1. Package.swiftを編集する
Package.swiftの例
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "LineCounter",
    platforms: [
        .macOS(.v10_15)
    ],
    products: [
        .executable(name: "lc", // コマンド名はここで決められる
                    targets: ["LineCounter"])
    ],
    targets: [
        .target(
            name: "LineCounter",
            dependencies: []),
        .testTarget(
            name: "LineCounterTests",
            dependencies: ["LineCounter"]),
    ]
)
  1. Command Line Tool を実装する
main.swift
import Foundation

guard CommandLine.arguments.count == 2 else {
    print("usage: lc [absolute path of a file]")
    exit(1)
}

let fileUrl = URL(fileURLWithPath: CommandLine.arguments[1])
guard let file = try? String(contentsOf: fileUrl, encoding: .utf8) else {
    print("Error: could not read \(fileUrl.absoluteString)")
    exit(2)
}

print(file.components(separatedBy: CharacterSet.newlines).count)
  1. ローカルで動作確認をする
$ swift run lc ${PWD}/Package.swift
23
  1. リモートリポジトリに push して tag を打つ
tagを打つ
$ git tag 0.0.1
$ git push origin 0.0.1
  • tag で Command Line Tool のバージョン管理をすることになる
  • GitHub の Release で Change Log をしっかり書くと良い

プロジェクトに作った Command Line Tool を追加する

  1. 任意のプロジェクトでFile -> Swift Packages -> Add Package Dependency...を開く
  2. 作った Command Line Tool のリポジトリ URL で検索して Swift Package を追加する

Build Phase で Command Line Tool を実行する

TARGETS -> Build PhasesRun Scriptを追加する

# コマンドラインツールのあるディレクトリに移動
cd ${BUILD_DIR%Build/*}SourcePackages/checkouts/LineCounter

# コマンドを実行
xcrun --sdk macosx swift run lc ${SRCROOT}/[目的のファイルのパス]
  • swift runだと実行できないため、xcrun --sdk maxosx swift runを利用する
  • 相対パスよりも絶対パスを使い、Xcode の環境変数を上手く使う

これで、TARGET をビルドするたびにコマンドが実行される