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

Provide default JVM version to be used by Mill server process, when .mill-jvm-version and system java are not provided #4597

Merged
merged 51 commits into from
Feb 25, 2025

Conversation

lihaoyi
Copy link
Member

@lihaoyi lihaoyi commented Feb 20, 2025

This PR makes Mill's server use a fixed JVM version by default in the case where no other Java version is available:

  1. No .mill-jvm-version file present
  2. No java command available environmentally
  3. The Mill client has no java.home set (i.e. it is a graal native image)

This makes running Mill on a clean environment without a JVM installed work by default. Previously, it either needed you to define a .mill-jvm-version or install a JVM, either of which is a manual step we now can now avoid. Now it instead uses the version of Java configured in Mill's own .mill-jvm-version file

We still allow any system java command to take precedence over the default, since that's what most people would expect when using the JVM.

Added an integration test in CI no-jvm-bootstrap that runs Mill with the native launcher with no .mill-jvm-version file and no system java installed to verify that it falls back to the Mill default JVM version. Needed to add some hacky test hooks to make sure we exercise the code path in question

Path millJvmVersionFile = millJvmVersionFile();

String javaHome = null;
if (Files.exists(millJvmVersionFile)) {
jvmId = Files.readString(millJvmVersionFile).trim();
if (jvmId.equals("global")) jvmId = null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI in coursier, system stands for "use the already installed / available JVM". Don't know which is better, if global feels natural to enough people, I could accept it in coursier too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

system works too, I was unaware of Coursier's default. I can change this over

Path millJvmVersionFile = millJvmVersionFile();

String javaHome = null;
if (Files.exists(millJvmVersionFile)) {
jvmId = Files.readString(millJvmVersionFile).trim();
if (jvmId.equals("global")) jvmId = null;
} else {
jvmId = "zulu:17.0.3";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to use zulu rather than temurin? I recall defaulting to zulu sometimes, because it has JDK 8 builds for macOS ARM64 while temurin doesn't, but that shouldn't be a problem here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC there were some JDKs that Zulu had published for Windows-ARM that Temurin hadn't for these versions, which is why we use Zulu in Mill's own config. But probably can find a version that works on Termurin

Copy link
Member Author

@lihaoyi lihaoyi Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the issue: Temurin doesn't publish JDKs for versions <21 on Windows-ARM (e.g. my Surface Laptop 7). The only options for JDK 17 on Windows-ARM are Liberica and Zulu

@lihaoyi lihaoyi changed the title Pin JVM version used by Mill server process, unless override by .mill-jvm-version containing global Pin JVM version used by Mill server process, unless override by .mill-jvm-version containing system Feb 21, 2025
Copy link
Member

@lefou lefou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@lihaoyi lihaoyi changed the title Pin JVM version used by Mill server process, unless override by .mill-jvm-version containing system Pin JVM version used by Mill server process, unless overriden by .mill-jvm-version containing system Feb 21, 2025
@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 21, 2025

Seems like CI is hitting No space left on device errors with this PR. I assume it has to be due to us leaking downloaded JVMs somewhere in Coursier, and now that we're calling Coursier every integration/example test it's adding up enough to be a problem. @alexarchambault probably will need your help to look into coursier see if we're leaking any temporary download files or something

@alexarchambault
Copy link
Collaborator

Not 100% sure, but I suspect the way the coursier archive cache is handled on CI might cause that. IIUC, things like this then this make the COURSIER_ARCHIVE_CACHE env var have an empty value. I think that must make it use the current directory as cache. And with sandbox stuff, that directory must change quite often, so JDKs must be unpacked more than they need to.

@alexarchambault
Copy link
Collaborator

Going to try to tweak how COURSIER_ARCHIVE_CACHE is handled in workflows in a separate PR

@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 21, 2025

Good catch with the COURSIER_ARCHIVE_CACHE, maybe the empty value is making it live in the cwd rather than just having the value unset.

@lihaoyi lihaoyi force-pushed the default-pinned-java branch from 645065b to 7882e6d Compare February 21, 2025 14:18
@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 21, 2025

Let me see if setting the default in github actions to null instead of "" helps

@lihaoyi lihaoyi changed the title Pin JVM version used by Mill server process, unless overriden by .mill-jvm-version containing system Pin JVM version used by Mill server process, unless overridden by .mill-jvm-version containing system Feb 21, 2025
@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 21, 2025

Looks like passing in null doesn't seem to help https://github.com/lihaoyi/mill-1/actions/runs/13458684002/job/37608878277

@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 21, 2025

Seems like setting coursierarchive to /tmp seems to have worked! A few other errors to sort out, but at least it seems we're unblocked now

@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 22, 2025

One possible point of confusion with this PR if you use Mill without configuring a java version on your JavaModules, and without configuring a .mill-jvm-version file in your build, you will get the Mill default zulu:17.0.3 JVM rather than your environmentally-installed java JVM.

This would be pretty confusing, and maybe in the absence of explicit configuration it's worth making application code build/run/tested with mill default to the system java rather than the Mill default.

In general it seems reasonable that the default JVM for user-code and for .mill build files would differ: user code may require a variety of JVM versions, while .mill build files are more under Mill's control and can demand a more fixed JVM version. But currently the user-code default JVM is also the Mill JVM, with not way to configure them separately.

I think the right thing to do here may be to separate the two:

  1. .mill-jvm-version that applies to application code, and defaults to system if not set
  2. .mill-build-jvm-version that applies to Mill itself, and defaults to zulu:17.0.3 if not set

I expect most people who want to tweak the Java version would care about .mill-jvm-version, and only very rarely would someone want to tweak .mill-build-jvm-version for doing fancy things in their .mill files.

However, the downside here is that someone would need to download two separate JVMs just to build their code. While the download is automatic, the user would still need to wait for it to complete, which might be slow since JVMs are big

@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 22, 2025

Another more lightweight change would be to only fall back to the hardcoded zulu:17.0.3 if there is no existing JVM installed. That would preserve the ability to use whatever JDK people have installed, while also keeping the Mill JDK consistent with the JavaModule default JDK, and avoid downloading two separate JDKs.

I'm not sure how to check whether there is already a JVM available, apart from shelling out to java -help which is pretty expensive (50-100ms). @alexarchambault do you know if there's any easy way to do such a check?

@alexarchambault
Copy link
Collaborator

Both coursier and Scala CLI do such things. I can't have a look right now, but IIRC, they might just look at JAVA_HOME or for a java binary in PATH (which needs quite some care on Windows, with PATHEXT and all), and maybe read the content of a file under JAVA_HOME 🤔

@alexarchambault
Copy link
Collaborator

coursier and Scala CLI don't require a minimum Java version (they do, like >= 8, but don't really check it IIRC), but Scala CLI does look at the Java version when it spawns a Bloop server (it needs >= 17).

@lihaoyi lihaoyi force-pushed the default-pinned-java branch from 05a229f to a286d19 Compare February 24, 2025 09:17
@lihaoyi lihaoyi force-pushed the default-pinned-java branch from 90e5a23 to de7c1d7 Compare February 24, 2025 11:22
@lihaoyi lihaoyi force-pushed the default-pinned-java branch from f7a0d41 to de8a343 Compare February 24, 2025 16:31
@lihaoyi
Copy link
Member Author

lihaoyi commented Feb 25, 2025

Decided to go with the more conservative approach of just providing a fallback default if there is no JVM available.

@lihaoyi lihaoyi merged commit 5281a01 into com-lihaoyi:main Feb 25, 2025
33 checks passed
@lefou lefou added this to the 0.13.0 milestone Feb 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants