diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala index 37ba3ccab..5ea305920 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala @@ -107,7 +107,7 @@ private[sbt] final class ProgressState( val parts = new String(bytes, "UTF-8").split(System.lineSeparator) def appendLine(l: String, appendNewline: Boolean): Unit = { toWrite ++= l.getBytes("UTF-8") - toWrite ++= clearScreenBytes + if (!l.getBytes("UTF-8").endsWith("\r")) toWrite ++= clearScreenBytes if (appendNewline) toWrite ++= lineSeparatorBytes } parts.dropRight(1).foreach(appendLine(_, true)) diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala index dac378cdf..2c2a6434c 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala @@ -346,7 +346,7 @@ object Terminal { consoleTerminalHolder.set(newConsoleTerminal()) if (hasVirtualIO) { hasProgress.set(isServer && isAnsiSupported) - activeTerminal.set(consoleTerminalHolder.get) + Terminal.set(consoleTerminalHolder.get) try withOut(withIn(f)) finally { jline.TerminalFactory.reset() @@ -382,7 +382,16 @@ object Terminal { } private[this] object ProxyTerminal extends Terminal { - private def t: Terminal = activeTerminal.get + private def t: Terminal = { + val current = activeTerminal.get + // if the activeTerminal is yet to be initialized on use, + // initialize to the conventional simple terminal for compatibility and testing + if (current ne null) current + else { + Terminal.set(Terminal.SimpleTerminal) + activeTerminal.get + } + } override private[sbt] def progressState: ProgressState = t.progressState override private[sbt] def enterRawMode(): Unit = t.enterRawMode() override private[sbt] def exitRawMode(): Unit = t.exitRawMode() diff --git a/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala b/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala index 9d617662f..d8b07e310 100644 --- a/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala +++ b/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala @@ -21,8 +21,8 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { "ManagedLogger" should "log to console" in { val log = newLogger("foo") context.addAppender("foo", asyncStdout -> Level.Info) - log.info("test") - log.debug("test") + log.info("test_info") + log.debug("test_debug") } it should "support event logging" in { diff --git a/internal/util-logging/src/test/scala/sbt/internal/util/ProgressStateSpec.scala b/internal/util-logging/src/test/scala/sbt/internal/util/ProgressStateSpec.scala new file mode 100644 index 000000000..4c3ae123a --- /dev/null +++ b/internal/util-logging/src/test/scala/sbt/internal/util/ProgressStateSpec.scala @@ -0,0 +1,41 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt.internal.util + +import java.io.{ File, PrintStream } + +import org.scalatest.{ BeforeAndAfterAll, FlatSpec } +import sbt.internal.util.Terminal.SimpleTerminal + +import scala.io.Source + +class ProgressStateSpec extends FlatSpec with BeforeAndAfterAll { + + private lazy val fileIn = new File("/tmp/tmp.txt") + private lazy val fileOut = Source.fromFile("/tmp/tmp.txt") + + override def afterAll(): Unit = { + fileIn.delete() + fileOut.close() + super.afterAll() + } + + "test" should "not clear after carriage return (\\r) " in { + val ps = new ProgressState(1, 8) + val in = "Hello\r\nWorld".getBytes() + + ps.write(SimpleTerminal, in, new PrintStream(fileIn), hasProgress = true) + + val clearScreenBytes = ConsoleAppender.ClearScreenAfterCursor.getBytes("UTF-8") + val check = fileOut.getLines().toList.map { line => + line.getBytes("UTF-8").endsWith(clearScreenBytes) + } + + assert(check === List(false, true)) + } +} diff --git a/main/src/main/scala/sbt/internal/RemoteCache.scala b/main/src/main/scala/sbt/internal/RemoteCache.scala index 4c885551f..fe113a4d8 100644 --- a/main/src/main/scala/sbt/internal/RemoteCache.scala +++ b/main/src/main/scala/sbt/internal/RemoteCache.scala @@ -10,29 +10,34 @@ 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.module.descriptor.{ DefaultArtifact, Artifact => IArtifact } import org.apache.ivy.core.report.DownloadStatus +import org.apache.ivy.core.resolve.DownloadOptions import org.apache.ivy.plugins.resolver.DependencyResolver -import std.TaskExtra._ // for join +import sbt.Defaults.prefix +import sbt.Keys._ +import sbt.Project._ +import sbt.ScopeFilter.Make._ +import sbt.SlashSyntax0._ import sbt.coursierint.LMCoursier +import sbt.internal.inc.{ HashUtil, JarUtils } +import sbt.internal.librarymanagement._ +import sbt.internal.remotecache._ +import sbt.io.IO +import sbt.io.Path.{ flat, rebase } +import sbt.io.syntax._ import sbt.librarymanagement._ 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.{ HashUtil, JarUtils } +import sbt.std.TaskExtra._ import sbt.util.InterfaceUtil.toOption import sbt.util.Logger +import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } +import xsbti.FileConverter + import scala.annotation.nowarn object RemoteCache { @@ -152,6 +157,25 @@ object RemoteCache { ) ++ inConfig(Compile)(configCacheSettings(compileArtifact(Compile, cachedCompileClassifier))) ++ inConfig(Test)(configCacheSettings(testArtifact(Test, cachedTestClassifier)))) + def getResourceFilePaths() = Def.task { + import sbt.librarymanagement.LibraryManagementCodec._ + val t = classDirectory.value + val dirs = resourceDirectories.value.toSet + val flt: File => Option[File] = flat(t) + val cacheDirectory = crossTarget.value / (prefix(configuration.value.name) + "caches") + + val converter = fileConverter.value + val transform: File => Option[File] = (f: File) => rebase(dirs, t)(f).orElse(flt(f)) + val resourcesInClassesDir = resources.value + .flatMap(x => transform(x).toList) + .map(f => converter.toVirtualFile(f.toPath).toString) + val json = Converter.toJson[Seq[String]](resourcesInClassesDir).get + val tmp = CompactPrinter(json) + val file = cacheDirectory / "resources.json" + IO.write(file, tmp) + file + } + @nowarn def configCacheSettings[A <: RemoteCacheArtifact]( cacheArtifactTask: Def.Initialize[Task[A]] @@ -167,6 +191,10 @@ object RemoteCache { if (af.exists && artp.length() > 0) { JarUtils.includeInJar(artp, Vector(af -> s"META-INF/inc_compile.zip")) } + val rf = getResourceFilePaths.value + if (rf.exists) { + JarUtils.includeInJar(artp, Vector(rf -> s"META-INF/resources.json")) + } // val testStream = (test / streams).?.value // testStream foreach { s => // val sf = Defaults.succeededFile(s.cacheDirectory) @@ -249,6 +277,7 @@ object RemoteCache { val smi = scalaModuleInfo.value val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value val nonPom = artifacts.filterNot(isPomArtifact).toVector + val converter = fileConverter.value m.withModule(log) { case (ivy, md, _) => val resolver = ivy.getSettings.getResolver(r.name) @@ -281,7 +310,7 @@ object RemoteCache { findJar(classifier, v, jars) match { case Some(jar) => - extractJar(art, jar) + extractJar(art, jar, converter) log.info(s"remote cache artifact extracted for $p $classifier") case None => @@ -369,11 +398,16 @@ object RemoteCache { jars.find(_.toString.endsWith(suffix)) } - private def extractJar(cacheArtifact: RemoteCacheArtifact, jar: File): Unit = + private def extractJar( + cacheArtifact: RemoteCacheArtifact, + jar: File, + converter: FileConverter + ): Unit = cacheArtifact match { case a: CompileRemoteCacheArtifact => extractCache(jar, a.extractDirectory, preserveLastModified = true) { output => extractAnalysis(output, a.analysisFile) + extractResourceList(output, converter) } case a: TestRemoteCacheArtifact => @@ -411,6 +445,23 @@ object RemoteCache { } } + private def extractResourceList(output: File, converter: FileConverter): Unit = { + import sbt.librarymanagement.LibraryManagementCodec._ + import sjsonnew.support.scalajson.unsafe.{ Converter, Parser } + import xsbti.VirtualFileRef + + val resourceFilesToDelete = output / "META-INF" / "resources.json" + if (resourceFilesToDelete.exists) { + val readFile = IO.read(resourceFilesToDelete) + val parseFile = Parser.parseUnsafe(readFile) + val resourceFiles = Converter.fromJsonUnsafe[Seq[String]](parseFile) + val paths = resourceFiles.map(f => converter.toPath(VirtualFileRef.of(f))) + val filesToDelete = paths.map(_.toFile) + for (file <- filesToDelete if file.getAbsolutePath.startsWith(output.getAbsolutePath)) + IO.delete(file) + } + } + private def extractTestResult(output: File, testResult: File): Unit = { //val expandedTestResult = output / "META-INF" / "succeeded_tests" //if (expandedTestResult.exists) { diff --git a/protocol/src/main/scala/sbt/internal/bsp/BuildTargetTag.scala b/protocol/src/main/scala/sbt/internal/bsp/BuildTargetTag.scala index e522fb482..fa1773f27 100644 --- a/protocol/src/main/scala/sbt/internal/bsp/BuildTargetTag.scala +++ b/protocol/src/main/scala/sbt/internal/bsp/BuildTargetTag.scala @@ -18,6 +18,7 @@ object BuildTargetTag { def fromConfig(config: String): Vector[String] = config match { case "test" => Vector(test) + case "it" => Vector(integrationTest) case "compile" => Vector(library) case _ => Vector.empty }