Skip to content

Commit 62836d4

Browse files
fmeumcopybara-github
authored andcommitted
Fix query command suggestion in error message with repo mappings
The package label in the query command suggested when a specified target isn't found in a package looked like `@rules_cc~1.0.0//pkg`, which doesn't work as the repo name is canonical, but the label isn't. This is fixed by reusing the logic from `Label#getUnambiguousCanonicalForm`. Closes #16482. PiperOrigin-RevId: 483358185 Change-Id: I5a6eeae9950c614d5268aed62474f5feb31a8ce6
1 parent ebd6e58 commit 62836d4

File tree

9 files changed

+140
-10
lines changed

9 files changed

+140
-10
lines changed

src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifier.java

+47
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,53 @@ public String getCanonicalForm() {
177177
return repository.getCanonicalForm() + "//" + getPackageFragment();
178178
}
179179

180+
/**
181+
* Returns an absolutely unambiguous canonical form for this package in label form. Parsing this
182+
* string in any environment, even when subject to repository mapping, should identify the same
183+
* package.
184+
*/
185+
public String getUnambiguousCanonicalForm() {
186+
return String.format("@@%s//%s", getRepository().getName(), getPackageFragment());
187+
}
188+
189+
/**
190+
* Returns a label representation for this package that is suitable for display. The returned
191+
* string is as simple as possible while referencing the current package when parsed in the
192+
* context of the main repository.
193+
*
194+
* @param mainRepositoryMapping the {@link RepositoryMapping} of the main repository
195+
* @return
196+
* <dl>
197+
* <dt><code>//some/pkg</code>
198+
* <dd>if this package lives in the main repository
199+
* <dt><code>@protobuf//some/pkg</code>
200+
* <dd>if this package lives in a repository with "protobuf" as <code>name</code> of a
201+
* repository in WORKSPACE or as apparent name of a Bzlmod dependency of the main module
202+
* <dt><code>@@protobuf~3.19.2//some/pkg</code>
203+
* <dd>only with Bzlmod if the current package belongs to a repository that is not visible
204+
* from the main module
205+
*/
206+
public String getDisplayForm(RepositoryMapping mainRepositoryMapping) {
207+
Preconditions.checkArgument(
208+
mainRepositoryMapping.ownerRepo() == null || mainRepositoryMapping.ownerRepo().isMain());
209+
if (repository.isMain()) {
210+
// Packages in the main repository can always use repo-relative form.
211+
return "//" + getPackageFragment();
212+
}
213+
if (!mainRepositoryMapping.usesStrictDeps()) {
214+
// If the main repository mapping is not using strict visibility, then Bzlmod is certainly
215+
// disabled, which means that canonical and apparent names can be used interchangeably from
216+
// the context of the main repository.
217+
return repository.getNameWithAt() + "//" + getPackageFragment();
218+
}
219+
// If possible, represent the repository with a non-canonical label using the apparent name the
220+
// main repository has for it, otherwise fall back to a canonical label.
221+
return mainRepositoryMapping
222+
.getInverse(repository)
223+
.map(apparentName -> "@" + apparentName + "//" + getPackageFragment())
224+
.orElseGet(this::getUnambiguousCanonicalForm);
225+
}
226+
180227
/**
181228
* Returns the package path, possibly qualified with a repository name.
182229
*

src/main/java/com/google/devtools/build/lib/cmdline/RepositoryMapping.java

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.google.common.collect.ImmutableMap;
2020
import java.util.HashMap;
2121
import java.util.Map;
22+
import java.util.Map.Entry;
23+
import java.util.Optional;
2224
import javax.annotation.Nullable;
2325

2426
/**
@@ -93,4 +95,22 @@ public RepositoryName get(String preMappingName) {
9395
return RepositoryName.createUnvalidated(preMappingName).toNonVisible(ownerRepo());
9496
}
9597
}
98+
99+
/**
100+
* Whether the repo with this mapping is subject to strict deps; when strict deps is off, unknown
101+
* apparent names are silently treated as canonical names.
102+
*/
103+
public boolean usesStrictDeps() {
104+
return ownerRepo() != null;
105+
}
106+
107+
/**
108+
* Returns the first apparent name in this mapping that maps to the given canonical name, if any.
109+
*/
110+
public Optional<String> getInverse(RepositoryName postMappingName) {
111+
return repositoryMapping().entrySet().stream()
112+
.filter(e -> e.getValue().equals(postMappingName))
113+
.map(Entry::getKey)
114+
.findFirst();
115+
}
96116
}

