简体   繁体   English

sbt插件与使用插件本身的多项目构建中的项目之间交织在一起的依赖关系

[英]Intertwined dependencies between sbt plugin and projects within multi-project build that uses the plugin itself

I'm developing a library that includes an sbt plugin. 我正在开发一个包含sbt插件的库。 Naturally, I'm using sbt to build this (multi-project) library. 当然,我正在使用sbt来构建这个(多项目)库。 My (simplified) project looks as follows: 我的(简化)项目如下:

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

Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build? 有没有办法让myProject / sbt-plugin中定义的sbt-plugin在统一版本中挂钩到myProject / framework的构建中?

Note: similar (but simpler) question: How to develop sbt plugin in multi-project build with projects that use it? 注意:类似(但更简单)的问题: 如何使用使用它的项目在多项目构建中开发sbt插件?

Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build? 有没有办法让myProject / sbt-plugin中定义的sbt-plugin在统一版本中挂钩到myProject / framework的构建中?

I have a working example on Github eed3si9n/plugin-bootstrap . 我在Github eed3si9n / plugin-bootstrap上有一个工作示例。 It's not super pretty, but it kind of works. 它不是很漂亮,但有点有用。 We can take advantage of the fact that sbt is recursive . 我们可以利用sbt是递归的这一事实。

The project directory is another build inside your build , which knows how to build your build. project目录是构建中的另一个构建 ,它知道如何构建构建。 To distinguish the builds, we sometimes use the term proper build to refer to your build, and meta-build to refer to the build in project . 为了区分构建,我们有时使用正确构建术语来指代您的构建,并使用元构建来引用project的构建。 The projects inside the metabuild can do anything any other project can do. metabuild中的项目可以执行任何其他项目可以执行的任何操作。 Your build definition is an sbt project. 您的构建定义是一个sbt项目。

By extension, we can think of the sbt plugins to be library- or inter-project dependencies to the root project of your metabuild. 通过扩展,我们可以将sbt插件视为元库的根项目的库或项目间依赖项。

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

In this example, think of the metabuild as a parallel universe or shadow world that has parallel multi-build structure as the proper build ( root , model , sbt-plugin ). 在此示例中,将metabuild视为具有并行多构建结构的并行Universe或阴影世界,作为正确的构建( rootmodelsbt-plugin )。

To reuse the source code from model and sbt-plugin subprojects in the proper build, we can re-create the multi-project build in the metabuild. 要在正确的构建中重用modelsbt-plugin子项目中的源代码,我们可以在metabuild中重新创建多项目构建。 This way we don't need to get into the circular dependency. 这样我们就不需要进入循环依赖。

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")
}

When sbt loads up, it will build metaModel and metaSbtSomething first, and use metaSbtSomething as a plugin to your proper build. 当sbt加载时,它将首先构建metaModelmetaSbtSomething ,并使用metaSbtSomething作为正确构建的插件。

If you have any other plugins you need you can just add it to project/plugins.sbt normally as I've added sbt-doge . 如果您有任何其他插件,您可以将它添加到project/plugins.sbt因为我添加了sbt-doge

proper build (build.sbt) 正确构建(build.sbt)

The proper build looks like a normal multi-project build. 正确的构建看起来像一个普通的多项目构建。 As you can see framework subproject uses SomethingPlugin . 正如您所见, framework子项目使用SomethingPlugin Important thing is that they share the source code, but the target directory is completely separated, so there are no interference once the proper build is loaded, and you are changing code around. 重要的是它们共享源代码,但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)
  )

demo 演示

In the SomethingPlugin example, I'm defining something task that uses foo.Model.x . SomethingPlugin示例中,我定义了something使用foo.Model.x任务。

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}") }
  )
}

Here's how we can invoke something task from the build: 以下是我们如何从构建中调用something任务:

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

1 comes from foo.Model.x , so this demonstrates that we are using the sbt-something plugin in framework subproject, and that the plugin is using metaModel . 1来自foo.Model.x ,所以这表明我们在framework子项目中使用了sbt-something插件,并且该插件使用了metaModel

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM