Add java_home_runfiles_path to JavaRuntimeInfo

JavaRuntimeInfo provides a runfiles path to bin/java, execpath to the JDK, and execpath to bin/java. This CL adds "java_home_runfiles_path" which provides the runfiles path to the JDK.

Added/modified tests to check all four JavaRuntimeInfo attributes in the java_home cases of absolute, hermetic (prebuilt), and generated.

PiperOrigin-RevId: 217572927
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntime.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntime.java
index 7ae44f1..68921a7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntime.java
@@ -81,6 +81,8 @@
       javaHome = javaBinaryExecPath.getParentDirectory().getParentDirectory();
       filesBuilder.add(java);
     }
+    PathFragment javaHomeRunfilesPath =
+        javaBinaryRunfilesPath.getParentDirectory().getParentDirectory();
 
     NestedSet<Artifact> filesToBuild = filesBuilder.build();
     NestedSet<Artifact> middleman =
@@ -94,7 +96,12 @@
 
     JavaRuntimeInfo javaRuntime =
         JavaRuntimeInfo.create(
-            filesToBuild, middleman, javaHome, javaBinaryExecPath, javaBinaryRunfilesPath);
+            filesToBuild,
+            middleman,
+            javaHome,
+            javaBinaryExecPath,
+            javaHomeRunfilesPath,
+            javaBinaryRunfilesPath);
 
     TemplateVariableInfo templateVariableInfo = new TemplateVariableInfo(
         ImmutableMap.of(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfo.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfo.java
index d48db3d..c5f7ee3 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfo.java
@@ -44,12 +44,14 @@
       NestedSet<Artifact> javaBaseInputsMiddleman,
       PathFragment javaHome,
       PathFragment javaBinaryExecPath,
+      PathFragment javaHomeRunfilesPath,
       PathFragment javaBinaryRunfilesPath) {
     return new JavaRuntimeInfo(
         javaBaseInputs,
         javaBaseInputsMiddleman,
         javaHome,
         javaBinaryExecPath,
+        javaHomeRunfilesPath,
         javaBinaryRunfilesPath);
   }
 
@@ -94,6 +96,7 @@
   private final NestedSet<Artifact> javaBaseInputsMiddleman;
   private final PathFragment javaHome;
   private final PathFragment javaBinaryExecPath;
+  private final PathFragment javaHomeRunfilesPath;
   private final PathFragment javaBinaryRunfilesPath;
 
   @AutoCodec.Instantiator
@@ -103,12 +106,14 @@
       NestedSet<Artifact> javaBaseInputsMiddleman,
       PathFragment javaHome,
       PathFragment javaBinaryExecPath,
+      PathFragment javaHomeRunfilesPath,
       PathFragment javaBinaryRunfilesPath) {
     super(PROVIDER);
     this.javaBaseInputs = javaBaseInputs;
     this.javaBaseInputsMiddleman = javaBaseInputsMiddleman;
     this.javaHome = javaHome;
     this.javaBinaryExecPath = javaBinaryExecPath;
+    this.javaHomeRunfilesPath = javaHomeRunfilesPath;
     this.javaBinaryRunfilesPath = javaBinaryRunfilesPath;
   }
 
@@ -134,6 +139,12 @@
     return javaBinaryExecPath;
   }
 
+  /** The runfiles path of the root directory of the Java installation. */
+  @Override
+  public PathFragment javaHomeRunfilesPath() {
+    return javaHomeRunfilesPath;
+  }
+
   @Override
   /** The runfiles path of the Java binary. */
   public PathFragment javaBinaryRunfilesPath() {
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaRuntimeInfoApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaRuntimeInfoApi.java
index bea3b41..8db3fb1 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaRuntimeInfoApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaRuntimeInfoApi.java
@@ -32,22 +32,32 @@
   )
   public PathFragment javaHome();
 
+  /** The execpath of the Java binary. */
   @SkylarkCallable(
       name = "java_executable_exec_path",
       doc = "Returns the execpath of the Java executable.",
-      structField = true
-  )
-  /** The execpath of the Java binary. */
+      structField = true)
   public PathFragment javaBinaryExecPath();
 
+  /** The runfiles path of the JDK. */
+  @SkylarkCallable(
+      name = "java_home_runfiles_path",
+      doc =
+          "Returns the path of the Java installation in runfiles trees. This should only be used "
+              + "when one needs to access the JDK during the execution of a binary or a test built "
+              + "by Bazel. In particular, when one needs the JDK during an action, "
+              + "java_home should be used instead.",
+      structField = true)
+  public PathFragment javaHomeRunfilesPath();
+
+  /** The runfiles path of the Java binary. */
   @SkylarkCallable(
       name = "java_executable_runfiles_path",
-      doc = "Returns the path of the Java executable in runfiles trees. This should only be used "
-          + "when one needs to access the JVM during the execution of a binary or a test built "
-          + "by Bazel. In particular, when one needs to invoke the JVM during an action, "
-          + "java_executable_exec_path should be used instead.",
-      structField = true
-  )
-  /** The runfiles path of the Java binary. */
+      doc =
+          "Returns the path of the Java executable in runfiles trees. This should only be used "
+              + "when one needs to access the JVM during the execution of a binary or a test built "
+              + "by Bazel. In particular, when one needs to invoke the JVM during an action, "
+              + "java_executable_exec_path should be used instead.",
+      structField = true)
   public PathFragment javaBinaryRunfilesPath();
 }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfoTest.java b/src/test/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfoTest.java
