Skip to content

Commit

Permalink
java version through shell (#4067)
Browse files Browse the repository at this point in the history
Detect java version through shell, when no release file is available.
  • Loading branch information
zmerr committed Jul 8, 2022
1 parent 1405f4d commit b1f8458
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 59 deletions.
39 changes: 39 additions & 0 deletions metals/src/main/scala/scala/meta/internal/builds/ShellRunner.scala
@@ -1,7 +1,10 @@
package scala.meta.internal.builds

import scala.concurrent.Await
import scala.concurrent.Future
import scala.concurrent.Promise
import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
import scala.util.Properties

import scala.meta.internal.metals.Cancelable
Expand Down Expand Up @@ -30,6 +33,7 @@ class ShellRunner(
) extends Cancelable {

private val cancelables = new MutableCancelable()

override def cancel(): Unit = {
cancelables.cancel()
}
Expand Down Expand Up @@ -130,3 +134,38 @@ class ShellRunner(
}

}

object ShellRunner {

def runSync(
args: List[String],
directory: AbsolutePath,
redirectErrorOutput: Boolean,
additionalEnv: Map[String, String] = Map.empty,
processErr: String => Unit = scribe.error(_),
propagateError: Boolean = false,
maybeJavaHome: Option[String]
): Option[String] = {

val sbOut = new StringBuilder()
val env = additionalEnv ++ maybeJavaHome.map("JAVA_HOME" -> _).toMap
val ps = SystemProcess.run(
args,
directory,
redirectErrorOutput,
env,
Some(s => {
sbOut.append(s)
sbOut.append(Properties.lineSeparator)
}),
Some(processErr),
propagateError
)

val exit = Await.result(ps.complete, 10 second)

if (exit == 0) {
Some(sbOut.toString())
} else None
}
}
Expand Up @@ -11,6 +11,7 @@ import scala.util.Properties
import scala.util.Try
import scala.util.control.NonFatal

import scala.meta.internal.builds.ShellRunner
import scala.meta.internal.io.FileIO
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.mtags.MD5
Expand Down Expand Up @@ -180,7 +181,8 @@ object JavaInteractiveSemanticdb {
def create(
javaHome: AbsolutePath,
workspace: AbsolutePath,
buildTargets: BuildTargets
buildTargets: BuildTargets,
jdkVersion: JdkVersion
): Option[JavaInteractiveSemanticdb] = {

def pathToJavac(p: AbsolutePath): AbsolutePath = {
Expand All @@ -197,22 +199,21 @@ object JavaInteractiveSemanticdb {

val javac = pathToJavac(jdkHome)

JdkVersion.getJavaVersionFromJavaHome(jdkHome) match {
case Some(version) if javac.exists =>
val pluginJars = Embedded.downloadSemanticdbJavac
val instance = new JavaInteractiveSemanticdb(
javac,
version,
pluginJars,
workspace,
buildTargets
)
Some(instance)
case value =>
scribe.warn(
s"Can't instantiate JavaInteractiveSemanticdb (version: ${value}, jdkHome: ${jdkHome}, javac exists: ${javac.exists})"
)
None
if (javac.exists) {
val pluginJars = Embedded.downloadSemanticdbJavac
val instance = new JavaInteractiveSemanticdb(
javac,
jdkVersion,
pluginJars,
workspace,
buildTargets
)
Some(instance)
} else {
scribe.warn(
s"Can't instantiate JavaInteractiveSemanticdb (version: ${jdkVersion}, jdkHome: ${jdkHome}, javac exists: ${javac.exists})"
)
None
}
}

Expand All @@ -227,38 +228,51 @@ case class JdkVersion(

object JdkVersion {

def getJavaVersionFromJavaHome(
javaHome: AbsolutePath
def maybeJdkVersionFromJavaHome(
maybeJavaHome: Option[AbsolutePath]
): Option[JdkVersion] = {

def fromReleaseFile: Option[JdkVersion] = {
val releaseFile = javaHome.resolve("release")
if (releaseFile.exists) {
val properties = ConfigFactory.parseFile(
releaseFile.toFile,
ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES)
)
try {
val version = properties
.getString("JAVA_VERSION")
.stripPrefix("\"")
.stripSuffix("\"")
JdkVersion.parse(version)
} catch {
case NonFatal(e) =>
scribe.error("Failed to read jdk version from `release` file", e)
None
}
} else None
maybeJavaHome.flatMap { javaHome =>
fromReleaseFile(javaHome).orElse {
fromShell(javaHome)
}
}
}

def jdk8Fallback: Option[JdkVersion] = {
val rtJar = javaHome.resolve("jre").resolve("lib").resolve("rt.jar")
if (rtJar.exists) Some(JdkVersion(8))
else None
}
def fromShell(javaHome: AbsolutePath): Option[JdkVersion] = {
ShellRunner
.runSync(
List(javaHome.resolve("bin/java").toString, "-version"),
javaHome,
redirectErrorOutput = true,
maybeJavaHome = Some(javaHome.toString())
)
.flatMap { javaVersionResponse =>
"\\d+\\.\\d+\\.\\d+".r
.findFirstIn(javaVersionResponse)
.flatMap(JdkVersion.parse)
}
}

def fromReleaseFile(javaHome: AbsolutePath): Option[JdkVersion] = {
val releaseFile = javaHome.resolve("release")
if (releaseFile.exists) {
val properties = ConfigFactory.parseFile(
releaseFile.toFile,
ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES)
)
try {
val version = properties
.getString("JAVA_VERSION")
.stripPrefix("\"")
.stripSuffix("\"")
JdkVersion.parse(version)
} catch {
case NonFatal(e) =>
scribe.error("Failed to read jdk version from `release` file", e)
None
}
} else None

fromReleaseFile.orElse(jdk8Fallback)
}

def parse(v: String): Option[JdkVersion] = {
Expand Down
Expand Up @@ -389,15 +389,24 @@ class MetalsLanguageServer(
workspace,
fingerprints
)
val javaInteractiveSemanticdb = {
val optJavaHome =
(userConfig.javaHome orElse JdkSources.defaultJavaHome)
.map(AbsolutePath(_))

optJavaHome.flatMap(
JavaInteractiveSemanticdb.create(_, workspace, buildTargets)
)
}
val optJavaHome =
(userConfig.javaHome orElse JdkSources.defaultJavaHome)
.map(AbsolutePath(_))
val maybeJdkVersion: Option[JdkVersion] =
JdkVersion.maybeJdkVersionFromJavaHome(optJavaHome)
val javaInteractiveSemanticdb =
for {
javaHome <- optJavaHome
jdkVersion <- maybeJdkVersion
javaSemanticDb <- JavaInteractiveSemanticdb.create(
javaHome,
workspace,
buildTargets,
jdkVersion
)
} yield javaSemanticDb

interactiveSemanticdbs = register(
new InteractiveSemanticdbs(
workspace,
Expand Down Expand Up @@ -727,7 +736,8 @@ class MetalsLanguageServer(
tables,
clientConfig,
mtagsResolver,
() => userConfig.javaHome
() => userConfig.javaHome,
maybeJdkVersion
)

fileDecoderProvider = new FileDecoderProvider(
Expand Down
Expand Up @@ -22,6 +22,7 @@ import scala.meta.internal.metals.FileDecoderProvider
import scala.meta.internal.metals.HtmlBuilder
import scala.meta.internal.metals.Icons
import scala.meta.internal.metals.JavaTarget
import scala.meta.internal.metals.JdkVersion
import scala.meta.internal.metals.Messages.CheckDoctor
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.MetalsHttpServer
Expand Down Expand Up @@ -54,7 +55,8 @@ final class Doctor(
tables: Tables,
clientConfig: ClientConfiguration,
mtagsResolver: MtagsResolver,
javaHome: () => Option[String]
javaHome: () => Option[String],
maybeJDKVersion: Option[JdkVersion]
)(implicit ec: ExecutionContext) {
private val isVisible = new AtomicBoolean(false)
private val hasProblems = new AtomicBoolean(false)
Expand All @@ -64,7 +66,8 @@ final class Doctor(
mtagsResolver,
currentBuildServer,
javaHome,
() => clientConfig.isTestExplorerProvider()
() => clientConfig.isTestExplorerProvider(),
maybeJDKVersion
)

def onVisibilityDidChange(newState: Boolean): Unit = {
Expand Down
Expand Up @@ -21,7 +21,8 @@ class ProblemResolver(
mtagsResolver: MtagsResolver,
currentBuildServer: () => Option[BspSession],
javaHome: () => Option[String],
isTestExplorerProvider: () => Boolean
isTestExplorerProvider: () => Boolean,
maybeJDKVersion: Option[JdkVersion]
) {

def isUnsupportedBloopVersion(): Boolean = {
Expand Down Expand Up @@ -324,7 +325,7 @@ class ProblemResolver(
target <- scalaTarget
javaHome <- target.jvmHome
version <-
JdkVersion.getJavaVersionFromJavaHome(javaHome.toAbsolutePath)
maybeJDKVersion
} yield version

val releaseVersion = javaTarget.releaseVersion.flatMap(JdkVersion.parse)
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/src/test/scala/tests/JdkVersionSuite.scala
@@ -0,0 +1,18 @@
package tests

import scala.meta.AbsolutePath
import scala.meta.internal.metals.JdkVersion

import munit.FunSuite

class JdkVersionSuite extends FunSuite {
val javaHome: AbsolutePath = AbsolutePath(System.getProperty("java.home"))
test("jdk-shell-version") {
assertEquals(
JdkVersion.fromShell(javaHome),
JdkVersion
.fromReleaseFile(javaHome)
.orElse(JdkVersion.parse(System.getProperty("java.version")))
)
}
}
Expand Up @@ -4,6 +4,7 @@ import java.nio.file.Files
import java.nio.file.Paths

import scala.meta.internal.metals.BuildInfo
import scala.meta.internal.metals.JdkVersion
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.ScalaTarget
import scala.meta.internal.metals.ScalaVersions
Expand Down Expand Up @@ -221,7 +222,10 @@ class ProblemResolverSuite extends FunSuite {
new TestMtagsResolver,
() => None,
() => javaHome,
() => isTestExplorerProvider
() => isTestExplorerProvider,
JdkVersion.maybeJdkVersionFromJavaHome(
Some(AbsolutePath(System.getProperty("java.home")))
)
)

val target =
Expand Down

0 comments on commit b1f8458

Please sign in to comment.