How to log in settingKey in SBT? - scala

I would like to do some logging in SBT. I tried to get streams when initializing settingKey. However the compiler complains that A setting cannot depend on a task.
The config snippet is here.
val appConfig = settingKey[Config]("The parsed application.conf in SBT")
appConfig := {
// ...
streams.value.log.error("Cannot find application.conf. Please check if -Dconfig.file/resource is setting correctly.")
// ...
}
Is there any method to do logging in settingKey? Thanks.

The proper way to log from an sbt setting is by using the Keys.sLog setting. This setting is set up by sbt and it holds the logger that it's used by other settings in the sbt universe.
val appConfig = settingKey[Config]("The parsed application.conf in SBT")
appConfig := {
// ...
Keys.sLog.value.log.error("Cannot find application.conf. Please check if -Dconfig.file/resource is setting correctly.")
// ...
}

A setting should only hold settable data or something directly computable from other settings . streams is a task, so you can make another task depend on it and the appConfig setting. For example:
val appConfig = settingKey[File]("application.conf file")
val parsedAppConfig = taskKey[Config]("The parsed application.conf in SBT")
parsedAppConfig := {
// ...
parse(appConfig.value)
// ...
streams.value.log.error("Cannot find application.conf. Please check if -Dconfig.file/resource is setting correctly.")
// ...
}

It's possible to go around streams and create a new ConsoleLogger() directly.
val appConfig = settingKey[Config]("The parsed application.conf in SBT")
appConfig := {
// ...
val logger: Logger = ConsoleLogger()
logger.error("Cannot find application.conf. Please check if -Dconfig.file/resource is setting correctly.")
// ...
}
Caveat: if streams.value.log gets set to something other than a ConsoleLogger, your log statement will be oblivious and still log to the console. That may be acceptable for your purposes though.

Related

Pureconfig - is it possible to include in conf file another conf file?

Is it possible to include in *conf file another conf file?
Current implementation:
// db-writer.conf
writer: {
name="DatabaseWriter",
model="model1",
table-name="model1",
append=false,
create-table-file="sql/create_table_model1.sql",
source-file="abcd.csv"
}
Desired solution:
// model1.conf + others model2.conf, model3.conf..
table: {
name="model1",
table-name="model1",
create-table-file="../sql/create_table_model1.sql"
}
//db-writer.conf
import model1.conf <=== some import?
writer: {
name="DatabaseWriter",
model="model1", <=== some reference like this?
append=false,
source-file="abcd.csv"
}
Reason why I would like to have it like this is :
to reduce duplicated definitions
to pre-define user conf file which are rare modified
I guess it is not possible - if not do you have any suggestion how to separate configs & reuse them?
I'm using scala 2.12 lang and pureconfig 0.14 (can be updated to any newer)
Pureconfig uses HOCON (though some of the interpretation of things like durations differ). HOCON include is supported.
So assuming that you have model1.conf in your resources (e.g. src/main/resources), all you need in db-writer.conf is
include "model1"
HOCON-style overrides and concatenation are also supported:
writer: ${table} {
name = "DatabaseWriter"
model = "model1"
append = false
source-file = "abcd"
}

How to call an InputKey from within a Task in an SBT Plugin

I am trying to make a task execute several inputKey.
myTask := Def.taskDyn {
val outputOfMyInputKey = myInputKey.[WHAT SHOULD I DO HERE].value
Def.task {
// do something with outputOfMyInputKey
}
}
Anybody knows how to call inputKey with default parameters ?
I tried parsed , evaluated, value, inputTaskValue but none of them works.
Thanks.
Take a look at this section of the sbt docs: Get a Task from an InputTask. You can use .toTask to provide input for your input task:
myInputKey.toTask("").value
Notice that if you provide a non-empty input, it should start with a space:
myInputKey.toTask(" arg1 arg2").value

scala- error loading a properties file packed inside jar

