I have an existing task called myTask, whose implementation I don't control.
I want to redefine it in this way:
myTask := {
val x = prepare()
try
myTask.value
finally
cleanup(x)
}
As you probably know, this code wouldn't work, as we don't control when myTask.value is executed.
prepare can be called with Def.sequential(), and cleanup with the andFinally construct. The only problem is how cleanup can get the return value of prepare().
Def.sequential{
Def.task{
prepare()
},
myTask
}.andFinally(cleanup(???))
One workaround is to use global variables, but this is a dirty hack.
Any ideas?
Related doc
I've tried to use global variables, and it works ok, even though it isn't the most elegant way to implement it.
I have:
project/MyTasks.scala
build.sbt
snippet in MyTasks.scala:
object MyTasks {
var x = Option.empty[String]
def prepare(): String = ???
def cleanup(x: String): Unit = ???
}
snippet in build.sbt:
myTask := Def.sequential{
Def.task{
MyTasks.x = Some(MyTasks.prepare())
},
myTask
}.andFinally {
MyTasks.cleanup(MyTasks.x.get)
MyTasks.x = None
}.value
In this way, we can get the state from prepare, and bypass SBT limitations.
Related
Is it possible to execute a Command in Sbt Task? If so, how? as Command requires a State, how could I obtain one?
I am trying to override a default task, here's what I've tried
dist := {
println("Turning coverage off")
Command.process("coverageOff")
dist.value
}
The signature of Command.process is (string, state) => _
I havent figure out where to get the State
Yes, you can run a command within a task. Here is what I'm currently doing to achieve it. First, define the following method in your build:
/**
* Convert the given command string to a release step action, preserving and invoking remaining commands
* Note: This was copied from https://github.com/sbt/sbt-release/blob/663cfd426361484228a21a1244b2e6b0f7656bdf/src/main/scala/ReleasePlugin.scala#L99-L115
*/
def runCommandAndRemaining(command: String): State => State = { st: State =>
import sbt.complete.Parser
#annotation.tailrec
def runCommand(command: String, state: State): State = {
val nextState = Parser.parse(command, state.combinedParser) match {
case Right(cmd) => cmd()
case Left(msg) => throw sys.error(s"Invalid programmatic input:\n$msg")
}
nextState.remainingCommands.toList match {
case Nil => nextState
case head :: tail => runCommand(head.commandLine, nextState.copy(remainingCommands = tail))
}
}
runCommand(command, st.copy(remainingCommands = Nil)).copy(remainingCommands = st.remainingCommands)
}
Then, just call any command from within a task using the above defined utility, e.g., runCommandAndRemaining("+myProject/publishLocal")(state.value).
In your specific case, it should boil down to
dist := {
val log = streams.value.log
log.debug("Turning coverage off")
runCommandAndRemaining("coverageOff")(state.value)
dist.value
}
Hope this helps!
State can be obtained by evaluating state.value
To access the current State from a task, use the state task as an
input. For example,
myTask := ... state.value ...
and commands can be called within task using Command.process like so
dist := {
println("Turning coverage off")
Command.process("coverageOff", state.value)
dist.value
}
After getting some help from gitter, it's not possible, one can however do the reverse, call a task in a command.
So if your use case is to run a command and a task sequentially (or vice versa), you can do something like this
lazy val newCommand = Command.command("name") { state =>
val newState = Command.process("comandName", state)
// run task
newState
}
I want to specify a function based on a string. I'm getting strings out of a map, in the example below they are the values in function while interating ove the map. Now for example, when the string value function == "networkInfo", I would like to "treat" that value as a real functions' name. It's hard to explain, but I think you guys will know what I mean.
My goal is to remove the switch statement and directly call c.AddFunc(spec, func() { networkInfo() }) where networkInfo is the function name, extracted from string function. I know this is possible, but I don't know how :(. Help is appreciated!
// ScheduleCronjobs starts the scheduler
func ScheduleCronjobs() {
tasks := props.P.GetStringMapString("tasks")
log.Infof("number of tasks: %d", len(tasks))
if len(tasks) != 0 {
c := cron.New()
// for each task, initialize
for function, spec := range tasks {
switch function {
case "networkInfo":
c.AddFunc(spec, func() { networkInfo() })
case "bla":
c.AddFunc(spec, func() { bla() })
default:
log.Errorf("unknown task: %q", function)
}
}
c.Start()
}
// after initialization, send out confirmation message
slack.SendMessage("tasks initialized", props.P.GetString("channel"))
}
Why not something like:
taskDefs := map[string]func(){
"networkInfo": networkInfo,
"bla": bla,
}
for function, spec := range tasks {
if fn, ok := taskDefs[function]; ok {
c.AddFunc(spec, func() { fn() }) // not sure if you need the enclosing func
} else {
log.Errorf("unknown task: %q", function)
}
}
If you do need varying signatures of your funcs then you'd actually need reflection, but if the types of the funcs are all the same, then using this map approach might be a simpler solution, without the overhead of reflection.
The only way I've found to find functions by name in a package is by actually parsing the source files. This repo is an example of finding funcs and storing them in a map with the name as the key.
The Go linker will silently drop unreferenced funcs, so if the only way you're referencing the func is through reflection it would break. That is why the map approach I suggest is superior; it let's the linker know the func is being used.
In a inputTask I'm programmatically calling another inputTask, e.g. testOnly, with parameter string as follows:
val readParams = inputKey[Unit]("reads version")
readParams := {
... // here some Parser code
val a = "*OnlyThisClassPls*"
testOnly.toTask(a)
}
Unfortunately instead of result I get an exception Illegal dynamic reference. Why?
I think I solved my problem.
I created a method which converts testOnly inputTask to dynamic task (taskDyn) with parameter
def testOnlyWithDynamicParams(params: String) = Def.taskDyn {
(testOnly in Test).toTask(params)
}
I defined an dynamic input task (inputTaskDyn) which uses method to convert and evaluates value at the end
readParams := Def.inputTaskDyn {
... // here some Parser code
val paramsForTestOnly = " *OnlyThisClassPls*"
testOnlyWithDynamicParams(paramsForTestOnly)
}.evaluated
I'm not sure if it is a best way but it works for me. If you know the better solution please correct me.
How can I get the value of a setting (say, name) and pass it as an argument to fullRunTask? I do not understand the implementation of fullRunTask.
For example:
lazy val foo = TaskKey[Unit]("foo")
fullRunTask(foo, Compile, "foo.Foo", name.value)
does not work because I can't reference name.value in this context.
Ok I got some help from Josh Suereth. Doing this with fullRunTask is a little more complex but the extra stuff it does (adding runner in myTask) does wasn't really necessary. Inlining the body of runTask did what I needed.
lazy val myTask = taskKey[Unit]("my custom run task")
myTask := {
val r = (runner in Compile).value
val input = name.value // or any other string setting(s)
val cp = (fullClasspath in Compile).value
toError(r.run("my.MainClass", data(cp), Seq(input), streams.value.log))
}
Can I retrieve a Method via reflection, somehow combine it with a target object, and return it as something that looks like a function in Scala (i.e. you can call it using parenthesis)? The argument list is variable. It doesn't have to be a "first-class" function (I've updated the question), just a syntactic-looking function call, e.g. f(args).
My attempt so far looks something like this (which technically is pseudo-code, just to avoid cluttering up the post with additional definitions):
class method_ref(o: AnyRef, m: java.lang.reflect.Method) {
def apply(args: Any*): some_return_type = {
var oa: Array[Object] = args.toArray.map { _.asInstanceOf[Object] }
println("calling: " + m.toString + " with: " + oa.length)
m.invoke(o, oa: _*) match {
case x: some_return_type => x;
case u => throw new Exception("unknown result" + u);
}
}
}
With the above I was able to get past the compiler errors, but now I have a run-time exception:
Caused by: java.lang.IllegalArgumentException: argument type mismatch
The example usage is something like:
var f = ... some expression returning method_ref ...;
...
var y = f(x) // looks like a function, doesn't it?
UPDATE
Changing the args:Any* to args:AnyRef* actually fixed my run-time problem, so the above approach (with the fix) works fine for what I was trying to accomplish. I think I ran into a more general issue with varargs here.
Sure. Here's some code I wrote that add an interface to a function. It's not exactly what you want, but I think it can be adapted with few changes. The most difficult change is on invoke, where you'll need to change the invoked method by the one obtained through reflection. Also, you'll have to take care that the received method you are processing is apply. Also, instead of f, you'd use the target object. It should probably look something like this:
def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = method match {
case m if /* m is apply */ => target.getClass().getMethod("name", /* parameter type */).invoke(target, args: _*)
case _ => /* ??? */
}
Anyway, here's the code:
import java.lang.reflect.{Proxy, InvocationHandler, Method}
class Handler[T, R](f: Function1[T, R])(implicit fm: Manifest[Function1[T, R]]) extends InvocationHandler {
def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = method.invoke(f, args: _*)
def withInterface[I](implicit m: Manifest[I]) = {
require(m <:< manifest[Function1[T, R]] && m.erasure.isInterface)
Proxy.newProxyInstance(m.erasure.getClassLoader(), Array(m.erasure), this).asInstanceOf[I]
}
}
object Handler {
def apply[T, R](f: Function1[T, R])(implicit fm: Manifest[Function1[T, R]]) = new Handler(f)
}
And use it like this:
trait CostFunction extends Function1[String, Int]
Handler { x: String => x.length } withInterface manifest[CostFunction]
The use of "manifest" there helps with syntax. You could write it like this:
Handler({ x: String => x.length }).withInterface[CostFunction] // or
Handler((_: String).length).withInterface[CostFunction]
One could also drop the manifest and use classOf instead with a few changes.
If you're not looking for a generic invoke that takes the method name--but rather, you want to capture a particular method on a particular object--and you don't want to get too deeply into manifests and such, I think the following is a decent solution:
class MethodFunc[T <: AnyRef](o: Object, m: reflect.Method, tc: Class[T]) {
def apply(oa: Any*): T = {
val result = m.invoke(o, oa.map(_.asInstanceOf[AnyRef]): _*)
if (result.getClass == tc) result.asInstanceOf[T]
else throw new IllegalArgumentException("Unexpected result " + result)
}
}
Let's see it in action:
val s = "Hi there, friend"
val m = s.getClass.getMethods.find(m => {
m.getName == "substring" && m.getParameterTypes.length == 2
}).get
val mf = new MethodFunc(s,m,classOf[String])
scala> mf(3,8)
res10: String = there
The tricky part is getting the correct type for the return value. Here it's left up to you to supply it. For example,if you supply classOf[CharSequence] it will fail because it's not the right class. (Manifests are better for this, but you did ask for simple...though I think "simple to use" is generally better than "simple to code the functionality".)