Create a new task that runs a program - scala

I need to define a custom tasks that computes the name of a main class and then runs it. I was thinking about something like this
customTask {
mainClass = compute main class name based on env
runMain(mainClass, jvm-args, fork=true)
}
and then in SBT i would be able to run
sbt> custom-task
can this be done in SBT 11.2 ?.

well you can give it a try .. I works fine for me -
lazy val testngRun = inputKey[Unit]("custom run task for testng")
testngRun := {
val one = (runMain in Compile).fullInput(" org.testng.TestNG -testclass com.pg.acceptance.testcase.PfsLoginServiceTest").evaluated
}

Late answer but you can create new SBT tasks as mentioned in documentation http://www.scala-sbt.org/release/docs/Detailed-Topics/Tasks#defining-a-new-task
You can run any scala code as the task code. Tasks can also take input arguments.
Pretty much powerful IMO.

Related

How to execute scala tests programmatically

I'm looking for a way to execute scala tests (implemented in munit, but it could be also ScalaTest) programmatically. I want to perform more or less what sbt test does out-of-the box inside my own scala code, without running sbt (focusing on test discovery and execution and getting back a report).
I some something like this in mind:
object Test extends App {
val tests = TestDiscovery.discover("package.that.has.tests")
val reports = tests.foreach(test => test.execute())
// do something with the reports, maybe print to console
}
Is there any documentation related to this?
Scala Test has execute() and run().
In order to understand the impact of all the args it's worth looking at the Scala Test shell as well

sbt illegal dynamic reference in runMain

I'm trying to run a code generator, and passing it the filename to write the output:
resourceGenerators in (proj, Compile) += Def.task {
val file = (resourceManaged in (proj, Compile)).value / "swagger.yaml"
(runMain in (proj, Compile)).toTask(s"api.swagger.SwaggerDump $file").value
Seq(file)
}.value
However, this gives me:
build.sbt:172: error: Illegal dynamic reference: file
(runMain in (proj, Compile)).toTask(s"api.swagger.SwaggerDump $file").value
Your code snippet has two problems:
You use { ... }.value instead of { ... }.taskValue. The type of resource generators is Seq[Task[Seq[File]]] and when you do value, you get Seq[File] not Task[Seq[File]]. That causes a legitimate compile error.
The dynamic variable file is used as the argument of toTask, which the current macro implementation prohibits.
Why static?
Sbt forces task implementations to have static dependencies on other tasks. Otherwise, sbt cannot perform task deduplication and cannot provide correct information in the inspect commands. That means that whichever task evaluation you perform inside a task cannot depend on a variable (a value known only at runtime), as your file in toTask does.
To overcome this limitation, there exists dynamic tasks, whose body allows you to return a task. Every "dynamic dependency" has to be defined inside a dynamic task, and then you can depend on the hoisted up dynamic values in the task that you return.
Dynamic solution
The following Scastie is the correct implementation of your task. I copy-paste the code so that folks can have a quick look, but go to that Scastie to check that it successfully compiles and runs.
resourceGenerators in (proj, Compile) += Def.taskDyn {
val file = (resourceManaged in (proj, Compile)).value / "swagger.yaml"
Def.task {
(runMain in (proj, Compile))
.toTask(s"api.swagger.SwaggerDump $file")
.value
Seq(file)
}
}.taskValue
Discussion
If you had fixed the taskValue error, should your task implementation correctly compile?
In my opinion, yes, but I haven't looked at the internal implementation good enough to assert that your task implementation does not hinder task deduplication and dependency extraction. If it does not, the illegal reference check should disappear.
This is a current limitation of sbt that I would like to get rid of, either by improving the whole macro implementation (hoisting up values and making sure that dependency analysis covers more cases) or by just improving the "illegal references checks" to not be over pessimistic. However, this is a hard problem, takes time and it's not likely to happen in the short term.
If this is an issue for you, please file a ticket in sbt/sbt. This is the only way to know the urgency of fixing this issue, if any. For now, the best we can do is to document it.

How to sequentially call an input task and other tasks in sbt

I am trying to override the it:run task in sbt so that it would run two other tasks (actually three if you include logging) in sequence. The first of these tasks is to provision the application (it:provision) and the second are to actually run the tests (it:test).
I started of with a simple Task[Unit] for my custom provision task.
lazy val provision = taskKey[Unit]("Provisions the application in an environment based on the configuration.")
I then defined a function to use for the it:run implementation as:
def runIntegrationTestsImpl(): Initialize[Task[Unit]] = Def.inputTask {
Def.sequential(
provision in IntegrationTest,
Def.task(state.value.log.info("Running integration tests.")),
test in IntegrationTest
).value
}
This worked fine. However what I really wanted was to make the provision task an InputTask[Unit].
So I then changed provision to:
lazy val provision = inputKey[Unit]("Provisions the application in an environment based on the configuration.")
And tried to update the it:run implementation:
def runIntegrationTestsImpl(): Initialize[InputTask[Unit]] = {
val parser = DefaultParsers.any.*.map(_.mkString)
Def.inputTask {
val prov = (provision in IntegrationTest).toTask(parser.parsed)
Def.sequential(
prov,
Def.task(state.value.log.info("Running integration tests.")),
test in IntegrationTest
).value
}
}
This is as close as I got to it compiling. I didn't really intend on parsing the arguments here but it seemed toTask was the only way to get an Initialize[Task[Unit]].
No matter what I try and do I still cannot get rid of the following error:
Illegal dynamic reference: prov
Which is referring to the first element in the sequence.
I seem to hit this problem a lot with the sbt macros. Is there a way to achieve what I want?

