Подтвердить что ты не робот

Определение задачи sbt, которая вызывает метод из кода проекта?

Я использую SBT для создания проекта scala. Я хочу определить очень простую задачу, когда при вводе generate в sbt:

sbt> generate

Он вызовет мой метод my.App.main(..) для создания чего-то.

В myproject/src/main/scala/my есть файл App.scala, а упрощенный код выглядит следующим образом:

object App {
   def main(args: Array[String]) {
       val source = readContentOfFile("mysource.txt")
       val result = convert(source)
       writeToFile(result, "mytarget.txt");
   }
   // ignore some methods here
}

Я попытался добавить следующий код в myproject/build.sbt:

lazy val generate = taskKey[Unit]("Generate my file")

generate := {
  my.App.main(Array())
}

Но он не компилируется, так как он не может найти my.App.

Затем я попытался добавить его в myproject/project/build.scala:

import sbt._
import my._

object HelloBuild extends Build {

  lazy val generate = taskKey[Unit]("Generate my file")

  generate := {
    App.main(Array())
  }

}

Но он все еще не может быть скомпилирован, что он не может найти пакет my.

Как определить такую ​​задачу в SBT?

4b9b3361

Ответ 1

В формате .sbt выполните следующие действия:

lazy val generate = taskKey[Unit]("Generate my file")

fullRunTask(generate, Compile, "my.App")

Это задокументировано на http://www.scala-sbt.org/0.13.2/docs/faq.html: "Как создать задачу пользовательского запуска в дополнение к запуску?"

Другой подход:

lazy val generate = taskKey[Unit]("Generate my file")

generate := (runMain in Compile).toTask(" my.App").value

который отлично работает в простых случаях, но не так настраивается.

Обновление: Совет Jacek для использования resourceGenerators или sourceGenerators вместо этого хорош, если он подходит для вашего прецедента - не могу сказать из вашего описания, что он делает.

Ответ 2

Другие ответы подходят к вопросу очень хорошо, но я думаю, что OP тоже может извлечь выгоду из моего:)

ОП спросил: "Я хочу определить очень простую задачу, что при вводе generate в sbt вызывается мой метод my.App.main(..) для создания чего-то". что может в конечном итоге усложнить сборку.

Sbt уже предлагает способ генерации файлов во время сборки - sourceGenerators и resourceGenerators - и я не могу заметить, что вам нужно определить отдельную задачу для этого, прочитав вопрос.

В Создание файлов (см. будущую версию документа в commit), вы можете прочитать:

sbt предоставляет стандартные привязки для добавления источника или генерации ресурсов задачи.

С помощью знания можно было бы подумать о следующем решении:

sourceGenerators in Compile += Def.task {
  my.App.main(Array()) // it not going to work without one change, though
  Seq[File]()          // a workaround before the above change is in effect
}.taskValue

Чтобы выполнить эту работу, вы должны вернуть Seq[File], который содержит файлы, сгенерированные (а не пустые Seq[File]()).

Основное изменение для работы кода - переместить класс my.App в папку project. Затем он становится частью определения сборки. Он также отражает то, что делает класс, поскольку он действительно является частью сборки, а не артефактом, являющимся его продуктом. Когда один и тот же код является частью сборки и самого артефакта, вы не разделяете разные проблемы. Если класс my.App участвует в сборке, он должен принадлежать ему - следовательно, переход в папку project.

Макет проекта будет выглядеть следующим образом:

$ tree
.
├── build.sbt
└── project
    ├── App.scala
    └── build.properties

Разделение проблем (aka @joescii in da haus)

В ответе @joescii (который я процитирую в ответе) есть пункт - "сделать его отдельным проектом, который могут использовать другие проекты. Для этого вам нужно будет поместить ваш объект App в отдельный проект и включить его как зависимость в project/project", т.е.

Предположим, что у вас есть отдельный проект build-utils с App.scala под src/main/scala. Это обычная конфигурация sbt с кодом Scala.

jacek:~/sandbox/so/generate-project-code
$ tree build-utils/
build-utils/
└── src
    └── main
        └── scala
            └── App.scala

Вы можете проверить его как обычное приложение Scala, не испортив его с помощью sbt. Никакой дополнительной настройки не требуется (и освобождает ваш разум от sbt, что может быть полезно время от времени - меньше настроек всегда помогает).

В другом проекте - project-code - который использует App.scala, который должен быть базой для сборки, build.sbt выглядит следующим образом:

Проект-код/​​build.sbt

lazy val generate = taskKey[Unit]("Generate my file")

generate := {
  my.App.main(Array())
}

Теперь самая важная часть - проводка между проектами, поэтому код App отображается для сборки project-code:

Проект-код/​​проект/build.sbt

lazy val buildUtils = RootProject(
  uri("file:/Users/jacek/sandbox/so/generate-project-code/build-utils")
)

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

С помощью определения (ов) сборки выполнение generate дает вам следующее:

jacek:~/sandbox/so/generate-project-code/project-code
$ sbt
[info] Loading global plugins from /Users/jacek/.sbt/0.13/plugins
[info] Loading project definition from /Users/jacek/sandbox/so/generate-project-code/project-code/project
[info] Updating {file:/Users/jacek/sandbox/so/generate-project-code/build-utils/}build-utils...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Updating {file:/Users/jacek/sandbox/so/generate-project-code/project-code/project/}plugins...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/jacek/sandbox/so/generate-project-code/build-utils/target/scala-2.10/classes...
[info] Set current project to project-code (in build file:/Users/jacek/sandbox/so/generate-project-code/project-code/)
> generate
Hello from App.main
[success] Total time: 0 s, completed May 2, 2014 2:54:29 PM

Я изменил код App следующим образом:

> eval "cat ../build-utils/src/main/scala/App.scala"!
package my

object App {
  def main(args: Array[String]) {
    println("Hello from App.main")
  }
}

Структура проекта выглядит следующим образом:

jacek:~/sandbox/so/generate-project-code/project-code
$ tree
.
├── build.sbt
└── project
    ├── build.properties
    └── build.sbt

Другие изменения aka goodies

Я также предлагаю некоторые другие изменения в коде генератора источника:

  • Переместите код из метода main в отдельный метод, который возвращает созданные файлы и вызовет main. Это упростит повторное использование кода в sourceGenerators (без ненужного Array(), чтобы вызвать его, а также явно возвращать файлы).
  • Используйте filter или map функции для convert (чтобы добавить более функциональный вкус).

Ответ 3

Решение, которое предлагает @SethTisue, будет работать. Другой подход - сделать его отдельным проектом, который могут использовать другие проекты. Для этого вам нужно будет поместить ваш объект App в отдельный проект и включить его как зависимость в project/project, или разместить его как плагин sbt в идеале с включенным определением этой задачи.

Для примера того, как создать lib, который упакован как плагин, посмотрите snmp4s. Каталог gen содержит код, который генерирует некоторые генерации кода (аналогично вашему коду App), а каталог sbt содержит оболочку плагина sbt для gen.