Использование Configafe Config Config Config для настройки ключа в build.sbt?

sbt.version = 0.13.1

В build.sbt Я назначаю ключ настройки, вызывая часть кода моего проекта, который, в свою очередь, настраивается через Configafe Config ConfigFactory. Моя зависимость имеет reference.conf в корневом банке, а сам мой проект содержит переопределение application.conf в src/main/resources.

lib/dependency также является моим кодом, btw.

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

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

seq(projSettings:_*)

Сборка даже не загружается, потому что она не может найти первый ключ conf, который я пытаюсь ссылаться в моем коде lib.

В моем файле сборки я попытался использовать несколько комбинаций областей видимости и манипуляции с Classpath, но безрезультатно. Я предположил, что jar reference.conf был бы в пути класса Compile scope, но он не работает, как я ожидаю.

Я провел большую часть вчерашней работы над документацией SBT по Classpath, Scopes, Keys, Tasks и ResourceGenerators. Я намерен выполнить собственный плагин, который полагается на параметр projKeyColorSetting в build.sbt следующим образом:

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

Ответ 1

Если вы получаете класс из foo.jar, то ConfigFactory.load() должен получить reference.conf, найденный в той же банке. Если это не так, то что-то не так, но трудно догадаться. Возможно, что reference.conf имеет в нем некоторый недопустимый синтаксис; может быть, что reference.conf не находится в банке; может быть, что reference.conf находится в подкаталоге вместо корня jar; трудно догадаться. Я бы попробовал -Dconfig.trace=loads искать проблемы там (он должен сказать вам, пытается ли config загружать reference.conf, например). Вы также можете сделать свой собственный classLoader.getResources и посмотреть, можете ли вы найти файл без использования конфигурации. Вы также можете попробовать ConfigFactory.parseResourcesAnySyntax("reference") и посмотреть, находятся ли ваши эталонные настройки, и попробовать напрямую вызвать ConfigFactory.load и посмотреть, находятся ли ваши настройки там. Как правило, дважды проверьте все допущения и посмотрите, где это происходит.

Что касается того, как добавить src/main/resources, две основные стратегии: 1) как-то получить его на пути к классу (что, вероятно, в этом случае затруднено, вам понадобится его до запуска sbt или потребуется сделать какой-то пользовательский класс ClassLoader весело) или, вероятно, более практично 2) загрузить его вручную с помощью ConfigFactory.parseFile().

Я бы, вероятно, взял ключ resourceDirectory в качестве зависимости от вашей задачи, а затем сделал что-то вроде (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)
}

Обратите внимание, что это включает в себя изменение 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 для таких ситуаций.

Ответ 2

Я думаю, что это ошибка в sbt.

Здесь мое понимание вашего прецедента и того, как sbt в конечном итоге вел себя.

Проект /build.properties

sbt.version=0.13.5-M2

Папка config-only-project предназначена для проекта со следующими двумя файлами: build.sbt и src/main/resources/application.conf. Это должно имитировать внешнюю зависимость от проекта с application.conf внутри.

build.sbt в config-only-project

libraryDependencies += "com.typesafe" % "config" % "1.2.0"

src/main/resources/application.conf в config-only-project

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

Мне хотелось бы объяснить это, но для меня это кажется слишком большой когнитивной нагрузкой: (

Ответ 3

Такая же проблема с проектом 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")
  }
)