blob: ebe499ee132f36e84e4d226c7fc269f4208eb7e3 [file] [log] [blame]
// 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.query2.query.output;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.Rule;
/** Logic for retrieving possible values for an attribute. */
public class PossibleAttributeValues {
private PossibleAttributeValues() {}
/**
* Returns the possible values of the specified attribute in the specified rule. For simple
* attributes, this is a single value. For configurable and computed attributes, this may be a
* list of values. See {@link AggregatingAttributeMapper#visitAttribute} for how the values are
* determined.
*
* <p>This applies an important optimization for label lists: instead of returning all possible
* values, it only returns possible <i>labels</i>. For example, given:
*
* <pre>
* select({
* ":c": ["//a:one", "//a:two"],
* ":d": ["//a:two"]
* })</pre>
*
* it returns:
*
* <pre>["//a:one", "//a:two"]</pre>
*
* which loses track of which label appears in which branch.
*
* <p>This avoids the memory overruns that can happen be iterating over every possible value for
* an <code>attr = select(...) + select(...) + select(...) + ...</code> expression. Query
* operations generally don't care about specific attribute values - they just care which labels
* are possible.
*
* @param mayTreatMultipleAsNone signals if attribute-value computation <b>may</b> be aborted if
* more than one possible value is encountered. Useful when the caller needs a single value or
* none at all, as is the use case of this method for many attribute types. This parameter is
* respected on a best-effort basis - multiple values may still be returned if an unoptimized
* code path is visited.
*/
public static Iterable<Object> forRuleAndAttribute(
Rule rule, Attribute attr, boolean mayTreatMultipleAsNone) {
AggregatingAttributeMapper attributeMap = AggregatingAttributeMapper.of(rule);
if (attr.getType().equals(BuildType.LABEL_LIST)
&& attributeMap.isConfigurable(attr.getName())) {
// TODO(gregce): Expand this to all collection types (we don't do this for scalars because
// there's currently no syntax for expressing multiple scalar values). This unfortunately
// isn't trivial because Bazel's label visitation logic includes special methods built
// directly into Type.
return ImmutableList.of(
attributeMap.getReachableLabels(attr.getName(), /* includeSelectKeys= */ false));
}
Iterable<?> concatenatedSelectsValue =
attributeMap.getConcatenatedSelectorListsOfListType(attr.getName(), attr.getType());
if (concatenatedSelectsValue != null) {
return Lists.newArrayList(concatenatedSelectsValue);
}
// The call to visitAttributes below is especially slow with selector lists.
@SuppressWarnings("unchecked") // Casting Iterable<T> -> Iterable<Object>
Iterable<Object> possibleValues =
(Iterable<Object>)
attributeMap.visitAttribute(attr.getName(), attr.getType(), mayTreatMultipleAsNone);
return possibleValues;
}
}