Adding in/out file validation

This commit is contained in:
2025-10-02 20:47:12 +02:00
parent 47614ad47f
commit 22a1f31683
7 changed files with 65 additions and 9 deletions

View File

@@ -11,5 +11,7 @@ lazy val root = project
libraryDependencies += "org.rogach" %% "scallop" % "5.2.0", libraryDependencies += "org.rogach" %% "scallop" % "5.2.0",
libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.11.5", libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.11.5",
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.6",
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.5.19",
libraryDependencies += "org.scalameta" %% "munit" % "1.2.0" % Test libraryDependencies += "org.scalameta" %% "munit" % "1.2.0" % Test
) )

View File

@@ -1,6 +1,6 @@
package com.minres.tgc.hammer package com.minres.tgc.hammer
import org.rogach.scallop.{ValueConverter, listArgConverter, singleArgConverter} import org.rogach.scallop.{ScallopOption, Util, ValueConverter, listArgConverter, singleArgConverter}
import os.* import os.*
import Global.* import Global.*

View File

@@ -1,6 +1,9 @@
package com.minres.tgc.hammer package com.minres.tgc.hammer
import ch.qos.logback.classic.Level
import com.minres.tgc.hammer.cli.{HammerConf, MySubcommand} import com.minres.tgc.hammer.cli.{HammerConf, MySubcommand}
import com.typesafe.scalalogging.Logger
import org.slf4j.LoggerFactory
import scala.compiletime.uninitialized import scala.compiletime.uninitialized
@@ -8,8 +11,19 @@ object Main {
var conf: HammerConf = uninitialized var conf: HammerConf = uninitialized
def main(args: Array[String]): Unit = { def main(args: Array[String]): Unit = {
var logger = Logger("TGCHammer")
conf = new HammerConf(args.toIndexedSeq) conf = new HammerConf(args.toIndexedSeq)
val logLevel = conf.verbose() match {
case 0 => Level.WARN
case 1 => Level.INFO
case 2 => Level.DEBUG
case _ => Level.TRACE
}
LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[ch.qos.logback.classic.Logger].setLevel(logLevel)
os.makeDir(Global.OUT_DIR)
os.makeDir(Global.TMP_DIR)
conf.subcommand match { conf.subcommand match {
case Some(c: MySubcommand) => case Some(c: MySubcommand) =>
val tasks = c.getRequiredTasks val tasks = c.getRequiredTasks

View File

@@ -6,9 +6,12 @@ import com.minres.tgc.hammer.FileUtils.*
class HammerConf(arguments: Seq[String]) extends ScallopConf(arguments) { class HammerConf(arguments: Seq[String]) extends ScallopConf(arguments) {
val outputDirectory: ScallopOption[Path] = opt[Path](default = Some("output".path()), descr = "The base output directory") val outputDirectory: ScallopOption[Path] = opt[Path](default = Some("output".path()), descr = "The base output directory")
val verbose: ScallopOption[Int] = tally()
addSubcommand(new LongnailCommand) addSubcommand(new LongnailCommand)
addSubcommand(new TreenailCommand) addSubcommand(new TreenailCommand)
addSubcommand(new LongnailSchedCommand) addSubcommand(new LongnailSchedCommand)
requireSubcommand()
verify() verify()
} }

View File

@@ -10,7 +10,10 @@ import com.minres.tgc.hammer.tasks.longnail.LongnailMergeTask
class LongnailMergeCommand extends MySubcommand("mergeISAX") { class LongnailMergeCommand extends MySubcommand("mergeISAX") {
val inputFiles: ScallopOption[List[Path]] = trailArg[List[Path]]("inputFiles", group = mainGroup, descr = "One or multiple input files; Both .core_desc as well as .mlir are supported (also mixed)") val inputFiles: ScallopOption[List[Path]] = trailArg[List[Path]]("inputFiles", group = mainGroup, descr = "One or multiple input files; Both .core_desc as well as .mlir are supported (also mixed)")
val output: ScallopOption[Path] = opt[Path](short = 'o', default = Some("merged.mlir".path()), descr = "The .mlir file containing the merged ISAXes") val output: ScallopOption[Path] = opt[Path](short = 'o', default = Some("merged.mlir".path()), descr = "The .mlir file containing the merged ISAXes")
val overwrite: ScallopOption[Boolean] = toggle()
check(inputFiles)(checkPathsIsFile)
checkNotIf(overwrite)(output)(checkPathDoesNotExist)
override def getRequiredTasks: Seq[Task] = { override def getRequiredTasks: Seq[Task] = {
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc") val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")

View File

@@ -4,7 +4,7 @@ import com.minres.tgc.hammer.options.ConfigElement
import com.minres.tgc.hammer.tasks.Task import com.minres.tgc.hammer.tasks.Task
import org.rogach.scallop.{ScallopOption, ScallopOptionGroup, Subcommand, Util} import org.rogach.scallop.{ScallopOption, ScallopOptionGroup, Subcommand, Util}
import java.nio.file.Files type Checker[T] = ScallopOption[T] => Either[String,Unit]
abstract class MySubcommand(name: String) extends Subcommand(name) { abstract class MySubcommand(name: String) extends Subcommand(name) {
protected val mainGroup: ScallopOptionGroup = group() protected val mainGroup: ScallopOptionGroup = group()
@@ -16,12 +16,42 @@ abstract class MySubcommand(name: String) extends Subcommand(name) {
elem elem
} }
def validateOSPathIsDirectory(pathOption: ScallopOption[os.Path]): Unit = addValidation { def checkPred[T](option: ScallopOption[T], predicate: T => Boolean, format: String): Either[String,Unit] = {
pathOption.toOption option.toOption.map {
.map { case o if predicate(o) => (Right(()))
case path if Files.isDirectory(path.toNIO) => Right(()) case o => Left(Util.format(format, o))
case path => Left(Util.format("File '%s' is not a directory", path)) }.getOrElse(Right(()))
} }
.getOrElse(Right(()))
def checkPredIt[T, CC <: Iterable](option: ScallopOption[CC[T]], predicate: T => Boolean, format: String): Either[String,Unit] = {
option.toOption.map(options => {
val problems = options.filterNot(predicate)
problems match {
case Nil => Right(())
case problem :: Nil => Left(Util.format(format, problem))
case _ => Left(s"Multiple: ${Util.format(format, problems.mkString(", "))}")
}
}).getOrElse(Right(()))
}
def checkPathExists: Checker[os.Path] = path => checkPred(path, os.exists, "File '%s' does not exist")
def checkPathDoesNotExist: Checker[os.Path] = path => checkPred(path, !os.exists(_), "File '%s' already exists")
def checkPathIsFile: Checker[os.Path] = path => checkPred(path, os.isFile, "File '%s' is not a file")
def checkPathIsDir: Checker[os.Path] = path => checkPred(path, os.isDir, "File '%s' is not a directory")
def checkPathsExists: Checker[List[os.Path]] = path => checkPredIt(path, os.exists, "File(s) '%s' do not exist")
def checkPathsDoesNotExists: Checker[List[os.Path]] = path => checkPredIt(path, !os.exists(_), "File(s) '%s' already exist")
def checkPathsIsFile: Checker[List[os.Path]] = path => checkPredIt(path, os.isFile, "File(s) '%s' are not files")
def checkPathsIsDir: Checker[List[os.Path]] = path => checkPredIt(path, os.isDir, "File(s) '%s' are not directories")
def check[T](option: ScallopOption[T])(check: Checker[T]): Unit = addValidation(check(option))
def checkPred[T, U](condOpt: ScallopOption[U], cond: U => Boolean)(option: ScallopOption[T])(check: Checker[T]): Unit = addValidation {
condOpt.toOption match {
case Some(value) if cond(value) => check(option)
case _ => Right(())
} }
} }
def checkIf[T](condOpt: ScallopOption[Boolean])(option: ScallopOption[T])(check: Checker[T]): Unit = checkPred(condOpt, identity)(option)(check)
def checkNotIf[T](condOpt: ScallopOption[Boolean])(option: ScallopOption[T])(check: Checker[T]): Unit = checkPred(condOpt, !_)(option)(check)
}

View File

@@ -8,6 +8,10 @@ import os.Path
class TreenailCommand extends MySubcommand("parseCoreDSL") { class TreenailCommand extends MySubcommand("parseCoreDSL") {
val coreDSL: ScallopOption[Path] = trailArg[Path]("coreDSL", descr = "The .core_desc input file to be converted") val coreDSL: ScallopOption[Path] = trailArg[Path]("coreDSL", descr = "The .core_desc input file to be converted")
val output: ScallopOption[Path] = opt[Path](short = 'o', default = Some("isax.mlir".path()), descr = "The .mlir file containing the converted ISAX") val output: ScallopOption[Path] = opt[Path](short = 'o', default = Some("isax.mlir".path()), descr = "The .mlir file containing the converted ISAX")
val overwrite: ScallopOption[Boolean] = toggle()
check(coreDSL)(checkPathIsFile)
checkNotIf(overwrite)(output)(checkPathDoesNotExist)
banner( banner(
"""Parse a CoreDSL input using Treenail into the MLIR representation """Parse a CoreDSL input using Treenail into the MLIR representation