събота, 16 юли 2016 г.

How to have value like baseDirectory into singleton object in Sbt?


I am currently a lamer in sbt technology. Sbt stands for Simple build tool. This is a build system that is highly developed, modern and is the de facto standard build tool in the Scala world. Currently I'm using sbt for several job tasks and last week I came across a rather unpleasant problem that probably is very easy to solve for experienced Scala and sbt developer. I am writing this article to help people with a similar problem in the future. Sbt has several global settings. One of this global settings is
baseDirectory. Depending on the scope, this is the base directory for the build, the project, the configuration, or the task. You can easy get value of project base directory from sbt console. Just type
baseDirectory and you will get the value of this setting.
georginaumov@georgi:~/Documents/hello$ sbt
[info] Loading project definition from /home/georginaumov/Documents/hello/project
[info] Set current project to hello (in build file:/home/georginaumov/Documents/hello/)
> baseDirectory
[info] /home/georginaumov/Documents/hello
>
view raw gistfile1.txt hosted with ❤ by GitHub
You can also use inspect baseDirectory and you will get additional information.
> inspect baseDirectory
[info] Setting: java.io.File = /home/georginaumov/Documents/hello
[info] Description:
[info] The base directory. Depending on the scope, this is the base directory for the build, project, configuration, or task.
[info] Provided by:
[info] {file:/home/georginaumov/Documents/hello/}root/*:baseDirectory
[info] Defined at:
[info] (sbt.Defaults) Defaults.scala:177
[info] Dependencies:
[info] *:thisProject
[info] Reverse dependencies:
[info] *:ivyPaths
[info] *:sourceDirectory
[info] *:printBaseDirectory
[info] *:runner
[info] *:unmanagedBase
[info] *:target
[info] Delegates:
[info] *:baseDirectory
[info] {.}/*:baseDirectory
[info] */*:baseDirectory
[info] Related:
[info] {.}/*:baseDirectory
>
view raw gistfile1.txt hosted with ❤ by GitHub
In sbt is a good practice to isolate long tasks into singleton object and keep build.sbt much shorter. This is very cool but in order to have some global settings into this singleton object there are several tricky things that you must to do. The same thing is true if you want to have streams available to the singleton object. My job task involved to process a certain way all the files that meet certain criteria. For this purpose, I had to have baseDirectory for project. When I tried to pass baseDirectory as a method argument in this way:
printBaseDirectory <<= streams map Tasks.printBaseDirectory(baseDirectory)
view raw gistfile1.txt hosted with ❤ by GitHub
And this:
def printBaseDirectory(baseDir: sbt.File)(streams: TaskStreams): Unit = {
streams.log.info("Here I want to print value of baseDirectory")
}
view raw gistfile1.txt hosted with ❤ by GitHub
Every time I got error like this:
error: type mismatch;
[error] Type error in expression
found : sbt.SettingKey[java.io.File]
required: sbt.File
(which expands to) java.io.File
I tried several things. For example to pass baseDirectory.value in this way:
printBaseDirectory <<= streams map Tasks.printBaseDirectory(baseDirectory.value)
view raw gistfile1.txt hosted with ❤ by GitHub
But I got errors that .value cannot be used in this way. In the end, I managed to solve the problem with using ".." into the singleton object, that means "the parent directory". But this solution is not good at all. What will happen if the location of .scala file is changed? I love stackoverflow. Sometimes you can ask and get valuable help and explanations. Also you can answer questions and thereby helping others. I asked the question and received a helpful guidance. So If you want to have baseDirectory or similar value into singleton object, you must define the object like this.
import sbt.Keys.TaskStreams
import sbt._
object Tasks {
def printBaseDirectory(streams: TaskStreams, dir: File): Unit = {
streams.log.info(dir.getAbsolutePath)
}
}
view raw gistfile1.txt hosted with ❤ by GitHub
And into build.sbt this:
lazy val printBaseDirectory: TaskKey[Unit] = TaskKey[Unit]("printBaseDirectory", "Print baseDirectory for the project", KeyRanks.ATask)
printBaseDirectory <<= (streams, baseDirectory) map Tasks.printBaseDirectory
view raw gistfile1.txt hosted with ❤ by GitHub
Or this:
lazy val printBaseDirectory: TaskKey[Unit] = TaskKey[Unit]("printBaseDirectory", "Print baseDirectory for the project", KeyRanks.ATask)
printBaseDirectory := {
Tasks.printBaseDirectory(streams.value, baseDirectory.value)
}
view raw gistfile1.txt hosted with ❤ by GitHub
I like second solution because looks much clearer.
You can also check example repository into github here. If you want you can check the stackoverflow question.

Няма коментари:

Публикуване на коментар