Skip to content

Commit 00a4fef

Browse files
yuyue730copybara-github
authored andcommitted
Use PackageLookupValue to do package lookup and subpackage boundary cross check in BzlLoadFunction.
This is a similar change as bazelbuild@c3a838b in which we switch from using `ContainingPackageLookupValue` to `PackageLookupValue` for `PackageFunction`. PiperOrigin-RevId: 534989868 Change-Id: Ic06ffbea8b13ad5eff4001049791c9a512e0211d
1 parent 44d3953 commit 00a4fef

File tree

5 files changed

+142
-101
lines changed

5 files changed

+142
-101
lines changed

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

Lines changed: 105 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
5151
import com.google.devtools.build.lib.server.FailureDetails.StarlarkLoading;
5252
import com.google.devtools.build.lib.server.FailureDetails.StarlarkLoading.Code;
53+
import com.google.devtools.build.lib.skyframe.PackageLookupValue.NoRepositoryPackageLookupValue;
5354
import com.google.devtools.build.lib.skyframe.StarlarkBuiltinsFunction.BuiltinsFailedException;
5455
import com.google.devtools.build.lib.util.DetailedExitCode;
5556
import com.google.devtools.build.lib.util.Fingerprint;
@@ -63,6 +64,7 @@
6364
import com.google.devtools.build.skyframe.SkyKey;
6465
import com.google.devtools.build.skyframe.SkyValue;
6566
import com.google.devtools.build.skyframe.SkyframeLookupResult;
67+
import java.util.ArrayList;
6668
import java.util.HashMap;
6769
import java.util.HashSet;
6870
import java.util.LinkedHashSet;
@@ -644,45 +646,103 @@ private BzlCompileValue.Key validatePackageAndGetCompileKey(
644646
return key.getCompileKey(getBuiltinsRoot(builtinsBzlPath));
645647
}
646648

