How to execute runMain from custom task? - sbt

I'm using sbt v0.13.5
In my project, I'd like to have an sbt task that can accept some input (a single string), do some work on it and spit back some output (again, a single string). I've created a helper class in my project that does this that I can call from the terminal via sbt runMain (sbt "runMain com.example.utils.ClassName someArgument").
What I'd like is to save myself some typing and have an sbt task that does this. I'd like to just be able to type sbt doThing withStuff and have a taskKey named doThing that calls runMain with the name of the class to run and whatever argument was passed in.
I know how to create tasks but I can't seem to figure out how to call runMain from a custom task definition in my build.sbt. Can anyone point me in the correct direction?

TaskKey[Unit]("myTask") := (runMain in Compile).toTask(" com.example.Main arg1 arg2").value
runMain is an InputTask. InputTask has toTask method since 0.13.1.
https://github.com/sbt/sbt/commit/9dcb8727d819fb
https://github.com/sbt/sbt/blob/v0.13.6/main/settings/src/main/scala/sbt/InputTask.scala#L33
http://www.scala-sbt.org/0.13/docs/Input-Tasks.html#Get+a+Task+from+an+InputTask

If anybody wanted to run the main class with arguments computed basing on settings, you need dynamic tasks. For example:
val myRun = taskKey[Unit]("...")
myRun := Def.taskDyn {
val appName = name.value
Def.task {
(runMain in createApi in Compile)
.toTask(s" com.softwaremill.MyMain $appName")
.value
}
}.value

Related

What is Def.task and .taskValue in SBT?

I am reading SBT docs quite thoroughly now and there is a brief mention of Def.task and taskValue but there is no explanation so far.
They say here following:
You can compute values of some tasks or settings to define or append a value for another task.
It’s done by using Def.task and taskValue as an argument to :=, +=, or ++=.
And provide following code snippet:
sourceGenerators in Compile += Def.task {
myGenerator(baseDirectory.value, (managedClasspath in Compile).value)
}.taskValue
This brings more questions than answers for me. How is that different from regular dependency of some SBT task on another task? When should I use this macro? etc.
I have also tried to check scaladoc but without any success really. That part of code is not documented well.
I think this particular example in the introductory part of documentation is unnecessarily complicated. In this example you have to use .taskValue just because sourceGenerators value type is Seq[Task[Seq[File]]], so you have to add a task to it, not the value of that task.
A simpler example for that "Tasks based on other keys’ values" is
homepage := Some(
url(s"https://github.com/${organization.value}/${name.value}")
)
In the right part of :=/+=/++=/~= operators you can use other tasks values with simple .value suffix. Writing
foo := bar.value + 1
is the same as
foo := Def.task { bar.value + 1 }.value
In this simple example it's just unnecessary, but Def.task becomes useful when you want to separate task implementation from the task key setting:
def fooTask(n: Int): Def.Initialize[Task[Int]] = Def.task {
bar.value + n
}
So Def.task allows you to write a task definition and use other tasks/settings inside (with .value). Then you can evaluate this task definition when you set the corresponding task key somewhere else (in your project settings):
foo := fooTask(5).value
But if you need to refer to the task definition itself without evaluating it, you can use .taskValue instead of .value, like in your example. See documentation on generating sources for more information about sourceGenerators.
Here are some other relevant parts of the sbt documentation about tasks:
Tasks
Tasks/Settings: Motivation
Execution semantics of tasks
Sequencing tasks

Finding a Module's path, using the Module object

What is the sane way to go from a Module object to a path to the file in which it was declared?
To be precise, I am looking for the file where the keyword module occurs.
The indirect method is to find the location of the automatically defined eval method in each module.
moduleloc(mm::Module) = first(functionloc(mm.eval, (Symbol,)))
for example
moduleloc(mm::Module) = first(functionloc(mm.eval, (Symbol,)))
using DataStructures
moduleloc(DataStructures)
Outputs:
/home/oxinabox/.julia/v0.6/DataStructures/src/DataStructures.jl
This indirect method works, but it feels like a bit of a kludge.
Have I missed some inbuilt function to do this?
I will remind answered that Modules are not the same thing as packages.
Consider the existence of submodules, or even modules that are being loaded via includeing some abolute path that is outside the package directory or loadpath.
Modules simply do not store the file location where they were defined. You can see that for yourself in their definition in C. Your only hope is to look through the bindings they hold.
Methods, on the other hand, do store their file location. And eval is the one function that is defined in every single module (although not baremodules). Slightly more correct might be:
moduleloc(mm::Module) = first(functionloc(mm.eval, (Any,)))
as that more precisely mirrors the auto-defined eval method.
If you aren't looking for a programmatic way of doing it you can use the methods function.
using DataFrames
locations = methods(DataFrames.readtable).ms
It's for all methods but it's hardly difficult to find the right one unless you have an enormous number of methods that differ only in small ways.
There is now pathof:
using DataStructures
pathof(DataStructures)
"/home/ederag/.julia/packages/DataStructures/59MD0/src/DataStructures.jl"
See also: pkgdir.
pkgdir(DataStructures)
"/home/ederag/.julia/packages/DataStructures/59MD0"
Tested with julia-1.7.3
require obviously needs to perform that operation. Looking into loading.jl, I found that finding the module path has changed a bit recently: in v0.6.0, there is a function
load_hook(prefix::String, name::String, ::Void)
which you can call "manually":
julia> Base.load_hook(Pkg.dir(), "DataFrames", nothing)
"/home/philipp/.julia/v0.6/DataFrames/src/DataFrames.jl"
However, this has changed to the better in the current master; there's now a function find_package, which we can copy:
macro return_if_file(path)
quote
path = $(esc(path))
isfile(path) && return path
end
end
function find_package(name::String)
endswith(name, ".jl") && (name = chop(name, 0, 3))
for dir in [Pkg.dir(); LOAD_PATH]
dir = abspath(dir)
#return_if_file joinpath(dir, "$name.jl")
#return_if_file joinpath(dir, "$name.jl", "src", "$name.jl")
#return_if_file joinpath(dir, name, "src", "$name.jl")
end
return nothing
end
and add a little helper:
find_package(m::Module) = find_package(string(module_name(m)))
Basically, this takes Pkg.dir() and looks in the "usual locations".
Additionally, chop in v0.6.0 doesn't take these additional arguments, which we can fix by adding
chop(s::AbstractString, m, n) = SubString(s, m, endof(s)-n)
Also, if you're not on Unix, you might want to care about the definitions of isfile_casesensitive above the linked code.
And if you're not so concerned about corner cases, maybe this is enough or can serve as a basis:
function modulepath(m::Module)
name = string(module_name(m))
Pkg.dir(name, "src", "$name.jl")
end
julia> Pkg.dir("DataStructures")
"/home/liso/.julia/v0.7/DataStructures"
Edit: I now realized that you want to use Module object!
julia> m = DataStructures
julia> Pkg.dir(repr(m))
"/home/liso/.julia/v0.7/DataStructures"
Edit2: I am not sure if you are trying to find path to module or to object defined in module (I hope that parsing path from next result is easy):
julia> repr(which(DataStructures.eval, (String,)))
"eval(x) in DataStructures at /home/liso/.julia/v0.7/DataStructures/src/DataStructures.jl:3"

How to quit/exit from file included in the terminal

