Refactor slash syntax to mostly use methods

**Problem**
Slash syntax is currently implemented via a series of implicit
converters (Conversion), which is not nice, partly because
the behavior is difficult to follow.

**Solution**
This removes all the implicit converters and moves the slashes
into:
1. / methods under Reference for subproject scoping.
2. / methods under ScopeAxis with Reference type constraint
   for the initial Zero scoping.
3. Return RefThenConfig structure for intermediate config scoping.
4. / method under `Scoped`, which is base trait for the keys
   to implement task scoping e.g. `compile / scalacOptions`.
5. Extension methods for ConfigKey.
6. Extension methods for AttributeKey.
This commit is contained in:
Eugene Yokota 2024-11-24 23:57:11 -05:00
parent 19ed461933
commit 70d7ddb258
37 changed files with 86 additions and 117 deletions

View File

@ -16,7 +16,6 @@ import java.nio.file.Path
import sbt.internal.util.complete.DefaultParsers.validID
import Def.{ ScopedKey, Setting, Settings }
import Scope.GlobalScope
import sbt.SlashSyntax0.given
import sbt.internal.parser.SbtParser
import sbt.io.IO
import scala.jdk.CollectionConverters.*

View File

@ -12,7 +12,6 @@ import sbt.Def.{ Initialize, ScopedKey }
import sbt.Previous._
import sbt.Scope.Global
import sbt.ScopeAxis.Select
import sbt.SlashSyntax0.given
import sbt.internal.util._
import sbt.std.TaskExtra._
import sbt.util.StampedFormat

View File

@ -14,8 +14,7 @@ import java.net.URI
import sbt.internal.util.AttributeKey
import sbt.io.IO
import sbt.librarymanagement.Configuration
import sbt.ScopeAxis.{ Select, This }
import sbt.SlashSyntax.RichConfiguration
import sbt.ScopeAxis.{ Select, This, RefThenConfig }
// in all of these, the URI must be resolved and normalized before it is definitive
@ -26,13 +25,13 @@ sealed trait Reference:
private[sbt] def asScope: Scope =
Scope(asScopeAxis, This, This, This)
def /(c: ConfigKey): RichConfiguration = RichConfiguration(asScope.rescope(c))
def /(c: ConfigKey): RefThenConfig = RefThenConfig(asScope.rescope(c))
def /(c: Configuration): RichConfiguration = RichConfiguration(asScope.rescope(c))
def /(c: Configuration): RefThenConfig = RefThenConfig(asScope.rescope(c))
// This is for handling `Zero / Zero / name`.
def /(configAxis: ScopeAxis[ConfigKey]): RichConfiguration =
new RichConfiguration(asScope.copy(config = configAxis))
def /(configAxis: ScopeAxis[ConfigKey]): RefThenConfig =
RefThenConfig(asScope.copy(config = configAxis))
final def /[K](key: Scoped.ScopingSetting[K]): K = key.rescope(asScope)

View File

