scala-cli
scala-cli copied to clipboard
cli: report the full Java version
Resolves: https://github.com/VirtusLab/scala-cli/issues/2975
When running scala-cli and no JVM options are passed as arguments to the command line, it uses the default configured Java to compile and run the script, and only prints outs the Java major number which is internally extracted by executing java -version and manipulating the output that relies on scala.build.internal.OsLibc#javaVersion() *
This new proposed approach uses scala.util.Properties#javaVersion() to obtain the full version
def javaVersion = propOrEmpty("java.version")
which is a convenient wrapper around java.lang.System#getProperty()
scala> System.getProperty("java.version")
val res0: String = 17.0.11
The current set of system properties can be checked here https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html#getProperties()
Here's how I tested the change
1) $ java --version naf@lnx
openjdk 17.0.11 2024-04-16
OpenJDK Runtime Environment Temurin-17.0.11+9 (build 17.0.11+9)
OpenJDK 64-Bit Server VM Temurin-17.0.11+9 (build 17.0.11+9, mixed mode, sharing)
2) $ ./mill -i show 'cli[]'.standaloneLauncher naf@lnx
...
3) $ /home/naf/oss/scala-cli/out/cli/3.3.3/standaloneLauncher.dest/launcher -e 'println("hello")'
Compiling project (Scala 3.4.2, JVM (17.0.11))
Compiled project (Scala 3.4.2, JVM (17.0.11))
hello
4) $ sdk use java 21.0.2-tem naf@lnx
Using java version 21.0.2-tem in this shell.
5) $ java --version
openjdk 21.0.2 2024-01-16 LTS
OpenJDK Runtime Environment Temurin-21.0.2+13 (build 21.0.2+13-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.2+13 (build 21.0.2+13-LTS, mixed mode, sharing)
6) $ /home/naf/oss/scala-cli/out/cli/3.3.3/standaloneLauncher.dest/launcher -e 'println("hello")' naf@lnx
Starting compilation server
Compiling project (Scala 3.4.2, JVM (21.0.2))
Compiled project (Scala 3.4.2, JVM (21.0.2))
hello
(*): There are plenty of places in the code base that rely on executing the Java binary to extract info, they could be replaced by using systems properties instead which are easier since the output is plain and minimal string manipulation is required. However, that's future work and this is good place to stop.
Note: I initially didn't modify any tests waiting to the CI build to uncover which specific tests fail because I didn't want to run all the test suite on my machine
So first things first, this working very much depends on the distribution of Java you are using. For example, when testing it with Temurin-17+35:
java -version
openjdk version "17" 2021-09-14
OpenJDK Runtime Environment Temurin-17+35 (build 17+35)
OpenJDK 64-Bit Server VM Temurin-17+35 (build 17+35, mixed mode)
the output I get on the JVM launcher is:
./mill -i scala -e 'println("Hello")'
# Compiling project (Scala 3.4.2, JVM (17))
# Compiled project (Scala 3.4.2, JVM (17))
Hello
This seems to be because Temurin doesn't report in the $MAJOR.$MINOR.$SECURITY format.
But okay, I can see that for many JVMs it is possible to get the minor & security versions.
Another problem is that Properties.javaVersion refers to the JVM internally used by Scala CLI.
You wouldn't catch this when testing on the JVM launcher (which will indeed use the JAVA_HOME passed to it for its internals), but Scala CLI is generally distributed as a native launcher.
That means Properties.javaVersion would point to the JVM used when building the native launcher, rather than the actual Java home used for running the user's project.
You can test this by building a native image:
./mill -i show 'cli[].nativeImage'
and then, with your changes you'll get incorrect logs:
cs java-home --jvm zulu:22
# /Users/pchabelski/Library/Caches/Coursier/arc/https/cdn.azul.com/zulu/bin/zulu22.30.13-ca-jdk22.0.1-macosx_x64.tar.gz/zulu22.30.13-ca-jdk22.0.1-macosx_x64
export JAVA_HOME=/Users/pchabelski/Library/Caches/Coursier/arc/https/cdn.azul.com/zulu/bin/zulu22.30.13-ca-jdk22.0.1-macosx_x64.tar.gz/zulu22.30.13-ca-jdk22.0.1-macosx_x64
scala-cli -e 'println("Hello")'
# Compiling project (Scala 3.4.2, JVM (17.0.6))
# Compiled project (Scala 3.4.2, JVM (17.0.6))
# Hello
TL;DR this won't work. There are reasons why we do the roundabout way of extracting the Java version from the established Java home.
That means Properties.javaVersion would point to the JVM used when building the native launcher, rather than the actual Java home used for running the user's project.
It is clear to me now why the initial proposed approach will not work.
So first things first, this working very much depends on the distribution of Java you are using. For example, when testing it with Temurin-17+35:
java -version openjdk version "17" 2021-09-14 OpenJDK Runtime Environment Temurin-17+35 (build 17+35) OpenJDK 64-Bit Server VM Temurin-17+35 (build 17+35, mixed mode)
the output I get on the JVM launcher is:
./mill -i scala -e 'println("Hello")' Compiling project (Scala 3.4.2, JVM (17)) Compiled project (Scala 3.4.2, JVM (17)) Hello
This seems to be because Temurin doesn't report in the $MAJOR.$MINOR.$SECURITY format. But okay, I can see that for many JVMs it is possible to get the minor & security versions.
If the Java distribution doesn't the full version report it, it's reasonable to show whatever it provides. There is no expectation to provide the $MAJOR.$MINOR.$SECURITY format when is not possible, so this feature is intended for those JVM distros that do report it.
There are reasons why we do the roundabout way of extracting the Java version from the established Java home.
So, the solution will be extracting not only the first number from the output from executing java -version
BTW. do notice that when a particular JVM is passed by a directive or option, its identifier is used directly. Extracting the full Java version out of that will be even trickier.
Well, the intended scope of this change doesnt involve the JVM passed by directive or option as reported in the Github issue. I think that can be tackled later.
@naferx closing this due to inactivity, feel free to open another attempt separately.