src/main/java/com/google/devtools/build/lib/packages/Package.java

+23-3
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,13 @@ public enum ConfigSettingVisibilityPolicy {
248248
*/
249249
private RepositoryMapping repositoryMapping;
250250

251+
/**
252+
* The repository mapping of the main repository. This is only used internally to obtain
253+
* user-friendly apparent names from canonical repository names in error message arising from this
254+
* package.
255+
*/
256+
private RepositoryMapping mainRepositoryMapping;
257+
251258
private Set<Label> defaultCompatibleWith = ImmutableSet.of();
252259
private Set<Label> defaultRestrictedTo = ImmutableSet.of();
253260

@@ -478,6 +485,7 @@ private void finishInit(Builder builder) {
478485
this.registeredExecutionPlatforms = ImmutableList.copyOf(builder.registeredExecutionPlatforms);
479486
this.registeredToolchains = ImmutableList.copyOf(builder.registeredToolchains);
480487
this.repositoryMapping = Preconditions.checkNotNull(builder.repositoryMapping);
488+
this.mainRepositoryMapping = Preconditions.checkNotNull(builder.mainRepositoryMapping);
481489
ImmutableMap.Builder<RepositoryName, ImmutableMap<String, RepositoryName>>
482490
repositoryMappingsBuilder = ImmutableMap.builder();
483491
if (!builder.externalPackageRepositoryMappings.isEmpty() && !builder.isRepoRulePackage()) {
@@ -731,7 +739,7 @@ private String getAlternateTargetSuggestion(String targetName) {
731739
String blazeQuerySuggestion =
732740
String.format(
733741
"Tip: use `query %s:*` to see all the targets in that package",
734-
packageIdentifier.getCanonicalForm());
742+
packageIdentifier.getDisplayForm(mainRepositoryMapping));
735743
return String.format(
736744
" (%s)", Joiner.on(" ").skipNulls().join(targetSuggestion, blazeQuerySuggestion));
737745
}
@@ -880,6 +888,7 @@ public static Builder newExternalPackageBuilder(
880888
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
881889
workspaceName,
882890
starlarkSemantics.getBool(BuildLanguageOptions.INCOMPATIBLE_NO_IMPLICIT_FILE_EXPORT),
891+
mainRepoMapping,
883892
mainRepoMapping)
884893
.setFilename(workspacePath);
885894
}
@@ -894,7 +903,11 @@ public static Builder newExternalPackageBuilderForBzlmod(
894903
basePackageId,
895904
DUMMY_WORKSPACE_NAME_FOR_BZLMOD_PACKAGES,
896905
starlarkSemantics.getBool(BuildLanguageOptions.INCOMPATIBLE_NO_IMPLICIT_FILE_EXPORT),
897-
repoMapping)
906+
repoMapping,
907+
// This mapping is *not* the main repository's mapping, but since it is only used to
908+
// construct a query command in an error message and the package built here can't be
909+
// seen by query, the particular value does not matter.
910+
RepositoryMapping.ALWAYS_FALLBACK)
898911
.setFilename(moduleFilePath);
899912
}
900913

