定义从项目代码调用方法的sbt任务?

时间:2014-05-01 14:43:06

标签: scala sbt

我使用SBT构建scala项目。我想定义一个非常简单的任务,当我在sbt:

中输入generate
sbt> generate

它将调用我的my.App.main(..)方法来生成内容。

App.scala中有myproject/src/main/scala/my个文件,简化代码如下:

object App {
   def main(args: Array[String]) {
       val source = readContentOfFile("mysource.txt")
       val result = convert(source)
       writeToFile(result, "mytarget.txt");
   }
   // ignore some methods here
}

我尝试将以下代码添加到myproject/build.sbt

lazy val generate = taskKey[Unit]("Generate my file")

generate := {
  my.App.main(Array())
}

但由于无法找到my.App,因此无法编译。

然后我尝试将其添加到myproject/project/build.scala

import sbt._
import my._

object HelloBuild extends Build {

  lazy val generate = taskKey[Unit]("Generate my file")

  generate := {
    App.main(Array())
  }

}

但它仍然无法编译,它无法找到包my

如何在SBT中定义这样的任务?

3 个答案:

答案 0 :(得分:14)

.sbt格式,执行:

lazy val generate = taskKey[Unit]("Generate my file")

fullRunTask(generate, Compile, "my.App")

这是在http://www.scala-sbt.org/0.13.2/docs/faq.html记录的,“我怎样才能创建自定义运行任务,除了运行?”

另一种方法是:

lazy val generate = taskKey[Unit]("Generate my file")

generate := (runMain in Compile).toTask(" my.App").value

在简单的情况下工作正常,但不是可自定义的。

更新:Jacek使用resourceGeneratorssourceGenerators的建议是好的,如果它适合您的使用案例 - 无法从您的描述中判断它是否存在。< / p>

答案 1 :(得分:9)

其他答案非常适合这个问题,但我认为OP也可能从我的中受益:)

OP询问“我想定义一个非常简单的任务,当我在sbt中输入generate时,会调用我的my.App.main(..)方法生成一些东西。”那个可能最终使构建复杂化。

Sbt已经提供了一种在构建时生成文件的方法 - sourceGeneratorsresourceGenerators - 我似乎没有注意到需要为此阅读问题来定义单独的任务。

Generating files(见the future version of the document in the commit)中,您可以阅读:

  

sbt提供了用于添加源或资源生成的标准挂钩   任务。

凭借这些知识,人们可以想到以下解决方案:

sourceGenerators in Compile += Def.task {
  my.App.main(Array()) // it's not going to work without one change, though
  Seq[File]()          // a workaround before the above change is in effect
}.taskValue

要完成这项工作,您应该返回包含生成的文件的Seq[File](而不是空Seq[File]())。

代码工作的主要变化是将my.App类移动到project文件夹。然后它成为构建定义的一部分。它也反映了类的功能,因为它实际上是构建的一部分,而不是它的产物。当相同的代码是构建工件本身的一部分时,您不会将不同的关注点分开。如果my.App类参与构建,它应该属于它 - 因此移动到project文件夹。

项目的布局如下:

$ tree
.
├── build.sbt
└── project
    ├── App.scala
    └── build.properties

关注点分离(又名 @joescii in da haus

@ joescii的答案中有一点(我在答案中扩展) - “使其成为其他项目可以使用的单独项目。要做到这一点,你需要放置App将对象放入一个单独的项目中,并将其作为依赖项包含在project/project中,即

假设您在build-utils下有App.scala个单独的项目src/main/scala。这是一个只有Scala代码的常规sbt配置。

jacek:~/sandbox/so/generate-project-code
$ tree build-utils/
build-utils/
└── src
    └── main
        └── scala
            └── App.scala

您可以将其作为常规Scala应用程序进行测试,而不会破坏sbt。不需要额外的设置(并且让你的思想从某些时候开始,这可能是有益的 - 更少的设置总是有帮助的。)

在另一个项目project-code中 - 使用App.scala作为构建的基础, build.sbt 如下:

<强>项目码/ build.sbt

lazy val generate = taskKey[Unit]("Generate my file")

generate := {
  my.App.main(Array())
}

现在最重要的部分 - 项目之间的连接,以便App的代码可用于构建project-code

<强>项目码/项目/ build.sbt

lazy val buildUtils = RootProject(
  uri("file:/Users/jacek/sandbox/so/generate-project-code/build-utils")
)

lazy val plugins = project in file(".") dependsOn buildUtils

使用构建定义,执行generate会为您提供以下内容:

jacek:~/sandbox/so/generate-project-code/project-code
$ sbt
[info] Loading global plugins from /Users/jacek/.sbt/0.13/plugins
[info] Loading project definition from /Users/jacek/sandbox/so/generate-project-code/project-code/project
[info] Updating {file:/Users/jacek/sandbox/so/generate-project-code/build-utils/}build-utils...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Updating {file:/Users/jacek/sandbox/so/generate-project-code/project-code/project/}plugins...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/jacek/sandbox/so/generate-project-code/build-utils/target/scala-2.10/classes...
[info] Set current project to project-code (in build file:/Users/jacek/sandbox/so/generate-project-code/project-code/)
> generate
Hello from App.main
[success] Total time: 0 s, completed May 2, 2014 2:54:29 PM

我已将App的代码更改为:

> eval "cat ../build-utils/src/main/scala/App.scala"!
package my

object App {
  def main(args: Array[String]) {
    println("Hello from App.main")
  }
}

项目结构如下:

jacek:~/sandbox/so/generate-project-code/project-code
$ tree
.
├── build.sbt
└── project
    ├── build.properties
    └── build.sbt

其他改变又名好东西

我还建议对源代码生成器的代码进行一些其他更改:

  • 将代码从main方法移出到一个单独的方法,该方法返回生成的文件并让main调用它。这将使sourceGenerators中的代码更容易重用(没有不必要的Array()来调用它以及显式返回文件)。
  • filter使用mapconvert函数(添加功能更强大的功能)。

答案 2 :(得分:4)

@SethTisue提出的解决方案将起作用。另一种方法是使其成为其他项目可以使用的独立项目。为此,您需要将App对象放入单独的项目中,并将其作为依赖项包含在project/project中,或者将其作为sbt插件打包,理想情况下包含此任务定义。

有关如何创建打包为插件的lib的示例,请查看snmp4sgen目录包含执行代码生成的代码(类似于App代码),sbt目录包含gen的sbt插件包装。