Skip to content

Commit cff0dc9

Browse files
ulfjackkatre
authored andcommitted
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 bazelbuild#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
1 parent f75eade commit cff0dc9

4 files changed

Lines changed: 79 additions & 40 deletions

File tree

src/main/java/com/google/devtools/build/lib/analysis/LocationExpander.java

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,21 @@ public static enum Options {
7272
ALLOW_DATA,
7373
}
7474

75-
private static final String LOCATION = "$(location";
75+
private static final boolean EXACTLY_ONE = false;
76+
private static final boolean ALLOW_MULTIPLE = true;
77+
78+
private static final boolean USE_ROOT_PATHS = false;
79+
private static final boolean USE_EXEC_PATHS = true;
7680

7781
private final RuleErrorConsumer ruleErrorConsumer;
78-
private final Function<String, String> locationFunction;
79-
private final Function<String, String> locationsFunction;
82+
private final ImmutableMap<String, Function<String, String>> functions;
8083

8184
@VisibleForTesting
8285
LocationExpander(
8386
RuleErrorConsumer ruleErrorConsumer,
84-
Function<String, String> locationFunction,
85-
Function<String, String> locationsFunction) {
87+
Map<String, Function<String, String>> functions) {
8688
this.ruleErrorConsumer = ruleErrorConsumer;
87-
this.locationFunction = locationFunction;
88-
this.locationsFunction = locationsFunction;
89+
this.functions = ImmutableMap.copyOf(functions);
8990
}
9091

9192
private LocationExpander(
@@ -95,8 +96,7 @@ private LocationExpander(
9596
boolean execPaths) {
9697
this(
9798
ruleErrorConsumer,
98-
new LocationFunction(root, locationMap, execPaths, false),
99-
new LocationFunction(root, locationMap, execPaths, true));
99+
allLocationFunctions(root, locationMap, execPaths));
100100
}
101101

