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
}
Related
I have the next code:
import zio._
import scala.concurrent.Future
case class AppError(description: String) extends Throwable
// legacy-code imitation
def method(x: Int): Task[Boolean] = {
Task.fromFuture { implicit ec => Future.successful(x == 0) }
}
def handler(input: Int): IO[AppError, Int] = {
for {
result <- method(input)
_ <- IO.fail(AppError("app error")).when(result)
} yield input
}
but this code does not compile, because compiler says result type is:
ZIO[Any, Throwable, Int]
How to convert from Task (where I call method) to IO?
You'll need to decide what you want to do with Throwable errors which are not AppError.
If you decide you want to map them to an AppError you can do:
method(input).mapError {
case ae: AppError => ae
case other => AppError(other.getMessage)
}
If you want to refine those errors and only keep the ones that are AppError then you can use one of the refine* family of operators, which will keep errors that match the predicate and terminate the fiber otherwise.
method(input).refineToOrDie[AppError] // IO[AppError, Boolean]
// Or
method(input).refineOrDie { case ae: AppError => ae } // IO[AppError, Boolean]
Or if you want to assume that all errors from method are considered "Fiber terminating", then you can use .orDie to absorb the error and kill the fiber:
method(input).orDie // UIO[Boolean]
Or if you want to recover from the error and handle it a different way then you could use the catch* family
method(input).catchAll(_ => UIO.succeed(false)) // UIO[Boolean]
Finally if you wanted to have the result mapped into an Either you could use .either, which will lift the error out of the error channel and map it into Either[E, A]
method(input).either // UIO[Either[Throwable, Boolean]]
There is a great cheat sheet (though admittedly a bit out of date) here as well
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.
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.
We have a situation in which two dependencies have exactly the same class (because one of the dependencies copied it and included in their own source).
This is causing sbt assembly to fail its deduplication checks.
How can I exclude a class from a particular jar?
You need a mergeStrategy, which will take one of the files.
mergeStrategy in assembly := {
case PathList("path", "to", "your", "DuplicatedClass.class") => MergeStrategy.first
case x => (mergeStrategy in assembly).value(x)
}
Update
If you want to handle the file depending on the JAR which it came from, I don't think you can with the merge strategies that assembly plugin defines. What you could do you could define your own strategy.
I would invert your condition though. I think the question should be "How can I include a class from a particular JAR?". The reason is that there can be more than two JARs having the same class, and you can only include one in the end.
You can tell from where the file comes by using AssemblyUtils.sourceOfFileForMerge.
project/IncludeFromJar.scala
import sbtassembly._
import java.io.File
import sbtassembly.Plugin.MergeStrategy
class IncludeFromJar(val jarName: String) extends MergeStrategy {
val name = "includeFromJar"
def apply(args: (File, String, Seq[File])): Either[String, Seq[(File, String)]] = {
val (tmp, path, files) = args
val includedFiles = files.flatMap { f =>
val (source, _, _, isFromJar) = sbtassembly.AssemblyUtils.sourceOfFileForMerge(tmp, f)
if(isFromJar && source.getName == jarName) Some(f -> path) else None
}
Right(includedFiles)
}
}
build.sbt
mergeStrategy in assembly := {
case PathList("path", "to", "your", "DuplicatedClass.class") => new IncludeFromJar("jarname.jar")
case x => (mergeStrategy in assembly).value(x)
}
What version of sbtassembly are yall using?
I believe Im using using a different version (0.14.2) because Im getting an error using use project/IncludeFromJar.scala.
When compiling I get the following error:
method apply cannot override final member
def apply(args: (File, String, Seq[File])): Either[String, Seq[(File, String)]] = {
^
Upon further investigation I found out that sbtassembly.MergeStrategy's apply method is final. Therefore, IncludeFromJar's apply method cannot override sbtassembly.MergeStrategy's even with specifying override def apply in IncludeFromJar
Thanks :)
Probably a bit late to the discussion but to help others that find this:
when you extend MergeStrategy you want to override the method apply with the signature:
def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]]
The apply method that is final with the tuple argument, calls the apply with the parameters split out as above
https://github.com/sbt/sbt-assembly/blob/edd35cfbaf05c3465371b63d38fda8ac579d766c/src/main/scala/sbtassembly/MergeStrategy.scala#L19
So the example becomes:
def includeFromJar(val jarName: String): sbtassembly.MergeStrategy = new sbtassembly.MergeStrategy {
val name = "includeFromJar"
def apply(tmp: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = {
val includedFiles = files.flatMap { f =>
val (source, _, _, isFromJar) = sbtassembly.AssemblyUtils.sourceOfFileForMerge(tmp, f)
if(isFromJar && source.getName == jarName) Some(f -> path) else None
}
Right(includedFiles)
}
}
mergeStrategy in assembly := {
case PathList("path", "to", "your", "DuplicatedClass.class") => includeFromJar("jarname.jar")
case x => (mergeStrategy in assembly).value(x)
}
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".)