diff --git a/.travis.yml b/.travis.yml index d180a58ff..a33abc64e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ -# Use Docker-based container (instead of OpenVZ) sudo: false +dist: trusty +group: stable cache: directories: @@ -36,6 +37,10 @@ notifications: email: - sbt-dev-bot@googlegroups.com +# Undo _JAVA_OPTIONS environment variable +before_script: + - _JAVA_OPTIONS= + script: # It doesn't need that much memory because compile and run are forked - sbt -J-XX:ReservedCodeCacheSize=128m -J-Xmx800M -J-Xms800M -J-server "$SBT_CMD" diff --git a/build.sbt b/build.sbt index 2affa420a..7e4e6d751 100644 --- a/build.sbt +++ b/build.sbt @@ -9,7 +9,7 @@ def buildLevelSettings: Seq[Setting[_]] = inThisBuild( Seq( organization := "org.scala-sbt", - version := "1.0.1-SNAPSHOT", + version := "1.0.3-SNAPSHOT", description := "sbt is an interactive build tool", bintrayOrganization := Some("sbt"), bintrayRepository := { diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 8d68e5945..971f57620 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -120,7 +120,9 @@ private[sbt] object JLine { def usingTerminal[T](f: jline.Terminal => T): T = withTerminal { t => t.restore - f(t) + val result = f(t) + t.restore + result } def createReader(): ConsoleReader = createReader(None, JLine.makeInputStream(true)) diff --git a/main-command/src/main/scala/sbt/BasicCommandStrings.scala b/main-command/src/main/scala/sbt/BasicCommandStrings.scala index 6d7b6c52e..efa853903 100644 --- a/main-command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main-command/src/main/scala/sbt/BasicCommandStrings.scala @@ -161,6 +161,12 @@ $AliasCommand name= def ShellDetailed = "Provides an interactive prompt and network server from which commands can be run." + def StartServer = "startServer" + def StartServerDetailed = + s"""$StartServer + Starts the server if it has not been started. This is intended to be used with + -Dsbt.server.autostart=false.""" + def OldShell = "oldshell" def OldShellDetailed = "Provides an interactive prompt from which commands can be run." diff --git a/main-command/src/main/scala/sbt/State.scala b/main-command/src/main/scala/sbt/State.scala index e73be3034..16608986b 100644 --- a/main-command/src/main/scala/sbt/State.scala +++ b/main-command/src/main/scala/sbt/State.scala @@ -16,7 +16,6 @@ import sbt.internal.util.{ } import sbt.internal.util.complete.HistoryCommands import sbt.internal.inc.classpath.ClassLoaderCache -import sbt.BasicCommandStrings.Shell /** * Data structure representing all command execution information. diff --git a/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala b/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala index 1ad599825..c551613ae 100644 --- a/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala +++ b/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala @@ -30,8 +30,8 @@ abstract class ServerConnection(connection: Socket) { bytesRead = in.read(readBuffer) buffer = buffer ++ readBuffer.toVector.take(bytesRead) // handle un-framing - val delimPos = buffer.indexOf(delimiter) - if (delimPos > 0) { + var delimPos = buffer.indexOf(delimiter) + while (delimPos > -1) { val chunk = buffer.take(delimPos) buffer = buffer.drop(delimPos + 1) @@ -47,6 +47,7 @@ abstract class ServerConnection(connection: Socket) { case event: StringEvent => onLogEntry(event) } ) + delimPos = buffer.indexOf(delimiter) } } catch { diff --git a/main-settings/src/main/scala/sbt/Def.scala b/main-settings/src/main/scala/sbt/Def.scala index 6b195ba8d..534e674e3 100644 --- a/main-settings/src/main/scala/sbt/Def.scala +++ b/main-settings/src/main/scala/sbt/Def.scala @@ -27,26 +27,35 @@ object Def extends Init[Scope] with TaskMacroExtra { Invisible) lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None) + def showFullKey(keyNameColor: Option[String]): Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) => displayFull(key, keyNameColor)) - def showRelativeKey(current: ProjectRef, - multi: Boolean, - keyNameColor: Option[String] = None): Show[ScopedKey[_]] = + def showRelativeKey( + current: ProjectRef, + multi: Boolean, + keyNameColor: Option[String] = None + ): Show[ScopedKey[_]] = Show[ScopedKey[_]]( - (key: ScopedKey[_]) => - Scope.display(key.scope, - withColor(key.key.label, keyNameColor), - ref => displayRelative(current, multi, ref))) + key => + Scope.display( + key.scope, + withColor(key.key.label, keyNameColor), + ref => displayRelative(current, multi, ref) + )) - def showBuildRelativeKey(currentBuild: URI, - multi: Boolean, - keyNameColor: Option[String] = None): Show[ScopedKey[_]] = + def showBuildRelativeKey( + currentBuild: URI, + multi: Boolean, + keyNameColor: Option[String] = None + ): Show[ScopedKey[_]] = Show[ScopedKey[_]]( - (key: ScopedKey[_]) => - Scope.display(key.scope, - withColor(key.key.label, keyNameColor), - ref => displayBuildRelative(currentBuild, multi, ref))) + key => + Scope.display( + key.scope, + withColor(key.key.label, keyNameColor), + ref => displayBuildRelative(currentBuild, multi, ref) + )) def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String = project match { @@ -55,15 +64,19 @@ object Def extends Init[Scope] with TaskMacroExtra { case ProjectRef(current.build, x) => x + "/" case _ => Reference.display(project) + "/" } + def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String = project match { case BuildRef(`currentBuild`) => "{.}/" case ProjectRef(`currentBuild`, x) => x + "/" case _ => Reference.display(project) + "/" } + def displayFull(scoped: ScopedKey[_]): String = displayFull(scoped, None) + def displayFull(scoped: ScopedKey[_], keyNameColor: Option[String]): String = Scope.display(scoped.scope, withColor(scoped.key.label, keyNameColor)) + def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String = Scope.displayMasked(scoped.scope, scoped.key.label, mask) diff --git a/main-settings/src/main/scala/sbt/Scope.scala b/main-settings/src/main/scala/sbt/Scope.scala index 55665e783..25dd46c85 100644 --- a/main-settings/src/main/scala/sbt/Scope.scala +++ b/main-settings/src/main/scala/sbt/Scope.scala @@ -124,16 +124,22 @@ object Scope { } def display(config: ConfigKey): String = config.name + ":" + def display(scope: Scope, sep: String): String = displayMasked(scope, sep, showProject, ScopeMask()) + def displayMasked(scope: Scope, sep: String, mask: ScopeMask): String = displayMasked(scope, sep, showProject, mask) + def display(scope: Scope, sep: String, showProject: Reference => String): String = displayMasked(scope, sep, showProject, ScopeMask()) - def displayMasked(scope: Scope, - sep: String, - showProject: Reference => String, - mask: ScopeMask): String = { + + def displayMasked( + scope: Scope, + sep: String, + showProject: Reference => String, + mask: ScopeMask + ): String = { import scope.{ project, config, task, extra } val configPrefix = config.foldStrict(display, "*:", ".:") val taskPrefix = task.foldStrict(_.label + "::", "", ".::") @@ -148,9 +154,12 @@ object Scope { (!mask.task || a.task == b.task) && (!mask.extra || a.extra == b.extra) - def projectPrefix(project: ScopeAxis[Reference], - show: Reference => String = showProject): String = + def projectPrefix( + project: ScopeAxis[Reference], + show: Reference => String = showProject + ): String = project.foldStrict(show, "*/", "./") + def showProject = (ref: Reference) => Reference.display(ref) + "/" def transformTaskName(s: String) = { diff --git a/main-settings/src/main/scala/sbt/ScopeMask.scala b/main-settings/src/main/scala/sbt/ScopeMask.scala index 4e6d6096e..65b7acb80 100644 --- a/main-settings/src/main/scala/sbt/ScopeMask.scala +++ b/main-settings/src/main/scala/sbt/ScopeMask.scala @@ -1,10 +1,12 @@ package sbt /** Specifies the Scope axes that should be used for an operation. `true` indicates an axis should be used. */ -final case class ScopeMask(project: Boolean = true, - config: Boolean = true, - task: Boolean = true, - extra: Boolean = true) { +final case class ScopeMask( + project: Boolean = true, + config: Boolean = true, + task: Boolean = true, + extra: Boolean = true +) { def concatShow(p: String, c: String, t: String, sep: String, x: String): String = { val sb = new StringBuilder if (project) sb.append(p) diff --git a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala index 732643a60..0fa3a630d 100644 --- a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala +++ b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala @@ -2,7 +2,7 @@ package sbt.std import sbt.SettingKey import sbt.internal.util.ConsoleAppender -import sbt.internal.util.appmacro.{ Convert, Converted, LinterDSL } +import sbt.internal.util.appmacro.{ Convert, LinterDSL } import scala.collection.mutable.{ HashSet => MutableSet } import scala.io.AnsiColor @@ -27,7 +27,7 @@ abstract class BaseTaskLinterDSL extends LinterDSL { def handleUncheckedAnnotation(exprAtUseSite: Tree, tt: TypeTree): Unit = { tt.original match { - case Annotated(annot, arg) => + case Annotated(annot, _) => Option(annot.tpe) match { case Some(AnnotatedType(annotations, _)) => val tpeAnnotations = annotations.flatMap(ann => Option(ann.tree.tpe).toList) @@ -65,7 +65,7 @@ abstract class BaseTaskLinterDSL extends LinterDSL { val wrapperName = nme.decodedName.toString val (qualName, isSettingKey) = Option(qual.symbol) - .map(sym => (sym.name.decodedName.toString, sym.info <:< typeOf[SettingKey[_]])) + .map(sym => (sym.name.decodedName.toString, qual.tpe <:< typeOf[SettingKey[_]])) .getOrElse((ap.pos.lineContent, false)) if (!isSettingKey && !shouldIgnore && isTask(wrapperName, tpe.tpe, qual)) { diff --git a/main-settings/src/main/scala/sbt/std/TaskMacro.scala b/main-settings/src/main/scala/sbt/std/TaskMacro.scala index 338484e87..9639a20ea 100644 --- a/main-settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main-settings/src/main/scala/sbt/std/TaskMacro.scala @@ -54,7 +54,7 @@ object FullInstance KeyRanks.DTask) def flatten[T](in: Initialize[Task[Initialize[Task[T]]]]): Initialize[Task[T]] = { - import Scoped._, TupleSyntax._ + import TupleSyntax._ (in, settingsData, Def.capturedTransformations) { (a: Task[Initialize[Task[T]]], data: Task[SS], f) => import TaskExtra.multT2Task @@ -63,7 +63,7 @@ object FullInstance } def flattenFun[S, T](in: Initialize[Task[S => Initialize[Task[T]]]]): Initialize[S => Task[T]] = { - import Scoped._, TupleSyntax._ + import TupleSyntax._ (in, settingsData, Def.capturedTransformations) { (a: Task[S => Initialize[Task[T]]], data: Task[SS], f) => (s: S) => import TaskExtra.multT2Task diff --git a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala index 451ed2bf7..eb52dbdc4 100644 --- a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala +++ b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala @@ -151,6 +151,15 @@ class TaskPosSpec { } } + locally { + import sbt._, Def._ + def withKey(foo: => SettingKey[String]) = { + Def.task { if (true) foo.value } + } + val foo = settingKey[String]("") + withKey(foo) + } + locally { import sbt._ import sbt.Def._ @@ -171,4 +180,17 @@ class TaskPosSpec { (1 to 10).map(_ => foo.value) } } + + locally { + import sbt._, Def._ + def withKey(bar: => SettingKey[Int]) = { + Def.task { + List(42).map { _ => + if (true) bar.value + } + } + } + val bar = settingKey[Int]("bar") + withKey(bar) + } } diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 8cd7720b7..4b2380d70 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -3,23 +3,16 @@ */ package sbt -import Keys.{ version, _ } -import sbt.internal.util.complete.{ DefaultParsers, Parser } -import sbt.internal.util.AttributeKey -import DefaultParsers._ -import Def.{ ScopedKey, Setting } -import sbt.internal.CommandStrings.{ - CrossCommand, - CrossRestoreSessionCommand, - SwitchCommand, - crossHelp, - crossRestoreSessionHelp, - switchHelp -} import java.io.File +import sbt.Def.{ ScopedKey, Setting } +import sbt.Keys._ import sbt.internal.Act +import sbt.internal.CommandStrings._ import sbt.internal.inc.ScalaInstance +import sbt.internal.util.AttributeKey +import sbt.internal.util.complete.DefaultParsers._ +import sbt.internal.util.complete.{ DefaultParsers, Parser } import sbt.io.IO import sbt.librarymanagement.CrossVersion @@ -94,7 +87,7 @@ object Cross { (currentRef :: currentProject.aggregate.toList.flatMap(findAggregates)).distinct } - private def crossVersions(extracted: Extracted, proj: ProjectRef): Seq[String] = { + private def crossVersions(extracted: Extracted, proj: ResolvedReference): Seq[String] = { import extracted._ (crossScalaVersions in proj get structure.data) getOrElse { // reading scalaVersion is a one-time deal @@ -225,12 +218,14 @@ object Cross { } private def switchScalaVersion(switch: Switch, state: State): State = { - val x = Project.extract(state) - import x._ + val extracted = Project.extract(state) + import extracted._ + + type ScalaVersion = String val (version, instance) = switch.version match { case ScalaHomeVersion(homePath, resolveVersion, _) => - val home = IO.resolve(x.currentProject.base, homePath) + val home = IO.resolve(extracted.currentProject.base, homePath) if (home.exists()) { val instance = ScalaInstance(home)(state.classLoaderCache.apply _) val version = resolveVersion.getOrElse(instance.actualVersion) @@ -241,10 +236,10 @@ object Cross { case NamedScalaVersion(v, _) => (v, None) } - val binaryVersion = CrossVersion.binaryScalaVersion(version) - - def logSwitchInfo(included: Seq[(ProjectRef, Seq[String])], - excluded: Seq[(ProjectRef, Seq[String])]) = { + def logSwitchInfo( + included: Seq[(ProjectRef, Seq[ScalaVersion])], + excluded: Seq[(ProjectRef, Seq[ScalaVersion])] + ) = { instance.foreach { case (home, instance) => @@ -262,7 +257,7 @@ object Cross { def detailedLog(msg: => String) = if (switch.verbose) state.log.info(msg) else state.log.debug(msg) - def logProject: (ProjectRef, Seq[String]) => Unit = (proj, scalaVersions) => { + def logProject: (ProjectRef, Seq[ScalaVersion]) => Unit = (proj, scalaVersions) => { val current = if (proj == currentRef) "*" else " " detailedLog(s" $current ${proj.project} ${scalaVersions.mkString("(", ", ", ")")}") } @@ -272,57 +267,67 @@ object Cross { excluded.foreach(logProject.tupled) } - val projects: Seq[Reference] = { + val projects: Seq[(ResolvedReference, Seq[ScalaVersion])] = { val projectScalaVersions = - structure.allProjectRefs.map(proj => proj -> crossVersions(x, proj)) + structure.allProjectRefs.map(proj => proj -> crossVersions(extracted, proj)) if (switch.version.force) { logSwitchInfo(projectScalaVersions, Nil) - structure.allProjectRefs ++ structure.units.keys.map(BuildRef.apply) + projectScalaVersions ++ structure.units.keys + .map(BuildRef.apply) + .map(proj => proj -> crossVersions(extracted, proj)) } else { + val binaryVersion = CrossVersion.binaryScalaVersion(version) val (included, excluded) = projectScalaVersions.partition { - case (proj, scalaVersions) => + case (_, scalaVersions) => scalaVersions.exists(v => CrossVersion.binaryScalaVersion(v) == binaryVersion) } logSwitchInfo(included, excluded) - included.map(_._1) + included } } - setScalaVersionForProjects(version, instance, projects, state, x) + setScalaVersionForProjects(version, instance, projects, state, extracted) } - private def setScalaVersionForProjects(version: String, - instance: Option[(File, ScalaInstance)], - projects: Seq[Reference], - state: State, - extracted: Extracted): State = { + private def setScalaVersionForProjects( + version: String, + instance: Option[(File, ScalaInstance)], + projects: Seq[(ResolvedReference, Seq[String])], + state: State, + extracted: Extracted + ): State = { import extracted._ - val newSettings = projects.flatMap { project => - val scope = Scope(Select(project), Zero, Zero, Zero) + val newSettings = projects.flatMap { + case (project, scalaVersions) => + val scope = Scope(Select(project), Zero, Zero, Zero) - instance match { - case Some((home, inst)) => - Seq( - scalaVersion in scope := version, - scalaHome in scope := Some(home), - scalaInstance in scope := inst - ) - case None => - Seq( - scalaVersion in scope := version, - scalaHome in scope := None - ) - } + instance match { + case Some((home, inst)) => + Seq( + scalaVersion in scope := version, + crossScalaVersions in scope := scalaVersions, + scalaHome in scope := Some(home), + scalaInstance in scope := inst + ) + case None => + Seq( + scalaVersion in scope := version, + crossScalaVersions in scope := scalaVersions, + scalaHome in scope := None + ) + } } val filterKeys: Set[AttributeKey[_]] = Set(scalaVersion, scalaHome, scalaInstance).map(_.key) + val projectsContains: Reference => Boolean = projects.map(_._1).toSet.contains + // Filter out any old scala version settings that were added, this is just for hygiene. val filteredRawAppend = session.rawAppend.filter(_.key match { case ScopedKey(Scope(Select(ref), Zero, Zero, Zero), key) - if filterKeys.contains(key) && projects.contains(ref) => + if filterKeys.contains(key) && projectsContains(ref) => false case _ => true }) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index d4f5ba1f2..1936641bf 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -82,7 +82,6 @@ import xsbti.compile.{ CompileOrder, CompileResult, DefinesClass, - IncOptionsUtil, Inputs, MiniSetup, PerClasspathEntryLookup, @@ -500,15 +499,8 @@ object Defaults extends BuildCommon { run := foregroundRunTask.evaluated, copyResources := copyResourcesTask.value, // note that we use the same runner and mainClass as plain run - bgRunMain := bgRunMainTask(exportedProductJars, - fullClasspathAsJars, - bgCopyClasspath in bgRunMain, - runner in run).evaluated, - bgRun := bgRunTask(exportedProductJars, - fullClasspathAsJars, - mainClass in run, - bgCopyClasspath in bgRun, - runner in run).evaluated + mainBgRunMainTaskForConfig(This), + mainBgRunTaskForConfig(This) ) ++ inTask(run)(runnerSettings) private[this] lazy val configGlobal = globalDefaults( @@ -1119,10 +1111,12 @@ object Defaults extends BuildCommon { toClean } - def bgRunMainTask(products: Initialize[Task[Classpath]], - classpath: Initialize[Task[Classpath]], - copyClasspath: Initialize[Boolean], - scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[JobHandle]] = { + def bgRunMainTask( + products: Initialize[Task[Classpath]], + classpath: Initialize[Task[Classpath]], + copyClasspath: Initialize[Boolean], + scalaRun: Initialize[Task[ScalaRun]] + ): Initialize[InputTask[JobHandle]] = { val parser = Defaults.loadForParser(discoveredMainClasses)((s, names) => Defaults.runMainParser(s, names getOrElse Nil)) Def.inputTask { @@ -1137,11 +1131,14 @@ object Defaults extends BuildCommon { } } } - def bgRunTask(products: Initialize[Task[Classpath]], - classpath: Initialize[Task[Classpath]], - mainClassTask: Initialize[Task[Option[String]]], - copyClasspath: Initialize[Boolean], - scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[JobHandle]] = { + + def bgRunTask( + products: Initialize[Task[Classpath]], + classpath: Initialize[Task[Classpath]], + mainClassTask: Initialize[Task[Option[String]]], + copyClasspath: Initialize[Boolean], + scalaRun: Initialize[Task[ScalaRun]] + ): Initialize[InputTask[JobHandle]] = { import Def.parserToInput val parser = Def.spaceDelimited() Def.inputTask { @@ -1156,6 +1153,7 @@ object Defaults extends BuildCommon { } } } + // runMain calls bgRunMain in the background and waits for the result. def foregroundRunMainTask: Initialize[InputTask[Unit]] = Def.inputTask { @@ -1163,6 +1161,7 @@ object Defaults extends BuildCommon { val service = bgJobService.value service.waitForTry(handle).get } + // run calls bgRun in the background and waits for the result. def foregroundRunTask: Initialize[InputTask[Unit]] = Def.inputTask { @@ -1170,8 +1169,11 @@ object Defaults extends BuildCommon { val service = bgJobService.value service.waitForTry(handle).get } - def runMainTask(classpath: Initialize[Task[Classpath]], - scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] = { + + def runMainTask( + classpath: Initialize[Task[Classpath]], + scalaRun: Initialize[Task[ScalaRun]] + ): Initialize[InputTask[Unit]] = { val parser = loadForParser(discoveredMainClasses)((s, names) => runMainParser(s, names getOrElse Nil)) Def.inputTask { @@ -1179,9 +1181,12 @@ object Defaults extends BuildCommon { scalaRun.value.run(mainClass, data(classpath.value), args, streams.value.log).get } } - def runTask(classpath: Initialize[Task[Classpath]], - mainClassTask: Initialize[Task[Option[String]]], - scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] = { + + def runTask( + classpath: Initialize[Task[Classpath]], + mainClassTask: Initialize[Task[Option[String]]], + scalaRun: Initialize[Task[ScalaRun]] + ): Initialize[InputTask[Unit]] = { import Def.parserToInput val parser = Def.spaceDelimited() Def.inputTask { @@ -1189,7 +1194,9 @@ object Defaults extends BuildCommon { scalaRun.value.run(mainClass, data(classpath.value), parser.parsed, streams.value.log).get } } + def runnerTask: Setting[Task[ScalaRun]] = runner := runnerInit.value + def runnerInit: Initialize[Task[ScalaRun]] = Def.task { val tmp = taskTemporaryDirectory.value val resolvedScope = resolvedScoped.value.scope @@ -1217,7 +1224,8 @@ object Defaults extends BuildCommon { } private def foreachJobTask( - f: (BackgroundJobService, JobHandle) => Unit): Initialize[InputTask[Unit]] = { + f: (BackgroundJobService, JobHandle) => Unit + ): Initialize[InputTask[Unit]] = { val parser: Initialize[State => Parser[Seq[JobHandle]]] = Def.setting { (s: State) => val extracted = Project.extract(s) val service = extracted.get(bgJobService) @@ -1232,6 +1240,7 @@ object Defaults extends BuildCommon { } } } + def psTask: Initialize[Task[Seq[JobHandle]]] = Def.task { val xs = bgList.value @@ -1241,9 +1250,11 @@ object Defaults extends BuildCommon { } xs } + def bgStopTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) => manager.stop(handle) } + def bgWaitForTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) => manager.waitFor(handle) } @@ -1294,17 +1305,25 @@ object Defaults extends BuildCommon { } )) - def mainBgRunTask = - bgRun := bgRunTask(exportedProductJars, - fullClasspathAsJars in Runtime, - mainClass in run, - bgCopyClasspath in bgRun, - runner in run).evaluated - def mainBgRunMainTask = - bgRunMain := bgRunMainTask(exportedProductJars, - fullClasspathAsJars in Runtime, - bgCopyClasspath in bgRunMain, - runner in run).evaluated + def mainBgRunTask = mainBgRunTaskForConfig(Select(Runtime)) + def mainBgRunMainTask = mainBgRunMainTaskForConfig(Select(Runtime)) + + private[this] def mainBgRunTaskForConfig(c: ScopeAxis[ConfigKey]) = + bgRun := bgRunTask( + exportedProductJars, + fullClasspathAsJars in (This, c, This), + mainClass in run, + bgCopyClasspath in bgRun, + runner in run + ).evaluated + + private[this] def mainBgRunMainTaskForConfig(c: ScopeAxis[ConfigKey]) = + bgRunMain := bgRunMainTask( + exportedProductJars, + fullClasspathAsJars in (This, c, This), + bgCopyClasspath in bgRunMain, + runner in run + ).evaluated def discoverMainClasses(analysis: CompileAnalysis): Seq[String] = analysis match { case analysis: Analysis => @@ -1316,6 +1335,7 @@ object Defaults extends BuildCommon { ConsoleProject(state.value, (initialCommands in consoleProject).value)(streams.value.log) println() } + def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console) def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick) def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] = @@ -1340,6 +1360,7 @@ object Defaults extends BuildCommon { private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit = args => w.println((command +: args).mkString(" ")) + private[this] def exported(s: TaskStreams, command: String): Seq[String] => Unit = args => { val w = s.text(ExportStream) try exported(w, command) @@ -1530,8 +1551,10 @@ object Defaults extends BuildCommon { lazy val runnerSettings: Seq[Setting[_]] = Seq(runnerTask, forkOptions := forkOptionsTask.value) lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ packageBase - lazy val configSettings - : Seq[Setting[_]] = Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++ Classpaths.compilerPluginConfig ++ deprecationSettings + + lazy val configSettings: Seq[Setting[_]] = + Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++ + Classpaths.compilerPluginConfig ++ deprecationSettings lazy val compileSettings: Seq[Setting[_]] = configSettings ++ @@ -1541,8 +1564,8 @@ object Defaults extends BuildCommon { lazy val testSettings: Seq[Setting[_]] = configSettings ++ testTasks lazy val itSettings: Seq[Setting[_]] = inConfig(IntegrationTest)(testSettings) - lazy val defaultConfigs: Seq[Setting[_]] = inConfig(Compile)(compileSettings) ++ inConfig(Test)( - testSettings) ++ inConfig(Runtime)(Classpaths.configSettings) + lazy val defaultConfigs: Seq[Setting[_]] = inConfig(Compile)(compileSettings) ++ + inConfig(Test)(testSettings) ++ inConfig(Runtime)(Classpaths.configSettings) // These are project level settings that MUST be on every project. lazy val coreDefaultSettings: Seq[Setting[_]] = @@ -1690,8 +1713,9 @@ object Classpaths { config.file.get }, packagedArtifact in makePom := ((artifact in makePom).value -> makePom.value), - deliver := deliverTask(publishConfiguration).value, - deliverLocal := deliverTask(publishLocalConfiguration).value, + deliver := deliverTask(makeIvyXmlConfiguration).value, + deliverLocal := deliverTask(makeIvyXmlLocalConfiguration).value, + makeIvyXml := deliverTask(makeIvyXmlConfiguration).value, publish := publishTask(publishConfiguration, deliver).value, publishLocal := publishTask(publishLocalConfiguration, deliverLocal).value, publishM2 := publishTask(publishM2Configuration, deliverLocal).value @@ -1871,6 +1895,17 @@ object Classpaths { .withProcess(pomPostProcess.value) .withFilterRepositories(pomIncludeRepository.value) .withAllRepositories(pomAllRepositories.value), + makeIvyXmlConfiguration := { + makeIvyXmlConfig( + publishMavenStyle.value, + sbt.Classpaths.deliverPattern(crossTarget.value), + if (isSnapshot.value) "integration" else "release", + ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector, + checksums.in(publish).value.toVector, + ivyLoggingLevel.value, + isSnapshot.value + ) + }, publishConfiguration := { publishConfig( publishMavenStyle.value, @@ -1884,6 +1919,18 @@ object Classpaths { isSnapshot.value ) }, + makeIvyXmlLocalConfiguration := { + makeIvyXmlConfig( + false, //publishMavenStyle.value, + sbt.Classpaths.deliverPattern(crossTarget.value), + if (isSnapshot.value) "integration" else "release", + ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector, + checksums.in(publish).value.toVector, + ivyLoggingLevel.value, + isSnapshot.value, + optResolverName = Some("local") + ) + }, publishLocalConfiguration := publishConfig( false, //publishMavenStyle.value, deliverPattern(crossTarget.value), @@ -2407,6 +2454,24 @@ object Classpaths { logging, overwrite) + def makeIvyXmlConfig(publishMavenStyle: Boolean, + deliverIvyPattern: String, + status: String, + configurations: Vector[ConfigRef], + checksums: Vector[String], + logging: sbt.librarymanagement.UpdateLogging = UpdateLogging.DownloadOnly, + overwrite: Boolean = false, + optResolverName: Option[String] = None) = + PublishConfiguration(publishMavenStyle, + Some(deliverIvyPattern), + Some(status), + Some(configurations), + optResolverName, + Vector.empty, + checksums, + Some(logging), + overwrite) + def deliverPattern(outputPath: File): String = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index a011fd938..07d6d4d92 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -363,6 +363,8 @@ object Keys { val publishLocalConfiguration = taskKey[PublishConfiguration]("Configuration for publishing to the local Ivy repository.").withRank(DTask) val publishM2Configuration = taskKey[PublishConfiguration]("Configuration for publishing to the local Maven repository.").withRank(DTask) val makePomConfiguration = settingKey[MakePomConfiguration]("Configuration for generating a pom.").withRank(DSetting) + val makeIvyXmlConfiguration = taskKey[PublishConfiguration]("Configuration for generating ivy.xml.").withRank(DSetting) + val makeIvyXmlLocalConfiguration = taskKey[PublishConfiguration]("Configuration for generating ivy.xml.").withRank(DSetting) val packagedArtifacts = taskKey[Map[Artifact, File]]("Packages all artifacts for publishing and maps the Artifact definition to the generated file.").withRank(CTask) val publishMavenStyle = settingKey[Boolean]("Configures whether to generate and publish a pom (true) or Ivy file (false).").withRank(BSetting) val credentials = taskKey[Seq[Credentials]]("The credentials to use for updating and publishing.").withRank(BMinusTask) @@ -370,6 +372,8 @@ object Keys { val makePom = taskKey[File]("Generates a pom for publishing when publishing Maven-style.").withRank(BPlusTask) val deliver = taskKey[File]("Generates the Ivy file for publishing to a repository.").withRank(BTask) val deliverLocal = taskKey[File]("Generates the Ivy file for publishing to the local repository.").withRank(BTask) + // makeIvyXml is currently identical to the confusingly-named "deliver", which may be deprecated in the future + val makeIvyXml = taskKey[File]("Generates the Ivy file for publishing to a repository.").withRank(BTask) val publish = taskKey[Unit]("Publishes artifacts to a repository.").withRank(APlusTask) val publishLocal = taskKey[Unit]("Publishes artifacts to the local Ivy repository.").withRank(APlusTask) val publishM2 = taskKey[Unit]("Publishes artifacts to the local Maven repository.").withRank(ATask) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 493f88117..f4043f989 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -195,6 +195,7 @@ object BuiltinCommands { multi, shell, oldshell, + startServer, BasicCommands.client, continuous, eval, @@ -737,6 +738,12 @@ object BuiltinCommands { else newState.clearGlobalLog } + def startServer: Command = + Command.command(StartServer, Help.more(StartServer, StartServerDetailed)) { s0 => + val exchange = StandardMain.exchange + exchange.runServer(s0) + } + private val sbtVersionRegex = """sbt\.version\s*=.*""".r private def isSbtVersionLine(s: String) = sbtVersionRegex.pattern matcher s matches () diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index 8ecfca99b..93b788c3d 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -15,9 +15,8 @@ import Def.ScopedKey import sbt.internal.Load import sbt.internal.CommandStrings._ import Cross.{ spacedFirst, requireSession } -import sbt.librarymanagement.Configurations._ import sbt.librarymanagement.VersionNumber -import Project.{ inConfig, inScope } +import Project.{ inScope } /** * Module responsible for plugin cross building. diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 8cc17a909..d56edb245 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -23,6 +23,9 @@ import scala.util.{ Success, Failure } * this exchange, which could serve command request from either of the channel. */ private[sbt] final class CommandExchange { + private val autoStartServer = sys.props.get("sbt.server.autostart") map { + _.toLowerCase == "true" + } getOrElse true private val lock = new AnyRef {} private var server: Option[ServerInstance] = None private var consoleChannel: Option[ConsoleChannel] = None @@ -61,13 +64,17 @@ private[sbt] final class CommandExchange { consoleChannel = Some(x) subscribe(x) } - runServer(s) + if (autoStartServer) runServer(s) + else s } private def newChannelName: String = s"channel-${nextChannelId.incrementAndGet()}" - private def runServer(s: State): State = { - val port = (s get serverPort) match { + /** + * Check if a server instance is running already, and start one if it isn't. + */ + private[sbt] def runServer(s: State): State = { + def port = (s get serverPort) match { case Some(x) => x case None => 5001 } diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index ea08836a9..308ce2047 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -2,15 +2,17 @@ package sbt package internal import java.util.concurrent.atomic.AtomicLong -import java.io.Closeable -import Def.{ ScopedKey, Setting, Classpath } +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 Def.{ Classpath, ScopedKey, Setting } import scala.concurrent.ExecutionContext import scala.util.Try import Scope.GlobalScope -import java.io.File -import sbt.io.{ IO, Hash } +import sbt.io.{ Hash, IO } import sbt.io.syntax._ -import sbt.util.{ Logger, LogExchange } +import sbt.util.{ LogExchange, Logger } import sbt.internal.util.{ Attributed, ManagedLogger } /** @@ -167,20 +169,25 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe override def toString(): String = s"BackgroundJobService(jobs=${jobs.map(_.id).mkString})" /** - * Copies products to the workind directory, and the rest to the serviceTempDir of this service, + * Copies products to the working directory, and the rest to the serviceTempDir of this service, * both wrapped in SHA-1 hash of the file contents. - * This is intended to mimize the file copying and accumulation of the unused JAR file. + * This is intended to minimize the file copying and accumulation of the unused JAR file. * Since working directory is wiped out when the background job ends, the product JAR is deleted too. * Meanwhile, the rest of the dependencies are cached for the duration of this service. */ - override def copyClasspath(products: Classpath, - full: Classpath, - workingDirectory: File): Classpath = { + override def copyClasspath( + products: Classpath, + full: Classpath, + workingDirectory: File + ): Classpath = { def syncTo(dir: File)(source0: Attributed[File]): Attributed[File] = { val source = source0.data - val hash8 = Hash.toHex(Hash(source)).take(8) + val hash8 = Hash.toHex(hash(source)).take(8) val dest = dir / hash8 / source.getName - if (!dest.exists) { IO.copyFile(source, dest) } + if (!dest.exists) { + if (source.isDirectory) IO.copyDirectory(source, dest) + else IO.copyFile(source, dest) + } Attributed.blank(dest) } val xs = (products.toVector map { syncTo(workingDirectory / "target") }) ++ @@ -188,6 +195,27 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe Thread.sleep(100) xs } + + /** An alternative to sbt.io.Hash that handles java.io.File being a directory. */ + private def hash(f: File) = { + val digest = MessageDigest.getInstance("SHA") + val buffer = new Array[Byte](8192) + Files.walkFileTree( + f.toPath, + new SimpleFileVisitor[Path]() { + override def visitFile(file: Path, attrs: BasicFileAttributes) = { + val dis = new DigestInputStream(new FileInputStream(file.toFile), digest) + try { + while (dis.read(buffer) >= 0) () + FileVisitResult.CONTINUE + } catch { + case _: IOException => FileVisitResult.TERMINATE + } finally dis.close() + } + } + ) + digest.digest + } } private[sbt] object BackgroundThreadPool { diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index b8bc171a5..22d51c5ce 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -29,8 +29,8 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil bytesRead = in.read(readBuffer) buffer = buffer ++ readBuffer.toVector.take(bytesRead) // handle un-framing - val delimPos = buffer.indexOf(delimiter) - if (delimPos > 0) { + var delimPos = buffer.indexOf(delimiter) + while (delimPos > -1) { val chunk = buffer.take(delimPos) buffer = buffer.drop(delimPos + 1) @@ -40,6 +40,7 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil errorDesc => println("Got invalid chunk from client: " + errorDesc), onCommand ) + delimPos = buffer.indexOf(delimiter) } } catch { diff --git a/notes/1.0.2.markdown b/notes/1.0.2.markdown new file mode 100644 index 000000000..44cad0df7 --- /dev/null +++ b/notes/1.0.2.markdown @@ -0,0 +1,37 @@ +This is a hotfix release for sbt 1.0.x series. + +### Bug fixes + +- Fixes terminal echo issue. [#3507][3507] by [@kczulko][@kczulko] +- Fixes `deliver` task, and adds `makeIvyXml` as a more sensibly named task. [#3487][3487] by [@cunei][@cunei] +- Replaces the deprecated use of `OkUrlFactory`, and fixes connection leaks. [lm#164][lm164] by [@dpratt][@dpratt] +- Refixes false positive in DSL checker for setting keys. [#3513][3513] by [@dwijnand][@dwijnand] +- Fixes `run` and `bgRun` not picking up changes to directories in the classpath. [#3517][3517] by [@dwijnand][@dwijnand] +- Fixes `++` so it won't change the value of `crossScalaVersion`. [#3495][3495]/[#3526][3526] by [@dwijnand][@dwijnand] +- Fixes sbt server missing some messages. [#3523][3523] by [@guillaumebort][@guillaumebort] +- Refixes `consoleProject`. [zinc#386][zinc386] by [@dwijnand][@dwijnand] +- Adds JVM flag `sbt.gigahorse` to enable/disable the internal use of Gigahorse to workaround NPE in `JavaNetAuthenticator` when used in conjunction with `repositories` override. [lm#167][lm167] by [@cunei][@cunei] +- Adds JVM flag `sbt.server.autostart` to enable/disable the automatic starting of sbt server with the sbt shell. This also adds new `startServer` command to manually start the server. by [@eed3si9n][@eed3si9n] + +### Internal + +- Fixes unused import warnings. [#3533][3533] by [@razvan-panda][@razvan-panda] + + [@dwijnand]: https://github.com/dwijnand + [@cunei]: https://github.com/cunei + [@eed3si9n]: https://github.com/eed3si9n + [@dpratt]: https://github.com/dpratt + [@kczulko]: https://github.com/kczulko + [@razvan-panda]: https://github.com/razvan-panda + [@guillaumebort]: https://github.com/guillaumebort + [3487]: https://github.com/sbt/sbt/pull/3487 + [lm164]: https://github.com/sbt/librarymanagement/pull/164 + [3495]: https://github.com/sbt/sbt/issues/3495 + [3526]: https://github.com/sbt/sbt/pull/3526 + [3513]: https://github.com/sbt/sbt/pull/3513 + [3517]: https://github.com/sbt/sbt/pull/3517 + [3507]: https://github.com/sbt/sbt/pull/3507 + [3533]: https://github.com/sbt/sbt/pull/3533 + [3523]: https://github.com/sbt/sbt/pull/3523 + [zinc386]: https://github.com/sbt/zinc/pull/386 + [lm167]: https://github.com/sbt/librarymanagement/pull/167 diff --git a/notes/1.0.1/sample.md b/notes/1.0.2/sample.md similarity index 100% rename from notes/1.0.1/sample.md rename to notes/1.0.2/sample.md diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 571b6b9be..b1b31a0b4 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -14,8 +14,8 @@ object Dependencies { // sbt modules private val ioVersion = "1.0.1" private val utilVersion = "1.0.1" - private val lmVersion = "1.0.0" - private val zincVersion = "1.0.0" + private val lmVersion = "1.0.2" + private val zincVersion = "1.0.1" private val sbtIO = "org.scala-sbt" %% "io" % ioVersion diff --git a/reset.sh b/reset.sh index a09f0bc7f..ce973dfb9 100755 --- a/reset.sh +++ b/reset.sh @@ -1,3 +1,3 @@ #!/bin/bash -rm -rf ~/.sbt/boot/scala-2.12.2/org.scala-sbt/sbt/1.0.0-SNAPSHOT/ +rm -rf ~/.sbt/boot/scala-2.12.3/org.scala-sbt/sbt/1.0.3-SNAPSHOT/ diff --git a/sbt/src/sbt-test/actions/cross-multiproject/build.sbt b/sbt/src/sbt-test/actions/cross-multiproject/build.sbt index 117efa724..d0eff709c 100644 --- a/sbt/src/sbt-test/actions/cross-multiproject/build.sbt +++ b/sbt/src/sbt-test/actions/cross-multiproject/build.sbt @@ -1,23 +1,31 @@ +inThisBuild(List( + crossScalaVersions := Seq("2.12.1", "2.11.8") +)) -lazy val rootProj = (project in file(".")). - aggregate(libProj, fooPlugin). - settings( +lazy val rootProj = (project in file(".")) + .aggregate(libProj, fooPlugin) + .settings( scalaVersion := "2.12.1" ) -lazy val libProj = (project in file("lib")). - settings( +lazy val libProj = (project in file("lib")) + .settings( name := "foo-lib", scalaVersion := "2.12.1", crossScalaVersions := Seq("2.12.1", "2.11.8") ) -lazy val fooPlugin =(project in file("sbt-foo")). - settings( +lazy val fooPlugin = (project in file("sbt-foo")) + .settings( name := "sbt-foo", sbtPlugin := true, scalaVersion := "2.12.1", crossScalaVersions := Seq("2.12.1") ) +lazy val extrasProj = (project in file("extras")) + .settings( + name := "foo-extras", + ) + addCommandAlias("build", "compile") diff --git a/sbt/src/sbt-test/actions/cross-multiproject/test b/sbt/src/sbt-test/actions/cross-multiproject/test index 154e71c2c..5458dcaf6 100644 --- a/sbt/src/sbt-test/actions/cross-multiproject/test +++ b/sbt/src/sbt-test/actions/cross-multiproject/test @@ -46,3 +46,10 @@ $ exists lib/target/scala-2.12 -$ exists lib/target/scala-2.11 $ exists sbt-foo/target/scala-2.12 -$ exists sbt-foo/target/scala-2.11 + +> clean +# Test ++ leaves crossScalaVersions unchanged +> ++2.12.1 +> +extrasProj/compile +$ exists extras/target/scala-2.11 +$ exists extras/target/scala-2.12 diff --git a/sbt/src/sbt-test/run/classpath/Main.scala b/sbt/src/sbt-test/run/classpath/Main.scala new file mode 100644 index 000000000..196834b7f --- /dev/null +++ b/sbt/src/sbt-test/run/classpath/Main.scala @@ -0,0 +1,9 @@ +package t + +import java.nio._, charset._, file._ + +object Main { + def main(args: Array[String]): Unit = { + println(new String(Files.readAllBytes(Paths.get(getClass().getResource("/a.txt").toURI())))) + } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/run/classpath/build.sbt b/sbt/src/sbt-test/run/classpath/build.sbt new file mode 100644 index 000000000..57556d8bb --- /dev/null +++ b/sbt/src/sbt-test/run/classpath/build.sbt @@ -0,0 +1 @@ +externalDependencyClasspath in Runtime += file("conf") \ No newline at end of file diff --git a/sbt/src/sbt-test/run/classpath/conf/a.txt b/sbt/src/sbt-test/run/classpath/conf/a.txt new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/sbt/src/sbt-test/run/classpath/conf/a.txt @@ -0,0 +1 @@ +foo diff --git a/sbt/src/sbt-test/run/classpath/test b/sbt/src/sbt-test/run/classpath/test new file mode 100644 index 000000000..62ea636c1 --- /dev/null +++ b/sbt/src/sbt-test/run/classpath/test @@ -0,0 +1 @@ +> run diff --git a/sbt/src/sbt-test/run/error/test b/sbt/src/sbt-test/run/error/test index d3599f40d..a603016e6 100644 --- a/sbt/src/sbt-test/run/error/test +++ b/sbt/src/sbt-test/run/error/test @@ -13,6 +13,7 @@ $ copy-file changes/ThreadRunError.scala src/main/scala/Run.scala $ copy-file changes/RunExplicitSuccess.scala src/main/scala/Run.scala > run -# explicitly calling System.exit(1) should fail the 'run' task -$ copy-file changes/RunExplicitFailure.scala src/main/scala/Run.scala --> run \ No newline at end of file +# https://github.com/sbt/sbt/issues/3543 +# # explicitly calling System.exit(1) should fail the 'run' task +# $ copy-file changes/RunExplicitFailure.scala src/main/scala/Run.scala +# -> run diff --git a/sbt/src/sbt-test/tests/fork-parallel/test b/sbt/src/sbt-test/tests/fork-parallel/test index 5226713d3..9d76b41e6 100644 --- a/sbt/src/sbt-test/tests/fork-parallel/test +++ b/sbt/src/sbt-test/tests/fork-parallel/test @@ -1,5 +1,6 @@ -> test --> check +# https://github.com/sbt/sbt/issues/3545 +# > test +# -> check > clean > set testForkedParallel := true diff --git a/testing/src/main/scala/sbt/internal/testing/StatusFormats.scala b/testing/src/main/scala/sbt/internal/testing/StatusFormats.scala index 862204488..d5ecfdaff 100644 --- a/testing/src/main/scala/sbt/internal/testing/StatusFormats.scala +++ b/testing/src/main/scala/sbt/internal/testing/StatusFormats.scala @@ -2,7 +2,7 @@ package sbt.internal.testing import sbt.testing.Status -import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } trait StatusFormats { self: sjsonnew.BasicJsonProtocol => implicit lazy val StatusFormat: JsonFormat[Status] = new JsonFormat[Status] {