Allow relative paths as arguments to `path.get_child`

This allows for usages such as:

```
some_path.get_child("nested", "directory")

relative_path = "user/provided"
some_path.get_child(relative_path)
```

RELNOTES: The `get_child` method of `path` now accepts an arbitrary
number of relative path strings as positional arguments.

Closes #15798.

PiperOrigin-RevId: 475519595
Change-Id: I92c317d7a0a0b02df6b392ab944c8a8cde5b2a93
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java
index 9489ec6..dc70f67 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java
@@ -18,13 +18,17 @@
 import com.google.devtools.build.docgen.annot.DocCategory;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import java.io.IOException;
 import javax.annotation.Nullable;
 import net.starlark.java.annot.Param;
 import net.starlark.java.annot.StarlarkBuiltin;
 import net.starlark.java.annot.StarlarkMethod;
+import net.starlark.java.eval.EvalException;
 import net.starlark.java.eval.Printer;
+import net.starlark.java.eval.Sequence;
 import net.starlark.java.eval.StarlarkValue;
+import net.starlark.java.eval.Tuple;
 
 /**
  * A Path object to be used into Starlark remote repository.
@@ -96,12 +100,19 @@
 
   @StarlarkMethod(
       name = "get_child",
-      doc = "Append the given path to this path and return the resulted path.",
-      parameters = {
-        @Param(name = "child_path", doc = "The path to append to this path."),
-      })
-  public StarlarkPath getChild(String childPath) {
-    return new StarlarkPath(path.getChild(childPath));
+      doc = "Returns the path obtained by joining this path with the given relative paths.",
+      extraPositionals =
+          @Param(
+              name = "relative_paths",
+              doc =
+                  "Zero or more relative path strings to append to this path with path separators"
+                      + "added as needed."))
+  public StarlarkPath getChild(Tuple relativePaths) throws EvalException {
+    return new StarlarkPath(
+        path.getRelative(
+            String.join(
+                Character.toString(PathFragment.SEPARATOR_CHAR),
+                Sequence.cast(relativePaths, String.class, "relative_paths"))));
   }
 
   @StarlarkMethod(
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD b/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD
index 0762c3c..3000579 100644
--- a/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD
@@ -36,11 +36,13 @@
         "//src/main/java/com/google/devtools/build/lib/util:abrupt_exit_exception",
         "//src/main/java/com/google/devtools/build/lib/vfs",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
+        "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
         "//src/main/java/net/starlark/java/eval",
         "//src/main/java/net/starlark/java/syntax",
         "//src/test/java/com/google/devtools/build/lib/analysis/util",
+        "//src/test/java/com/google/devtools/build/lib/starlark/util",
         "//src/test/java/com/google/devtools/build/lib/testutil",
         "//third_party:guava",
         "//third_party:jsr305",
@@ -82,11 +84,13 @@
         "//src/main/java/com/google/devtools/build/lib/util:abrupt_exit_exception",
         "//src/main/java/com/google/devtools/build/lib/vfs",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
+        "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
         "//src/main/java/net/starlark/java/eval",
         "//src/main/java/net/starlark/java/syntax",
         "//src/test/java/com/google/devtools/build/lib/analysis/util",
+        "//src/test/java/com/google/devtools/build/lib/starlark/util",
         "//src/test/java/com/google/devtools/build/lib/testutil",
         "//third_party:guava",
         "//third_party:jsr305",
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPathTest.java b/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPathTest.java
new file mode 100644
index 0000000..e6ebb59
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPathTest.java
@@ -0,0 +1,50 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.repository.starlark;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.build.lib.starlark.util.BazelEvaluationTestCase;
+import com.google.devtools.build.lib.vfs.DigestHashFunction;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for complex functions of {@link StarlarkPath}. */
+@RunWith(JUnit4.class)
+public class StarlarkPathTest {
+
+  private final BazelEvaluationTestCase ev = new BazelEvaluationTestCase();
+  private final FileSystem fs = new InMemoryFileSystem(DigestHashFunction.SHA256);
+  private final Path wd = FileSystemUtils.getWorkingDirectory(fs);
+
+  @Before
+  public void setup() throws Exception {
+    ev.update("wd", new StarlarkPath(wd));
+  }
+
+  @Test
+  public void testStarlarkPathGetChild() throws Exception {
+    assertThat(ev.eval("wd.get_child()")).isEqualTo(new StarlarkPath(wd));
+    assertThat(ev.eval("wd.get_child('foo')")).isEqualTo(new StarlarkPath(wd.getChild("foo")));
+    assertThat(ev.eval("wd.get_child('a','b/c','/d/')"))
+        .isEqualTo(new StarlarkPath(wd.getRelative("a/b/c/d")));
+  }
+}
diff --git a/tools/cpp/windows_cc_configure.bzl b/tools/cpp/windows_cc_configure.bzl
index 146a88b..2691c7e 100644
--- a/tools/cpp/windows_cc_configure.bzl
+++ b/tools/cpp/windows_cc_configure.bzl
@@ -298,7 +298,7 @@
 
     # By checking the source code of VCVARSALL.BAT in VC 2015, we know that
     # when devenv.exe or wdexpress.exe exists, VCVARSALL.BAT supports Windows SDK selection.
-    vc_common_ide = repository_ctx.path(vc_path).dirname.get_child("Common7").get_child("IDE")
+    vc_common_ide = repository_ctx.path(vc_path).dirname.get_child("Common7", "IDE")
     for tool in ["devenv.exe", "wdexpress.exe"]:
         if vc_common_ide.get_child(tool).exists:
             return True
diff --git a/tools/jdk/local_java_repository.bzl b/tools/jdk/local_java_repository.bzl
index 336b794d..05686c7 100644
--- a/tools/jdk/local_java_repository.bzl
+++ b/tools/jdk/local_java_repository.bzl
@@ -125,7 +125,7 @@
     )
 
     extension = ".exe" if repository_ctx.os.name.find("windows") != -1 else ""
-    java_bin = java_home_path.get_child("bin").get_child("java" + extension)
+    java_bin = java_home_path.get_child("bin", "java" + extension)
 
     if not java_bin.exists:
         # Java binary does not exist