Skip to content

Commit b04acc8

Browse files
authored
generateDomainClassesCheck: analogous to scalafmtCheck: safety net for PRs (#239)
* upgrade * documentation text updates * generateDomainClassesCheck: analogous to `scalafmtCheck` Additional fixes: * fix FileUtils.deleteRecursively * generateDomainClasses now deletes it's target directory - for real this time * readme * fix tests: allow to 'not delete' the codegen target dir... ...now that 'deleteRecursively' works :) * run scalafmt to avoid build warnings...
1 parent 6123419 commit b04acc8

File tree

11 files changed

+86
-37
lines changed

11 files changed

+86
-37
lines changed

Diff for: .github/workflows/pr.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: pr
22
on: pull_request
33
jobs:
44
pr:
5-
runs-on: ubuntu-22.04
5+
runs-on: ubuntu-latest
66
steps:
7-
- uses: actions/checkout@v2
7+
- uses: actions/checkout@v3
88
with:
99
fetch-depth: 1
1010
- uses: olafurpg/setup-scala@v10

Diff for: .github/workflows/release.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ on:
66
jobs:
77
release:
88
concurrency: release
9-
runs-on: ubuntu-22.04
9+
runs-on: ubuntu-latest
1010
steps:
11-
- uses: actions/checkout@v2
11+
- uses: actions/checkout@v3
1212
with:
1313
fetch-depth: 0
1414
- uses: olafurpg/setup-scala@v10

Diff for: README.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@
22

33
# overflowdb-codegen
44

5-
## Configuration overview: TODO
5+
## generateDomainClasses
6+
Freshly generates the domain classes to the configured location. If both the schema as well as the dependency tree (which includes the codegen plugin) are unchanged, it will not run. This is to avoid long build times due to unnecessary regeneration and recompilation for consecutive `sbt compile` runs.
7+
8+
Note: the regular `sbt clean` does not touch/delete/handle the codegen-generated sources. This is on purpose, since the idea is to have the generated domain classes committed to the repository.
9+
10+
## generateDomainClassesCheck
11+
Fails if domain classes are not generated with the latest versions. Analogous to `scalafmtCheck`, i.e. run this on PRs.
12+
13+
## Example repositories / builds
14+
* TODO
615

716
## Disable temporarily
817
You can temporarily disable the codegen in your build by setting the environment variable `ODB_CODEGEN_DISABLE=true`. That's useful e.g. if you made some manual changes to the generated files that would otherwise be overridden by the codegen.

Diff for: codegen/src/main/scala/overflowdb/codegen/CodeGen.scala

+12-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ class CodeGen(schema: Schema) {
3131
this
3232
}
3333

34-
def run(outputDir: java.io.File): Seq[java.io.File] = {
34+
def run(outputDir: java.io.File, deleteExistingFiles: Boolean = true): Seq[java.io.File] = {
35+
println(s"writing domain classes to $outputDir")
36+
if (deleteExistingFiles && outputDir.exists)
37+
deleteRecursively(outputDir)
38+
3539
warnForDuplicatePropertyDefinitions()
3640
val _outputDir = outputDir.toScala
3741
val results =
@@ -1938,6 +1942,13 @@ class CodeGen(schema: Schema) {
19381942
|$src
19391943
|""".stripMargin)
19401944
}
1945+
1946+
private def deleteRecursively(file: java.io.File): Unit = {
1947+
if (file.isDirectory)
1948+
file.listFiles.foreach(deleteRecursively)
1949+
if (file.exists)
1950+
file.delete()
1951+
}
19411952
}
19421953

19431954
object CodeGen {

Diff for: codegen/src/main/scala/overflowdb/codegen/Formatter.scala

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ object Formatter {
1313
|""".stripMargin
1414

1515
def run(sourceFiles: Seq[File], scalafmtConfig: Option[File]): Unit = {
16+
println(s"invoking scalafmt on ${sourceFiles.size} files")
1617
val configFile: File = scalafmtConfig.getOrElse(
1718
Files
1819
.createTempFile("overflowdb-scalafmt", ".conf")

Diff for: integration-tests/schemas/build.sbt

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@ generateDomainClasses := Def.taskDyn {
1414

1515
if (outputRoot.exists && lastSchemaMd5 == Some(currentSchemaMd5)) {
1616
Def.task {
17-
// inputs did not change, don't regenerate
17+
streams.value.log.info("no need to run codegen, inputs did not change")
1818
FileUtils.listFilesRecursively(outputRoot)
1919
}
2020
} else {
2121
Def.task {
22-
FileUtils.deleteRecursively(outputRoot)
2322
val invoked = (Compile/runMain).toTask(s" CodegenForAllSchemas").value
2423
lastSchemaMd5(currentSchemaMd5)
2524
FileUtils.listFilesRecursively(outputRoot)

Diff for: integration-tests/schemas/src/main/scala/CodegenForAllSchemas.scala

+10-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ object CodegenForAllSchemas {
1010
else "scala-2.13"
1111

1212
val outputDir = new File(s"integration-tests/schemas/target/$scalaVersion/odb-codegen")
13+
println(s"running CodegenForAllSchemas; deleting outputDir first: $outputDir")
14+
deleteRecursively(outputDir)
1315

1416
Seq(
1517
new TestSchema01,
@@ -21,7 +23,7 @@ object CodegenForAllSchemas {
2123
new TestSchema05,
2224
new TestSchema06,
2325
).foreach { schema =>
24-
new CodeGen(schema.instance).disableScalafmt.run(outputDir)
26+
new CodeGen(schema.instance).run(outputDir, deleteExistingFiles = false)
2527
}
2628
}
2729

@@ -33,4 +35,11 @@ object CodegenForAllSchemas {
3335
case u: java.net.URLClassLoader => u.getURLs() ++ classpathUrls(cl.getParent)
3436
case _ => Array.empty
3537
}
38+
39+
private def deleteRecursively(file: File): Unit = {
40+
if (file.isDirectory)
41+
file.listFiles.foreach(deleteRecursively)
42+
if (file.exists && !file.delete())
43+
throw new java.io.IOException(s"Unable to delete ${file.getAbsolutePath}")
44+
}
3645
}

Diff for: project/FileUtils.scala

+3-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import java.io.File
22
import java.nio.file.Files
33
import java.security.{DigestInputStream, MessageDigest}
4-
import scala.collection.JavaConverters._
4+
import scala.jdk.CollectionConverters.*
55

66
object FileUtils {
77

@@ -11,24 +11,13 @@ object FileUtils {
1111
}
1212
}
1313

14-
def deleteRecursively(root: File): Unit = {
15-
if (root.exists) {
16-
Files.walk(root.toPath).iterator.asScala.map(_.toFile).collect {
17-
case file if (file.isDirectory) => deleteRecursively(file)
18-
case file => file.delete()
19-
}
20-
}
21-
}
22-
2314
def md5(roots: File*): String = {
2415
val md = MessageDigest.getInstance("MD5")
2516
roots.foreach { root =>
2617
Files.walk(root.toPath).filter(!_.toFile.isDirectory).forEach { path =>
2718
val dis = new DigestInputStream(Files.newInputStream(path), md)
28-
// fully consume the inputstream
29-
while (dis.available > 0) {
30-
dis.read
31-
}
19+
// fully consume the InputStream
20+
while (dis.available > 0) dis.read
3221
dis.close
3322
}
3423
}

Diff for: project/build.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=1.9.2
1+
sbt.version=1.9.3

Diff for: sbt-overflowdb/src/main/scala/overflowdb/codegen/sbt/FileUtils.scala

+5-7
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ object FileUtils {
1313
}
1414
}
1515

16-
def deleteRecursively(root: File): Unit = {
17-
if (root.exists) {
18-
Files.walk(root.toPath).iterator.asScala.map(_.toFile).collect {
19-
case file if (file.isDirectory) => deleteRecursively(file)
20-
case file => file.delete()
21-
}
22-
}
16+
def deleteRecursively(file: File): Unit = {
17+
if (file.isDirectory)
18+
file.listFiles.foreach(deleteRecursively)
19+
if (file.exists)
20+
file.delete()
2321
}
2422

2523
def md5(roots: File*): String = {

Diff for: sbt-overflowdb/src/main/scala/overflowdb/codegen/sbt/OdbCodegenSbtPlugin.scala

+39-6
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import scala.util.Try
1111
object OdbCodegenSbtPlugin extends AutoPlugin {
1212

1313
object autoImport {
14-
val generateDomainClasses = taskKey[File]("generate overflowdb domain classes for the given schema - return value is the output root directory")
14+
val generateDomainClasses = taskKey[File]("regenerates domain classes for the given schema; return value is the output root directory")
15+
val generateDomainClassesCheck = taskKey[Unit]("Fails if domain classes are not generated with the latest versions. Analogous to `scalafmtCheck`, i.e. run this on PRs.")
1516
val outputDir = settingKey[File]("target directory for the generated domain classes, e.g. `Projects.domainClasses/scalaSource`")
1617
val classWithSchema = settingKey[String]("class with schema field, e.g. `org.example.MyDomain$`")
1718
val fieldName = settingKey[String]("(static) field name for schema within the specified `classWithSchema` with schema field, e.g. `org.example.MyDomain$`")
18-
val disableFormatting = settingKey[Boolean]("disable scalafmt formatting")
19+
val disableFormatting = settingKey[Boolean]("disable automatic scalafmt invocation")
1920

2021
lazy val baseSettings: Seq[Def.Setting[_]] = Seq(
2122
generateDomainClasses := generateDomainClassesTask.value,
23+
generateDomainClassesCheck := generateDomainClassesCheckTask.value,
2224
generateDomainClasses/disableFormatting := false,
2325
)
2426
}
@@ -32,7 +34,7 @@ object OdbCodegenSbtPlugin extends AutoPlugin {
3234
// a group of settings that are automatically added to projects.
3335
override val projectSettings = inConfig(Compile)(autoImport.baseSettings)
3436

35-
lazy val generateDomainClassesTask =
37+
lazy val generateDomainClassesTask = {
3638
Def.taskDyn {
3739
val classWithSchemaValue = (generateDomainClasses/classWithSchema).value
3840
val fieldNameValue = (generateDomainClasses/fieldName).value
@@ -49,7 +51,7 @@ object OdbCodegenSbtPlugin extends AutoPlugin {
4951
}
5052

5153
val schemaAndDependenciesHashFile = target.value / "overflowdb-schema-and-dependencies.md5"
52-
val dependenciesFile = target.value / "dependenciesCP.txt"
54+
val dependenciesFile = target.value / "dependenciesCP.txt" // includes codegen version!
5355
IO.write(dependenciesFile, dependencyClasspath.value.mkString(System.lineSeparator))
5456
lazy val currentSchemaAndDependenciesHash =
5557
FileUtils.md5(sourceDirectory.value, baseDirectory.value/"build.sbt", dependenciesFile)
@@ -63,11 +65,10 @@ object OdbCodegenSbtPlugin extends AutoPlugin {
6365
outputDirValue
6466
}
6567
} else if (outputDirValue.exists && lastSchemaAndDependenciesHash == Some(currentSchemaAndDependenciesHash)) {
66-
// inputs did not change, don't regenerate
68+
streams.value.log.info("inputs did not change -> no need to run codegen")
6769
Def.task { outputDirValue }
6870
} else {
6971
Def.task {
70-
FileUtils.deleteRecursively(outputDirValue)
7172
(Compile/runMain).toTask(
7273
s" overflowdb.codegen.Main --classWithSchema=$classWithSchemaValue --field=$fieldNameValue --out=$outputDirValue $disableFormattingParamMaybe $scalafmtConfigFileMaybe"
7374
).value
@@ -76,6 +77,38 @@ object OdbCodegenSbtPlugin extends AutoPlugin {
7677
}
7778
}
7879
}
80+
}
81+
82+
lazy val generateDomainClassesCheckTask = {
83+
Def.taskDyn {
84+
streams.value.log.info("generateDomainClassesCheck: running codegen for comparison")
85+
val classWithSchemaValue = (generateDomainClasses/classWithSchema).value
86+
val fieldNameValue = (generateDomainClasses/fieldName).value
87+
val outputDirValue = (generateDomainClasses/outputDir).value
88+
val tempOutputDir = target.value / "generate-domain-classes-check"
89+
90+
val disableFormattingParamMaybe =
91+
if ((generateDomainClasses/disableFormatting).value) "--noformat"
92+
else ""
93+
94+
val scalafmtConfigFileMaybe = {
95+
val file = (generateDomainClasses/scalafmtConfig).value
96+
if (file.exists) s"--scalafmtConfig=$file"
97+
else ""
98+
}
99+
100+
Def.task {
101+
(Compile/runMain).toTask(
102+
s" overflowdb.codegen.Main --classWithSchema=$classWithSchemaValue --field=$fieldNameValue --out=$tempOutputDir $disableFormattingParamMaybe $scalafmtConfigFileMaybe"
103+
).value
104+
105+
val generatedSourcesIdentical = FileUtils.md5(outputDirValue) == FileUtils.md5(tempOutputDir)
106+
if (!generatedSourcesIdentical) {
107+
throw new MessageOnlyException("there are differences in the generated and the existing sources, please run `generateDomainClasses` to fix this")
108+
}
109+
}
110+
}
111+
}
79112

80113

81114
}

0 commit comments

Comments
 (0)