What can I do within a file "example.jl" to exit/return from a call to include() in the command line
julia> include("example.jl")
without existing julia itself. quit() will just terminate julia itself.
Edit: For me this would be useful while interactively developing code, for example to include a test file and return from the execution to the julia prompt when a certain condition is met or do only compile the tests I am currently working on without reorganizing the code to much.
I'm not quite sure what you're looking to do, but it sounds like you might be better off writing your code as a function, and use a return to exit. You could even call the function in the include.
Kristoffer will not love it, but
stop(text="Stop.") = throw(StopException(text))
struct StopException{T}
S::T
end
function Base.showerror(io::IO, ex::StopException, bt; backtrace=true)
Base.with_output_color(get(io, :color, false) ? :green : :nothing, io) do io
showerror(io, ex.S)
end
end
will give a nice, less alarming message than just throwing an error.
julia> stop("Stopped. Reason: Converged.")
ERROR: "Stopped. Reason: Converged."
Source: https://discourse.julialang.org/t/a-julia-equivalent-to-rs-stop/36568/12
You have a latent need for a debugging workflow in Julia. If you use Revise.jl and Rebugger.jl you can do exactly what you are asking for.
You can put in a breakpoint and step into code that is in an included file.
If you include a file from the julia prompt that you want tracked by Revise.jl, you need to use includet(.
The keyboard shortcuts in Rebugger let you iterate and inspect variables and modify code and rerun it from within an included file with real values.
Revise lets you reload functions and modules without needing to restart a julia session to pick up the changes.
https://timholy.github.io/Rebugger.jl/stable/
https://timholy.github.io/Revise.jl/stable/
The combination is very powerful and is described deeply by Tim Holy.
https://www.youtube.com/watch?v=SU0SmQnnGys
https://youtu.be/KuM0AGaN09s?t=515
Note that there are some limitations with Revise, such as it doesn't reset global variables, so if you are using some global count or something, it won't reset it for the next run through or when you go back into it. Also it isn't great with runtests.jl and the Test package. So as you develop with Revise, when you are done, you move it into your runtests.jl.
Also the Juno IDE (Atom + uber-juno package) has good support for code inspection and running line by line and the debugging has gotten some good support lately. I've used Rebugger from the julia prompt more than from the Juno IDE.
Hope that helps.
#DanielArndt is right.
It's just create a dummy function in your include file and put all the code inside (except other functions and variable declaration part that will be place before). So you can use return where you wish. The variables that only are used in the local context can stay inside dummy function. Then it's just call the new function in the end.
Suppose that the previous code is:
function func1(...)
....
end
function func2(...)
....
end
var1 = valor1
var2 = valor2
localVar = valor3
1st code part
# I want exit here!
2nd code part
Your code will look like this:
var1 = valor1
var2 = valor2
function func1(...)
....
end
function func2(...)
....
end
function dummy()
localVar = valor3
1st code part
return # it's the last running line!
2nd code part
end
dummy()
Other possibility is placing the top variables inside a function with a global prefix.
function dummy()
global var1 = valor1
global var2 = valor2
...
end
That global variables can be used inside auxiliary function (static scope) and outside in the REPL
Another variant only declares the variables and its posterior use is free
function dummy()
global var1, var2
...
end

What are the steps necessary to pass R objects to a Rust program?

Both R and Rust can interface with C code, so I think it is very possible. I am a bit unclear about how to proceed, however.
I have read these sections looking for answers:
R-extensions System-and-foreign-language-interfaces
The Rust foreign function interface guide
But while I am well-versed in R I am not a systems programmer and confused by what the build-chain looks like for such an endeavor.
Using Rinternals.h would be ideal, but I would settle for the simpler .C interface as well.
I have been struggling on this for a while as well, but once you know how I'ts actually not that difficult.
First create a Rust library following these instructions: rust-inside-other-languages.
Here is an example Rust library:
//src/lib.rs
#[no_mangle]
pub fn kelvin_to_fahrenheit(n: f64) -> f64 {
n * 9.0/5.0 - 459.67
}
If you follow the instructions in rust-inside-other-languages, then you should be able to generate a *.so (or *.dll or .dylib, depending on your system). Let's presume this compiled file is called libtempr.so.
Now create a C++ file which will pass the functions you need to R:
//embed.cpp
extern "C" {
double kelvin_to_fahrenheit(double);
}
// [[Rcpp::export]]
double cpp_kelvin_to_fahrenheit(double k) {
double f = kelvin_to_fahrenheit(k);
return(f);
}
Now before starting R, make sure the environment variable LD_LIBRARY_PATH contains the directory where the shared object generated previously (libtempr.so) is stored. In the shell do:
$ export LD_LIBRARY_PATH=/home/sam/path/to/shared/object:$LD_LIBRARY_PATH
$ rstudio # I highly recommend using Rstudio for your R coding
Finally in Rstudo, write this file:
library(Rcpp)
Sys.setenv("PKG_LIBS"="-L/home/sam/path/to/shared/object -ltempr")
sourceCpp("/home/sam/path/to/embed.cpp", verbose = T, rebuild = T)
cpp_kelvin_to_fahrenheit(300)
Be careful that in Sys.setenv the -L option points to the directory containing your Rust shared object.
Also be careful that -l option is the name of your shared object without the lib prefix and without the .so (or whatever it is on your system) postfix.
Using Sys.setenv in R to set the LD_LIBRARY_PATH variable DOES NOT WORK. Export the variable before starting R.
The verbose option is there so that you can see what Rcpp does to compile your C++ file. Notice how the options in PKG_LIBS above are used for compiling your C++ file.
The rebuild options is there to force a rebuild of the C++ file every time you run this line of R code.
If you did everything well, then run the R file above in the interactive console and it should output 80.33 when you reach the last line.
If anything is not clear, ask in the comments, and I'll try to improve my answer.
Hope it helped :)
Final note, the base functions dyn.load and .C can be used as an alternative approach. But this requires writing a lot more boilerplate wrapper code than this approach.
If R can interface with C code, so it is no problem at all to compile shared library from Rust code which exposes C-style functions.
Then you can easily use your library as it was written in C or C++. Of course, you will not able to use Rust object and libraries directly from R, you will have to make appropriate C interface for converting their functions.
Here is how can I do that for SBCL, and I suppose it would be very similar for R:
On Rust side
Some code:
% cat experiment.rs
extern crate libc;
use libc::{c_int, c_char};
use std::{ffi, str};
#[no_mangle]
pub extern fn rust_code_string_to_int(s: *const c_char, r: *mut c_int) -> c_int {
let string = String::from_utf8_lossy(unsafe { ffi::CStr::from_ptr(s).to_bytes() });
match <isize as str::FromStr>::from_str(&*string) {
Ok(value) => { unsafe { *r = value as c_int }; 0 },
Err(_) => -1,
}
}
Then I'm making shared lib:
% rustc --crate-type dylib experiment.rs
% nm -a libexperiment.dylib | grep rust_code_string_to_int
0000000000001630 t __ZN23rust_code_string_to_int10__rust_abiE
00000000000015e0 T _rust_code_string_to_int
Next, on SBCL side
Now I'm just loading my shared lib and then I have access to my rust_code_string_to_int function:
RUST> (sb-alien:load-shared-object "libexperiment.dylib")
#P"libexperiment.dylib"
RUST> (sb-alien:with-alien ((result sb-alien:int 0))
(values (sb-alien:alien-funcall (sb-alien:extern-alien "rust_code_string_to_int"
(sb-alien:function sb-alien:int
(sb-alien:c-string :external-format :utf-8)
(sb-alien:* sb-alien:int)))
(sb-alien:make-alien-string "42")
(sb-alien:addr result))
result))
0
42

How to define a custom task in project/Build.scala in sbt?

With the following task declaration in project/Build.scala, the print task is not recognised when I type in print at an SBT console.
lazy val print = task { println("print") }
What's wrong?
You need a TaskKey for this to work that can be instantiated by using the taskKey macro:
lazy val printTask = taskKey[Unit]("print")
I recommend having a look at the corresponding documentation about tasks.
The documentation says:
The name of the val is used when referring to the task in Scala code. The string passed to the TaskKey method is used at runtime, such as at the command line
taskKey[Unit]("print") := println("print")
Then in your SBT console,
> print
print
In more complex code, you'll usually see keys separate from the settings.
val printTask = taskKey[Unit]("print")
printTask := println("print")

Resources