Skip to content

Commit

Permalink
Merge pull request #213 from regiskuckaertz/rk-caching
Browse files Browse the repository at this point in the history
fix: use FileFunction.cached
  • Loading branch information
blast-hardcheese committed Jun 12, 2023
2 parents 0ff7cd6 + 1f243e6 commit 26db8e2
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 85 deletions.
37 changes: 13 additions & 24 deletions modules/core/src/main/scala/AbstractCodegenPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dev.guardrail
package sbt

import _root_.sbt.{Keys => SbtKeys, _}
import _root_.sbt.{Keys => SbtKeys, Types => _, _}
import _root_.sbt.plugins.JvmPlugin
import dev.guardrail.runner.GuardrailRunner
import dev.guardrail.terms.protocol.PropertyRequirement
Expand Down Expand Up @@ -188,34 +188,23 @@ trait AbstractGuardrailPlugin extends GuardrailRunner { self: AutoPlugin =>
}

private def cachedGuardrailTask(projectName: String, scope: String, scalaBinaryVersion: String)(kind: String, streams: _root_.sbt.Keys.TaskStreams)(tasks: List[(String, Args)], sources: Seq[java.io.File]) = {
import _root_.sbt.util.CacheImplicits._

if (BuildInfo.organization == "com.twilio" && tasks.nonEmpty) {
streams.log.warn(s"""${projectName} / ${scope}: sbt-guardrail has changed organizations! Please change "com.twilio" to "dev.guardrail" to continue receiving updates""")
}

def calcResult() =
GuardrailAnalysis(BuildInfo.version, Tasks.guardrailTask(guardrailRunner)(tasks, sources.head).toList)

val cachedResult = Tracked.lastOutput[Unit, GuardrailAnalysis](streams.cacheStoreFactory.sub("guardrail").sub(scalaBinaryVersion).sub(kind).make("last")) {
(_, prev) =>
val tracker = Tracked.inputChanged[String, GuardrailAnalysis](streams.cacheStoreFactory.sub("guardrail").sub(scalaBinaryVersion).sub(kind).make("input")) {
(changed: Boolean, in: String) =>
prev match {
case None => calcResult()
case Some(prevResult) =>
if (changed) {
calcResult()
} else prevResult
}

val inputFiles = tasks.flatMap(_._2.specPath).map(file(_)).toSet
val cacheDir = streams.cacheDirectory / "guardrail" / scalaBinaryVersion / kind

val cachedFn = _root_.sbt.util.FileFunction
.cached(cacheDir, inStyle = FilesInfo.hash, outStyle = FilesInfo.hash) {
_ =>
GuardrailAnalysis(
BuildInfo.version,
Tasks.guardrailTask(guardrailRunner)(tasks, sources.head)
).products
}

val inputs = tasks.flatMap(_._2.specPath.map( x => (FileInfo.hash(new java.io.File(x)))))

tracker(new String(inputs.flatMap(_.hash).toArray))
}

cachedResult(()).products
cachedFn(inputFiles).toSeq
}

def scopedSettings(name: String, scope: Configuration) = {
Expand Down
4 changes: 2 additions & 2 deletions modules/core/src/main/scala/GuardrailAnalysis.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import _root_.sbt._
import _root_.sbt.util.CacheImplicits._
import sjsonnew.{ :*:, LList, LNil}

case class GuardrailAnalysis(guardrailVersion: String, products: List[java.io.File]) {
case class GuardrailAnalysis(guardrailVersion: String, products: Set[java.io.File]) {
def ++(that: GuardrailAnalysis): GuardrailAnalysis =
GuardrailAnalysis(guardrailVersion, products ++ that.products)
}
object GuardrailAnalysis {

private val from: (String :*: List[java.io.File] :*: LNil) => GuardrailAnalysis = {
private val from: (String :*: Set[java.io.File] :*: LNil) => GuardrailAnalysis = {
case ((_, version) :*: (_, in) :*: LNil) => GuardrailAnalysis(version, in)
}

Expand Down
119 changes: 60 additions & 59 deletions modules/core/src/main/scala/Tasks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class CodegenFailedException extends FeedbackProvidedException
object Tasks {
def guardrailTask(
runner: Map[String,NonEmptyList[ArgsImpl]] => Target[List[Path]]
)(tasks: List[Types.Args], sourceDir: File): Seq[File] = {
)(tasks: List[Types.Args], sourceDir: File): Set[File] = {
// swagger-parser uses SPI to find extensions on the classpath (by default, only the OAPI2 -> OAPI3 converter)
// See https://github.com/swagger-api/swagger-parser#extensions
// That being said, Scala's classloader seems to have some issues finding SPI resources:
Expand All @@ -27,66 +27,67 @@ object Tasks {
val oldClassLoader = Thread.currentThread().getContextClassLoader()
Thread.currentThread().setContextClassLoader(classOf[SwaggerParserExtension].getClassLoader)

val preppedTasks: Map[String, NonEmptyList[ArgsImpl]] = tasks.foldLeft(Map.empty[String, NonEmptyList[ArgsImpl]]) { case (acc, (language, args)) =>
val prepped = args.copy(outputPath=Some(sourceDir.getPath))
acc.updated(language, acc.get(language).fold(NonEmptyList.one(prepped))(_ :+ prepped))
}
try {
val preppedTasks: Map[String, NonEmptyList[ArgsImpl]] = tasks.foldLeft(Map.empty[String, NonEmptyList[ArgsImpl]]) { case (acc, (language, args)) =>
val prepped = args.copy(outputPath=Some(sourceDir.getPath))
acc.updated(language, acc.get(language).fold(NonEmptyList.one(prepped))(_ :+ prepped))
}

val /*(logger,*/ paths/*)*/ =
runner
.apply(preppedTasks)
.fold[List[java.nio.file.Path]]({
case MissingArg(args, Error.ArgName(arg)) =>
println(s"${AnsiColor.RED}Missing argument:${AnsiColor.RESET} ${AnsiColor.BOLD}${arg}${AnsiColor.RESET} (In block ${args})")
throw new CodegenFailedException()
case MissingDependency(name) =>
println(s"""${AnsiColor.RED}Missing dependency:${AnsiColor.RESET} ${AnsiColor.BOLD}libraryDependencies += "dev.guardrail" %% "${name}" % "<check latest version>"${AnsiColor.RESET}""")
throw new CodegenFailedException()
case NoArgsSpecified =>
List.empty
case NoFramework =>
println(s"${AnsiColor.RED}No framework specified${AnsiColor.RESET}")
throw new CodegenFailedException()
case PrintHelp =>
List.empty
case UnknownArguments(args) =>
println(s"${AnsiColor.RED}Unknown arguments: ${args.mkString(" ")}${AnsiColor.RESET}")
throw new CodegenFailedException()
case UnparseableArgument(name, message) =>
println(s"${AnsiColor.RED}Unparseable argument ${name}: ${message}${AnsiColor.RESET}")
throw new CodegenFailedException()
case UnknownFramework(name) =>
println(s"${AnsiColor.RED}Unknown framework specified: ${name}${AnsiColor.RESET}")
throw new CodegenFailedException()
case RuntimeFailure(message) =>
println(s"${AnsiColor.RED}Error:${AnsiColor.RESET}${message}")
throw new CodegenFailedException()
case UserError(message) =>
println(s"${AnsiColor.RED}Error:${AnsiColor.RESET}${message}")
throw new CodegenFailedException()
case MissingModule(section, choices) =>
println(s"${AnsiColor.RED}Error: Missing module ${section}. Options are: ${choices.mkString(", ")}${AnsiColor.RESET}")
throw new CodegenFailedException()
case ModuleConflict(section) =>
println(s"${AnsiColor.RED}Error: Too many modules specified for ${section}${AnsiColor.RESET}")
throw new CodegenFailedException()
case UnspecifiedModules(choices) =>
val result =
choices.toSeq.sortBy(_._1).foldLeft(Seq.empty[String]) { case (acc, (module, choices)) =>
val nextLabel = Option(choices).filter(_.nonEmpty).fold("<no choices found>")(_.toSeq.sorted.mkString(", "))
acc :+ s" ${AnsiColor.BOLD}${AnsiColor.WHITE}${module}:${AnsiColor.RESET} [${AnsiColor.BLUE}${nextLabel}${AnsiColor.RESET}]"
}
println(s"${AnsiColor.RED}Unsatisfied module(s):${AnsiColor.RESET}")
result.foreach(println)
throw new CodegenFailedException()
case UnusedModules(unused) =>
println(s"${AnsiColor.RED}Unused modules specified:${AnsiColor.RESET} ${unused.toList.mkString(", ")}")
throw new CodegenFailedException()
}, identity)
//.runEmpty
val /*(logger,*/ paths/*)*/ =
runner
.apply(preppedTasks)
.fold[List[java.nio.file.Path]]({
case MissingArg(args, Error.ArgName(arg)) =>
println(s"${AnsiColor.RED}Missing argument:${AnsiColor.RESET} ${AnsiColor.BOLD}${arg}${AnsiColor.RESET} (In block ${args})")
throw new CodegenFailedException()
case MissingDependency(name) =>
println(s"""${AnsiColor.RED}Missing dependency:${AnsiColor.RESET} ${AnsiColor.BOLD}libraryDependencies += "dev.guardrail" %% "${name}" % "<check latest version>"${AnsiColor.RESET}""")
throw new CodegenFailedException()
case NoArgsSpecified =>
List.empty
case NoFramework =>
println(s"${AnsiColor.RED}No framework specified${AnsiColor.RESET}")
throw new CodegenFailedException()
case PrintHelp =>
List.empty
case UnknownArguments(args) =>
println(s"${AnsiColor.RED}Unknown arguments: ${args.mkString(" ")}${AnsiColor.RESET}")
throw new CodegenFailedException()
case UnparseableArgument(name, message) =>
println(s"${AnsiColor.RED}Unparseable argument ${name}: ${message}${AnsiColor.RESET}")
throw new CodegenFailedException()
case UnknownFramework(name) =>
println(s"${AnsiColor.RED}Unknown framework specified: ${name}${AnsiColor.RESET}")
throw new CodegenFailedException()
case RuntimeFailure(message) =>
println(s"${AnsiColor.RED}Error:${AnsiColor.RESET}${message}")
throw new CodegenFailedException()
case UserError(message) =>
println(s"${AnsiColor.RED}Error:${AnsiColor.RESET}${message}")
throw new CodegenFailedException()
case MissingModule(section, choices) =>
println(s"${AnsiColor.RED}Error: Missing module ${section}. Options are: ${choices.mkString(", ")}${AnsiColor.RESET}")
throw new CodegenFailedException()
case ModuleConflict(section) =>
println(s"${AnsiColor.RED}Error: Too many modules specified for ${section}${AnsiColor.RESET}")
throw new CodegenFailedException()
case UnspecifiedModules(choices) =>
val result =
choices.toSeq.sortBy(_._1).foldLeft(Seq.empty[String]) { case (acc, (module, choices)) =>
val nextLabel = Option(choices).filter(_.nonEmpty).fold("<no choices found>")(_.toSeq.sorted.mkString(", "))
acc :+ s" ${AnsiColor.BOLD}${AnsiColor.WHITE}${module}:${AnsiColor.RESET} [${AnsiColor.BLUE}${nextLabel}${AnsiColor.RESET}]"
}
println(s"${AnsiColor.RED}Unsatisfied module(s):${AnsiColor.RESET}")
result.foreach(println)
throw new CodegenFailedException()
case UnusedModules(unused) =>
println(s"${AnsiColor.RED}Unused modules specified:${AnsiColor.RESET} ${unused.toList.mkString(", ")}")
throw new CodegenFailedException()
}, identity)
//.runEmpty

Thread.currentThread().setContextClassLoader(oldClassLoader)
paths.map(_.toFile).distinct
paths.map(_.toFile).toSet
} finally Thread.currentThread().setContextClassLoader(oldClassLoader)
}

def watchSources(tasks: List[Types.Args]): Seq[WatchSource] = {
Expand Down

0 comments on commit 26db8e2

Please sign in to comment.