From 8e9efbeaac438741379a7d3128314961b0344181 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Wed, 24 Jul 2019 19:20:03 -0700 Subject: [PATCH] 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`. --- .../scala/sbt/internal/FileChangesMacro.scala | 32 +++++++++++++++---- sbt/src/sbt-test/nio/clean/build.sbt | 2 +- .../sbt-test/nio/dynamic-outputs/build.sbt | 2 +- sbt/src/sbt-test/nio/file-hashes/build.sbt | 6 ++-- sbt/src/sbt-test/nio/glob-dsl/build.sbt | 8 ++--- sbt/src/sbt-test/nio/make-clone/build.sbt | 10 +++--- sbt/src/sbt-test/watch/commands/build.sbt | 2 +- .../watch/dynamic-inputs/project/Build.scala | 2 +- sbt/src/sbt-test/watch/overlapping/build.sbt | 2 +- 9 files changed, 43 insertions(+), 23 deletions(-) diff --git a/main/src/main/scala/sbt/internal/FileChangesMacro.scala b/main/src/main/scala/sbt/internal/FileChangesMacro.scala index e103a54f6..5a2a5a9e7 100644 --- a/main/src/main/scala/sbt/internal/FileChangesMacro.scala +++ b/main/src/main/scala/sbt/internal/FileChangesMacro.scala @@ -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) + }) + } } diff --git a/sbt/src/sbt-test/nio/clean/build.sbt b/sbt/src/sbt-test/nio/clean/build.sbt index 30d65a36b..89ec4d4e3 100644 --- a/sbt/src/sbt-test/nio/clean/build.sbt +++ b/sbt/src/sbt-test/nio/clean/build.sbt @@ -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) diff --git a/sbt/src/sbt-test/nio/dynamic-outputs/build.sbt b/sbt/src/sbt-test/nio/dynamic-outputs/build.sbt index 0a57e6faa..1f7ffcb64 100644 --- a/sbt/src/sbt-test/nio/dynamic-outputs/build.sbt +++ b/sbt/src/sbt-test/nio/dynamic-outputs/build.sbt @@ -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) diff --git a/sbt/src/sbt-test/nio/file-hashes/build.sbt b/sbt/src/sbt-test/nio/file-hashes/build.sbt index d9d9186c7..c6b89ae4a 100644 --- a/sbt/src/sbt-test/nio/file-hashes/build.sbt +++ b/sbt/src/sbt-test/nio/file-hashes/build.sbt @@ -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 { diff --git a/sbt/src/sbt-test/nio/glob-dsl/build.sbt b/sbt/src/sbt-test/nio/glob-dsl/build.sbt index 0f519f646..2a235473a 100644 --- a/sbt/src/sbt-test/nio/glob-dsl/build.sbt +++ b/sbt/src/sbt-test/nio/glob-dsl/build.sbt @@ -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) } diff --git a/sbt/src/sbt-test/nio/make-clone/build.sbt b/sbt/src/sbt-test/nio/make-clone/build.sbt index c6efc7d5a..948c82d45 100644 --- a/sbt/src/sbt-test/nio/make-clone/build.sbt +++ b/sbt/src/sbt-test/nio/make-clone/build.sbt @@ -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("").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")) () diff --git a/sbt/src/sbt-test/watch/commands/build.sbt b/sbt/src/sbt-test/watch/commands/build.sbt index bf3d80a7c..be6925842 100644 --- a/sbt/src/sbt-test/watch/commands/build.sbt +++ b/sbt/src/sbt-test/watch/commands/build.sbt @@ -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") } diff --git a/sbt/src/sbt-test/watch/dynamic-inputs/project/Build.scala b/sbt/src/sbt-test/watch/dynamic-inputs/project/Build.scala index 75fd43fc2..2ef4e9b51 100644 --- a/sbt/src/sbt-test/watch/dynamic-inputs/project/Build.scala +++ b/sbt/src/sbt-test/watch/dynamic-inputs/project/Build.scala @@ -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 { diff --git a/sbt/src/sbt-test/watch/overlapping/build.sbt b/sbt/src/sbt-test/watch/overlapping/build.sbt index 5b3d01204..c55168579 100644 --- a/sbt/src/sbt-test/watch/overlapping/build.sbt +++ b/sbt/src/sbt-test/watch/overlapping/build.sbt @@ -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