diff --git a/.travis.yml b/.travis.yml index e7247c4c0..88c2bed03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: include: - env: - SBT_LOCAL=true - - SBT_VERSION_PROP=-Dsbt.version=1.4.1-SNAPSHOT + - SBT_VERSION_PROP=-Dsbt.version=1.4.2-SNAPSHOT - TRAVIS_JDK=adopt@1.8.0-222 - SBT_CMD="++$SCALA_213; $UTIL_TESTS; ++$SCALA_212; $UTIL_TESTS; scripted actions/* source-dependencies/*1of3 dependency-management/*1of4 java/*" diff --git a/build.sbt b/build.sbt index 9a3265239..0f24a0767 100644 --- a/build.sbt +++ b/build.sbt @@ -8,14 +8,15 @@ import java.nio.file.{ Files, Path => JPath } import scala.util.Try ThisBuild / version := { - val v = "1.4.1-SNAPSHOT" + // update .travis.yml too for dog fooding + val v = "1.4.2-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / versionScheme := Some("early-semver") ThisBuild / scalafmtOnCompile := !(Global / insideCI).value ThisBuild / Test / scalafmtOnCompile := !(Global / insideCI).value ThisBuild / turbo := true -ThisBuild / usePipelining := !(Global / insideCI).value +ThisBuild / usePipelining := false // !(Global / insideCI).value val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys") Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 2f4b7ebaa..97605fd5d 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -2532,6 +2532,14 @@ object Classpaths { excludeFilter in unmanagedJars value ) ).map(exportClasspath) ++ Seq( + externalDependencyClasspath / outputFileStamps := { + val stamper = timeWrappedStamper.value + val converter = fileConverter.value + externalDependencyClasspath.value flatMap { file0 => + val p = file0.data.toPath + FileStamp(stamper.library(converter.toVirtualFile(p))).map(p -> _) + } + }, dependencyClasspathFiles := data(dependencyClasspath.value).map(_.toPath), dependencyClasspathFiles / outputFileStamps := { val stamper = timeWrappedStamper.value diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index b00fed740..14b3d54dc 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -386,6 +386,7 @@ object Keys { val pushRemoteCacheArtifact = settingKey[Boolean]("Enables publishing an artifact to remote cache.") val pushRemoteCacheConfiguration = taskKey[PublishConfiguration]("") val pushRemoteCacheTo = settingKey[Option[Resolver]]("The resolver to publish remote cache to.") + val remoteCacheResolvers = settingKey[Seq[Resolver]]("Resolvers for remote cache.") val remoteCachePom = taskKey[File]("Generates a pom for publishing when publishing Maven-style.") val usePipelining = settingKey[Boolean]("Use subproject pipelining for compilation.").withRank(BSetting) val exportPipelining = settingKey[Boolean]("Product early output so downstream subprojects can do pipelining.").withRank(BSetting) diff --git a/main/src/main/scala/sbt/internal/RemoteCache.scala b/main/src/main/scala/sbt/internal/RemoteCache.scala index a6f24cb81..be3b29b82 100644 --- a/main/src/main/scala/sbt/internal/RemoteCache.scala +++ b/main/src/main/scala/sbt/internal/RemoteCache.scala @@ -9,19 +9,29 @@ package sbt package internal import java.io.File +import java.nio.file.Path import Keys._ import SlashSyntax0._ +import ScopeFilter.Make._ import Project._ // for tag and inTask() + +import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact, DefaultArtifact } +import org.apache.ivy.core.resolve.DownloadOptions +import org.apache.ivy.core.report.DownloadStatus +import org.apache.ivy.plugins.resolver.DependencyResolver import std.TaskExtra._ // for join import sbt.coursierint.LMCoursier import sbt.librarymanagement._ -import sbt.librarymanagement.ivy.Credentials +import sbt.librarymanagement.ivy.{ Credentials, IvyPaths, UpdateOptions } import sbt.librarymanagement.syntax._ +import sbt.nio.FileStamp +import sbt.nio.Keys.{ inputFileStamps, outputFileStamps } import sbt.internal.librarymanagement._ import sbt.io.IO import sbt.io.syntax._ import sbt.internal.remotecache._ -import sbt.internal.inc.JarUtils +import sbt.internal.inc.{ HashUtil, JarUtils } +import sbt.util.InterfaceUtil.toOption import sbt.util.Logger object RemoteCache { @@ -41,91 +51,49 @@ object RemoteCache { .map(_.take(commitLength)) lazy val globalSettings: Seq[Def.Setting[_]] = Seq( - remoteCacheId := gitCommitId, - remoteCacheIdCandidates := gitCommitIds(5), + remoteCacheId := "", + remoteCacheIdCandidates := Nil, pushRemoteCacheTo :== None ) lazy val projectSettings: Seq[Def.Setting[_]] = (Seq( - remoteCacheProjectId := { - val o = organization.value - val m = moduleName.value - val id = remoteCacheId.value - val c = (projectID / crossVersion).value - val v = toVersion(id) - ModuleID(o, m, v).cross(c) - }, - pushRemoteCacheConfiguration / publishMavenStyle := true, - pushRemoteCacheConfiguration / packagedArtifacts := Def.taskDyn { - val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value - - artifacts - .map(a => a.packaged.map(file => (a.artifact, file))) - .join - .apply(_.join.map(_.toMap)) - }.value, + pushRemoteCache := (Def.taskDyn { + val arts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value + val configs = arts flatMap { art => + art.packaged.scopedKey.scope match { + case Scope(_, Select(c), _, _) => Some(c) + case _ => None + } + } + val filter = ScopeFilter(configurations = inConfigurationsByKeys(configs: _*)) + Def.task { + val _ = pushRemoteCache.all(filter).value + () + } + }).value, + pullRemoteCache := (Def.taskDyn { + val arts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value + val configs = arts flatMap { art => + art.packaged.scopedKey.scope match { + case Scope(_, Select(c), _, _) => Some(c) + case _ => None + } + } + val filter = ScopeFilter(configurations = inConfigurationsByKeys(configs: _*)) + Def.task { + val _ = pullRemoteCache.all(filter).value + () + } + }).value, pushRemoteCacheConfiguration / remoteCacheArtifacts := { enabledOnly(remoteCacheArtifact.toSettingKey, defaultArtifactTasks).apply(_.join).value }, + pushRemoteCacheConfiguration / publishMavenStyle := true, Compile / packageCache / pushRemoteCacheArtifact := true, Test / packageCache / pushRemoteCacheArtifact := true, Compile / packageCache / artifact := Artifact(moduleName.value, cachedCompileClassifier), Test / packageCache / artifact := Artifact(moduleName.value, cachedTestClassifier), remoteCachePom / pushRemoteCacheArtifact := true, - pushRemoteCacheConfiguration := { - Classpaths.publishConfig( - (pushRemoteCacheConfiguration / publishMavenStyle).value, - Classpaths.deliverPattern(crossTarget.value), - if (isSnapshot.value) "integration" else "release", - ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector, - (pushRemoteCacheConfiguration / packagedArtifacts).value.toVector, - (pushRemoteCacheConfiguration / checksums).value.toVector, - Classpaths.getPublishTo(pushRemoteCacheTo.value).name, - ivyLoggingLevel.value, - isSnapshot.value - ) - }, - pullRemoteCache := { - val log = streams.value.log - val smi = scalaModuleInfo.value - val dr = (pullRemoteCache / dependencyResolution).value - val is = (pushRemoteCache / ivySbt).value - val t = crossTarget.value / "cache-download" - val p = remoteCacheProjectId.value - val ids = remoteCacheIdCandidates.value - val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value - val applicable = artifacts.filterNot(isPomArtifact) - val classifiers = applicable.flatMap(_.artifact.classifier).toVector - - var found = false - ids foreach { - id: String => - val v = toVersion(id) - val modId = p.withRevision(v) - if (found) () - else - pullFromMavenRepo0(modId, classifiers, smi, is, dr, t, log) match { - case Right(xs0) => - val jars = xs0.distinct - - applicable.foreach { art => - val classifier = art.artifact.classifier - - findJar(classifier, v, jars) match { - case Some(jar) => - extractJar(art, jar) - log.info(s"remote cache artifact extracted for $p $classifier") - - case None => - log.info(s"remote cache artifact not found for $p $classifier") - } - } - found = true - case Left(unresolvedWarning) => - log.info(s"remote cache not found for ${v}") - } - } - }, remoteCachePom := { val s = streams.value val config = (remoteCachePom / makePomConfiguration).value @@ -142,50 +110,40 @@ object RemoteCache { }, remoteCachePom / remoteCacheArtifact := { PomRemoteCacheArtifact((makePom / artifact).value, remoteCachePom) - } + }, + remoteCacheResolvers := pushRemoteCacheTo.value.toVector, ) ++ inTask(pushRemoteCache)( Seq( + ivyPaths := IvyPaths(baseDirectory.value, crossTarget.value / "remote-cache"), ivyConfiguration := { - val other = pushRemoteCacheTo.value.toVector val config0 = Classpaths.mkIvyConfiguration.value config0 - .withOtherResolvers(other) + .withResolvers(remoteCacheResolvers.value.toVector) + .withOtherResolvers(pushRemoteCacheTo.value.toVector) .withResolutionCacheDir(crossTarget.value / "alt-resolution") + .withPaths(ivyPaths.value) + .withUpdateOptions(UpdateOptions().withGigahorse(true)) }, ivySbt := { - val config0 = ivyConfiguration.value Credentials.register(credentials.value, streams.value.log) + val config0 = ivyConfiguration.value new IvySbt(config0, CustomHttp.okhttpClient.value) }, - ivyModule := { - val is = ivySbt.value - new is.Module(moduleSettings.value) - }, - moduleSettings := { - val smi = scalaModuleInfo.value - ModuleDescriptorConfiguration(remoteCacheProjectId.value, projectInfo.value) - .withScalaModuleInfo(smi) - }, - pushRemoteCache.in(Defaults.TaskZero) := (Def.task { - val s = streams.value - val config = pushRemoteCacheConfiguration.value - IvyActions.publish(ivyModule.value, config, s.log) - } tag (Tags.Publish, Tags.Network)).value ) ) ++ inTask(pullRemoteCache)( Seq( dependencyResolution := Defaults.dependencyResolutionTask.value, csrConfiguration := { - val rs = pushRemoteCacheTo.value.toVector + val rs = pushRemoteCacheTo.value.toVector ++ remoteCacheResolvers.value.toVector LMCoursier.scalaCompilerBridgeConfigurationTask.value .withResolvers(rs) } ) - ) ++ inConfig(Compile)(packageCacheSettings(compileArtifact(Compile, cachedCompileClassifier))) - ++ inConfig(Test)(packageCacheSettings(testArtifact(Test, cachedTestClassifier)))) + ) ++ inConfig(Compile)(configCacheSettings(compileArtifact(Compile, cachedCompileClassifier))) + ++ inConfig(Test)(configCacheSettings(testArtifact(Test, cachedTestClassifier)))) - private def packageCacheSettings[A <: RemoteCacheArtifact]( - cacheArtifact: Def.Initialize[Task[A]] + def configCacheSettings[A <: RemoteCacheArtifact]( + cacheArtifactTask: Def.Initialize[Task[A]] ): Seq[Def.Setting[_]] = inTask(packageCache)( Seq( @@ -206,10 +164,128 @@ object RemoteCache { // } artp }, - remoteCacheArtifact := cacheArtifact.value, + pushRemoteCacheArtifact := true, + remoteCacheArtifact := cacheArtifactTask.value, packagedArtifact := (artifact.value -> packageCache.value), artifactPath := Defaults.artifactPathSetting(artifact).value ) + ) ++ inTask(pushRemoteCache)( + Seq( + moduleSettings := { + val smi = scalaModuleInfo.value + ModuleDescriptorConfiguration(remoteCacheProjectId.value, projectInfo.value) + .withScalaModuleInfo(smi) + }, + pushRemoteCache.in(Defaults.TaskZero) := (Def.task { + val s = streams.value + val config = pushRemoteCacheConfiguration.value + val is = (pushRemoteCache / ivySbt).value + val m = new is.Module(moduleSettings.value) + IvyActions.publish(m, config, s.log) + } tag (Tags.Publish, Tags.Network)).value, + ) + ) ++ Seq( + remoteCacheIdCandidates := List(remoteCacheId.value), + remoteCacheProjectId := { + val o = organization.value + val m = moduleName.value + val id = remoteCacheId.value + val c = (projectID / crossVersion).value + val v = toVersion(id) + ModuleID(o, m, v).cross(c) + }, + remoteCacheId := { + val inputs = (unmanagedSources / inputFileStamps).value + val cp = (externalDependencyClasspath / outputFileStamps).?.value.getOrElse(Nil) + val extraInc = (extraIncOptions.value) flatMap { + case (k, v) => + Vector(k, v) + } + combineHash(extractHash(inputs) ++ extractHash(cp) ++ extraInc) + }, + pushRemoteCacheConfiguration := { + Classpaths.publishConfig( + (pushRemoteCacheConfiguration / publishMavenStyle).value, + Classpaths.deliverPattern(crossTarget.value), + if (isSnapshot.value) "integration" else "release", + ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector, + (pushRemoteCacheConfiguration / packagedArtifacts).value.toVector, + (pushRemoteCacheConfiguration / checksums).value.toVector, + Classpaths.getPublishTo(pushRemoteCacheTo.value).name, + ivyLoggingLevel.value, + isSnapshot.value + ) + }, + pushRemoteCacheConfiguration / packagedArtifacts := Def.taskDyn { + val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value + artifacts + .map(a => a.packaged.map(file => (a.artifact, file))) + .join + .apply(_.join.map(_.toMap)) + }.value, + pushRemoteCacheConfiguration / remoteCacheArtifacts := { + List((packageCache / remoteCacheArtifact).value) + }, + pullRemoteCache := { + import scala.collection.JavaConverters._ + val log = streams.value.log + val r = remoteCacheResolvers.value.head + val p = remoteCacheProjectId.value + val ids = remoteCacheIdCandidates.value + val is = (pushRemoteCache / ivySbt).value + val m = new is.Module((pushRemoteCache / moduleSettings).value) + val smi = scalaModuleInfo.value + val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value + val nonPom = artifacts.filterNot(isPomArtifact).toVector + m.withModule(log) { + case (ivy, md, _) => + val resolver = ivy.getSettings.getResolver(r.name) + if (resolver eq null) sys.error(s"undefined resolver '${r.name}'") + val cross = CrossVersion(p, smi) + val crossf: String => String = cross.getOrElse(identity _) + var found = false + ids foreach { + id: String => + val v = toVersion(id) + val modId = p.withRevision(v).withName(crossf(p.name)) + val ivyId = IvySbt.toID(modId) + if (found) () + else { + val rawa = nonPom map { _.artifact } + val seqa = CrossVersion.substituteCross(rawa, cross) + val as = seqa map { a => + val extra = a.classifier match { + case Some(c) => Map("e:classifier" -> c) + case None => Map.empty + } + new DefaultArtifact(ivyId, null, a.name, a.`type`, a.extension, extra.asJava) + } + pullFromMavenRepo0(as, resolver, log) match { + case Right(xs0) => + val jars = xs0.distinct + + nonPom.foreach { art => + val classifier = art.artifact.classifier + + findJar(classifier, v, jars) match { + case Some(jar) => + extractJar(art, jar) + log.info(s"remote cache artifact extracted for $p $classifier") + + case None => + log.info(s"remote cache artifact not found for $p $classifier") + } + } + found = true + case Left(e) => + log.info(s"remote cache not found for ${v}") + log.debug(e.getMessage) + } + } + } + () + } + }, ) def isPomArtifact(artifact: RemoteCacheArtifact): Boolean = @@ -245,26 +321,35 @@ object RemoteCache { private def toVersion(v: String): String = s"0.0.0-$v" + private lazy val doption = new DownloadOptions private def pullFromMavenRepo0( - modId: ModuleID, - classifiers: Vector[String], - smi: Option[ScalaModuleInfo], - is: IvySbt, - dr: DependencyResolution, - cacheDir: File, + artifacts: Vector[IArtifact], + r: DependencyResolver, log: Logger - ): Either[UnresolvedWarning, Vector[File]] = { - def dummyModule(deps: Vector[ModuleID]): ModuleDescriptorConfiguration = { - val module = ModuleID("com.example.temp", "fake", "0.1.0-SNAPSHOT") - val info = ModuleInfo("fake", "", None, None, Vector(), "", None, None, Vector()) - ModuleDescriptorConfiguration(module, info) - .withScalaModuleInfo(smi) - .withDependencies(deps) + ): Either[Throwable, Vector[File]] = { + try { + val files = r.download(artifacts.toArray, doption).getArtifactsReports.toVector map { + report => + if (report == null) sys.error(s"failed to download $artifacts: " + r.toString) + else + report.getDownloadStatus match { + case DownloadStatus.NO => + val o = report.getArtifactOrigin + if (o.isLocal) { + val localFile = new File(o.getLocation) + if (!localFile.exists) sys.error(s"$localFile doesn't exist") + else localFile + } else report.getLocalFile + case DownloadStatus.SUCCESSFUL => + report.getLocalFile + case DownloadStatus.FAILED => + sys.error(s"failed to download $artifacts: " + r.toString) + } + } + Right(files) + } catch { + case e: Throwable => Left(e) } - val deps = classifiers.map(modId.classifier) - val mconfig = dummyModule(deps) - val m = new is.Module(mconfig) - dr.retrieve(m, cacheDir, log) } private def findJar(classifier: Option[String], ver: String, jars: Vector[File]): Option[File] = { @@ -322,7 +407,7 @@ object RemoteCache { } private def defaultArtifactTasks: Seq[TaskKey[File]] = - Seq(remoteCachePom, Compile / packageCache, Test / packageCache) + Seq(Compile / packageCache, Test / packageCache) private def enabledOnly[A]( key: SettingKey[A], @@ -332,4 +417,14 @@ object RemoteCache { Classpaths.forallIn(pushRemoteCacheArtifact, pkgTasks))(_ zip _ collect { case (a, true) => a }) + + private def extractHash(inputs: Seq[(Path, FileStamp)]): Vector[String] = + inputs.toVector map { + case (_, stamp0) => toOption(stamp0.stamp.getHash).getOrElse("cafe") + } + + private def combineHash(vs: Vector[String]): String = { + val hashValue = HashUtil.farmHash(vs.sorted.mkString("").getBytes("UTF-8")) + java.lang.Long.toHexString(hashValue) + } } diff --git a/sbt/src/sbt-test/actions/remote-cache-semanticdb/build.sbt b/sbt/src/sbt-test/actions/remote-cache-semanticdb/build.sbt index d35dd9ea0..374410229 100644 --- a/sbt/src/sbt-test/actions/remote-cache-semanticdb/build.sbt +++ b/sbt/src/sbt-test/actions/remote-cache-semanticdb/build.sbt @@ -10,8 +10,9 @@ pushRemoteCacheTo := Some( MavenCache("local-cache", (ThisBuild / baseDirectory).value / "remote-cache-semanticdb") ) -remoteCacheId := "fixed-id" - -remoteCacheIdCandidates := Seq(remoteCacheId.value) - -pushRemoteCacheConfiguration := pushRemoteCacheConfiguration.value.withOverwrite(true) +Compile / remoteCacheId := "fixed-id" +Compile / remoteCacheIdCandidates := Seq((Compile / remoteCacheId).value) +Test / remoteCacheId := "fixed-id" +Test / remoteCacheIdCandidates := Seq((Test / remoteCacheId).value) +Compile / pushRemoteCacheConfiguration := (Compile / pushRemoteCacheConfiguration).value.withOverwrite(true) +Test / pushRemoteCacheConfiguration := (Test / pushRemoteCacheConfiguration).value.withOverwrite(true) diff --git a/sbt/src/sbt-test/actions/remote-cache-semanticdb/test b/sbt/src/sbt-test/actions/remote-cache-semanticdb/test index bb3bff980..3875dd2ea 100644 --- a/sbt/src/sbt-test/actions/remote-cache-semanticdb/test +++ b/sbt/src/sbt-test/actions/remote-cache-semanticdb/test @@ -10,11 +10,6 @@ $ exists target/scala-2.12/test-classes/MyTest$.class $ exists target/scala-2.12/test-classes/META-INF/semanticdb/src/test/scala/MyTest.scala.semanticdb $ exists target/scala-2.12/test-zinc/inc_compile_2.12.zip -# Pom file -$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id.pom -$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id.pom.md5 -$ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id.pom.sha1 - # Compile $ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar $ exists remote-cache-semanticdb/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.md5 diff --git a/sbt/src/sbt-test/actions/remote-cache/build.sbt b/sbt/src/sbt-test/actions/remote-cache/build.sbt index e14afca15..920bc2e0c 100644 --- a/sbt/src/sbt-test/actions/remote-cache/build.sbt +++ b/sbt/src/sbt-test/actions/remote-cache/build.sbt @@ -1,38 +1,74 @@ import sbt.internal.remotecache.CustomRemoteCacheArtifact +import sbt.internal.inc.Analysis +import complete.DefaultParsers._ + +lazy val CustomArtifact = config("custom-artifact") + +// Reset compiler iterations, necessary because tests run in batch mode +val recordPreviousIterations = taskKey[Unit]("Record previous iterations.") +val checkIterations = inputKey[Unit]("Verifies the accumulated number of iterations of incremental compilation.") + +ThisBuild / scalaVersion := "2.12.12" +ThisBuild / pushRemoteCacheTo := Some( + MavenCache("local-cache", (ThisBuild / baseDirectory).value / "r") +) lazy val root = (project in file(".")) .configs(CustomArtifact) .settings( name := "my-project", - scalaVersion := "2.12.12", - pushRemoteCacheTo := Some( - MavenCache("local-cache", (ThisBuild / baseDirectory).value / "remote-cache") - ), - remoteCacheId := "fixed-id", - remoteCacheIdCandidates := Seq("fixed-id"), - pushRemoteCacheConfiguration := pushRemoteCacheConfiguration.value.withOverwrite(true), - pushRemoteCacheConfiguration / remoteCacheArtifacts += { - val art = (CustomArtifact / artifact).value - val packaged = CustomArtifact / packageCache - val extractDirectory = (CustomArtifact / sourceManaged).value - CustomRemoteCacheArtifact(art, packaged, extractDirectory, preserveLastModified = false) - }, + customArtifactSettings, + pushRemoteCacheConfiguration / remoteCacheArtifacts += (CustomArtifact / packageCache / remoteCacheArtifact).value, + + Compile / pushRemoteCacheConfiguration := (Compile / pushRemoteCacheConfiguration).value.withOverwrite(true), + Test / pushRemoteCacheConfiguration := (Test / pushRemoteCacheConfiguration).value.withOverwrite(true), + Compile / sourceGenerators += Def.task { val extractDirectory = (CustomArtifact / sourceManaged).value val output = extractDirectory / "HelloWorld.scala" IO.write(output, "class HelloWorld") Seq(output) - }.taskValue - ) - .settings(customArtifactSettings) + }.taskValue, + // bring back fixed-id because JDK 8/11 would be different intentionally + Compile / remoteCacheId := "fixed-id", + Compile / remoteCacheIdCandidates := Seq((Compile / remoteCacheId).value), + Test / remoteCacheId := "fixed-id", + Test / remoteCacheIdCandidates := Seq((Test / remoteCacheId).value), + CustomArtifact / remoteCacheId := "fixed-id", + CustomArtifact / remoteCacheIdCandidates := Seq((CustomArtifact / remoteCacheId).value), -lazy val CustomArtifact = config("custom-artifact") + // test tasks + recordPreviousIterations := { + val log = streams.value.log + CompileState.previousIterations = { + val previousAnalysis = (previousCompile in Compile).value.analysis.asScala + previousAnalysis match { + case None => + log.info("No previous analysis detected") + 0 + case Some(a: Analysis) => a.compilations.allCompilations.size + } + } + }, + checkIterations := { + val expected: Int = (Space ~> NatBasic).parsed + val actual: Int = ((compile in Compile).value match { case a: Analysis => a.compilations.allCompilations.size }) - CompileState.previousIterations + assert(expected == actual, s"Expected $expected compilations, got $actual") + } + ) def customArtifactSettings: Seq[Def.Setting[_]] = { val classifier = "custom-artifact" + def cachedArtifactTask = Def.task { + val art = (CustomArtifact / artifact).value + val packaged = CustomArtifact / packageCache + val extractDirectory = (CustomArtifact / sourceManaged).value + CustomRemoteCacheArtifact(art, packaged, extractDirectory, preserveLastModified = false) + } inConfig(CustomArtifact)( + sbt.internal.RemoteCache.configCacheSettings(cachedArtifactTask) ++ Seq( packageOptions := { val n = name.value + "-" + classifier diff --git a/sbt/src/sbt-test/actions/remote-cache/project/CompileState.scala b/sbt/src/sbt-test/actions/remote-cache/project/CompileState.scala new file mode 100644 index 000000000..078db9c7b --- /dev/null +++ b/sbt/src/sbt-test/actions/remote-cache/project/CompileState.scala @@ -0,0 +1,4 @@ +// This is necessary because tests are run in batch mode +object CompileState { + @volatile var previousIterations: Int = -1 +} diff --git a/sbt/src/sbt-test/actions/remote-cache/test b/sbt/src/sbt-test/actions/remote-cache/test index e90fade3e..5c81f25a5 100644 --- a/sbt/src/sbt-test/actions/remote-cache/test +++ b/sbt/src/sbt-test/actions/remote-cache/test @@ -1,3 +1,4 @@ +> recordPreviousIterations > compile > pushRemoteCache @@ -11,25 +12,20 @@ $ exists target/scala-2.12/test-classes/MyTest.class $ exists target/scala-2.12/test-classes/MyTest$.class $ exists target/scala-2.12/test-zinc/inc_compile_2.12.zip -# Pom file -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id.pom -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id.pom.md5 -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id.pom.sha1 - # Compile -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.md5 -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.sha1 +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.md5 +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-compile.jar.sha1 # Test -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.md5 -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.sha1 +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.md5 +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-cached-test.jar.sha1 # Custom artifact -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar.md5 -$ exists remote-cache/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar.sha1 +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar.md5 +$ exists r/my-project/my-project_2.12/0.0.0-fixed-id/my-project_2.12-0.0.0-fixed-id-custom-artifact.jar.sha1 > clean @@ -54,5 +50,8 @@ $ exists target/scala-2.12/test-classes/MyTest.class $ exists target/scala-2.12/test-classes/MyTest$.class $ exists target/scala-2.12/test-zinc/inc_compile_2.12.zip +> debug +> checkIterations 1 + # Artifacts can be pushed twice (enabled overriding) > pushRemoteCache \ No newline at end of file