Add the LABEL_KEYED_STRING_DICT type for attributes.
This enables both native and Skylark rules to declare attributes which
have labels/Targets as keys, and have string values.
--
PiperOrigin-RevId: 148365033
MOS_MIGRATED_REVID=148365033
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java b/src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java
index 44623f7..4a1c283 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java
@@ -17,6 +17,7 @@
import static com.google.devtools.build.lib.packages.BuildType.FILESET_ENTRY_LIST;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_DICT_UNARY;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_KEYED_STRING_DICT;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
import static com.google.devtools.build.lib.packages.BuildType.NODEP_LABEL;
@@ -45,6 +46,7 @@
import com.google.devtools.build.lib.query2.proto.proto2api.Build.Attribute.SelectorEntry.Builder;
import com.google.devtools.build.lib.query2.proto.proto2api.Build.Attribute.Tristate;
import com.google.devtools.build.lib.query2.proto.proto2api.Build.LabelDictUnaryEntry;
+import com.google.devtools.build.lib.query2.proto.proto2api.Build.LabelKeyedStringDictEntry;
import com.google.devtools.build.lib.query2.proto.proto2api.Build.LabelListDictEntry;
import com.google.devtools.build.lib.query2.proto.proto2api.Build.StringDictEntry;
import com.google.devtools.build.lib.query2.proto.proto2api.Build.StringDictUnaryEntry;
@@ -61,7 +63,15 @@
private static final ImmutableSet<Type<?>> depTypes =
ImmutableSet.<Type<?>>of(
- STRING, LABEL, OUTPUT, STRING_LIST, LABEL_LIST, OUTPUT_LIST, DISTRIBUTIONS);
+ STRING,
+ LABEL,
+ OUTPUT,
+ STRING_LIST,
+ LABEL_LIST,
+ LABEL_DICT_UNARY,
+ LABEL_KEYED_STRING_DICT,
+ OUTPUT_LIST,
+ DISTRIBUTIONS);
private static final ImmutableSet<Type<?>> noDepTypes =
ImmutableSet.<Type<?>>of(NODEP_LABEL_LIST, NODEP_LABEL);
@@ -230,6 +240,15 @@
.setValue(dictEntry.getValue().toString());
builder.addLabelDictUnaryValue(entry);
}
+ } else if (type == LABEL_KEYED_STRING_DICT) {
+ Map<Label, String> dict = (Map<Label, String>) value;
+ for (Map.Entry<Label, String> dictEntry : dict.entrySet()) {
+ LabelKeyedStringDictEntry.Builder entry =
+ LabelKeyedStringDictEntry.newBuilder()
+ .setKey(dictEntry.getKey().toString())
+ .setValue(dictEntry.getValue());
+ builder.addLabelKeyedStringDictValue(entry);
+ }
} else if (type == FILESET_ENTRY_LIST) {
List<FilesetEntry> filesetEntries = (List<FilesetEntry>) value;
for (FilesetEntry filesetEntry : filesetEntries) {
@@ -302,6 +321,8 @@
void addLabelDictUnaryValue(LabelDictUnaryEntry.Builder builder);
+ void addLabelKeyedStringDictValue(LabelKeyedStringDictEntry.Builder builder);
+
void addLabelListDictValue(LabelListDictEntry.Builder builder);
void addIntListValue(int i);
@@ -362,6 +383,11 @@
}
@Override
+ public void addLabelKeyedStringDictValue(LabelKeyedStringDictEntry.Builder builder) {
+ attributeBuilder.addLabelKeyedStringDictValue(builder);
+ }
+
+ @Override
public void addLabelListDictValue(LabelListDictEntry.Builder builder) {
attributeBuilder.addLabelListDictValue(builder);
}
@@ -488,6 +514,11 @@
}
@Override
+ public void addLabelKeyedStringDictValue(LabelKeyedStringDictEntry.Builder builder) {
+ selectorEntryBuilder.addLabelKeyedStringDictValue(builder);
+ }
+
+ @Override
public void addLabelListDictValue(LabelListDictEntry.Builder builder) {
selectorEntryBuilder.addLabelListDictValue(builder);
}
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 051b7e0..263fe63 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.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.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 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 @@
}
/**
+ * 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}.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ProtoUtils.java b/src/main/java/com/google/devtools/build/lib/packages/ProtoUtils.java
index c34ad2e..7c0335d 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/ProtoUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/ProtoUtils.java
@@ -18,6 +18,7 @@
import static com.google.devtools.build.lib.packages.BuildType.FILESET_ENTRY_LIST;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_DICT_UNARY;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_KEYED_STRING_DICT;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
import static com.google.devtools.build.lib.packages.BuildType.NODEP_LABEL;
@@ -68,6 +69,7 @@
.put(TRISTATE, Discriminator.TRISTATE)
.put(INTEGER_LIST, Discriminator.INTEGER_LIST)
.put(STRING_DICT_UNARY, Discriminator.STRING_DICT_UNARY)
+ .put(LABEL_KEYED_STRING_DICT, Discriminator.LABEL_KEYED_STRING_DICT)
.build();
/** Returns the {@link Discriminator} value corresponding to the provided {@link Type}. */