From 3861e8a1ea01317985095669252b89a589b6ee8d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 18 Aug 2019 21:47:15 -0400 Subject: [PATCH] improve the error message when Scala version was not detected When Scala version is set to `auto`, and for some reason the resolution is incomplete (it seems to happen at CI more frequently than normal lately), the launcher will print out the following error message. ## before ``` error during sbt execution: No Scala version specified or detected ``` ## after now it will print this instead: ``` [error] [launcher] error during sbt launcher: sbt/sbt#4955! [error] [launcher] sbt launcher is unable to detect the Scala version for jline:jline; [error] [launcher] this likely indicates an incomplete artifact resolution and/or corrupt boot cache (~/.sbt/boot/). [error] [launcher] the following is the full classpath that was retrieved for jline:jline: [error] [launcher] * /tmp/boot/other/jline/jline/2.14.6/jline-2.14.6.jar ``` --- .../src/main/scala/xsbt/boot/Boot.scala | 8 ++++-- .../src/main/scala/xsbt/boot/CheckProxy.scala | 2 +- .../main/scala/xsbt/boot/Configuration.scala | 2 +- .../src/main/scala/xsbt/boot/Find.scala | 2 +- .../src/main/scala/xsbt/boot/JAnsi.scala | 2 +- .../src/main/scala/xsbt/boot/Launch.scala | 13 +++++++-- .../src/main/scala/xsbt/boot/Locks.scala | 2 +- .../src/main/scala/xsbt/boot/Pre.scala | 2 +- .../src/main/scala/xsbt/boot/Update.scala | 28 +++++++++++-------- 9 files changed, 38 insertions(+), 23 deletions(-) diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Boot.scala b/launcher-implementation/src/main/scala/xsbt/boot/Boot.scala index 43c9642..df30013 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Boot.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Boot.scala @@ -24,7 +24,7 @@ object Boot { val sec = Duration(s).toSeconds if (sec >= 1) { (sec to 1 by -1) foreach { i => - Console.err.println(s"[info] standing by: $i") + Console.err.println(s"[info] [launcher] standing by: $i") Thread.sleep(1000) } } @@ -56,7 +56,7 @@ object Boot { Launch(args) map exit catch { case b: BootException => errorAndExit(b.toString) - case r: xsbti.RetrieveException => errorAndExit("Error: " + r.getMessage) + case r: xsbti.RetrieveException => errorAndExit(r.getMessage) case r: xsbti.FullReload => Some(new LauncherArguments(r.arguments.toList, false)) case e: Throwable => e.printStackTrace @@ -64,7 +64,9 @@ object Boot { } private def errorAndExit(msg: String): Nothing = { - Console.err.println(msg) + msg.linesIterator.toList foreach { line => + Console.err.println("[error] [launcher] " + line) + } exit(1) } diff --git a/launcher-implementation/src/main/scala/xsbt/boot/CheckProxy.scala b/launcher-implementation/src/main/scala/xsbt/boot/CheckProxy.scala index 6ef0836..422d96c 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/CheckProxy.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/CheckProxy.scala @@ -27,7 +27,7 @@ object CheckProxy { copyEnv(envPassword, sysPassword) } catch { case e: MalformedURLException => - Console.err.println(s"[warn] could not parse $envURL setting: ${e.toString}") + Console.err.println(s"[warn] [launcher] could not parse $envURL setting: ${e.toString}") } } } diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Configuration.scala b/launcher-implementation/src/main/scala/xsbt/boot/Configuration.scala index 014f9f6..6c059e2 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Configuration.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Configuration.scala @@ -39,7 +39,7 @@ object Configuration { } def setProperty(head: String): Unit = { head.split("=", 2) match { - case Array("") => Console.err.println(s"[warn] invalid system property '$head'") + case Array("") => Console.err.println(s"[warn] [launcher] invalid system property '$head'") case Array(key) => sys.props += key -> "" case Array(key, value) => sys.props += key -> value } diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Find.scala b/launcher-implementation/src/main/scala/xsbt/boot/Find.scala index 676a086..231a285 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Find.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Find.scala @@ -29,7 +29,7 @@ class Find(config: LaunchConfiguration) { case Nil => Some(current) case head :: Nil => Some(head) case xs => - Console.err.println("search method is 'only' and multiple ancestor directories match:\n\t" + fromRoot.mkString("\n\t")) + Console.err.println("[error] [launcher] search method is 'only' and multiple ancestor directories match:\n\t" + fromRoot.mkString("\n\t")) System.exit(1) None } diff --git a/launcher-implementation/src/main/scala/xsbt/boot/JAnsi.scala b/launcher-implementation/src/main/scala/xsbt/boot/JAnsi.scala index acdec8f..601c17f 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/JAnsi.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/JAnsi.scala @@ -18,6 +18,6 @@ object JAnsi { * mitigation code that should not render sbt completely unusable if jansi initialization fails. * [From Mark Harrah, https://github.com/sbt/sbt/pull/633#issuecomment-11957578]. */ - case ex: Throwable => Console.err.println("Jansi found on class path but initialization failed: " + ex) + case ex: Throwable => Console.err.println("[error] [launcher] Jansi found on class path but initialization failed: " + ex) } } diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala b/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala index 6fad053..b91c4f0 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala @@ -28,7 +28,7 @@ object Launch { if (arguments.isLocate) { if (!newArgs2.isEmpty) { // TODO - Print the arguments without exploding proguard size. - Console.err.println("[warn] --locate option ignores arguments") + Console.err.println("[warn] [launcher] --locate option ignores arguments") } locate(currentDirectory, config) } else { @@ -224,7 +224,16 @@ class Launch private[xsbt] (val bootDirectory: File, val lockBoot: Boolean, val retrievedApp.fullClasspath.find(f => testInterface.matcher(f.getName).find()).foreach { f => scalaProviderClassLoader.set(TestInterfaceLoader(f, initLoader)) } - val scalaVersion = getOrError(strictOr(explicitScalaVersion, retrievedApp.detectedScalaVersion), "No Scala version specified or detected") + // https://github.com/sbt/sbt/issues/4955 + // When sbt core artifacts are not properly downloaded, this the point that fails with "No Scala version specified or detected" + val scalaVersion = getOrError( + strictOr(explicitScalaVersion, retrievedApp.detectedScalaVersion), + s"""sbt/sbt#4955! + |sbt launcher is unable to detect the Scala version for ${id.groupID}:${id.name}; + |this likely indicates an incomplete artifact resolution and/or corrupt boot cache (~/.sbt/boot/). + |the following is the full classpath that was retrieved for ${id.groupID}:${id.name}: + |""".stripMargin ++ retrievedApp.fullClasspath.toList.map(x => " * " + x.getAbsolutePath).mkString(System.lineSeparator) + ) val scalaProvider = getScala(scalaVersion, "(for " + id.name + ")") val resolvedId = resolveId(retrievedApp.resolvedAppVersion, id) diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Locks.scala b/launcher-implementation/src/main/scala/xsbt/boot/Locks.scala index 0dbd306..a5416b2 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Locks.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Locks.scala @@ -87,7 +87,7 @@ object Locks extends xsbti.GlobalLock { { val freeLock = try { channel.tryLock } catch { case e: NullPointerException => throw new InternalLockNPE(e) } if (freeLock eq null) { - Console.err.println("waiting for lock on " + file + " to be available..."); + Console.err.println("[info] waiting for lock on " + file + " to be available..."); val lock = try { channel.lock } catch { case e: NullPointerException => throw new InternalLockNPE(e) } try { run.call } finally { lock.release() } diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Pre.scala b/launcher-implementation/src/main/scala/xsbt/boot/Pre.scala index 824b5eb..a85cac8 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Pre.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Pre.scala @@ -25,7 +25,7 @@ object Pre { def require(condition: Boolean, msg: => String): Unit = if (!condition) throw new IllegalArgumentException(msg) def error(msg: String): Nothing = throw new BootException(prefixError(msg)) def declined(msg: String): Nothing = throw new BootException(msg) - def prefixError(msg: String): String = "error during sbt execution: " + msg + def prefixError(msg: String): String = "error during sbt launcher: " + msg def toBoolean(s: String) = java.lang.Boolean.parseBoolean(s) def toArray[T: ClassManifest](list: List[T]) = { diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Update.scala b/launcher-implementation/src/main/scala/xsbt/boot/Update.scala index 560fee1..63aaf6f 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Update.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Update.scala @@ -106,7 +106,7 @@ final class Update(config: UpdateConfiguration) { catch { case e: Exception => e.printStackTrace(logWriter) - log(e.toString) + log("[error] [launcher] " + e.toString) Console.err.println(" (see " + logFile + " for complete log)") new UpdateResult(false, None, None) } finally { @@ -130,8 +130,8 @@ final class Update(config: UpdateConfiguration) { addDependency(moduleID, scalaOrg, CompilerModuleName, scalaVersion, "default;optional(default)", u.classifiers) val ddesc = addDependency(moduleID, scalaOrg, LibraryModuleName, scalaVersion, "default", u.classifiers) excludeJUnit(moduleID) - val scalaOrgString = if (scalaOrg != ScalaOrg) " " + scalaOrg else "" - Console.err.println(s"[info] getting $scalaOrgString Scala $scalaVersion ${reason}...") + val scalaOrgString = if (scalaOrg != ScalaOrg) scalaOrg + " " else "" + Console.err.println(s"[info] [launcher] getting ${scalaOrgString}Scala $scalaVersion ${reason}...") ddesc.getDependencyId case u: UpdateApp => val app = u.id @@ -141,7 +141,7 @@ final class Update(config: UpdateConfiguration) { case _ => app.getName } val ddesc = addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)", u.classifiers) - Console.err.println(s"[info] getting ${app.groupID} $resolvedName ${app.getVersion} $reason (this may take some time)...") + Console.err.println(s"[info] [launcher] getting ${app.groupID} $resolvedName ${app.getVersion} $reason (this may take some time)...") ddesc.getDependencyId } update(moduleID, target, dep) @@ -205,7 +205,10 @@ final class Update(config: UpdateConfiguration) { logExceptions(resolveReport) val seen = new java.util.LinkedHashSet[Any] seen.addAll(resolveReport.getAllProblemMessages) - Console.err.println(seen.toArray.mkString(System.getProperty("line.separator"))) + Console.err.println( + seen.toArray.map(x => s"[error] [launcher] $x") + .mkString(System.getProperty("line.separator")) + ) error("error retrieving required libraries") } val modules = moduleRevisionIDs(resolveReport) @@ -320,12 +323,12 @@ final class Update(config: UpdateConfiguration) { case MavenCentral => mavenMainResolver case ScalaToolsReleases => - log(s"$ScalaToolsReleases deprecated. use $SonatypeOSSReleases instead.") + log(s"[warn] [launcher] $ScalaToolsReleases deprecated. use $SonatypeOSSReleases instead.") sonatypeReleases case SonatypeOSSReleases => sonatypeReleases case ScalaToolsSnapshots => - log(s"$ScalaToolsSnapshots deprecated. use $SonatypeOSSSnapshots instead.") + log(s"[warn] [launcher] $ScalaToolsSnapshots deprecated. use $SonatypeOSSSnapshots instead.") scalaSnapshots(getScalaVersion) case SonatypeOSSSnapshots => scalaSnapshots(getScalaVersion) @@ -368,15 +371,15 @@ final class Update(config: UpdateConfiguration) { private def centralRepositoryRoot(secure: Boolean) = { val value = (if (secure) "https" else "http") + "://repo1.maven.org/maven2/" if (!secure) { - log(s"[warn] insecure HTTP request is deprecated '$value' via 'sbt.repository.secure'; switch to HTTPS") - log(s"[warn] Maven Central HTTP access is scheduled to end in January 2020") + log(s"[warn] [launcher] insecure HTTP request is deprecated '$value' via 'sbt.repository.secure'; switch to HTTPS") + log(s"[warn] [launcher] Maven Central HTTP access is scheduled to end in January 2020") } value } private def jcenterRepositoryRoot(secure: Boolean) = { val value = (if (secure) "https" else "http") + "://jcenter.bintray.com/" if (!secure) { - log(s"[warn] insecure HTTP request is deprecated '$value' via 'sbt.repository.secure'; switch to HTTPS") + log(s"[warn] [launcher] insecure HTTP request is deprecated '$value' via 'sbt.repository.secure'; switch to HTTPS") } value } @@ -417,7 +420,7 @@ final class Update(config: UpdateConfiguration) { private def log(msg: String) = { try { logWriter.println(msg) } - catch { case e: Exception => Console.err.println("error writing to update log file: " + e.toString) } + catch { case e: Exception => Console.err.println("[error] [launcher] error writing to update log file: " + e.toString) } Console.err.println(msg) } } @@ -433,8 +436,9 @@ private final class SbtIvyLogger(logWriter: PrintWriter) extends DefaultMessageL if (isAlwaysIgnoreMessage(msg)) () else { logWriter.println(msg) - if (level <= getLevel && acceptMessage(msg)) + if (level <= getLevel && acceptMessage(msg)) { Console.err.println(msg) + } } override def rawlog(msg: String, level: Int): Unit = { log(msg, level) } /** This is a hack to filter error messages about 'unknown resolver ...'. */