sbt.version=0.13.1
In build.sbt
I am assigning a setting key by calling a piece of my project dependency's code that in turn configures itself via Typesafe Config's ConfigFactory
. My dependency has a reference.conf
in the root of the jar, and my project itself contains an overriding application.conf
in src/main/resources
.
The lib/dependency is also my code, btw.
import com.mylib.Finders
import com.myproj.sbt.Keys._
projKeyColorSetting in Compile := Finders.findColor // this calls ConfigFactory.load
seq(projSettings:_*)
The build doesn't even load because it can't find the first conf key I attempt to reference in my lib code.
I've tried a number of combinations of scoping and Classpath manipulation in my build file, but to no avail. I assumed that the jar's reference.conf
would have been on the Compile
scope's classpath but it doesn't work as I expect.
I spent the majority of yesterday poring over SBT documentation on Classpath, Scopes, Keys, Tasks, and ResourceGenerators - my intention is to execute a custom plugin that relies on the projKeyColorSetting
setting in build.sbt
as follows:
lazy val projSettings = inConfig(Compile) {
Seq(
resourceGenerators in Compile <+= Def.task {
val fileCreated = createColorFile(projKeyColorSetting.value)
Seq(fileCreated)
}
)
}
If you are getting a class from foo.jar, then ConfigFactory.load()
should get a reference.conf found in the same jar. If it doesn't, then something is wrong but it's hard to guess what. It could be that reference.conf has some invalid syntax in it possibly; it could be that reference.conf isn't in the jar; it could be that reference.conf is in a subdirectory instead of root of the jar; hard to guess. I'd try -Dconfig.trace=loads
to look for problems in there (it should tell you whether config tries to load the reference.conf for example). You could also do your own classLoader.getResources
and see if you can find the file without config involved.
You could also try ConfigFactory.parseResourcesAnySyntax("reference")
and see if your reference settings are in there, and try calling ConfigFactory.load
directly and see if your settings are in there. Just in general, double-check all assumptions and see where it goes wrong.
As for how to add src/main/resources, the two basic strategies would be 1) to get it on the classpath somehow (which is probably difficult in this case; you would need it before even launching sbt or would need to do some kind of custom ClassLoader fun) or probably more practical 2) load it manually with ConfigFactory.parseFile()
.
I would probably grab the resourceDirectory
key as a dependency of your task and then do something like (untested):
myTask := {
val resourceDir = (resourceDirectory in Compile).value
val appConfig = ConfigFactory.parseFile(resourceDir / "application.conf")
val config = ConfigFactory.load(appConfig) // puts reference.conf underneath
Finders.findColor(config)
}
Note that this involves changing findColor to take a Config
parameter, or maybe you would prefer to make Finders a non-singleton that can be constructed with a Config
; see the example at https://github.com/typesafehub/config/blob/master/examples/scala/simple-lib/src/main/scala/simplelib/SimpleLib.scala#L22 where I was trying to illustrate that when using a Config
usually a library should both default to ConfigFactory.load
but also have a constructor that allows a custom Config
for situations like this.
I think it's a bug in sbt.
Here's my understanding of your use case and how sbt ultimately behaved.
project/build.properties
sbt.version=0.13.5-M2
The folder config-only-project is for a project with the following two files - build.sbt and src/main/resources/application.conf. This is to simulate an external dependency on a project with application.conf
inside.
build.sbt in config-only-project
libraryDependencies += "com.typesafe" % "config" % "1.2.0"
src/main/resources/application.conf in config-only-project
app-name {
hello = "Hello from Typesafe Config"
}
The following files configure the default plugins
project as well as the build configuration itself (and hence the build for the project under investigation).
project/build.sbt
lazy val configOnlyProject = uri("../config-only-project")
lazy val plugins = project in file(".") dependsOn (configOnlyProject)
project/build.scala
import sbt._
import Keys._
import com.typesafe.config._
object build extends Build {
lazy val mySetting = taskKey[String]("Setting using Typesafe Config")
lazy val myS = mySetting := {
// Compiler issue Config conf???
println((fullClasspath in Compile).value)
val conf = ConfigFactory.load()
conf getString "app-name.hello"
}
lazy val configOnlyProject = uri("config-only-project")
lazy val root = project in file(".") settings (myS) dependsOn (configOnlyProject)
}
This gives the following directory structure:
jacek:~/sandbox/so/setting-typesafe-config
$ tree
.
├── config-only-project
│ ├── build.sbt
│ ├── project
│ └── src
│ └── main
│ └── resources
│ └── application.conf
└── project
├── application.conf
├── build.properties
├── build.sbt
└── build.scala
6 directories, 6 files
What I couldn't understand was that the setting itself didn't work - neither for the main project nor for the plugins
project.
> mySetting
List(Attributed(/Users/jacek/sandbox/so/setting-typesafe-config/target/scala-2.10/classes), Attributed(/Users/jacek/sandbox/so/setting-typesafe-config/config-only-project/target/scala-2.10/classes), Attributed(/Users/jacek/.sbt/boot/scala-2.10.4/lib/scala-library.jar), Attributed(/Users/jacek/.ivy2/cache/com.typesafe/config/bundles/config-1.2.0.jar))
[trace] Stack trace suppressed: run last root/*:mySetting for the full output.
[error] (root/*:mySetting) com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'app-name'
[error] Total time: 0 s, completed Mar 31, 2014 10:24:12 PM
The error was as follows:
> last root/*:mySetting
com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'app-name'
at com.typesafe.config.impl.SimpleConfig.findKey(SimpleConfig.java:124)
at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:147)
at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:159)
at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:164)
at com.typesafe.config.impl.SimpleConfig.getString(SimpleConfig.java:206)
at build$$anonfun$myS$1.apply(build.scala:11)
at build$$anonfun$myS$1.apply(build.scala:7)
at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:42)
at sbt.std.Transform$$anon$4.work(System.scala:64)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:744)
[error] (root/*:mySetting) com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'app-name'
It did work when I executed the same code in the Scala console:
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import com.typesafe.config._
import com.typesafe.config._
scala> ConfigFactory.load()
res0: com.typesafe.config.Config = ...
scala> res0 getString "app-name.hello"
res1: String = Hello from Typesafe Config
When I switched to the plugins
project it worked fine, too:
> reload plugins
[info] Loading project definition from /Users/jacek/sandbox/so/setting-typesafe-config/project
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import com.typesafe.config._
import com.typesafe.config._
scala> ConfigFactory.load()
res0: com.typesafe.config.Config = ...
scala> res0 getString "app-name.hello"
res1: String = Hello from Typesafe Config
I wish I could explain it, but it seems too much cognitive load for me :(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With