scala 使用 Typesafe Config 的 ConfigFactory 在 build.sbt 中设置关键设置?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22663213/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Typesafe Config's ConfigFactory to set key setting in build.sbt?
提问by bloo
sbt.version=0.13.1
sbt.version= 0.13.1
In build.sbtI 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.confin the root of the jar, and my project itself contains an overriding application.confin src/main/resources.
在build.sbt我通过调用我的项目依赖项的一段代码来分配一个设置键,该代码又通过 Typesafe Config 的ConfigFactory. 我的依赖reference.conf项在 jar 的根目录中有一个,而我的项目本身application.conf在src/main/resources.
The lib/dependency is also my code, btw.
lib/依赖也是我的代码,顺便说一句。
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.
构建甚至没有加载,因为它找不到我试图在我的 lib 代码中引用的第一个 conf 键。
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.confwould have been on the Compilescope's classpath but it doesn't work as I expect.
我在我的构建文件中尝试了多种范围界定和类路径操作的组合,但无济于事。我假设 jarreference.conf会在Compile作用域的类路径上,但它不像我预期的那样工作。
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 projKeyColorSettingsetting in build.sbtas follows:
我昨天大部分时间都在研究关于类路径、作用域、键、任务和资源生成器的 SBT 文档——我的目的是执行一个依赖于projKeyColorSetting设置的自定义插件,build.sbt如下所示:
lazy val projSettings = inConfig(Compile) {
Seq(
resourceGenerators in Compile <+= Def.task {
val fileCreated = createColorFile(projKeyColorSetting.value)
Seq(fileCreated)
}
)
}
回答by Havoc P
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=loadsto 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.getResourcesand 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.loaddirectly and see if your settings are in there. Just in general, double-check all assumptions and see where it goes wrong.
如果您从 foo.jar 获取一个类,那么ConfigFactory.load()应该在同一个 jar 中找到一个 reference.conf。如果没有,则说明有问题,但很难猜测是什么。可能是 reference.conf 中有一些无效的语法;可能是 reference.conf 不在 jar 中;可能是 reference.conf 位于子目录中而不是 jar 的根目录中;很难猜。我会尝试-Dconfig.trace=loads在那里寻找问题(例如,它应该告诉您 config 是否尝试加载 reference.conf)。您也可以自己做classLoader.getResources,看看是否可以在不涉及配置的情况下找到该文件。您也可以尝试ConfigFactory.parseResourcesAnySyntax("reference")查看您的参考设置是否在那里,然后尝试调用ConfigFactory.load直接看看你的设置是否在那里。一般来说,仔细检查所有假设,看看哪里出了问题。
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().
至于如何添加 src/main/resources,两个基本策略是 1) 以某种方式将它放在类路径上(在这种情况下这可能很困难;您甚至在启动 sbt 之前就需要它,或者需要做某种事情自定义类加载器的乐趣)或者可能更实用 2)使用ConfigFactory.parseFile().
I would probably grab the resourceDirectorykey as a dependency of your task and then do something like (untested):
我可能会将resourceDirectory密钥作为您任务的依赖项,然后执行以下操作(未经测试):
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 Configparameter, 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#L22where I was trying to illustrate that when using a Configusually a library should both default to ConfigFactory.loadbut also have a constructor that allows a custom Configfor situations like this.
请注意,这涉及更改 findColor 以获取Config参数,或者您可能更愿意让 Finders 成为可以使用Config;构造的非单例。请参阅https://github.com/typesafehub/config/blob/master/examples/scala/simple-lib/src/main/scala/simplelib/SimpleLib.scala#L22上的示例,我试图在使用时说明这一点一个Config通常国会图书馆应该都默认ConfigFactory.load,但也有一个构造函数,允许自定义Config为这样的情况。
回答by Jacek Laskowski
I think it's a bug in sbt.
我认为这是 sbt 中的一个错误。
Here's my understanding of your use case and how sbt ultimately behaved.
这是我对您的用例以及 sbt 最终表现的理解。
project/build.properties
项目/build.properties
sbt.version=0.13.5-M2
The folder config-only-projectis for a project with the following two files - build.sbtand src/main/resources/application.conf. This is to simulate an external dependency on a project with application.confinside.
文件夹config-only-project用于包含以下两个文件的项目 - build.sbt和src/main/resources/application.conf。这是为了模拟对application.conf内部项目的外部依赖。
build.sbtin config-only-project
build.sbt中config-only-project
libraryDependencies += "com.typesafe" % "config" % "1.2.0"
src/main/resources/application.confin config-only-project
src/main/resources/application.conf中config-only-project
app-name {
hello = "Hello from Typesafe Config"
}
The following files configure the default pluginsproject as well as the build configuration itself (and hence the build for the project under investigation).
以下文件配置默认plugins项目以及构建配置本身(以及正在调查的项目的构建)。
project/build.sbt
项目/build.sbt
lazy val configOnlyProject = uri("../config-only-project")
lazy val plugins = project in file(".") dependsOn (configOnlyProject)
project/build.scala
项目/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 pluginsproject.
我无法理解的是设置本身不起作用 - 无论是对于主项目还是对于plugins项目。
> 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.apply(build.scala:11)
at build$$anonfun$myS.apply(build.scala:7)
at scala.Function1$$anonfun$compose.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219.apply(TypeFunctions.scala:42)
at sbt.std.Transform$$anon.work(System.scala:64)
at sbt.Execute$$anonfun$submit$$anonfun$apply.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$$anonfun$apply.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$$anonfun$submit.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$$anon$$anonfun.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$$anon.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:
当我在 Scala 控制台中执行相同的代码时,它确实起作用了:
> 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 pluginsproject it worked fine, too:
当我切换到该plugins项目时,它也运行良好:
> 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 :(
我希望我能解释一下,但对我来说认知负担似乎太大了 :(
回答by Stan
Same issue with a Play project, solved by adding a ClassLoaderparameter to ConfigFactory.parseResourcesAnySyntax()in the task definition:
与 Play 项目相同的问题,通过在任务定义中添加ClassLoader参数来解决ConfigFactory.parseResourcesAnySyntax():
import com.typesafe.config.ConfigFactory
lazy val root = (project in file(".")).
settings(
myTask := {
val cl = new java.net.URLClassLoader(Array((resourceDirectory
in Compile).value.toURI.toURL))
// load ./conf/foo.conf
val config = ConfigFactory.parseResourcesAnySyntax(cl, "foo.conf")
}
)

