diff options
author | 2016-07-14 22:31:34 +0000 | |
---|---|---|
committer | 2016-07-15 13:31:20 +0000 | |
commit | 937cb800767178b245587276d81d17b94384e44b (patch) | |
tree | b649d5082a07d34863c5d096c175eb501f62ec07 /src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java | |
parent | 3b25028750dd7a6df6777f6c70c1feae9063a630 (diff) |
Remove not-quite necessary serialization bits
What we really are doing here is formatting.
--
MOS_MIGRATED_REVID=127481183
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java | 568 |
1 files changed, 568 insertions, 0 deletions
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 new file mode 100644 index 0000000000..54a223fbe8 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/packages/AttributeFormatter.java @@ -0,0 +1,568 @@ +// Copyright 2014 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.packages; + +import static com.google.devtools.build.lib.packages.BuildType.DISTRIBUTIONS; +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_LIST; +import static com.google.devtools.build.lib.packages.BuildType.LICENSE; +import static com.google.devtools.build.lib.packages.BuildType.NODEP_LABEL; +import static com.google.devtools.build.lib.packages.BuildType.NODEP_LABEL_LIST; +import static com.google.devtools.build.lib.packages.BuildType.OUTPUT; +import static com.google.devtools.build.lib.packages.BuildType.OUTPUT_LIST; +import static com.google.devtools.build.lib.packages.BuildType.TRISTATE; +import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; +import static com.google.devtools.build.lib.syntax.Type.INTEGER; +import static com.google.devtools.build.lib.syntax.Type.INTEGER_LIST; +import static com.google.devtools.build.lib.syntax.Type.STRING; +import static com.google.devtools.build.lib.syntax.Type.STRING_DICT; +import static com.google.devtools.build.lib.syntax.Type.STRING_DICT_UNARY; +import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; +import static com.google.devtools.build.lib.syntax.Type.STRING_LIST_DICT; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.packages.BuildType.Selector; +import com.google.devtools.build.lib.packages.BuildType.SelectorList; +import com.google.devtools.build.lib.query2.proto.proto2api.Build; +import com.google.devtools.build.lib.query2.proto.proto2api.Build.Attribute.Discriminator; +import com.google.devtools.build.lib.query2.proto.proto2api.Build.Attribute.SelectorEntry; +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.LabelListDictEntry; +import com.google.devtools.build.lib.query2.proto.proto2api.Build.StringDictEntry; +import com.google.devtools.build.lib.query2.proto.proto2api.Build.StringDictUnaryEntry; +import com.google.devtools.build.lib.query2.proto.proto2api.Build.StringListDictEntry; +import com.google.devtools.build.lib.syntax.GlobCriteria; +import com.google.devtools.build.lib.syntax.GlobList; +import com.google.devtools.build.lib.syntax.Type; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +/** Common utilities for serializing {@link Attribute}s as protocol buffers. */ +public class AttributeFormatter { + + private static final ImmutableSet<Type<?>> depTypes = + ImmutableSet.<Type<?>>of( + STRING, LABEL, OUTPUT, STRING_LIST, LABEL_LIST, OUTPUT_LIST, DISTRIBUTIONS); + + private static final ImmutableSet<Type<?>> noDepTypes = + ImmutableSet.<Type<?>>of(NODEP_LABEL_LIST, NODEP_LABEL); + + private AttributeFormatter() {} + + /** + * Convert attribute value to proto representation. + * + * <p>If {@param value} is null, only the {@code name}, {@code explicitlySpecified}, {@code + * nodep} (if applicable), and {@code type} fields will be included in the proto message. + * + * <p>If {@param includeGlobs} is true then {@link GlobCriteria} will be included in the proto + * message if present. + * + * <p>If {@param encodeBooleanAndTriStateAsIntegerAndString} is true then boolean and tristate + * values are also encoded as integers and strings. + */ + public static Build.Attribute getAttributeProto( + Attribute attr, + @Nullable Object value, + boolean explicitlySpecified, + boolean includeGlobs, + boolean encodeBooleanAndTriStateAsIntegerAndString) { + return getAttributeProto( + attr.getName(), + attr.getType(), + value, + explicitlySpecified, + includeGlobs, + encodeBooleanAndTriStateAsIntegerAndString); + } + + @VisibleForTesting + static Build.Attribute getAttributeProto( + String name, + Type<?> type, + @Nullable Object value, + boolean explicitlySpecified, + boolean includeGlobs, + boolean encodeBooleanAndTriStateAsIntegerAndString) { + Build.Attribute.Builder attrPb = Build.Attribute.newBuilder(); + attrPb.setName(name); + attrPb.setExplicitlySpecified(explicitlySpecified); + maybeSetNoDep(type, attrPb); + + if (value instanceof SelectorList<?>) { + attrPb.setType(Discriminator.SELECTOR_LIST); + writeSelectorListToBuilder(attrPb, type, (SelectorList<?>) value, includeGlobs); + } else { + attrPb.setType(ProtoUtils.getDiscriminatorFromType(type)); + if (value != null) { + AttributeBuilderAdapter adapter = + new AttributeBuilderAdapter(attrPb, encodeBooleanAndTriStateAsIntegerAndString); + writeAttributeValueToBuilder(adapter, type, value, includeGlobs); + } + } + + return attrPb.build(); + } + + private static void maybeSetNoDep(Type<?> type, Build.Attribute.Builder attrPb) { + if (depTypes.contains(type)) { + attrPb.setNodep(false); + } else if (noDepTypes.contains(type)) { + attrPb.setNodep(true); + } + } + + private static void writeSelectorListToBuilder( + Build.Attribute.Builder attrPb, + Type<?> type, + SelectorList<?> selectorList, + boolean includeGlobs) { + Build.Attribute.SelectorList.Builder selectorListBuilder = + Build.Attribute.SelectorList.newBuilder(); + selectorListBuilder.setType(ProtoUtils.getDiscriminatorFromType(type)); + for (Selector<?> selector : selectorList.getSelectors()) { + Build.Attribute.Selector.Builder selectorBuilder = Build.Attribute.Selector.newBuilder() + .setNoMatchError(selector.getNoMatchError()) + .setHasDefaultValue(selector.hasDefault()); + + // Note that the order of entries returned by selector.getEntries is stable. The map's + // entries' order is preserved from the sorting performed by the SelectorValue constructor. + for (Entry<Label, ?> entry : selector.getEntries().entrySet()) { + Label condition = entry.getKey(); + Builder selectorEntryBuilder = SelectorEntry.newBuilder() + .setLabel(condition.toString()) + .setIsDefaultValue(!selector.isValueSet(condition)); + + Object conditionValue = entry.getValue(); + if (conditionValue != null) { + writeAttributeValueToBuilder( + new SelectorEntryBuilderAdapter(selectorEntryBuilder), + type, + conditionValue, + includeGlobs); + } + selectorBuilder.addEntries(selectorEntryBuilder); + } + selectorListBuilder.addElements(selectorBuilder); + } + attrPb.setSelectorList(selectorListBuilder); + } + + /** + * 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. + */ + @SuppressWarnings("unchecked") + private static void writeAttributeValueToBuilder( + AttributeValueBuilderAdapter builder, Type<?> type, Object value, boolean includeGlobs) { + if (type == INTEGER) { + builder.setIntValue((Integer) value); + } else if (type == STRING || type == LABEL || type == NODEP_LABEL || type == OUTPUT) { + builder.setStringValue(value.toString()); + } else if (type == STRING_LIST || type == LABEL_LIST || type == NODEP_LABEL_LIST + || type == OUTPUT_LIST || type == DISTRIBUTIONS) { + for (Object entry : (Collection<?>) value) { + builder.addStringListValue(entry.toString()); + } + } else if (type == INTEGER_LIST) { + for (Integer entry : (Collection<Integer>) value) { + builder.addIntListValue(entry); + } + } else if (type == BOOLEAN) { + builder.setBooleanValue((Boolean) value); + } else if (type == TRISTATE) { + builder.setTristateValue(triStateToProto((TriState) value)); + } 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()); + } + builder.setLicense(licensePb); + } else if (type == STRING_DICT) { + Map<String, String> dict = (Map<String, String>) value; + for (Map.Entry<String, String> keyValueList : dict.entrySet()) { + StringDictEntry.Builder entry = + StringDictEntry.newBuilder() + .setKey(keyValueList.getKey()) + .setValue(keyValueList.getValue()); + builder.addStringDictValue(entry); + } + } else if (type == STRING_DICT_UNARY) { + Map<String, String> dict = (Map<String, String>) value; + for (Map.Entry<String, String> dictEntry : dict.entrySet()) { + StringDictUnaryEntry.Builder entry = + StringDictUnaryEntry.newBuilder() + .setKey(dictEntry.getKey()) + .setValue(dictEntry.getValue()); + builder.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()) { + StringListDictEntry.Builder entry = + StringListDictEntry.newBuilder().setKey(dictEntry.getKey()); + for (Object dictEntryValue : dictEntry.getValue()) { + entry.addValue(dictEntryValue.toString()); + } + builder.addStringListDictValue(entry); + } + } else if (type == LABEL_DICT_UNARY) { + Map<String, Label> dict = (Map<String, Label>) value; + for (Map.Entry<String, Label> dictEntry : dict.entrySet()) { + LabelDictUnaryEntry.Builder entry = + LabelDictUnaryEntry.newBuilder() + .setKey(dictEntry.getKey()) + .setValue(dictEntry.getValue().toString()); + builder.addLabelDictUnaryValue(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()); + } + } + + if (filesetEntry.getExcludes() != null) { + for (String exclude : filesetEntry.getExcludes()) { + filesetEntryPb.addExclude(exclude); + } + } + + builder.addFilesetListValue(filesetEntryPb); + } + } else { + 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().setGlob(criteria.isGlob()); + for (String include : criteria.getIncludePatterns()) { + criteriaPb.addInclude(include); + } + for (String exclude : criteria.getExcludePatterns()) { + criteriaPb.addExclude(exclude); + } + + builder.addGlobCriteria(criteriaPb); + } + } + } + + private static Tristate triStateToProto(TriState value) { + switch (value) { + case AUTO: + return Tristate.AUTO; + case NO: + return Tristate.NO; + case YES: + return Tristate.YES; + default: + throw new AssertionError("Expected AUTO/NO/YES to cover all possible cases"); + } + } + + // 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"); + } + } + + /** + * An adapter used by {@link #writeAttributeValueToBuilder} in order to reuse the same code for + * writing to both {@link Build.Attribute.Builder} and {@link SelectorEntry.Builder} objects. + */ + private interface AttributeValueBuilderAdapter { + + void addStringListValue(String s); + + void addFilesetListValue(Build.FilesetEntry.Builder builder); + + void addGlobCriteria(Build.GlobCriteria.Builder builder); + + void addLabelDictUnaryValue(LabelDictUnaryEntry.Builder builder); + + void addLabelListDictValue(LabelListDictEntry.Builder builder); + + void addIntListValue(int i); + + void addStringDictUnaryValue(StringDictUnaryEntry.Builder builder); + + void addStringDictValue(StringDictEntry.Builder builder); + + void addStringListDictValue(StringListDictEntry.Builder builder); + + void setBooleanValue(boolean b); + + void setIntValue(int i); + + void setLicense(Build.License.Builder builder); + + void setStringValue(String s); + + void setTristateValue(Tristate tristate); + } + + /** + * An {@link AttributeValueBuilderAdapter} which writes to a {@link Build.Attribute.Builder}. + * + * <p>If {@param encodeBooleanAndTriStateAsIntegerAndString} is {@code true}, then {@link + * Boolean} and {@link TriState} attribute values also write to the integer and string fields. + * This offers backwards compatibility to clients that expect attribute values of those types. + */ + private static class AttributeBuilderAdapter implements AttributeValueBuilderAdapter { + private final boolean encodeBooleanAndTriStateAsIntegerAndString; + private final Build.Attribute.Builder attributeBuilder; + + private AttributeBuilderAdapter( + Build.Attribute.Builder attributeBuilder, + boolean encodeBooleanAndTriStateAsIntegerAndString) { + this.attributeBuilder = Preconditions.checkNotNull(attributeBuilder); + this.encodeBooleanAndTriStateAsIntegerAndString = encodeBooleanAndTriStateAsIntegerAndString; + } + + public void addStringListValue(String s) { + attributeBuilder.addStringListValue(s); + } + + @Override + public void addFilesetListValue(Build.FilesetEntry.Builder builder) { + attributeBuilder.addFilesetListValue(builder); + } + + @Override + public void addGlobCriteria(Build.GlobCriteria.Builder builder) { + attributeBuilder.addGlobCriteria(builder); + } + + @Override + public void addLabelDictUnaryValue(LabelDictUnaryEntry.Builder builder) { + attributeBuilder.addLabelDictUnaryValue(builder); + } + + @Override + public void addLabelListDictValue(LabelListDictEntry.Builder builder) { + attributeBuilder.addLabelListDictValue(builder); + } + + @Override + public void addIntListValue(int i) { + attributeBuilder.addIntListValue(i); + } + + @Override + public void addStringDictUnaryValue(StringDictUnaryEntry.Builder builder) { + attributeBuilder.addStringDictUnaryValue(builder); + } + + @Override + public void addStringDictValue(StringDictEntry.Builder builder) { + attributeBuilder.addStringDictValue(builder); + } + + @Override + public void addStringListDictValue(StringListDictEntry.Builder builder) { + attributeBuilder.addStringListDictValue(builder); + } + + @Override + public void setBooleanValue(boolean b) { + if (b) { + attributeBuilder.setBooleanValue(true); + if (encodeBooleanAndTriStateAsIntegerAndString) { + attributeBuilder.setStringValue("true"); + attributeBuilder.setIntValue(1); + } + } else { + attributeBuilder.setBooleanValue(false); + if (encodeBooleanAndTriStateAsIntegerAndString) { + attributeBuilder.setStringValue("false"); + attributeBuilder.setIntValue(0); + } + } + } + + @Override + public void setIntValue(int i) { + attributeBuilder.setIntValue(i); + } + + @Override + public void setLicense(Build.License.Builder builder) { + attributeBuilder.setLicense(builder); + } + + @Override + public void setStringValue(String s) { + attributeBuilder.setStringValue(s); + } + + @Override + public void setTristateValue(Tristate tristate) { + switch (tristate) { + case AUTO: + attributeBuilder.setTristateValue(Tristate.AUTO); + if (encodeBooleanAndTriStateAsIntegerAndString) { + attributeBuilder.setIntValue(-1); + attributeBuilder.setStringValue("auto"); + } + break; + case NO: + attributeBuilder.setTristateValue(Tristate.NO); + if (encodeBooleanAndTriStateAsIntegerAndString) { + attributeBuilder.setIntValue(0); + attributeBuilder.setStringValue("no"); + } + break; + case YES: + attributeBuilder.setTristateValue(Tristate.YES); + if (encodeBooleanAndTriStateAsIntegerAndString) { + attributeBuilder.setIntValue(1); + attributeBuilder.setStringValue("yes"); + } + break; + default: + throw new AssertionError("Expected AUTO/NO/YES to cover all possible cases"); + } + } + } + + /** + * An {@link AttributeValueBuilderAdapter} which writes to a {@link SelectorEntry.Builder}. + * + * <p>Note that there is no {@code encodeBooleanAndTriStateAsIntegerAndString} parameter needed + * here. This is because the clients that expect those alternate encodings of boolean and + * tristate attribute values do not support {@link SelectorList} values. When providing output to + * those clients, we compute the set of possible attribute values (expanding {@link SelectorList} + * values, evaluating computed defaults, and flattening collections of collections; see {@link + * com.google.devtools.build.lib.packages.AggregatingAttributeMapper#getPossibleAttributeValues} + * and {@link + * com.google.devtools.build.lib.packages.AggregatingAttributeMapper#flattenAttributeValues}). + */ + private static class SelectorEntryBuilderAdapter implements AttributeValueBuilderAdapter { + private final SelectorEntry.Builder selectorEntryBuilder; + + private SelectorEntryBuilderAdapter(Builder selectorEntryBuilder) { + this.selectorEntryBuilder = Preconditions.checkNotNull(selectorEntryBuilder); + } + + public void addStringListValue(String s) { + selectorEntryBuilder.addStringListValue(s); + } + + @Override + public void addFilesetListValue(Build.FilesetEntry.Builder builder) { + selectorEntryBuilder.addFilesetListValue(builder); + } + + @Override + public void addGlobCriteria(Build.GlobCriteria.Builder builder) { + selectorEntryBuilder.addGlobCriteria(builder); + } + + @Override + public void addLabelDictUnaryValue(LabelDictUnaryEntry.Builder builder) { + selectorEntryBuilder.addLabelDictUnaryValue(builder); + } + + @Override + public void addLabelListDictValue(LabelListDictEntry.Builder builder) { + selectorEntryBuilder.addLabelListDictValue(builder); + } + + @Override + public void addIntListValue(int i) { + selectorEntryBuilder.addIntListValue(i); + } + + @Override + public void addStringDictUnaryValue(StringDictUnaryEntry.Builder builder) { + selectorEntryBuilder.addStringDictUnaryValue(builder); + } + + @Override + public void addStringDictValue(StringDictEntry.Builder builder) { + selectorEntryBuilder.addStringDictValue(builder); + } + + @Override + public void addStringListDictValue(StringListDictEntry.Builder builder) { + selectorEntryBuilder.addStringListDictValue(builder); + } + + @Override + public void setBooleanValue(boolean b) { + selectorEntryBuilder.setBooleanValue(b); + } + + @Override + public void setIntValue(int i) { + selectorEntryBuilder.setIntValue(i); + } + + @Override + public void setLicense(Build.License.Builder builder) { + selectorEntryBuilder.setLicense(builder); + } + + @Override + public void setStringValue(String s) { + selectorEntryBuilder.setStringValue(s); + } + + @Override + public void setTristateValue(Tristate tristate) { + selectorEntryBuilder.setTristateValue(tristate); + } + } +} + |