How to specify command line arguments within build.sbt - scala

There are a number of Q&A about how to send command line args to sbt run . My question is: how to specify the command line args in a hard-coded manner within build.sbt - where we know how to specify the class itself:
mainClass in Global := Some("mypackage.MyMainClas")
We need to specify the command line parameters in a hardcoded manner in build.sbt due to our toolchain.

You can try create custom run task with the default arguments, like:
lazy val myParameters = Array("arg1", "arg3")
lazy val myRunTask = taskKey[Unit]("A custom run task.")
fullRunTask(myRunTask, Runtime, "mypackage.MyMainClas", myParameters: _*)
and run with: sbt myRunTask.
Reference:
http://www.scala-sbt.org/0.13/docs/Faq.html#How+can+I+create+a+custom+run+task%2C+in+addition+to+%3F

Related

Environment variables set up from build.sbt

I have been trying to set up environment variables directly from build.sbt file as I need to use an assembly jar name which is defined in this file. So I've been trying to output the result of defined environment variable using echo and in the context of the Scala application code sys.props.get("SPARK_APP_JAR_PATH") but the result is blank or None, respectively.
What can be wrong with the environment variable configuration?
This how I defined the env variable in build.sbt:
assemblyJarName in assembly := "spark_app_example_test.jar"
mainClass in assembly := Some("example.SparkAppExample")
test in assembly := {}
fork := true
val envVars = sys.props("SPARK_APP_JAR_PATH") = "/path/to/jar/file"
That's how it works for me:
lazy val dockerRepo: String = sys.props.getOrElse("DOCKER_REPO", s"bpf.docker.repo")
Or with your example:
lazy val envVars = sys.props.getOrElse("SPARK_APP_JAR_PATH", "/path/to/jar/file")
According to the documentation:
System properties can be provided either as JVM options, or as SBT arguments, in both cases as -Dprop=value. The following properties influence SBT execution.
e.g. sbt -DSPARK_APP_JAR_PATH=/path/to/jar/file
See https://www.scala-sbt.org/release/docs/Command-Line-Reference.html#Command+Line+Options

SBT - How can I add/modify values to application.conf file based on an external source

I read that SBT has functionality to generate source code and resource files.
In my case I want to add/modify a field in an application.conf file during compilation/packaging of the project (leaving the others in place)
For instance my application.conf file has something like:
A {
B = "Some Value"
C = "Some value to be modified"
}
I would like in the SBT to read an external file and change or add the value of A.B or A.C
So if it is possible to do something along the lines of:
build.sbt
lazy val myProject = project.in(file('myproject')
// pseudo code - How do I do this?
.sourceGenerators in Compile += "Read file /path/to/external/file and add or replace the value of application.conf A.B = some external value"
You can replace the values with environment variable values provided while compiling / building your project. For that you'd have to
A {
B = "Some Value"
B = ${?B_ENV}
C = "Some value to be modified"
C = ${?C_ENV}
}
Where B_ENV and C_ENV are the environment variables you set in your terminal either before build or within the build command (before it)
$ B_ENV=1 C_ENV=2 sbt run
Source: https://www.playframework.com/documentation/2.6.x/ProductionConfiguration#using-environment-variables
In this case you can do without sbt and this approach would also work with maven or cradle.
The *.conf support orignates from typesafe config (https://github.com/lightbend/config).
There is a feature to get environment variables to be used in the configuration which should be a good fit to solve the problem.
There are two approaches I would suggest to use
1.) Fail on missing configuration
If configuration of this vallue is important and to prevent the deplyment of misconfigurated application the startup should fail on missing environment variables.
in application.conf
key=${TEST} // expects "TEST" to be set, fails otherwise
2.) Hardcoded value with override
If there is a sensible default behaviour that only in some circumstances should be changed.
in application.conf
key="test" // hardcoded key
key=${?TEST} // override "key" with 3nv "$TEST" value, when it is given

Test initialCommands in SBT

I have a subproject in my build.sbt with a rather long setting for initialCommands, comprising a list of imports and some definitions. I'd like to test this as part of regular CI, because otherwise I won't notice breaking changes after refactoring code. It is not clear to me how to do so.
Just running sbt console doesn't seem to cut it, because there is always a "successful" exit code even when code doesn't compile.
Moving the code out into an object defined in a special source file won't help because I need the list of imports to be present (and I don't want to cakeify my whole code base).
Moving the code out into a source file and then loading that with :load also always gives a successful exit code.
I found out about scala -e but that does strange things on my machine (see the error log below).
This is Scala 2.12.
$ scala -e '1'
cat: /release: No such file or directory
Exception in thread "main" java.net.UnknownHostException: <my-host-name-here>: <my-host-name-here>: Name or service not known
You could generate a file and run it like any other test file:
(sourceGenerators in Test) += Def.task {
val contents = """object TestRepl {
{{}}
}""".replace("{{}}", (initialCommands in console).value)
val file = (sourceManaged in Test).value / "repltest.scala"
IO.write(file, contents)
Seq(file)
}.taskValue

How to pass scalacOptions (Xelide-below) to sbt via command line

I am trying to call sbt assembly from the command line passing it a scalac compiler flag to elides (elide-below 1).
I have managed to get the flag working in the build.sbt by adding this line to the build.sbt
scalacOptions ++= Seq("-Xelide-below", "1")
And also it's working fine when I start sbt and run the following:
$> sbt
$> set scalacOptions in ThisBuild ++=Seq("-Xelide-below", "0")
But I would like to know how to pass this in when starting sbt, so that my CI jobs can use it while doing different assembly targets (ie. dev/test/prod).
One way to pass the elide level as a command line option is to use system properties
scalacOptions ++= Seq("-Xelide-below", sys.props.getOrElse("elide.below", "0"))
and run sbt -Delide.below=20 assembly. Quick, dirty and easy.
Another more verbose way to accomplish the same thing is to define different commands for producing test/prod artifacts.
lazy val elideLevel = settingKey[Int]("elide code below this level.")
elideLevel in Global := 0
scalacOptions ++= Seq("-Xelide-below", elideLevel.value.toString)
def assemblyCommand(name: String, level: Int) =
Command.command(s"${name}Assembly") { s =>
s"set elideLevel in Global := $level" ::
"assembly" ::
s"set elideLevel in Global := 0" ::
s
}
commands += assemblyCommand("test", 10)
commands += assemblyCommand("prod", 1000)
and you can run sbt testAssembly prodAssembly. This buys you a cleaner command name in combination with the fact that you don't have to exit an active sbt-shell session to call for example testAssembly. My sbt-shell sessions tend to live for a long time so I personally prefer the second option.

Custom run task for subproject with arguments from build.sbt?

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")