Skip to content

Commit ece17d5

Browse files
fmeumShreeM01
andauthored
Add $(rlocationpath(s) ...) expansion (bazelbuild#16668)
The new location expansion pattern `rlocationpath` and its plural version `rlocationpaths` resolve to the path(s) suitable for the Rlocation function offered by runfiles libraries. Compared to the `rootpath` pattern, which can returns `../repo_name/pkg/file` for a file in an external repository and `pkg/file` for a file in the main repository, the path returned by `rlocationpath` is always of the form `repo_name/pkg/file`. RELNOTES: The new path variable `$(rlocationpath ...)` and its plural form `$(rlocationpaths ...)` can be used to expand labels to the paths accepted by the `Rlocation` function of runfiles libraries. This is the preferred way to access data dependencies at runtime and works on all platforms, even when runfiles are not enabled (e.g., on Windows by default). Work towards bazelbuild#16124 Fixes bazelbuild#10923 Closes bazelbuild#16667. PiperOrigin-RevId: 485930083 Change-Id: I85c0ef29c8332eeabd8473d2611ede546eb24016 Co-authored-by: kshyanashree <[email protected]>
1 parent 8c80ef8 commit ece17d5

File tree

7 files changed

+282
-92
lines changed

7 files changed

+282
-92
lines changed

src/main/java/com/google/devtools/build/docgen/templates/be/make-variables.vm

+61-18
Original file line numberDiff line numberDiff line change
@@ -262,15 +262,15 @@ title: Make Variables
262262
</p>
263263
<p>
264264
Output files are staged similarly, but are also prefixed with the subpath
265-
<code>bazel-out/cpu-compilation_mode/bin</code> (or for certain outputs:
266-
<code>bazel-out/cpu-compilation_mode/genfiles</code>, or for the outputs
267-
of host tools: <code>bazel-out/host/bin</code>). In the above example,
268-
<code>//testapp:app</code> is a host tool because it appears in
265+
<code>bazel-out/cpu-compilation_mode/bin</code> (or for the outputs of
266+
tools: <code>bazel-out/cpu-opt-exec-hash/bin</code>). In the above example,
267+
<code>//testapp:app</code> is a tool because it appears in
269268
<code>show_app_output</code>'s <code><a
270269
href="$expander.expandRef("genrule.tools")">tools</a></code> attribute.
271270
So its output file <code>app</code> is written to
272-
<code>bazel-myproject/bazel-out/host/bin/testapp/app</code>. The exec path
273-
is thus <code>bazel-out/host/bin/testapp/app</code>. This extra prefix
271+
<code>bazel-myproject/bazel-out/cpu-opt-exec-hash/bin/testapp/app</code>.
272+
The exec path is thus <code>
273+
bazel-out/cpu-opt-exec-hash/bin/testapp/app</code>. This extra prefix
274274
makes it possible to build the same target for, say, two different CPUs in
275275
the same build without the results clobbering each other.
276276
</p>
@@ -284,15 +284,57 @@ title: Make Variables
284284

285285
<li>
286286
<p>
287-
<code>rootpath</code>: Denotes the runfiles path that a built binary can
288-
use to find its dependencies at runtime.
289-
</p>
287+
<code>rootpath</code>: Denotes the path that a built binary can use to
288+
find a dependency at runtime relative to the subdirectory of its runfiles
289+
directory corresponding to the main repository.
290+
<strong>Note:</strong> This only works if <a
291+
href="/reference/command-line-reference#flag--enable_runfiles">
292+
<code>--enable_runfiles</code></a> is enabled, which is not the case on
293+
Windows by default. Use <code>rlocationpath</code> instead for
294+
cross-platform support.
290295
<p>
291-
This is the same as <code>execpath</code> but strips the output prefixes
292-
described above. In the above example this means both
296+
This is similar to <code>execpath</code> but strips the configuration
297+
prefixes described above. In the example from above this means both
293298
<code>empty.source</code> and <code>app</code> use pure workspace-relative
294299
paths: <code>testapp/empty.source</code> and <code>testapp/app</code>.
295300
</p>
301+
<p>
302+
The <code>rootpath</code> of a file in an external repository
303+
<code>repo</code> will start with <code>../repo/</code>, followed by the
304+
repository-relative path.
305+
</p>
306+
<p>
307+
This has the same "one output only" requirements as <code>execpath</code>.
308+
</p>
309+
</li>
310+
311+
<li>
312+
<p>
313+
<code>rlocationpath</code>: The path a built binary can pass to the <code>
314+
Rlocation</code> function of a runfiles library to find a dependency at
315+
runtime, either in the runfiles directory (if available) or using the
316+
runfiles manifest.
317+
</p>
318+
<p>
319+
This is similar to <code>rootpath</code> in that it does not contain
320+
configuration prefixes, but differs in that it always starts with the
321+
name of the repository. In the example from above this means that <code>
322+
empty.source</code> and <code>app</code> result in the following
323+
paths: <code>myproject/testapp/empty.source</code> and <code>
324+
myproject/testapp/app</code>.
325+
</p>
326+
<p>
327+
The <code>rlocationpath</code> of a file in an external repository
328+
<code>repo</code> will start with <code>repo/</code>, followed by the
329+
repository-relative path.
330+
</p>
331+
<p>
332+
Passing this path to a binary and resolving it to a file system path using
333+
the runfiles libraries is the preferred approach to find dependencies at
334+
runtime. Compared to <code>rootpath</code>, it has the advantage that it
335+
works on all platforms and even if the runfiles directory is not
336+
available.
337+
</p>
296338
<p>
297339
This has the same "one output only" requirements as <code>execpath</code>.
298340
</p>
@@ -303,24 +345,25 @@ title: Make Variables
303345
<code>rootpath</code>, depending on the attribute being expanded. This is
304346
legacy pre-Starlark behavior and not recommended unless you really know what
305347
it does for a particular rule. See <a
306-
href="https://github.com/bazelbuild/bazel/issues/2475#issuecomment-339318016">#2475</a>
348+
href="https://github.com/bazelbuild/bazel/issues/2475#issuecomment-339318016">#2475</a>
307349
for details.
308350
</li>
309351
</ul>
310352

311353
<p>
312-
<code>execpaths</code>, <code>rootpaths</code>, and <code>locations</code> are
313-
the plural variations of <code>execpath</code>, <code>rootpath</code>, and
314-
<code>location</code>, respectively. They support labels producing multiple
315-
outputs, in which case each output is listed separated by a space. Zero-output
316-
rules and malformed labels produce build errors.
354+
<code>execpaths</code>, <code>rootpaths</code>, <code>rlocationpaths</code>,
355+
and <code>locations</code> are the plural variations of <code>execpath</code>,
356+
<code>rootpath</code>, <code>rlocationpaths</code>, and<code>location</code>,
357+
respectively. They support labels producing multiple outputs, in which case
358+
each output is listed separated by a space. Zero-output rules and malformed
359+
labels produce build errors.
317360
</p>
318361

319362
<p>
320363
All referenced labels must appear in the consuming target's <code>srcs</code>,
321364
output files, or <code>deps</code>. Otherwise the build fails. C++ targets can
322365
also reference labels in <code><a
323-
href="$expander.expandRef("cc_binary.data")">data</a></code>.
366+
href="$expander.expandRef("cc_binary.data")">data</a></code>.
324367
</p>
325368

326369
<p>

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

+83-34
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static java.util.stream.Collectors.joining;
1818

1919
import com.google.common.annotations.VisibleForTesting;
20+
import com.google.common.base.Preconditions;
2021
import com.google.common.base.Supplier;
2122
import com.google.common.base.Suppliers;
2223
import com.google.common.collect.ImmutableCollection;
@@ -26,7 +27,9 @@
2627
import com.google.common.collect.Maps;
2728
import com.google.common.collect.Sets;
2829
import com.google.devtools.build.lib.actions.Artifact;
30+
import com.google.devtools.build.lib.analysis.LocationExpander.LocationFunction.PathType;
2931
import com.google.devtools.build.lib.cmdline.Label;
32+
import com.google.devtools.build.lib.cmdline.LabelConstants;
3033
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
3134
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
3235
import com.google.devtools.build.lib.packages.BuildType;
@@ -63,34 +66,35 @@ public final class LocationExpander {
6366
private static final boolean EXACTLY_ONE = false;
6467
private static final boolean ALLOW_MULTIPLE = true;
6568

66-
private static final boolean USE_LOCATION_PATHS = false;
67-
private static final boolean USE_EXEC_PATHS = true;
68-
6969
private final RuleErrorConsumer ruleErrorConsumer;
7070
private final ImmutableMap<String, LocationFunction> functions;
7171
private final RepositoryMapping repositoryMapping;
72+
private final String workspaceRunfilesDirectory;
7273

7374
@VisibleForTesting
7475
LocationExpander(
7576
RuleErrorConsumer ruleErrorConsumer,
7677
Map<String, LocationFunction> functions,
77-
RepositoryMapping repositoryMapping) {
78+
RepositoryMapping repositoryMapping,
79+
String workspaceRunfilesDirectory) {
7880
this.ruleErrorConsumer = ruleErrorConsumer;
7981
this.functions = ImmutableMap.copyOf(functions);
8082
this.repositoryMapping = repositoryMapping;
83+
this.workspaceRunfilesDirectory = workspaceRunfilesDirectory;
8184
}
8285

8386
private LocationExpander(
84-
RuleErrorConsumer ruleErrorConsumer,
87+
RuleContext ruleContext,
8588
Label root,
8689
Supplier<Map<Label, Collection<Artifact>>> locationMap,
8790
boolean execPaths,
8891
boolean legacyExternalRunfiles,
8992
RepositoryMapping repositoryMapping) {
9093
this(
91-
ruleErrorConsumer,
94+
ruleContext,
9295
allLocationFunctions(root, locationMap, execPaths, legacyExternalRunfiles),
93-
repositoryMapping);
96+
repositoryMapping,
97+
ruleContext.getWorkspaceName());
9498
}
9599

96100
/**
@@ -215,7 +219,10 @@ private String expand(String value, ErrorReporter reporter) {
215219
// (2) Call appropriate function to obtain string replacement.
216220
String functionValue = value.substring(nextWhitespace + 1, end).trim();
217221
try {
218-
String replacement = functions.get(fname).apply(functionValue, repositoryMapping);
222+
String replacement =
223+
functions
224+
.get(fname)
225+
.apply(functionValue, repositoryMapping, workspaceRunfilesDirectory);
219226
result.append(replacement);
220227
} catch (IllegalStateException ise) {
221228
reporter.report(ise.getMessage());
@@ -230,23 +237,29 @@ private String expand(String value, ErrorReporter reporter) {
230237

231238
@VisibleForTesting
232239
static final class LocationFunction {
240+
enum PathType {
241+
LOCATION,
242+
EXEC,
243+
RLOCATION,
244+
}
245+
233246
private static final int MAX_PATHS_SHOWN = 5;
234247

235248
private final Label root;
236249
private final Supplier<Map<Label, Collection<Artifact>>> locationMapSupplier;
237-
private final boolean execPaths;
250+
private final PathType pathType;
238251
private final boolean legacyExternalRunfiles;
239252
private final boolean multiple;
240253

241254
LocationFunction(
242255
Label root,
243256
Supplier<Map<Label, Collection<Artifact>>> locationMapSupplier,
244-
boolean execPaths,
257+
PathType pathType,
245258
boolean legacyExternalRunfiles,
246259
boolean multiple) {
247260
this.root = root;
248261
this.locationMapSupplier = locationMapSupplier;
249-
this.execPaths = execPaths;
262+
this.pathType = Preconditions.checkNotNull(pathType);
250263
this.legacyExternalRunfiles = legacyExternalRunfiles;
251264
this.multiple = multiple;
252265
}
@@ -257,10 +270,13 @@ static final class LocationFunction {
257270
* using the {@code repositoryMapping}.
258271
*
259272
* @param arg The label-like string to be expanded, e.g. ":foo" or "//foo:bar"
260-
* @param repositoryMapping map of {@code RepositoryName}s defined in the main workspace
273+
* @param repositoryMapping map of apparent repository names to {@code RepositoryName}s
274+
* @param workspaceRunfilesDirectory name of the runfiles directory corresponding to the main
275+
* repository
261276
* @return The expanded value
262277
*/
263-
public String apply(String arg, RepositoryMapping repositoryMapping) {
278+
public String apply(
279+
String arg, RepositoryMapping repositoryMapping, String workspaceRunfilesDirectory) {
264280
Label label;
265281
try {
266282
label = root.getRelativeWithRemapping(arg, repositoryMapping);
@@ -269,14 +285,13 @@ public String apply(String arg, RepositoryMapping repositoryMapping) {
269285
String.format(
270286
"invalid label in %s expression: %s", functionName(), e.getMessage()), e);
271287
}
272-
Collection<String> paths = resolveLabel(label);
288+
Set<String> paths = resolveLabel(label, workspaceRunfilesDirectory);
273289
return joinPaths(paths);
274290
}
275291

276-
/**
277-
* Returns all target location(s) of the given label.
278-
*/
279-
private Collection<String> resolveLabel(Label unresolved) throws IllegalStateException {
292+
/** Returns all target location(s) of the given label. */
293+
private Set<String> resolveLabel(Label unresolved, String workspaceRunfilesDirectory)
294+
throws IllegalStateException {
280295
Collection<Artifact> artifacts = locationMapSupplier.get().get(unresolved);
281296

282297
if (artifacts == null) {
@@ -286,7 +301,7 @@ private Collection<String> resolveLabel(Label unresolved) throws IllegalStateExc
286301
unresolved, functionName()));
287302
}
288303

289-
Set<String> paths = getPaths(artifacts);
304+
Set<String> paths = getPaths(artifacts, workspaceRunfilesDirectory);
290305
if (paths.isEmpty()) {
291306
throw new IllegalStateException(
292307
String.format(
@@ -311,24 +326,41 @@ private Collection<String> resolveLabel(Label unresolved) throws IllegalStateExc
311326
* Extracts list of all executables associated with given collection of label artifacts.
312327
*
313328
* @param artifacts to get the paths of
329+
* @param workspaceRunfilesDirectory name of the runfiles directory corresponding to the main
330+
* repository
314331
* @return all associated executable paths
315332
*/
316-
private Set<String> getPaths(Collection<Artifact> artifacts) {
333+
private Set<String> getPaths(
334+
Collection<Artifact> artifacts, String workspaceRunfilesDirectory) {
317335
TreeSet<String> paths = Sets.newTreeSet();
318336
for (Artifact artifact : artifacts) {
319-
PathFragment execPath =
320-
execPaths
321-
? artifact.getExecPath()
322-
: legacyExternalRunfiles
323-
? artifact.getPathForLocationExpansion()
324-
: artifact.getRunfilesPath();
325-
if (execPath != null) { // omit middlemen etc
326-
paths.add(execPath.getCallablePathString());
337+
PathFragment path = getPath(artifact, workspaceRunfilesDirectory);
338+
if (path != null) { // omit middlemen etc
339+
paths.add(path.getCallablePathString());
327340
}
328341
}
329342
return paths;
330343
}
331344

345+
private PathFragment getPath(Artifact artifact, String workspaceRunfilesDirectory) {
346+
switch (pathType) {
347+
case LOCATION:
348+
return legacyExternalRunfiles
349+
? artifact.getPathForLocationExpansion()
350+
: artifact.getRunfilesPath();
351+
case EXEC:
352+
return artifact.getExecPath();
353+
case RLOCATION:
354+
PathFragment runfilesPath = artifact.getRunfilesPath();
355+
if (runfilesPath.startsWith(LabelConstants.EXTERNAL_RUNFILES_PATH_PREFIX)) {
356+
return runfilesPath.relativeTo(LabelConstants.EXTERNAL_RUNFILES_PATH_PREFIX);
357+
} else {
358+
return PathFragment.create(workspaceRunfilesDirectory).getRelative(runfilesPath);
359+
}
360+
}
361+
throw new IllegalStateException("Unexpected PathType: " + pathType);
362+
}
363+
332364
private String joinPaths(Collection<String> paths) {
333365
return paths.stream().map(ShellEscaper::escapeString).collect(joining(" "));
334366
}
@@ -346,27 +378,44 @@ static ImmutableMap<String, LocationFunction> allLocationFunctions(
346378
return new ImmutableMap.Builder<String, LocationFunction>()
347379
.put(
348380
"location",
349-
new LocationFunction(root, locationMap, execPaths, legacyExternalRunfiles, EXACTLY_ONE))
381+
new LocationFunction(
382+
root,
383+
locationMap,
384+
execPaths ? PathType.EXEC : PathType.LOCATION,
385+
legacyExternalRunfiles,
386+
EXACTLY_ONE))
350387
.put(
351388
"locations",
352389
new LocationFunction(
353-
root, locationMap, execPaths, legacyExternalRunfiles, ALLOW_MULTIPLE))
390+
root,
391+
locationMap,
392+
execPaths ? PathType.EXEC : PathType.LOCATION,
393+
legacyExternalRunfiles,
394+
ALLOW_MULTIPLE))
354395
.put(
355396
"rootpath",
356397
new LocationFunction(
357-
root, locationMap, USE_LOCATION_PATHS, legacyExternalRunfiles, EXACTLY_ONE))
398+
root, locationMap, PathType.LOCATION, legacyExternalRunfiles, EXACTLY_ONE))
358399
.put(
359400
"rootpaths",
360401
new LocationFunction(
361-
root, locationMap, USE_LOCATION_PATHS, legacyExternalRunfiles, ALLOW_MULTIPLE))
402+
root, locationMap, PathType.LOCATION, legacyExternalRunfiles, ALLOW_MULTIPLE))
362403
.put(
363404
"execpath",
364405
new LocationFunction(
365-
root, locationMap, USE_EXEC_PATHS, legacyExternalRunfiles, EXACTLY_ONE))
406+
root, locationMap, PathType.EXEC, legacyExternalRunfiles, EXACTLY_ONE))
366407
.put(
367408
"execpaths",
368409
new LocationFunction(
369-
root, locationMap, USE_EXEC_PATHS, legacyExternalRunfiles, ALLOW_MULTIPLE))
410+
root, locationMap, PathType.EXEC, legacyExternalRunfiles, ALLOW_MULTIPLE))
411+
.put(
412+
"rlocationpath",
413+
new LocationFunction(
414+
root, locationMap, PathType.RLOCATION, legacyExternalRunfiles, EXACTLY_ONE))
415+
.put(
416+
"rlocationpaths",
417+
new LocationFunction(
418+
root, locationMap, PathType.RLOCATION, legacyExternalRunfiles, ALLOW_MULTIPLE))
370419
.build();
371420
}
372421

0 commit comments

Comments
 (0)