Skip to content

Commit 9d3a8b0

Browse files
fmeumcopybara-github
authored andcommitted
Stringify Labels in display form in Args
`Label`s added to `Args` are now formatted in display form, which means that they will be repo-relative if referring to the main repo and use an apparent repository name if possible. Previously, labels were formatted in repo-relative form if referring to the main repo and using canonical repository names exclusively for external repos. At the same time, `Args` docs explicitly stated that the exact stringification of any type other than `File` is unspecified. The previous behavior was problematic since it neither always used canonical labels (which could be useful for writing tests that check these labels against an allowlist) nor provided labels that could be included in BUILD files (canonical names are explicitly unstable). Furthermore, whether a label resulted in a string prefixed with a single or two `@`s already dependended on a user choice, namely the value of `--enable_bzlmod`. Thus, this change is not considered to be breaking. It makes it so that existing rulesets relying on default stringification to return a BUILD-file compatible label in WORKSPACE continue to do so with Bzlmod with no code changes. This change aims to be as memory efficient as possible: * Single labels or sequences of labels that reference targets in the main repo incur no memory overhead. * Labels referring to external repos as well as `NestedSet`s of labels result in an additional Skyframe dependency for each target using the command line as well as one additional reference (4 bytes) stored in the command line's `arguments`. Work towards #20486 Closes #21702. PiperOrigin-RevId: 620925978 Change-Id: I54aa807c41bf783aee223482d2309f5cee2726b5
1 parent 852273f commit 9d3a8b0

18 files changed

+450
-75
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.devtools.build.lib.actions.ArtifactRoot;
2626
import com.google.devtools.build.lib.actions.MiddlemanFactory;
2727
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
28+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
2829
import com.google.devtools.build.lib.events.ExtendedEventHandler;
2930
import com.google.devtools.build.lib.vfs.PathFragment;
3031
import com.google.devtools.build.skyframe.SkyFunction;
@@ -166,4 +167,9 @@ Artifact.DerivedArtifact getDerivedArtifact(
166167
ImmutableSet<Artifact> getTreeArtifactsConflictingWithFiles();
167168

168169
ActionKeyContext getActionKeyContext();
170+
171+
/**
172+
* Returns and registers a Skyframe dependency on the {@link RepositoryMapping} of the main repo.
173+
*/
174+
RepositoryMapping getMainRepoMapping() throws InterruptedException;
169175
}

src/main/java/com/google/devtools/build/lib/analysis/BUILD

