diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index e75c97832..cd05b9c77 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -3,7 +3,7 @@ */ package sbt -import scala.concurrent.duration.Duration +import scala.concurrent.duration.{ FiniteDuration, Duration } import Attributed.data import Scope.{ fillTaskAxis, GlobalScope, ThisScope } import sbt.Compiler.InputsWithPrevious @@ -27,7 +27,7 @@ import org.apache.ivy.core.module.{ descriptor, id } import descriptor.ModuleDescriptor, id.ModuleRevisionId import java.io.{ File, PrintWriter } import java.net.{ URI, URL, MalformedURLException } -import java.util.concurrent.Callable +import java.util.concurrent.{ TimeUnit, Callable } import sbinary.DefaultProtocol.StringFormat import Cache.seqFormat import CommandStrings.ExportStream @@ -115,7 +115,8 @@ object Defaults extends BuildCommon { pomPostProcess :== idFun, pomAllRepositories :== false, pomIncludeRepository :== Classpaths.defaultRepositoryFilter, - updateOptions := UpdateOptions() + updateOptions := UpdateOptions(), + forceUpdatePeriod :== None ) /** Core non-plugin settings for sbt builds. These *must* be on every build or the sbt engine will fail to run at all. */ @@ -1324,7 +1325,15 @@ object Classpaths { def updateTask: Initialize[Task[UpdateReport]] = Def.task { val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached) val isRoot = executionRoots.value contains resolvedScoped.value + val forceUpdate = forceUpdatePeriod.value val s = streams.value + val fullUpdateOutput = s.cacheDirectory / "out" + val forceUpdateByTime = forceUpdate match { + case None => false + case Some(period) => + val elapsedDuration = new FiniteDuration(System.currentTimeMillis() - fullUpdateOutput.lastModified(), TimeUnit.MILLISECONDS) + fullUpdateOutput.exists() && elapsedDuration > period + } val scalaProvider = appConfiguration.value.provider.scalaProvider // Only substitute unmanaged jars for managed jars when the major.minor parts of the versions the same for: @@ -1360,7 +1369,7 @@ object Classpaths { if (executionRoots.value exists { _.key == evicted.key }) EvictionWarningOptions.empty else (evictionWarningOptions in update).value cachedUpdate(s.cacheDirectory / updateCacheName.value, show, ivyModule.value, uc, transform, - skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, + skip = (skip in update).value, force = isRoot || forceUpdateByTime, depsUpdated = depsUpdated, uwConfig = uwConfig, logicalClock = logicalClock, depDir = Some(depDir), ewo = ewo, log = s.log) } diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 59faafdae..939dd9b77 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -5,7 +5,7 @@ package sbt import java.io.File import java.net.URL -import scala.concurrent.duration.Duration +import scala.concurrent.duration.{ FiniteDuration, Duration } import Def.ScopedKey import complete._ import inc.Analysis @@ -324,6 +324,7 @@ object Keys { val publishArtifact = SettingKey[Boolean]("publish-artifact", "Enables (true) or disables (false) publishing an artifact.", AMinusSetting) val packagedArtifact = TaskKey[(Artifact, File)]("packaged-artifact", "Generates a packaged artifact, returning the Artifact and the produced File.", CTask) val checksums = SettingKey[Seq[String]]("checksums", "The list of checksums to generate and to verify for dependencies.", BSetting) + val forceUpdatePeriod = SettingKey[Option[FiniteDuration]]("force-update-period", "Duration after which to force a full update to occur", CSetting) val classifiersModule = TaskKey[GetClassifiersModule]("classifiers-module", rank = CTask) val conflictWarning = SettingKey[ConflictWarning]("conflict-warning", "Configures warnings for conflicts in dependency management.", CSetting) diff --git a/notes/0.13.9/forceupdateperiod.markdown b/notes/0.13.9/forceupdateperiod.markdown new file mode 100644 index 000000000..49e171b5d --- /dev/null +++ b/notes/0.13.9/forceupdateperiod.markdown @@ -0,0 +1,9 @@ + [@ajsquared]: https://github.com/ajsquared + + +### Changes with compatibility implications + +### Improvements +- Adds `forceUpdatePeriod` key, that takes values of `Option[FiniteDuration]`. If set, a full `update` will occur after that amount of time without needing to explicitly run the `update` task. By [@ajsquared][@ajsquared] + +### Fixes diff --git a/sbt/src/sbt-test/dependency-management/force-update-period/build.sbt b/sbt/src/sbt-test/dependency-management/force-update-period/build.sbt new file mode 100644 index 000000000..ca910bdd9 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/force-update-period/build.sbt @@ -0,0 +1,13 @@ +libraryDependencies += "log4j" % "log4j" % "1.2.16" % "compile" + +autoScalaLibrary := false + +TaskKey[Unit]("check-last-update-time") <<= streams map { (s) => + val fullUpdateOutput = s.cacheDirectory / "out" + val timeDiff = System.currentTimeMillis()-fullUpdateOutput.lastModified() + val exists = fullUpdateOutput.exists() + s.log.info(s"Amount of time since last full update: $timeDiff") + if (exists && timeDiff > 5000) { + sys.error("Full update not perfomed") + } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/dependency-management/force-update-period/test b/sbt/src/sbt-test/dependency-management/force-update-period/test new file mode 100644 index 000000000..39e776d09 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/force-update-period/test @@ -0,0 +1,11 @@ +$ absent target/resolution-cache +> compile +$ exists target/resolution-cache +> checkLastUpdateTime +$ sleep 10000 +> compile +# This is expected to fail +-> checkLastUpdateTime +> set forceUpdatePeriod := Some(new scala.concurrent.duration.FiniteDuration(5000, java.util.concurrent.TimeUnit.MILLISECONDS)) +> compile +> checkLastUpdateTime \ No newline at end of file