I have a Scala script that looks something like:
#!/bin/sh
PATH=${SCALA_HOME}:${PATH}
exec scala "$0" "$#"
!#
Console.println("Hello, world!")
Is there some way in Gradle to set the version of Scala to be used, have it implicitly set SCALA_HOME, and execute the script?
There is no built-in feature for this. The way to tackle this is to declare two tasks: A Copy task that downloads the Scala distribution and unpacks it to a local directory, and an Exec task that sets the SCALA_HOME environment variable to the copy task's output directory and executes your script.
Here is an example of executing Scala from Gradle. It is a nascent attempt to build a plugin using Scalafmt. Of note is how nasty it is to use static values.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.geirsson:scalafmt-core_2.11:0.4.2'
}
}
task scalafmt << {
String target = "object FormatMe { List(Split(Space, 0).withPolicy(SingleLineBlock(close)), Split(Newline, 1).withPolicy{ case Decision(t#FormatToken(_, `close`, _), s) => Decision(t, List(Split(Newline, 0)))}.withIndent(2, close, Right)) }"
def config = org.scalafmt.config.ScalafmtConfig$.MODULE$.default
def range = scala.collection.immutable.Set$EmptySet$.MODULE$
println org.scalafmt.Scalafmt.format(target, config, range).formattedCode()
}
I know this is not quite on topic but I couldn't find anywhere else to put this and I hope it is of some value to people who ended up here for the same reason I did.
Gradle has an Exec task in which you can set the environment to be passed to the new process. You could pass SCALA_HOME through that.
Related
My jenkinsfile looks like this:
stage('Build Scala Code and Generate Dockerfile') {
container('sbt') {
sh "sbt -batch myapp/docker:stage"
}
}
For certain deployments [debugging] I would like to skip tests so that the build happens faster. Is there a way to do this in sbt? I am using the sbt docker plugin.
If you are adding a boolean parameter DEBUG to tell Jenkins, that you are doing a debug deployment, you could change your stage like this:
stage('Build Scala Code and Generate Dockerfile') {
container('sbt') {
sh "sbt ${params.DEBUG ? 'set test in Test := {}' : ''} -batch myapp/docker:stage"
}
}
Edit: since don't want a parameter, this might be better for you:
stage('Build Scala Code and Generate Dockerfile') {
container('sbt') {
sh "sbt 'set test in Test := {}' -batch myapp/docker:stage"
}
}
During the sbt build of my scala program i write the current sha1 hash to a file so i can use it easily from the application. This is how it looks like in my build.sbt file:
val dummy = {
val sha1 = Process("git rev-parse HEAD").lines.head
IO.write(file("conf/version.conf"), s"""sha1="$sha1"""")
}
The problem ist that now the build has the dependency that git command line has to be installed, otherwise it will fail since it cannot execute the git command.
Is it possible in sbt to ignore an error that occurs during the build and somehow just take "unknown" as the sha1 hash? The sbt docs say something about "failures" http://www.scala-sbt.org/0.13.5/docs/Detailed-Topics/Tasks.html but i am not sure if this can be applied to my problem.
sbt files are normal Scala files in most regards. Simply assign to sha1 the result of a try / catch expression:
val sha1 = try {
Process("git rev-parse HEAD").lines.head
} catch { case e: NonFatal => "unknown" }
IO.write(file("conf/version.conf"), s"""sha1="$sha1"""")
I have following code in my plugin:
#Override
void apply(Project project) {
project.extensions.create(EXTENSION,TestExtension)
project.task("task1") << {
println "Task 1"
println(project.mmm.test)
def extension = project.extensions.findByName(EXTENSION)
println(extension.test)
}
project.task("task2",type: TestTask) {
println "Task 2 "
def extension = project.extensions.findByName(EXTENSION)
// conventionMapping.test = {extension.test}
// println(project.extensions.findByName(EXTENSION).test)
// test = "test"
}
}
In task 1 extension.test return correct value. However in task2 extension.test always return null. What I am doing wrong? Is there a better way to pass some of the extensions values as input for task? I am using gradle 1.12 with jdk 1.8 on Mac. Best Regards
Edit :correct version:
project.task("task2", type: TestTask) {
project.afterEvaluate {
def extension = project.extensions.findByName(EXTENSION)
println(project.extensions.findByName(EXTENSION).test)
test = project.extensions.findByName(EXTENSION).test
}
}
task1 prints the value at execution time (notice the <<), task2 at configuration time (before the rest of the build script after the apply plugin: ... has been evaluated). This explains why the println for task1 works as expected, and the println for task2 doesn't.
However, configuring a task at execution time is too late. Instead, a plugin needs to defer reading user-provided values until the end of the configuration phase (after build scripts have been evaluated, but before any task has been executed). There are several techniques for doing so. One of the simpler ones is to wrap any such read access with project.afterEvaluate { ... }.
Updated answer as some time has passed and Gradle evolved its concepts and its syntax in the meantime.
To use up-to-date and optimally configured task you should use following syntax:
tasks.register("task1") {
doLast {
def extension = project.extensions.findByName(EXTENSION)
println(project.extensions.findByName(EXTENSION).test)
test = project.extensions.findByName(EXTENSION).test
}
}
Task Action:
A task has both configuration and actions. When using the doLast, you
are simply using a shortcut to define an action. Code defined in the
configuration section of your task will get executed during the
configuration phase of the build regardless of what task was targeted.
Deprecated << Operator
<< was deprecated in 4.x and removed in 5.0. See task action (doLast) on how to evaluate task logic at execution phase when all extensions and configurations should be evaluated.
Task Configuration Avoidance
To avoid the cost of creating a task if this won't be executed on your invoked Gradle command TaskContainer.register(String) method.
Avoid afterEvaluate
afterEvaluate should in most cases be avoided, see #sterling's comments from the link. You have now the possibility to evaluate task action part in execution phase, and additionally you can also rely on Task Inputs/Outputs with the combination of Lazy Configuration.
I have a subproject named oppenheimer in my project. It's very simple to run this project from the sbt console.
[myproject] $ oppenheimer/run
I can also pass in a command line argument as such:
[myproject] $ oppenheimer/run migrate
[myproject] $ oppenheimer/run clean
How can I do this from build.sbt? Is it possible to define a task that does this? It would suffice to have something like this:
val customMigrate = ...
val customClean = ...
And this is so that I could use it elsewhere in the project, like such:
(test in Test) <<= (test in Test).dependsOn(customMigrate)
The answer is given in the sbt FAQ section "How can I create a custom run task, in addition to run?". Basically:
lazy val customMigrate = taskKey[Unit]("custom run task")
fullRunTask(customMigrate, Test, "foo.Main", "migrate")
I have the following Scala 2.10 script that works fine:
#!/bin/bash
classpath="${CLASSPATH}"
unset CLASSPATH
exec ${SCALA_HOME}/bin/scala -cp "${classpath}" "$0" "$#" 2>&1
!#
import stuff
But when CLASSPATH isn't unset, it fails with stuff like:
$ ./setter-for-catan.scala
./setter-for-catan.scala:12: error: not found: object play
import play.api.libs.json.JsArray
^
one error found
Why is this happening?
The scala script has a modest -debug option.
Use -Ylog-classpath to see what the compiler is using.
Use -nc to say "no compile server daemon".
Use fsc -shutdown to start over.
Package changes are anathema, so unexpected dirs in the path with package names, or old package objects, etc, cause inexplicable build problems.
Use PathResolver to dump what classpath it sees.
An empty directory with your package name can interfere with package discovery.
${SCALA_HOME}/bin/scala -cp "${classpath}" scala.tools.util.PathResolver
${SCALA_HOME}/bin/scala -cp "${classpath}" scala.tools.util.PathResolver some-args
You'll see something like:
apm#mara:~/tmp/scripts$ ./foo.sh
object Environment {
scalaHome = /media/Software/scala-2.10.1 (useJavaClassPath = true)
javaBootClassPath = <1122 chars>
javaExtDirs =
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/ext
/usr/java/packages/lib/ext
javaUserClassPath = ""
scalaExtDirs =
}
object Defaults {
scalaHome = /media/Software/scala-2.10.1
javaBootClassPath =
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/resources.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rt.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/sunrsasign.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/jsse.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/jce.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/charsets.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/netx.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/plugin.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rhino.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/jfr.jar
/usr/lib/jvm/java-7-openjdk-amd64/jre/classes
/media/Software/scala-2.10.1/lib/akka-actors.jar
/media/Software/scala-2.10.1/lib/jline.jar
/media/Software/scala-2.10.1/lib/scala-actors.jar
/media/Software/scala-2.10.1/lib/scala-actors-migration.jar
/media/Software/scala-2.10.1/lib/scala-compiler.jar
/media/Software/scala-2.10.1/lib/scala-library.jar
/media/Software/scala-2.10.1/lib/scala-partest.jar
/media/Software/scala-2.10.1/lib/scalap.jar
/media/Software/scala-2.10.1/lib/scala-reflect.jar
/media/Software/scala-2.10.1/lib/scala-swing.jar
/media/Software/scala-2.10.1/lib/typesafe-config.jar
scalaLibDirFound = Some(/media/Software/scala-2.10.1/lib)
scalaLibFound = /media/Software/scala-2.10.1/lib/scala-library.jar
scalaBootClassPath =
scalaPluginPath = /media/Software/scala-2.10.1/misc/scala-devel/plugins
}
COMMAND: 'scala some-args'
RESIDUAL: 'scala some-args'
There may be some funky state leftover from the compiler daemon. Try fsc -shutdown or scala -nc to reset the daemon.