sbt 插件和使用插件本身的多项目构建中的项目之间的相互依赖关系

Posted

技术标签:

【中文标题】sbt 插件和使用插件本身的多项目构建中的项目之间的相互依赖关系【英文标题】:Intertwined dependencies between sbt plugin and projects within multi-project build that uses the plugin itself 【发布时间】:2016-09-22 07:30:24 【问题描述】:

我正在开发一个包含 sbt 插件的库。自然,我使用 sbt 来构建这个(多项目)库。我的(简化的)项目如下所示:

myProject/                        # Top level of library
  -> models                       # One project in the multi-project sbt build.
      -> src/main/scala/...       # Defines common models for both sbt-plugin and framework
  -> sbt-plugin                   # The sbt plugin build
      -> src/main/scala/...
  -> framework                    # The framework. Ideally, the sbt plugin is run as part of 
      -> src/main/scala/...       # compiling this directory.
  -> project/                     # Multi-project build configuration

有没有办法将 myProject/sbt-plugin 中定义的 sbt-plugin 挂接到 myProject/framework 的构建中?

注意:类似(但更简单)的问题:How to develop sbt plugin in multi-project build with projects that use it?

【问题讨论】:

【参考方案1】:

有没有办法将 myProject/sbt-plugin 中定义的 sbt-plugin 挂接到 myProject/framework 的构建中?

我在 Github eed3si9n/plugin-bootstrap 上有一个工作示例。它不是超级漂亮,但它有点工作。我们可以利用sbt is recursive这一事实。

project 目录是您的构建中的另一个构建,它知道如何构建您的构建。为了区分构建,我们有时使用术语 proper build 来指代您的构建,而 meta-build 来指代 project 中的构建。元构建中的项目可以做任何其他项目可以做的任何事情。 您的构建定义是一个 sbt 项目。

通过扩展,我们可以将 sbt 插件视为元构建根项目的库或项目间依赖项。

元构建定义 (project/plugins.sbt)

在此示例中,将元构建视为平行宇宙或阴影世界,具有并行多构建结构作为正确构建(rootmodelsbt-plugin)。

要在正确的构建中重用来自modelsbt-plugin 子项目的源代码,我们可以在元构建中重新创建多项目构建。这样我们就不需要进入循环依赖了。

addSbtPlugin("com.eed3si9n" % "sbt-doge" % "0.1.5")

lazy val metaroot = (project in file(".")).
  dependsOn(metaSbtSomething)

lazy val metaModel = (project in file("model")).
  settings(
    sbtPlugin := true,
    scalaVersion := "2.10.6",
    unmanagedSourceDirectories in Compile :=
      mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "model")
  )

lazy val metaSbtSomething = (project in file("sbt-plugin")).
  dependsOn(metaModel).
  settings(
    sbtPlugin := true,
    scalaVersion := "2.10.6",
    unmanagedSourceDirectories in Compile :=
      mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "sbt-plugin")
  )

def mirrorScalaSource(baseDirectory: File): Seq[File] = 
  val scalaSourceDir = baseDirectory / "src" / "main" / "scala"
  if (scalaSourceDir.exists) scalaSourceDir :: Nil
  else sys.error(s"Missing source directory: $scalaSourceDir")

当 sbt 加载时,它会先构建 metaModelmetaSbtSomething,然后使用 metaSbtSomething 作为插件来正确构建。

如果您有任何其他需要的插件,您可以将其添加到project/plugins.sbt,就像我添加了sbt-doge一样。

正确构建 (build.sbt)

正确的构建看起来像一个正常的多项目构建。 如您所见,framework 子项目使用SomethingPlugin。重要的是它们共享源代码,但target 目录是完全分开的,因此一旦加载了正确的构建,并且您正在更改代码,就不会受到干扰。

import Dependencies._

lazy val root = (project in file(".")).
  aggregate(model, framework, sbtSomething).
  settings(inThisBuild(List(
      scalaVersion := scala210,
      organization := "com.example"
    )),
    name := "Something Root"
  )

// Defines common models for both sbt-plugin and framework
lazy val model = (project in file("model")).
  settings(
    name := "Something Model",
    crossScalaVersions := Seq(scala211, scala210)
  )

// The framework. Ideally, the sbt plugin is run as part of building this.
lazy val framework = (project in file("framework")).
  enablePlugins(SomethingPlugin).
  dependsOn(model).
  settings(
    name := "Something Framework",
    crossScalaVersions := Seq(scala211, scala210),
    // using sbt-something
    somethingX := "a"
  )

lazy val sbtSomething = (project in file("sbt-plugin")).
  dependsOn(model).
  settings(
    sbtPlugin := true,
    name := "sbt-something",
    crossScalaVersions := Seq(scala210)
  )

演示

SomethingPlugin 示例中,我定义了使用foo.Model.xsomething 任务。

package foo

import sbt._

object SomethingPlugin extends AutoPlugin 
  def requries = sbt.plugins.JvmPlugin
  object autoImport 
    lazy val something = taskKey[Unit]("")
    lazy val somethingX = settingKey[String]("")
  
  import autoImport._
  override def projectSettings = Seq(
    something :=  println(s"something! $Model.x") 
  )

以下是我们如何从构建中调用something 任务:

Something Root> framework/something
something! 1
[success] Total time: 0 s, completed May 29, 2016 3:01:07 PM

1 来自foo.Model.x,因此这表明我们在framework 子项目中使用了sbt-something 插件,并且该插件正在使用metaModel

【讨论】:

以上是关于sbt 插件和使用插件本身的多项目构建中的项目之间的相互依赖关系的主要内容,如果未能解决你的问题,请参考以下文章

IntelliJ 14 - 创建/导入 Scala / SBT 项目

如何通过将 sbt-plugin 用作多项目构建中的依赖项来访问它的子项目?

SBT 多项目构建:针对不同子项目的 2 个不同版本的 sbt play 插件

如何在 sbt 项目中使用 sbt 插件作为库依赖项?

sbt-native-packager 插件在 Play 项目的 rpm 构建中出错

在SBT构建中的多项目构建中的自定义任务