【问题标题】:how to write an sbt plugin to launch the app with an agent如何编写一个 sbt 插件来使用代理启动应用程序
【发布时间】:2014-05-26 15:09:09
【问题描述】:

我想在开源项目之前为我的项目创建一个 sbt 插件。

该项目在运行应用程序的开始时附加一个 Java 代理,以检测它以进行各种类型的分析。代理写出文本文件以供以后处理。

我希望能够写一个sbt插件,可以

  • 有一个名为runWithProfilingrun 的替代品,它启动一个新的java 进程,将代理添加到参数列表中,并传递所有用户命令。
  • 退出时,我想调用一些任意的后处理代码来生成 HTML 报告

我大致知道如何创建新命令,但我不知道如何最好地实现 run 的替代方法...我不想通过复制 @ 的所有代码来重新发明轮子987654324@ 可以。有没有一种方法可以调用run,但确保我的参数被传递(一次)并且它绝对是一个新的java进程?

另外,能够为测试做同样的事情会很棒。

更新:这是我目前拥有的代码,但它存在一些问题,标记为TODOs

import sbt._
import Keys._
import sbt.Attributed.data

object LionPlugin extends Plugin {

  val lion = TaskKey[Unit]("lion", "Run a main class with lions-share profiling.")

  override val projectSettings = Seq(
    fork := true,
    javaOptions ++= Seq(
      "-Xloggc:gc.log", "-XX:+PrintGCDetails", "-XX:+PrintGCDateStamps",
      "-XX:+PrintTenuringDistribution", "-XX:+PrintHeapAtGC"
      // TODO: need to get hold of the local jar file for a particular artifact
      // IMPL: pass the jar as the agent
    ),
    lion <<= (
      runner,
      fullClasspath in Runtime,
      mainClass in Runtime,
      streams in Runtime
      ) map runLion
  )

  // TODO: update to a task that can take parameters (e.g. number of repeats, profiling settings)
  def runLion(runner: ScalaRun, cp: Classpath, main: Option[String], streams: TaskStreams): Unit = {
    assert(runner.isInstanceOf[ForkRun], "didn't get a forked runner... SBT is b0rk3d")
    println("RUNNING with " + runner.getClass)

    // TODO: ask user if main is None, like 'run' does
    val m = main.getOrElse("Scratch")

    // TODO: get the user's arguments
    val args = Nil

    runner.run(m, data(cp), args, streams.log)

    // IMPL: post-process and produce the report

    println("FINISHED")
  }

}

【问题讨论】:

    标签: scala plugins sbt


    【解决方案1】:

    插件作者需要遵守不成文的希波克拉底誓言,即“首先,不要伤害”。您的实现目前强制自己进入每个子项目并改变默认行为的forkjavaOptions,我认为这是危险的。我认为您需要复制您的任务范围内的 run 参数,以便默认设置不受损害。

    // TODO: update to a task that can take parameters (e.g. number of repeats, profiling settings)
    

    请参阅 Plugins Best Practices 和现有插件,例如 sbt-appengine

    在 sbt-appengine devServer 是一个输入任务,你可以设置一堆参数。

    gae.devServer       := {
      val args = startArgsParser.parsed
      val x = (products in Compile).value
      AppEngine.restartDevServer(streams.value, (gae.reLogTag in gae.devServer).value,
        thisProjectRef.value, (gae.reForkOptions in gae.devServer).value,
        (mainClass in gae.devServer).value, (fullClasspath in gae.devServer).value,
        (gae.reStartArgs in gae.devServer).value, args,
        packageWar.value,
        (gae.onStartHooks in gae.devServer).value, (gae.onStopHooks in gae.devServer).value)
    }
    

    正如您所见,代码的核心实际上是在 AppEngine 对象下的方法中实现的,因此其他人可以潜在地重用您的东西。 方法中的许多参数(在本例中为 restartDevServer)的范围为 gae.devServer 任务,如 (mainClass in gae.devServer)

    您希望构建用户如何设置插件?他们是打算将它作为全局插件启用一次并在任何地方使用相同的设置,还是要为project/lion.sbt 中的每个构建启用它? Plugins Best Practices 的建议是提供baseLionSettingslionSettings,以便构建用户可以选择启用lion 任务的子项目。

    根据实际运行情况,您可能想看看重用来自sbt-revolver 的代码,类似于我在 sbt-appengine 中所做的。

    【讨论】:

    • 这非常有用,谢谢!我非常希望允许用户在每个模块的基础上进行设置,并保持用户的 fork/javaOptions 不变。恐怕我仍然不知道如何做到这一点,即使在阅读了你的资料之后。我最初有例如fork in lion := true,但这被忽略了。
    • 虽然,我会窃取你的方法并直接使用Fork.java...这意味着我永远不会改变任何一个设置。
    【解决方案2】:

    请参阅“除了运行之外,我如何创建自定义运行任务?”在http://www.scala-sbt.org/0.13.0/docs/faq.html。在您的自定义任务中,您需要将 fork 设置为 true 以启动新的 JVM。

    【讨论】:

    • 这只是冰山一角...实际上调用fullRunTask 很棘手。这是一个Setting,我不知道怎么称呼它......
    • 道歉,取消标记答案,以便我可以更新更多细节
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-09
    • 1970-01-01
    相关资源
    最近更新 更多