Add extension methods for input and output files

It is tedious to write (foo / allInputFiles).value so I added simple
extension method macros that expand `foo.inputFiles` to
(foo / allInputFiles).value and `foo.outputFiles` to
`(foo / allOutputFiles).value`.
This commit is contained in:
Ethan Atkins 2019-07-24 19:20:03 -07:00
parent f126206231
commit 8e9efbeaac
9 changed files with 43 additions and 23 deletions

View File

@ -34,6 +34,14 @@ object FileChangesMacro {
"`changedOutputFiles` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
)
def changedOutputFiles: Option[ChangedFiles] = macro changedOutputFilesImpl[T]
@compileTimeOnly(
"`inputFiles` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
)
def inputFiles: Seq[NioPath] = macro inputFilesImpl[T]
@compileTimeOnly(
"`outputFiles` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
)
def outputFiles: Seq[NioPath] = macro outputFilesImpl[T]
}
def changedInputFilesImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Option[ChangedFiles]] = {
impl[T](c)(c.universe.reify(changedInputFiles), c.universe.reify(inputFileStamps))
@ -50,16 +58,28 @@ object FileChangesMacro {
mapKey: c.Expr[TaskKey[Seq[(NioPath, FileStamp)]]]
): c.Expr[Option[ChangedFiles]] = {
import c.universe._
val taskTpe = c.weakTypeOf[TaskKey[T]]
lazy val err = "Couldn't expand file change macro."
val taskKey = c.Expr[TaskKey[T]](c.macroApplication match {
case Select(Apply(_, k :: Nil), _) if k.tpe <:< taskTpe => k
case _ => c.abort(c.enclosingPosition, err)
})
val taskKey = getTaskKey(c)
reify {
val changes = (changeKey.splice in taskKey.splice).value
import sbt.nio.FileStamp.Formats._
Previous.runtimeInEnclosingTask(mapKey.splice in taskKey.splice).value.flatMap(changes)
}
}
def inputFilesImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Seq[NioPath]] = {
val taskKey = getTaskKey(c)
c.universe.reify((allInputFiles in taskKey.splice).value)
}
def outputFilesImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Seq[NioPath]] = {
val taskKey = getTaskKey(c)
c.universe.reify((allOutputFiles in taskKey.splice).value)
}
private def getTaskKey[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[TaskKey[T]] = {
import c.universe._
val taskTpe = c.weakTypeOf[TaskKey[T]]
lazy val err = "Couldn't expand file change macro."
c.Expr[TaskKey[T]](c.macroApplication match {
case Select(Apply(_, k :: Nil), _) if k.tpe <:< taskTpe => k
case _ => c.abort(c.enclosingPosition, err)
})
}
}

View File

