Add logging and update validation
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
package com.minres.tgc.hammer
|
package com.minres.tgc.hammer
|
||||||
|
|
||||||
import com.minres.tgc.hammer.FileUtils.*
|
|
||||||
import os.Path
|
import os.Path
|
||||||
|
|
||||||
object Global {
|
object Global {
|
||||||
|
@@ -11,7 +11,7 @@ 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")
|
val logger = Logger("TGCHammer")
|
||||||
conf = new HammerConf(args.toIndexedSeq)
|
conf = new HammerConf(args.toIndexedSeq)
|
||||||
|
|
||||||
val logLevel = conf.verbose() match {
|
val logLevel = conf.verbose() match {
|
||||||
@@ -22,14 +22,23 @@ object Main {
|
|||||||
}
|
}
|
||||||
LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[ch.qos.logback.classic.Logger].setLevel(logLevel)
|
LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[ch.qos.logback.classic.Logger].setLevel(logLevel)
|
||||||
|
|
||||||
os.makeDir(Global.OUT_DIR)
|
logger.trace(s"Creating output directory ${Global.OUT_DIR}")
|
||||||
os.makeDir(Global.TMP_DIR)
|
if (conf.emptyOutputDir()) os.remove.all(Global.OUT_DIR)
|
||||||
|
os.makeDir.all(Global.OUT_DIR)
|
||||||
|
logger.trace(s"Creating temp directory ${Global.TMP_DIR}")
|
||||||
|
os.makeDir.all(Global.TMP_DIR)
|
||||||
conf.subcommand match {
|
conf.subcommand match {
|
||||||
case Some(c: MySubcommand) =>
|
case Some(c: MySubcommand) =>
|
||||||
|
logger.info(s"Executing subcommand ${c.name}")
|
||||||
val tasks = c.getRequiredTasks
|
val tasks = c.getRequiredTasks
|
||||||
|
logger.debug(s"Subcommand requires ${tasks.size} tasks")
|
||||||
|
logger.info(s"Validating tasks")
|
||||||
tasks.foreach(_.validate())
|
tasks.foreach(_.validate())
|
||||||
|
logger.info(s"Executing tasks")
|
||||||
tasks.foreach(_.execute())
|
tasks.foreach(_.execute())
|
||||||
case _ =>
|
case _ =>
|
||||||
|
logger.error(s"Found no subcommand, see help below")
|
||||||
|
conf.printHelp()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,16 +2,25 @@ package com.minres.tgc.hammer.cli
|
|||||||
|
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
import os.Path
|
import os.Path
|
||||||
import com.minres.tgc.hammer.FileUtils.*
|
import com.minres.tgc.hammer.util.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()
|
val verbose: ScallopOption[Int] = tally(short = 'v', descr = "Controls the logging of tgc-hammer itself, using it multiple times (-vv) will print more. (0x => WARN, 1x => INFO, 2x => DEBUG, 3x => TRACE)")
|
||||||
|
val emptyOutputDir: ScallopOption[Boolean] = toggle(default = Some(true), descrYes = "Whether to empty the output directory at the start; defaults to true")
|
||||||
addSubcommand(new LongnailCommand)
|
addSubcommand(new LongnailCommand)
|
||||||
addSubcommand(new TreenailCommand)
|
addSubcommand(new TreenailCommand)
|
||||||
|
addSubcommand(new LongnailMergeCommand)
|
||||||
addSubcommand(new LongnailSchedCommand)
|
addSubcommand(new LongnailSchedCommand)
|
||||||
|
|
||||||
requireSubcommand()
|
banner("""This program offers several subcommands to execute different tasks.
|
||||||
|
|The main subcommand to create the hardware module of an ISAX is `isaxHLS`.
|
||||||
|
|For more information about the several subcommands use `tgc-hammer subcommand --help` with the name of the respective subcommand.
|
||||||
|
|
|
||||||
|
|Usage: tgc-hammer isaxHLS -c VexRiscv --useMinIISolution isax.core_desc
|
||||||
|
|""".stripMargin)
|
||||||
|
|
||||||
|
shortSubcommandsHelp(true)
|
||||||
|
|
||||||
verify()
|
verify()
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,22 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
import com.minres.tgc.hammer.FileUtils.*
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import com.minres.tgc.hammer.Global.*
|
import com.minres.tgc.hammer.Global.*
|
||||||
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
||||||
import com.minres.tgc.hammer.tasks.longnail.{LongnailHLSTask, LongnailMergeTask, LongnailScheduleTask, SchedulingParameters}
|
import com.minres.tgc.hammer.tasks.longnail.{LongnailHLSTask, LongnailMergeTask, LongnailScheduleTask, SchedulingParameters}
|
||||||
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
import os.Path
|
import os.Path
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
class LongnailCommand extends MySubcommand("isaxHLS") with CoreSelection {
|
class LongnailCommand extends MySubcommand("isaxHLS") with CoreSelection with Logging[LongnailCommand] with ValidationUtils {
|
||||||
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 useMinIISolution: ScallopOption[Boolean] = toggle(name = "useMinIISolution", group = mainGroup, default = Some(false), descrYes = "Whether to automatically choose the scheduling solution with the lowest II. If not activated, a manual selection during execution will be required!")
|
val useMinIISolution: ScallopOption[Boolean] = toggle(name = "useMinIISolution", group = mainGroup, default = Some(false), descrYes = "Whether to automatically choose the scheduling solution with the lowest II. If not activated, a manual selection during execution will be required!")
|
||||||
val schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
val schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
||||||
|
|
||||||
|
check(inputFiles)(checkPathsIsFile)
|
||||||
|
|
||||||
banner(
|
banner(
|
||||||
"""Run the complete longnail flow from CoreDSL/MLIR to receive a SystemVerilog representation of the ISAXes
|
"""Run the complete longnail flow from CoreDSL/MLIR to receive a SystemVerilog representation of the ISAXes
|
||||||
|Usage: tgc-hammer isaxHLS -c VexRiscv --useMinIISolution isax.core_desc
|
|Usage: tgc-hammer isaxHLS -c VexRiscv --useMinIISolution isax.core_desc
|
||||||
@@ -34,11 +37,17 @@ class LongnailCommand extends MySubcommand("isaxHLS") with CoreSelection {
|
|||||||
tasks += LongnailMergeTask(allMlirFiles, concatInput, mergedInput)
|
tasks += LongnailMergeTask(allMlirFiles, concatInput, mergedInput)
|
||||||
tasks += LongnailScheduleTask(mergedInput, getCoreDatasheet, schedParams)
|
tasks += LongnailScheduleTask(mergedInput, getCoreDatasheet, schedParams)
|
||||||
}
|
}
|
||||||
|
val verilogOutput = OUT_DIR / "verilog"
|
||||||
|
os.makeDir.all(verilogOutput)
|
||||||
if (useMinIISolution()) {
|
if (useMinIISolution()) {
|
||||||
tasks += LongnailHLSTask(schedParams.schedulingSolutionFile(), None, OUT_DIR)
|
tasks += LongnailHLSTask(schedParams.schedulingSolutionFile(), None, verilogOutput)
|
||||||
} else {
|
} else {
|
||||||
???
|
???
|
||||||
}
|
}
|
||||||
tasks.toSeq
|
tasks.toSeq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, verilog output in ${OUT_DIR / "verilog"}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,12 @@ package com.minres.tgc.hammer.cli
|
|||||||
import com.minres.tgc.hammer.tasks.{CopyTask, Task, TreenailTask}
|
import com.minres.tgc.hammer.tasks.{CopyTask, Task, TreenailTask}
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
import os.Path
|
import os.Path
|
||||||
import com.minres.tgc.hammer.FileUtils.*
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import com.minres.tgc.hammer.Global.*
|
import com.minres.tgc.hammer.Global.*
|
||||||
import com.minres.tgc.hammer.tasks.longnail.LongnailMergeTask
|
import com.minres.tgc.hammer.tasks.longnail.LongnailMergeTask
|
||||||
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
|
|
||||||
class LongnailMergeCommand extends MySubcommand("mergeISAX") {
|
class LongnailMergeCommand extends MySubcommand("mergeISAX") with Logging[LongnailMergeCommand] with ValidationUtils {
|
||||||
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()
|
val overwrite: ScallopOption[Boolean] = toggle()
|
||||||
@@ -19,16 +20,24 @@ class LongnailMergeCommand extends MySubcommand("mergeISAX") {
|
|||||||
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
||||||
|
|
||||||
if (inputFiles().size == 1) {
|
if (inputFiles().size == 1) {
|
||||||
|
log.debug(s"Only one input file, no merge required...")
|
||||||
if (coreDSLFiles.size == 1) {
|
if (coreDSLFiles.size == 1) {
|
||||||
|
log.trace(s"CoreDSL input file, creating translation task to MLIR...")
|
||||||
Seq(TreenailTask(coreDSLFiles.head, output()))
|
Seq(TreenailTask(coreDSLFiles.head, output()))
|
||||||
} else {
|
} else {
|
||||||
|
log.trace(s"MLIR input file, creating copy task...")
|
||||||
Seq(CopyTask(mlirFiles.head, output()))
|
Seq(CopyTask(mlirFiles.head, output()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
log.debug(s"Multiple input files, creating translation tasks to MLIR and merge task...")
|
||||||
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
||||||
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
||||||
val concatInput = TMP_DIR / "concat.mlir"
|
val concatInput = TMP_DIR / "concat.mlir"
|
||||||
treenailTasks :+ LongnailMergeTask(allMlirFiles, concatInput, output())
|
treenailTasks :+ LongnailMergeTask(allMlirFiles, concatInput, output())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, merged MLIR output in ${output()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,19 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
import com.minres.tgc.hammer.FileUtils.*
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import com.minres.tgc.hammer.Global.*
|
import com.minres.tgc.hammer.Global.*
|
||||||
import com.minres.tgc.hammer.tasks.longnail.{LongnailMergeTask, LongnailScheduleTask}
|
import com.minres.tgc.hammer.tasks.longnail.{LongnailMergeTask, LongnailScheduleTask}
|
||||||
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
||||||
import com.minres.tgc.hammer.tasks.longnail.SchedulingParameters
|
import com.minres.tgc.hammer.tasks.longnail.SchedulingParameters
|
||||||
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
import os.Path
|
import os.Path
|
||||||
|
|
||||||
class LongnailSchedCommand extends MySubcommand("scheduleISAX") with CoreSelection {
|
class LongnailSchedCommand extends MySubcommand("scheduleISAX") with CoreSelection with Logging[LongnailSchedCommand] with ValidationUtils {
|
||||||
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 schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
val schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
||||||
|
|
||||||
|
check(inputFiles)(checkPathsIsFile)
|
||||||
|
|
||||||
banner(
|
banner(
|
||||||
"""Generate Scheduling information for the provided ISAXes using Longnail
|
"""Generate Scheduling information for the provided ISAXes using Longnail
|
||||||
@@ -19,15 +21,23 @@ class LongnailSchedCommand extends MySubcommand("scheduleISAX") with CoreSelecti
|
|||||||
|""".stripMargin)
|
|""".stripMargin)
|
||||||
|
|
||||||
override def getRequiredTasks: Seq[Task] = {
|
override def getRequiredTasks: Seq[Task] = {
|
||||||
|
log.debug(s"Creating translation task to MLIR for CoreDSL inputs...")
|
||||||
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
||||||
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
||||||
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
||||||
if (allMlirFiles.size == 1) {
|
if (allMlirFiles.size == 1) {
|
||||||
|
log.debug(s"Single input file, only creating schedule task...")
|
||||||
treenailTasks :+ LongnailScheduleTask(allMlirFiles.head, getCoreDatasheet, schedParams)
|
treenailTasks :+ LongnailScheduleTask(allMlirFiles.head, getCoreDatasheet, schedParams)
|
||||||
} else {
|
} else {
|
||||||
val mergedInput = TMP_DIR / "merged.mlir"
|
val mergedInput = TMP_DIR / "merged.mlir"
|
||||||
val concatInput = TMP_DIR / "concat.mlir"
|
val concatInput = TMP_DIR / "concat.mlir"
|
||||||
|
log.debug(s"Multiple input files, creating merge and schedule task...")
|
||||||
treenailTasks :+ LongnailMergeTask(allMlirFiles, concatInput, mergedInput) :+ LongnailScheduleTask(mergedInput, getCoreDatasheet, schedParams)
|
treenailTasks :+ LongnailMergeTask(allMlirFiles, concatInput, mergedInput) :+ LongnailScheduleTask(mergedInput, getCoreDatasheet, schedParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, scheduling solutions in ${schedParams.schedulingSolutionFile()}")
|
||||||
|
log.info(s"Scheduling selection file (KConf) in ${schedParams.schedule.schedulingSelectionFile()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,55 +3,28 @@ package com.minres.tgc.hammer.cli
|
|||||||
import com.minres.tgc.hammer.options.ConfigElement
|
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 com.minres.tgc.hammer.util.{Checker, ValidationBase}
|
||||||
|
|
||||||
type Checker[T] = ScallopOption[T] => Either[String,Unit]
|
abstract class MySubcommand(val name: String) extends Subcommand(name) with ValidationBase[ScallopOption] {
|
||||||
|
|
||||||
abstract class MySubcommand(name: String) extends Subcommand(name) {
|
|
||||||
protected val mainGroup: ScallopOptionGroup = group()
|
protected val mainGroup: ScallopOptionGroup = group()
|
||||||
|
|
||||||
def getRequiredTasks: Seq[Task]
|
def getRequiredTasks: Seq[Task]
|
||||||
|
def cleanup(): Unit
|
||||||
|
|
||||||
def addConfigElement[T <: ConfigElement](elem: T): T = {
|
def addConfigElement[T <: ConfigElement](elem: T): T = {
|
||||||
elem.init(this, null)
|
elem.init(this, null)
|
||||||
elem
|
elem
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkPred[T](option: ScallopOption[T], predicate: T => Boolean, format: String): Either[String,Unit] = {
|
|
||||||
option.toOption.map {
|
def check[T](option: ScallopOption[T])(check: Checker[T]): Unit = {
|
||||||
case o if predicate(o) => (Right(()))
|
addValidation(check(option))
|
||||||
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 {
|
def checkPred[T, U](condOpt: ScallopOption[U], cond: U => Boolean)(option: ScallopOption[T])(check: Checker[T]): Unit = addValidation {
|
||||||
condOpt.toOption match {
|
condOpt.toOption match {
|
||||||
case Some(value) if cond(value) => check(option)
|
case Some(value) if cond(value) => check(option)
|
||||||
case _ => Right(())
|
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)
|
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,18 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
import com.minres.tgc.hammer.FileUtils.*
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
||||||
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
import os.Path
|
import os.Path
|
||||||
|
|
||||||
class TreenailCommand extends MySubcommand("parseCoreDSL") {
|
class TreenailCommand extends MySubcommand("parseCoreDSL") with Logging[TreenailCommand] with ValidationUtils {
|
||||||
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()
|
val overwrite: ScallopOption[Boolean] = toggle()
|
||||||
|
|
||||||
check(coreDSL)(checkPathIsFile)
|
check(coreDSL)(checkPathIsFile)
|
||||||
|
check(coreDSL)(checkPathIsCoreDSL)
|
||||||
checkNotIf(overwrite)(output)(checkPathDoesNotExist)
|
checkNotIf(overwrite)(output)(checkPathDoesNotExist)
|
||||||
|
|
||||||
banner(
|
banner(
|
||||||
@@ -23,4 +25,8 @@ class TreenailCommand extends MySubcommand("parseCoreDSL") {
|
|||||||
TreenailTask(coreDSL(), output())
|
TreenailTask(coreDSL(), output())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, MLIR output in ${output()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ import scala.compiletime.uninitialized
|
|||||||
|
|
||||||
trait BaseOption[T](using conv: ValueConverter[T]) extends ConfigElement {
|
trait BaseOption[T](using conv: ValueConverter[T]) extends ConfigElement {
|
||||||
protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T]
|
protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T]
|
||||||
private var scallop: ScallopOption[T] = uninitialized
|
protected[options] var scallop: ScallopOption[T] = uninitialized
|
||||||
def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
||||||
scallop = createScallop(scallopConf, group)
|
scallop = createScallop(scallopConf, group)
|
||||||
}
|
}
|
||||||
|
@@ -11,5 +11,6 @@ class CommandGroup(name: String) extends BaseGroup {
|
|||||||
|
|
||||||
override def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
override def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
||||||
options.foreach(_.init(scallopConf, group))
|
options.foreach(_.init(scallopConf, group))
|
||||||
|
initValidation(scallopConf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,18 +1,39 @@
|
|||||||
package com.minres.tgc.hammer.options
|
package com.minres.tgc.hammer.options
|
||||||
|
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.util.{Checker, ValidationBase, ValidationUtils}
|
||||||
import org.rogach.scallop.{ScallopConf, ScallopOptionGroup, ValueConverter}
|
import org.rogach.scallop.{ScallopConf, ScallopOptionGroup, ValueConverter}
|
||||||
import os.Shellable
|
import os.Shellable
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
trait BaseGroup extends ConfigElement {
|
trait BaseGroup extends ConfigElement with ValidationBase[BaseOption] with ValidationUtils {
|
||||||
protected val options = mutable.ListBuffer[ConfigElement]()
|
protected val options = mutable.ListBuffer[ConfigElement]()
|
||||||
protected def add[T <: ConfigElement](option: T): T = {
|
protected def add[T <: ConfigElement](option: T): T = {
|
||||||
options += option
|
options += option
|
||||||
option
|
option
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def initValidation(scallopConf: ScallopConf): Unit = {
|
||||||
|
validations.foreach(fn => scallopConf.addValidation(fn()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var validations: List[() => Either[String, Unit]] = Nil
|
||||||
|
|
||||||
|
private def addValidation(fn: () => Either[String, Unit]): Unit = {
|
||||||
|
validations :+= fn
|
||||||
|
}
|
||||||
|
|
||||||
|
def check[T](option: BaseOption[T])(check: Checker[T]): Unit = {
|
||||||
|
addValidation(() => check(option.scallop))
|
||||||
|
}
|
||||||
|
def checkPred[T, U](condOpt: BaseOption[U], cond: U => Boolean)(option: BaseOption[T])(check: Checker[T]): Unit = addValidation { () =>
|
||||||
|
condOpt.scallop.toOption match {
|
||||||
|
case Some(value) if cond(value) => check(option.scallop)
|
||||||
|
case _ => Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def value[T](cliName: String, toolName: String, descr: String = "", default: => Option[T] = None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(cliName, toolName, descr, default, required, cliShort))
|
def value[T](cliName: String, toolName: String, descr: String = "", default: => Option[T] = None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(cliName, toolName, descr, default, required, cliShort))
|
||||||
def valueS[T](name: String, descr: String = "", default: => Option[T] = None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(name, name, descr, default, required, cliShort))
|
def valueS[T](name: String, descr: String = "", default: => Option[T] = None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(name, name, descr, default, required, cliShort))
|
||||||
|
|
||||||
@@ -36,5 +57,6 @@ trait OptionGroup extends BaseGroup {
|
|||||||
def init(scallopConf: ScallopConf, g: ScallopOptionGroup): Unit = {
|
def init(scallopConf: ScallopConf, g: ScallopOptionGroup): Unit = {
|
||||||
val group = scallopConf.group(name)
|
val group = scallopConf.group(name)
|
||||||
options.foreach(_.init(scallopConf, group))
|
options.foreach(_.init(scallopConf, group))
|
||||||
|
initValidation(scallopConf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import os.*
|
|||||||
|
|
||||||
case class CopyTask(from: Path, to: Path) extends Task {
|
case class CopyTask(from: Path, to: Path) extends Task {
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
|
assert(isFile(from), "Input file does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
package com.minres.tgc.hammer.tasks
|
package com.minres.tgc.hammer.tasks
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.util.Logging
|
||||||
import os.{Path, Shellable}
|
import os.{Path, Shellable}
|
||||||
|
|
||||||
trait Task {
|
trait Task extends Logging[Task] {
|
||||||
def validate(): Unit
|
def validate(): Unit
|
||||||
def execute(): Unit
|
def execute(): Unit
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package com.minres.tgc.hammer.tasks
|
package com.minres.tgc.hammer.tasks
|
||||||
|
|
||||||
import com.minres.tgc.hammer.Global
|
import com.minres.tgc.hammer.Global
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import os.*
|
import os.*
|
||||||
|
|
||||||
case class TreenailTask(coreDSLInput: Path, output: Path) extends Task {
|
case class TreenailTask(coreDSLInput: Path, output: Path) extends Task {
|
||||||
@@ -9,6 +9,7 @@ case class TreenailTask(coreDSLInput: Path, output: Path) extends Task {
|
|||||||
|
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
assert(isFile(EXECUTABLE), "Treenail Executable is missing, build Treenail")
|
assert(isFile(EXECUTABLE), "Treenail Executable is missing, build Treenail")
|
||||||
|
assert(isCoreDSLFile(coreDSLInput), "Input file does not exist")
|
||||||
}
|
}
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
runExecutable(EXECUTABLE, "-o", output, coreDSLInput)
|
runExecutable(EXECUTABLE, "-o", output, coreDSLInput)
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
import os.Path
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import os.*
|
||||||
|
|
||||||
class LongnailHLSTask(schedulingSolutionFile: Path, schedulingSelectionFile: Option[Path], outDirectory: Path) extends LongnailBaseTask {
|
class LongnailHLSTask(schedulingSolutionFile: Path, schedulingSelectionFile: Option[Path], outDirectory: Path) extends LongnailBaseTask {
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
super.validate()
|
super.validate()
|
||||||
|
assert(isMLIRFile(schedulingSolutionFile), "Scheduling solution file does not exist")
|
||||||
|
schedulingSelectionFile.foreach(f => assert(isFile(f), "Scheduling selection file does not exist"))
|
||||||
|
assert(isDir(outDirectory), "Output directory does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
import os.Path
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import os.*
|
||||||
|
|
||||||
class LongnailMergeTask(mlirFiles: Seq[Path], concatMLIR: Path, mergedMLIR: Path) extends LongnailBaseTask {
|
class LongnailMergeTask(mlirFiles: Seq[Path], concatMLIR: Path, mergedMLIR: Path) extends LongnailBaseTask {
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
super.validate()
|
super.validate()
|
||||||
|
mlirFiles.foreach(f => assert(f.isMLIRFile, s"Input file $f does not exist or is not an MLIR file"))
|
||||||
}
|
}
|
||||||
|
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
|
@@ -1,33 +1,18 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
import com.minres.tgc.hammer.options.*
|
|
||||||
import com.minres.tgc.hammer.tasks.longnail.LongnailBaseTask
|
import com.minres.tgc.hammer.tasks.longnail.LongnailBaseTask
|
||||||
import os.Path
|
import com.minres.tgc.hammer.util.FileUtils.isMLIRFile
|
||||||
|
import os.*
|
||||||
|
|
||||||
class LongnailScheduleTask(isaxMLIR: Path, coreDatasheet: Path, params: SchedulingParameters) extends LongnailBaseTask {
|
class LongnailScheduleTask(isaxMLIR: Path, coreDatasheet: Path, params: SchedulingParameters) extends LongnailBaseTask {
|
||||||
|
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
super.validate()
|
super.validate()
|
||||||
|
assert(isMLIRFile(isaxMLIR), "Input file does not exist")
|
||||||
|
assert(isFile(coreDatasheet), "Core datasheet does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
/*runExecutable(EXECUTABLE,
|
|
||||||
"--lower-coredsl-to-lil",
|
|
||||||
"--max-unroll-factor", params.maxLoopUnrollFactor,
|
|
||||||
"--schedule-lil",
|
|
||||||
"--datasheet", params.base.coreDatasheet,
|
|
||||||
"--library", params.base.cellLibrary,
|
|
||||||
"--opTyLibrary", params.base.opTyLibrary,
|
|
||||||
"--clockTime", params.base.clockPeriod,
|
|
||||||
"--schedulingAlgo", params.base.schedulingAlgo,
|
|
||||||
"--solver", params.base.ilpSolver,
|
|
||||||
"--schedulingTimeout", params.schedulingTimeout,
|
|
||||||
"--schedRefineTimeout", params.schedulingRefineTimeout,
|
|
||||||
"--solSelKconfPath", params.schedulingKconf,
|
|
||||||
"--verbose", params.base.verbose.toString,
|
|
||||||
"-o", params.schedulingMLIR,
|
|
||||||
isaxMLIR
|
|
||||||
)*/
|
|
||||||
runExecutable(EXECUTABLE,
|
runExecutable(EXECUTABLE,
|
||||||
"--lower-coredsl-to-lil",
|
"--lower-coredsl-to-lil",
|
||||||
params.getToolParameters,
|
params.getToolParameters,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
import com.minres.tgc.hammer.FileUtils.*
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import com.minres.tgc.hammer.Global.OUT_DIR
|
import com.minres.tgc.hammer.Global.OUT_DIR
|
||||||
import com.minres.tgc.hammer.options.*
|
import com.minres.tgc.hammer.options.*
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
@@ -9,22 +9,26 @@ import os.Path
|
|||||||
class SchedulingParameters extends OptionGroup {
|
class SchedulingParameters extends OptionGroup {
|
||||||
override def name: String = "Longnail Scheduling Args"
|
override def name: String = "Longnail Scheduling Args"
|
||||||
|
|
||||||
add(new CommandGroup("--prepare-schedule-lil") {
|
class PrepareOptions extends CommandGroup("--prepare-schedule-lil") {
|
||||||
choiceS(Seq("LEGACY", "MS", "PAMS", "PARAMS", "MI_MS", "MI_PAMS", "MI_PARAMS"), name = "schedulingAlgo", default = Some("LEGACY"), descr =
|
val schedulingAlgo = choiceS(Seq("LEGACY", "MS", "PAMS", "PARAMS", "MI_MS", "MI_PAMS", "MI_PARAMS"), name = "schedulingAlgo", default = Some("LEGACY"), descr =
|
||||||
"Scheduling algorithm used by Longnail; Modulo Scheduling (MS) can be extended with Predicate-aware (PA) and Resource-aware (RA), Inter-Instruction sharing is activated in the MI variants")
|
"Scheduling algorithm used by Longnail; Modulo Scheduling (MS) can be extended with Predicate-aware (PA) and Resource-aware (RA), Inter-Instruction sharing is activated in the MI variants")
|
||||||
value[Path](cliName = "cellLibrary", toolName = "library", descr = "The cell library used by Longnail (example: longnail/test/LILToHW/longnail*.yaml")
|
val cellLibrary = value[Path](cliName = "cellLibrary", toolName = "library", descr = "The cell library used by Longnail (example: longnail/test/LILToHW/longnail*.yaml")
|
||||||
valueS[Path](name = "opTyLibrary", descr = "The operator type model used for detailed data (e.g. from OL SKY130 in longnail/opTyLibraries/OL2.yaml)")
|
val opTyLibrary = valueS[Path](name = "opTyLibrary", descr = "The operator type model used for detailed data (e.g. from OL SKY130 in longnail/opTyLibraries/OL2.yaml)")
|
||||||
})
|
check(cellLibrary)(checkPathIsFile)
|
||||||
|
check(opTyLibrary)(checkPathIsFile)
|
||||||
|
}
|
||||||
|
val prepare = add(new PrepareOptions)
|
||||||
|
|
||||||
add(new CommandGroup("--schedule-lil") {
|
class ScheduleOptions extends CommandGroup("--schedule-lil") {
|
||||||
valueS[Int](name = "schedulingTimeout", default = Some(10), descr = "Longnail scheduling timeout in seconds")
|
val schedulingTimeout = valueS[Int](name = "schedulingTimeout", default = Some(10), descr = "Longnail scheduling timeout in seconds")
|
||||||
value[Int](cliName = "schedulingRefineTimeout", toolName = "schedRefineTimeout", default = Some(10), descr = "Longnail schedule refinement timeout in seconds")
|
val schedulingRefineTimeout = value[Int](cliName = "schedulingRefineTimeout", toolName = "schedRefineTimeout", default = Some(10), descr = "Longnail schedule refinement timeout in seconds")
|
||||||
value[Path](cliName = "schedulingKconf", toolName = "solSelKconfPath", default = Some(OUT_DIR / "scheduling.KConfig"), descr = "Path for the created KConfig file for selecting a scheduling solution")
|
val schedulingSelectionFile = value[Path](cliName = "schedulingKconf", toolName = "solSelKconfPath", default = Some(OUT_DIR / "scheduling.KConfig"), descr = "Path for the created KConfig file for selecting a scheduling solution")
|
||||||
choice(Seq("CBC", "GLPK", "SCIP", "HIGHS", "GUROBI", "CPLEX", "XPRESS", "COPT"), cliName = "ilpSolver", toolName = "solver", default = Some("CBC"), descr = "The ILP solver used by Longnail; currently only CBC is tested")
|
val ilpSolver = choice(Seq("CBC", "GLPK", "SCIP", "HIGHS", "GUROBI", "CPLEX", "XPRESS", "COPT"), cliName = "ilpSolver", toolName = "solver", default = Some("CBC"), descr = "The ILP solver used by Longnail; currently only CBC is tested")
|
||||||
toggle(cliName = "verboseSched", toolName = "verbose", descr = "Enable verbose ILP solver messages")
|
val verboseSched = toggle(cliName = "verboseSched", toolName = "verbose", descr = "Enable verbose ILP solver messages")
|
||||||
value[Int](cliName = "clockPeriod", toolName = "clockTime", descr = "The target clock period; uses same time unit as delays in opTyLibrary")
|
val clockPeriod = value[Int](cliName = "clockPeriod", toolName = "clockTime", descr = "The target clock period; uses same time unit as delays in opTyLibrary")
|
||||||
})
|
}
|
||||||
|
val schedule = add(new ScheduleOptions)
|
||||||
|
|
||||||
value[Int](cliName = "--maxLoopUnrollFactor", toolName = "max-unroll-factor", default = Some(16), descr = "Longnail max loop unroll factor")
|
val maxLoopUnrollFactor = value[Int](cliName = "--maxLoopUnrollFactor", toolName = "max-unroll-factor", default = Some(16), descr = "Longnail max loop unroll factor")
|
||||||
val schedulingSolutionFile: ValueOption[Path] = value[Path](cliName = "--schedulingMLIR", toolName = "o", default = Some(OUT_DIR / "scheduling_solutions.mlir"), descr = "Output file with different scheduling solutions")
|
val schedulingSolutionFile = value[Path](cliName = "--schedulingMLIR", toolName = "o", default = Some(OUT_DIR / "scheduling_solutions.mlir"), descr = "Output file with different scheduling solutions")
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
package com.minres.tgc.hammer
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.Global.BASE_DIR
|
||||||
import org.rogach.scallop.{ScallopOption, Util, ValueConverter, listArgConverter, singleArgConverter}
|
import org.rogach.scallop.{ScallopOption, Util, ValueConverter, listArgConverter, singleArgConverter}
|
||||||
import os.*
|
import os.*
|
||||||
import Global.*
|
|
||||||
|
|
||||||
import scala.collection.IterableOps
|
import scala.collection.IterableOps
|
||||||
|
|
||||||
@@ -13,6 +13,10 @@ object FileUtils {
|
|||||||
path / os.up / newName
|
path / os.up / newName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension (x: os.Path)
|
||||||
|
def isMLIRFile: Boolean = isFile(x) && x.ext == "mlir"
|
||||||
|
def isCoreDSLFile: Boolean = isFile(x) && x.ext == "core_desc"
|
||||||
|
|
||||||
extension (x: String)
|
extension (x: String)
|
||||||
def asPath(relative: Path): Path = {
|
def asPath(relative: Path): Path = {
|
||||||
Path(x, relative)
|
Path(x, relative)
|
||||||
@@ -31,4 +35,5 @@ object FileUtils {
|
|||||||
implicit val osPathRelBaseListConverter: ValueConverter[List[Path]] = {
|
implicit val osPathRelBaseListConverter: ValueConverter[List[Path]] = {
|
||||||
listArgConverter[Path](_.asPath(BASE_DIR))
|
listArgConverter[Path](_.asPath(BASE_DIR))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
|
trait Logging[T: ClassTag] {
|
||||||
|
protected val log: Logger = Logger[T]
|
||||||
|
|
||||||
|
protected def assert(boolean: Boolean, msg: String): Unit = {
|
||||||
|
if (!boolean) log.error(msg)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
import org.rogach.scallop.ScallopOption
|
||||||
|
|
||||||
|
trait ValidationBase[O[_]] {
|
||||||
|
def check[T](option: O[T])(check: Checker[T]): Unit
|
||||||
|
def checkPred[T, U](condOpt: O[U], cond: U => Boolean)(option: O[T])(check: Checker[T]): Unit
|
||||||
|
def checkIf[T](condOpt: O[Boolean])(option: O[T])(check: Checker[T]): Unit = checkPred(condOpt, identity)(option)(check)
|
||||||
|
def checkNotIf[T](condOpt: O[Boolean])(option: O[T])(check: Checker[T]): Unit = checkPred(condOpt, !_)(option)(check)
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
import org.rogach.scallop.{ScallopOption, Util}
|
||||||
|
|
||||||
|
|
||||||
|
type Checker[T] = ScallopOption[T] => Either[String,Unit]
|
||||||
|
|
||||||
|
trait ValidationUtils { this: ValidationBase[?] =>
|
||||||
|
|
||||||
|
|
||||||
|
private def checkPredicate[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(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkPredicateIt[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 => checkPredicate(path, os.exists, "File '%s' does not exist")
|
||||||
|
def checkPathDoesNotExist: Checker[os.Path] = path => checkPredicate(path, !os.exists(_), "File '%s' already exists")
|
||||||
|
def checkPathIsFile: Checker[os.Path] = path => checkPredicate(path, os.isFile, "File '%s' is not a file")
|
||||||
|
def checkPathIsDir: Checker[os.Path] = path => checkPredicate(path, os.isDir, "File '%s' is not a directory")
|
||||||
|
|
||||||
|
|
||||||
|
def checkPathsExists: Checker[List[os.Path]] = path => checkPredicateIt(path, os.exists, "File(s) '%s' do not exist")
|
||||||
|
def checkPathsDoesNotExists: Checker[List[os.Path]] = path => checkPredicateIt(path, !os.exists(_), "File(s) '%s' already exist")
|
||||||
|
def checkPathsIsFile: Checker[List[os.Path]] = path => checkPredicateIt(path, os.isFile, "File(s) '%s' are not files")
|
||||||
|
def checkPathsIsDir: Checker[List[os.Path]] = path => checkPredicateIt(path, os.isDir, "File(s) '%s' are not directories")
|
||||||
|
|
||||||
|
def checkPathIsCoreDSL: Checker[os.Path] = path => checkPredicate(path, p => p.ext == "core_desc", "File '%s' is not a CoreDSL file")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user