Skip to content

Commit

Permalink
Merge pull request sbt#7644 from eed3si9n/wip/testquick
Browse files Browse the repository at this point in the history
  • Loading branch information
eed3si9n committed Sep 12, 2024
2 parents 941ae3d + a0597e9 commit 1cb3105
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 191 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,11 @@ trait Cont:
$input,
codeContentHash = Digest.dummy($codeContentHash),
extraHash = Digest.dummy($extraHash),
tags = $tagsExpr
tags = $tagsExpr,
config = $cacheConfigExpr,
)({ _ =>
$block
})($cacheConfigExpr)
})
}

// This will generate following code for Def.declareOutput(...):
Expand Down
16 changes: 15 additions & 1 deletion main-actions/src/main/scala/sbt/Pkg.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,21 @@ object Pkg:
val sources: Seq[(HashedVirtualFileRef, String)],
val jar: VirtualFileRef,
val options: Seq[PackageOption]
)
) {
import sbt.util.CacheImplicits.hashedVirtualFileRefToStr
private def sourcesStr: String =
sources
.map { case (k, v) =>
s"${hashedVirtualFileRefToStr(k)}=$v"
}
.mkString(",\n ")
override def toString(): String = s"""Configuration(
sources = Seq(${sourcesStr}),
jar = ...,
options = ...,
)
"""
}

