| // 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.LABEL_LIST_DICT; | |
| 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.base.Optional; | |
| import com.google.common.base.Predicates; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.common.collect.ImmutableSetMultimap; | |
| import com.google.common.collect.Iterables; | |
| import com.google.common.collect.Sets; | |
| 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.syntax.Type; | |
| import com.google.devtools.build.lib.util.Preconditions; | |
| import java.util.Set; | |
| /** | |
| * Shared code used in proto buffer output for rules and rule classes. | |
| */ | |
| public class ProtoUtils { | |
| /** | |
| * This map contains all attribute types that are recognized by the protocol | |
| * output formatter. | |
| * | |
| * <p>If you modify this map, please ensure that {@link #getTypeFromDiscriminator} can still | |
| * resolve a {@link Discriminator} value to exactly one {@link Type} (using an optional nodep | |
| * hint, as described below). | |
| */ | |
| static final ImmutableMap<Type<?>, Discriminator> TYPE_MAP = | |
| new ImmutableMap.Builder<Type<?>, Discriminator>() | |
| .put(INTEGER, Discriminator.INTEGER) | |
| .put(DISTRIBUTIONS, Discriminator.DISTRIBUTION_SET) | |
| .put(LABEL, Discriminator.LABEL) | |
| // NODEP_LABEL attributes are not really strings. This is implemented | |
| // this way for the sake of backward compatibility. | |
| .put(NODEP_LABEL, Discriminator.STRING) | |
| .put(LABEL_LIST, Discriminator.LABEL_LIST) | |
| .put(NODEP_LABEL_LIST, Discriminator.STRING_LIST) | |
| .put(STRING, Discriminator.STRING) | |
| .put(STRING_LIST, Discriminator.STRING_LIST) | |
| .put(OUTPUT, Discriminator.OUTPUT) | |
| .put(OUTPUT_LIST, Discriminator.OUTPUT_LIST) | |
| .put(LICENSE, Discriminator.LICENSE) | |
| .put(STRING_DICT, Discriminator.STRING_DICT) | |
| .put(FILESET_ENTRY_LIST, Discriminator.FILESET_ENTRY_LIST) | |
| .put(LABEL_DICT_UNARY, Discriminator.LABEL_DICT_UNARY) | |
| .put(LABEL_LIST_DICT, Discriminator.LABEL_LIST_DICT) | |
| .put(STRING_LIST_DICT, Discriminator.STRING_LIST_DICT) | |
| .put(BOOLEAN, Discriminator.BOOLEAN) | |
| .put(TRISTATE, Discriminator.TRISTATE) | |
| .put(INTEGER_LIST, Discriminator.INTEGER_LIST) | |
| .put(STRING_DICT_UNARY, Discriminator.STRING_DICT_UNARY) | |
| .build(); | |
| static final ImmutableSet<Type<?>> NODEP_TYPES = ImmutableSet.of(NODEP_LABEL, NODEP_LABEL_LIST); | |
| static final ImmutableSetMultimap<Discriminator, Type<?>> INVERSE_TYPE_MAP = | |
| TYPE_MAP.asMultimap().inverse(); | |
| /** Returns the {@link Discriminator} value corresponding to the provided {@link Type}. */ | |
| public static Discriminator getDiscriminatorFromType(Type<?> type) { | |
| Preconditions.checkArgument(TYPE_MAP.containsKey(type), type); | |
| return TYPE_MAP.get(type); | |
| } | |
| /** Returns the {@link Type} associated with an {@link Build.Attribute}. */ | |
| static Type<?> getTypeFromAttributePb( | |
| Build.Attribute attrPb, String ruleClassName, String attrName) { | |
| Optional<Boolean> nodepHint = | |
| attrPb.hasNodep() ? Optional.of(attrPb.getNodep()) : Optional.<Boolean>absent(); | |
| Discriminator attrPbDiscriminator = attrPb.getType(); | |
| boolean isSelectorList = attrPbDiscriminator.equals(Discriminator.SELECTOR_LIST); | |
| return getTypeFromDiscriminator( | |
| isSelectorList ? attrPb.getSelectorList().getType() : attrPbDiscriminator, | |
| nodepHint, | |
| ruleClassName, | |
| attrName); | |
| } | |
| /** | |
| * Returns the set of {@link Type}s associated with a {@link Discriminator} value. | |
| * | |
| * <p>The set will contain more than one {@link Type} when {@param discriminator} is either | |
| * {@link Discriminator#STRING} or {@link Discriminator#STRING_LIST}, because each of them | |
| * corresponds with two {@link Type} values. A nodeps hint is needed to determine which {@link | |
| * Type} applies. | |
| */ | |
| private static ImmutableSet<Type<?>> getTypesFromDiscriminator(Discriminator discriminator) { | |
| Preconditions.checkArgument(INVERSE_TYPE_MAP.containsKey(discriminator), discriminator); | |
| return INVERSE_TYPE_MAP.get(discriminator); | |
| } | |
| /** | |
| * Returns the {@link Type} associated with a {@link Discriminator} value, given an optional | |
| * nodeps hint. | |
| */ | |
| private static Type<?> getTypeFromDiscriminator( | |
| Discriminator discriminator, | |
| Optional<Boolean> nodeps, | |
| String ruleClassName, | |
| String attrName) { | |
| Preconditions.checkArgument(INVERSE_TYPE_MAP.containsKey(discriminator), discriminator); | |
| ImmutableSet<Type<?>> possibleTypes = ProtoUtils.getTypesFromDiscriminator(discriminator); | |
| Type<?> preciseType; | |
| if (possibleTypes.size() == 1) { | |
| preciseType = Iterables.getOnlyElement(possibleTypes); | |
| } else { | |
| // If there is more than one possible type associated with the discriminator, then the | |
| // discriminator must be either Discriminator.STRING or Discriminator.STRING_LIST. | |
| // | |
| // If it is Discriminator.STRING, then its possible Type<?>s are {NODEP_LABEL, STRING}. The | |
| // nodeps hint must be present in order to distinguish between them. If nodeps is true, | |
| // then the Type<?> must be NODEP_LABEL, and if false, it must be STRING. | |
| // | |
| // A similar relation holds for the Discriminator value STRING_LIST, and its possible | |
| // Type<?>s {NODEP_LABEL_LIST, STRING_LIST}. | |
| Preconditions.checkArgument(nodeps.isPresent(), | |
| "Nodeps hint is required when discriminator is associated with more than one type." | |
| + " Discriminator: \"%s\", Rule class: \"%s\", Attr: \"%s\"", discriminator, | |
| ruleClassName, attrName); | |
| if (nodeps.get()) { | |
| Set<Type<?>> nodepType = Sets.filter(possibleTypes, Predicates.in(NODEP_TYPES)); | |
| Preconditions.checkState(nodepType.size() == 1, | |
| "There should be exactly one NODEP type associated with discriminator \"%s\"" | |
| + ", but found these: %s. Rule class: \"%s\", Attr: \"%s\"", discriminator, | |
| nodepType, ruleClassName, attrName); | |
| preciseType = Iterables.getOnlyElement(nodepType); | |
| } else { | |
| Set<Type<?>> notNodepType = | |
| Sets.filter(possibleTypes, Predicates.not(Predicates.in(NODEP_TYPES))); | |
| Preconditions.checkState(notNodepType.size() == 1, | |
| "There should be exactly one non-NODEP type associated with discriminator \"%s\"" | |
| + ", but found these: %s. Rule class: \"%s\", Attr: \"%s\"", discriminator, | |
| notNodepType, ruleClassName, attrName); | |
| preciseType = Iterables.getOnlyElement(notNodepType); | |
| } | |
| } | |
| return preciseType; | |
| } | |
| } |