647-
// Do package lookup.
648-
PathFragment dir = Label.getContainingDirectory(label);
649-
PackageIdentifier dirId = PackageIdentifier.create(label.getRepository(), dir);
650-
ContainingPackageLookupValue packageLookup;
651-
try {
652-
packageLookup =
653-
(ContainingPackageLookupValue)
654-
env.getValueOrThrow(
655-
ContainingPackageLookupValue.key(dirId),
656-
BuildFileNotFoundException.class,
657-
InconsistentFilesystemException.class);
658-
} catch (BuildFileNotFoundException | InconsistentFilesystemException e) {
659-
throw BzlLoadFailedException.errorFindingContainingPackage(label.toPathFragment(), e);
660-
}
661-
if (packageLookup == null) {
662-
return null;
649+
// The block below derives all (sub)directories that could possibly contain (sub)packages and
650+
// add them to a list of PackageLookup keys. These (sub)directories include the label path, and
651+
// all subdirectories from label path to the bzl file. For example,
652+
// 1. If the label is //a/b/c:d.bzl, allPackageLookupKeys only contains //a/b/c. There is no
653+
// subdirectory under label path.
654+
// 2. If the label name contains '/', for example, //a/b/c:d/e/f.bzl, allPackageLookupKeys
655+
// contain //a/b/c, //a/b/c/d and //a/b/c/d/e.
656+
List<PackageLookupValue.Key> allPackageLookupKeys = new ArrayList<>();
657+
allPackageLookupKeys.add(PackageLookupValue.key(label.getPackageIdentifier()));
658+
RepositoryName labelRepository = label.getRepository();
659+
PathFragment subpkgPath = label.getPackageFragment();
660+
PathFragment labelAsRelativePath = PathFragment.create(label.getName()).getParentDirectory();
661+
for (String segment : labelAsRelativePath.segments()) {
662+
subpkgPath = subpkgPath.getRelative(segment);
663+
PackageLookupValue.Key currentPackageLookupKey =
664+
PackageLookupValue.key(PackageIdentifier.create(labelRepository, subpkgPath));
665+
allPackageLookupKeys.add(currentPackageLookupKey);
666+
}
667+
668+
SkyframeLookupResult packageLookupResults = env.getValuesAndExceptions(allPackageLookupKeys);
669+
670+
// We intentionally choose not to check `env.valuesMissing()` here. It is possible that all
671+
// PackageLookupValues are already not null but `env.valuesMissing()` is still true from a prior
672+
// request. Returning `null` in this case causes unnecessary Skyframe restarts.
673+
674+
PackageLookupValue.Key candidateKey = null;
675+
PackageLookupValue candidateValue = null;
676+
for (PackageLookupValue.Key packageLookupKey : allPackageLookupKeys) {
677+
// Iterate in order of the directory structure so that the candidate{Key,Value} will end up as
678+
// the deepest package, in other words the "containing package".
679+
PackageLookupValue packageLookupValue;
680+
try {
681+
packageLookupValue =
682+
(PackageLookupValue)
683+
packageLookupResults.getOrThrow(
684+
packageLookupKey,
685+
BuildFileNotFoundException.class,
686+
InconsistentFilesystemException.class);
687+
} catch (BuildFileNotFoundException | InconsistentFilesystemException e) {
688+
throw BzlLoadFailedException.errorFindingContainingPackage(label.toPathFragment(), e);
689+
}
690+
691+
if (packageLookupValue == null) {
692+
return null;
693+
}
694+
695+
if (packageLookupValue instanceof NoRepositoryPackageLookupValue) {
696+
throw BzlLoadFailedException.noBuildFile(label, packageLookupValue.getErrorMsg());
697+
}
698+
699+
if (packageLookupValue.packageExists()) {
700+
candidateKey = packageLookupKey;
701+
candidateValue = packageLookupValue;
702+
}
663703
}
664704

665-
// Resolve to compile key or error.
666-
BzlCompileValue.Key compileKey;
667-
boolean packageOk =
668-
packageLookup.hasContainingPackage()
669-
&& packageLookup.getContainingPackageName().equals(label.getPackageIdentifier());
670-
if (key.isBuildPrelude() && !packageOk) {
671-
// Ignore the prelude, its package doesn't exist.
672-
compileKey = BzlCompileValue.EMPTY_PRELUDE_KEY;
673-
} else {
674-
if (packageOk) {
675-
compileKey = key.getCompileKey(packageLookup.getContainingPackageRoot());
705+
if (candidateKey != null && candidateKey.argument().equals(label.getPackageIdentifier())) {
706+
if (candidateValue.packageExists()) {
707+
return key.getCompileKey(candidateValue.getRoot());
676708
} else {
677-
if (!packageLookup.hasContainingPackage()) {
678-
throw BzlLoadFailedException.noBuildFile(
679-
label, packageLookup.getReasonForNoContainingPackage());
709+
throw BzlLoadFailedException.noBuildFile(label, candidateValue.getErrorMsg());
710+
}
711+
} else {
712+
if (key.isBuildPrelude()) {
713+
return BzlCompileValue.EMPTY_PRELUDE_KEY;
714+
}
715+
if (candidateKey == null) {
716+
// If we cannot find any subpackage below label's package directory, it is still possible
717+
// that the label's package is a subpackage itself. This case should be rare, so we choose
718+
// to still handle it using ContainingPackageLookup node.
719+
ContainingPackageLookupValue containingPackageLookup;
720+
try {
721+
containingPackageLookup =
722+
(ContainingPackageLookupValue)
723+
env.getValueOrThrow(
724+
ContainingPackageLookupValue.key(label.getPackageIdentifier()),
725+
BuildFileNotFoundException.class,
726+
InconsistentFilesystemException.class);
727+
} catch (BuildFileNotFoundException | InconsistentFilesystemException e) {
728+
throw BzlLoadFailedException.errorFindingContainingPackage(label.toPathFragment(), e);
729+
}
730+
731+
if (containingPackageLookup == null) {
732+
return null;
733+
}
734+
735+
if (containingPackageLookup.hasContainingPackage()) {
736+
throw BzlLoadFailedException.labelSubpackageCrossesBoundary(
737+
label, containingPackageLookup);
680738
} else {
681-
throw BzlLoadFailedException.labelCrossesPackageBoundary(label, packageLookup);
739+
throw BzlLoadFailedException.noBuildFile(label, /* reason= */ null);
682740
}
741+
} else {
742+
throw BzlLoadFailedException.subpackageCrossesLabelPackageBoundary(
743+
label, candidateKey.argument(), candidateValue);
683744
}
684745
}
685-
return compileKey;
686746
}
687747

688748
private Root getBuiltinsRoot(String builtinsBzlPath) {
@@ -1583,17 +1643,21 @@ static BzlLoadFailedException noBuildFile(Label file, @Nullable String reason) {
15831643
Code.PACKAGE_NOT_FOUND);
15841644
}
15851645

1586-
static BzlLoadFailedException labelCrossesPackageBoundary(
1646+
static BzlLoadFailedException labelSubpackageCrossesBoundary(
15871647
Label label, ContainingPackageLookupValue containingPackageLookupValue) {
15881648
return new BzlLoadFailedException(
1589-
ContainingPackageLookupValue.getErrorMessageForLabelCrossingPackageBoundary(
1590-
// We don't actually know the proper Root to pass in here (since we don't e.g. know
1591-
// the root of the bzl/BUILD file that is trying to load 'label'). Therefore we just
1592-
// pass in the Root of the containing package in order to still get a useful error
1593-
// message for the user.
1594-
containingPackageLookupValue.getContainingPackageRoot(),
1595-
label,
1596-
containingPackageLookupValue),
1649+
ContainingPackageLookupValue.getErrorMessageForLabelSubpackageCrossesBoundary(
1650+
containingPackageLookupValue, label),
1651+
Code.LABEL_CROSSES_PACKAGE_BOUNDARY);
1652+
}
1653+
1654+
static BzlLoadFailedException subpackageCrossesLabelPackageBoundary(
1655+
Label label,
1656+
PackageIdentifier subpackageIdentifier,
1657+
PackageLookupValue packageLookupValue) {
1658+
return new BzlLoadFailedException(
1659+
PackageLookupValue.getErrorMessageForSubpackageCrossesLabelPackageBoundary(
1660+
packageLookupValue.getRoot(), label, subpackageIdentifier, packageLookupValue),
15971661
Code.LABEL_CROSSES_PACKAGE_BOUNDARY);
15981662
}
15991663

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

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -59,48 +59,27 @@ public static Key key(PackageIdentifier id) {
5959
return Key.create(id);
6060
}
6161

62-
static String getErrorMessageForLabelCrossingPackageBoundary(
63-
Root pkgRoot,
64-
Label label,
65-
ContainingPackageLookupValue containingPkgLookupValue) {
62+
/**
63+
* Creates the error message for the input {@linkplain Label label} if label itself is a
64+
* subpackage crosses boundary when an outer package exists.
65+
*/
66+
static String getErrorMessageForLabelSubpackageCrossesBoundary(
67+
ContainingPackageLookupValue containingPkgLookupValue, Label label) {
6668
PackageIdentifier containingPkg = containingPkgLookupValue.getContainingPackageName();
67-
boolean crossesPackageBoundaryBelow =
68-
containingPkg.getSourceRoot().startsWith(label.getPackageIdentifier().getSourceRoot());
69-
PathFragment labelNameFragment = PathFragment.create(label.getName());
70-
String message;
71-
if (crossesPackageBoundaryBelow) {
72-
message =
73-
String.format("Label '%s' is invalid because '%s' is a subpackage", label, containingPkg);
74-
} else {
75-
message =
76-
String.format(
77-
"Label '%s' is invalid because '%s' is not a package", label, label.getPackageName());
78-
}
79-
80-
Root containingRoot = containingPkgLookupValue.getContainingPackageRoot();
81-
if (pkgRoot.equals(containingRoot)) {
82-
PathFragment containingPkgFragment = containingPkg.getPackageFragment();
83-
PathFragment labelNameInContainingPackage =
84-
crossesPackageBoundaryBelow
85-
? labelNameFragment.subFragment(
86-
containingPkgFragment.segmentCount()
87-
- label.getPackageFragment().segmentCount(),
88-
labelNameFragment.segmentCount())
89-
: label.toPathFragment().relativeTo(containingPkgFragment);
90-
message += "; perhaps you meant to put the colon here: '";
91-
if (containingPkg.getRepository().isMain()) {
92-
message += "//";
93-
}
94-
message += containingPkg + ":" + labelNameInContainingPackage + "'?";
95-
} else {
96-
message +=
97-
"; have you deleted "
98-
+ containingPkg
99-
+ "/BUILD? "
100-
+ "If so, use the --deleted_packages="
101-
+ containingPkg
102-
+ " option";
103-
}
69+
Preconditions.checkState(
70+
label.getPackageIdentifier().getSourceRoot().startsWith(containingPkg.getSourceRoot()),
71+
"Label's path should start with outer package's path.");
72+
73+
String message =
74+
String.format(
75+
"Label '%s' is invalid because '%s' is not a package", label, label.getPackageName());
76+
PathFragment labelNameInContainingPackage =
77+
label.toPathFragment().relativeTo(containingPkg.getPackageFragment());
78+
message += "; perhaps you meant to put the colon here: '";
79+
if (containingPkg.getRepository().isMain()) {
80+
message += "//";
81+
}
82+
message += containingPkg + ":" + labelNameInContainingPackage + "'?";
10483
return message;
10584
}
10685

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ private static boolean maybeAddEventAboutLabelCrossingSubpackage(
881881
return true;
882882
}
883883
String errMsg =
884-
PackageLookupValue.getErrorMessageForLabelCrossingPackageBoundary(
884+
PackageLookupValue.getErrorMessageForSubpackageCrossesLabelPackageBoundary(
885885
pkgRoot, target.getLabel(), subpackageIdentifier, packageLookupValue);
886886
if (errMsg != null) {
887887
Event error =

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -401,13 +401,13 @@ public String getErrorMsg() {
401401
}
402402

403403
/**
404-
* Creates the error message for the input {@linkplain Label label} has a subpackage crossing
405-
* boundary.
404+
* Creates the error message for the input {@linkplain Label label} if it contains a subpackage
405+
* crossing boundary.
406406
*
407407
* <p>Returns {@code null} if no subpackage is discovered or the subpackage is marked as DELETED.
408408
*/
409409
@Nullable
410-
static String getErrorMessageForLabelCrossingPackageBoundary(
410+
static String getErrorMessageForSubpackageCrossesLabelPackageBoundary(
411411
Root pkgRoot,
412412
Label label,
413413
PackageIdentifier subpackageIdentifier,
@@ -422,19 +422,19 @@ static String getErrorMessageForLabelCrossingPackageBoundary(
422422
if (pkgRoot.equals(subPackageRoot)) {
423423
PathFragment labelRootPathFragment = label.getPackageIdentifier().getSourceRoot();
424424
PathFragment subpackagePathFragment = subpackageIdentifier.getSourceRoot();
425-
if (subpackagePathFragment.startsWith(labelRootPathFragment)) {
426-
PathFragment labelNameInSubpackage =
427-
PathFragment.create(label.getName())
428-
.subFragment(
429-
subpackagePathFragment.segmentCount() - labelRootPathFragment.segmentCount());
430-
message += "; perhaps you meant to put the" + " colon here: '";
431-
if (subpackageIdentifier.getRepository().isMain()) {
432-
message += "//";
433-
}
434-
message += subpackageIdentifier + ":" + labelNameInSubpackage + "'?";
435-
} else {
436-
// TODO: Is this a valid case? How do we handle this case?
425+
Preconditions.checkState(
426+
subpackagePathFragment.startsWith(labelRootPathFragment),
427+
"Subpackage should start with label's package path when they share the same package"
428+
+ " root");
429+
PathFragment labelNameInSubpackage =
430+
PathFragment.create(label.getName())
431+
.subFragment(
432+
subpackagePathFragment.segmentCount() - labelRootPathFragment.segmentCount());
433+
message += "; perhaps you meant to put the" + " colon here: '";
434+
if (subpackageIdentifier.getRepository().isMain()) {
435+
message += "//";
437436
}
437+
message += subpackageIdentifier + ":" + labelNameInSubpackage + "'?";
438438
} else {
439439
message +=
440440
"; have you deleted "

src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,9 +1679,7 @@ public void testPreludeCanAccessBzlDialectFeatures() throws Exception {
16791679

16801680
@Test
16811681
public void testPreludeNeedNotBePresent() throws Exception {
1682-
scratch.file(
1683-
"pkg/BUILD", //
1684-
"print('FOO')");
1682+
scratch.file("pkg/BUILD", "print('FOO')");
16851683

16861684
getConfiguredTarget("//pkg:BUILD");
16871685
assertContainsEvent("FOO");

0 commit comments

Comments
 (0)