Auto-detecting Java installations

Some applications need to discover Java installations.

NixOS already populates JAVA_HOME and adds java/javac to the PATH, which is a great start.

However, some applications want to discover not just the ‘current’ Java, but all installed/available JRE/JDK’s. Unfortunately, there’s no nice convention on how to make their JAVA_HOME's discoverable. The result of this is that applications are resorting to terrible hacks

In the long run, I would like those terrible hacks to also discover JDK’s installed on NixOS.

Perhaps we should think about providing a not-too-terrible way to discover JRE/JDK’s in NixOS, and then add support for that approach to those tools?

One way I could imagine would be to introduce a JAVA_HOMES environment variable to hold a colon-separated list, and have each JDK package contribute its JAVA_HOME to that list (just like it contributes its bin directory to the PATH). Would that make sense?

Why do you want to have multiple JDK’s available at the same time?

Things like:

  • Build on JDK8, but test that the resulting artifact also works on JDK11
  • Build on JDK11 but target JDK8, and also test the artifacts indeed work with JDK8
  • Build part of your artifact targeting JDK8, but another part targeting JDK11

I agree these are ‘niche’ use cases, and we shouldn’t support them when this comes at a prohibitive cost. But exposing them through a JAVA_HOMES environment variable seems fairly non-intrusive, and would be rather useful at least to me ;).

As a workaround to make this work with sbt for now I have in my shell.nix:

{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  ...
  JAVA_8_HOME = "${pkgs.jdk}/lib/openjdk";
  JAVA_11_HOME = "${pkgs.jdk11}/lib/openjdk";
}

And then in an .sbt file:

fullJavaHomes in ThisBuild := fullJavaHomes.value + (("1.8", file(sys.env("JAVA_8_HOME")))) + (("11", file(sys.env("JAVA_11_HOME"))))

Not ideal, but gets the job done. I would still love to see a nice convention so we can solve this ‘upstream’ (in nix/sbt) instead of for each local build.

1 Like

Gradle 6.7 introduced what they call “Toolchains” (also see the docs). While it allows to automatically download specified JDKs (which is not what I expect Nix users to want), it also “auto-detects” JDKs present on the system. @raboof would probably call most of these detection mechansims “dirty hacks” :stuck_out_tongue:

They expose two properties to register JDKs: org.gradle.java.installations.{fromEnv,paths} with slightly different semantics. Please consult the docs referenced above.

@raboof could use o.g.j.i.fromEnv=JAVA_8_HOME,JAVA_11_HOME.

I thought about adding an argument to gradleGen so that we could pass e.g. a list of JDKs that should be “forwarded” to Gradle. Sadly I couldn’t find a way to make that happen, because AFAIK the properties mentioned above cannot be set via environment variables (they are not in org.gradle.project.*) and I couldn’t make passing via GRADLE_OPTS work.

Then I thought, that maybe we could implement yet another detection mechanism, directly in Gradle, and ended up here. Any progress or new findings with regards to auto-detecting JDKs on top of Nix?

I would - though to be clear there’s nothing wrong with using dirty hacks when no more reliable mechanism is available - it just indicates there’s a need for a more reliable mechanism :wink:

Not really. I made an incomplete prototype of providing JAVA_x_HOME environment variables (#76699), but since I received no encouraging feedback I abandoned that.

I still like that approach (or at least I don’t see any particularly viable alternative). The main criticism seems to be it’s no “generally accepted standard”, which is fair enough - so perhaps the next step is to lobby for it to become a “generally accepted standard” outside of NixOS first :slight_smile: .