1. プラグイン

プラグイン 

既存のプラグインの使用方法に焦点を当てた入門ページがあるので、最初にそちらを読むとよいかもしれません。

プラグインは、ビルド定義で外部コードを使用する方法です。プラグインは、タスクを実装するために使用されるライブラリにすることができます (markdown 処理タスクを作成するために Knockoff を使用できます)。プラグインは、すべてのプロジェクトに自動的に追加されるか、選択したプロジェクトに明示的に宣言される sbt 設定のシーケンスを定義できます。たとえば、プラグインは proguard タスクと関連する (オーバーライド可能な) 設定を追加するかもしれません。最後に、プラグインは新しいコマンドを定義できます (commands 設定を介して)。

sbt 0.13.5 では、プラグイン間の依存関係管理の改善と明示的にスコープされた自動インポートを備えた自動プラグインが導入されました。今後、自動プラグインに移行することをお勧めします。プラグインのベストプラクティスページでは、現在進化している sbt プラグインの作成ガイドラインについて説明しています。一般的なベストプラクティスも参照してください。

自動プラグインの使用 

一般的な状況は、リポジトリに公開されているバイナリプラグインを使用する場合です。必要なすべての sbt プラグイン、一般的な依存関係、および必要なリポジトリを含む project/plugins.sbt を作成できます。

addSbtPlugin("org.example" % "plugin" % "1.0")
addSbtPlugin("org.example" % "another-plugin" % "2.0")

// plain library (not an sbt plugin) for use in the build definition
libraryDependencies += "org.example" % "utilities" % "1.3"

resolvers += "Example Plugin Repository" at "https://example.org/repo/"

多くの自動プラグインはプロジェクトに設定を自動的に追加しますが、明示的な有効化が必要なものもあります。次に例を示します。

lazy val util = (project in file("util"))
  .enablePlugins(FooPlugin, BarPlugin)
  .disablePlugins(plugins.IvyPlugin)
  .settings(
    name := "hello-util"
  )

プラグインの使用の詳細については、入門ガイドのプラグインの使用を参照してください。

説明別 

プラグイン定義は、project/ フォルダーにあるプロジェクトです。このプロジェクトのクラスパスは、project/ 内のビルド定義とプロジェクトのベースディレクトリにある .sbt ファイルに使用されるクラスパスです。また、eval および set コマンドにも使用されます。

具体的には、

  1. project/ プロジェクトで宣言された管理された依存関係は取得され、通常のプロジェクトと同様にビルド定義クラスパスで使用できます。
  2. project/lib/ 内の管理されていない依存関係は、通常のプロジェクトと同様にビルド定義で使用できます。
  3. project/ プロジェクトのソースはビルド定義ファイルであり、管理対象および管理対象外の依存関係から構築されたクラスパスを使用してコンパイルされます。
  4. プロジェクトの依存関係は、project/plugins.sbt (通常のプロジェクトの build.sbt ファイルと同様) で宣言でき、ビルド定義で使用できます。

ビルド定義クラスパスは、sbt/sbt.autoplugins 記述子ファイルで検索され、sbt.AutoPlugin 実装の名前が含まれています.

reload plugins コマンドは、現在のビルドを (ルート) プロジェクトの project/ ビルド定義に変更します。これにより、ビルド定義プロジェクトを通常のプロジェクトのように操作できます。 reload return は元のビルドに戻ります。プラグイン定義プロジェクトの保存されていないセッション設定はすべて破棄されます。

自動プラグインは、プロジェクトに自動的に挿入する設定を定義するモジュールです。さらに、自動プラグインは次の機能を提供します。

  • .sbt ファイルと eval および set コマンドに選択的な名前を自動的にインポートします。
  • 他の自動プラグインへのプラグインの依存関係を指定します。
  • すべての依存関係が存在する場合、自動的にアクティブになります。
  • 必要に応じて、projectSettingsbuildSettings、および globalSettings を指定します。

プラグインの依存関係 

従来のプラグインが既存のプラグインからいくつかの機能を再利用したい場合、ライブラリの依存関係としてプラグインを取り込み、次のいずれかを実行します。

  1. 依存関係からの設定シーケンスを独自の設定シーケンスの一部として追加するか、
  2. ビルドユーザーに正しい順序で含めるように指示します。