How can I call another task from my SBT task?

I'm trying to call the runTask inside of my task and considered this would work:
name := "hello"
version := "1.0"
scalaVersion := "2.10.2"
lazy val hello = taskKey[Unit]("executes hey")
lazy val helloTask = hello <<= runTask(fullClasspath, "sample.Hey" in run, runner in run)
But, well, it doesn't. Any ideas on how I could do this?
General answer:
To answer your general question, the solution is to make your task depend on the other task. Invoking the task directly would do an end run around the dependency system, the parallel execution system, etc. You depend on, and invoke, the task like this (in 0.13-style syntax):
myTask := {
...
val result = otherTask.value
...
}
Note that otherTask will be invoked before myTask begins, rather than at the point in the body of myTask where the dependency appears; because that's how dependencies work.
If for whatever reason you find doing it the "normal" way inappropriate or unacceptable, consider that good style in sbt is to separate the declaration of a task from its implementation. A typical task implementation simply marshals arguments and then calls a method that actually does the work. If the task you want to call is implemented that way, then an answer to "How do I call task T?" is "Don't; call the same code T calls."
Specific answer:
But from your example, it looks to me like the problem you're actually trying to solve is "How can I create a custom run task, in addition to run?" This question is answered in the sbt FAQ; see http://www.scala-sbt.org/0.13.0/docs/faq.html. The answer is to use the convenience methods fullRunTask and fullRunInputTask.
Incidentally, if you look at the source code for those methods, you'll see that they don't make a task that invokes another task; rather, they take the "call the same code" approach.

Run custom task automatically before/after standard task

I often want to do some customization before one of the standard tasks are run. I realize I can make new tasks that executes existing tasks in the order I want, but I find that cumbersome and the chance that a developer misses that he is supposed to run my-compile instead of compile is big and leads to hard to fix errors.
So I want to define a custom task (say prepare-app) and inject it into the dependency tree of the existing tasks (say package-bin) so that every time someone invokes package-bin my custom tasks is run right before it.
I tried doing this
def mySettings = {
inConfig(Compile)(Seq(prepareAppTask <<= packageBin in Compile map { (pkg: File) =>
// fiddle with the /target folder before package-bin makes it into a jar
})) ++
Seq(name := "my project", version := "1.0")
}
lazy val prepareAppTask = TaskKey[Unit]("prepare-app")
but it's not executed automatically by package-bin right before it packages the compile output into a jar. So how do I alter the above code to be run at the right time ?
More generally where do I find info about hooking into other tasks like compile and is there a general way to ensure that your own tasks are run before and after a standard tasks are invoked ?.
Extending an existing task is documented the SBT documentation for Tasks (look at the section Modifying an Existing Task).
Something like this:
compile in Compile <<= (compile in Compile) map { _ =>
// what you want to happen after compile goes here
}
Actually, there is another way - define your task to depend on compile
prepareAppTask := (whatever you want to do) dependsOn compile
and then modify packageBin to depend on that:
packageBin <<= packageBin dependsOn prepareAppTask
(all of the above non-tested, but the general thrust should work, I hope).
As an update for the previous answer by #Paul Butcher, this could be done in a bit different way in SBT 1.x versions since <<== is no longer supported. I took an example of a sample task to run before the compilation that I use in one of my projects:
lazy val wsdlImport = TaskKey[Unit]("wsdlImport", "Generates Java classes from WSDL")
wsdlImport := {
import sys.process._
"./wsdl/bin/wsdl_import.sh" !
// or do whatever stuff you need
}
(compile in Compile) := ((compile in Compile) dependsOn wsdlImport).value
This is very similar to how it was done before 1.x.
Also, there is another way suggested by official SBT docs, which is basically a composition of tasks (instead of dependencies hierarchy). Taking the same example as above:
(compile in Compile) := {
val w = wsdlImport.value
val c = (compile in Compile).value
// you can add more tasks to composition or perform some actions with them
c
}
It feels like giving more flexibility in some cases, though the first example looks a bit neater, as for me.
Tested on SBT 1.2.3 but should work with other 1.x as well.