mirror of https://github.com/sbt/sbt.git
Add dsl for collecting globs
Right now, the sbt.internal.io.Source is something of a second class
citizen within sbt. Since sbt 0.13, there have been extension classes
defined that can convert a file to a PathFinder but no analog has been
introduced for sbt.internal.io.Source.
Given that sbt.internal.io.Source was not really intended to be part of
the public api (just look at its package), I think it makes sense to
just replace it with Glob. In this commit, I add extension
methods to Glob and Seq[Glob] that make it possible to easily
retrieve all of the files for a particular Glob within a task. The
upshot is that where previously, we'd have had to write something like:
watchSources += Source(baseDirectory.value / "src" / "main" / "proto", "*.proto", NothingFilter)
now we can write
watchGlobs += baseDirectory.value / "src" / "main" / "proto" * "*.proto"
Moreover, within a task, we can now do something like:
foo := {
val allWatchGlobs: Seq[File] = watchGlobs.value.all
println(allWatchSources.mkString("all watch source files:\n", "\n", ""))
}
Before we would have had to manually retrieve the files.
The implementation of the dsl uses the new GlobExtractor class which
proxies file look ups through a FileTree.Repository. This makes it so
that, by default, all file i/o using Sources will use the default
FileTree.Repository. The default is a macro that returns
`sbt.Keys.fileTreeRepository.value: @sbtUnchecked`. By doing it this
way, the default repository can only be used within a task definition
(since it delegates to `fileTreeRepository.value`). It does not,
however, prevent the user from explicitly providing a
FileTree.Repository instance which the user is free to instantiate
however they wish.
Bonus: optimize imports in Def.scala and Defaults.scala
This commit is contained in:
parent
d0310cc866
commit
571b179574
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal.util.appmacro
|
||||||
|
|
||||||
|
import scala.reflect.macros.blackbox
|
||||||
|
|
||||||
|
object MacroDefaults {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macro to generated default file tree repository. It must be defined as an untyped tree because
|
||||||
|
* sbt.Keys is not available in this project. This is meant for internal use only, but must be
|
||||||
|
* public because its a macro.
|
||||||
|
* @param c the macro context
|
||||||
|
* @return the tree expressing the default file tree repository.
|
||||||
|
*/
|
||||||
|
def fileTreeRepository(c: blackbox.Context): c.Tree = {
|
||||||
|
import c.universe._
|
||||||
|
q"sbt.Keys.fileTreeRepository.value: @sbtUnchecked"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,6 @@ import scala.concurrent.duration._
|
||||||
|
|
||||||
class WatchedSpec extends FlatSpec with Matchers {
|
class WatchedSpec extends FlatSpec with Matchers {
|
||||||
object Defaults {
|
object Defaults {
|
||||||
private val fileTreeViewConfig = FileTreeViewConfig.default(50.millis)
|
|
||||||
def config(
|
def config(
|
||||||
globs: Seq[Glob],
|
globs: Seq[Glob],
|
||||||
fileEventMonitor: Option[FileEventMonitor[FileCacheEntry]] = None,
|
fileEventMonitor: Option[FileEventMonitor[FileCacheEntry]] = None,
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,13 @@
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import sbt.internal.util.Types.const
|
import sbt.internal.util.Types.const
|
||||||
import sbt.internal.util.{ Attributed, AttributeKey, Init, ConsoleAppender }
|
import sbt.internal.util.{ AttributeKey, Attributed, ConsoleAppender, Init }
|
||||||
import sbt.util.Show
|
import sbt.util.Show
|
||||||
import sbt.internal.util.complete.Parser
|
import sbt.internal.util.complete.Parser
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import Scope.{ ThisScope, GlobalScope }
|
|
||||||
|
import Scope.{ GlobalScope, ThisScope }
|
||||||
import KeyRanks.{ DTask, Invisible }
|
import KeyRanks.{ DTask, Invisible }
|
||||||
|
|
||||||
/** A concrete settings system that uses `sbt.Scope` for the scope type. */
|
/** A concrete settings system that uses `sbt.Scope` for the scope type. */
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ import sbt.internal.util.Types._
|
||||||
import sbt.internal.util._
|
import sbt.internal.util._
|
||||||
import sbt.internal.util.complete._
|
import sbt.internal.util.complete._
|
||||||
import sbt.io.Path._
|
import sbt.io.Path._
|
||||||
import sbt.io.syntax._
|
|
||||||
import sbt.io._
|
import sbt.io._
|
||||||
|
import sbt.io.syntax._
|
||||||
import sbt.librarymanagement.Artifact.{ DocClassifier, SourceClassifier }
|
import sbt.librarymanagement.Artifact.{ DocClassifier, SourceClassifier }
|
||||||
import sbt.librarymanagement.Configurations.{
|
import sbt.librarymanagement.Configurations.{
|
||||||
Compile,
|
Compile,
|
||||||
|
|
@ -68,8 +68,8 @@ import sbt.testing.{ AnnotatedFingerprint, Framework, Runner, SubclassFingerprin
|
||||||
import sbt.util.CacheImplicits._
|
import sbt.util.CacheImplicits._
|
||||||
import sbt.util.InterfaceUtil.{ toJavaFunction => f1 }
|
import sbt.util.InterfaceUtil.{ toJavaFunction => f1 }
|
||||||
import sbt.util._
|
import sbt.util._
|
||||||
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
|
||||||
import sjsonnew._
|
import sjsonnew._
|
||||||
|
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
||||||
import xsbti.CrossValue
|
import xsbti.CrossValue
|
||||||
import xsbti.compile.{ AnalysisContents, IncOptions, IncToolOptionsUtil }
|
import xsbti.compile.{ AnalysisContents, IncOptions, IncToolOptionsUtil }
|
||||||
|
|
||||||
|
|
@ -80,6 +80,7 @@ import scala.xml.NodeSeq
|
||||||
|
|
||||||
// incremental compiler
|
// incremental compiler
|
||||||
import sbt.SlashSyntax0._
|
import sbt.SlashSyntax0._
|
||||||
|
import sbt.internal.GlobLister._
|
||||||
import sbt.internal.inc.{
|
import sbt.internal.inc.{
|
||||||
Analysis,
|
Analysis,
|
||||||
AnalyzingCompiler,
|
AnalyzingCompiler,
|
||||||
|
|
@ -280,10 +281,14 @@ object Defaults extends BuildCommon {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
watchStartMessage := Watched.defaultStartWatch,
|
watchStartMessage := Watched.defaultStartWatch,
|
||||||
|
fileTreeRepository := state.value
|
||||||
|
.get(Keys.globalFileTreeRepository)
|
||||||
|
.map(FileTree.repository)
|
||||||
|
.getOrElse(FileTree.Repository.polling),
|
||||||
externalHooks := {
|
externalHooks := {
|
||||||
val view = FileManagement.dataView.value
|
val repository = fileTreeRepository.value
|
||||||
compileOptions =>
|
compileOptions =>
|
||||||
Some(ExternalHooks(compileOptions, view))
|
Some(ExternalHooks(compileOptions, repository))
|
||||||
},
|
},
|
||||||
watchAntiEntropy :== new FiniteDuration(500, TimeUnit.MILLISECONDS),
|
watchAntiEntropy :== new FiniteDuration(500, TimeUnit.MILLISECONDS),
|
||||||
watchLogger := streams.value.log,
|
watchLogger := streams.value.log,
|
||||||
|
|
@ -373,13 +378,12 @@ object Defaults extends BuildCommon {
|
||||||
crossPaths.value
|
crossPaths.value
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
unmanagedSources := FileManagement
|
unmanagedSources := {
|
||||||
.collectFiles(
|
val filter =
|
||||||
unmanagedSourceDirectories,
|
(includeFilter in unmanagedSources).value -- (excludeFilter in unmanagedSources).value
|
||||||
includeFilter in unmanagedSources,
|
val baseSources = if (sourcesInBase.value) baseDirectory.value * filter :: Nil else Nil
|
||||||
excludeFilter in unmanagedSources
|
(unmanagedSourceDirectories.value.map(_ ** filter) ++ baseSources).all
|
||||||
)
|
},
|
||||||
.value,
|
|
||||||
watchSources in ConfigGlobal := (watchSources in ConfigGlobal).value ++ {
|
watchSources in ConfigGlobal := (watchSources in ConfigGlobal).value ++ {
|
||||||
val baseDir = baseDirectory.value
|
val baseDir = baseDirectory.value
|
||||||
val bases = unmanagedSourceDirectories.value
|
val bases = unmanagedSourceDirectories.value
|
||||||
|
|
@ -413,13 +417,11 @@ object Defaults extends BuildCommon {
|
||||||
resourceDirectories := Classpaths
|
resourceDirectories := Classpaths
|
||||||
.concatSettings(unmanagedResourceDirectories, managedResourceDirectories)
|
.concatSettings(unmanagedResourceDirectories, managedResourceDirectories)
|
||||||
.value,
|
.value,
|
||||||
unmanagedResources := FileManagement
|
unmanagedResources := {
|
||||||
.collectFiles(
|
val filter =
|
||||||
unmanagedResourceDirectories,
|
(includeFilter in unmanagedResources).value -- (excludeFilter in unmanagedResources).value
|
||||||
includeFilter in unmanagedResources,
|
unmanagedResourceDirectories.value.map(_ ** filter).all
|
||||||
excludeFilter in unmanagedResources
|
},
|
||||||
)
|
|
||||||
.value,
|
|
||||||
watchSources in ConfigGlobal := (watchSources in ConfigGlobal).value ++ {
|
watchSources in ConfigGlobal := (watchSources in ConfigGlobal).value ++ {
|
||||||
val bases = unmanagedResourceDirectories.value
|
val bases = unmanagedResourceDirectories.value
|
||||||
val include = (includeFilter in unmanagedResources).value
|
val include = (includeFilter in unmanagedResources).value
|
||||||
|
|
@ -433,7 +435,8 @@ object Defaults extends BuildCommon {
|
||||||
managedResources := generate(resourceGenerators).value,
|
managedResources := generate(resourceGenerators).value,
|
||||||
resources := Classpaths.concat(managedResources, unmanagedResources).value
|
resources := Classpaths.concat(managedResources, unmanagedResources).value
|
||||||
)
|
)
|
||||||
def addBaseSources = FileManagement.appendBaseSources
|
// This exists for binary compatibility and probably never should have been public.
|
||||||
|
def addBaseSources: Seq[Def.Setting[Task[Seq[File]]]] = Nil
|
||||||
lazy val outputConfigPaths = Seq(
|
lazy val outputConfigPaths = Seq(
|
||||||
classDirectory := crossTarget.value / (prefix(configuration.value.name) + "classes"),
|
classDirectory := crossTarget.value / (prefix(configuration.value.name) + "classes"),
|
||||||
semanticdbTargetRoot := crossTarget.value / (prefix(configuration.value.name) + "meta"),
|
semanticdbTargetRoot := crossTarget.value / (prefix(configuration.value.name) + "meta"),
|
||||||
|
|
@ -1205,9 +1208,12 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
def collectFiles(
|
def collectFiles(
|
||||||
dirs: ScopedTaskable[Seq[File]],
|
dirs: ScopedTaskable[Seq[File]],
|
||||||
filter: ScopedTaskable[FileFilter],
|
include: ScopedTaskable[FileFilter],
|
||||||
excludes: ScopedTaskable[FileFilter]
|
exclude: ScopedTaskable[FileFilter]
|
||||||
): Initialize[Task[Seq[File]]] = FileManagement.collectFiles(dirs, filter, excludes)
|
): Initialize[Task[Seq[File]]] = Def.task {
|
||||||
|
val filter = include.toTask.value -- exclude.toTask.value
|
||||||
|
dirs.toTask.value.map(_ ** filter).all
|
||||||
|
}
|
||||||
def artifactPathSetting(art: SettingKey[Artifact]): Initialize[File] =
|
def artifactPathSetting(art: SettingKey[Artifact]): Initialize[File] =
|
||||||
Def.setting {
|
Def.setting {
|
||||||
val f = artifactName.value
|
val f = artifactName.value
|
||||||
|
|
@ -1807,8 +1813,7 @@ object Defaults extends BuildCommon {
|
||||||
) :+ (classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.RuntimeDependencies)
|
) :+ (classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.RuntimeDependencies)
|
||||||
|
|
||||||
lazy val compileSettings: Seq[Setting[_]] =
|
lazy val compileSettings: Seq[Setting[_]] =
|
||||||
configSettings ++
|
configSettings ++ (mainBgRunMainTask +: mainBgRunTask) ++
|
||||||
(mainBgRunMainTask +: mainBgRunTask +: FileManagement.appendBaseSources) ++
|
|
||||||
Classpaths.addUnmanagedLibrary ++ runtimeLayeringSettings
|
Classpaths.addUnmanagedLibrary ++ runtimeLayeringSettings
|
||||||
|
|
||||||
private val testLayeringSettings: Seq[Setting[_]] = TaskRepository.proxy(
|
private val testLayeringSettings: Seq[Setting[_]] = TaskRepository.proxy(
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ object Keys {
|
||||||
val watch = SettingKey(BasicKeys.watch)
|
val watch = SettingKey(BasicKeys.watch)
|
||||||
val suppressSbtShellNotification = settingKey[Boolean]("""True to suppress the "Executing in batch mode.." message.""").withRank(CSetting)
|
val suppressSbtShellNotification = settingKey[Boolean]("""True to suppress the "Executing in batch mode.." message.""").withRank(CSetting)
|
||||||
val enableGlobalCachingFileTreeRepository = settingKey[Boolean]("Toggles whether or not to create a global cache of the file system that can be used by tasks to quickly list a path").withRank(DSetting)
|
val enableGlobalCachingFileTreeRepository = settingKey[Boolean]("Toggles whether or not to create a global cache of the file system that can be used by tasks to quickly list a path").withRank(DSetting)
|
||||||
|
val fileTreeRepository = taskKey[FileTree.Repository]("A repository of the file system.")
|
||||||
val pollInterval = settingKey[FiniteDuration]("Interval between checks for modified sources by the continuous execution command.").withRank(BMinusSetting)
|
val pollInterval = settingKey[FiniteDuration]("Interval between checks for modified sources by the continuous execution command.").withRank(BMinusSetting)
|
||||||
val pollingGlobs = settingKey[Seq[Glob]]("Directories that cannot be cached and must always be rescanned. Typically these will be NFS mounted or something similar.").withRank(DSetting)
|
val pollingGlobs = settingKey[Seq[Glob]]("Directories that cannot be cached and must always be rescanned. Typically these will be NFS mounted or something similar.").withRank(DSetting)
|
||||||
val watchAntiEntropy = settingKey[FiniteDuration]("Duration for which the watch EventMonitor will ignore events for a file after that file has triggered a build.").withRank(BMinusSetting)
|
val watchAntiEntropy = settingKey[FiniteDuration]("Duration for which the watch EventMonitor will ignore events for a file after that file has triggered a build.").withRank(BMinusSetting)
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@ import java.util.Optional
|
||||||
|
|
||||||
import sbt.Stamped
|
import sbt.Stamped
|
||||||
import sbt.internal.inc.ExternalLookup
|
import sbt.internal.inc.ExternalLookup
|
||||||
|
import sbt.io.FileTreeDataView.Entry
|
||||||
import sbt.io.syntax._
|
import sbt.io.syntax._
|
||||||
import sbt.io.{ AllPassFilter, FileTreeDataView, FileTreeRepository, TypedPath }
|
import sbt.io.{ AllPassFilter, Glob, TypedPath }
|
||||||
import xsbti.compile._
|
import xsbti.compile._
|
||||||
import xsbti.compile.analysis.Stamp
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
|
|
@ -20,10 +21,8 @@ import scala.collection.mutable
|
||||||
|
|
||||||
private[sbt] object ExternalHooks {
|
private[sbt] object ExternalHooks {
|
||||||
private val javaHome = Option(System.getProperty("java.home")).map(Paths.get(_))
|
private val javaHome = Option(System.getProperty("java.home")).map(Paths.get(_))
|
||||||
def apply(
|
def apply(options: CompileOptions, repo: FileTree.Repository): DefaultExternalHooks = {
|
||||||
options: CompileOptions,
|
def listEntries(glob: Glob): Seq[Entry[FileCacheEntry]] = repo.get(glob)
|
||||||
view: FileTreeDataView[FileCacheEntry]
|
|
||||||
): DefaultExternalHooks = {
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
val sources = options.sources()
|
val sources = options.sources()
|
||||||
val cachedSources = new java.util.HashMap[File, Stamp]
|
val cachedSources = new java.util.HashMap[File, Stamp]
|
||||||
|
|
@ -32,28 +31,19 @@ private[sbt] object ExternalHooks {
|
||||||
case sf: Stamped => cachedSources.put(sf, sf.stamp)
|
case sf: Stamped => cachedSources.put(sf, sf.stamp)
|
||||||
case f: File => cachedSources.put(f, converter(f))
|
case f: File => cachedSources.put(f, converter(f))
|
||||||
}
|
}
|
||||||
view match {
|
|
||||||
case r: FileTreeRepository[FileCacheEntry] =>
|
|
||||||
r.register(options.classesDirectory ** AllPassFilter)
|
|
||||||
options.classpath.foreach {
|
|
||||||
case f if f.getName.endsWith(".jar") => r.register(f.toGlob)
|
|
||||||
case f => r.register(f ** AllPassFilter)
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
val allBinaries = new java.util.HashMap[File, Stamp]
|
val allBinaries = new java.util.HashMap[File, Stamp]
|
||||||
options.classpath.foreach {
|
options.classpath.foreach {
|
||||||
case f if f.getName.endsWith(".jar") =>
|
case f if f.getName.endsWith(".jar") =>
|
||||||
// This gives us the entry for the path itself, which is necessary if the path is a jar file
|
// This gives us the entry for the path itself, which is necessary if the path is a jar file
|
||||||
// rather than a directory.
|
// rather than a directory.
|
||||||
view.listEntries(f.toGlob) foreach { e =>
|
listEntries(f.toGlob) foreach { e =>
|
||||||
e.value match {
|
e.value match {
|
||||||
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case f =>
|
case f =>
|
||||||
view.listEntries(f ** "*.jar") foreach { e =>
|
listEntries(f ** AllPassFilter) foreach { e =>
|
||||||
e.value match {
|
e.value match {
|
||||||
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,8 @@ package internal
|
||||||
import sbt.BasicCommandStrings.ContinuousExecutePrefix
|
import sbt.BasicCommandStrings.ContinuousExecutePrefix
|
||||||
import sbt.Keys._
|
import sbt.Keys._
|
||||||
import sbt.internal.io.HybridPollingFileTreeRepository
|
import sbt.internal.io.HybridPollingFileTreeRepository
|
||||||
import sbt.io.FileTreeDataView.{ Entry, Observable, Observer, Observers }
|
import sbt.io.FileTreeDataView.{ Observable, Observer, Observers }
|
||||||
import sbt.io._
|
import sbt.io.{ FileTreeRepository, _ }
|
||||||
import sbt.io.syntax._
|
|
||||||
import sbt.util.Logger
|
import sbt.util.Logger
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -85,76 +84,8 @@ private[sbt] object FileManagement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def entryFilter(
|
|
||||||
include: FileFilter,
|
|
||||||
exclude: FileFilter
|
|
||||||
): Entry[FileCacheEntry] => Boolean = { e =>
|
|
||||||
val tp = e.typedPath
|
|
||||||
/*
|
|
||||||
* The TypedPath has the isDirectory and isFile properties embedded. By overriding
|
|
||||||
* these methods in java.io.File, FileFilters may be applied without needing to
|
|
||||||
* stat the file (which is expensive) for isDirectory and isFile checks.
|
|
||||||
*/
|
|
||||||
val file = new java.io.File(tp.toPath.toString) {
|
|
||||||
override def isDirectory: Boolean = tp.isDirectory
|
|
||||||
override def isFile: Boolean = tp.isFile
|
|
||||||
}
|
|
||||||
include.accept(file) && !exclude.accept(file)
|
|
||||||
}
|
|
||||||
private[sbt] def repo: Def.Initialize[Task[FileTreeRepository[FileCacheEntry]]] = Def.task {
|
private[sbt] def repo: Def.Initialize[Task[FileTreeRepository[FileCacheEntry]]] = Def.task {
|
||||||
lazy val msg = s"Tried to get FileTreeRepository for uninitialized state."
|
lazy val msg = s"Tried to get FileTreeRepository for uninitialized state."
|
||||||
state.value.get(Keys.globalFileTreeRepository).getOrElse(throw new IllegalStateException(msg))
|
state.value.get(Keys.globalFileTreeRepository).getOrElse(throw new IllegalStateException(msg))
|
||||||
}
|
}
|
||||||
private[sbt] def dataView: Def.Initialize[Task[FileTreeDataView[FileCacheEntry]]] = Def.task {
|
|
||||||
state.value
|
|
||||||
.get(Keys.globalFileTreeRepository)
|
|
||||||
.map(toDataView)
|
|
||||||
.getOrElse(FileTreeView.DEFAULT.asDataView(FileCacheEntry.default))
|
|
||||||
}
|
|
||||||
private def toDataView(r: FileTreeRepository[FileCacheEntry]): FileTreeDataView[FileCacheEntry] =
|
|
||||||
new FileTreeDataView[FileCacheEntry] {
|
|
||||||
private def reg(glob: Glob): FileTreeDataView[FileCacheEntry] = { r.register(glob); r }
|
|
||||||
override def listEntries(glob: Glob): Seq[Entry[FileCacheEntry]] = reg(glob).listEntries(glob)
|
|
||||||
override def list(glob: Glob): Seq[TypedPath] = reg(glob).list(glob)
|
|
||||||
override def close(): Unit = {}
|
|
||||||
}
|
|
||||||
private[sbt] def collectFiles(
|
|
||||||
dirs: ScopedTaskable[Seq[File]],
|
|
||||||
filter: ScopedTaskable[FileFilter],
|
|
||||||
excludes: ScopedTaskable[FileFilter]
|
|
||||||
): Def.Initialize[Task[Seq[File]]] =
|
|
||||||
Def.task {
|
|
||||||
val sourceDirs = dirs.toTask.value
|
|
||||||
val view: FileTreeDataView[FileCacheEntry] = dataView.value
|
|
||||||
val include = filter.toTask.value
|
|
||||||
val ex = excludes.toTask.value
|
|
||||||
val sourceFilter: Entry[FileCacheEntry] => Boolean = entryFilter(include, ex)
|
|
||||||
sourceDirs.flatMap { dir =>
|
|
||||||
view
|
|
||||||
.listEntries(dir.toPath ** AllPassFilter)
|
|
||||||
.flatMap {
|
|
||||||
case e if sourceFilter(e) => e.value.toOption.map(Stamped.file(e.typedPath, _))
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private[sbt] def appendBaseSources: Seq[Def.Setting[Task[Seq[File]]]] = Seq(
|
|
||||||
unmanagedSources := {
|
|
||||||
val sources = unmanagedSources.value
|
|
||||||
val include = (includeFilter in unmanagedSources).value
|
|
||||||
val excl = (excludeFilter in unmanagedSources).value
|
|
||||||
val baseDir = baseDirectory.value
|
|
||||||
val r: FileTreeDataView[FileCacheEntry] = dataView.value
|
|
||||||
if (sourcesInBase.value) {
|
|
||||||
val filter: Entry[FileCacheEntry] => Boolean = entryFilter(include, excl)
|
|
||||||
sources ++
|
|
||||||
r.listEntries(baseDir * AllPassFilter)
|
|
||||||
.flatMap {
|
|
||||||
case e if filter(e) => e.value.toOption.map(Stamped.file(e.typedPath, _))
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
} else sources
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import java.nio.file.{ WatchService => _ }
|
||||||
|
|
||||||
|
import sbt.internal.util.appmacro.MacroDefaults
|
||||||
|
import sbt.io.FileTreeDataView.Entry
|
||||||
|
import sbt.io._
|
||||||
|
|
||||||
|
import scala.language.experimental.macros
|
||||||
|
|
||||||
|
object FileTree {
|
||||||
|
trait Repository extends sbt.internal.Repository[Seq, Glob, Entry[FileCacheEntry]]
|
||||||
|
object Repository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a default [[Repository]] that works within a task definition, e.g. Def.task. It's
|
||||||
|
* implemented as a macro so that it can call `.value` on a TaskKey. Using a macro also allows
|
||||||
|
* us to use classes that aren't actually available in this project, e.g. sbt.Keys.
|
||||||
|
* @return a [[Repository]] instance
|
||||||
|
*/
|
||||||
|
implicit def default: FileTree.Repository = macro MacroDefaults.fileTreeRepository
|
||||||
|
private[sbt] object polling extends Repository {
|
||||||
|
val view = FileTreeView.DEFAULT.asDataView(FileCacheEntry.default)
|
||||||
|
override def get(key: Glob): Seq[Entry[FileCacheEntry]] = view.listEntries(key)
|
||||||
|
override def close(): Unit = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private class ViewRepository(underlying: FileTreeDataView[FileCacheEntry]) extends Repository {
|
||||||
|
override def get(key: Glob): Seq[Entry[FileCacheEntry]] = underlying.listEntries(key)
|
||||||
|
override def close(): Unit = {}
|
||||||
|
}
|
||||||
|
private class CachingRepository(underlying: FileTreeRepository[FileCacheEntry])
|
||||||
|
extends Repository {
|
||||||
|
override def get(key: Glob): Seq[Entry[FileCacheEntry]] = {
|
||||||
|
underlying.register(key)
|
||||||
|
underlying.listEntries(key)
|
||||||
|
}
|
||||||
|
override def close(): Unit = underlying.close()
|
||||||
|
}
|
||||||
|
private[sbt] def repository(underlying: FileTreeDataView[FileCacheEntry]): Repository =
|
||||||
|
underlying match {
|
||||||
|
case r: FileTreeRepository[FileCacheEntry] => new CachingRepository(r)
|
||||||
|
case v => new ViewRepository(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import sbt.io.{ Glob, TypedPath }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve files from a repository. This should usually be an extension class for
|
||||||
|
* sbt.io.internal.Glob (or a Traversable collection of source instances) that allows us to
|
||||||
|
* actually retrieve the files corresponding to those sources.
|
||||||
|
*/
|
||||||
|
sealed trait GlobLister extends Any {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sources described this [[GlobLister]].
|
||||||
|
*
|
||||||
|
* @param repository the [[FileTree.Repository]] to delegate file i/o.
|
||||||
|
* @return the files described by this [[GlobLister]].
|
||||||
|
*/
|
||||||
|
def all(implicit repository: FileTree.Repository): Seq[Stamped.File]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unique sources described this [[GlobLister]].
|
||||||
|
*
|
||||||
|
* @param repository the [[FileTree.Repository]] to delegate file i/o.
|
||||||
|
* @return the files described by this [[GlobLister]] with any duplicates removed.
|
||||||
|
*/
|
||||||
|
def unique(implicit repository: FileTree.Repository): Seq[Stamped.File]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides implicit definitions to provide a [[GlobLister]] given a Glob or
|
||||||
|
* Traversable[Glob].
|
||||||
|
*/
|
||||||
|
object GlobLister extends GlobListers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides implicit definitions to provide a [[GlobLister]] given a Glob or
|
||||||
|
* Traversable[Glob].
|
||||||
|
*/
|
||||||
|
private[sbt] trait GlobListers {
|
||||||
|
import GlobListers._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a [[GlobLister]] given a particular [[Glob]]s.
|
||||||
|
*
|
||||||
|
* @param source the input Glob
|
||||||
|
*/
|
||||||
|
implicit def fromGlob(source: Glob): GlobLister = new impl(source :: Nil)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a [[GlobLister]] given a collection of Globs. If the input collection type
|
||||||
|
* preserves uniqueness, e.g. `Set[Glob]`, then the output of [[GlobLister.all]] will be
|
||||||
|
* the unique source list. Otherwise duplicates are possible in all and it is necessary to call
|
||||||
|
* [[GlobLister.unique]] to de-duplicate the files.
|
||||||
|
*
|
||||||
|
* @param sources the collection of sources
|
||||||
|
* @tparam T the source collection type
|
||||||
|
*/
|
||||||
|
implicit def fromTraversableGlob[T <: Traversable[Glob]](sources: T): GlobLister =
|
||||||
|
new impl(sources)
|
||||||
|
}
|
||||||
|
private[internal] object GlobListers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements [[GlobLister]] given a collection of Globs. If the input collection type
|
||||||
|
* preserves uniqueness, e.g. `Set[Glob]`, then the output will be the unique source list.
|
||||||
|
* Otherwise duplicates are possible.
|
||||||
|
*
|
||||||
|
* @param globs the input globs
|
||||||
|
* @tparam T the collection type
|
||||||
|
*/
|
||||||
|
private class impl[T <: Traversable[Glob]](val globs: T) extends AnyVal with GlobLister {
|
||||||
|
private def get[T0 <: Traversable[Glob]](
|
||||||
|
traversable: T0,
|
||||||
|
repository: FileTree.Repository
|
||||||
|
): Seq[Stamped.File] =
|
||||||
|
traversable.flatMap { glob =>
|
||||||
|
val sourceFilter: TypedPath => Boolean = glob.toTypedPathFilter
|
||||||
|
repository.get(glob).flatMap {
|
||||||
|
case e if sourceFilter(e.typedPath) => e.value.toOption.map(Stamped.file(e.typedPath, _))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}.toIndexedSeq: Seq[Stamped.File]
|
||||||
|
|
||||||
|
override def all(implicit repository: FileTree.Repository): Seq[Stamped.File] =
|
||||||
|
get(globs, repository)
|
||||||
|
override def unique(implicit repository: FileTree.Repository): Seq[Stamped.File] =
|
||||||
|
get(globs.toSet[Glob], repository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,8 @@ package object sbt
|
||||||
with sbt.BuildSyntax
|
with sbt.BuildSyntax
|
||||||
with sbt.OptionSyntax
|
with sbt.OptionSyntax
|
||||||
with sbt.SlashSyntax
|
with sbt.SlashSyntax
|
||||||
with sbt.Import {
|
with sbt.Import
|
||||||
|
with sbt.internal.GlobListers {
|
||||||
// IO
|
// IO
|
||||||
def uri(s: String): URI = new URI(s)
|
def uri(s: String): URI = new URI(s)
|
||||||
def file(s: String): File = new File(s)
|
def file(s: String): File = new File(s)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue