From 802d1062ac59456be7219e176b28d5d07cc63f1c Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Tue, 26 Mar 2024 13:50:51 +0100 Subject: [PATCH 1/2] improvement: Simplify resolving presentation compiler Previously, we would have a lot of fallbacks that would be used if mtags was not avaiable. Now, each version of the compiler has a stable version of the compiler being released so there is no longer a reason to handle it via fallback. Instead we want to be explicit about what we are resolving. It seems that previous way was flaky and while I can't reproduce the issues people had, I think simplifying it would help out at lest to get the exact failure of what is going on. --- .../meta/internal/metals/MtagsResolver.scala | 158 ++++-------------- 1 file changed, 33 insertions(+), 125 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala b/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala index 6ed53fc6a1a..94d0f9f3046 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala @@ -5,18 +5,13 @@ import java.util.concurrent.ConcurrentHashMap import scala.concurrent.duration._ import scala.util.control.NonFatal -import scala.meta.internal.jdk.CollectionConverters._ import scala.meta.internal.metals.BuildInfo import scala.meta.internal.semver.SemVer -import coursierapi.error.SimpleResolutionError -import org.jsoup.Jsoup - trait MtagsResolver { /** * Try and resolve mtags module for a given version of Scala. - * Can contain a bunch of fallbacks in case of non stable versions. * @return information to use and load the presentation compiler implementation */ def resolve(scalaVersion: String): Option[MtagsBinaries] @@ -93,7 +88,18 @@ object MtagsResolver { removedScalaVersions.contains(version) def resolve(scalaVersion: String): Option[MtagsBinaries] = { - resolve(scalaVersion, original = None) + if (hasStablePresentationCompiler(scalaVersion)) + resolve( + scalaVersion, + original = None, + resolveType = ResolveType.StablePC, + ) + else + resolve( + scalaVersion, + original = None, + resolveType = ResolveType.Regular, + ) } private object ResolveType extends Enumeration { @@ -103,17 +109,15 @@ object MtagsResolver { /** * Resolving order is following: * 1. Built-in mtags matching scala version - * 2. Mtags matching exact scala version - * 3. Stable presentation compiler matching exact scala version - * 4. If scala version is a nightly, try to find latest supported snapshot + * 2. If presentation compiler is available we resolve it otherwise we resolve mtags. */ private def resolve( scalaVersion: String, original: Option[String], - resolveType: ResolveType.Value = ResolveType.Regular, + resolveType: ResolveType.Value, ): Option[MtagsBinaries] = { - def fetch(tries: Int = 0): State = logResolution { + def fetch(fetchResolveType: ResolveType.Value, tries: Int = 5): State = try { val metalsVersion = removedScalaVersions.getOrElse( scalaVersion, @@ -124,7 +128,7 @@ object MtagsResolver { s"$scalaVersion is no longer supported in the current Metals versions, using the last known supported version $metalsVersion" ) } - val jars = resolveType match { + val jars = fetchResolveType match { case ResolveType.StablePC => Embedded.downloadScala3PresentationCompiler(scalaVersion) case _ => Embedded.downloadMtags(scalaVersion, metalsVersion) @@ -134,16 +138,17 @@ object MtagsResolver { MtagsBinaries.Artifacts( scalaVersion, jars, - resolveType == ResolveType.StablePC, + fetchResolveType == ResolveType.StablePC, ) ) } catch { + case NonFatal(_) if tries > 0 => + fetch(fetchResolveType, tries - 1) case NonFatal(e) => - State.Failure(System.currentTimeMillis(), tries, e) + State.Failure(System.currentTimeMillis(), e) } - } + def shouldResolveAgain(failure: State.Failure): Boolean = { - failure.tries < State.maxTriesInARow || (System .currentTimeMillis() - failure.lastTryMillis) > 5.minutes.toMillis } @@ -159,8 +164,7 @@ object MtagsResolver { s"Resolved latest nightly mtags version: $scalaVersion" } scribe.debug(msg) - case _: State.Failure - if !hasStablePresentationCompiler(scalaVersion) => + case fail: State.Failure => val errorMsg = resolveType match { case ResolveType.Regular => s"Failed to resolve mtags for $scalaVersion" @@ -169,7 +173,7 @@ object MtagsResolver { case ResolveType.Nightly => s"Failed to resolve latest nightly mtags version: $scalaVersion" } - scribe.info(errorMsg) + scribe.error(errorMsg, fail.exception) case _ => } state @@ -186,126 +190,30 @@ object MtagsResolver { original.getOrElse(scalaVersion), (_, value) => { value match { - case null => fetch() + case null => logResolution(fetch(resolveType)) case succ: State.Success => succ + case failure: State.Failure if shouldResolveAgain(failure) => + logResolution(fetch(resolveType)) case failure: State.Failure => - if (shouldResolveAgain(failure)) - fetch(failure.tries + 1) - else { - failure - } + failure } }, ) - def logError(e: Throwable): Unit = { - val msg = s"Failed to fetch mtags for ${scalaVersion}" - e match { - case _: SimpleResolutionError => - // no need to log traces for coursier error - // all explanation is in message - scribe.error(msg + "\n" + e.getMessage()) - case _ => - scribe.error(msg, e) - } - } - computed match { - case State.Success(v) => - Some(v) - // Fallback to Stable PC version - case _: State.Failure - if resolveType != ResolveType.StablePC && - hasStablePresentationCompiler(scalaVersion) => - resolve( - scalaVersion, - None, - ResolveType.StablePC, - ) - // Try to download latest supported snapshot - case _: State.Failure - if resolveType != ResolveType.Nightly && - scalaVersion.contains("NIGHTLY") || - scalaVersion.contains("nonbootstrapped") => - findLatestSnapshot(scalaVersion) match { - case None => None - case Some(latestSnapshot) => - scribe.warn(s"Using latest stable version $latestSnapshot") - resolve( - latestSnapshot, - Some(scalaVersion), - ResolveType.Nightly, - ) - } - case failure: State.Failure => - logError(failure.exception) - None - case _ => None + case State.Success(v) => Some(v) + case _: State.Failure => None } } } - /** - * Nightlies version are able to work with artifacts compiled within the - * same RC version. - * - * For example 3.2.2-RC1-bin-20221009-2052fc2-NIGHTLY presentation compiler - * will work with classfiles compiled with 3.2.2-RC1-bin-20220910-ac6cd1c-NIGHTLY - * - * @param exactVersion version we failed to find and looking for an alternative for - * @return latest supported nightly version by thise version of metals - */ - private def findLatestSnapshot(exactVersion: String): Option[String] = try { - - val metalsVersion = BuildInfo.metalsVersion - - // strip timestamp to get only 3.2.2-RC1 - val rcVersion = SemVer.Version - .fromString(exactVersion) - .copy(nightlyDate = None) - .toString() - - val url = - s"https://oss.sonatype.org/content/repositories/snapshots/org/scalameta/" - - val allScalametaArtifacts = Jsoup.connect(url).get - - // find all the nightlies for current RC - val lastNightlies = allScalametaArtifacts - .select("a") - .asScala - .filter { a => - val name = a.text() - name.contains("NIGHTLY") && name.contains(rcVersion) - } - - // find last supported Scala version for this metals version - lastNightlies.reverseIterator - .find { nightlyLink => - val link = nightlyLink.attr("href") - - val mtagsPage = Jsoup.connect(link).get - - mtagsPage - .select("a") - .asScala - .find(_.text() == metalsVersion + "/") - .isDefined - } - .map(_.text().stripPrefix("mtags_").stripSuffix("/")) - - } catch { - case NonFatal(t) => - scribe.error("Could not check latest nightlies", t) - None - } - sealed trait State object State { - val maxTriesInARow: Int = 2 case class Success(v: MtagsBinaries.Artifacts) extends State - case class Failure(lastTryMillis: Long, tries: Int, exception: Throwable) - extends State + case class Failure( + lastTryMillis: Long, + exception: Throwable, + ) extends State } } From f287115b3957b526bfba4fd254c038637d37b3f8 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Wed, 27 Mar 2024 13:39:24 +0100 Subject: [PATCH 2/2] refactor: Remove no longer used nightly resolution --- .../scala/scala/meta/internal/metals/MtagsResolver.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala b/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala index 94d0f9f3046..4616e3d11bb 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MtagsResolver.scala @@ -103,7 +103,7 @@ object MtagsResolver { } private object ResolveType extends Enumeration { - val Regular, StablePC, Nightly = Value + val Regular, StablePC = Value } /** @@ -160,8 +160,6 @@ object MtagsResolver { case ResolveType.Regular => s"Resolved mtags for $scalaVersion" case ResolveType.StablePC => s"Resolved Scala 3 presentation compiler for $scalaVersion" - case ResolveType.Nightly => - s"Resolved latest nightly mtags version: $scalaVersion" } scribe.debug(msg) case fail: State.Failure => @@ -170,8 +168,6 @@ object MtagsResolver { s"Failed to resolve mtags for $scalaVersion" case ResolveType.StablePC => s"Failed to resolve Scala 3 presentation compiler for $scalaVersion" - case ResolveType.Nightly => - s"Failed to resolve latest nightly mtags version: $scalaVersion" } scribe.error(errorMsg, fail.exception) case _ =>