This is a newbie question, I read a lot but I am a bit confused.
I pass a properties file from inside a Jar, configuration is read, all is fine.
I wanted to add a try catch.I tried this but it does not work because the loading does not produce an exception if the properties file is not present. Therefore 3 questions:
Is it correct to load files like this?
Does it make sense to put a try/catch since the config is inside the jar?
If so, any suggestions on how to?
var appProps : Config = ConfigFactory.load()
try {
appProps = ConfigFactory.load("application.properties")
} catch {
case e: Exception => {
log.error("application.properties file not found")
sc.stop()
System.exit(1)
}
}
Your code looks ok in general.
ConfigFactory.load("resource.config") will handle a missing resource like an empty resource. As a result you get an empty Config. So, a try-catch-block does not really make sense.
You would usually specify a fallback configuration like this:
val appProps = ConfigFactory.load(
"application.properties"
).withFallBack(
ConfigFactory.load()
)
EDIT:
The sentence
As a result you get an empty Config
Is somewhat incomplete. ConfigFactory.load(resourceBaseName: String) applies defaultReference() and defaultOverrides(). So your resulting Config object probably contains some generic environment data and is not empty.
As far as I can see, your best option to check if the resource is there and emit an error message if not, is to look up the resource yourself:
val configFile = "application.properties"
val config = Option(getClass.getClassLoader.getResource(configFile)).fold {
println(s"Config not found!")
ConfigFactory.load()
} { resource =>
ConfigFactory.load(configFile)
}

sbt package separate jars for different test types

We have just upgraded to using sbt 12.2 on our project. I would like to have our tests packaged in separate jars such as: project-unit.jar, project-integration.jar
Our current folder structure is:
src
main
...
test
scala
com.company.unit
com.company.integration
com.company.functional
Any suggestion is very much appreciated.
Thanks
I have found a way to do this by:
Create a packaging task for the appropriate test type:
val integrationPackaging = TaskKey[File]("package-integration")
Add package settings to the test settings:
lazy val integrationTestSettings = createTestSettings("integration", IntegrationTests) ++ createPackageSettings("integration", IntegrationTests, integrationPackaging)
private def createPackageSettings(testType: String, testConfiguration: Configuration, packagingTaskKey: TaskKey[sbt.File]) = {
inConfig(testConfiguration)(Defaults.testSettings) ++
Defaults.packageTaskSettings(packagingTaskKey, mappings in (Test, packageBin)) ++
(artifactName in packagingTaskKey := {(scalaversion, moduleId, artifact) => "%s_%s-%s-%s.jar" format (moduleId.name, scalaversion.binary, buildVersion, testType)}) ++
(mappings in packagingTaskKey ~= { (ms: Seq[(File,String)]) =>
ms.filter { case (file, path) =>{ !file.getName.endsWith(".class") || file.getAbsolutePath.contains(testType) }
}
})
}
Note in the create package settings, I had to:
add default test settings to ensure all test settings are inherited
add default test package task settings (using mappings in Test packageBin)
add artifactName (what you want the jar name to be)
add specific mappings for the package task key (created in step 1 above), this is the crux of it, I had to define a filter that ensures only files with the path containing the testType (e.g. "integration", "unit" or "functional") are selected and resource files (e.g. files that do not end with ".class")

SBT how to run InputTask

I am creating some custom tasks in my SBT project and need to call other tasks for that.
How can i call inputTasks from inside my tasks and support them some input?
Since you can factor your own tasks around this I'm assuming you're trying to use the run task. It took a bit of digging, but I've finally made it work; in a nutshell, this is what you do (assuming your task is named deployTask, tweak to match your needs):
deployTask <<= ( fullClasspath in Compile, runner ) map { ( classpath, runner ) =>
val logger = ConsoleLogger() // Not sure this is optimal
Run.executeTrapExit( {
Run.run( "com.sample.MainClass",
classpath map { _.data },
Seq( "option1", "option2", "..." ), // <-- Options go here
logger )( runner )
}, logger )
}
This doesn't invoke the InputTask directly (I haven't found a way to do that yet), but it at least lets you run arbitrary Java code.