これは、アプリケーション内のプラグインの数が増えるにつれて複雑になり、エラーが発生しやすくなります。自動プラグインの主な目標は、この設定の依存関係の問題を軽減することです。自動プラグインは他の自動プラグインに依存でき、これらの依存関係設定が最初にロードされるようにします。

SbtLessPluginSbtCoffeeScriptPlugin があり、それが SbtJsTaskPluginSbtWebPlugin、および JvmPlugin に依存しているとします。これらのプラグインすべてを手動でアクティブ化する代わりに、プロジェクトは次のように SbtLessPluginSbtCoffeeScriptPlugin をアクティブ化できます。

lazy val root = (project in file("."))
  .enablePlugins(SbtLessPlugin, SbtCoffeeScriptPlugin)

これにより、プラグインから正しい順序で正しい設定シーケンスが取り込まれます。ここでの重要な概念は、必要なプラグインを宣言することで、sbt がギャップを埋めることができるということです。

ただし、プラグインの実装で自動プラグインを生成する必要はありません。これはプラグインコンシューマーにとって便利ですが、自動的な性質のため、常に適切とは限りません。

グローバルプラグイン 

$HOME/.sbt/1.0/plugins/ ディレクトリは、グローバルプラグイン定義プロジェクトとして扱われます。これは通常の sbt プロジェクトであり、そのクラスパスは、プロジェクトごとのプラグインについて上記で説明したように、そのユーザーのすべての sbt プロジェクト定義で使用できます。

自動プラグインの作成 

最小限の sbt プラグインは、sbt が実行される Scala のバージョン (現在、2.12.18) に対してビルドされた Scala ライブラリ、または Java ライブラリです。このタイプのライブラリのために特別なことは何もする必要はありません。より一般的なプラグインは、sbt タスク、コマンド、または設定を提供します。この種のプラグインは、これらの設定を自動的に提供するか、ユーザーが明示的に統合できるようにします.

自動プラグインを作成するには、プロジェクトを作成し、SbtPlugin を有効にします。

ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / organization := "com.example"
ThisBuild / homepage := Some(url("https://github.com/sbt/sbt-hello"))

lazy val root = (project in file("."))
  .enablePlugins(SbtPlugin)
  .settings(
    name := "sbt-hello",
    pluginCrossBuild / sbtVersion := {
      scalaBinaryVersion.value match {
        case "2.12" => "1.2.8" // set minimum sbt version
      }
    }
  )

注意すべき点がいくつかあります

  • sbt プラグインは、sbt 自体がコンパイルされている Scala 2.12.x でコンパイルする必要があります。 scalaVersion を指定しない場合、sbt はプラグインに適した Scala バージョンをデフォルトで使用します.
  • デフォルトでは、sbt プラグインは使用している sbt バージョンでコンパイルされます。 sbt は前方互換性を維持しないため、通常はすべてのプラグインユーザーも最新バージョンにアップグレードする必要があります。 pluginCrossBuild / sbtVersion は、プラグインを*古い*バージョンの sbt に対してコンパイルするためのオプションの設定であり、プラグインユーザーは sbt バージョンの範囲から選択できます。

次に、プラグインコードを作成し、プロジェクトをリポジトリに公開します。プラグインは、前のセクションで説明したように使用できます。

まず、適切な名前空間で、sbt.AutoPlugin を拡張して自動プラグインオブジェクトを定義します。

projectSettings と buildSettings 

自動プラグインを使用すると、提供されるすべての設定 (例: assemblySettings) は、projectSettings メソッドを介してプラグインによって直接提供されます。 sbt プロジェクトに hello という名前のタスクを追加するプラグインの例を次に示します。

package sbthello

import sbt._
import Keys._

object HelloPlugin extends AutoPlugin {
  override def trigger = allRequirements

  object autoImport {
    val helloGreeting = settingKey[String]("greeting")
    val hello = taskKey[Unit]("say hello")
  }

  import autoImport._
  override lazy val globalSettings: Seq[Setting[_]] = Seq(
    helloGreeting := "hi",
  )

  override lazy val projectSettings: Seq[Setting[_]] = Seq(
    hello := {
      val s = streams.value
      val g = helloGreeting.value
      s.log.info(g)
    }
  )
}

プラグインがビルドレベル (つまり、ThisBuild 内) で設定を追加する必要がある場合、buildSettings メソッドがあります。ここで返される設定は、この AutoPlugin をアクティブにするビルドのプロジェクト数に関係なく、特定のビルドスコープに一度だけ追加されることが保証されています。