@ -15,7 +15,7 @@ copyFile := Def.task {
prev match {
case Some(v: Int) if changes.isEmpty => v
case _ =>
changes.getOrElse((copyFile / allInputFiles).value).foreach { p =>
changes.getOrElse(copyFile.inputFiles).foreach { p =>
val outDir = baseDirectory.value / "out"
IO.createDirectory(outDir)
IO.copyFile(p.toFile, outDir / p.getFileName.toString)

View File

@ -5,7 +5,7 @@ foo / fileInputs += baseDirectory.value.toGlob / "base" / "*.txt"
foo / target := baseDirectory.value / "out"
foo := {
val out = baseDirectory.value / "out"
((foo / allInputFiles).value: Seq[Path]).map { p =>
foo.inputFiles.map { p =>
val f = p.toFile
val target = out / f.getName
IO.copyFile (f, target)

View File

@ -11,7 +11,7 @@ checkModified := {
val changes = foo.changedInputFiles
val modified = changes.map(_.updated).getOrElse(Nil)
println(modified)
val allFiles = (foo / allInputFiles).value
val allFiles = foo.inputFiles
if (modified.isEmpty) assert(true)
else {
assert(modified != allFiles)
@ -21,7 +21,7 @@ checkModified := {
val checkRemoved = taskKey[Unit]("check that removed files are returned")
checkRemoved := Def.taskDyn {
val files = (foo / allInputFiles).value
val files = foo.inputFiles
val removed = foo.changedInputFiles.map(_.deleted).getOrElse(Nil)
if (removed.isEmpty) Def.task(assert(true))
else Def.task {
@ -32,7 +32,7 @@ checkRemoved := Def.taskDyn {
val checkAdded = taskKey[Unit]("check that modified files are returned")
checkAdded := Def.taskDyn {
val files = (foo / allInputFiles).value
val files = foo.inputFiles
val added = foo.changedInputFiles.map(_.created).getOrElse(Nil)
if (added.isEmpty || (files.toSet == added.toSet)) Def.task(assert(true))
else Def.task {

View File

@ -5,7 +5,7 @@ val foo = taskKey[Seq[File]]("Retrieve Foo.txt")
foo / fileInputs += baseDirectory.value.toGlob / ** / "*.txt"
foo := (foo / allInputFiles).value.map(_.toFile)
foo := foo.inputFiles.map(_.toFile)
val checkFoo = taskKey[Unit]("Check that the Foo.txt file is retrieved")
@ -16,7 +16,7 @@ val bar = taskKey[Seq[File]]("Retrieve Bar.md")
bar / fileInputs += baseDirectory.value.toGlob / "base" / "subdir" / "nested-subdir" / "*.md"
bar := (bar / allInputFiles).value.map(_.toFile)
bar := bar.inputFiles.map(_.toFile)
val checkBar = taskKey[Unit]("Check that the Bar.md file is retrieved")
@ -32,7 +32,7 @@ val checkAll = taskKey[Unit]("Check that the Bar.md file is retrieved")
checkAll := {
import sbt.dsl.LinterLevel.Ignore
val expected = Set("Foo.txt", "Bar.md").map(baseDirectory.value / "base" / "subdir" / "nested-subdir" / _)
val actual = (all / allInputFiles).value.map(_.toFile).toSet
val actual = all.inputFiles.map(_.toFile).toSet
assert(actual == expected)
}
@ -55,6 +55,6 @@ depth / fileInputs ++= {
checkDepth := {
val expected = Seq("Bar.md").map(baseDirectory.value / "base/subdir/nested-subdir" / _)
val actual = (depth / allInputFiles).value.map(_.toFile)
val actual = depth.inputFiles.map(_.toFile)
assert(actual == expected)
}

View File

@ -16,7 +16,7 @@ compileLib := {
val name = path.getFileName.toString
objectDir.resolve(name.substring(0, name.lastIndexOf('.')) + ".o")
}
val allFiles: Seq[Path] = (compileLib / allInputFiles).value
val allFiles: Seq[Path] = compileLib.inputFiles
val changedFiles: Option[Seq[Path]] = compileLib.changedInputFiles match {
case Some(ChangedFiles(c, d, u)) =>
d.foreach(p => Files.deleteIfExists(objectPath(p)))
@ -52,7 +52,7 @@ linkLib / target := baseDirectory.value / "out" / "lib"
linkLib := {
val changedObjects = compileLib.changedOutputFiles
val outPath = (linkLib / target).value.toPath
val allObjects = (compileLib / allOutputFiles).value.map(_.toString)
val allObjects = compileLib.outputFiles.map(_.toString)
val logger = streams.value.log
linkLib.previous match {
case Some(p: Path) if changedObjects.isEmpty =>
@ -90,7 +90,7 @@ compileMain := {
logger.info(s"Not building $outPath: no dependencies have changed")
p
case _ =>
(compileMain / allInputFiles).value match {
compileMain.inputFiles match {
case Seq(main) =>
Files.createDirectories(outDir)
logger.info(s"Building executable $outPath")
@ -112,7 +112,7 @@ compileMain := {
val executeMain = inputKey[Unit]("run the main method")
executeMain := {
val args = Def.spaceDelimited("<arguments>").parsed
val binary: Seq[Path] = (compileMain / allOutputFiles).value
val binary: Seq[Path] = compileMain.outputFiles
val logger = streams.value.log
binary match {
case Seq(b) =>
@ -131,7 +131,7 @@ executeMain := {
val checkOutput = inputKey[Unit]("check the output value")
checkOutput := {
val args @ Seq(arg, res) = Def.spaceDelimited("").parsed
val binary: Path = (compileMain / allOutputFiles).value.head
val binary: Path = compileMain.outputFiles.head
val output = RunBinary(binary, args, linkLib.value)
assert(output.contains(s"f($arg) = $res"))
()

View File

@ -5,7 +5,7 @@ import scala.collection.JavaConverters._
val foo = taskKey[Unit]("foo")
foo := {
val fooTxt = baseDirectory.value / "foo.txt"
val _ = println(s"foo inputs: ${(foo / allInputFiles).value}")
val _ = println(s"foo inputs: ${foo.inputFiles}")
IO.write(fooTxt, "foo")
println(s"foo wrote to $foo")
}

View File

@ -24,7 +24,7 @@ object Build {
lazy val root = (project in file(".")).settings(
reloadFile := baseDirectory.value / "reload",
foo / fileInputs += baseDirectory.value.toGlob / "foo.txt",
foo := (foo / allInputFiles).value,
foo := foo.inputFiles,
setStringValue := Def.taskDyn {
// This hides foo / fileInputs from the input graph
Def.taskDyn {

View File

@ -8,7 +8,7 @@ foo / watchForceTriggerOnAnyChange := true
foo / fileInputs := baseDirectory.value.toGlob / "files" / "foo.txt" :: Nil
foo / watchTriggers := baseDirectory.value.toGlob / ** / "foo.txt" :: Nil
foo := {
(foo / allInputFiles).value.foreach { p =>
foo.inputFiles.foreach { p =>
Files.setLastModifiedTime(p, FileTime.fromMillis(Files.getLastModifiedTime(p).toMillis + 3000))
}
sbt.nio.Stamps.check(foo).value