编译后SBT在项目中运行代码

时间:2014-01-29 14:51:27

标签: sbt

我们需要在编译步骤后运行一些代码。在编译步骤之后使事情变得容易:

compile in Compile <<= (compile in Compile) map{x=>
    // post-compile work
    doFoo()
    x
}

但是如何在新编译的代码中运行某些东西?

有关场景的更多信息:我们在电梯项目中使用较少的css。我们希望电梯能够动态编译成css(如果需要)以帮助dev,但在构建期间,在测试等运行之前,使用相同的代码产生更少。 less-sbt可能有所帮助,但我们对如何解决这个问题感兴趣。

2 个答案:

答案 0 :(得分:3)

您可以像这样使用triggeredBy方法:

yourTask <<= (fullClasspath in Runtime) map {classpath =>
  val loader: ClassLoader = ClasspathUtilities.toLoader(classpath.map(_.data).map(_.getAbsoluteFile))
  loader.loadClass("your.class.Here").newInstance()
} triggeredBy(compile in Compile)

这将在任何编译之后使用应用程序的运行时类路径实例化刚刚编译过的类。

答案 1 :(得分:2)

如果你为此解释了你的使用场景,那可能会有所帮助,因为这里有一些不同的可能的解决方案路径,在它们之间进行选择可能会涉及你没有告诉我们的注意事项。

您将无法将普通方法调用写入已编译的代码中。这是不可能的,因为在编译构建定义时,sbt还没有查看你的项目代码。

警告:漫无目的地大声思考。

我可以建议的一个技巧是访问testLoader in Test以获取加载已编译类的类加载器,然后使用反射来调用那里的方法。例如,在我自己的构建中,我有:

val netlogoVersion = taskKey[String]("...")

netlogoVersion := {
  (testLoader in Test).value
    .loadClass("org.nlogo.api.Version")
    .getMethod("version")
    .invoke(null).asInstanceOf[String]
}

我不确定访问testLoader in Test是否会真正适用于您的情况,因为testLoader会加载您的测试类以及常规类,因此您可能会在{{1}之间获得循环依赖关系}和compile in Compile

如果你想尝试制作一个只加载常规类的类加载器,那么,嗯。您可以在compile in Test的实现中查看sbt源代码并将其用于灵感,修改传递给createTestLoader的参数。 (您也可以查看ClasspathUtilities.makeLoader中的类似代码。它会调用Run.run0作为makeLoader任务实施的一部分。)

您可能考虑的另一个路径是重用run任务后面的机制来运行您的代码。您将无法以这种方式在编译的代码中调用任意方法,只能使用run方法,但如果您不需要返回值,也许您可​​以使用该方法。

main方法可用于创建整个fullRunTask类似的任务。请参阅“除了运行之外,如何创建自定义运行任务?”来自http://www.scala-sbt.org/0.13.1/docs/faq.htmlrun使得创建一个单独的任务变得非常容易,该任务在编译的代码中运行某些东西,但是它本身不会让你一直到解决方案,因为你需要一种方法将该任务附加到现有{ {1}}任务。如果你走这条路,我建议把它作为一个单独的问题。

考虑绕过fullRunTask,只是将自己的电话召集到compile in Compile。他们使用相同的机器。在我自己的版本中,我目前使用fullRunTask,但在sbt添加Run.run之前,这是基于fullRunTask的等效代码:

fullRunTask

原谅sbt 0.12,预宏语法;如果使用0.13宏重做,这看起来会更好。

无论如何,希望这个大脑转储中的东西证明是有用的。