diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifier.java b/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifier.java
index 82ea54975275ab..147191af51be6b 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifier.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifier.java
@@ -21,6 +21,7 @@
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Objects;
+import java.util.Optional;
import javax.annotation.concurrent.Immutable;
/**
@@ -177,6 +178,50 @@ public String getCanonicalForm() {
return repository.getCanonicalForm() + "//" + getPackageFragment();
}
+ /**
+ * Returns an absolutely unambiguous canonical form for this package in label form. Parsing this
+ * string in any environment, even when subject to repository mapping, should identify the same
+ * package.
+ */
+ public String getUnambiguousCanonicalForm() {
+ return String.format("@@%s//%s", getRepository().getName(), getPackageFragment());
+ }
+
+ /**
+ * Returns a label representation for this package that is suitable for display. The returned
+ * string is as simple as possible while referencing the current package when parsed in the
+ * context of the main repository.
+ *
+ * @param mainRepositoryMapping the {@link RepositoryMapping} of the main repository
+ * @return
//some/pkg
+ *
if this package lives in the main repository
+ *
@protobuf//some/pkg
+ *
if this package lives in a repository with "protobuf" as name of a repository
+ * in WORKSPACE or as local name of a Bzlmod dependency of the main module
+ *
@@protobuf~3.19.2//some/pkg
+ *
only with Bzlmod if the current package belongs to a repository that is not visible from
+ * the main module
+ */
+ public String getDisplayForm(RepositoryMapping mainRepositoryMapping) {
+ Preconditions.checkArgument(
+ mainRepositoryMapping.ownerRepo() == null || mainRepositoryMapping.ownerRepo().isMain());
+ if (repository.isMain()) {
+ // Packages in the main repository can always use repo-relative form.
+ return "//" + getPackageFragment();
+ }
+ if (mainRepositoryMapping.ownerRepo() == null) {
+ // If the main repository mapping is not using strict visibility, then Bzlmod is certainly
+ // disabled, which means that canonical and local names can be used interchangeably from the
+ // context of the main repository.
+ return repository.getNameWithAt() + "//" + getPackageFragment();
+ }
+ // If possible, represent the repository with a non-canonical label using the local name the
+ // main repository has for it, otherwise fall back to a canonical label.
+ return mainRepositoryMapping.getInverse(repository)
+ .map(localName -> "@" + localName + "//" + getPackageFragment())
+ .orElseGet(this::getUnambiguousCanonicalForm);
+ }
+
/**
* Returns the package path, possibly qualified with a repository name.
*
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryMapping.java b/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryMapping.java
index e504ff95f39d8c..0a95cb6b6b8342 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryMapping.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryMapping.java
@@ -19,6 +19,8 @@
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
import javax.annotation.Nullable;
/**
@@ -93,4 +95,14 @@ public RepositoryName get(String preMappingName) {
return RepositoryName.createUnvalidated(preMappingName).toNonVisible(ownerRepo());
}
}
+
+ /**
+ * Returns the first apparent name in this mapping that maps to the given canonical name, if any.
+ */
+ public Optional getInverse(RepositoryName postMappingName) {
+ return repositoryMapping().entrySet().stream()
+ .filter(e -> e.getValue().equals(postMappingName))
+ .map(Entry::getKey)
+ .findFirst();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index a73a3d38f20d15..ac0caafce97ab3 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -248,6 +248,13 @@ public enum ConfigSettingVisibilityPolicy {
*/
private RepositoryMapping repositoryMapping;
+ /**
+ * The repository mapping of the main repository. This is only used internally to obtain
+ * user-friendly apparent names from canonical repository names in error message arising from this
+ * package.
+ */
+ private RepositoryMapping mainRepositoryMapping;
+
private Set