aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java
diff options
context:
space:
mode:
authorGravatar Greg Estren <gregce@google.com>2015-03-24 21:09:39 +0000
committerGravatar Lukacs Berki <lberki@google.com>2015-03-25 10:31:02 +0000
commit507a5ff1b22f3398d6c9d8e35b8f6ff3ab40637f (patch)
tree338048fb9697c81083e93c33db29f75dfba20fbd /src/main/java
parent85c765db4fb580e77b467d3e15fc0482d4ec5600 (diff)
Merge PackageSerializer's and ProtoOutputFormatter's duplicated
serialization logic. Among other things, this fixes an out-of-sync bug where ProtoOutputFormatter's version knew how to handle configurable attributes while PackageSerializer's version crashed. The merged logic preserves ProtoOutputFormatter's semantics: configurable attributes work, but their values are merged together into a flattened list, so the original select structure can't be reproduced later. -- MOS_MIGRATED_REVID=89435116
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java275
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/RawAttributeMapper.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/query2/output/ProtoOutputFormatter.java282
4 files changed, 180 insertions, 399 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java b/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java
index 83b8a64730..d7008eddb3 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java
@@ -34,6 +34,7 @@ import static com.google.devtools.build.lib.packages.Type.STRING_LIST_DICT;
import static com.google.devtools.build.lib.packages.Type.TRISTATE;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.License.DistributionType;
@@ -46,6 +47,7 @@ import com.google.devtools.build.lib.syntax.Label;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -110,16 +112,10 @@ public class PackageSerializer {
result.setRuleClass(rule.getRuleClass());
result.setParseableLocation(serializeLocation(rule.getLocation()));
for (Attribute attribute : rule.getAttributes()) {
- Object value = attribute.getName().equals("visibility")
- ? rule.getVisibility().getDeclaredLabels()
- // TODO(bazel-team): support configurable attributes. AggregatingAttributeMapper
- // may make more sense here. Computed defaults may add complications.
- : RawAttributeMapper.of(rule).get(attribute.getName(), attribute.getType());
- if (value != null) {
- PackageSerializer.addAttributeToProto(result, attribute, value,
- rule.getAttributeLocation(attribute.getName()),
- rule.isAttributeValueExplicitlySpecified(attribute),
- true);
+ if (!RawAttributeMapper.of(rule).isNull(attribute.getName(), attribute.getType())) {
+ PackageSerializer.addAttributeToProto(result, attribute,
+ getAttributeValues(rule, attribute), rule.getAttributeLocation(attribute.getName()),
+ rule.isAttributeValueExplicitlySpecified(attribute), true);
}
}
@@ -180,7 +176,7 @@ public class PackageSerializer {
case PROGRESS:
kind = Build.Event.EventKind.PROGRESS;
break;
- default: throw new IllegalStateException();
+ default: throw new IllegalArgumentException("unexpected event type: " + event.getKind());
}
result.setKind(kind);
@@ -267,29 +263,48 @@ public class PackageSerializer {
}
/**
+ * Returns the possible values of the specified attribute in the specified rule. For
+ * non-configured attributes, this is a single value. For configurable attributes, this
+ * may be multiple values.
+ */
+ public static Iterable<Object> getAttributeValues(Rule rule, Attribute attr) {
+ List<Object> values = new LinkedList<>(); // Not an ImmutableList: may host null values.
+
+ if (attr.getName().equals("visibility")) {
+ values.add(rule.getVisibility().getDeclaredLabels());
+ } else {
+ for (Object o :
+ AggregatingAttributeMapper.of(rule).visitAttribute(attr.getName(), attr.getType())) {
+ values.add(o);
+ }
+ }
+
+ return values;
+ }
+
+ /**
* Adds the serialized version of the specified attribute to the specified message.
*
- * @param result the message to amend
+ * @param rulePb the message to amend
* @param attr the attribute to add
- * @param value the value of the attribute
+ * @param values the possible values of the attribute (can be a multi-value list for
+ * configurable attributes)
* @param location the location of the attribute in the source file
* @param explicitlySpecified whether the attribute was explicitly specified or not
* @param includeGlobs add glob expression for attributes that contain them
*/
- // TODO(bazel-team): This is a copy of the code in ProtoOutputFormatter.
@SuppressWarnings("unchecked")
public static void addAttributeToProto(
- Build.Rule.Builder result, Attribute attr, Object value, Location location,
- Boolean explicitlySpecified, boolean includeGlobs) {
+ Build.Rule.Builder rulePb, Attribute attr, Iterable<Object> values,
+ Location location, Boolean explicitlySpecified, boolean includeGlobs) {
// Get the attribute type. We need to convert and add appropriately
com.google.devtools.build.lib.packages.Type<?> type = attr.getType();
Build.Attribute.Builder attrPb = Build.Attribute.newBuilder();
// Set the type, name and source
- Build.Attribute.Discriminator attributeProtoType = ProtoUtils.getDiscriminatorFromType(type);
attrPb.setName(attr.getName());
- attrPb.setType(attributeProtoType);
+ attrPb.setType(ProtoUtils.getDiscriminatorFromType(type));
if (location != null) {
attrPb.setParseableLocation(serializeLocation(location));
@@ -299,6 +314,22 @@ public class PackageSerializer {
attrPb.setExplicitlySpecified(explicitlySpecified);
}
+ // Convenience binding for single-value attributes. Because those attributes can only
+ // have a single value, when we encounter configurable versions of them we need to
+ // react somehow to having multiple possible values to report. We currently just
+ // refrain from setting *any* value in that scenario. This variable is set to null
+ // to indicate that.
+ //
+ // For example, for "linkstatic = select({':foo': 0, ':bar': 1})", "values" will contain [0, 1].
+ // Since linkstatic is a single-value string element, its proto field (string_value) can't
+ // store both values. Since no use case today actually needs this, we just skip it.
+ //
+ // TODO(bazel-team): support this properly. This will require syntactic change to build.proto
+ // (or reinterpretation of its current fields).
+ Object singleAttributeValue = Iterables.size(values) == 1
+ ? Iterables.getOnlyElement(values)
+ : null;
+
/*
* Set the appropriate type and value. Since string and string list store
* values for multiple types, use the toString() method on the objects
@@ -306,142 +337,172 @@ public class PackageSerializer {
* both an integer and string representation.
*/
if (type == INTEGER) {
- attrPb.setIntValue((Integer) value);
+ if (singleAttributeValue != null) {
+ attrPb.setIntValue((Integer) singleAttributeValue);
+ }
} else if (type == STRING || type == LABEL || type == NODEP_LABEL || type == OUTPUT) {
- attrPb.setStringValue(value.toString());
+ if (singleAttributeValue != null) {
+ attrPb.setStringValue(singleAttributeValue.toString());
+ }
} else if (type == STRING_LIST || type == LABEL_LIST || type == NODEP_LABEL_LIST
|| type == OUTPUT_LIST || type == DISTRIBUTIONS) {
- Collection<?> values = (Collection<?>) value;
- for (Object entry : values) {
- attrPb.addStringListValue(entry.toString());
+ for (Object value : values) {
+ for (Object entry : (Collection<?>) value) {
+ attrPb.addStringListValue(entry.toString());
+ }
}
} else if (type == INTEGER_LIST) {
- Collection<Integer> values = (Collection<Integer>) value;
- for (Integer entry : values) {
- attrPb.addIntListValue(entry);
+ for (Object value : values) {
+ for (Integer entry : (Collection<Integer>) value) {
+ attrPb.addIntListValue(entry);
+ }
}
} else if (type == BOOLEAN) {
- if ((Boolean) value) {
- attrPb.setStringValue("true");
- attrPb.setBooleanValue(true);
- } else {
- attrPb.setStringValue("false");
- attrPb.setBooleanValue(false);
+ if (singleAttributeValue != null) {
+ if ((Boolean) singleAttributeValue) {
+ attrPb.setStringValue("true");
+ attrPb.setBooleanValue(true);
+ } else {
+ attrPb.setStringValue("false");
+ attrPb.setBooleanValue(false);
+ }
+ // This maintains partial backward compatibility for external users of the
+ // protobuf that were expecting an integer field and not a true boolean.
+ attrPb.setIntValue((Boolean) singleAttributeValue ? 1 : 0);
}
- // This maintains partial backward compatibility for external users of the
- // protobuf that were expecting an integer field and not a true boolean.
- attrPb.setIntValue((Boolean) value ? 1 : 0);
} else if (type == TRISTATE) {
- switch ((TriState) value) {
- case AUTO:
+ if (singleAttributeValue != null) {
+ switch ((TriState) singleAttributeValue) {
+ case AUTO:
attrPb.setIntValue(-1);
attrPb.setStringValue("auto");
attrPb.setTristateValue(Build.Attribute.Tristate.AUTO);
break;
- case NO:
+ case NO:
attrPb.setIntValue(0);
attrPb.setStringValue("no");
attrPb.setTristateValue(Build.Attribute.Tristate.NO);
break;
- case YES:
+ case YES:
attrPb.setIntValue(1);
attrPb.setStringValue("yes");
attrPb.setTristateValue(Build.Attribute.Tristate.YES);
break;
+ default:
+ throw new AssertionError("Expected AUTO/NO/YES to cover all possible cases");
+ }
}
} else if (type == LICENSE) {
- License license = (License) value;
- Build.License.Builder licensePb = Build.License.newBuilder();
- for (License.LicenseType licenseType : license.getLicenseTypes()) {
- licensePb.addLicenseType(licenseType.toString());
- }
- for (Label exception : license.getExceptions()) {
- licensePb.addException(exception.toString());
+ if (singleAttributeValue != null) {
+ License license = (License) singleAttributeValue;
+ Build.License.Builder licensePb = Build.License.newBuilder();
+ for (License.LicenseType licenseType : license.getLicenseTypes()) {
+ licensePb.addLicenseType(licenseType.toString());
+ }
+ for (Label exception : license.getExceptions()) {
+ licensePb.addException(exception.toString());
+ }
+ attrPb.setLicense(licensePb);
}
- attrPb.setLicense(licensePb);
} else if (type == STRING_DICT) {
+ // TODO(bazel-team): support better de-duping here and in other dictionaries.
+ for (Object value : values) {
Map<String, String> dict = (Map<String, String>) value;
- for (Map.Entry<String, String> dictEntry : dict.entrySet()) {
- Build.StringDictEntry entry = Build.StringDictEntry.newBuilder()
- .setKey(dictEntry.getKey())
- .setValue(dictEntry.getValue())
- .build();
- attrPb.addStringDictValue(entry);
+ for (Map.Entry<String, String> keyValueList : dict.entrySet()) {
+ Build.StringDictEntry entry = Build.StringDictEntry.newBuilder()
+ .setKey(keyValueList.getKey())
+ .setValue(keyValueList.getValue())
+ .build();
+ attrPb.addStringDictValue(entry);
+ }
}
} else if (type == STRING_DICT_UNARY) {
- Map<String, String> dict = (Map<String, String>) value;
- for (Map.Entry<String, String> dictEntry : dict.entrySet()) {
- Build.StringDictUnaryEntry entry = Build.StringDictUnaryEntry.newBuilder()
- .setKey(dictEntry.getKey())
- .setValue(dictEntry.getValue())
- .build();
- attrPb.addStringDictUnaryValue(entry);
+ for (Object value : values) {
+ Map<String, String> dict = (Map<String, String>) value;
+ for (Map.Entry<String, String> dictEntry : dict.entrySet()) {
+ Build.StringDictUnaryEntry entry = Build.StringDictUnaryEntry.newBuilder()
+ .setKey(dictEntry.getKey())
+ .setValue(dictEntry.getValue())
+ .build();
+ attrPb.addStringDictUnaryValue(entry);
+ }
}
} else if (type == STRING_LIST_DICT) {
- Map<String, List<String>> dict = (Map<String, List<String>>) value;
- for (Map.Entry<String, List<String>> dictEntry : dict.entrySet()) {
- Build.StringListDictEntry.Builder entry = Build.StringListDictEntry.newBuilder()
- .setKey(dictEntry.getKey());
- for (Object dictEntryValue : dictEntry.getValue()) {
- entry.addValue(dictEntryValue.toString());
+ for (Object value : values) {
+ Map<String, List<String>> dict = (Map<String, List<String>>) value;
+ for (Map.Entry<String, List<String>> dictEntry : dict.entrySet()) {
+ Build.StringListDictEntry.Builder entry = Build.StringListDictEntry.newBuilder()
+ .setKey(dictEntry.getKey());
+ for (Object dictEntryValue : dictEntry.getValue()) {
+ entry.addValue(dictEntryValue.toString());
+ }
+ attrPb.addStringListDictValue(entry);
}
- attrPb.addStringListDictValue(entry);
}
} else if (type == LABEL_LIST_DICT) {
- Map<String, List<Label>> dict = (Map<String, List<Label>>) value;
- for (Map.Entry<String, List<Label>> dictEntry : dict.entrySet()) {
- Build.LabelListDictEntry.Builder entry = Build.LabelListDictEntry.newBuilder()
- .setKey(dictEntry.getKey());
- for (Object dictEntryValue : dictEntry.getValue()) {
- entry.addValue(dictEntryValue.toString());
+ for (Object value : values) {
+ Map<String, List<Label>> dict = (Map<String, List<Label>>) value;
+ for (Map.Entry<String, List<Label>> dictEntry : dict.entrySet()) {
+ Build.LabelListDictEntry.Builder entry = Build.LabelListDictEntry.newBuilder()
+ .setKey(dictEntry.getKey());
+ for (Object dictEntryValue : dictEntry.getValue()) {
+ entry.addValue(dictEntryValue.toString());
+ }
+ attrPb.addLabelListDictValue(entry);
}
- attrPb.addLabelListDictValue(entry);
}
} else if (type == FILESET_ENTRY_LIST) {
- List<FilesetEntry> filesetEntries = (List<FilesetEntry>) value;
- for (FilesetEntry filesetEntry : filesetEntries) {
- Build.FilesetEntry.Builder filesetEntryPb = Build.FilesetEntry.newBuilder()
- .setSource(filesetEntry.getSrcLabel().toString())
- .setDestinationDirectory(filesetEntry.getDestDir().getPathString())
- .setSymlinkBehavior(symlinkBehaviorToPb(filesetEntry.getSymlinkBehavior()))
- .setStripPrefix(filesetEntry.getStripPrefix())
- .setFilesPresent(filesetEntry.getFiles() != null);
-
- if (filesetEntry.getFiles() != null) {
- for (Label file : filesetEntry.getFiles()) {
- filesetEntryPb.addFile(file.toString());
+ for (Object value : values) {
+ List<FilesetEntry> filesetEntries = (List<FilesetEntry>) value;
+ for (FilesetEntry filesetEntry : filesetEntries) {
+ Build.FilesetEntry.Builder filesetEntryPb = Build.FilesetEntry.newBuilder()
+ .setSource(filesetEntry.getSrcLabel().toString())
+ .setDestinationDirectory(filesetEntry.getDestDir().getPathString())
+ .setSymlinkBehavior(symlinkBehaviorToPb(filesetEntry.getSymlinkBehavior()))
+ .setStripPrefix(filesetEntry.getStripPrefix())
+ .setFilesPresent(filesetEntry.getFiles() != null);
+
+ if (filesetEntry.getFiles() != null) {
+ for (Label file : filesetEntry.getFiles()) {
+ filesetEntryPb.addFile(file.toString());
+ }
}
- }
- if (filesetEntry.getExcludes() != null) {
- for (String exclude : filesetEntry.getExcludes()) {
- filesetEntryPb.addExclude(exclude);
+ if (filesetEntry.getExcludes() != null) {
+ for (String exclude : filesetEntry.getExcludes()) {
+ filesetEntryPb.addExclude(exclude);
+ }
}
- }
- attrPb.addFilesetListValue(filesetEntryPb);
+ attrPb.addFilesetListValue(filesetEntryPb);
+ }
}
} else {
- throw new IllegalStateException("Unknown type: " + type);
+ throw new AssertionError("Unknown type: " + type);
}
- if (includeGlobs && value instanceof GlobList<?>) {
- GlobList<?> globList = (GlobList<?>) value;
-
- for (GlobCriteria criteria : globList.getCriteria()) {
- Build.GlobCriteria.Builder criteriaPb = Build.GlobCriteria.newBuilder();
- criteriaPb.setGlob(criteria.isGlob());
- for (String include : criteria.getIncludePatterns()) {
- criteriaPb.addInclude(include);
- }
- for (String exclude : criteria.getExcludePatterns()) {
- criteriaPb.addExclude(exclude);
+ if (includeGlobs) {
+ for (Object value : values) {
+ if (value instanceof GlobList<?>) {
+ GlobList<?> globList = (GlobList<?>) value;
+
+ for (GlobCriteria criteria : globList.getCriteria()) {
+ Build.GlobCriteria.Builder criteriaPb = Build.GlobCriteria.newBuilder()
+ .setGlob(criteria.isGlob());
+ for (String include : criteria.getIncludePatterns()) {
+ criteriaPb.addInclude(include);
+ }
+ for (String exclude : criteria.getExcludePatterns()) {
+ criteriaPb.addExclude(exclude);
+ }
+
+ attrPb.addGlobCriteria(criteriaPb);
+ }
}
-
- attrPb.addGlobCriteria(criteriaPb);
}
}
- result.addAttribute(attrPb);
+
+ rulePb.addAttribute(attrPb);
}
// This is needed because I do not want to use the SymlinkBehavior from the
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RawAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/RawAttributeMapper.java
index 7de23d7c19..6cc12d03dd 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RawAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RawAttributeMapper.java
@@ -88,6 +88,13 @@ public class RawAttributeMapper extends AbstractAttributeMapper {
}
/**
+ * Returns true if this attribute has a null value.
+ */
+ public <T> boolean isNull(String attributeName, Type<T> type) {
+ return !isConfigurable(attributeName, type) && (get(attributeName, type) == null);
+ }
+
+ /**
* If the attribute is configurable for this rule instance, returns its configuration
* keys. Else returns an empty list.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java b/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
index 9272e1d285..e8247c540a 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
@@ -21,8 +21,8 @@ import com.google.common.collect.Sets;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.graph.Digraph;
import com.google.devtools.build.lib.graph.Node;
-import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.PackageSerializer;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.syntax.EvalUtils;
@@ -40,7 +40,6 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -450,10 +449,6 @@ public abstract class OutputFormatter implements Serializable {
* non-configured attributes, this is a single value. For configurable attributes, this
* may be multiple values.
*
- * <p>This is needed because the visibility attribute is replaced with an empty list
- * during package loading if it is public or private in order not to visit
- * the package called 'visibility'.
- *
* @return a pair, where the first value is the set of possible values and the
* second is an enum that tells where the values come from (declared on the
* rule, declared as a package level default or a
@@ -461,11 +456,9 @@ public abstract class OutputFormatter implements Serializable {
*/
protected static Pair<Iterable<Object>, AttributeValueSource> getAttributeValues(
Rule rule, Attribute attr) {
- List<Object> values = new LinkedList<>(); // Not an ImmutableList: may host null values.
AttributeValueSource source;
if (attr.getName().equals("visibility")) {
- values.add(rule.getVisibility().getDeclaredLabels());
if (rule.isVisibilitySpecified()) {
source = AttributeValueSource.RULE;
} else if (rule.getPackage().isDefaultVisibilitySet()) {
@@ -474,15 +467,11 @@ public abstract class OutputFormatter implements Serializable {
source = AttributeValueSource.DEFAULT;
}
} else {
- for (Object o :
- AggregatingAttributeMapper.of(rule).visitAttribute(attr.getName(), attr.getType())) {
- values.add(o);
- }
source = rule.isAttributeValueExplicitlySpecified(attr)
? AttributeValueSource.RULE : AttributeValueSource.DEFAULT;
}
- return Pair.of((Iterable<Object>) values, source);
+ return Pair.of(PackageSerializer.getAttributeValues(rule, attr), source);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/query2/output/ProtoOutputFormatter.java b/src/main/java/com/google/devtools/build/lib/query2/output/ProtoOutputFormatter.java
index 0e22559d38..29f07ec804 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/output/ProtoOutputFormatter.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/output/ProtoOutputFormatter.java
@@ -13,57 +13,29 @@
// limitations under the License.
package com.google.devtools.build.lib.query2.output;
-import static com.google.devtools.build.lib.packages.Type.BOOLEAN;
-import static com.google.devtools.build.lib.packages.Type.DISTRIBUTIONS;
-import static com.google.devtools.build.lib.packages.Type.FILESET_ENTRY_LIST;
-import static com.google.devtools.build.lib.packages.Type.INTEGER;
-import static com.google.devtools.build.lib.packages.Type.INTEGER_LIST;
-import static com.google.devtools.build.lib.packages.Type.LABEL;
-import static com.google.devtools.build.lib.packages.Type.LABEL_LIST;
-import static com.google.devtools.build.lib.packages.Type.LABEL_LIST_DICT;
-import static com.google.devtools.build.lib.packages.Type.LICENSE;
-import static com.google.devtools.build.lib.packages.Type.NODEP_LABEL;
-import static com.google.devtools.build.lib.packages.Type.NODEP_LABEL_LIST;
-import static com.google.devtools.build.lib.packages.Type.OUTPUT;
-import static com.google.devtools.build.lib.packages.Type.OUTPUT_LIST;
-import static com.google.devtools.build.lib.packages.Type.STRING;
-import static com.google.devtools.build.lib.packages.Type.STRING_DICT;
-import static com.google.devtools.build.lib.packages.Type.STRING_DICT_UNARY;
-import static com.google.devtools.build.lib.packages.Type.STRING_LIST;
-import static com.google.devtools.build.lib.packages.Type.STRING_LIST_DICT;
-import static com.google.devtools.build.lib.packages.Type.TRISTATE;
import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.GENERATED_FILE;
import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.PACKAGE_GROUP;
import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.RULE;
import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.SOURCE_FILE;
-import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.graph.Digraph;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.InputFile;
-import com.google.devtools.build.lib.packages.License;
import com.google.devtools.build.lib.packages.OutputFile;
import com.google.devtools.build.lib.packages.PackageGroup;
+import com.google.devtools.build.lib.packages.PackageSerializer;
import com.google.devtools.build.lib.packages.ProtoUtils;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Target;
-import com.google.devtools.build.lib.packages.TriState;
import com.google.devtools.build.lib.query2.FakeSubincludeTarget;
import com.google.devtools.build.lib.query2.output.OutputFormatter.UnorderedFormatter;
import com.google.devtools.build.lib.query2.proto.proto2api.Build;
-import com.google.devtools.build.lib.syntax.FilesetEntry;
-import com.google.devtools.build.lib.syntax.GlobCriteria;
-import com.google.devtools.build.lib.syntax.GlobList;
import com.google.devtools.build.lib.syntax.Label;
import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
import com.google.devtools.build.lib.util.BinaryPredicate;
import java.io.IOException;
import java.io.PrintStream;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
/**
* An output formatter that outputs a protocol buffer representation
@@ -136,7 +108,8 @@ public class ProtoOutputFormatter extends OutputFormatter implements UnorderedFo
.setLocation(location);
for (Attribute attr : rule.getAttributes()) {
- addAttributeToProto(rulePb, attr, getAttributeValues(rule, attr).first, null,
+ PackageSerializer.addAttributeToProto(rulePb, attr,
+ PackageSerializer.getAttributeValues(rule, attr), null,
rule.isAttributeValueExplicitlySpecified(attr), false);
}
@@ -242,253 +215,4 @@ public class ProtoOutputFormatter extends OutputFormatter implements UnorderedFo
return targetPb.build();
}
-
- /**
- * Adds the serialized version of the specified attribute to the specified message.
- *
- * @param rulePb the message to amend
- * @param attr the attribute to add
- * @param value the possible values of the attribute (can be a multi-value list for
- * configurable attributes)
- * @param location the location of the attribute in the source file
- * @param explicitlySpecified whether the attribute was explicitly specified or not
- * @param includeGlobs add glob expression for attributes that contain them
- */
- @SuppressWarnings("unchecked")
- public static void addAttributeToProto(
- Build.Rule.Builder rulePb, Attribute attr, Iterable<Object> values,
- Location location, Boolean explicitlySpecified, boolean includeGlobs) {
- // Get the attribute type. We need to convert and add appropriately
- com.google.devtools.build.lib.packages.Type<?> type = attr.getType();
-
- Build.Attribute.Builder attrPb = Build.Attribute.newBuilder();
-
- // Set the type, name and source
- attrPb.setName(attr.getName());
- attrPb.setType(ProtoUtils.getDiscriminatorFromType(type));
-
- if (location != null) {
- attrPb.setParseableLocation(serialize(location));
- }
-
- if (explicitlySpecified != null) {
- attrPb.setExplicitlySpecified(explicitlySpecified);
- }
-
- // Convenience binding for single-value attributes. Because those attributes can only
- // have a single value, when we encounter configurable versions of them we need to
- // react somehow to having multiple possible values to report. We currently just
- // refrain from setting *any* value in that scenario. This variable is set to null
- // to indicate that scenario.
- Object singleAttributeValue = Iterables.size(values) == 1
- ? Iterables.getOnlyElement(values)
- : null;
-
- /*
- * Set the appropriate type and value. Since string and string list store
- * values for multiple types, use the toString() method on the objects
- * instead of casting them. Note that Boolean and TriState attributes have
- * both an integer and string representation.
- */
- if (type == INTEGER) {
- if (singleAttributeValue != null) {
- attrPb.setIntValue((Integer) singleAttributeValue);
- }
- } else if (type == STRING || type == LABEL || type == NODEP_LABEL || type == OUTPUT) {
- if (singleAttributeValue != null) {
- attrPb.setStringValue(singleAttributeValue.toString());
- }
- } else if (type == STRING_LIST || type == LABEL_LIST || type == NODEP_LABEL_LIST
- || type == OUTPUT_LIST || type == DISTRIBUTIONS) {
- for (Object value : values) {
- for (Object entry : (Collection<?>) value) {
- attrPb.addStringListValue(entry.toString());
- }
- }
- } else if (type == INTEGER_LIST) {
- for (Object value : values) {
- for (Integer entry : (Collection<Integer>) value) {
- attrPb.addIntListValue(entry);
- }
- }
- } else if (type == BOOLEAN) {
- if (singleAttributeValue != null) {
- if ((Boolean) singleAttributeValue) {
- attrPb.setStringValue("true");
- attrPb.setBooleanValue(true);
- } else {
- attrPb.setStringValue("false");
- attrPb.setBooleanValue(false);
- }
- // This maintains partial backward compatibility for external users of the
- // protobuf that were expecting an integer field and not a true boolean.
- attrPb.setIntValue((Boolean) singleAttributeValue ? 1 : 0);
- }
- } else if (type == TRISTATE) {
- if (singleAttributeValue != null) {
- switch ((TriState) singleAttributeValue) {
- case AUTO:
- attrPb.setIntValue(-1);
- attrPb.setStringValue("auto");
- attrPb.setTristateValue(Build.Attribute.Tristate.AUTO);
- break;
- case NO:
- attrPb.setIntValue(0);
- attrPb.setStringValue("no");
- attrPb.setTristateValue(Build.Attribute.Tristate.NO);
- break;
- case YES:
- attrPb.setIntValue(1);
- attrPb.setStringValue("yes");
- attrPb.setTristateValue(Build.Attribute.Tristate.YES);
- break;
- default:
- throw new IllegalStateException("Execpted AUTO/NO/YES to cover all possible cases");
- }
- }
- } else if (type == LICENSE) {
- if (singleAttributeValue != null) {
- License license = (License) singleAttributeValue;
- Build.License.Builder licensePb = Build.License.newBuilder();
- for (License.LicenseType licenseType : license.getLicenseTypes()) {
- licensePb.addLicenseType(licenseType.toString());
- }
- for (Label exception : license.getExceptions()) {
- licensePb.addException(exception.toString());
- }
- attrPb.setLicense(licensePb);
- }
- } else if (type == STRING_DICT) {
- // TODO(bazel-team): support better de-duping here and in other dictionaries.
- for (Object value : values) {
- Map<String, String> dict = (Map<String, String>) value;
- for (Map.Entry<String, String> keyValueList : dict.entrySet()) {
- Build.StringDictEntry entry = Build.StringDictEntry.newBuilder()
- .setKey(keyValueList.getKey())
- .setValue(keyValueList.getValue())
- .build();
- attrPb.addStringDictValue(entry);
- }
- }
- } else if (type == STRING_DICT_UNARY) {
- for (Object value : values) {
- Map<String, String> dict = (Map<String, String>) value;
- for (Map.Entry<String, String> dictEntry : dict.entrySet()) {
- Build.StringDictUnaryEntry entry = Build.StringDictUnaryEntry.newBuilder()
- .setKey(dictEntry.getKey())
- .setValue(dictEntry.getValue())
- .build();
- attrPb.addStringDictUnaryValue(entry);
- }
- }
- } else if (type == STRING_LIST_DICT) {
- for (Object value : values) {
- Map<String, List<String>> dict = (Map<String, List<String>>) value;
- for (Map.Entry<String, List<String>> dictEntry : dict.entrySet()) {
- Build.StringListDictEntry.Builder entry = Build.StringListDictEntry.newBuilder()
- .setKey(dictEntry.getKey());
- for (Object dictEntryValue : dictEntry.getValue()) {
- entry.addValue(dictEntryValue.toString());
- }
- attrPb.addStringListDictValue(entry);
- }
- }
- } else if (type == LABEL_LIST_DICT) {
- for (Object value : values) {
- Map<String, List<Label>> dict = (Map<String, List<Label>>) value;
- for (Map.Entry<String, List<Label>> dictEntry : dict.entrySet()) {
- Build.LabelListDictEntry.Builder entry = Build.LabelListDictEntry.newBuilder()
- .setKey(dictEntry.getKey());
- for (Object dictEntryValue : dictEntry.getValue()) {
- entry.addValue(dictEntryValue.toString());
- }
- attrPb.addLabelListDictValue(entry);
- }
- }
- } else if (type == FILESET_ENTRY_LIST) {
- for (Object value : values) {
- List<FilesetEntry> filesetEntries = (List<FilesetEntry>) value;
- for (FilesetEntry filesetEntry : filesetEntries) {
- Build.FilesetEntry.Builder filesetEntryPb = Build.FilesetEntry.newBuilder()
- .setSource(filesetEntry.getSrcLabel().toString())
- .setDestinationDirectory(filesetEntry.getDestDir().getPathString())
- .setSymlinkBehavior(symlinkBehaviorToPb(filesetEntry.getSymlinkBehavior()))
- .setStripPrefix(filesetEntry.getStripPrefix())
- .setFilesPresent(filesetEntry.getFiles() != null);
-
- if (filesetEntry.getFiles() != null) {
- for (Label file : filesetEntry.getFiles()) {
- filesetEntryPb.addFile(file.toString());
- }
- }
-
- if (filesetEntry.getExcludes() != null) {
- for (String exclude : filesetEntry.getExcludes()) {
- filesetEntryPb.addExclude(exclude);
- }
- }
-
- attrPb.addFilesetListValue(filesetEntryPb);
- }
- }
- } else {
- throw new IllegalStateException("Unknown type: " + type);
- }
-
- if (includeGlobs) {
- for (Object value : values) {
- if (value instanceof GlobList<?>) {
- GlobList<?> globList = (GlobList<?>) value;
-
- for (GlobCriteria criteria : globList.getCriteria()) {
- Build.GlobCriteria.Builder criteriaPb = Build.GlobCriteria.newBuilder()
- .setGlob(criteria.isGlob());
- for (String include : criteria.getIncludePatterns()) {
- criteriaPb.addInclude(include);
- }
- for (String exclude : criteria.getExcludePatterns()) {
- criteriaPb.addExclude(exclude);
- }
-
- attrPb.addGlobCriteria(criteriaPb);
- }
- }
- }
- }
-
- rulePb.addAttribute(attrPb);
- }
-
- // This is needed because I do not want to use the SymlinkBehavior from the
- // protocol buffer all over the place, so there are two classes that do
- // essentially the same thing.
- private static Build.FilesetEntry.SymlinkBehavior symlinkBehaviorToPb(
- FilesetEntry.SymlinkBehavior symlinkBehavior) {
- switch (symlinkBehavior) {
- case COPY:
- return Build.FilesetEntry.SymlinkBehavior.COPY;
- case DEREFERENCE:
- return Build.FilesetEntry.SymlinkBehavior.DEREFERENCE;
- default:
- throw new AssertionError("Unhandled FilesetEntry.SymlinkBehavior");
- }
- }
-
- private static Build.Location serialize(Location location) {
- Build.Location.Builder result = Build.Location.newBuilder();
-
- result.setStartOffset(location.getStartOffset());
- if (location.getStartLineAndColumn() != null) {
- result.setStartLine(location.getStartLineAndColumn().getLine());
- result.setStartColumn(location.getStartLineAndColumn().getColumn());
- }
-
- result.setEndOffset(location.getEndOffset());
- if (location.getEndLineAndColumn() != null) {
- result.setEndLine(location.getEndLineAndColumn().getLine());
- result.setEndColumn(location.getEndLineAndColumn().getColumn());
- }
-
- return result.build();
- }
}