Skip to content

Commit 02853f8

Browse files
haxorzcopybara-github
authored andcommitted
Fix non-determinism in the FailureDetail produced for a package with multiple label crosses subpackage boundary errors.
While I'm here, also make the code a tiny bit simpler: * Explicitly create the empty inner `ArrayList` on each loop iteration. There's no need to use `#computeIfAbsent`; we know no other loop iteration will be processing the same `Target`. * Use a `List<Pair<Target, List<PackageLookupValue.Key>>>` instead of a `Map<Target, List<PackageLookupValue.Key>>` since we don't need lookup semantics. PiperOrigin-RevId: 533585562 Change-Id: Iaa3c3d9302a0eef533b55d1f17c33de3ec23666b
1 parent 2b104c8 commit 02853f8

File tree

3 files changed

+28
-10
lines changed

3 files changed

+28
-10
lines changed

src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -805,14 +805,16 @@ private static void handleLabelsCrossingSubpackagesAndPropagateInconsistentFiles
805805
PathFragment pkgDir = pkgId.getPackageFragment();
806806
// Contains a key for each package whose label that might have a presence of a subpackage.
807807
// Values are all potential subpackages of the label.
808-
Map<Target, List<PackageLookupValue.Key>> targetSubpackagePackageLookupKeyMap = new HashMap<>();
808+
List<Pair<Target, List<PackageLookupValue.Key>>> targetsAndSubpackagePackageLookupKeys =
809+
new ArrayList<>();
809810
Set<PackageLookupValue.Key> allPackageLookupKeys = new HashSet<>();
810811
for (Target target : pkgBuilder.getTargets()) {
811812
Label label = target.getLabel();
812813
PathFragment dir = Label.getContainingDirectory(label);
813814
if (dir.equals(pkgDir)) {
814815
continue;
815816
}
817+
List<PackageLookupValue.Key> subpackagePackageLookupKeys = new ArrayList<>();
816818
String labelName = label.getName();
817819
PathFragment labelAsRelativePath = PathFragment.create(labelName).getParentDirectory();
818820
PathFragment subpackagePath = pkgDir;
@@ -821,14 +823,13 @@ private static void handleLabelsCrossingSubpackagesAndPropagateInconsistentFiles
821823
subpackagePath = subpackagePath.getRelative(segment);
822824
PackageLookupValue.Key currentPackageLookupKey =
823825
PackageLookupValue.key(PackageIdentifier.create(pkgId.getRepository(), subpackagePath));
824-
targetSubpackagePackageLookupKeyMap
825-
.computeIfAbsent(target, t -> new ArrayList<>())
826-
.add(currentPackageLookupKey);
826+
subpackagePackageLookupKeys.add(currentPackageLookupKey);
827827
allPackageLookupKeys.add(currentPackageLookupKey);
828828
}
829+
targetsAndSubpackagePackageLookupKeys.add(Pair.of(target, subpackagePackageLookupKeys));
829830
}
830831

831-
if (targetSubpackagePackageLookupKeyMap.isEmpty()) {
832+
if (targetsAndSubpackagePackageLookupKeys.isEmpty()) {
832833
return;
833834
}
834835

@@ -837,9 +838,11 @@ private static void handleLabelsCrossingSubpackagesAndPropagateInconsistentFiles
837838
return;
838839
}
839840

840-
for (Map.Entry<Target, List<PackageLookupValue.Key>> entry :
841-
targetSubpackagePackageLookupKeyMap.entrySet()) {
842-
List<PackageLookupValue.Key> targetPackageLookupKeys = entry.getValue();
841+
for (Pair<Target, List<PackageLookupValue.Key>> targetAndSubpackagePackageLookupKeys :
842+
targetsAndSubpackagePackageLookupKeys) {
843+
Target target = targetAndSubpackagePackageLookupKeys.getFirst();
844+
List<PackageLookupValue.Key> targetPackageLookupKeys =
845+
targetAndSubpackagePackageLookupKeys.getSecond();
843846
// Iterate from the deepest potential subpackage to the shallowest in that we only want to
844847
// display the deepest subpackage in the error message for each target.
845848
for (PackageLookupValue.Key packageLookupKey : Lists.reverse(targetPackageLookupKeys)) {
@@ -858,10 +861,9 @@ private static void handleLabelsCrossingSubpackagesAndPropagateInconsistentFiles
858861
throw new InternalInconsistentFilesystemException(pkgId, e);
859862
}
860863

861-
Target target = entry.getKey();
862864
if (maybeAddEventAboutLabelCrossingSubpackage(
863865
pkgBuilder, pkgRoot, target, packageLookupKey.argument(), packageLookupValue)) {
864-
pkgBuilder.getTargets().remove(entry.getKey());
866+
pkgBuilder.getTargets().remove(target);
865867
pkgBuilder.setContainsErrors();
866868
break;
867869
}

src/test/java/com/google/devtools/build/lib/pkgcache/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ java_test(
181181
"//src/main/java/com/google/devtools/build/lib/vfs",
182182
"//src/main/java/com/google/devtools/common/options",
183183
"//src/main/java/net/starlark/java/syntax",
184+
"//src/main/protobuf:failure_details_java_proto",
184185
"//src/test/java/com/google/devtools/build/lib/analysis/util",
185186
"//src/test/java/com/google/devtools/build/lib/testutil",
186187
"//src/test/java/com/google/devtools/build/lib/testutil:JunitUtils",

src/test/java/com/google/devtools/build/lib/pkgcache/PackageLoadingTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
4141
import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction;
4242
import com.google.devtools.build.lib.runtime.QuiescingExecutorsImpl;
43+
import com.google.devtools.build.lib.server.FailureDetails.PackageLoading;
4344
import com.google.devtools.build.lib.skyframe.BazelSkyframeExecutorConstants;
4445
import com.google.devtools.build.lib.skyframe.PrecomputedValue;
4546
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
@@ -578,4 +579,18 @@ public void testDeterminismOfInputFileLocation() throws Exception {
578579
InputFile f = (InputFile) p.getTarget("f.sh");
579580
assertThat(f.getLocation().line()).isEqualTo(1);
580581
}
582+
583+
@Test
584+
public void testDeterminismOfFailureDetailOnMultipleLabelCrossingSubpackageBoundaryErrors()
585+
throws Exception {
586+
reporter.removeHandler(failFastHandler);
587+
scratch.file("p/sub/BUILD");
588+
scratch.file("p/BUILD", "sh_library(name = 'sub/a')", "sh_library(name = 'sub/b')");
589+
Package p = getPackage("p");
590+
assertThat(p.getFailureDetail().getPackageLoading().getCode())
591+
.isEqualTo(PackageLoading.Code.LABEL_CROSSES_PACKAGE_BOUNDARY);
592+
// We used to non-deterministically pick a target whose label crossed a subpackage boundary, but
593+
// now we deterministically pick the first one (alphabetically by target name).
594+
assertThat(p.getFailureDetail().getMessage()).startsWith("Label '//p:sub/a' is invalid");
595+
}
581596
}

0 commit comments

Comments
 (0)