如何编写sbt插件以使用代理启动应用程序 [英] how to write an sbt plugin to launch the app with an agent

查看:91
本文介绍了如何编写sbt插件以使用代理启动应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to create an sbt plugin for my project before I open source it.

该项目将Java代理附加到开始运行应用程序的过程中,以对其进行各种类型的性能分析进行检测.代理会写出文本文件以供以后处理.

The project attaches a Java agent to the start of running an application, to instrument it for various types of profiling. The agent writes out text files for later processing.

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

I would like to be able to write an sbt plugin that can

  • 具有run的替代名称,称为runWithProfiling,它将启动新的Java进程,并将代理添加到参数列表中,并传递所有用户命令.
  • 在退出时,我想调用一些任意的后处理代码以生成HTML报告
  • have an alternative to run called runWithProfiling which launches a new java process, with the agent added to the argument list, and passing all the users commands.
  • on exit, I then want to invoke some arbitrary post-processing code to produce an HTML report

我大致知道如何创建新命令,但是我不知道如何最好地实现run的替代方案...我不想通过复制所有run可以.有什么方法可以调用run,但要确保(一次)传递我的参数,并且它肯定是一个新的Java进程?

I know roughly how to create the new command, but I don't know how to best implement an alternative to run... I don't want to re-invent the wheel by copying all the code that run does. Is there a way I can invoke run but ensure that my parameters are passed (one time) and that it is definitely a new java process?

此外,能够在测试中执行相同的操作会很棒.

Also, being able to do the same for the tests would be great.

更新:这是我目前拥有的代码,但遇到了一些问题,标记为TODO s

UPDATE: this is the code that I currently have, but it suffers from several problems, marked up as 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")
  }

}

推荐答案

插件作者必须遵守未成文的希波克拉底誓言,即首先,不要伤害".您的实现当前将自身强制到每个子项目,并更改默认行为的forkjavaOptions,我认为这很危险.我认为您需要复制范围为您的任务的run参数,以便默认设置不受损害.

Plugin authors need to abide by an unwritten Hippocratic Oath, which is "first, do no harm." Your implementation currently forces itself to every subproject and mutates fork and javaOptions of the default behavior, which I think is dangerous. I think you need to duplicate the run parameters scoped to your task so the default settings are unharmed.

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

请参见插件最佳做法和现有的像 sbt-appengine 这样的插件例子.

See Plugins Best Practices and existing plugins like sbt-appengine for an example.

在sbt-appengine中,devServer是一项输入任务,您可以设置一堆参数.

In sbt-appengine devServer is an input task that you can set a bunch of parameters.

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).

As you see the gut of the code is actually implemented in a method under AppEngine object, so someone else can reuse your stuff potentially. Many of the parameters into the method (in this case restartDevServer) are scoped to gae.devServer task like (mainClass in gae.devServer).

您打算如何由构建用户设置插件?他们是要一次性将其作为全局插件启用并在各处使用相同的设置,还是要为project/lion.sbt中的每个版本启用它? 插件最佳做法的建议是baseLionSettingslionSettings,以便构建用户可以选择要启用lion任务的子项目.

How do you intend the plugin to be set up by the build users? Are they going to enable it once as a global plugin and use the same settings everywhere, or are they going to enable it for each build in project/lion.sbt? Plugins Best Practices's recommendation is to provide baseLionSettings and lionSettings so the build user can pick and chose which subproject would have the lion task enabled.

根据实际运行情况,您可能想看看重用 sbt-revolver 与我在sbt-appengine中所做的类似.

As per actual running goes, you might want to take a look at reusing the code from sbt-revolver similar to what I did in sbt-appengine.

这篇关于如何编写sbt插件以使用代理启动应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