Add rootpath(s) and execpath(s) functions to template expansion

In addition to the $(location) function, we now also support $(rootpath) and
$(execpath) functions.

Unfortunately, we have to do this in two places since the Skylark API for expand_location has to continue calling into LocationExpander in order to preserve its semantic contract.

Progress on #2475.

RELNOTES[NEW]:
    In addition to $(location), Bazel now also supports $(rootpath) to obtain
    the root-relative path (i.e., for runfiles locations), and $(execpath) to
    obtain the exec path (i.e., for build-time locations)

PiperOrigin-RevId: 174454119
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java
index 6dc4574..a1137cd 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java
@@ -63,4 +63,36 @@
 
     assertThat(result).isEqualTo("foo 'spaces/file with space A' 'spaces/file with space B' bar");
   }
+
+  @Test
+  public void otherPathExpansion() throws Exception {
+    scratch.file(
+        "expansion/BUILD",
+        "genrule(name='foo', outs=['foo.txt'], cmd='never executed')",
+        "sh_library(name='lib', srcs=[':foo'])");
+
+    LocationExpander expander = makeExpander("//expansion:lib");
+    assertThat(expander.expand("foo $(execpath :foo) bar"))
+        .matches("foo .*-out/.*/expansion/foo\\.txt bar");
+    assertThat(expander.expand("foo $(execpaths :foo) bar"))
+        .matches("foo .*-out/.*/expansion/foo\\.txt bar");
+    assertThat(expander.expand("foo $(rootpath :foo) bar"))
+        .matches("foo expansion/foo.txt bar");
+    assertThat(expander.expand("foo $(rootpaths :foo) bar"))
+        .matches("foo expansion/foo.txt bar");
+  }
+
+  @Test
+  public void otherPathMultiExpansion() throws Exception {
+    scratch.file(
+        "expansion/BUILD",
+        "genrule(name='foo', outs=['foo.txt', 'bar.txt'], cmd='never executed')",
+        "sh_library(name='lib', srcs=[':foo'])");
+
+    LocationExpander expander = makeExpander("//expansion:lib");
+    assertThat(expander.expand("foo $(execpaths :foo) bar"))
+        .matches("foo .*-out/.*/expansion/bar\\.txt .*-out/.*/expansion/foo\\.txt bar");
+    assertThat(expander.expand("foo $(rootpaths :foo) bar"))
+        .matches("foo expansion/bar.txt expansion/foo.txt bar");
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderTest.java
index 7fe4066..f49cf59 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderTest.java
@@ -16,10 +16,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.packages.AbstractRuleErrorConsumer;
 import com.google.devtools.build.lib.packages.RuleErrorConsumer;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Function;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -60,8 +62,9 @@
   private LocationExpander makeExpander(RuleErrorConsumer ruleErrorConsumer) throws Exception {
     return new LocationExpander(
         ruleErrorConsumer,
-        (String s) -> "one(" + s + ")",
-        (String s) -> "more(" + s + ")");
+        ImmutableMap.<String, Function<String, String>>of(
+            "location", (String s) -> "one(" + s + ")",
+            "locations", (String s) -> "more(" + s + ")"));
   }
 
   private String expand(String input) throws Exception {