From 73a196798f0573608d4adf472606eed576a307c0 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Mon, 18 Nov 2019 11:35:04 -0800 Subject: [PATCH] Move background job service directory location Rather than putting the background job temporary files in whatever java.io.tmpdir points to, this commit moves the files into a subdirectory of target in the project root directory. To make the directory configurable via settings, I had to move the declaration of the bgJobService setting later in the project initialization process. I don't think this should matter because background jobs shouldn't be created until after the project has loaded all of its settings.. --- main/src/main/scala/sbt/Defaults.scala | 2 +- main/src/main/scala/sbt/Keys.scala | 1 + main/src/main/scala/sbt/Main.scala | 2 +- .../DefaultBackgroundJobService.scala | 53 ++++++++++++++----- main/src/main/scala/sbt/internal/Load.scala | 1 - 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 8605a4139..688d6ca71 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -351,7 +351,7 @@ object Defaults extends BuildCommon { sys.env.contains("CI") || SysProp.ci, // watch related settings pollInterval :== Watch.defaultPollInterval, - ) ++ LintUnused.lintSettings + ) ++ LintUnused.lintSettings ++ DefaultBackgroundJobService.backgroundJobServiceSettings ) def defaultTestTasks(key: Scoped): Seq[Setting[_]] = diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index ae23c5a0d..a64ac4fec 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -251,6 +251,7 @@ object Keys { val javaOptions = taskKey[Seq[String]]("Options passed to a new JVM when forking.").withRank(BPlusTask) val envVars = taskKey[Map[String, String]]("Environment variables used when forking a new JVM").withRank(BTask) + val bgJobServiceDirectory = settingKey[File]("The directory for temporary files used by background jobs.") val bgJobService = settingKey[BackgroundJobService]("Job manager used to run background jobs.") val bgList = taskKey[Seq[JobHandle]]("List running background jobs.") val ps = taskKey[Seq[JobHandle]]("bgList variant that displays on the log.") diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 6337d1e59..4da7133ff 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -130,7 +130,7 @@ object StandardMain { try { MainLoop.runLogged(s) } finally { - try DefaultBackgroundJobService.backgroundJobService.shutdown() + try DefaultBackgroundJobService.shutdown() finally hook.close() () } diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index 7eea5304d..3c8a23ded 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -12,7 +12,8 @@ import java.io.{ Closeable, File, FileInputStream, IOException } import java.nio.file.attribute.BasicFileAttributes import java.nio.file.{ FileVisitResult, Files, Path, SimpleFileVisitor } import java.security.{ DigestInputStream, MessageDigest } -import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.{ AtomicLong, AtomicReference } import sbt.Def.{ Classpath, ScopedKey, Setting } import sbt.Scope.GlobalScope @@ -61,13 +62,16 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe private val nextId = new AtomicLong(1) private val pool = new BackgroundThreadPool() - private var serviceTempDirOpt: Option[File] = None - private def serviceTempDir = serviceTempDirOpt match { - case Some(dir) => dir - case _ => - val dir = IO.createTemporaryDirectory - serviceTempDirOpt = Some(dir) - dir + private[sbt] def serviceTempDirBase: File + private val serviceTempDirRef = new AtomicReference[File] + private def serviceTempDir: File = serviceTempDirRef.synchronized { + serviceTempDirRef.get match { + case null => + val dir = IO.createUniqueDirectory(serviceTempDirBase) + serviceTempDirRef.set(dir) + dir + case s => s + } } // hooks for sending start/stop events protected def onAddJob(@deprecated("unused", "") job: JobHandle): Unit = () @@ -166,7 +170,7 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe } } pool.close() - serviceTempDirOpt foreach IO.delete + Option(serviceTempDirRef.get).foreach(IO.delete) } private def withHandle(job: JobHandle)(f: ThreadJobHandle => Unit): Unit = job match { @@ -465,14 +469,37 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable { } } -private[sbt] class DefaultBackgroundJobService extends AbstractBackgroundJobService { +private[sbt] class DefaultBackgroundJobService(private[sbt] val serviceTempDirBase: File) + extends AbstractBackgroundJobService { + @deprecated("Use the constructor that specifies the background job temporary directory", "1.4.0") + def this() = this(IO.createTemporaryDirectory) override def makeContext(id: Long, spawningTask: ScopedKey[_], state: State): ManagedLogger = { val extracted = Project.extract(state) LogManager.constructBackgroundLog(extracted.structure.data, state)(spawningTask) } } private[sbt] object DefaultBackgroundJobService { - lazy val backgroundJobService: DefaultBackgroundJobService = new DefaultBackgroundJobService - lazy val backgroundJobServiceSetting: Setting[_] = - ((Keys.bgJobService in GlobalScope) :== backgroundJobService) + + private[this] val backgroundJobServices = new ConcurrentHashMap[File, DefaultBackgroundJobService] + private[sbt] def shutdown(): Unit = { + backgroundJobServices.values.forEach(_.shutdown()) + backgroundJobServices.clear() + } + private[sbt] lazy val backgroundJobServiceSetting: Setting[_] = + (Keys.bgJobService in GlobalScope) := { + val path = (sbt.Keys.bgJobServiceDirectory in GlobalScope).value + val newService = new DefaultBackgroundJobService(path) + backgroundJobServices.putIfAbsent(path, newService) match { + case null => newService + case s => + newService.shutdown() + s + } + } + private[sbt] lazy val backgroundJobServiceSettings: Seq[Def.Setting[_]] = Def.settings( + Keys.bgJobServiceDirectory in GlobalScope := { + sbt.Keys.appConfiguration.value.baseDirectory / "target" / "bg-jobs" + }, + backgroundJobServiceSetting + ) } diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 622d41cd6..dcc046081 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -130,7 +130,6 @@ private[sbt] object Load { def injectGlobal(state: State): Seq[Setting[_]] = (appConfiguration in GlobalScope :== state.configuration) +: LogManager.settingsLogger(state) +: - DefaultBackgroundJobService.backgroundJobServiceSetting +: EvaluateTask.injectSettings def defaultWithGlobal(