override def buildSettings: Seq[Setting[_]] = Nil

globalSettings は、グローバル設定 (in Global) に一度だけ追加されます。これらにより、プラグインは新しい機能または新しいデフォルトを自動的に提供できます。この機能の主な用途の 1 つは、IDE プラグインなどのコマンドをグローバルに追加することです。

override def globalSettings: Seq[Setting[_]] = Nil

設定のデフォルト値を定義するには、globalSettings を使用します。

プラグインの依存関係の実装 

次のステップは、プラグインの依存関係を定義することです。

package sbtless

import sbt._
import Keys._
object SbtLessPlugin extends AutoPlugin {
  override def requires = SbtJsTaskPlugin
  override lazy val projectSettings = ...
}

requires メソッドは、依存関係リストを構築するための DSL である Plugins 型の値を返します。 requires メソッドには、通常、次の値のいずれかが含まれます。

  • empty (プラグインなし)
  • 他の自動プラグイン
  • && 演算子 (複数の依存関係を定義するため)

ルートプラグインとトリガーされたプラグイン 

一部のプラグインは、常にプロジェクトで明示的に有効にする必要があります。 これらをルートプラグイン、つまりプラグイン依存関係グラフの「ルート」ノードであるプラグインと呼びます。 自動プラグインは、デフォルトではルートプラグインです。

自動プラグインは、依存関係が満たされている場合にプラグインがプロジェクトに自動的にアタッチされる方法も提供します。これらをトリガープラグインと呼び、`trigger` メソッドをオーバーライドすることで作成されます。

たとえば、ビルドにコマンドを自動的に追加できるトリガープラグインを作成したい場合があります。これを行うには、`requires` メソッドが `empty` を返すように設定し、`trigger` メソッドを `allRequirements` でオーバーライドします。

package sbthello

import sbt._
import Keys._

object HelloPlugin2 extends AutoPlugin {
  override def trigger = allRequirements
  override lazy val buildSettings = Seq(commands += helloCommand)
  lazy val helloCommand =
    Command.command("hello") { (state: State) =>
      println("Hi!")
      state
    }
}

ビルドユーザーはこのプラグインを `project/plugins.sbt` に含める必要がありますが、`build.sbt` に含める必要はもうありません。これは、要件を持つプラグインを指定する場合に、より興味深いものになります。別のプラグインに依存するように `SbtLessPlugin` を変更してみましょう。

package sbtless
import sbt._
import Keys._
object SbtLessPlugin extends AutoPlugin {
  override def trigger = allRequirements
  override def requires = SbtJsTaskPlugin
  override lazy val projectSettings = ...
}

実際には、`PlayScala` プラグイン(ご存知ない方のために説明すると、Play フレームワークは sbt プラグインです)は、必要なプラグインの1つとして `SbtJsTaskPlugin` をリストしています。そのため、`build.sbt` を次のように定義すると

lazy val root = (project in file("."))
  .enablePlugins(PlayScala)

`SbtLessPlugin` の設定シーケンスは、`PlayScala` の設定の後のどこかに自動的に追加されます。

これにより、プラグインは既存のプラグインをより多くの機能でサイレントかつ正しく拡張できます。また、ユーザーからの順序付けの負担を軽減し、プラグインの作成者がユーザーに機能を提供する際の自由度とパワーを高めることができます。

autoImport でインポートを制御する 

自動プラグインが `autoImport` という名前の `val` または `object` などの安定したフィールドを提供する場合、フィールドの内容は `set`、`eval`、および `.sbt` ファイルでワイルドカードインポートされます。次の例では、hello コマンドを `greeting` の値を簡単に取得するタスクに置き換えます。実際には、コマンドよりも設定またはタスクを優先することをお勧めします

package sbthello

import sbt._
import Keys._

object HelloPlugin3 extends AutoPlugin {
  object autoImport {
    val greeting = settingKey[String]("greeting")
    val hello = taskKey[Unit]("say hello")
  }
  import autoImport._
  override def trigger = allRequirements
  override lazy val buildSettings = Seq(
    greeting := "Hi!",
    hello := helloTask.value)
  lazy val helloTask =
    Def.task {
      println(greeting.value)
    }
}

通常、`autoImport` は、インポートや修飾を必要とせずに、新しいキー(`SettingKey`、`TaskKey`、または `InputKey`)またはコアメソッドを提供するために使用されます。

プラグインの例 

