使用Typesafe Config的ConfigFactory在build.sbt中设置密钥设置?

时间:2014-03-26 13:56:09

标签: scala sbt

sbt.version = 0.13.1

build.sbt中我通过调用我的项目依赖项代码来分配设置键,而该代码又通过Typesafe Config的ConfigFactory进行自我配置。我的依赖项在jar的根目录中有一个reference.conf,我的项目本身在application.conf中包含一个覆盖src/main/resources

lib / dependency也是我的代码,顺便说一句。

import com.mylib.Finders
import com.myproj.sbt.Keys._

projKeyColorSetting in Compile := Finders.findColor // this calls ConfigFactory.load

seq(projSettings:_*)

构建甚至没有加载,因为它无法找到我尝试在我的lib代码中引用的第一个conf键。

我在构建文件中尝试了一些范围和类路径操作的组合,但无济于事。我假设jar的reference.conf会出现在Compile范围的类路径上,但它不能像我期望的那样工作。

我昨天花了大部分时间仔细阅读有关Classpath,Scopes,Keys,Tasks和ResourceGenerators的SBT文档 - 我的目的是执行一个依赖于projKeyColorSetting中的build.sbt设置的自定义插件如下:

lazy val projSettings = inConfig(Compile) {
    Seq(
        resourceGenerators in Compile <+= Def.task {
            val fileCreated = createColorFile(projKeyColorSetting.value)
            Seq(fileCreated)
        }
    )
 }

4 个答案:

答案 0 :(得分:7)

如果你从foo.jar获得一个类,那么ConfigFactory.load()应该在同一个jar中找到一个reference.conf。如果没有,那么有些事情是错的,但很难猜到是什么。可能是reference.conf中可能存在一些无效的语法;可能是reference.conf不在jar中;可能是reference.conf在子目录而不是jar的根目录中;很难猜到。我试着在-Dconfig.trace=loads中寻找问题(它应该告诉你配置是否尝试加载reference.conf)。你也可以自己做classLoader.getResources,看看你是否能找到没有配置的文件。 您也可以尝试ConfigFactory.parseResourcesAnySyntax("reference")并查看您的参考设置是否在那里,然后尝试直接致电ConfigFactory.load,看看您的设置是否在那里。总的来说,仔细检查所有假设并查看它出错的地方。

至于如何添加src / main / resources,两个基本策略是1)以某种方式在类路径上获取它(在这种情况下可能很难;你甚至在启动sbt之前需要它或者需要它做某种自定义ClassLoader乐趣)或者可能更实用2)用ConfigFactory.parseFile()手动加载它。

我可能会将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)
}

请注意,这涉及将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的构造函数对于这样的情况。

答案 1 :(得分:1)

我认为这是sbt中的一个错误。

以下是我对您的用例的理解以及最终的行为方式。

<强>项目/ build.properties

sbt.version=0.13.5-M2

文件夹 config-only-project 适用于包含以下两个文件的项目 - build.sbt src / main / resources / application.conf 即可。这是为了模拟内部application.conf的项目的外部依赖。

config-only-project

中的

build.sbt

libraryDependencies += "com.typesafe" % "config" % "1.2.0"
config-only-project

中的

src / main / resources / application.conf

app-name {
  hello = "Hello from Typesafe Config"
}

以下文件配置默认的plugins项目以及构建配置本身(以及因此正在调查的项目的构建)。

<强>项目/ build.sbt

lazy val configOnlyProject = uri("../config-only-project")

lazy val plugins = project in file(".") dependsOn (configOnlyProject)

<强>项目/ 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)
}

这提供了以下目录结构:

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

我无法理解的是,设置本身并不起作用 - 既不是主项目也不是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

错误如下:

> 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'

当我在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

当我切换到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

我希望我能解释一下,但对我来说似乎有太多的认知负担:(

答案 2 :(得分:1)

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

答案 3 :(得分:0)

sbt 1.4.4的问题在于,我试图在我的元版本(project/build.sbt)中添加一个依赖项,以便可以在主版本中使用它。

该库正在使用ConfigFactory.load(),该库使用了当前线程中的ClassLoader,而该线程最终成为SbtMetaClassLoader,后者没有用于元构建依赖项的加载器。

解决方法是将库更改为使用ConfigFactory.load(getClass.getClassLoader),以便它使用的是类加载器,该类加载器从库以及大概是JAR的其余部分(包括reference.conf文件)中加载类。