Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java version through shell #4067

Merged
merged 12 commits into from Jul 8, 2022
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
tgodzik marked this conversation as resolved.
Show resolved Hide resolved
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] = {
zmerr marked this conversation as resolved.
Show resolved Hide resolved
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)
tgodzik marked this conversation as resolved.
Show resolved Hide resolved
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