@@ -974,6 +987,11 @@ public boolean recordLoadedModules() {
974987
* workspace.
975988
*/
976989
private final RepositoryMapping repositoryMapping;
990+
/**
991+
* The repository mapping of the main repository. This is only used to resolve user-friendly
992+
* apparent names from canonical repository names in error message arising from this package.
993+
*/
994+
private final RepositoryMapping mainRepositoryMapping;
977995
/** Converts label literals to Label objects within this package. */
978996
private final LabelConverter labelConverter;
979997

@@ -1098,10 +1116,12 @@ public T intern(T sample) {
10981116
PackageIdentifier id,
10991117
String workspaceName,
11001118
boolean noImplicitFileExport,
1101-
RepositoryMapping repositoryMapping) {
1119+
RepositoryMapping repositoryMapping,
1120+
RepositoryMapping mainRepositoryMapping) {
11021121
this.pkg = new Package(id, workspaceName, packageSettings.succinctTargetNotFoundErrors());
11031122
this.noImplicitFileExport = noImplicitFileExport;
11041123
this.repositoryMapping = repositoryMapping;
1124+
this.mainRepositoryMapping = mainRepositoryMapping;
11051125
this.labelConverter = new LabelConverter(id, repositoryMapping);
11061126
if (pkg.getName().startsWith("javatests/")) {
11071127
setDefaultTestonly(true);

src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -465,13 +465,15 @@ public Package.Builder newPackageBuilder(
465465
PackageIdentifier packageId,
466466
String workspaceName,
467467
StarlarkSemantics starlarkSemantics,
468-
RepositoryMapping repositoryMapping) {
468+
RepositoryMapping repositoryMapping,
469+
RepositoryMapping mainRepositoryMapping) {
469470
return new Package.Builder(
470471
packageSettings,
471472
packageId,
472473
workspaceName,
473474
starlarkSemantics.getBool(BuildLanguageOptions.INCOMPATIBLE_NO_IMPLICIT_FILE_EXPORT),
474-
repositoryMapping);
475+
repositoryMapping,
476+
mainRepositoryMapping);
475477
}
476478

477479
/** Returns a new {@link NonSkyframeGlobber}. */

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
3434
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
3535
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
36+
import com.google.devtools.build.lib.cmdline.RepositoryName;
3637
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
3738
import com.google.devtools.build.lib.events.Event;
3839
import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
@@ -1215,6 +1216,8 @@ private LoadedPackage loadPackage(
12151216
RepositoryMappingValue repositoryMappingValue =
12161217
(RepositoryMappingValue)
12171218
env.getValue(RepositoryMappingValue.key(packageId.getRepository()));
1219+
RepositoryMappingValue mainRepositoryMappingValue =
1220+
(RepositoryMappingValue) env.getValue(RepositoryMappingValue.key(RepositoryName.MAIN));
12181221
RootedPath buildFileRootedPath = packageLookupValue.getRootedPath(packageId);
12191222
FileValue buildFileValue = getBuildFileValue(env, buildFileRootedPath);
12201223
RuleVisibility defaultVisibility = PrecomputedValue.DEFAULT_VISIBILITY.get(env);
@@ -1346,7 +1349,8 @@ private LoadedPackage loadPackage(
13461349
packageId,
13471350
workspaceName,
13481351
starlarkBuiltinsValue.starlarkSemantics,
1349-
repositoryMapping)
1352+
repositoryMapping,
1353+
mainRepositoryMappingValue.getRepositoryMapping())
13501354
.setFilename(buildFileRootedPath)
13511355
.setDefaultVisibility(defaultVisibility)
13521356
// "defaultVisibility" comes from the command line.

src/test/java/com/google/devtools/build/lib/cmdline/PackageIdentifierTest.java

+34-3
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@
1616

1717
import static com.google.common.truth.Truth.assertThat;
1818

19+
import com.google.common.collect.ImmutableMap;
1920
import com.google.devtools.build.lib.vfs.PathFragment;
2021
import org.junit.Test;
2122
import org.junit.runner.RunWith;
2223
import org.junit.runners.JUnit4;
2324

24-
/**
25-
* Unit tests for {@link PackageIdentifier}.
26-
*/
25+
/** Unit tests for {@link PackageIdentifier}. */
2726
@RunWith(JUnit4.class)
2827
public class PackageIdentifierTest {
2928
@Test
@@ -93,4 +92,36 @@ public void testRunfilesDir() throws Exception {
9392
assertThat(PackageIdentifier.create("", PathFragment.create("bar/baz")).getRunfilesPath())
9493
.isEqualTo(PathFragment.create("bar/baz"));
9594
}
95+
96+
@Test
97+
public void testDisplayFormInMainRepository() throws Exception {
98+
PackageIdentifier pkg =
99+
PackageIdentifier.create(RepositoryName.MAIN, PathFragment.create("some/pkg"));
100+
101+
assertThat(pkg.getDisplayForm(RepositoryMapping.ALWAYS_FALLBACK)).isEqualTo("//some/pkg");
102+
assertThat(
103+
pkg.getDisplayForm(
104+
RepositoryMapping.create(
105+
ImmutableMap.of("foo", RepositoryName.create("bar")), RepositoryName.MAIN)))
106+
.isEqualTo("//some/pkg");
107+
}
108+
109+
@Test
110+
public void testDisplayFormInExternalRepository() throws Exception {
111+
RepositoryName repo = RepositoryName.create("canonical");
112+
PackageIdentifier pkg = PackageIdentifier.create(repo, PathFragment.create("some/pkg"));
113+
114+
assertThat(pkg.getDisplayForm(RepositoryMapping.ALWAYS_FALLBACK))
115+
.isEqualTo("@canonical//some/pkg");
116+
assertThat(
117+
pkg.getDisplayForm(
118+
RepositoryMapping.create(ImmutableMap.of("local", repo), RepositoryName.MAIN)))
119+
.isEqualTo("@local//some/pkg");
120+
assertThat(
121+
pkg.getDisplayForm(
122+
RepositoryMapping.create(
123+
ImmutableMap.of("local", RepositoryName.create("other_repo")),
124+
RepositoryName.MAIN)))
125+
.isEqualTo("@@canonical//some/pkg");
126+
}
96127
}

src/test/java/com/google/devtools/build/lib/packages/PackageTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ private Package.Builder pkgBuilder(String name) {
164164
PackageIdentifier.createInMainRepo(name),
165165
"workspace",
166166
/*noImplicitFileExport=*/ true,
167+
RepositoryMapping.ALWAYS_FALLBACK,
167168
RepositoryMapping.ALWAYS_FALLBACK);
168169
result.setFilename(
169170
RootedPath.toRootedPath(

src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ private Package.Builder createDummyPackageBuilder() {
265265
PackageIdentifier.createInMainRepo(TEST_PACKAGE_NAME),
266266
"TESTING",
267267
StarlarkSemantics.DEFAULT,
268+
RepositoryMapping.ALWAYS_FALLBACK,
268269
RepositoryMapping.ALWAYS_FALLBACK)
269270
.setFilename(RootedPath.toRootedPath(root, testBuildfilePath));
270271
}

src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ public final class RuleFactoryTest extends PackageLoadingTestCase {
5858
private Package.Builder newBuilder(PackageIdentifier id, Path filename) {
5959
return packageFactory
6060
.newPackageBuilder(
61-
id, "TESTING", StarlarkSemantics.DEFAULT, RepositoryMapping.ALWAYS_FALLBACK)
61+
id,
62+
"TESTING",
63+
StarlarkSemantics.DEFAULT,
64+
RepositoryMapping.ALWAYS_FALLBACK,
65+
RepositoryMapping.ALWAYS_FALLBACK)
6266
.setFilename(RootedPath.toRootedPath(root, filename));
6367
}
6468

0 commit comments

Comments
 (0)