object Configuration:
given IsoLList.Aux[
Expand Down
3 changes: 2 additions & 1 deletion main-actions/src/main/scala/sbt/Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ object Tests {
in.filter(t => seen.add(f(t)))
}

// Called by Defaults
def apply(
frameworks: Map[TestFramework, Framework],
testLoader: ClassLoader,
Expand Down Expand Up @@ -340,7 +341,7 @@ object Tests {
apply(frameworks, testLoader, runners, o, config, log)
}

def testTask(
private[sbt] def testTask(
loader: ClassLoader,
frameworks: Map[TestFramework, Framework],
runners: Map[TestFramework, Runner],
Expand Down
85 changes: 13 additions & 72 deletions main/src/main/scala/sbt/Defaults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ import sbt.SlashSyntax0._
import sbt.internal.inc.{
Analysis,
AnalyzingCompiler,
FileAnalysisStore,
ManagedLoggedReporter,
MixedAnalyzingCompiler,
ScalaInstance
Expand Down Expand Up @@ -140,6 +139,7 @@ import xsbti.compile.{
TastyFiles,
TransactionalManagerType
}
import sbt.internal.IncrementalTest

object Defaults extends BuildCommon {
final val CacheDirectoryName = "cache"
Expand All @@ -153,18 +153,6 @@ object Defaults extends BuildCommon {

def lock(app: xsbti.AppConfiguration): xsbti.GlobalLock = LibraryManagement.lock(app)

private[sbt] def extractAnalysis(
metadata: StringAttributeMap,
converter: FileConverter
): Option[CompileAnalysis] =
def asBinary(file: File) = FileAnalysisStore.binary(file).get.asScala
def asText(file: File) = FileAnalysisStore.text(file).get.asScala
for
ref <- metadata.get(Keys.analysis)
file = converter.toPath(VirtualFileRef.of(ref)).toFile
content <- asBinary(file).orElse(asText(file))
yield content.getAnalysis

private[sbt] def globalDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] =
Def.defaultSettings(inScope(GlobalScope)(ss))

Expand Down Expand Up @@ -1323,7 +1311,8 @@ object Defaults extends BuildCommon {
testListeners :== Nil,
testOptions :== Nil,
testResultLogger :== TestResultLogger.Default,
testOnly / testFilter :== (selectedFilter _)
testOnly / testFilter :== (IncrementalTest.selectedFilter _),
extraTestDigests :== Nil,
)
)
lazy val testTasks: Seq[Setting[_]] =
Expand All @@ -1342,7 +1331,11 @@ object Defaults extends BuildCommon {
.storeAs(definedTestNames)
.triggeredBy(compile)
.value,
testQuick / testFilter := testQuickFilter.value,
definedTestDigests := IncrementalTest.definedTestDigestTask
.triggeredBy(compile)
.value,
testQuick / testFilter := IncrementalTest.filterTask.value,
extraTestDigests ++= IncrementalTest.extraTestDigestsTask.value,
executeTests := {
import sbt.TupleSyntax.*
(
Expand Down Expand Up @@ -1422,7 +1415,10 @@ object Defaults extends BuildCommon {
),
Keys.logLevel.?.value.getOrElse(stateLogLevel),
) +:
new TestStatusReporter(succeededFile((test / streams).value.cacheDirectory)) +:
TestStatusReporter(
definedTestDigests.value,
Def.cacheConfiguration.value,
) +:
(TaskZero / testListeners).value
},
testOptions := Tests.Listeners(testListeners.value) +: (TaskZero / testOptions).value,
Expand Down Expand Up @@ -1491,46 +1487,6 @@ object Defaults extends BuildCommon {
)
}

def testQuickFilter: Initialize[Task[Seq[String] => Seq[String => Boolean]]] =
Def.task {
val cp = (test / fullClasspath).value
val s = (test / streams).value
val converter = fileConverter.value
val analyses = cp
.flatMap(a => extractAnalysis(a.metadata, converter))
.collect { case analysis: Analysis => analysis }
val succeeded = TestStatus.read(succeededFile(s.cacheDirectory))
val stamps = collection.mutable.Map.empty[String, Long]
def stamp(dep: String): Option[Long] =
analyses.flatMap(internalStamp(dep, _, Set.empty)).maxOption
def internalStamp(c: String, analysis: Analysis, alreadySeen: Set[String]): Option[Long] = {
if (alreadySeen.contains(c)) None
else
def computeAndStoreStamp: Option[Long] = {
import analysis.{ apis, relations }
val internalDeps = relations
.internalClassDeps(c)
.flatMap(internalStamp(_, analysis, alreadySeen + c))
val externalDeps = relations.externalDeps(c).flatMap(stamp)
val classStamps = relations.productClassName.reverse(c).flatMap { pc =>
apis.internal.get(pc).map(_.compilationTimestamp)
}
val maxStamp = (internalDeps ++ externalDeps ++ classStamps).maxOption
maxStamp.foreach(maxStamp => stamps(c) = maxStamp)
maxStamp
}
stamps.get(c).orElse(computeAndStoreStamp)
}
def noSuccessYet(test: String) = succeeded.get(test) match {
case None => true
case Some(ts) => stamps.synchronized(stamp(test)).exists(_ > ts)
}
args =>
for (filter <- selectedFilter(args))
yield (test: String) => filter(test) && noSuccessYet(test)
}
def succeededFile(dir: File) = dir / "succeeded_tests"

@nowarn
def inputTests(key: InputKey[_]): Initialize[InputTask[Unit]] =
inputTests0.mapReferenced(Def.mapScope(_ in key.key))
Expand Down Expand Up @@ -1747,21 +1703,6 @@ object Defaults extends BuildCommon {
result
}

def selectedFilter(args: Seq[String]): Seq[String => Boolean] = {
def matches(nfs: Seq[NameFilter], s: String) = nfs.exists(_.accept(s))

val (excludeArgs, includeArgs) = args.partition(_.startsWith("-"))

val includeFilters = includeArgs map GlobFilter.apply
val excludeFilters = excludeArgs.map(_.substring(1)).map(GlobFilter.apply)

(includeFilters, excludeArgs) match {
case (Nil, Nil) => Seq(const(true))
case (Nil, _) => Seq((s: String) => !matches(excludeFilters, s))
case _ =>
includeFilters.map(f => (s: String) => (f.accept(s) && !matches(excludeFilters, s)))
}
}
def detectTests: Initialize[Task[Seq[TestDefinition]]] =
Def.task {
Tests.discover(loadedTestFrameworks.value.values.toList, compile.value, streams.value.log)._1
Expand Down Expand Up @@ -2625,7 +2566,7 @@ object Defaults extends BuildCommon {
val cachedAnalysisMap: Map[VirtualFile, CompileAnalysis] = (
for
attributed <- cp
analysis <- extractAnalysis(attributed.metadata, converter)
analysis <- BuildDef.extractAnalysis(attributed.metadata, converter)
yield (converter.toVirtualFile(attributed.data), analysis)
).toMap
val cachedPerEntryDefinesClassLookup: VirtualFile => DefinesClass =
Expand Down
5 changes: 4 additions & 1 deletion main/src/main/scala/sbt/Keys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import sbt.librarymanagement._
import sbt.librarymanagement.ivy.{ Credentials, IvyConfiguration, IvyPaths, UpdateOptions }
import sbt.nio.file.Glob
import sbt.testing.Framework
import sbt.util.{ cacheLevel, ActionCacheStore, Level, Logger, LoggerContext }
import sbt.util.{ cacheLevel, ActionCacheStore, Digest, Level, Logger, LoggerContext }
import xsbti.{ HashedVirtualFileRef, VirtualFile, VirtualFileRef }
import xsbti.compile._
import xsbti.compile.analysis.ReadStamps
Expand Down Expand Up @@ -349,8 +349,10 @@ object Keys {
// Test Keys
val testLoader = taskKey[ClassLoader]("Provides the class loader used for testing.").withRank(DTask)
val loadedTestFrameworks = taskKey[Map[TestFramework, Framework]]("Loads Framework definitions from the test loader.").withRank(DTask)
@cacheLevel(include = Array.empty)
val definedTests = taskKey[Seq[TestDefinition]]("Provides the list of defined tests.").withRank(BMinusTask)
val definedTestNames = taskKey[Seq[String]]("Provides the set of defined test names.").withRank(BMinusTask)
val definedTestDigests = taskKey[Map[String, Digest]]("Provides a unique digest of defined tests.").withRank(DTask)
val executeTests = taskKey[Tests.Output]("Executes all tests, producing a report.").withRank(CTask)
val test = taskKey[Unit]("Executes all tests.").withRank(APlusTask)
val testOnly = inputKey[Unit]("Executes the tests provided as arguments or all tests if no arguments are provided.").withRank(ATask)
Expand All @@ -364,6 +366,7 @@ object Keys {
val testResultLogger = settingKey[TestResultLogger]("Logs results after a test task completes.").withRank(DTask)
val testGrouping = taskKey[Seq[Tests.Group]]("Collects discovered tests into groups. Whether to fork and the options for forking are configurable on a per-group basis.").withRank(BMinusTask)
val isModule = AttributeKey[Boolean]("isModule", "True if the target is a module.", DSetting)
val extraTestDigests = taskKey[Seq[Digest]]("Extra digests that would invalidate test caching").withRank(DTask)

// Classpath/Dependency Management Keys
type Classpath = Def.Classpath
Expand Down
18 changes: 1 addition & 17 deletions main/src/main/scala/sbt/RemoteCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,7 @@ object RemoteCache {
.withResolvers(rs)
}
)
) ++ inConfig(Compile)(
configCacheSettings(compileArtifact(Compile, cachedCompileClassifier))
)
++ inConfig(Test)(configCacheSettings(testArtifact(Test, cachedTestClassifier))))
) ++ inConfig(Compile)(configCacheSettings(compileArtifact(Compile, cachedCompileClassifier))))

def getResourceFilePaths() = Def.task {
val syncDir = crossTarget.value / (prefix(configuration.value.name) + "sync")
Expand Down Expand Up @@ -383,19 +380,6 @@ object RemoteCache {
)
}

def testArtifact(
configuration: Configuration,
classifier: String
): Def.Initialize[Task[TestRemoteCacheArtifact]] = Def.task {
TestRemoteCacheArtifact(
Artifact(moduleName.value, classifier),
configuration / packageCache,
(configuration / classDirectory).value,
(configuration / compileAnalysisFile).value,
Defaults.succeededFile((configuration / test / streams).value.cacheDirectory)
)
}

private def toVersion(v: String): String = s"0.0.0-$v"

private lazy val doption = new DownloadOptions
Expand Down
27 changes: 21 additions & 6 deletions main/src/main/scala/sbt/internal/BuildDef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import Keys.{ organization, thisProject, autoGeneratedProject }
import Def.Setting
// import sbt.ProjectExtra.apply
import sbt.io.Hash
import sbt.internal.util.Attributed
import sbt.internal.inc.ReflectUtilities
import xsbti.FileConverter
import sbt.internal.util.{ Attributed, StringAttributeMap }
import sbt.internal.inc.{ FileAnalysisStore, ReflectUtilities }
import xsbti.{ FileConverter, VirtualFileRef }
import xsbti.compile.CompileAnalysis

trait BuildDef {
def projectDefinitions(@deprecated("unused", "") baseDirectory: File): Seq[Project] = projects
Expand All @@ -33,7 +34,7 @@ trait BuildDef {
def rootProject: Option[Project] = None
}

private[sbt] object BuildDef {
private[sbt] object BuildDef:
val defaultEmpty: BuildDef = new BuildDef { override def projects = Nil }

val default: BuildDef = new BuildDef {
Expand Down Expand Up @@ -78,5 +79,19 @@ private[sbt] object BuildDef {
in: Seq[Attributed[_]],
converter: FileConverter
): Seq[xsbti.compile.CompileAnalysis] =
in.flatMap(a => Defaults.extractAnalysis(a.metadata, converter))
}
in.flatMap(a => extractAnalysis(a.metadata, converter))

private[sbt] def extractAnalysis(
metadata: StringAttributeMap,
converter: FileConverter
): Option[CompileAnalysis] =
import sbt.OptionSyntax.*
def asBinary(file: File) = FileAnalysisStore.binary(file).get.asScala
def asText(file: File) = FileAnalysisStore.text(file).get.asScala
for
ref <- metadata.get(Keys.analysis)
file = converter.toPath(VirtualFileRef.of(ref)).toFile
content <- asBinary(file).orElse(asText(file))
yield content.getAnalysis

end BuildDef
Loading

0 comments on commit 1cb3105

Please sign in to comment.