diff --git a/toolflow/build.sbt b/toolflow/build.sbt index c3f0ab2..99cf425 100644 --- a/toolflow/build.sbt +++ b/toolflow/build.sbt @@ -11,5 +11,7 @@ lazy val root = project libraryDependencies += "org.rogach" %% "scallop" % "5.2.0", 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 ) diff --git a/toolflow/src/main/scala/com/minres/tgc/hammer/FileUtils.scala b/toolflow/src/main/scala/com/minres/tgc/hammer/FileUtils.scala index 237f2dd..26b496f 100644 --- a/toolflow/src/main/scala/com/minres/tgc/hammer/FileUtils.scala +++ b/toolflow/src/main/scala/com/minres/tgc/hammer/FileUtils.scala @@ -1,6 +1,6 @@ package com.minres.tgc.hammer -import org.rogach.scallop.{ValueConverter, listArgConverter, singleArgConverter} +import org.rogach.scallop.{ScallopOption, Util, ValueConverter, listArgConverter, singleArgConverter} import os.* import Global.* diff --git a/toolflow/src/main/scala/com/minres/tgc/hammer/Main.scala b/toolflow/src/main/scala/com/minres/tgc/hammer/Main.scala index e866a8d..6d4392a 100644 --- a/toolflow/src/main/scala/com/minres/tgc/hammer/Main.scala +++ b/toolflow/src/main/scala/com/minres/tgc/hammer/Main.scala @@ -1,6 +1,9 @@ package com.minres.tgc.hammer +import ch.qos.logback.classic.Level import com.minres.tgc.hammer.cli.{HammerConf, MySubcommand} +import com.typesafe.scalalogging.Logger +import org.slf4j.LoggerFactory import scala.compiletime.uninitialized @@ -8,8 +11,19 @@ object Main { var conf: HammerConf = uninitialized def main(args: Array[String]): Unit = { + var logger = Logger("TGCHammer") 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 { case Some(c: MySubcommand) => val tasks = c.getRequiredTasks diff --git a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/HammerConf.scala b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/HammerConf.scala index 7d99bd7..6ab7eb2 100644 --- a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/HammerConf.scala +++ b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/HammerConf.scala @@ -6,9 +6,12 @@ import com.minres.tgc.hammer.FileUtils.* class HammerConf(arguments: Seq[String]) extends ScallopConf(arguments) { 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 TreenailCommand) addSubcommand(new LongnailSchedCommand) + requireSubcommand() + verify() } diff --git a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/LongnailMergeCommand.scala b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/LongnailMergeCommand.scala index 9ba9498..ea16913 100644 --- a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/LongnailMergeCommand.scala +++ b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/LongnailMergeCommand.scala @@ -10,7 +10,10 @@ import com.minres.tgc.hammer.tasks.longnail.LongnailMergeTask 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 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] = { val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc") diff --git a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/MySubcommand.scala b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/MySubcommand.scala index 20b05f8..19c60a8 100644 --- a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/MySubcommand.scala +++ b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/MySubcommand.scala @@ -4,7 +4,7 @@ import com.minres.tgc.hammer.options.ConfigElement import com.minres.tgc.hammer.tasks.Task 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) { protected val mainGroup: ScallopOptionGroup = group() @@ -16,12 +16,42 @@ abstract class MySubcommand(name: String) extends Subcommand(name) { elem } - def validateOSPathIsDirectory(pathOption: ScallopOption[os.Path]): Unit = addValidation { - pathOption.toOption - .map { - case path if Files.isDirectory(path.toNIO) => Right(()) - case path => Left(Util.format("File '%s' is not a directory", path)) - } - .getOrElse(Right(())) + def checkPred[T](option: ScallopOption[T], predicate: T => Boolean, format: String): Either[String,Unit] = { + option.toOption.map { + case o if predicate(o) => (Right(())) + case o => Left(Util.format(format, o)) + }.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) } diff --git a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/TreenailCommand.scala b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/TreenailCommand.scala index e2c0a90..2879e33 100644 --- a/toolflow/src/main/scala/com/minres/tgc/hammer/cli/TreenailCommand.scala +++ b/toolflow/src/main/scala/com/minres/tgc/hammer/cli/TreenailCommand.scala @@ -8,6 +8,10 @@ import os.Path class TreenailCommand extends MySubcommand("parseCoreDSL") { 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 overwrite: ScallopOption[Boolean] = toggle() + + check(coreDSL)(checkPathIsFile) + checkNotIf(overwrite)(output)(checkPathDoesNotExist) banner( """Parse a CoreDSL input using Treenail into the MLIR representation