index d399f76..700a942 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfoTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/java/JavaRuntimeInfoTest.java
@@ -35,6 +35,7 @@
             NestedSetBuilder.emptySet(Order.STABLE_ORDER),
             PathFragment.create(""),
             PathFragment.create(""),
+            PathFragment.create(""),
             PathFragment.create(""));
     JavaRuntimeInfo b =
         JavaRuntimeInfo.create(
@@ -42,6 +43,7 @@
             NestedSetBuilder.emptySet(Order.STABLE_ORDER),
             PathFragment.create(""),
             PathFragment.create(""),
+            PathFragment.create(""),
             PathFragment.create(""));
 
     new EqualsTester()
diff --git a/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java b/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
index 89e3474..72fcf84 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
@@ -49,10 +49,11 @@
       + "//tools/jdk:current_host_java_runtime";
 
   @Test
-  public void testJavaRuntimeProviderJavaExecutableAbsolute() throws Exception {
-    scratch.file("a/BUILD",
+  public void testJavaRuntimeProviderJavaAbsolute() throws Exception {
+    scratch.file(
+        "a/BUILD",
         "load(':rule.bzl', 'jrule')",
-        "java_runtime(name='jvm', srcs=[], java_home='/foo/bar/')",
+        "java_runtime(name='jvm', srcs=[], java_home='/foo/bar')",
         "java_runtime_alias(name='alias')",
         "jrule(name='r')");
 
@@ -61,24 +62,33 @@
         "def _impl(ctx):",
         "  provider = ctx.attr._java_runtime[java_common.JavaRuntimeInfo]",
         "  return struct(",
-        "    java_executable = provider.java_executable_exec_path,",
-        "    java_runfiles = provider.java_executable_runfiles_path,",
-        ")",
+        "    java_home_exec_path = provider.java_home,",
+        "    java_executable_exec_path = provider.java_executable_exec_path,",
+        "    java_home_runfiles_path = provider.java_home_runfiles_path,",
+        "    java_executable_runfiles_path = provider.java_executable_runfiles_path,",
+        "  )",
         "jrule = rule(_impl, attrs = { '_java_runtime': attr.label(default=Label('//a:alias'))})");
 
     useConfiguration("--javabase=//a:jvm");
     ConfiguredTarget ct = getConfiguredTarget("//a:r");
-    @SuppressWarnings("unchecked") PathFragment javaExecutable =
-        (PathFragment) ct.get("java_executable");
-    assertThat(javaExecutable.getPathString()).startsWith("/foo/bar/bin/java");
-    @SuppressWarnings("unchecked") PathFragment javaRunfiles =
-        (PathFragment) ct.get("java_runfiles");
-    assertThat(javaRunfiles.getPathString()).startsWith("/foo/bar/bin/java");
+    @SuppressWarnings("unchecked")
+    PathFragment javaHomeExecPath = (PathFragment) ct.get("java_home_exec_path");
+    assertThat(javaHomeExecPath.getPathString()).isEqualTo("/foo/bar");
+    @SuppressWarnings("unchecked")
+    PathFragment javaExecutableExecPath = (PathFragment) ct.get("java_executable_exec_path");
+    assertThat(javaExecutableExecPath.getPathString()).startsWith("/foo/bar/bin/java");
+    @SuppressWarnings("unchecked")
+    PathFragment javaHomeRunfilesPath = (PathFragment) ct.get("java_home_runfiles_path");
+    assertThat(javaHomeRunfilesPath.getPathString()).isEqualTo("/foo/bar");
+    @SuppressWarnings("unchecked")
+    PathFragment javaExecutableRunfiles = (PathFragment) ct.get("java_executable_runfiles_path");
+    assertThat(javaExecutableRunfiles.getPathString()).startsWith("/foo/bar/bin/java");
   }
 
   @Test
-  public void testJavaRuntimeProviderJavaExecutableHermetic() throws Exception {
-    scratch.file("a/BUILD",
+  public void testJavaRuntimeProviderJavaHermetic() throws Exception {
+    scratch.file(
+        "a/BUILD",
         "load(':rule.bzl', 'jrule')",
         "java_runtime(name='jvm', srcs=[], java_home='foo/bar')",
         "java_runtime_alias(name='alias')",
@@ -89,26 +99,36 @@
         "def _impl(ctx):",
         "  provider = ctx.attr._java_runtime[java_common.JavaRuntimeInfo]",
         "  return struct(",
-        "    java_executable = provider.java_executable_exec_path,",
-        "    java_runfiles = provider.java_executable_runfiles_path,",
-        ")",
+        "    java_home_exec_path = provider.java_home,",
+        "    java_executable_exec_path = provider.java_executable_exec_path,",
+        "    java_home_runfiles_path = provider.java_home_runfiles_path,",
+        "    java_executable_runfiles_path = provider.java_executable_runfiles_path,",
+        "  )",
         "jrule = rule(_impl, attrs = { '_java_runtime': attr.label(default=Label('//a:alias'))})");
 
     useConfiguration("--javabase=//a:jvm");
     ConfiguredTarget ct = getConfiguredTarget("//a:r");
-    @SuppressWarnings("unchecked") PathFragment javaExecutable =
-        (PathFragment) ct.get("java_executable");
-    assertThat(javaExecutable.getPathString()).startsWith("a/foo/bar/bin/java");
-    @SuppressWarnings("unchecked") PathFragment javaRunfiles =
-        (PathFragment) ct.get("java_runfiles");
-    assertThat(javaRunfiles.getPathString()).startsWith("a/foo/bar/bin/java");
+    @SuppressWarnings("unchecked")
+    PathFragment javaHomeExecPath = (PathFragment) ct.get("java_home_exec_path");
+    assertThat(javaHomeExecPath.getPathString()).isEqualTo("a/foo/bar");
+    @SuppressWarnings("unchecked")
+    PathFragment javaExecutableExecPath = (PathFragment) ct.get("java_executable_exec_path");
+    assertThat(javaExecutableExecPath.getPathString()).startsWith("a/foo/bar/bin/java");
+    @SuppressWarnings("unchecked")
+    PathFragment javaHomeRunfilesPath = (PathFragment) ct.get("java_home_runfiles_path");
+    assertThat(javaHomeRunfilesPath.getPathString()).isEqualTo("a/foo/bar");
+    @SuppressWarnings("unchecked")
+    PathFragment javaExecutableRunfiles = (PathFragment) ct.get("java_executable_runfiles_path");
+    assertThat(javaExecutableRunfiles.getPathString()).startsWith("a/foo/bar/bin/java");
   }
 
   @Test
-  public void testJavaRuntimeProviderJavaHome() throws Exception {
-    scratch.file("a/BUILD",
+  public void testJavaRuntimeProviderJavaGenerated() throws Exception {
+    scratch.file(
+        "a/BUILD",
         "load(':rule.bzl', 'jrule')",
-        "java_runtime(name='jvm', srcs=[], java_home='/foo/bar/')",
+        "genrule(name='gen', cmd='', outs=['foo/bar/bin/java'])",
+        "java_runtime(name='jvm', srcs=[], java='foo/bar/bin/java')",
         "java_runtime_alias(name='alias')",
         "jrule(name='r')");
 
@@ -117,15 +137,29 @@
         "def _impl(ctx):",
         "  provider = ctx.attr._java_runtime[java_common.JavaRuntimeInfo]",
         "  return struct(",
-        "    java_home = provider.java_home",
-        ")",
+        "    java_home_exec_path = provider.java_home,",
+        "    java_executable_exec_path = provider.java_executable_exec_path,",
+        "    java_home_runfiles_path = provider.java_home_runfiles_path,",
+        "    java_executable_runfiles_path = provider.java_executable_runfiles_path,",
+        "  )",
         "jrule = rule(_impl, attrs = { '_java_runtime': attr.label(default=Label('//a:alias'))})");
 
     useConfiguration("--javabase=//a:jvm");
     ConfiguredTarget ct = getConfiguredTarget("//a:r");
-    @SuppressWarnings("unchecked") PathFragment javaHome =
-        (PathFragment) ct.get("java_home");
-    assertThat(javaHome.getPathString()).isEqualTo("/foo/bar");
+    @SuppressWarnings("unchecked")
+    PathFragment javaHomeExecPath = (PathFragment) ct.get("java_home_exec_path");
+    assertThat(javaHomeExecPath.getPathString())
+        .isEqualTo(getGenfilesArtifactWithNoOwner("a/foo/bar").getExecPathString());
+    @SuppressWarnings("unchecked")
+    PathFragment javaExecutableExecPath = (PathFragment) ct.get("java_executable_exec_path");
+    assertThat(javaExecutableExecPath.getPathString())
+        .startsWith(getGenfilesArtifactWithNoOwner("a/foo/bar/bin/java").getExecPathString());
+    @SuppressWarnings("unchecked")
+    PathFragment javaHomeRunfilesPath = (PathFragment) ct.get("java_home_runfiles_path");
+    assertThat(javaHomeRunfilesPath.getPathString()).isEqualTo("a/foo/bar");
+    @SuppressWarnings("unchecked")
+    PathFragment javaExecutableRunfiles = (PathFragment) ct.get("java_executable_runfiles_path");
+    assertThat(javaExecutableRunfiles.getPathString()).startsWith("a/foo/bar/bin/java");
   }
 
   @Test