@ -32,6 +32,9 @@ final case class Scope private (
def rescope(config: ConfigKey): Scope = copy(config = Select(config))
def rescope(task: AttributeKey[?]): Scope = copy(task = Select(task))
final def /[K](key: Scoped.ScopingSetting[K]): K = scope(key)
def scope[K](key: Scoped.ScopingSetting[K]): K = key.rescope(this)
def copy(
project: ScopeAxis[Reference] = this.project,
config: ScopeAxis[ConfigKey] = this.config,

View File

@ -9,8 +9,12 @@
package sbt
import sbt.internal.util.Util.*
import sbt.librarymanagement.Configuration
import sbt.internal.util.AttributeKey
enum ScopeAxis[+A1]:
import ScopeAxis.RefThenConfig
/**
* Select is a type constructor that is used to wrap type `S`
* to make a scope component, equivalent of Some in Option.
@ -46,6 +50,19 @@ enum ScopeAxis[+A1]:
def map[A2](f: A1 => A2): ScopeAxis[A2] =
foldStrict(s => Select(f(s)): ScopeAxis[A2], Zero: ScopeAxis[A2], This: ScopeAxis[A2])
def asScope(using A1 <:< Reference): Scope =
Scope(this.asInstanceOf[ScopeAxis[Reference]], This, This, This)
inline def /[K](key: Scoped.ScopingSetting[K])(using A1 <:< Reference): K = key.rescope(asScope)
inline def /(c: ConfigKey)(using A1 <:< Reference): RefThenConfig =
RefThenConfig(asScope.rescope(c))
inline def /(c: Configuration)(using A1 <:< Reference): RefThenConfig =
RefThenConfig(asScope.rescope(c))
// This is for handling `Zero / Zero / name`.
inline def /(configAxis: ScopeAxis[ConfigKey])(using A1 <:< Reference): RefThenConfig =
RefThenConfig(asScope.copy(config = configAxis))
end ScopeAxis
object ScopeAxis:
@ -55,4 +72,15 @@ object ScopeAxis:
def fromOption[A1](o: Option[A1]): ScopeAxis[A1] = o match
case Some(v) => ScopeAxis.Select(v)
case None => ScopeAxis.Zero
/** Temporary data structure to capture first two axis using slash syntax. */
class RefThenConfig(val scope: Scope):
override def toString(): String = scope.toString()
inline def /[K](key: Scoped.ScopingSetting[K]): K = scope / key
inline def /(task: AttributeKey[?]): Scope = scope.copy(task = Select(task))
/** This is for handling `Zero / Zero / Zero / name`. */
inline def /(taskAxis: ScopeAxis[AttributeKey[?]]): Scope = scope.copy(task = taskAxis)
end RefThenConfig
end ScopeAxis

View File

@ -13,88 +13,32 @@ import sbt.internal.util.AttributeKey
import sbt.ScopeAxis.{ Select, This }
/**
* SlashSyntax implements the slash syntax to scope keys for build.sbt DSL.
* The implicits are set up such that the order that the scope components
* must appear in the order of the project axis, the configuration axis, and
* the task axis. This ordering is the same as the shell syntax.
* SlashSyntax implements part of the slash syntax to scope keys for build.sbt DSL.
*
* @example
* {{{
* Global / cancelable := true
* ThisBuild / scalaVersion := "2.12.2"
* Test / test := ()
* console / scalacOptions += "-deprecation"
* console.key / scalacOptions += "-deprecation"
* Compile / console / scalacOptions += "-Ywarn-numeric-widen"
* projA / Compile / console / scalacOptions += "-feature"
* Zero / Zero / name := "foo"
* }}}
*/
trait SlashSyntax:
import SlashSyntax.*
given Conversion[ScopeAxis[Reference], RichReference] =
(a: ScopeAxis[Reference]) => RichReference(Scope(a, This, This, This))
given Conversion[ConfigKey, RichConfiguration] =
(c: ConfigKey) => RichConfiguration(Scope(This, Select(c), This, This))
given Conversion[Configuration, RichConfiguration] =
(c: Configuration) => (c: ConfigKey)
/**
* This handles task scoping an existing scoped key (such as `Compile / test`)
* into a task scoping in `(Compile / test) / name`.
* Handles slash syntax for `key.key / key`.
*/
given Conversion[Scoped, Scope] =
(t: Scoped) => t.scope.copy(task = Select(t.key))
extension [A1](a: AttributeKey[A1])
def asScope: Scope = Scope(This, This, Select(a), This)
def /[K](key: Scoped.ScopingSetting[K]): K = a.asScope.scope(key)
given Conversion[Scoped, RichScope] =
(t: Scoped) => RichScope(t: Scope)
given [A1]: Conversion[AttributeKey[A1], Scope] =
(a: AttributeKey[A1]) => Scope(This, This, Select(a), This)
given [A1]: Conversion[AttributeKey[A1], RichScope] =
(a: AttributeKey[A1]) => RichScope(a: Scope)
given Conversion[Scope, RichScope] =
(scope: Scope) => RichScope(scope)
end SlashSyntax
object SlashSyntax:
sealed trait HasSlashKey {
protected def scope: Scope
def /[K](key: Scoped.ScopingSetting[K]): K = key.rescope(scope)
}
sealed trait HasSlashKeyOrAttrKey extends HasSlashKey {
def /(key: AttributeKey[?]): Scope = scope.rescope(key)
}
/** RichReference wraps a reference to provide the `/` operator for scoping. */
final class RichReference(protected val scope: Scope) extends HasSlashKeyOrAttrKey {
def /(c: ConfigKey): RichConfiguration = new RichConfiguration(scope.rescope(c))
def /(c: Configuration): RichConfiguration = new RichConfiguration(scope.rescope(c))
// This is for handling `Zero / Zero / name`.
def /(configAxis: ScopeAxis[ConfigKey]): RichConfiguration =
new RichConfiguration(scope.copy(config = configAxis))
}
/** RichConfiguration wraps a configuration to provide the `/` operator for scoping. */
final class RichConfiguration(protected val scope: Scope) extends HasSlashKeyOrAttrKey {
// This is for handling `Zero / Zero / Zero / name`.
def /(taskAxis: ScopeAxis[AttributeKey[?]]): Scope =
scope.copy(task = taskAxis)
}
/**
* RichScope wraps a general scope to provide the `/` operator for scoping.
*/
final class RichScope(protected val scope: Scope) extends HasSlashKeyOrAttrKey
extension (c: ConfigKey)
def asScope: Scope = Scope(This, Select(c), This, This)
def /[K](key: Scoped.ScopingSetting[K]): K = c.asScope.scope(key)
def /(task: AttributeKey[?]): Scope = c.asScope.copy(task = Select(task))
extension (c: Configuration)
def asScope: Scope = (c: ConfigKey).asScope
def /[K](key: Scoped.ScopingSetting[K]): K = (c: ConfigKey) / key
def /(task: AttributeKey[?]): Scope = (c: ConfigKey) / task
end SlashSyntax
private[sbt] object SlashSyntax0 extends SlashSyntax

View File

@ -33,6 +33,9 @@ sealed trait Scoped extends Equals:
})
override def hashCode(): Int = (scope, key).##
final def /[K](subkey: Scoped.ScopingSetting[K]): K =
scope.copy(task = Select(key)).scope(subkey)
end Scoped
/** A SettingKey, TaskKey or `Initialize[Task]` that can be converted into an `Initialize[Task]`. */
@ -76,9 +79,6 @@ sealed abstract class SettingKey[A1]
private[sbt] final inline def rescope(scope: Scope): SettingKey[A1] =
Scoped.scopedSetting(Scope.replaceThis(this.scope)(scope), this.key)
final def /[A1](subkey: Scoped.ScopingSetting[A1]): A1 =
subkey.rescope(scope.copy(task = Select(key)))
/** Internal function for the setting macro. */
inline def settingMacro[A](inline a: A): Initialize[A] =
${ std.SettingMacro.settingMacroImpl[A]('a) }
@ -157,9 +157,6 @@ sealed abstract class TaskKey[A1]
private[sbt] final inline def rescope(scope: Scope): TaskKey[A1] =
Scoped.scopedTask(Scope.replaceThis(this.scope)(scope), this.key)
final def /[A1](subkey: Scoped.ScopingSetting[A1]): A1 =
subkey.rescope(scope.copy(task = Select(key)))
inline def +=[A2](inline v: A2)(using Append.Value[A1, A2]): Setting[Task[A1]] =
append1[A2](taskMacro(v))
@ -240,9 +237,6 @@ sealed trait InputKey[A1]
private[sbt] final inline def rescope(scope: Scope): InputKey[A1] =
Scoped.scopedInput(Scope.replaceThis(this.scope)(scope), this.key)
final def /[A1](subkey: Scoped.ScopingSetting[A1]): A1 =
subkey.rescope(scope.copy(task = Select(key)))
private inline def inputTaskMacro[A2](inline a: A2): Def.Initialize[InputTask[A2]] =
${ std.InputTaskMacro.inputTaskMacroImpl('a) }

View File

@ -11,7 +11,6 @@ object AppendSpec {
val onLoad = SettingKey[State => State]("onLoad")
import Scope.Global
import SlashSyntax0.given
def doSideEffect(): Unit = ()

View File

@ -13,7 +13,7 @@ import hedgehog.*
import hedgehog.runner.*
import Scope.{ Global, ThisScope }
import ScopeAxis.{ Select, This, Zero }
import SlashSyntax0.given
import SlashSyntax0.*
import BuildSettingsInstances.given
import _root_.sbt.internal.util.AttributeKey
@ -23,6 +23,7 @@ object SlashSyntaxSpec extends Properties:
example("Zero / compile", zeroCompile),
property("Reference / key", propReferenceKey),
property("Reference / Config / key", propReferenceConfigKey),
example("Zero / Zero / compile", zeroZeroCompile),
property("Reference / task.key / key", propReferenceAttrKeyKey),
property("Reference / task / key", propReferenceTaskKey),
property("Reference / inputtask / key", propReferenceInputTaskKey),
@ -37,6 +38,7 @@ object SlashSyntaxSpec extends Properties:
property("task / key", propTaskKey),
property("inputtask / key", propInputTaskKey),
property("Scope / key", propScopeKey),
property("Reference / Config? / key", propReferenceConfigAxisKey),
property("Reference? / key", propReferenceAxisKey),
property("Reference? / Config? / key", propReferenceAxisConfigAxisKey),
// property("Reference? / task.key? / key", propReferenceAxisAttrKeyAxisKey),
@ -65,6 +67,10 @@ object SlashSyntaxSpec extends Properties:
val actual = Zero / compile
Result.assert(actual.scope.project == Zero && actual.key == compile.key)
def zeroZeroCompile: Result =
val actual = Zero / Zero / compile
Result.assert(actual.scope.project == Zero && actual.key == compile.key)
def propReferenceKey: Property =
for
ref <- gen[Reference].forAll
@ -330,6 +336,23 @@ object SlashSyntaxSpec extends Properties:
else true)
)
def propReferenceConfigAxisKey: Property =
for
ref <- gen[Reference].forAll
config <- gen[ScopeAxis[ConfigKey]].forAll
k <- genKey[Unit].forAll
actual = k match
case k: InputKey[?] => ref / config / k
case k: TaskKey[?] => ref / config / k
case k: SettingKey[?] => ref / config / k
yield Result.assert(
actual.key == k.key &&
(if k.scope.project == This then actual.scope.project == Select(ref)
else true) &&
(if k.scope.config == This then actual.scope.config == config
else true)
)
def propReferenceAxisKey: Property =
for
ref <- gen[ScopeAxis[Reference]].forAll

View File

@ -13,6 +13,7 @@ import sjsonnew._
import sbt.Def.{ Setting, inputKey, settingKey, taskKey }
import sbt.Scope.Global
import sbt.ScopeAxis.Zero
import sbt.SlashSyntax0.*
import sbt.librarymanagement.ModuleID
import sbt.librarymanagement.syntax._
import sbt.{ LocalProject, ProjectReference, ThisBuild }

View File

@ -13,7 +13,6 @@ import sbt.Def.{ ScopedKey, Setting }
import sbt.Keys._
import sbt.ProjectExtra.extract
import sbt.ScopeAxis.{ Select, Zero }
import sbt.SlashSyntax0.given
import sbt.internal.Act
import sbt.internal.CommandStrings._
import sbt.internal.inc.ScalaInstance

View File

@ -98,7 +98,7 @@ import scala.util.control.NonFatal
import scala.xml.NodeSeq
// incremental compiler
import sbt.SlashSyntax0.given
import sbt.SlashSyntax0.*
import sbt.internal.inc.{
Analysis,
AnalyzingCompiler,

View File

@ -16,7 +16,6 @@ import sbt.Keys.{ TaskProgress => _, name => _, _ }
import sbt.BuildExtra.*
import sbt.ProjectExtra.*
import sbt.Scope.Global
import sbt.SlashSyntax0.given
import sbt.internal.Aggregation.KeyValue
import sbt.internal.TaskName._
import sbt.internal._

View File

@ -17,7 +17,6 @@ import sbt.util.Show
import std.Transform.DummyTaskMap
import sbt.EvaluateTask.extractedTaskConfig
import sbt.ProjectExtra.setProject
import sbt.SlashSyntax0.given
final case class Extracted(
structure: BuildStructure,

View File

@ -14,7 +14,6 @@ import sbt.Keys._
import Scope.GlobalScope
import Def.ScopedKey
import sbt.ScopeAxis.Select
import sbt.SlashSyntax0.given
import sbt.internal.Load
import sbt.internal.CommandStrings._
import Cross.{ spacedFirst, requireSession }

View File

@ -38,7 +38,6 @@ import Keys.{
import Project.LoadAction
import Scope.{ Global, ThisScope }
import sbt.ScopeAxis.Select
import sbt.SlashSyntax0.given
import Def.{ Flattened, Initialize, ScopedKey, Setting }
import sbt.internal.{
Load,

View File

@ -22,7 +22,7 @@ import sbt.Project.{ inConfig => _, * }
import sbt.ProjectExtra.*
import sbt.ScopeFilter.Make._
import sbt.ScopeAxis.Select
import sbt.SlashSyntax0.given
import sbt.SlashSyntax0.*
import sbt.coursierint.LMCoursier
import sbt.internal.inc.{ HashUtil, JarUtils }
import sbt.internal.librarymanagement._

View File

@ -15,7 +15,7 @@ import sbt.Keys._
import sbt.nio.Keys._
import sbt.ProjectExtra.*
import sbt.ScopeFilter.Make._
import sbt.SlashSyntax0.given
import sbt.SlashSyntax0.*
import sbt.internal.inc.ModuleUtilities
import sbt.internal.inc.classpath.ClasspathUtil
import sbt.internal.librarymanagement.cross.CrossVersionUtil

View File

@ -15,7 +15,6 @@ import sbt.Def.{ ScopedKey, Settings }
import sbt.Keys.{ showSuccess, showTiming, timingFormat }
import sbt.ProjectExtra.*
import sbt.ScopeAxis.{ Select, Zero }
import sbt.SlashSyntax0.given
import sbt.internal.util.complete.Parser
import sbt.internal.util.complete.Parser.{ failure, seq, success }
import sbt.internal.util._

View File

@ -17,7 +17,6 @@ import Def.{ ScopeLocal, ScopedKey, Setting, displayFull }
import BuildPaths.outputDirectory
import Scope.GlobalScope
import sbt.ScopeAxis.{ Select, This, Zero }
import sbt.SlashSyntax0.given
import BuildStreams.Streams
import sbt.io.syntax._
import sbt.internal.inc.MappedFileConverter

View File

@ -14,7 +14,6 @@ import java.net.URL
import java.nio.file.Path
import sbt.ClassLoaderLayeringStrategy._
import sbt.Keys._
import sbt.SlashSyntax0.given
import sbt.internal.classpath.ClassLoaderCache
import sbt.internal.inc.ScalaInstance
import sbt.internal.inc.classpath.ClasspathUtil

View File

@ -17,7 +17,6 @@ import sbt.Keys._
// import sbt.Project.richInitializeTask
import sbt.ProjectExtra.*
import sbt.ScopeAxis.Zero
import sbt.SlashSyntax0.given
import sbt.io.syntax._
import sbt.nio.Keys._
import sbt.nio.file._
@ -162,7 +161,7 @@ private[sbt] object Clean {
): Def.Initialize[Task[Unit]] =
(Def
.task {
taskKey.scope / taskKey.key
taskKey.scope.rescope(taskKey.key)
})
.flatMapTask { case scope =>
Def.task {

View File

@ -24,7 +24,6 @@ import sbt.BasicCommandStrings._
import sbt.Def._
import sbt.Keys._
import sbt.ProjectExtra.extract
import sbt.SlashSyntax0.given
import sbt.internal.Continuous.{ ContinuousState, FileStampRepository }
import sbt.internal.LabeledFunctions._
import sbt.internal.io.WatchState

View File

@ -19,7 +19,6 @@ import sbt.Cross._
import sbt.Def.{ ScopedKey, Setting }
import sbt.ProjectExtra.extract
import sbt.ScopeAxis.{ Select, Zero }
import sbt.SlashSyntax0.given
import sbt.internal.util.complete.DefaultParsers._
import sbt.internal.util.AttributeKey
import sbt.internal.util.complete.{ DefaultParsers, Parser }

View File

@ -19,7 +19,6 @@ import java.util.concurrent.atomic.{ AtomicLong, AtomicReference }
import sbt.Def.{ Classpath, ScopedKey, Setting }
import sbt.ProjectExtra.extract
import sbt.Scope.GlobalScope
import sbt.SlashSyntax0.given
import sbt.internal.inc.classpath.ClasspathFilter
import sbt.internal.util.{ Attributed, ManagedLogger }
import sbt.io.syntax._

View File

@ -22,7 +22,7 @@ import Def.{ ScopedKey, Setting }
import Keys._
import Configurations.{ Compile, Runtime }
import sbt.ProjectExtra.{ extract, runUnloadHooks, setProject }
import sbt.SlashSyntax0.given
import sbt.SlashSyntax0.*
import java.io.File
import org.apache.ivy.core.module.{ descriptor, id }
import descriptor.ModuleDescriptor, id.ModuleRevisionId

View File

@ -26,7 +26,7 @@ import Def.Setting
import Keys._
import Scope.Global
import sbt.ProjectExtra.{ extract, setProject }
import sbt.SlashSyntax0.given
import sbt.SlashSyntax0.*
import sbt.io.IO
import xsbti.HashedVirtualFileRef

View File

@ -14,7 +14,6 @@ import sbt.internal.util.{ FilePosition, NoPosition, SourcePosition }
import java.io.File
import ProjectExtra.{ extract, scopedKeyData }
import Scope.Global
import sbt.SlashSyntax0.given
import sbt.Def._
object LintUnused {

View File

@ -16,7 +16,7 @@ import sbt.Project.inScope
import sbt.ProjectExtra.{ prefixConfigs, setProject, showLoadingKey, structure }
import sbt.Scope.GlobalScope
import sbt.ScopeAxis.{ Select, Zero }
import sbt.SlashSyntax0.given
import sbt.SlashSyntax0.*
import sbt.internal.BuildStreams._
import sbt.internal.inc.classpath.ClasspathUtil
import sbt.internal.inc.{ MappedFileConverter, ScalaInstance, ZincLmUtil, ZincUtil }

View File

@ -14,7 +14,6 @@ import sbt.Keys._
import sbt.ProjectExtra.showContextKey
import sbt.Scope.Global
import sbt.ScopeAxis.{ Select, Zero }
import sbt.SlashSyntax0.given
import sbt.internal.util.MainAppender._
import sbt.internal.util.{ Terminal => ITerminal, _ }
import sbt.util.{ Level, Logger, LoggerContext }

View File

@ -19,7 +19,7 @@ import EvaluateConfigurations.{ evaluateConfiguration => evaluate }
import Configurations.Compile
import Scope.Global
import sbt.ProjectExtra.{ extract, setProject }
import sbt.SlashSyntax0.given
import sbt.SlashSyntax0.*
import sbt.io.{ Hash, IO }

View File

@ -17,7 +17,6 @@ import ProjectExtra.{ relation }
import Def.{ ScopedKey, Setting }
import Scope.Global
import sbt.ScopeAxis.{ Select, This, Zero }
import sbt.SlashSyntax0.given
import complete._
import DefaultParsers._

View File

@ -14,7 +14,6 @@ import sbt.Keys._
// import sbt.Project.richInitializeTask
import sbt.ProjectExtra.*
import sbt.ScopeAxis.Zero
import sbt.SlashSyntax0.given
import sbt.internal.io.Source
import sbt.internal.nio.Globs
import sbt.internal.util.complete.Parser

View File

@ -18,7 +18,6 @@ import sbt.Keys._
import sbt.ProjectExtra.*
import sbt.ScopeFilter.Make._
import sbt.Scoped.richTaskSeq
import sbt.SlashSyntax0.given
import sbt.StandardMain.exchange
import sbt.internal.bsp._
import sbt.internal.langserver.ErrorCodes

View File

@ -23,7 +23,6 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import sbt.BasicCommandStrings.{ Shutdown, TerminateAction }
import sbt.ProjectExtra.extract
import sbt.SlashSyntax0.given
import sbt.internal.langserver.{ CancelRequestParams, ErrorCodes, LogMessageParams, MessageType }
import sbt.internal.protocol.{
JsonRpcNotificationMessage,

View File

@ -15,7 +15,6 @@ import sbt.BasicCommandStrings.{ RebootCommand, Shutdown, TerminateAction }
import sbt.Keys.{ baseDirectory, pollInterval, state }
import sbt.ProjectExtra.extract
import sbt.Scope.Global
import sbt.SlashSyntax0.given
import sbt.internal.CommandStrings.LoadProject
import sbt.internal.SysProp
import sbt.internal.util.{ AttributeKey, Terminal }

View File

@ -14,7 +14,6 @@ import java.util.concurrent.ConcurrentHashMap
import sbt.Keys._
import sbt.ScopeAxis.{ Select, Zero }
import sbt.SlashSyntax0.given
import sbt.internal.Clean.ToSeqPath
import sbt.internal.Continuous.FileStampRepository
import sbt.internal.util.KeyTag