Skip to content

Commit

Permalink
automatically invoke codegen for test schemas (#194)
Browse files Browse the repository at this point in the history
* WIP automatically invoke codegen for test schemas

* only invoke codegen if inputs changed

* Formatter: use $projectRoot/.scalafmt.conf if available
  • Loading branch information
mpollmeier authored May 3, 2024
1 parent 9cd9eb1 commit f17ba4c
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 90 deletions.
26 changes: 25 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,20 @@ lazy val formats = project
)
)

lazy val generateDomainClassesForTestSchemas = taskKey[Unit]("generate domain classes for test schemas")

/** tests that make use of the sample schemas (and the corresponding generated domain classes) */
lazy val tests = project
.in(file("tests"))
.dependsOn(formats, testSchemasDomainClasses)
.settings(
name := "flatgraph-tests",
publish / skip := true,
libraryDependencies += "com.github.pathikrit" %% "better-files" % "3.9.2" % Test
libraryDependencies += "com.github.pathikrit" %% "better-files" % "3.9.2" % Test,
Test/compile := (Test/compile).dependsOn(testSchemas/generateDomainClassesForTestSchemas).value,
)


lazy val domainClassesGenerator_3 = project
.in(file("domain-classes-generator_3"))
.settings(
Expand Down Expand Up @@ -119,6 +123,26 @@ lazy val testSchemas = project
name := "test-schemas",
scalaVersion := scala3,
publish / skip := true,
generateDomainClassesForTestSchemas := Def.taskDyn {
/** invoking the codegen and scalafmt is expensive, so we only want to do so if the hashsum of the
* inputs (codegen implementation, build setup, test schemas, scalafmt config) is unknown or different to the
* last known one. We persist the hashsum to preserve it between sbt sessions.
*/
val lastKnownHashsumFile = target.value / "codegen-inputs-hash.md5"
def lastKnownHashsum: Option[String] = scala.util.Try(IO.read(lastKnownHashsumFile)).toOption
val inputsHashsum = FileUtils.md5(sourceDirectory.value, file("build.sbt"), (domainClassesGenerator_3/sourceDirectory).value)

if (lastKnownHashsum == Some(inputsHashsum)) {
Def.task {
streams.value.log.info("no need to regenerate domain classes for test schemas")
}
} else {
Def.task {
(Compile/runMain).toTask(s" flatgraph.testdomains.GenerateDomainClasses").value
IO.write(lastKnownHashsumFile, inputsHashsum)
}
}
}.value,
)

lazy val testSchemasDomainClasses = project
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package flatgraph.codegen

import java.nio.file.Path
import java.nio.file.{Files, Path, Paths}
import org.scalafmt.interfaces.Scalafmt
import scala.util.Try

object Formatter {
val defaultScalafmtConfig = """
|version=3.7.11
|runner.dialect=scala3
|align.preset=some
|maxColumn=120
|""".stripMargin
val defaultScalafmtConfig: String = {
Try {
os.read(os.pwd / ".scalafmt.conf")
}.toOption.getOrElse("""version=3.7.12
|runner.dialect=scala3
|preset = IntelliJ
|maxColumn=140
|align.preset=true
|""".stripMargin)
}

def run(sourceFiles: Seq[Path], scalafmtConfig: Option[Path]): Unit = {
println(s"invoking scalafmt on ${sourceFiles.size} files")
// println(s"invoking scalafmt on ${sourceFiles.size} files")
val configFile: Path =
scalafmtConfig.getOrElse(os.temp(contents = defaultScalafmtConfig, prefix = "flatgraph-scalafmt", suffix = ".conf").toNIO)

Expand Down
22 changes: 22 additions & 0 deletions project/FileUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import java.io.File
import java.nio.file.Files
import java.security.{DigestInputStream, MessageDigest}

object FileUtils {

def md5(roots: File*): String = {
val md = MessageDigest.getInstance("MD5")
roots.foreach { root =>
Files.walk(root.toPath).filter(!_.toFile.isDirectory).forEach { path =>
val dis = new DigestInputStream(Files.newInputStream(path), md)
// fully consume the inputstream
while (dis.available > 0) {
dis.read
}
dis.close
}
}
md.digest.map(b => String.format("%02x", Byte.box(b))).mkString
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,16 @@ object Accessors {
def intMandatory: Int = flatgraph.Accessors.getNodePropertySingle(node.graph, node.nodeKind, 1, node.seq(), 42: Int)
}
final class Access_Property_int_optional(val node: nodes.StoredNode) extends AnyVal {
def intOptional: Option[Int] =
flatgraph.Accessors.getNodePropertyOption[Int](node.graph, node.nodeKind, 2, node.seq)
def intOptional: Option[Int] = flatgraph.Accessors.getNodePropertyOption[Int](node.graph, node.nodeKind, 2, node.seq)
}
final class Access_Property_string_list(val node: nodes.StoredNode) extends AnyVal {
def stringList: IndexedSeq[String] =
flatgraph.Accessors.getNodePropertyMulti[String](node.graph, node.nodeKind, 3, node.seq)
def stringList: IndexedSeq[String] = flatgraph.Accessors.getNodePropertyMulti[String](node.graph, node.nodeKind, 3, node.seq)
}
final class Access_Property_string_mandatory(val node: nodes.StoredNode) extends AnyVal {
def stringMandatory: String =
flatgraph.Accessors.getNodePropertySingle(node.graph, node.nodeKind, 4, node.seq(), "<empty>": String)
def stringMandatory: String = flatgraph.Accessors.getNodePropertySingle(node.graph, node.nodeKind, 4, node.seq(), "<empty>": String)
}
final class Access_Property_string_optional(val node: nodes.StoredNode) extends AnyVal {
def stringOptional: Option[String] =
flatgraph.Accessors.getNodePropertyOption[String](node.graph, node.nodeKind, 5, node.seq)
def stringOptional: Option[String] = flatgraph.Accessors.getNodePropertyOption[String](node.graph, node.nodeKind, 5, node.seq)
}
/* accessors for concrete stored nodes end */

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import flatgraph.testdomains.generic.edges
import flatgraph.FormalQtyType

object GraphSchema extends flatgraph.Schema {
private val nodeLabels = IndexedSeq("node_a", "node_b")
val nodeKindByLabel = nodeLabels.zipWithIndex.toMap
val edgeLabels = Array("connected_to")
val edgeKindByLabel = edgeLabels.zipWithIndex.toMap
val edgePropertyAllocators: Array[Int => Array[?]] =
Array(size => Array.fill(size)("<empty>") /* label = connected_to, id = 0 */ )
private val nodeLabels = IndexedSeq("node_a", "node_b")
val nodeKindByLabel = nodeLabels.zipWithIndex.toMap
val edgeLabels = Array("connected_to")
val edgeKindByLabel = edgeLabels.zipWithIndex.toMap
val edgePropertyAllocators: Array[Int => Array[?]] = Array(size => Array.fill(size)("<empty>") /* label = connected_to, id = 0 */ )
val nodeFactories: Array[(flatgraph.Graph, Int) => nodes.StoredNode] =
Array((g, seq) => new nodes.NodeA(g, seq), (g, seq) => new nodes.NodeB(g, seq))
val edgeFactories: Array[(flatgraph.GNode, flatgraph.GNode, Int, Any) => flatgraph.Edge] =
Expand All @@ -24,9 +23,8 @@ object GraphSchema extends flatgraph.Schema {
size => new Array[String](size),
size => new Array[flatgraph.GNode](size)
)
val normalNodePropertyNames =
Array("int_list", "int_mandatory", "int_optional", "string_list", "string_mandatory", "string_optional")
val nodePropertyByLabel = normalNodePropertyNames.zipWithIndex.toMap.updated("node_b", 6)
val normalNodePropertyNames = Array("int_list", "int_mandatory", "int_optional", "string_list", "string_mandatory", "string_optional")
val nodePropertyByLabel = normalNodePropertyNames.zipWithIndex.toMap.updated("node_b", 6)
val nodePropertyDescriptors: Array[FormalQtyType.FormalQuantity | FormalQtyType.FormalType] = {
val nodePropertyDescriptors = new Array[FormalQtyType.FormalQuantity | FormalQtyType.FormalType](28)
for (idx <- Range(0, 28)) {
Expand Down Expand Up @@ -61,8 +59,7 @@ object GraphSchema extends flatgraph.Schema {
override def getEdgeKindByLabel(label: String): Int = edgeKindByLabel.getOrElse(label, flatgraph.Schema.UndefinedKind)
override def getNodePropertyNames(nodeLabel: String): Set[String] = {
nodeLabel match {
case "node_a" =>
Set("int_list", "int_mandatory", "int_optional", "string_list", "string_mandatory", "string_optional")
case "node_a" => Set("int_list", "int_mandatory", "int_optional", "string_list", "string_mandatory", "string_optional")
case "node_b" => Set("string_optional")
case _ => Set.empty
}
Expand All @@ -80,18 +77,17 @@ object GraphSchema extends flatgraph.Schema {
else null
}

override def getPropertyKindByName(label: String): Int =
nodePropertyByLabel.getOrElse(label, flatgraph.Schema.UndefinedKind)
override def getNumberOfPropertyKinds: Int = 7
override def makeNode(graph: flatgraph.Graph, nodeKind: Short, seq: Int): nodes.StoredNode =
nodeFactories(nodeKind)(graph, seq)
override def getPropertyKindByName(label: String): Int = nodePropertyByLabel.getOrElse(label, flatgraph.Schema.UndefinedKind)
override def getNumberOfPropertyKinds: Int = 7
override def makeNode(graph: flatgraph.Graph, nodeKind: Short, seq: Int): nodes.StoredNode = nodeFactories(nodeKind)(graph, seq)
override def makeEdge(src: flatgraph.GNode, dst: flatgraph.GNode, edgeKind: Short, subSeq: Int, property: Any): flatgraph.Edge =
edgeFactories(edgeKind)(src, dst, subSeq, property)
override def allocateEdgeProperty(nodeKind: Int, direction: flatgraph.Edge.Direction, edgeKind: Int, size: Int): Array[?] =
edgePropertyAllocators(edgeKind)(size)
override def getNodePropertyFormalType(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalType =
nodePropertyDescriptors(propertyOffsetArrayIndex(nodeKind, propertyKind)).asInstanceOf[FormalQtyType.FormalType]
override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity =
nodePropertyDescriptors(1 + propertyOffsetArrayIndex(nodeKind, propertyKind))
.asInstanceOf[FormalQtyType.FormalQuantity]
override def getNodePropertyFormalType(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalType = nodePropertyDescriptors(
propertyOffsetArrayIndex(nodeKind, propertyKind)
).asInstanceOf[FormalQtyType.FormalType]
override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity = nodePropertyDescriptors(
1 + propertyOffsetArrayIndex(nodeKind, propertyKind)
).asInstanceOf[FormalQtyType.FormalQuantity]
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,7 @@ object Accessors {
def stringMandatoryExact(value: String): Iterator[NodeType] = traversal match {
case init: flatgraph.misc.InitNodeIterator[flatgraph.GNode @unchecked] if init.isVirgin && init.hasNext =>
val someNode = init.next
flatgraph.Accessors
.getWithInverseIndex(someNode.graph, someNode.nodeKind, 4, value)
.asInstanceOf[Iterator[NodeType]]
flatgraph.Accessors.getWithInverseIndex(someNode.graph, someNode.nodeKind, 4, value).asInstanceOf[Iterator[NodeType]]
case _ => traversal.filter { _.stringMandatory == value }
}

Expand Down Expand Up @@ -246,9 +244,7 @@ object Accessors {
def stringOptionalExact(value: String): Iterator[NodeType] = traversal match {
case init: flatgraph.misc.InitNodeIterator[flatgraph.GNode @unchecked] if init.isVirgin && init.hasNext =>
val someNode = init.next
flatgraph.Accessors
.getWithInverseIndex(someNode.graph, someNode.nodeKind, 5, value)
.asInstanceOf[Iterator[NodeType]]
flatgraph.Accessors.getWithInverseIndex(someNode.graph, someNode.nodeKind, 5, value).asInstanceOf[Iterator[NodeType]]
case _ =>
traversal.filter { node =>
val tmp = node.stringOptional; tmp.isDefined && tmp.get == value
Expand Down Expand Up @@ -440,9 +436,7 @@ object Accessors {
def stringMandatoryExact(value: String): Iterator[NodeType] = traversal match {
case init: flatgraph.misc.InitNodeIterator[flatgraph.GNode @unchecked] if init.isVirgin && init.hasNext =>
val someNode = init.next
flatgraph.Accessors
.getWithInverseIndex(someNode.graph, someNode.nodeKind, 4, value)
.asInstanceOf[Iterator[NodeType]]
flatgraph.Accessors.getWithInverseIndex(someNode.graph, someNode.nodeKind, 4, value).asInstanceOf[Iterator[NodeType]]
case _ => traversal.filter { _.stringMandatory == value }
}

Expand Down Expand Up @@ -504,9 +498,7 @@ object Accessors {
def stringOptionalExact(value: String): Iterator[NodeType] = traversal match {
case init: flatgraph.misc.InitNodeIterator[flatgraph.GNode @unchecked] if init.isVirgin && init.hasNext =>
val someNode = init.next
flatgraph.Accessors
.getWithInverseIndex(someNode.graph, someNode.nodeKind, 5, value)
.asInstanceOf[Iterator[NodeType]]
flatgraph.Accessors.getWithInverseIndex(someNode.graph, someNode.nodeKind, 5, value).asInstanceOf[Iterator[NodeType]]
case _ =>
traversal.filter { node =>
val tmp = node.stringOptional; tmp.isDefined && tmp.get == value
Expand Down Expand Up @@ -580,9 +572,7 @@ object Accessors {
def stringOptionalExact(value: String): Iterator[NodeType] = traversal match {
case init: flatgraph.misc.InitNodeIterator[flatgraph.GNode @unchecked] if init.isVirgin && init.hasNext =>
val someNode = init.next
flatgraph.Accessors
.getWithInverseIndex(someNode.graph, someNode.nodeKind, 5, value)
.asInstanceOf[Iterator[NodeType]]
flatgraph.Accessors.getWithInverseIndex(someNode.graph, someNode.nodeKind, 5, value).asInstanceOf[Iterator[NodeType]]
case _ =>
traversal.filter { node =>
val tmp = node.stringOptional; tmp.isDefined && tmp.get == value
Expand Down Expand Up @@ -632,8 +622,7 @@ trait ConcreteStoredConversions extends ConcreteBaseConversions {
): Traversal_Property_int_list[NodeType] = new Traversal_Property_int_list(traversal.iterator)
implicit def accessPropertyIntMandatoryTraversal[NodeType <: nodes.StoredNode & nodes.StaticType[nodes.HasIntMandatoryEMT]](
traversal: IterableOnce[NodeType]
): Traversal_Property_int_mandatory[NodeType] =
new Traversal_Property_int_mandatory(traversal.iterator)
): Traversal_Property_int_mandatory[NodeType] = new Traversal_Property_int_mandatory(traversal.iterator)
implicit def accessPropertyIntOptionalTraversal[NodeType <: nodes.StoredNode & nodes.StaticType[nodes.HasIntOptionalEMT]](
traversal: IterableOnce[NodeType]
): Traversal_Property_int_optional[NodeType] = new Traversal_Property_int_optional(traversal.iterator)
Expand All @@ -642,12 +631,10 @@ trait ConcreteStoredConversions extends ConcreteBaseConversions {
): Traversal_Property_string_list[NodeType] = new Traversal_Property_string_list(traversal.iterator)
implicit def accessPropertyStringMandatoryTraversal[NodeType <: nodes.StoredNode & nodes.StaticType[nodes.HasStringMandatoryEMT]](
traversal: IterableOnce[NodeType]
): Traversal_Property_string_mandatory[NodeType] =
new Traversal_Property_string_mandatory(traversal.iterator)
): Traversal_Property_string_mandatory[NodeType] = new Traversal_Property_string_mandatory(traversal.iterator)
implicit def accessPropertyStringOptionalTraversal[NodeType <: nodes.StoredNode & nodes.StaticType[nodes.HasStringOptionalEMT]](
traversal: IterableOnce[NodeType]
): Traversal_Property_string_optional[NodeType] =
new Traversal_Property_string_optional(traversal.iterator)
): Traversal_Property_string_optional[NodeType] = new Traversal_Property_string_optional(traversal.iterator)
}

trait ConcreteBaseConversions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ object Accessors {
def name: String = flatgraph.Accessors.getNodePropertySingle(node.graph, node.nodeKind, 0, node.seq(), "": String)
}
final class Access_Property_songType(val node: nodes.StoredNode) extends AnyVal {
def songtype: Option[String] =
flatgraph.Accessors.getNodePropertyOption[String](node.graph, node.nodeKind, 1, node.seq)
def songtype: Option[String] = flatgraph.Accessors.getNodePropertyOption[String](node.graph, node.nodeKind, 1, node.seq)
}
/* accessors for concrete stored nodes end */

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ object GraphSchema extends flatgraph.Schema {
(s, d, subseq, p) => new edges.Sungby(s, d, subseq, p),
(s, d, subseq, p) => new edges.Writtenby(s, d, subseq, p)
)
val nodePropertyAllocators: Array[Int => Array[?]] =
Array(size => new Array[String](size), size => new Array[String](size))
val normalNodePropertyNames = Array("name", "songType")
val nodePropertyByLabel = normalNodePropertyNames.zipWithIndex.toMap
val nodePropertyAllocators: Array[Int => Array[?]] = Array(size => new Array[String](size), size => new Array[String](size))
val normalNodePropertyNames = Array("name", "songType")
val nodePropertyByLabel = normalNodePropertyNames.zipWithIndex.toMap
val nodePropertyDescriptors: Array[FormalQtyType.FormalQuantity | FormalQtyType.FormalType] = {
val nodePropertyDescriptors = new Array[FormalQtyType.FormalQuantity | FormalQtyType.FormalType](8)
for (idx <- Range(0, 8)) {
Expand Down Expand Up @@ -63,18 +62,17 @@ object GraphSchema extends flatgraph.Schema {
else null
}

override def getPropertyKindByName(label: String): Int =
nodePropertyByLabel.getOrElse(label, flatgraph.Schema.UndefinedKind)
override def getNumberOfPropertyKinds: Int = 2
override def makeNode(graph: flatgraph.Graph, nodeKind: Short, seq: Int): nodes.StoredNode =
nodeFactories(nodeKind)(graph, seq)
override def getPropertyKindByName(label: String): Int = nodePropertyByLabel.getOrElse(label, flatgraph.Schema.UndefinedKind)
override def getNumberOfPropertyKinds: Int = 2
override def makeNode(graph: flatgraph.Graph, nodeKind: Short, seq: Int): nodes.StoredNode = nodeFactories(nodeKind)(graph, seq)
override def makeEdge(src: flatgraph.GNode, dst: flatgraph.GNode, edgeKind: Short, subSeq: Int, property: Any): flatgraph.Edge =
edgeFactories(edgeKind)(src, dst, subSeq, property)
override def allocateEdgeProperty(nodeKind: Int, direction: flatgraph.Edge.Direction, edgeKind: Int, size: Int): Array[?] =
edgePropertyAllocators(edgeKind)(size)
override def getNodePropertyFormalType(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalType =
nodePropertyDescriptors(propertyOffsetArrayIndex(nodeKind, propertyKind)).asInstanceOf[FormalQtyType.FormalType]
override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity =
nodePropertyDescriptors(1 + propertyOffsetArrayIndex(nodeKind, propertyKind))
.asInstanceOf[FormalQtyType.FormalQuantity]
override def getNodePropertyFormalType(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalType = nodePropertyDescriptors(
propertyOffsetArrayIndex(nodeKind, propertyKind)
).asInstanceOf[FormalQtyType.FormalType]
override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity = nodePropertyDescriptors(
1 + propertyOffsetArrayIndex(nodeKind, propertyKind)
).asInstanceOf[FormalQtyType.FormalQuantity]
}
Loading

0 comments on commit f17ba4c

Please sign in to comment.