102102
/**
@@ -167,44 +167,40 @@ private String expand(String value, ErrorReporter reporter) {
167167
StringBuilder result = new StringBuilder(value.length());
168168

169169
while (true) {
170-
// (1) Find '$(location ' or '$(locations '.
171-
Function<String, String> func = locationFunction;
172-
int start = value.indexOf(LOCATION, restart);
173-
int scannedLength = LOCATION.length();
174-
if (start == -1 || start + scannedLength == attrLength) {
170+
// (1) Find '$(<fname> '.
171+
int start = value.indexOf("$(", restart);
172+
if (start == -1) {
175173
result.append(value.substring(restart));
176174
break;
177175
}
178-
if (value.charAt(start + scannedLength) == 's') {
179-
scannedLength++;
180-
if (start + scannedLength == attrLength) {
181-
result.append(value.substring(restart));
182-
break;
183-
}
184-
func = locationsFunction;
176+
int nextWhitespace = value.indexOf(' ', start);
177+
if (nextWhitespace == -1) {
178+
result.append(value, restart, start + 2);
179+
restart = start + 2;
180+
continue;
185181
}
186-
if (value.charAt(start + scannedLength) != ' ') {
187-
result.append(value, restart, start + scannedLength);
188-
restart = start + scannedLength;
182+
String fname = value.substring(start + 2, nextWhitespace);
183+
if (!functions.containsKey(fname)) {
184+
result.append(value, restart, start + 2);
185+
restart = start + 2;
189186
continue;
190187
}
191188

192189
result.append(value, restart, start);
193-
scannedLength++;
194190

195-
int end = value.indexOf(')', start + scannedLength);
191+
int end = value.indexOf(')', nextWhitespace);
196192
if (end == -1) {
197193
reporter.report(
198194
String.format(
199195
"unterminated $(%s) expression",
200-
value.substring(start + 2, start + scannedLength - 1)));
196+
value.substring(start + 2, nextWhitespace)));
201197
return value;
202198
}
203199

204200
// (2) Call appropriate function to obtain string replacement.
205-
String functionValue = value.substring(start + scannedLength, end).trim();
201+
String functionValue = value.substring(nextWhitespace + 1, end).trim();
206202
try {
207-
String replacement = func.apply(functionValue);
203+
String replacement = functions.get(fname).apply(functionValue);
208204
result.append(replacement);
209205
} catch (IllegalStateException ise) {
210206
reporter.report(ise.getMessage());
@@ -322,6 +318,18 @@ private String functionName() {
322318
}
323319
}
324320

321+
static ImmutableMap<String, Function<String, String>> allLocationFunctions(
322+
Label root, Supplier<Map<Label, Collection<Artifact>>> locationMap, boolean execPaths) {
323+
return new ImmutableMap.Builder<String, Function<String, String>>()
324+
.put("location", new LocationFunction(root, locationMap, execPaths, EXACTLY_ONE))
325+
.put("locations", new LocationFunction(root, locationMap, execPaths, ALLOW_MULTIPLE))
326+
.put("rootpath", new LocationFunction(root, locationMap, USE_ROOT_PATHS, EXACTLY_ONE))
327+
.put("rootpaths", new LocationFunction(root, locationMap, USE_ROOT_PATHS, ALLOW_MULTIPLE))
328+
.put("execpath", new LocationFunction(root, locationMap, USE_EXEC_PATHS, EXACTLY_ONE))
329+
.put("execpaths", new LocationFunction(root, locationMap, USE_EXEC_PATHS, ALLOW_MULTIPLE))
330+
.build();
331+
}
332+
325333
/**
326334
* Extracts all possible target locations from target specification.
327335
*

src/main/java/com/google/devtools/build/lib/analysis/LocationTemplateContext.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import com.google.common.collect.ImmutableMap;
2121
import com.google.common.collect.ImmutableSet;
2222
import com.google.devtools.build.lib.actions.Artifact;
23-
import com.google.devtools.build.lib.analysis.LocationExpander.LocationFunction;
2423
import com.google.devtools.build.lib.analysis.LocationExpander.Options;
2524
import com.google.devtools.build.lib.analysis.stringtemplate.ExpansionException;
2625
import com.google.devtools.build.lib.analysis.stringtemplate.TemplateContext;
@@ -49,17 +48,15 @@
4948
*/
5049
final class LocationTemplateContext implements TemplateContext {
5150
private final TemplateContext delegate;
52-
private final Function<String, String> locationFunction;
53-
private final Function<String, String> locationsFunction;
51+
private final ImmutableMap<String, Function<String, String>> functions;
5452

5553
private LocationTemplateContext(
5654
TemplateContext delegate,
5755
Label root,
5856
Supplier<Map<Label, Collection<Artifact>>> locationMap,
5957
boolean execPaths) {
6058
this.delegate = delegate;
61-
this.locationFunction = new LocationFunction(root, locationMap, execPaths, false);
62-
this.locationsFunction = new LocationFunction(root, locationMap, execPaths, true);
59+
this.functions = LocationExpander.allLocationFunctions(root, locationMap, execPaths);
6360
}
6461

6562
private LocationTemplateContext(
@@ -98,10 +95,9 @@ public String lookupVariable(String name) throws ExpansionException {
9895
@Override
9996
public String lookupFunction(String name, String param) throws ExpansionException {
10097
try {
101-
if ("location".equals(name)) {
102-
return locationFunction.apply(param);
103-
} else if ("locations".equals(name)) {
104-
return locationsFunction.apply(param);
98+
Function<String, String> f = functions.get(name);
99+
if (f != null) {
100+
return f.apply(param);
105101
}
106102
} catch (IllegalStateException e) {
107103
throw new ExpansionException(e.getMessage(), e);

src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,36 @@ public void locations_spaces() throws Exception {
6363

6464
assertThat(result).isEqualTo("foo 'spaces/file with space A' 'spaces/file with space B' bar");
6565
}
66+
67+
@Test
68+
public void otherPathExpansion() throws Exception {
69+
scratch.file(
70+
"expansion/BUILD",
71+
"genrule(name='foo', outs=['foo.txt'], cmd='never executed')",
72+
"sh_library(name='lib', srcs=[':foo'])");
73+
74+
LocationExpander expander = makeExpander("//expansion:lib");
75+
assertThat(expander.expand("foo $(execpath :foo) bar"))
76+
.matches("foo .*-out/.*/expansion/foo\\.txt bar");
77+
assertThat(expander.expand("foo $(execpaths :foo) bar"))
78+
.matches("foo .*-out/.*/expansion/foo\\.txt bar");
79+
assertThat(expander.expand("foo $(rootpath :foo) bar"))
80+
.matches("foo expansion/foo.txt bar");
81+
assertThat(expander.expand("foo $(rootpaths :foo) bar"))
82+
.matches("foo expansion/foo.txt bar");
83+
}
84+
85+
@Test
86+
public void otherPathMultiExpansion() throws Exception {
87+
scratch.file(
88+
"expansion/BUILD",
89+
"genrule(name='foo', outs=['foo.txt', 'bar.txt'], cmd='never executed')",
90+
"sh_library(name='lib', srcs=[':foo'])");
91+
92+
LocationExpander expander = makeExpander("//expansion:lib");
93+
assertThat(expander.expand("foo $(execpaths :foo) bar"))
94+
.matches("foo .*-out/.*/expansion/bar\\.txt .*-out/.*/expansion/foo\\.txt bar");
95+
assertThat(expander.expand("foo $(rootpaths :foo) bar"))
96+
.matches("foo expansion/bar.txt expansion/foo.txt bar");
97+
}
6698
}

src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
import static com.google.common.truth.Truth.assertThat;
1818

19+
import com.google.common.collect.ImmutableMap;
1920
import com.google.devtools.build.lib.packages.AbstractRuleErrorConsumer;
2021
import com.google.devtools.build.lib.packages.RuleErrorConsumer;
2122
import java.util.ArrayList;
2223
import java.util.List;
24+
import java.util.function.Function;
2325
import org.junit.Test;
2426
import org.junit.runner.RunWith;
2527
import org.junit.runners.JUnit4;
@@ -60,8 +62,9 @@ public boolean hasErrors() {
6062
private LocationExpander makeExpander(RuleErrorConsumer ruleErrorConsumer) throws Exception {
6163
return new LocationExpander(
6264
ruleErrorConsumer,
63-
(String s) -> "one(" + s + ")",
64-
(String s) -> "more(" + s + ")");
65+
ImmutableMap.<String, Function<String, String>>of(
66+
"location", (String s) -> "one(" + s + ")",
67+
"locations", (String s) -> "more(" + s + ")"));
6568
}
6669

6770
private String expand(String input) throws Exception {

0 commit comments

Comments
 (0)