Don't keep packages in the default repository around after loading.
Previously, this would get thrown when referring to the same package
from both the main and default repositories:
java.lang.IllegalArgumentException: Multiple entries with same key: tools/cpp=/home/brian/971-Robot-Code and tools/cpp=/home/brian/971-Robot-Code
at com.google.common.collect.ImmutableMap.checkNoConflict(ImmutableMap.java:136)
at com.google.common.collect.RegularImmutableMap.checkNoConflictInKeyBucket(RegularImmutableMap.java:98)
at com.google.common.collect.RegularImmutableMap.fromEntryArray(RegularImmutableMap.java:84)
at com.google.common.collect.ImmutableMap$Builder.build(ImmutableMap.java:295)
at com.google.devtools.build.lib.buildtool.BuildTool.transformPackageRoots(BuildTool.java:301)
at com.google.devtools.build.lib.buildtool.BuildTool.buildTargets(BuildTool.java:209)
at com.google.devtools.build.lib.buildtool.BuildTool.processRequest(BuildTool.java:334)
at com.google.devtools.build.lib.runtime.commands.TestCommand.doTest(TestCommand.java:119)
at com.google.devtools.build.lib.runtime.commands.TestCommand.exec(TestCommand.java:104)
at com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.exec(BlazeCommandDispatcher.java:371)
at com.google.devtools.build.lib.runtime.BlazeRuntime$3.exec(BlazeRuntime.java:1016)
at com.google.devtools.build.lib.server.RPCService.executeRequest(RPCService.java:65)
at com.google.devtools.build.lib.server.RPCServer.executeRequest(RPCServer.java:434)
at com.google.devtools.build.lib.server.RPCServer.serve(RPCServer.java:229)
at com.google.devtools.build.lib.runtime.BlazeRuntime.serverMain(BlazeRuntime.java:975)
at com.google.devtools.build.lib.runtime.BlazeRuntime.main(BlazeRuntime.java:772)
at com.google.devtools.build.lib.bazel.BazelMain.main(BazelMain.java:55)
And this would get thrown for any packages in the main repository loaded
from other repositories:
java.lang.RuntimeException: Unrecoverable error while evaluating node 'PACKAGE:@//tools/build_rules/go/toolchain' (requested by nodes )
at com.google.devtools.build.skyframe.ParallelEvaluator$Evaluate.run(ParallelEvaluator.java:982)
at com.google.devtools.build.lib.concurrent.AbstractQueueVisitor$WrappedRunnable.run(AbstractQueueVisitor.java:499)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException: Invalid BUILD file name for package '@//tools/build_rules/go/toolchain': /home/brian/bazel/tools/build_rules/go/toolchain/BUILD
at com.google.devtools.build.lib.packages.Package.finishInit(Package.java:299)
at com.google.devtools.build.lib.packages.Package$Builder.finishBuild(Package.java:1308)
at com.google.devtools.build.lib.skyframe.PackageFunction.compute(PackageFunction.java:501)
at com.google.devtools.build.skyframe.ParallelEvaluator$Evaluate.run(ParallelEvaluator.java:933)
... 4 more
Sponsor's comment: note the abundance of new Label.resolveRepositoryRelative() calls. They are ugly, but it's only making existing ugliness explicit. Yes, we should fix it, especially in the implementation of configurable attributes.
Refs #940
--
Change-Id: I8bd7f7b00bec58a7157507595421bc50c81b404c
Reviewed-on: https://bazel-review.googlesource.com/#/c/2591
MOS_MIGRATED_REVID=117429733
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/Label.java b/src/main/java/com/google/devtools/build/lib/cmdline/Label.java
index b1bcffa..030e689 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/Label.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/Label.java
@@ -48,7 +48,7 @@
* Package names that aren't made relative to the current repository because they mean special
* things to Bazel.
*/
- private static final ImmutableSet<PathFragment> ABSOLUTE_PACKAGE_NAMES = ImmutableSet.of(
+ public static final ImmutableSet<PathFragment> ABSOLUTE_PACKAGE_NAMES = ImmutableSet.of(
// Used for select
new PathFragment("conditions"),
// dependencies that are a function of the configuration
@@ -59,7 +59,7 @@
Label.EXTERNAL_PACKAGE_NAME);
public static final PackageIdentifier EXTERNAL_PACKAGE_IDENTIFIER =
- PackageIdentifier.createInDefaultRepo(EXTERNAL_PACKAGE_NAME);
+ PackageIdentifier.createInMainRepo(EXTERNAL_PACKAGE_NAME);
public static final String EXTERNAL_PATH_PREFIX = "external";
@@ -71,9 +71,28 @@
* {@literal @}foo//bar
* {@literal @}foo//bar:baz
* </pre>
+ *
+ * <p>Treats labels in the default repository as being in the main repository instead.
*/
public static Label parseAbsolute(String absName) throws LabelSyntaxException {
- String repo = PackageIdentifier.DEFAULT_REPOSITORY;
+ return parseAbsolute(absName, true);
+ }
+
+ /**
+ * Factory for Labels from absolute string form. e.g.
+ * <pre>
+ * //foo/bar
+ * //foo/bar:quux
+ * {@literal @}foo//bar
+ * {@literal @}foo//bar:baz
+ * </pre>
+ *
+ * @param defaultToMain Treat labels in the default repository as being in the main
+ * one instead.
+ */
+ public static Label parseAbsolute(String absName, boolean defaultToMain)
+ throws LabelSyntaxException {
+ String repo = defaultToMain ? "@" : PackageIdentifier.DEFAULT_REPOSITORY;
int packageStartPos = absName.indexOf("//");
if (packageStartPos > 0) {
repo = absName.substring(0, packageStartPos);
@@ -83,8 +102,12 @@
LabelValidator.PackageAndTarget labelParts = LabelValidator.parseAbsoluteLabel(absName);
PackageIdentifier pkgIdWithoutRepo =
validate(labelParts.getPackageName(), labelParts.getTargetName());
+ PathFragment packageFragment = pkgIdWithoutRepo.getPackageFragment();
+ if (repo.isEmpty() && ABSOLUTE_PACKAGE_NAMES.contains(packageFragment)) {
+ repo = "@";
+ }
return new Label(
- PackageIdentifier.create(repo, pkgIdWithoutRepo.getPackageFragment()),
+ PackageIdentifier.create(repo, packageFragment),
labelParts.getTargetName());
} catch (BadLabelException e) {
throw new LabelSyntaxException(e.getMessage());
@@ -98,14 +121,18 @@
*
* <p>Do not use this when the argument is not hard-wired.
*/
- public static Label parseAbsoluteUnchecked(String absName) {
+ public static Label parseAbsoluteUnchecked(String absName, boolean defaultToMain) {
try {
- return parseAbsolute(absName);
+ return parseAbsolute(absName, defaultToMain);
} catch (LabelSyntaxException e) {
throw new IllegalArgumentException(e);
}
}
+ public static Label parseAbsoluteUnchecked(String absName) {
+ return parseAbsoluteUnchecked(absName, true);
+ }
+
/**
* Factory for Labels from separate components.
*
@@ -243,7 +270,7 @@
}
private Object writeReplace() {
- return new LabelSerializationProxy(toString());
+ return new LabelSerializationProxy(getUnambiguousCanonicalForm());
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
@@ -322,7 +349,7 @@
/**
* Renders this label in canonical form.
*
- * <p>invariant: {@code parseAbsolute(x.toString()).equals(x)}
+ * <p>invariant: {@code parseAbsolute(x.toString(), false).equals(x)}
*/
@Override
public String toString() {
@@ -332,21 +359,46 @@
/**
* Renders this label in canonical form.
*
- * <p>invariant: {@code parseAbsolute(x.toString()).equals(x)}
+ * <p>invariant: {@code parseAbsolute(x.getCanonicalForm(), false).equals(x)}
*/
public String getCanonicalForm() {
+ return getDefaultCanonicalForm();
+ }
+
+ public String getUnambiguousCanonicalForm() {
return packageIdentifier.getRepository() + "//" + packageIdentifier.getPackageFragment()
+ ":" + name;
}
/**
+ * Renders this label in canonical form, except with labels in the main and default
+ * repositories conflated.
+ */
+ public String getDefaultCanonicalForm() {
+ String repository;
+ if (packageIdentifier.getRepository().isMain()) {
+ repository = "";
+ } else {
+ repository = packageIdentifier.getRepository().getName();
+ }
+ return repository + "//" + packageIdentifier.getPackageFragment()
+ + ":" + name;
+ }
+
+ /**
* Renders this label in shorthand form.
*
* <p>Labels with canonical form {@code //foo/bar:bar} have the shorthand form {@code //foo/bar}.
* All other labels have identical shorthand and canonical forms.
*/
public String toShorthandString() {
- return packageIdentifier.getRepository() + (getPackageFragment().getBaseName().equals(name)
+ String repository;
+ if (packageIdentifier.getRepository().isMain()) {
+ repository = "";
+ } else {
+ repository = packageIdentifier.getRepository().getName();
+ }
+ return repository + (getPackageFragment().getBaseName().equals(name)
? "//" + getPackageFragment()
: toString());
}
@@ -391,7 +443,7 @@
}
if (LabelValidator.isAbsolute(relName)) {
- return resolveRepositoryRelative(parseAbsolute(relName));
+ return resolveRepositoryRelative(parseAbsolute(relName, false));
} else if (relName.equals(":")) {
throw new LabelSyntaxException("':' is not a valid package-relative label");
} else if (relName.charAt(0) == ':') {
@@ -407,11 +459,12 @@
* <p>This is necessary so that dependency edges in remote repositories do not need to explicitly
* mention their repository name. Otherwise, referring to e.g. <code>//a:b</code> in a remote
* repository would point back to the main repository, which is usually not what is intended.
+ *
+ * <p>The return value will not be in the default repository.
*/
public Label resolveRepositoryRelative(Label relative) {
if (packageIdentifier.getRepository().isDefault()
- || !relative.packageIdentifier.getRepository().isDefault()
- || ABSOLUTE_PACKAGE_NAMES.contains(relative.getPackageIdentifier().getPackageFragment())) {
+ || !relative.packageIdentifier.getRepository().isDefault()) {
return relative;
} else {
try {
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/LabelSerializationProxy.java b/src/main/java/com/google/devtools/build/lib/cmdline/LabelSerializationProxy.java
index 5d7e6d0..c28e185 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/LabelSerializationProxy.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/LabelSerializationProxy.java
@@ -44,6 +44,6 @@
}
private Object readResolve() {
- return Label.parseAbsoluteUnchecked(labelString);
+ return Label.parseAbsoluteUnchecked(labelString, false);
}
}
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 4c995eb..69fb31a 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
@@ -60,19 +60,22 @@
}
}
- // Temporary factory for identifiers without explicit repositories.
- // TODO(bazel-team): remove all usages of this.
+ /**
+ * This is only used by legacy callers. Actually creates the package identifier in the main
+ * repository, not the default one.
+ */
+ // TODO(lberki): Remove this method.
+ @Deprecated
public static PackageIdentifier createInDefaultRepo(String name) {
- return createInDefaultRepo(new PathFragment(name));
+ return create(MAIN_REPOSITORY_NAME, new PathFragment(name));
}
- public static PackageIdentifier createInDefaultRepo(PathFragment name) {
- try {
- return create(DEFAULT_REPOSITORY, name);
- } catch (LabelSyntaxException e) {
- throw new IllegalArgumentException("could not create package identifier for " + name
- + ": " + e.getMessage());
- }
+ public static PackageIdentifier createInMainRepo(String name) {
+ return createInMainRepo(new PathFragment(name));
+ }
+
+ public static PackageIdentifier createInMainRepo(PathFragment name) {
+ return create(MAIN_REPOSITORY_NAME, name);
}
/**
@@ -136,6 +139,14 @@
return repository.getPathFragment().getRelative(pkgName);
}
+ public PackageIdentifier makeAbsolute() {
+ if (!repository.isDefault()) {
+ return this;
+ }
+
+ return create(MAIN_REPOSITORY_NAME, pkgName);
+ }
+
/**
* Returns the name of this package.
*
@@ -145,7 +156,7 @@
*/
@Override
public String toString() {
- return (repository.isDefault() ? "" : repository + "//") + pkgName;
+ return (repository.isDefault() || repository.isMain() ? "" : repository + "//") + pkgName;
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryName.java b/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryName.java
index 6f5811d..9a4e5a7 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryName.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryName.java
@@ -178,6 +178,13 @@
}
/**
+ * Returns if this is the default repository, that is, {@link #name} is "@".
+ */
+ public boolean isMain() {
+ return name.equals("@");
+ }
+
+ /**
* Returns the repository name, with leading "{@literal @}" (or "" for the default repository).
*/
// TODO(bazel-team): Use this over toString()- easier to track its usage.
@@ -189,7 +196,7 @@
* Returns the path at which this repository is mapped within the exec root.
*/
public PathFragment getPathFragment() {
- return isDefault() || this.equals(PackageIdentifier.MAIN_REPOSITORY_NAME)
+ return isDefault() || isMain()
? PathFragment.EMPTY_FRAGMENT
: new PathFragment(Label.EXTERNAL_PATH_PREFIX).getRelative(strippedName());
}
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
index f8c1a83..c405efa 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
@@ -270,7 +270,7 @@
Preconditions.checkArgument(excludedSubdirectories.isEmpty(),
"Target pattern \"%s\" of type %s cannot be evaluated with excluded subdirectories: %s.",
getOriginalPattern(), getType(), excludedSubdirectories);
- if (resolver.isPackage(PackageIdentifier.createInDefaultRepo(path))) {
+ if (resolver.isPackage(PackageIdentifier.createInMainRepo(path))) {
// User has specified a package name. lookout for default target.
callback.process(resolver.getExplicitTarget(label("//" + path)).getTargets());
} else {
@@ -281,7 +281,7 @@
// first BUILD file is found (i.e. longest prefix match).
for (int i = pieces.size() - 1; i > 0; i--) {
String packageName = SLASH_JOINER.join(pieces.subList(0, i));
- if (resolver.isPackage(PackageIdentifier.createInDefaultRepo(packageName))) {
+ if (resolver.isPackage(PackageIdentifier.createInMainRepo(packageName))) {
String targetName = SLASH_JOINER.join(pieces.subList(i, pieces.size()));
callback.process(
resolver
@@ -304,7 +304,7 @@
public PackageIdentifier getDirectory() {
int lastSlashIndex = path.lastIndexOf('/');
// The package name cannot be illegal because we verified it during target parsing
- return PackageIdentifier.createInDefaultRepo(
+ return PackageIdentifier.createInMainRepo(
lastSlashIndex < 0 ? "" : path.substring(0, lastSlashIndex));
}
@@ -591,7 +591,7 @@
String originalPattern = pattern;
final boolean includesRepo = pattern.startsWith("@");
- RepositoryName repository = PackageIdentifier.DEFAULT_REPOSITORY_NAME;
+ RepositoryName repository = null;
if (includesRepo) {
int pkgStart = pattern.indexOf("//");
if (pkgStart < 0) {
@@ -638,6 +638,14 @@
+ "' should not end in a slash");
}
+ if (repository == null) {
+ if (packagePart.startsWith(Label.EXTERNAL_PACKAGE_NAME.toString())) {
+ repository = PackageIdentifier.DEFAULT_REPOSITORY_NAME;
+ } else {
+ repository = PackageIdentifier.MAIN_REPOSITORY_NAME;
+ }
+ }
+
if (packagePart.endsWith("/...")) {
String realPackagePart = removeSuffix(packagePart, "/...");
PackageIdentifier packageIdentifier;