diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/packages/BuildType.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/packages/BuildType.java | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java index 051b7e033f..263fe63e79 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java +++ b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.packages; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -22,6 +23,7 @@ import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.packages.License.DistributionType; import com.google.devtools.build.lib.packages.License.LicenseParsingException; +import com.google.devtools.build.lib.syntax.Printer; import com.google.devtools.build.lib.syntax.Runtime; import com.google.devtools.build.lib.syntax.SelectorValue; import com.google.devtools.build.lib.syntax.Type; @@ -29,6 +31,7 @@ import com.google.devtools.build.lib.syntax.Type.ConversionException; import com.google.devtools.build.lib.syntax.Type.DictType; import com.google.devtools.build.lib.syntax.Type.LabelClass; import com.google.devtools.build.lib.syntax.Type.ListType; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -55,6 +58,11 @@ public final class BuildType { public static final DictType<String, Label> LABEL_DICT_UNARY = DictType.create( Type.STRING, LABEL); /** + * The type of a dictionary keyed by {@linkplain #LABEL labels} with string values. + */ + public static final DictType<Label, String> LABEL_KEYED_STRING_DICT = + LabelKeyedDictType.create(Type.STRING); + /** * The type of a list of {@linkplain #LABEL labels}. */ public static final ListType<Label> LABEL_LIST = ListType.create(LABEL); @@ -247,6 +255,70 @@ public final class BuildType { } /** + * Dictionary type specialized for label keys, which is able to detect collisions caused by the + * fact that labels have multiple equivalent representations in Skylark code. + */ + private static class LabelKeyedDictType<ValueT> extends DictType<Label, ValueT> { + private LabelKeyedDictType(Type<ValueT> valueType) { + super(LABEL, valueType, LabelClass.DEPENDENCY); + } + + public static <ValueT> LabelKeyedDictType<ValueT> create(Type<ValueT> valueType) { + Preconditions.checkArgument( + valueType.getLabelClass() == LabelClass.NONE + || valueType.getLabelClass() == LabelClass.DEPENDENCY, + "Values associated with label keys must not be labels themselves."); + return new LabelKeyedDictType<>(valueType); + } + + @Override + public Map<Label, ValueT> convert(Object x, Object what, Object context) + throws ConversionException { + Map<Label, ValueT> result = super.convert(x, what, context); + // The input is known to be a map because super.convert succeded; otherwise, a + // ConversionException would have been thrown. + Map<?, ?> input = (Map<?, ?>) x; + + if (input.size() == result.size()) { + // No collisions found. Exit early. + return result; + } + // Look for collisions in order to produce a nicer error message. + Map<Label, List<Object>> convertedFrom = new LinkedHashMap<>(); + for (Object original : input.keySet()) { + Label label = LABEL.convert(original, what, context); + if (!convertedFrom.containsKey(label)) { + convertedFrom.put(label, new ArrayList<Object>()); + } + convertedFrom.get(label).add(original); + } + StringBuilder errorMessage = new StringBuilder(); + errorMessage.append("duplicate labels"); + if (what != null) { + errorMessage.append(" in ").append(what); + } + errorMessage.append(':'); + boolean isFirstEntry = true; + for (Map.Entry<Label, List<Object>> entry : convertedFrom.entrySet()) { + if (entry.getValue().size() == 1) { + continue; + } + if (isFirstEntry) { + isFirstEntry = false; + } else { + errorMessage.append(','); + } + errorMessage.append(' '); + errorMessage.append(entry.getKey()); + errorMessage.append(" (as "); + Printer.write(errorMessage, entry.getValue()); + errorMessage.append(')'); + } + throw new ConversionException(errorMessage.toString()); + } + } + + /** * Like Label, LicenseType is a derived type, which is declared specially * in order to allow syntax validation. It represents the licenses, as * described in {@ref License}. |