+4
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ java_library(
411411
"//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_and_data",
412412
"//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_key",
413413
"//src/main/java/com/google/devtools/build/lib/skyframe:configured_value_creation_exception",
414+
"//src/main/java/com/google/devtools/build/lib/skyframe:repository_mapping_value",
414415
"//src/main/java/com/google/devtools/build/lib/skyframe:starlark_builtins_value",
415416
"//src/main/java/com/google/devtools/build/lib/skyframe:workspace_status_value",
416417
"//src/main/java/com/google/devtools/build/lib/skyframe/config",
@@ -421,6 +422,7 @@ java_library(
421422
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/core",
422423
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform",
423424
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/test",
425+
"//src/main/java/com/google/devtools/build/lib/supplier",
424426
"//src/main/java/com/google/devtools/build/lib/unsafe:string",
425427
"//src/main/java/com/google/devtools/build/lib/util",
426428
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
@@ -2450,9 +2452,11 @@ java_library(
24502452
"//src/main/java/com/google/devtools/build/lib/actions",
24512453
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
24522454
"//src/main/java/com/google/devtools/build/lib/actions:commandline_item",
2455+
"//src/main/java/com/google/devtools/build/lib/cmdline",
24532456
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
24542457
"//src/main/java/com/google/devtools/build/lib/concurrent",
24552458
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi",
2459+
"//src/main/java/com/google/devtools/build/lib/supplier",
24562460
"//src/main/java/net/starlark/java/eval",
24572461
"//src/main/java/net/starlark/java/syntax",
24582462
"//third_party:guava",

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

+16
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@
2727
import com.google.devtools.build.lib.actions.ArtifactFactory;
2828
import com.google.devtools.build.lib.actions.ArtifactRoot;
2929
import com.google.devtools.build.lib.actions.MiddlemanFactory;
30+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
31+
import com.google.devtools.build.lib.cmdline.RepositoryName;
3032
import com.google.devtools.build.lib.events.ExtendedEventHandler;
3133
import com.google.devtools.build.lib.events.StoredEventHandler;
3234
import com.google.devtools.build.lib.packages.Target;
35+
import com.google.devtools.build.lib.skyframe.RepositoryMappingValue;
3336
import com.google.devtools.build.lib.skyframe.StarlarkBuiltinsValue;
3437
import com.google.devtools.build.lib.skyframe.WorkspaceStatusValue;
3538
import com.google.devtools.build.lib.util.Pair;
@@ -368,6 +371,19 @@ private WorkspaceStatusValue getWorkspaceStatusValue() throws InterruptedExcepti
368371
return workspaceStatusValue;
369372
}
370373

374+
@Override
375+
public RepositoryMapping getMainRepoMapping() throws InterruptedException {
376+
var mainRepoMapping =
377+
(RepositoryMappingValue)
378+
skyframeEnv.getValue(RepositoryMappingValue.key(RepositoryName.MAIN));
379+
if (mainRepoMapping == null) {
380+
// This isn't expected to happen since the main repository mapping is computed before the
381+
// analysis phase.
382+
throw new MissingDepException("Restart due to missing main repository mapping");
383+
}
384+
return mainRepoMapping.getRepositoryMapping();
385+
}
386+
371387
@Override
372388
public ActionLookupKey getOwner() {
373389
return owner;

src/main/java/com/google/devtools/build/lib/analysis/starlark/Args.java

+42-17
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
import com.google.devtools.build.lib.actions.ParamFileInfo;
2424
import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
2525
import com.google.devtools.build.lib.actions.SingleStringArgFormatter;
26+
import com.google.devtools.build.lib.cmdline.Label;
27+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
2628
import com.google.devtools.build.lib.collect.nestedset.Depset;
2729
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
2830
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
2931
import com.google.devtools.build.lib.starlarkbuildapi.CommandLineArgsApi;
32+
import com.google.devtools.build.lib.supplier.InterruptibleSupplier;
3033
import com.google.errorprone.annotations.CanIgnoreReturnValue;
3134
import java.nio.charset.StandardCharsets;
3235
import java.util.ArrayList;
@@ -70,7 +73,8 @@ public void repr(Printer printer) {
7073
@Override
7174
public void debugPrint(Printer printer, StarlarkSemantics semantics) {
7275
try {
73-
printer.append(Joiner.on(" ").join(build().arguments()));
76+
printer.append(
77+
Joiner.on(" ").join(build(/* mainRepoMappingSupplier= */ () -> null).arguments()));
7478
} catch (CommandLineExpansionException e) {
7579
printer.append("Cannot expand command line: " + e.getMessage());
7680
} catch (InterruptedException e) {
@@ -102,7 +106,8 @@ public void debugPrint(Printer printer, StarlarkSemantics semantics) {
102106
public abstract ImmutableSet<Artifact> getDirectoryArtifacts();
103107

104108
/** Returns the command line built by this {@link Args} object. */
105-
public abstract CommandLine build();
109+
public abstract CommandLine build(
110+
InterruptibleSupplier<RepositoryMapping> mainRepoMappingSupplier) throws InterruptedException;
106111

107112
/**
108113
* Returns a frozen {@link Args} representation corresponding to an already-registered action.
@@ -157,7 +162,7 @@ public ImmutableSet<Artifact> getDirectoryArtifacts() {
157162
}
158163

159164
@Override
160-
public CommandLine build() {
165+
public CommandLine build(InterruptibleSupplier<RepositoryMapping> mainRepoMappingSupplier) {
161166
return commandLine;
162167
}
163168

@@ -259,6 +264,12 @@ private static class MutableArgs extends Args implements StarlarkValue, Mutabili
259264
*/
260265
private boolean flagPerLine = false;
261266

267+
/**
268+
* True if the command line needs to stringify any {@link Label}s without an explicit 'map_each'
269+
* function.
270+
*/
271+
private boolean mayStringifyExternalLabel = false;
272+
262273
// May be set explicitly once -- if unset defaults to ParameterFileType.SHELL_QUOTED.
263274
private ParameterFileType parameterFileType = null;
264275
private String flagFormatString;
@@ -307,6 +318,9 @@ public CommandLineArgsApi addArgument(
307318
"Args.add() doesn't accept vectorized arguments. Please use Args.add_all() or"
308319
+ " Args.add_joined() instead.");
309320
}
321+
if (value instanceof Label label && !label.getRepository().isMain()) {
322+
mayStringifyExternalLabel = true;
323+
}
310324
addSingleArg(value, format != Starlark.NONE ? (String) format : null);
311325
return this;
312326
}
@@ -436,8 +450,12 @@ private void addVectorArg(
436450
validateFormatString("format_each", formatEach);
437451
validateFormatString("format_joined", formatJoined);
438452
StarlarkCustomCommandLine.VectorArg.Builder vectorArg;
439-
if (value instanceof Depset) {
440-
Depset starlarkNestedSet = (Depset) value;
453+
if (value instanceof Depset starlarkNestedSet) {
454+
if (mapEach == null && Label.class.equals(starlarkNestedSet.getElementClass())) {
455+
// We don't want to eagerly check whether all labels reference targets in the main repo,
456+
// so just assume they might not. Nested sets of labels should be rare.
457+
mayStringifyExternalLabel = true;
458+
}
441459
NestedSet<?> nestedSet = starlarkNestedSet.getSet();
442460
if (nestedSet.isEmpty() && omitIfEmpty) {
443461
return;
@@ -451,8 +469,16 @@ private void addVectorArg(
451469
if (starlarkList.isEmpty() && omitIfEmpty) {
452470
return;
453471
}
454-
if (expandDirectories) {
455-
scanForDirectories(starlarkList);
472+
for (Object object : starlarkList) {
473+
if (expandDirectories && isDirectory(object)) {
474+
directoryArtifacts.add((Artifact) object);
475+
}
476+
// Labels referencing targets in the main repo are stringified as //pkg:name and thus
477+
// don't require a RepositoryMapping. If a map_each function is provided, default
478+
// stringification via Label#toString() is not used.
479+
if (mapEach == null && object instanceof Label label && !label.getRepository().isMain()) {
480+
mayStringifyExternalLabel = true;
481+
}
456482
}
457483
vectorArg = new StarlarkCustomCommandLine.VectorArg.Builder(starlarkList);
458484
}
@@ -573,8 +599,10 @@ private MutableArgs(@Nullable Mutability mutability, StarlarkSemantics starlarkS
573599
}
574600

575601
@Override
576-
public CommandLine build() {
577-
return commandLine.build(flagPerLine);
602+
public CommandLine build(InterruptibleSupplier<RepositoryMapping> mainRepoMappingSupplier)
603+
throws InterruptedException {
604+
return commandLine.build(
605+
flagPerLine, mayStringifyExternalLabel ? mainRepoMappingSupplier.get() : null);
578606
}
579607

580608
@Override
@@ -585,18 +613,15 @@ public Mutability mutability() {
585613
@Override
586614
public ImmutableSet<Artifact> getDirectoryArtifacts() {
587615
for (NestedSet<?> collection : potentialDirectoryArtifacts) {
588-
scanForDirectories(collection.toList());
616+
for (Object object : collection.toList()) {
617+
if (isDirectory(object)) {
618+
directoryArtifacts.add((Artifact) object);
619+
}
620+
}
589621
}
590622
potentialDirectoryArtifacts.clear();
591623
return ImmutableSet.copyOf(directoryArtifacts);
592624
}
593625

594-
private void scanForDirectories(Iterable<?> objects) {
595-
for (Object object : objects) {
596-
if (isDirectory(object)) {
597-
directoryArtifacts.add((Artifact) object);
598-
}
599-
}
600-
}
601626
}
602627
}

src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkActionFactory.java

+19-9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
5252
import com.google.devtools.build.lib.cmdline.Label;
5353
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
54+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
5455
import com.google.devtools.build.lib.collect.nestedset.Depset;
5556
import com.google.devtools.build.lib.collect.nestedset.Depset.TypeException;
5657
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -68,6 +69,7 @@
6869
import com.google.devtools.build.lib.starlarkbuildapi.FileApi;
6970
import com.google.devtools.build.lib.starlarkbuildapi.StarlarkActionFactoryApi;
7071
import com.google.devtools.build.lib.starlarkbuildapi.TemplateDictApi;
72+
import com.google.devtools.build.lib.supplier.InterruptibleSupplier;
7173
import com.google.devtools.build.lib.util.OS;
7274
import com.google.devtools.build.lib.vfs.PathFragment;
7375
import com.google.protobuf.GeneratedMessage;
@@ -332,7 +334,8 @@ public void symlink(
332334
}
333335

334336
@Override
335-
public void write(FileApi output, Object content, Boolean isExecutable) throws EvalException {
337+
public void write(FileApi output, Object content, Boolean isExecutable)
338+
throws EvalException, InterruptedException {
336339
context.checkMutable("actions.write");
337340
RuleContext ruleContext = getRuleContext();
338341

@@ -347,7 +350,7 @@ public void write(FileApi output, Object content, Boolean isExecutable) throws E
347350
ruleContext.getActionOwner(),
348351
NestedSetBuilder.wrap(Order.STABLE_ORDER, args.getDirectoryArtifacts()),
349352
(Artifact) output,
350-
args.build(),
353+
args.build(getMainRepoMappingSupplier()),
351354
args.getParameterFileType());
352355
} else {
353356
throw new AssertionError("Unexpected type: " + content.getClass().getSimpleName());
@@ -373,7 +376,7 @@ public void run(
373376
Object shadowedActionUnchecked,
374377
Object resourceSetUnchecked,
375378
Object toolchainUnchecked)
376-
throws EvalException {
379+
throws EvalException, InterruptedException {
377380
context.checkMutable("actions.run");
378381
execGroupUnchecked = context.maybeOverrideExecGroup(execGroupUnchecked);
379382
toolchainUnchecked = context.maybeOverrideToolchain(toolchainUnchecked);
@@ -382,7 +385,7 @@ public void run(
382385
boolean useAutoExecGroups = ruleContext.useAutoExecGroups();
383386

384387
StarlarkAction.Builder builder = new StarlarkAction.Builder();
385-
buildCommandLine(builder, arguments);
388+
buildCommandLine(builder, arguments, getMainRepoMappingSupplier());
386389
if (executableUnchecked instanceof Artifact) {
387390
Artifact executable = (Artifact) executableUnchecked;
388391
FilesToRunProvider provider = context.getExecutableRunfiles(executable, "executable");
@@ -560,15 +563,15 @@ public void runShell(
560563
Object shadowedActionUnchecked,
561564
Object resourceSetUnchecked,
562565
Object toolchainUnchecked)
563-
throws EvalException {
566+
throws EvalException, InterruptedException {
564567
context.checkMutable("actions.run_shell");
565568
execGroupUnchecked = context.maybeOverrideExecGroup(execGroupUnchecked);
566569
toolchainUnchecked = context.maybeOverrideToolchain(toolchainUnchecked);
567570

568571
RuleContext ruleContext = getRuleContext();
569572

570573
StarlarkAction.Builder builder = new StarlarkAction.Builder();
571-
buildCommandLine(builder, arguments);
574+
buildCommandLine(builder, arguments, getMainRepoMappingSupplier());
572575

573576
// When we use a shell command, add an empty argument before other arguments.
574577
// e.g. bash -c "cmd" '' 'arg1' 'arg2'
@@ -631,8 +634,11 @@ public void runShell(
631634
builder);
632635
}
633636

634-
private static void buildCommandLine(SpawnAction.Builder builder, Sequence<?> argumentsList)
635-
throws EvalException {
637+
private static void buildCommandLine(
638+
SpawnAction.Builder builder,
639+
Sequence<?> argumentsList,
640+
InterruptibleSupplier<RepositoryMapping> repoMappingSupplier)
641+
throws EvalException, InterruptedException {
636642
ImmutableList.Builder<String> stringArgs = null;
637643
for (Object value : argumentsList) {
638644
if (value instanceof String) {
@@ -647,7 +653,7 @@ private static void buildCommandLine(SpawnAction.Builder builder, Sequence<?> ar
647653
}
648654
Args args = (Args) value;
649655
ParamFileInfo paramFileInfo = args.getParamFileInfo();
650-
builder.addCommandLine(args.build(), paramFileInfo);
656+
builder.addCommandLine(args.build(repoMappingSupplier), paramFileInfo);
651657
} else {
652658
throw Starlark.errorf(
653659
"expected list of strings or ctx.actions.args() for arguments instead of %s",
@@ -1046,6 +1052,10 @@ public void repr(Printer printer) {
10461052
context.repr(printer);
10471053
}
10481054

1055+
private InterruptibleSupplier<RepositoryMapping> getMainRepoMappingSupplier() {
1056+
return context.getRuleContext().getAnalysisEnvironment()::getMainRepoMapping;
1057+
}
1058+
10491059
/** The analysis context for {@code Starlark} actions */
10501060
// For now, this contains methods necessary for SubruleContext to begin using
10511061
// StarlarkActionFactory without any invasive changes to the latter. It will be improved once the

0 commit comments

Comments
 (0)