使用编译器插件

时间:2016-05-14 12:47:42

标签: sbt

我想创建一个与默认配置相同的编译配置,但添加了一个编译器插件。在我的特殊情况下,我希望有一个" dev"配置,但使用linter插件(https://github.com/HairyFotr/linter),因为它减慢了编译时间,并且不需要在生产或持续集成中运行它。

现在这就是我的尝试:

lazy val Dev = config("dev") extend Compile

lazy val root = (project in file(".")).configs(Dev).settings(
    inConfig(Dev)(addCompilerPlugin("org.psywerx.hairyfotr" %% "linter" % "0.1.12")): _*)

并且 应该工作,因为当我inspect dev:libraryDependencies时,它是我所期望的 - 它有org.psywerx.hairyfotr:linter:0.1.12:plugin->default(compile)。通常,如果我使用"插件添加库#34;范围,它适用于默认设置:

libraryDependencies += ("org.psywerx.hairyfotr" %% "linter" % "0.1.12" % "plugin"

如果我在不同的配置下添加它,它就不起作用,所以这里肯定会有其他的东西。

2 个答案:

答案 0 :(得分:2)

这解决了这个问题,但并没有完全按照问题的方式解决。这是完整的build.sbt

libraryDependencies ++= Seq(
  "org.psywerx.hairyfotr" %% "linter" % "0.1.14" % "test")

val linter = Command.command("linter")(state => {
  val linterJar = for {
    (newState, result) <- Project.runTask(fullClasspath in Test, state)
    cp <- result.toEither.right.toOption
    linter <- cp.find(
      _.get(moduleID.key).exists(mId =>
        mId.organization == "org.psywerx.hairyfotr" &&
          mId.name == "linter_2.11"))
  } yield linter.data.absolutePath

  val res = Project.runTask(scalacOptions, state)
  res match {
    case Some((newState, result)) =>
      result.toEither.right.foreach { defaultScalacOptions =>
        Project.runTask(compile in Test,
          Project.extract(state).append(
            scalacOptions := defaultScalacOptions ++ linterJar.map(p => Seq(s"-Xplugin:$p")).getOrElse(Seq.empty),
            newState))
      }
    case None => sys.error("Couldn't get defaultScalacOptions")
  }
  state
})

lazy val root = (project in file(".")).configs(Test).settings(commands ++= Seq(linter))

您返回未修改状态的事实意味着您不会更改项目设置。因此,如果您运行sbt linter,则应使用其他scalacOptions编译项目,但如果您在同一个sbt会话中运行compile,则不会使用这些其他设置。

这里棘手的问题是scalacOptions实际上是TaskKey,而不是SettingKey。我不知道为什么会这样,但为了获得它的价值,你必须完成这项任务。一个原因可能是,在sbt中,您无法根据任务进行设置,但您可以根据任务创建任务。换句话说,scalacOptions可以依赖于其他任务值,也可能在内部确实,我还没有检查过。如果当前的答案对您有用,我可以尝试考虑更优雅的方法来实现相同的结果。

编辑:修改了代码,为linter插件指定了scalacOptions。请注意,该插件必须是托管依赖项,而不仅仅是下载的jar,才能使此解决方案正常工作。如果你想让它不受管理,那就是一种方式,但我现在还没有进入它。此外,为了便于说明,我还自由地将其用于测试代码。

答案 1 :(得分:1)

在源代码中查看Defaults.scala,似乎编译命令始终从编译范围中获取选项。所以,如果我是正确的,你只能有一组编译选项!

这似乎可以通过scalacOptions表现相同的事实得到证实,这也是为什么我没有看到这些类似问题的非黑客答案的原因:

我很高兴被证明是错的。

编辑:FWIW,可能无法在同一个项目中定义另一个scalac选项配置文件,但您可以在&#34;不同的&#中执行此操作 34;项目:

lazy val dev = (project in file(".")).
settings(target := baseDirectory.value / "target" / "dev").
settings(addCompilerPlugin("org.psywerx.hairyfotr" %% "linter" % "0.1.12"): _*)

这样做的缺点是它有一个单独的输出目录,因此需要更多空间,更重要的是,不会在两个项目之间进行增量编译。然而,在花了一些时间思考它之后,这可能是设计上的。毕竟,即使直径不足,一些scalac编译选项也可以想象地改变输出。这会使尝试将增量编译的元数据从一组scalac选项保留到另一组scalac选项变得毫无意义。因此,不同的scalac选项确实需要不同的目标目录。