// 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.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. * *

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, Discriminator> TYPE_MAP = new ImmutableMap.Builder, 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(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> NODEP_TYPES = ImmutableSet.of(NODEP_LABEL, NODEP_LABEL_LIST); static final ImmutableSetMultimap> 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 nodepHint = attrPb.hasNodep() ? Optional.of(attrPb.getNodep()) : Optional.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. * *

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> 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 nodeps, String ruleClassName, String attrName) { Preconditions.checkArgument(INVERSE_TYPE_MAP.containsKey(discriminator), discriminator); ImmutableSet> 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 Types 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 // Types {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> 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> 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; } }