Skip to content

Commit 58a2922

Browse files
committed
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 bazelbuild#20486 Closes bazelbuild#21702. PiperOrigin-RevId: 620925978 Change-Id: I54aa807c41bf783aee223482d2309f5cee2726b5
1 parent c580d62 commit 58a2922

18 files changed

+415
-55
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.devtools.build.lib.actions.MiddlemanFactory;
2727
import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoKey;
2828
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
29+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
2930
import com.google.devtools.build.lib.events.ExtendedEventHandler;
3031
import com.google.devtools.build.lib.vfs.PathFragment;
3132
import com.google.devtools.build.skyframe.SkyFunction;
@@ -176,4 +177,9 @@ ImmutableList<Artifact> getBuildInfo(
176177
ImmutableSet<Artifact> getTreeArtifactsConflictingWithFiles();
177178

178179
ActionKeyContext getActionKeyContext();
180+
181+
/**
182+
* Returns and registers a Skyframe dependency on the {@link RepositoryMapping} of the main repo.
183+
*/
184+
RepositoryMapping getMainRepoMapping() throws InterruptedException;
179185
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ java_library(
414414
"//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_and_data",
415415
"//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_key",
416416
"//src/main/java/com/google/devtools/build/lib/skyframe:configured_value_creation_exception",
417+
"//src/main/java/com/google/devtools/build/lib/skyframe:repository_mapping_value",
417418
"//src/main/java/com/google/devtools/build/lib/skyframe:starlark_builtins_value",
418419
"//src/main/java/com/google/devtools/build/lib/skyframe:workspace_status_value",
419420
"//src/main/java/com/google/devtools/build/lib/skyframe/config",
@@ -424,6 +425,7 @@ java_library(
424425
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/core",
425426
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform",
426427
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/test",
428+
"//src/main/java/com/google/devtools/build/lib/supplier",
427429
"//src/main/java/com/google/devtools/build/lib/unsafe:string",
428430
"//src/main/java/com/google/devtools/build/lib/util",
429431
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
@@ -2420,9 +2422,11 @@ java_library(
24202422
"//src/main/java/com/google/devtools/build/lib/actions",
24212423
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
24222424
"//src/main/java/com/google/devtools/build/lib/actions:commandline_item",
2425+
"//src/main/java/com/google/devtools/build/lib/cmdline",
24232426
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
24242427
"//src/main/java/com/google/devtools/build/lib/concurrent",
24252428
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi",
2429+
"//src/main/java/com/google/devtools/build/lib/supplier",
24262430
"//src/main/java/net/starlark/java/eval",
24272431
"//src/main/java/net/starlark/java/syntax",
24282432
"//third_party:guava",

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

+16
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@
3030
import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoCollection;
3131
import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoKey;
3232
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
33+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
34+
import com.google.devtools.build.lib.cmdline.RepositoryName;
3335
import com.google.devtools.build.lib.events.ExtendedEventHandler;
3436
import com.google.devtools.build.lib.events.StoredEventHandler;
3537
import com.google.devtools.build.lib.packages.Target;
3638
import com.google.devtools.build.lib.skyframe.BuildInfoCollectionValue;
39+
import com.google.devtools.build.lib.skyframe.RepositoryMappingValue;
3740
import com.google.devtools.build.lib.skyframe.StarlarkBuiltinsValue;
3841
import com.google.devtools.build.lib.skyframe.WorkspaceStatusValue;
3942
import com.google.devtools.build.lib.util.Pair;
@@ -385,6 +388,19 @@ public ImmutableList<Artifact> getBuildInfo(
385388
return stamp ? collection.getStampedBuildInfo() : collection.getRedactedBuildInfo();
386389
}
387390

391+
@Override
392+
public RepositoryMapping getMainRepoMapping() throws InterruptedException {
393+
var mainRepoMapping =
394+
(RepositoryMappingValue)
395+
skyframeEnv.getValue(RepositoryMappingValue.key(RepositoryName.MAIN));
396+
if (mainRepoMapping == null) {
397+
// This isn't expected to happen since the main repository mapping is computed before the
398+
// analysis phase.
399+
throw new MissingDepException("Restart due to missing main repository mapping");
400+
}
401+
return mainRepoMapping.getRepositoryMapping();
402+
}
403+
388404
@Override
389405
public ActionLookupKey getOwner() {
390406
return owner;

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

+42-17
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
2525
import com.google.devtools.build.lib.actions.SingleStringArgFormatter;
2626
import com.google.devtools.build.lib.analysis.starlark.StarlarkCustomCommandLine.ScalarArg;
27+
import com.google.devtools.build.lib.cmdline.Label;
28+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
2729
import com.google.devtools.build.lib.collect.nestedset.Depset;
2830
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
2931
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
3032
import com.google.devtools.build.lib.starlarkbuildapi.CommandLineArgsApi;
33+
import com.google.devtools.build.lib.supplier.InterruptibleSupplier;
3134
import com.google.errorprone.annotations.CanIgnoreReturnValue;
3235
import java.nio.charset.StandardCharsets;
3336
import java.util.ArrayList;
@@ -71,7 +74,8 @@ public void repr(Printer printer) {
7174
@Override
7275
public void debugPrint(Printer printer, StarlarkSemantics semantics) {
7376
try {
74-
printer.append(Joiner.on(" ").join(build().arguments()));
77+
printer.append(
78+
Joiner.on(" ").join(build(/* mainRepoMappingSupplier= */ () -> null).arguments()));
7579
} catch (CommandLineExpansionException e) {
7680
printer.append("Cannot expand command line: " + e.getMessage());
7781
} catch (InterruptedException e) {
@@ -103,7 +107,8 @@ public void debugPrint(Printer printer, StarlarkSemantics semantics) {
103107
public abstract ImmutableSet<Artifact> getDirectoryArtifacts();
104108

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

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

160165
@Override
161-
public CommandLine build() {
166+
public CommandLine build(InterruptibleSupplier<RepositoryMapping> mainRepoMappingSupplier) {
162167
return commandLine;
163168
}
164169

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

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

577603
@Override
578-
public CommandLine build() {
579-
return commandLine.build(flagPerLine);
604+
public CommandLine build(InterruptibleSupplier<RepositoryMapping> mainRepoMappingSupplier)
605+
throws InterruptedException {
606+
return commandLine.build(
607+
flagPerLine, mayStringifyExternalLabel ? mainRepoMappingSupplier.get() : null);
580608
}
581609

582610
@Override
@@ -587,18 +615,15 @@ public Mutability mutability() {
587615
@Override
588616
public ImmutableSet<Artifact> getDirectoryArtifacts() {
589617
for (NestedSet<?> collection : potentialDirectoryArtifacts) {
590-
scanForDirectories(collection.toList());
618+
for (Object object : collection.toList()) {
619+
if (isDirectory(object)) {
620+
directoryArtifacts.add((Artifact) object);
621+
}
622+
}
591623
}
592624
potentialDirectoryArtifacts.clear();
593625
return ImmutableSet.copyOf(directoryArtifacts);
594626
}
595627

596-
private void scanForDirectories(Iterable<?> objects) {
597-
for (Object object : objects) {
598-
if (isDirectory(object)) {
599-
directoryArtifacts.add((Artifact) object);
600-
}
601-
}
602-
}
603628
}
604629
}

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

+19-9
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
5656
import com.google.devtools.build.lib.cmdline.Label;
5757
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
58+
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
5859
import com.google.devtools.build.lib.collect.nestedset.Depset;
5960
import com.google.devtools.build.lib.collect.nestedset.Depset.TypeException;
6061
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -72,6 +73,7 @@
7273
import com.google.devtools.build.lib.starlarkbuildapi.FileApi;
7374
import com.google.devtools.build.lib.starlarkbuildapi.StarlarkActionFactoryApi;
7475
import com.google.devtools.build.lib.starlarkbuildapi.TemplateDictApi;
76+
import com.google.devtools.build.lib.supplier.InterruptibleSupplier;
7577
import com.google.devtools.build.lib.util.OS;
7678
import com.google.devtools.build.lib.vfs.PathFragment;
7779
import com.google.protobuf.GeneratedMessage;
@@ -362,7 +364,8 @@ public void symlink(
362364
}
363365

364366
@Override
365-
public void write(FileApi output, Object content, Boolean isExecutable) throws EvalException {
367+
public void write(FileApi output, Object content, Boolean isExecutable)
368+
throws EvalException, InterruptedException {
366369
context.checkMutable("actions.write");
367370
RuleContext ruleContext = getRuleContext();
368371

@@ -377,7 +380,7 @@ public void write(FileApi output, Object content, Boolean isExecutable) throws E
377380
ruleContext.getActionOwner(),
378381
NestedSetBuilder.wrap(Order.STABLE_ORDER, args.getDirectoryArtifacts()),
379382
(Artifact) output,
380-
args.build(),
383+
args.build(getMainRepoMappingSupplier()),
381384
args.getParameterFileType());
382385
} else {
383386
throw new AssertionError("Unexpected type: " + content.getClass().getSimpleName());
@@ -403,7 +406,7 @@ public void run(
403406
Object shadowedActionUnchecked,
404407
Object resourceSetUnchecked,
405408
Object toolchainUnchecked)
406-
throws EvalException {
409+
throws EvalException, InterruptedException {
407410
context.checkMutable("actions.run");
408411
execGroupUnchecked = context.maybeOverrideExecGroup(execGroupUnchecked);
409412
toolchainUnchecked = context.maybeOverrideToolchain(toolchainUnchecked);
@@ -412,7 +415,7 @@ public void run(
412415
boolean useAutoExecGroups = ruleContext.useAutoExecGroups();
413416

414417
StarlarkAction.Builder builder = new StarlarkAction.Builder();
415-
buildCommandLine(builder, arguments);
418+
buildCommandLine(builder, arguments, getMainRepoMappingSupplier());
416419
if (executableUnchecked instanceof Artifact) {
417420
Artifact executable = (Artifact) executableUnchecked;
418421
FilesToRunProvider provider = context.getExecutableRunfiles(executable, "executable");
@@ -599,15 +602,15 @@ public void runShell(
599602
Object shadowedActionUnchecked,
600603
Object resourceSetUnchecked,
601604
Object toolchainUnchecked)
602-
throws EvalException {
605+
throws EvalException, InterruptedException {
603606
context.checkMutable("actions.run_shell");
604607
execGroupUnchecked = context.maybeOverrideExecGroup(execGroupUnchecked);
605608
toolchainUnchecked = context.maybeOverrideToolchain(toolchainUnchecked);
606609

607610
RuleContext ruleContext = getRuleContext();
608611

609612
StarlarkAction.Builder builder = new StarlarkAction.Builder();
610-
buildCommandLine(builder, arguments);
613+
buildCommandLine(builder, arguments, getMainRepoMappingSupplier());
611614

612615
// When we use a shell command, add an empty argument before other arguments.
613616
// e.g. bash -c "cmd" '' 'arg1' 'arg2'
@@ -671,8 +674,11 @@ public void runShell(
671674
builder);
672675
}
673676

674-
private static void buildCommandLine(SpawnAction.Builder builder, Sequence<?> argumentsList)
675-
throws EvalException {
677+
private static void buildCommandLine(
678+
SpawnAction.Builder builder,
679+
Sequence<?> argumentsList,
680+
InterruptibleSupplier<RepositoryMapping> repoMappingSupplier)
681+
throws EvalException, InterruptedException {
676682
List<String> stringArgs = new ArrayList<>();
677683
for (Object value : argumentsList) {
678684
if (value instanceof String) {
@@ -684,7 +690,7 @@ private static void buildCommandLine(SpawnAction.Builder builder, Sequence<?> ar
684690
}
685691
Args args = (Args) value;
686692
ParamFileInfo paramFileInfo = args.getParamFileInfo();
687-
builder.addCommandLine(args.build(), paramFileInfo);
693+
builder.addCommandLine(args.build(repoMappingSupplier), paramFileInfo);
688694
} else {
689695
throw Starlark.errorf(
690696
"expected list of strings or ctx.actions.args() for arguments instead of %s",
@@ -1094,6 +1100,10 @@ public void repr(Printer printer) {
10941100
context.repr(printer);
10951101
}
10961102

1103+
private InterruptibleSupplier<RepositoryMapping> getMainRepoMappingSupplier() {
1104+
return context.getRuleContext().getAnalysisEnvironment()::getMainRepoMapping;
1105+
}
1106+
10971107
/** The analysis context for {@code Starlark} actions */
10981108
// For now, this contains methods necessary for SubruleContext to begin using
10991109
// StarlarkActionFactory without any invasive changes to the latter. It will be improved once the

0 commit comments

Comments
 (0)