典型的なプラグインの例

build.sbt:

ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / organization := "com.example"
ThisBuild / homepage := Some(url("https://github.com/sbt/sbt-obfuscate"))

lazy val root = (project in file("."))
  .enablePlugins(SbtPlugin)
  .settings(
    name := "sbt-obfuscate",
    pluginCrossBuild / sbtVersion := {
      scalaBinaryVersion.value match {
        case "2.12" => "1.2.8" // set minimum sbt version
      }
    }
  )

ObfuscatePlugin.scala:

package sbtobfuscate

import sbt._
import sbt.Keys._

object ObfuscatePlugin extends AutoPlugin {
  // by defining autoImport, the settings are automatically imported into user's `*.sbt`
  object autoImport {
    // configuration points, like the built-in `version`, `libraryDependencies`, or `compile`
    val obfuscate = taskKey[Seq[File]]("Obfuscates files.")
    val obfuscateLiterals = settingKey[Boolean]("Obfuscate literals.")

    // default values for the tasks and settings
    lazy val baseObfuscateSettings: Seq[Def.Setting[_]] = Seq(
      obfuscate := {
        Obfuscate(sources.value, (obfuscate / obfuscateLiterals).value)
      },
      obfuscate / obfuscateLiterals := false
    )
  }

  import autoImport._
  override def requires = sbt.plugins.JvmPlugin

  // This plugin is automatically enabled for projects which are JvmPlugin.
  override def trigger = allRequirements

  // a group of settings that are automatically added to projects.
  override val projectSettings =
    inConfig(Compile)(baseObfuscateSettings) ++
    inConfig(Test)(baseObfuscateSettings)
}

object Obfuscate {
  def apply(sources: Seq[File], obfuscateLiterals: Boolean): Seq[File] = {
    // TODO obfuscate stuff!
    sources
  }
}

使用例 

プラグインを使用するビルド定義は次のようになります。 `obfuscate.sbt`

obfuscate / obfuscateLiterals := true

グローバルプラグインの例 

最も単純なグローバルプラグイン定義は、`$HOME/.sbt/1.0/plugins/build.sbt` でライブラリまたはプラグインを宣言することです。

libraryDependencies += "org.example" %% "example-plugin" % "0.1"

このプラグインは、現在のユーザーのすべての sbt プロジェクトで使用できます。

さらに

  • Jar は `$HOME/.sbt/1.0/plugins/lib/` に直接配置でき、現在のユーザーのすべてのビルド定義で使用できます。
  • ソースからビルドされたプラグインへの依存関係は、.scala ビルド定義 で説明されているように、`$HOME/.sbt/1.0/plugins/project/Build.scala` で宣言できます。
  • プラグインは、`$HOME/.sbt/1.0/plugins/MyPlugin.scala` など、`$HOME/.sbt/1.0/plugins/` の Scala ソースファイルで直接定義できます。 `$HOME/.sbt/1.0/plugins//build.sbt` には `sbtPlugin := true` が含まれている必要があります。これは、最初にプラグインを開発するときのターンアラウンドを短縮するために使用できます。
  1. グローバルプラグインコードを編集する
  2. 変更されたプラグインを使用するプロジェクトを `reload` する
  3. sbt はプラグインを再構築し、プロジェクトに使用します。

    さらに、プラグインは、再コンパイルすることなく、マシン上の他のプロジェクトで使用できます。このアプローチでは、`publishLocal` のオーバーヘッドと、プラグインを使用するプロジェクトの plugins ディレクトリの `clean` をスキップします。

これらはすべて、`$HOME/.sbt/1.0/plugins/` が標準プロジェクトであり、そのクラスパスがすべての sbt プロジェクトのビルド定義に追加されることの結果です。

ビルド定義でライブラリを使用する例 

例として、Grizzled Scala ライブラリをプラグインとして追加します。これは sbt 固有の機能を提供するものではありませんが、プラグインを宣言する方法を示しています。

1a) 手動管理 

  1. https://oss.sonatype.org/content/repositories/releases/org/clapper/grizzled-scala_2.8.1/1.0.4/grizzled-scala_2.8.1-1.0.4.jar から jar を手動でダウンロードします
  2. `project/lib/` に配置します

1b) 自動管理:直接編集アプローチ 

`project/plugins.sbt` を編集して、以下を含めます

libraryDependencies += "org.clapper" %% "grizzled-scala" % "1.0.4"

