Skip to content

Commit

Permalink
Upgrade sbt riddl (#446)
Browse files Browse the repository at this point in the history
* Try to use the JVM to run riddlc instead of path

Signed-off-by: reidspencer <reid.spencer@yoppworks.com>

* Don't rely on a Tuple for proper hash function

Signed-off-by: reidspencer <reid.spencer@yoppworks.com>

* WIP: trying to get tasking right

Signed-off-by: reidspencer <reid.spencer@yoppworks.com>

* Remove commands to make the plugin compile

Signed-off-by: reidspencer <reid.spencer@yoppworks.com>

* Improve sbt-riddl significantly

* riddlc path can now be specified directly but will also find it in
  the user's PATH env var.
* there are four commands: riddl, info, hugo, and stats
* the riddl testkits and libraries are included in the build so that
  test infrastructure is ready to go for running riddl tests

Signed-off-by: reidspencer <reid.spencer@yoppworks.com>

---------

Signed-off-by: reidspencer <reid.spencer@yoppworks.com>
  • Loading branch information
reid-spencer authored Oct 1, 2023
1 parent beecf45 commit 60f57d6
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
package com.reactific.riddl.sbt.plugin

import com.reactific.riddl.sbt.SbtRiddlPluginBuildInfo
import com.reactific.riddl.sbt.plugin.RiddlSbtPlugin.V
import sbt.{Def, *}
import sbt.*
import sbt.Keys.*
import sbt.internal.util.ManagedLogger
import sbt.plugins.JvmPlugin

import java.io.File
import java.nio.file.FileSystem
import scala.language.postfixOps
import scala.sys.process.*

Expand All @@ -21,7 +23,7 @@ object RiddlSbtPlugin extends AutoPlugin {

object autoImport {

lazy val riddlcPath = settingKey[File]("Path to `riddlc` compiler")
lazy val riddlcPath = settingKey[Option[File]]("Optional path to riddlc").withRank(KeyRanks.Invisible)

lazy val riddlcConf = settingKey[File]("Path to the config file")

Expand All @@ -30,37 +32,11 @@ object RiddlSbtPlugin extends AutoPlugin {
lazy val riddlcMinVersion = {
settingKey[String]("Ensure the riddlc used is at least this version")
}
}

import autoImport.*

lazy val compileTask = taskKey[Unit]("A task to invoke riddlc compiler")
lazy val infoTask = taskKey[Unit]("A task to invoke riddlc info command")

// Allow riddlc to be run from inside an sbt shell
def riddlcCommand = Command.args(
name = "riddlc",
display = "<options> <command> <args...> ; `riddlc help` for details"
) { (state, args) =>
val project = Project.extract(state)
val path = project.get(riddlcPath)
val minVersion = project.get(riddlcMinVersion)
runRiddlc(path, args, minVersion)
state
lazy val findRiddlcTask = taskKey[File]("Find the riddlc program locally")
}

def infoCommand = Command.args(
name = "info",
display = "prints out riddlc info"
) { (state, _) =>
val project = Project.extract(state)
val path = project.get(riddlcPath)
val minVersion = project.get(riddlcMinVersion)
val options = Seq("info")
runRiddlc(path, options, minVersion)
state

}
import autoImport.*

object V {
val scala = "3.3.1" // NOTE: Synchronize with Helpers.C.withScala3
Expand All @@ -69,7 +45,14 @@ object RiddlSbtPlugin extends AutoPlugin {
val riddl: String = SbtRiddlPluginBuildInfo.version
}

/*private def getLogger(project: Extracted, state: State): ManagedLogger = {
val (_, strms) = project.runTask(, state)
project.structure.streams(state).log
strms.log
}*/

override def projectSettings: Seq[Setting[_]] = Seq(
// Global / excludeLintKeys ++= Seq(riddlcConf, riddlcOptions),
scalaVersion := V.scala,
libraryDependencies ++= Seq(
"com.reactific" %% "riddlc" % V.riddl,
Expand All @@ -79,34 +62,97 @@ object RiddlSbtPlugin extends AutoPlugin {
"org.scalacheck" %% "scalacheck" % V.scalacheck % Test
),
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ScalaLibrary,
riddlcPath := file("riddlc"),
riddlcPath := None,
riddlcOptions := Seq("--show-times"),
riddlcConf := file("src/main/riddl/riddlc.conf"),
riddlcOptions := Seq("--show-times", "--hide-warnings"),
riddlcMinVersion := SbtRiddlPluginBuildInfo.version,
compileTask := {
val execPath = riddlcPath.value
val conf = riddlcConf.value.getAbsoluteFile.toString
val options = riddlcOptions.value
val version = riddlcMinVersion.value
val args = options ++ Seq("from", conf, "validate")
runRiddlc(execPath, args, version)
},
infoTask := {
val execPath = riddlcPath.value
val options = Seq("info")
val version = riddlcMinVersion.value
runRiddlc(execPath, options, version)
},
commands ++= Seq(riddlcCommand),
Compile / compile := Def.taskDyn {
val c = (Compile / compile).value
Def.task {
val _ = (Compile / compileTask).value
c
findRiddlcTask := {
val found: File = riddlcPath.value match {
case Some(path) =>
if (path.getAbsolutePath.endsWith("riddlc")) path else {
throw new IllegalStateException(s"Your riddlcPath setting is not the full path to the riddlc program ")
}
case None =>
val riddlc_path = System.getenv("RIDDLC_PATH")
if (riddlc_path.contains("riddlc")) {
new File(riddlc_path)
} else {
val user_path = System.getenv("PATH")
// FIXME: Won't work on Windoze, make it work
val parts = user_path.split(":")
val with_riddlc = parts.map { part: String =>
if (part.contains("riddlc")) part else part + "/riddlc"
}
with_riddlc.find { (potential: String) =>
Path(potential).exists
} match {
case Some(found) =>
new File(found)
case None =>
throw new IllegalStateException(
"Can't find the 'riddlc' program in your path. Please install.\n" +
parts.mkString("\n")
)
}
}
}
if (!found.exists) {
throw new IllegalStateException(s"riddlc in PATH environment var, but executable not found: $found")
}
}.value
found
},
commands ++= Seq(riddlcCommand, infoCommand, hugoCommand, validateCommand, statsCommand)
)

private def runRiddlcAction(state: State, args: Seq[String]): (State, Int) = {
val project = Project.extract(state)
val riddlcPath = project.runTask(findRiddlcTask, state)
val minimumVersion: String = project.get(riddlcMinVersion)
val options: Seq[String] = project.get(riddlcOptions)
val rc = runRiddlc(riddlcPath._2, minimumVersion, options, args)
state -> rc
}

// Allow riddlc to be run from inside an sbt shell
private def riddlcCommand: Command = {
Command.args(
name = "riddlc",
display = "<options> <command> <args...> ; `riddlc help` for details"
) { (state: State, args: Seq[String]) =>
runRiddlcAction(state, args)._1
}
}

def infoCommand: Command = {
Command.command("info") { (state: State) =>
runRiddlcAction(state, Seq("info"))._1
}
}

def hugoCommand: Command = {
Command.command("hugo") { (state: State) =>
val project = Project.extract(state)
val conf = project.get(riddlcConf).getAbsoluteFile.toString
runRiddlcAction(state, Seq("from", conf, "hugo"))._1
}
}

def validateCommand: Command = {
Command.command("validate") { (state: State) =>
val project = Project.extract(state)
val conf = project.get(riddlcConf).getAbsoluteFile.toString
runRiddlcAction(state, Seq("from", conf, "validate"))._1
}
}

def statsCommand: Command = {
Command.command("stats") { (state: State) =>
val project = Project.extract(state)
val conf = project.get(riddlcConf).getAbsoluteFile.toString
runRiddlcAction(state, Seq("from", conf, "stats"))._1
}
}

private def versionTriple(version: String): (Int, Int, Int) = {
val trimmed = version.indexOf('-') match {
case x: Int if x < 0 => version
Expand All @@ -129,30 +175,33 @@ object RiddlSbtPlugin extends AutoPlugin {
}

private def checkVersion(
riddlc: sbt.File,
riddlcPath: File,
minimumVersion: String
): Unit = {
import scala.sys.process.*
val check = riddlc.toString + " version"
val check = riddlcPath.getAbsolutePath + " version"
val actualVersion = check.!!<.trim
val minVersion = minimumVersion.trim
if (!versionSameOrLater(actualVersion, minVersion)) {
throw new IllegalArgumentException(
s"riddlc version $actualVersion is below minimum required: $minVersion"
)
} else { println(s"riddlc version = $actualVersion") }
} // else { println(s"riddlc version = $actualVersion") }
}

private def runRiddlc(
riddlc: sbt.File,
args: Seq[String],
minimumVersion: String
): Unit = {
checkVersion(riddlc, minimumVersion)
val command = riddlc.toString + " " + args.mkString(" ")
// streams: TaskStreams,
riddlcPath: File,
minimumVersion: String,
options: Seq[String],
args: Seq[String]
): Int = {
checkVersion(riddlcPath, minimumVersion)
// streams.log.info(s"Running: riddlc ${args.mkString(" ")}\n")
// println(s"Running: riddlc ${args.mkString(" ")}\n")
// FIXME: use sbt I/O
val logger = ProcessLogger(println(_))
println(s"RIDDLC: $command")
val rc = command.!(logger)
logger.out(s"RC=$rc")
val process = Process(riddlcPath.getAbsolutePath, options ++ args)
process.!(logger)
}
}
50 changes: 2 additions & 48 deletions sbt-riddl/src/sbt-test/sbt-riddl/simple/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,6 @@ lazy val root = (project in file("."))
.enablePlugins(RiddlSbtPlugin)
.settings(
version := "0.1",
riddlcOptions := Seq.empty[String],
riddlcConf := file("src/main/riddl/riddlc.conf"),
riddlcPath := file("/Users/reid/Code/reactific/riddl/riddlc/target/universal/stage/bin/riddlc"),
TaskKey[String]("checkInfoOutput") := {
val which_cmd = Process("/bin/zsh", Seq("-c", "which riddlc"))
val riddlc_path: String = {
val which_out = (which_cmd.lineStream_!).toSeq.mkString.trim
if (!which_out.startsWith("/")) {
"/Users/reid/Code/reactific/riddl/riddlc/target/universal/stage/bin/riddlc"
} else {
which_out
}
}
println(s"RIDDLC path is: $riddlc_path")
if (!riddlc_path.contains("riddlc")) {
sys.error(s"which command didn't return riddlc path but:\n$riddlc_path")
}
val args = Seq("--verbose", "info")
val p1 = Process(riddlc_path, args)
val out1 = (p1 !!).trim
println(out1)
if (!out1.contains("name: riddlc")) {
sys.error(s"wrong output from 'riddlc info' command:\n$out1")
}
val plugin_version = sys.props.get("plugin.version").getOrElse("0")
if (!out1.contains(s"version: ")) {
sys.error(s"output should contain 'version: $plugin_version")
}
val v = Seq(
"--verbose",
"--suppress-missing-warnings",
"from",
"src/main/riddl/riddlc.conf",
"validate"
)
val p2 = Process(riddlc_path, v)
val out2 = (p2 !!).trim
println(out2)
if (
!out2
.contains("Ran: from src/main/riddl/riddlc.conf validate: success=yes")
) {
sys.error(
"riddlc output did not contain 'Ran: from riddlc.conf validate: success=yes'"
)
}
riddlc_path
}
riddlcOptions := Seq("--show-times", "--verbose"),
riddlcConf := file("src/main/riddl/riddlc.conf")
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ class GenerateTestSiteSpec extends RunCommandSpecBase {
"validate RIDDL and generate Hugo" in {
val command = Array(
"--show-times",
"--suppress-style-warnings",
"--suppress-missing-warnings",
"--verbose",
"from",
"src/main/riddl/riddl.conf",
"hugo"
"validate"
)
runWith(command)
}
Expand Down
16 changes: 8 additions & 8 deletions sbt-riddl/src/sbt-test/sbt-riddl/simple/test
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Put in sbt-test scripted checks in this file
# See: https://www.scala-sbt.org/1.x/docs/Testing-sbt-plugins.html for options

# Check for basic sanity with info output
> checkInfoOutput

# now try the compile command
> compile
# See if the minimal info command works
> info

# Try a simple riddlc command
# Try that with the riddlc command
> riddlc info

# And we support a short form of that too
> info
# now try the validate command
> validate

# now try the stats command
> stats

# Compile the test code
> Test/compile
Expand Down

0 comments on commit 60f57d6

Please sign in to comment.