JOOQ sbt 0.3.16 to 1.2.8 migration - sbt

How migrate JOOQ building and configuration from sbt 0.3.16 to sbt 1.2.8
The JOOQ build definition in 0.3.16 as below.
val generateJOOQ = taskKey[Seq[File]]("Generate JooQ classes")
val generateJOOQTask = (baseDirectory, dependencyClasspath in Compile, runner in Compile, streams) map { (base, cp, r, s) =>
toError (r.run(
"org.jooq.util.GenerationTool",
cp.files,
Array("conf/openDoor.xml"),
s.log))
((base / "app" / "jooqSteps") ** "*.scala").get
}
generateJOOQ <<= generateJOOQTask
and the result when running sbt (1.2.8):
build.sbt:70: error: value map is not a member of (sbt.SettingKey[java.io.File], sbt.TaskKey[sbt.Keys.Classpath], sbt.TaskKey[sbt.ScalaRun], sbt.TaskKey[sbt.Keys
.TaskStreams])
val generateJOOQTask = (baseDirectory, dependencyClasspath in Compile, runner in Compile, streams) map { (base, cp, r, s) =>
^
C:\workPlay\anchor\build.sbt:71: error: not found: value toError
toError (r.run(
^
[error] sbt.compiler.EvalException: Type error in expression
Thank you for your help

According to migrating from tuple enrichments guide and dropped deprecations release notes we could migrate generateJOOQTask as follows
val generateJOOQ = taskKey[Seq[File]]("Generate JooQ classes")
generateJOOQ := {
val base = baseDirectory.value
val cp = (dependencyClasspath in Compile).value
val r = (runner in Compile).value
val s = streams.value
r.run(
"org.jooq.util.GenerationTool",
cp.files,
Array("conf/openDoor.xml"),
s.log
).failed foreach (sys error _.getMessage)
((base / "app" / "jooqSteps") ** "*.scala").get
}

Related

FlyWay plugin with sbt

I'm trying to apply FlyWay plugin by sbt build configuration.
In plugins.sbt
In my build.sbt:
lazy val CustomConfig = config("custom") extend Runtime
lazy val customSettings: Seq[Def.Setting[_]] = Seq(
flywayUser := "andrej",
flywayPassword := "123456",
flywayUrl := "jdbc:postgresql://localhost:5432/database",
flywayLocations += "db/migration"
)
lazy val flyWay = (project in file("."))
.settings(inConfig(CustomConfig)(FlywayPlugin.flywayBaseSettings(CustomConfig) ++
customSettings): _*)
In resources.db.migration-directory sql-file is created.
And trying to run migration to database it with command: sbt flywayMigrate
But it returns the following errors:
[error] Expected ';'
[error] Not a valid command: flywayMigrate
[error] No such setting/task
[error] flywayMigrate
[error] ^
Looks like you did not enable the plugin.
Add following line to your project/plugin.sbt
addSbtPlugin("io.github.davidmweber" % "flyway-sbt" % "7.4.0")
and enable the plugin in you build.sbt file:
enablePlugins(FlywayPlugin)

In a sbt project, how to get full list of dependencies with scope?

In a sbt project, how to get full list of dependencies(including transitive dependencies) with scope?
For Example:
xxx.jar(Compile)
yyy.jar(Provided)
zzz.jar(Test)
...
From the sbt shell you can execute one or all of the following commands:
Test / fullClasspath
Runtime / fullClasspath
Compile / fullClasspath
Which will output the jars associated with the scope (Test/Runtime/Compile).
If you want to get a bit more fancy, sbt provides a number of ways of interacting with the outputs generated through the dependency management system. The documentation is here.
For example, you could add this to your build.sbt file:
lazy val printReport = taskKey[Unit]("Report which jars are in each scope.")
printReport := {
val updateReport: UpdateReport = update.value
val jarFilter: ArtifactFilter = artifactFilter(`type` = "jar")
val testFilter = configurationFilter(name = "test")
val compileFilter = configurationFilter(name = "compile")
val testJars = updateReport.matching(jarFilter && testFilter)
val compileJars = updateReport.matching(jarFilter && compileFilter)
println("Test jars:\n===")
for (jar <- testJars.sorted) yield {println(jar.getName)}
println("\n\n******\n\n")
println("compile jars:\n===")
for (jar <- compileJars.sorted) yield {println(jar.getName)}
}
It creates a new task printReport which can be executed like a normal sbt command with sbt printReport. It takes the value of the UpdateReport which is generated by the update task, and then filters for jar files in the respective test/compile scopes before printing the results.

When using a Scala compiler plugin in sbt, how do you set a library dependency for the plugin?

I'm using a compiler plugin I wrote that depends on the Kyro serialization library. When attempting to use my plugin I set this up in build.sbt (top-level) like this:
lazy val dependencies =
new {
val munit = "org.scalameta" %% "munit" % "0.7.12" % Test
val kyro = "com.esotericsoftware" % "kryo" % "5.0.0-RC9"
}
lazy val commonDependencies = Seq(
dependencies.kyro,
dependencies.munit
)
lazy val root = (project in file("."))
.settings(
libraryDependencies ++= commonDependencies,
Test / parallelExecution := false
)
addCompilerPlugin("co.blocke" %% "dotty-reflection" % reflectionLibVersion)
But when I compile my target project, I get a java.lang.NoClassDefFoundError that it can't find Kyro. I've added kyro to my dependencies, but since this is for the compiler, not my app, it's not picking that up.
How can I properly tell sbt about a dependency my plugin needs?

Passing a project to an sbt dynamic task

I have a sbt project that includes code generation.
Part of the build.sbt is
lazy val generator = (project in file("generator")).
settings(mainClass := Some("com.example.Generator"))
lazy val generate = (project in file("generate")).
dependsOn(generator).
settings(runGeneration)
def runGeneration: SettingsDefinition = sourceGenerators in Compile += Def.taskDyn {
val cachedFun = FileFunction.cached(
streams.value.cacheDirectory / "generation"
) { (in: Set[File]) =>
val dir = (sourceManaged in Compile).value
(generator / run in Compile).toTask(" " + dir.getAbsolutePath).value
collectFiles(dir)
}
val dependentFiles = ((generator / fullClasspath in Compile) map { cp => cp.files }).taskValue.value
val genFiles = cachedFun(dependenctFiles).toSeq
Def.task {
genFiles
}
}.taskValue
This seems to work and only generate files when a dependency has changed. However, I expect to have multiple generators. Rather than copy the code, I attempted to pass the generator project to it:
lazy val generate = (project in file("generate")).
dependsOn(generator).
settings(runGeneration(generator))
def runGeneration(p: project): SettingsDefinition =
<same as before but with p instead of generator>
This results in an error parsing the build file:
build.sbt:155: error: Illegal dynamic reference: File
val dependentFiles = ((p / fullClasspath in Compile) map { cp => cp.files }).taskValue.value
^
[error] sbt.compiler.EvalException: Type error in expression
[error] sbt.compiler.EvalException: Type error in expression
I am guessing the problem is that it cannot figure out at compile time if there is a dependency loop, so it convervatively gives an error.
Is there a way to get this to work? Is there an entirely different construct that lets me know if running generator will produce a different result?
The underlying problem is that task definitions in sbt have two components, which look like they can be intermingled, but cannot. If you write code like
Def.task {
val doIt = checkIfShouldDoIt()
if (doIt) {
someTask.value
} else {
()
}
}
this naively looks like it will only run someTask if doIt is true. What actually happens is that someTask.value declares a dependency of this task on someTask and someTask is run before anything is done for this task. To write the above code in a way that more directly maps to what actually happens, one would write
Def.task {
val someTaskValue = someTask.value
val doIt = checkIfShouldDoIt()
if (doIt) {
someTaskValue
} else {
()
}
}
The attempt to run the task only when the dependencies had changed could not work in a single task.
My working solution does the following. I modified the generator to take an additional argument and do nothing if that argument was false. The two tasks were
// Task to check if we need to generate
def checkGeneration(p: Project) = Def.taskDyn {
var needToGenerate = false
val cachedFunction = FileFunction.cached(someDir) {
(in: Set[File]) =>
needToGenerate = ! in.isEmpty
Set()
}
val dependentFiles = ((p / fullClasspath in Compile) map { cp => cp.files }).taskValue
Def.task {
cachedFun(dependentFiles.value.toSet)
needToGenerate
}
}
// Task to run generation
def runGeneration(p: Project): SettingsDefinition = sourceGenerators in Compile += Def.taskDyn {
val needToGenerate = checkGeneration(p).value
Def.task {
// Run generator as before but pass needToGenerate as additional argument
...
// Used FileFunction.cached as before to find the generated files (but not run the generator)
...
}
}
It is possible that I have more dynamic tasks than I need, but this works.

How do I specify scope in build.sbt

I have a variable libDir defined in Global & when I try to use it inside one of the sub-projects (with specifying the scope), it fails with:
[info] Loading project definition from /Users/chichu/ws/tip/TestMain/project
References to undefined settings:
*/*:libDir from Streaming/*:install (/Users/chinchu/ws/tip/TestMain/build.sbt:124)
Did you mean TestRoot/*:libDir ?
Here's the snippet from build.sbt:
===
def Streaming(name: String, dir: String,
archiveName: String, main: String): Project = {
...
...
install := {
val jarName = assembly.value
sbt.IO.copyFile(jarName, (libDir in Global).value) // copy over the jar
}
}
..
..
lazy val installDir = SettingKeyFile
libDir := baseDirectory.value / "install/lib"
==
Why is it not able to resolve "libDir" even when I specify "in Global" ? I also tried "libDir in TestRoot" & it reports "error: not found: value TestRoot"
Thanks
-C
The specification says, that if you define a setting like this:
libDir := baseDirectory.value / "install/lib"
It will set a task and a configuration scope to Global, but a project scope will be set to current project (that is a root project in your case).
When you refer to your setting as libDir in Global, you set configuration scope to Global but still, you are in the current build, that is some project, that tries to define the install setting.
You should define your libDir like this:
libDir in Global := baseDirectory.value / "install/lib"
which will set also the project scope to Global.
Example build.sbt
lazy val installDir = settingKey[File]("ff")
lazy val install = taskKey[Unit]("prints install")
installDir in Global := baseDirectory.value / "install/lib"
val projectA = project.in(file("projectA")).settings(
install := {
val install = (installDir in Global).value
println(install)
}
)
val projectB = project
Alternative Solution
Alternatively you could give a root project an explicit id, that is in your main build.sbt add a line
val root = Project("root", file("."))
then in your install task refer to Global in that project (root is a name of the project)
(libDir in Global in root).value
Example build.sbt
lazy val installDir = settingKey[File]("ff")
lazy val install = taskKey[Unit]("prints install")
installDir := baseDirectory.value / "install/lib"
lazy val root = Project("root", file("."))
lazy val projectA = project.in(file("projectA")).settings(
install := {
val install = (installDir in Global in root).value
println(install)
}
)
lazy val projectB = project

Resources