sbt が実行されている場合は、`reload` を実行します。

1c) 自動管理:コマンドラインアプローチ 

`reload plugins` を使用して、`project/` の plugins プロジェクトに変更できます。

$ sbt
> reload plugins
[info] Set current project to default (in build file:/Users/sbt/demo2/project/)
>

次に、通常どおり依存関係を追加し、`project/plugins.sbt` に保存できます。依存関係が正しいことを確認するために `update` を実行すると便利ですが、必須ではありません。

> set libraryDependencies += "org.clapper" %% "grizzled-scala" % "1.0.4"
...
> update
...
> session save
...

メインプロジェクトに戻るには、`reload return` を使用します

> reload return
[info] Set current project to root (in build file:/Users/sbt/demo2/)

1d) プロジェクトの依存関係 

このバリアントは、sbt の外部プロジェクトサポートを使用して、プラグインのソース依存関係を宣言する方法を示しています。これは、プラグインがソースからビルドされ、クラスパスで使用されることを意味します。

`project/plugins.sbt` を編集する

lazy val root = (project in file(".")).dependsOn(assemblyPlugin)

lazy val assemblyPlugin = RootProject(uri("git://github.com/sbt/sbt-assembly"))

sbt が実行されている場合は、`reload` を実行します。

このアプローチは、プラグインを開発するときに役立つことに注意してください。プラグインを使用するプロジェクトは、`reload` でプラグインを再構築します。これにより、`publishLocal` と `update` の中間ステップが節約されます。また、リポジトリからプラグインの開発バージョンを操作するためにも使用できます。

ただし、コミットまたはタグをフラグメントとしてリポジトリに追加することで、明示的に指定することをお勧めします。

lazy val assemblyPlugin = uri("git://github.com/sbt/sbt-assembly#0.9.1")

この方法を使用する際の注意点の1つは、ローカルの sbt がリモートプラグインのビルドを実行しようとすることです。多くのプラグインが複数の sbt バージョンに対してクロス公開されているため、プラグイン独自のビルドで異なる sbt バージョンが使用されている可能性があります。そのため、可能な場合はバイナリ成果物を使用することをお勧めします。

2) ライブラリの使用 

Grizzled Scala は、ビルド定義で使用できるようになりました。これには、`eval` および `set` コマンドと、`.sbt` および `project/*.scala` ファイルが含まれます。

> eval grizzled.sys.os

`build.sbt` ファイル内

import grizzled.sys._
import OperatingSystem._

libraryDependencies ++=
    if(os == Windows)
        Seq("org.example" % "windows-only" % "1.0")
    else
        Seq.empty

プラグインの公開 

プラグインは、他のプロジェクトと同様に公開できます。プラグインを Maven レイアウトのリポジトリに公開する場合は、sbt 1.9.x 以降を使用してください。

ただし、Maven レイアウトに従うリポジトリにプラグインを公開しようとすると、1つの注意点があります。

アーティファクトリポジトリがアーティファクトが Maven レイアウトに準拠していることを期待し、準拠していないアーティファクトを拒否する場合、次のことができます。1.(推奨)あなたとプラグインのコンシューマーが sbt 1.9.x 以降を使用している場合

sbt 1.9 以降では、新しい Maven スタイルと従来の Maven スタイルの両方でプラグインを公開しようとします(下位互換性のため)。従来の Maven スタイルは、Maven レイアウトと完全には互換性がありません。`sbtPluginPublishLegacyMavenStyle := false` で無効にする必要があります。このプラグインは、従来の Maven スタイルのみを解決できるため、1.9 より古い sbt では使用できません(または、sbt-vspp で説明されているトリックを使用する必要があります)。3. sbt < 1.9.x を使用している場合

https://github.com/esbeetee/sbt-vspp/ を使用できます。5. sbt 1.9.x を使用できず、sbt-vspp を使用できない/したくない場合

アーティファクトリ設定に `POM 整合性チェックの抑制` のようなオプションがあり、Maven レイアウトに完全に準拠していなくてもアーティファクトを送信できるはずです。

これについての詳細は、次の issue を参照してください。

ベストプラクティス 

プラグインの作成者の場合は、プラグインのベストプラクティス ページを参照してください。プラグインの一貫性を確保し、他のプラグインとうまく連携するためのガイドラインが含まれています。

sbt プラグインのクロスビルドについては、プラグインのクロスビルド も参照してください。