SBT - 过渡收集子项目信息的任务

时间:2017-03-31 11:34:06

标签: scala sbt

我正在尝试实施一项简单的任务(并再次学习SBT - 同时)。在花了大约8个小时试图找到解决方案后,尝试了许多死胡同,阅读了大量的SBT文档和SO条目,并积累了大量的愤怒和挫折,我在这里发布了这个问题。

我需要实现一个任务transitiveBaseDirectories,它将(在sbt控制台中)允许我在多项目构建中查询项目的依赖项目任务(示例结构在本文末尾) )像这样:

projectA/transitiveBaseDirectories

(构成所需的输出)

Seq(File("./projectA"), File("./projectAA"), File("./projectAAA"), File("./projectAB")))

这个示例项目

lazy val transitiveBaseDirectories = taskKey[Seq[File]](
    "Returns a list of baseDirectory-ies of all projects a given project transitively depends on."
)

def transitiveBaseDirectoriesImpl = ???

lazy val root = project.dependsOn(projectA, projectB)

lazy val projectA = project.dependsOn(projectAA, projectAB)

lazy val projectAA = project.dependsOn(projectAAA)

lazy val projectAAA = project

lazy val projectAB = project.dependsOn(projectAB)

lazy val projectB = project

然后有必要以某种方式使transitiveBaseDirectories可用于所有项目 - 我也不知道该怎么做。

我故意不在这里展示我失败的所有尝试(Product.extract(state.value)baseDirectory.all(ScopeFilter(inDependencies(...)))等各种各样的尝试)。在我的SBT探索中,我曾多次感受到“几乎在那里”的感觉,接着是混乱的浪潮,并意识到我只是没有完成任务所需的SBT大图,而且更加沮丧。我已经放弃了,这也许是我在SBT中执行一项非平凡任务的第三次机会,它从未结束(在更好的情况下,我已经成功了,但是我并没有真正明白我做了什么)。

我真的很感激这里有一个简单但完整的解决方案,因为如果我没有提出太多要求,我已经过了很多次了。

非常感谢。

2 个答案:

答案 0 :(得分:1)

我假设你想要所有项目的传递依赖。

因此,我首先要完成一项任务,以获得所有直接的项目依赖关系。

val directProjectDeps = taskKey[Map[String,Seq[ProjectRef]]]("project dependencies!")

directProjectDeps := {
  val extracted = Project extract state.value
  extracted.structure.allProjects.map { p =>
    val deps = p.dependencies.map(_.project)
    (p.id, deps)
  }.toMap
}

然后需要做一些图遍历以获得所有传递依赖。这是一项单独任务中的简单实现。

val transitiveProjectDeps = taskKey[Map[String,Seq[ProjectRef]]]("transitive project dependencies!")

transitiveProjectDeps := {
  val deps = directProjectDeps.value

  def transitives(id: String): Seq[ProjectRef] = {
    val direct = deps(id)
    direct ++ direct.flatMap(d => transitives(d.project))
  }

  deps.map{ case(id,_) => (id,transitives(id)) }
}

为避免处理对任务的动态引用,请使用extract来访问所有ProjectRefs的baseDirectory设置

val transitiveProjectDirs = taskKey[Map[String,Seq[File]]]("transitive project dependencies!")
transitiveProjectDirs := {
  val extracted = Project extract state.value
  transitiveProjectDeps.value.mapValues { v =>
    v.map(ref => extracted.get(baseDirectory in ref))
  }
}

仅为当前项目获取目录。

val myTransitiveProjectDirs = taskKey[Seq[File]]("just my dirs")
myTransitiveProjectDirs :=
  transitiveProjectDirs.value.apply(thisProjectRef.value.project)

要在任何子项目中提供所有任务,最简单的方法是将它们放入插件中。

答案 1 :(得分:1)

非常感谢Justin Kaeser。为了完整起见,整个解决方案(也将任务添加到所有项目中)是

使用以下内容创建文件project/TransitiveDirsPlugin.scala

import sbt.Keys._
import sbt._

object TransitiveDirsPlugin extends AutoPlugin {
  override def trigger: PluginTrigger = allRequirements

  private[this] val directProjectDeps =
    taskKey[Map[String, Seq[ProjectRef]]]("project dependencies!")

  private[this] val transitiveProjectDeps =
    taskKey[Map[String, Seq[ProjectRef]]]("transitive project dependencies!")

  private[this] val transitiveProjectDirs =
    taskKey[Map[String, Seq[File]]]("transitive project dependencies!")

  private[this] val myTransitiveProjectDirs: TaskKey[Seq[File]] =
    taskKey[Seq[File]]("just my dirs")

  override lazy val projectSettings = Seq(
    directProjectDeps := {
      val extracted = Project extract state.value
      extracted.structure.allProjects.map { p ⇒
        val deps = p.dependencies.map(_.project)
        (p.id, deps)
      }.toMap
    },

    transitiveProjectDeps := {
      val deps = directProjectDeps.value

      def transitives(id: String): Seq[ProjectRef] = {
        val direct = deps(id)
        direct ++ direct.flatMap(d => transitives(d.project))
      }

      deps.map { case (id, _) => (id, transitives(id)) }
    },

    transitiveProjectDirs := {
      val extracted = Project extract state.value
      transitiveProjectDeps.value.mapValues { v =>
        v.map(ref => extracted.get(baseDirectory in ref))
      }
    },

    myTransitiveProjectDirs :=
      transitiveProjectDirs.value.apply(thisProjectRef.value.project)
  )
}

现在我可以使用show projectA/myTransitiveProjectDirs等。谢谢Justin。