diff options
author | 2015-02-11 16:51:37 +0000 | |
---|---|---|
committer | 2015-02-11 16:51:37 +0000 | |
commit | a46125ca0e4569a41d2d078c3168dbfd8950534a (patch) | |
tree | 505db840e7bf95f8cccede02f64dacbd044f3332 /src/main/java/com/google | |
parent | 523d44229c7c9ebba728695a3c6ec8b0f3609638 (diff) |
Extract an EvaluableBlazeQueryEnvironment abstract class from BlazeQueryEnvironment that exposes an evaluateQuery method, and also implements the non-LabelVisitor-specific parts of BlazeQueryEnvironment, for other implementations' uses.
Most of the code is just a straight refactoring of BlazeQueryEnvironment into EvaluableBlazeQueryEnvironment (and BlazeTargetAccessor). Ignoring whitespace changes in [] may be your friend for seeing that it's a straight move.
This also allows us to change tests to use QueryCommand.newQueryEnvironment, in preparation for newQueryEnvironment potentially returning a different EvaluableBlazeQueryEnvironment subclass depending on the circumstances.
--
MOS_MIGRATED_REVID=86088953
Diffstat (limited to 'src/main/java/com/google')
4 files changed, 414 insertions, 293 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/query2/AbstractBlazeQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/AbstractBlazeQueryEnvironment.java new file mode 100644 index 0000000000..48840eac7c --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/AbstractBlazeQueryEnvironment.java @@ -0,0 +1,217 @@ +// Copyright 2015 Google Inc. 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; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import com.google.devtools.build.lib.cmdline.ResolvedTargets; +import com.google.devtools.build.lib.cmdline.TargetParsingException; +import com.google.devtools.build.lib.events.ErrorSensingEventHandler; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.EventHandler; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment; +import com.google.devtools.build.lib.query2.engine.QueryEvalResult; +import com.google.devtools.build.lib.query2.engine.QueryException; +import com.google.devtools.build.lib.query2.engine.QueryExpression; +import com.google.devtools.build.lib.syntax.Label; +import com.google.devtools.build.lib.util.BinaryPredicate; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * {@link QueryEnvironment} that can evaluate queries to produce a result, and implements as much + * of QueryEnvironment as possible while remaining mostly agnostic as to the objects being stored. + */ +public abstract class AbstractBlazeQueryEnvironment<T> implements QueryEnvironment<T> { + protected final ErrorSensingEventHandler eventHandler; + private final TargetPatternEvaluator targetPatternEvaluator; + private final Map<String, Set<T>> letBindings = new HashMap<>(); + protected final Map<String, ResolvedTargets<Target>> resolvedTargetPatterns = new HashMap<>(); + protected final boolean keepGoing; + protected final boolean strictScope; + + protected final BinaryPredicate<Rule, Attribute> dependencyFilter; + private final Predicate<Label> labelFilter; + + private final Set<Setting> settings; + private final List<QueryFunction> extraFunctions; + + protected AbstractBlazeQueryEnvironment(TargetPatternEvaluator targetPatternEvaluator, + boolean keepGoing, + boolean strictScope, + Predicate<Label> labelFilter, + EventHandler eventHandler, + Set<Setting> settings, + Iterable<QueryFunction> extraFunctions) { + this.eventHandler = new ErrorSensingEventHandler(eventHandler); + this.targetPatternEvaluator = targetPatternEvaluator; + this.keepGoing = keepGoing; + this.strictScope = strictScope; + this.dependencyFilter = constructDependencyFilter(settings); + this.labelFilter = labelFilter; + this.settings = Sets.immutableEnumSet(settings); + this.extraFunctions = ImmutableList.copyOf(extraFunctions); + } + + private static BinaryPredicate<Rule, Attribute> constructDependencyFilter(Set<Setting> settings) { + BinaryPredicate<Rule, Attribute> specifiedFilter = + settings.contains(Setting.NO_HOST_DEPS) ? Rule.NO_HOST_DEPS : Rule.ALL_DEPS; + if (settings.contains(Setting.NO_IMPLICIT_DEPS)) { + specifiedFilter = Rule.and(specifiedFilter, Rule.NO_IMPLICIT_DEPS); + } + if (settings.contains(Setting.NO_NODEP_DEPS)) { + specifiedFilter = Rule.and(specifiedFilter, Rule.NO_NODEP_ATTRIBUTES); + } + return specifiedFilter; + } + + /** + * Evaluate the specified query expression in this environment. + * + * @return a {@link QueryEvalResult} object that contains the resulting set of targets and a bit + * to indicate whether errors occured during evaluation; note that the + * success status can only be false if {@code --keep_going} was in effect + * @throws QueryException if the evaluation failed and {@code --nokeep_going} was in + * effect + */ + public QueryEvalResult<T> evaluateQuery(QueryExpression expr) throws QueryException { + // Some errors are reported as QueryExceptions and others as ERROR events + // (if --keep_going). + eventHandler.resetErrors(); + resolvedTargetPatterns.clear(); + + // In the --nokeep_going case, errors are reported in the order in which the patterns are + // specified; using a linked hash set here makes sure that the left-most error is reported. + Set<String> targetPatternSet = new LinkedHashSet<>(); + expr.collectTargetPatterns(targetPatternSet); + try { + resolvedTargetPatterns.putAll(preloadOrThrow(targetPatternSet)); + } catch (TargetParsingException e) { + // Unfortunately, by evaluating the patterns in parallel, we lose some location information. + throw new QueryException(expr, e.getMessage()); + } + + Set<T> resultNodes; + try { + resultNodes = expr.eval(this); + } catch (QueryException e) { + throw new QueryException(e, expr); + } + + if (eventHandler.hasErrors()) { + if (!keepGoing) { + // This case represents loading-phase errors reported during evaluation + // of target patterns that don't cause evaluation to fail per se. + throw new QueryException("Evaluation of query \"" + expr + + "\" failed due to BUILD file errors"); + } else { + eventHandler.handle(Event.warn("--keep_going specified, ignoring errors. " + + "Results may be inaccurate")); + } + } + + return new QueryEvalResult<>(!eventHandler.hasErrors(), resultNodes); + } + + public QueryEvalResult<T> evaluateQuery(String query) throws QueryException { + return evaluateQuery(QueryExpression.parse(query, this)); + } + + @Override + public void reportBuildFileError(QueryExpression caller, String message) throws QueryException { + if (!keepGoing) { + throw new QueryException(caller, message); + } else { + // Keep consistent with evaluateQuery() above. + eventHandler.handle(Event.error("Evaluation of query \"" + caller + "\" failed: " + message)); + } + } + + public abstract Target getTarget(Label label) throws TargetNotFoundException, QueryException; + + @Override + public Set<T> getVariable(String name) { + return letBindings.get(name); + } + + @Override + public Set<T> setVariable(String name, Set<T> value) { + return letBindings.put(name, value); + } + + protected boolean validateScope(Label label, boolean strict) throws QueryException { + if (!labelFilter.apply(label)) { + String error = String.format("target '%s' is not within the scope of the query", label); + if (strict) { + throw new QueryException(error); + } else { + eventHandler.handle(Event.warn(error + ". Skipping")); + return false; + } + } + return true; + } + + public Set<T> evalTargetPattern(QueryExpression caller, String pattern) + throws QueryException { + if (!resolvedTargetPatterns.containsKey(pattern)) { + try { + resolvedTargetPatterns.putAll(preloadOrThrow(ImmutableList.of(pattern))); + } catch (TargetParsingException e) { + // Will skip the target and keep going if -k is specified. + resolvedTargetPatterns.put(pattern, ResolvedTargets.<Target>empty()); + reportBuildFileError(caller, e.getMessage()); + } + } + return getTargetsMatchingPattern(caller, pattern); + } + + private Map<String, ResolvedTargets<Target>> preloadOrThrow(Collection<String> patterns) + throws TargetParsingException { + try { + // Note that this may throw a RuntimeException if deps are missing in Skyframe and this is + // being called from within a SkyFunction. + return targetPatternEvaluator.preloadTargetPatterns( + eventHandler, patterns, keepGoing); + } catch (InterruptedException e) { + // TODO(bazel-team): Propagate the InterruptedException from here [skyframe-loading]. + throw new TargetParsingException("interrupted"); + } + } + + @Override + public boolean isSettingEnabled(Setting setting) { + return settings.contains(Preconditions.checkNotNull(setting)); + } + + @Override + public Iterable<QueryFunction> getFunctions() { + ImmutableList.Builder<QueryFunction> builder = ImmutableList.builder(); + builder.addAll(DEFAULT_QUERY_FUNCTIONS); + builder.addAll(extraFunctions); + return builder.build(); + } + +} diff --git a/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java index 7fabdc75aa..0cc6da0b12 100644 --- a/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java @@ -13,76 +13,47 @@ // limitations under the License. package com.google.devtools.build.lib.query2; -import static com.google.devtools.build.lib.packages.Type.BOOLEAN; -import static com.google.devtools.build.lib.packages.Type.TRISTATE; - import com.google.common.base.Preconditions; import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; -import com.google.devtools.build.lib.cmdline.ResolvedTargets; -import com.google.devtools.build.lib.cmdline.TargetParsingException; -import com.google.devtools.build.lib.events.ErrorSensingEventHandler; -import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; 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.NoSuchThingException; -import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper; import com.google.devtools.build.lib.packages.OutputFile; import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.Target; -import com.google.devtools.build.lib.packages.TargetUtils; -import com.google.devtools.build.lib.packages.Type; import com.google.devtools.build.lib.pkgcache.PackageProvider; import com.google.devtools.build.lib.pkgcache.TargetEdgeObserver; import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; import com.google.devtools.build.lib.pkgcache.TargetProvider; import com.google.devtools.build.lib.query2.engine.BlazeQueryEvalResult; -import com.google.devtools.build.lib.query2.engine.QueryEnvironment; +import com.google.devtools.build.lib.query2.engine.QueryEvalResult; import com.google.devtools.build.lib.query2.engine.QueryException; import com.google.devtools.build.lib.query2.engine.QueryExpression; import com.google.devtools.build.lib.query2.engine.SkyframeRestartQueryException; import com.google.devtools.build.lib.syntax.Label; -import com.google.devtools.build.lib.util.BinaryPredicate; import com.google.devtools.build.lib.vfs.PathFragment; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; import java.util.Set; /** * The environment of a Blaze query. Not thread-safe. */ -public class BlazeQueryEnvironment implements QueryEnvironment<Target> { - protected final ErrorSensingEventHandler eventHandler; +public class BlazeQueryEnvironment extends AbstractBlazeQueryEnvironment<Target> { private final TargetProvider targetProvider; - private final TargetPatternEvaluator targetPatternEvaluator; private final Digraph<Target> graph = new Digraph<>(); private final ErrorPrintingTargetEdgeErrorObserver errorObserver; private final LabelVisitor labelVisitor; - private final Map<String, Set<Target>> letBindings = new HashMap<>(); - private final Map<String, ResolvedTargets<Target>> resolvedTargetPatterns = new HashMap<>(); - protected final boolean keepGoing; - private final boolean strictScope; protected final int loadingPhaseThreads; - private final BinaryPredicate<Rule, Attribute> dependencyFilter; - private final Predicate<Label> labelFilter; - - private final Set<Setting> settings; - private final List<QueryFunction> extraFunctions; - private final BlazeTargetAccessor accessor = new BlazeTargetAccessor(); + private final BlazeTargetAccessor accessor = new BlazeTargetAccessor(this); /** * Note that the correct operation of this class critically depends on the Reporter being a @@ -104,18 +75,13 @@ public class BlazeQueryEnvironment implements QueryEnvironment<Target> { EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> extraFunctions) { - this.eventHandler = new ErrorSensingEventHandler(eventHandler); + super(targetPatternEvaluator, keepGoing, strictScope, labelFilter, eventHandler, settings, + extraFunctions + ); this.targetProvider = packageProvider; - this.targetPatternEvaluator = targetPatternEvaluator; this.errorObserver = new ErrorPrintingTargetEdgeErrorObserver(this.eventHandler); - this.keepGoing = keepGoing; - this.strictScope = strictScope; this.loadingPhaseThreads = loadingPhaseThreads; - this.dependencyFilter = constructDependencyFilter(settings); this.labelVisitor = new LabelVisitor(packageProvider, dependencyFilter); - this.labelFilter = labelFilter; - this.settings = Sets.immutableEnumSet(settings); - this.extraFunctions = ImmutableList.copyOf(extraFunctions); } /** @@ -136,78 +102,11 @@ public class BlazeQueryEnvironment implements QueryEnvironment<Target> { loadingPhaseThreads, Rule.ALL_LABELS, eventHandler, settings, extraFunctions); } - private static BinaryPredicate<Rule, Attribute> constructDependencyFilter(Set<Setting> settings) { - BinaryPredicate<Rule, Attribute> specifiedFilter = - settings.contains(Setting.NO_HOST_DEPS) ? Rule.NO_HOST_DEPS : Rule.ALL_DEPS; - if (settings.contains(Setting.NO_IMPLICIT_DEPS)) { - specifiedFilter = Rule.and(specifiedFilter, Rule.NO_IMPLICIT_DEPS); - } - if (settings.contains(Setting.NO_NODEP_DEPS)) { - specifiedFilter = Rule.and(specifiedFilter, Rule.NO_NODEP_ATTRIBUTES); - } - return specifiedFilter; - } - - /** - * Evaluate the specified query expression in this environment. - * - * @return a {@link BlazeQueryEvalResult} object that contains the resulting set of targets, the - * partial graph, and a bit to indicate whether errors occured during evaluation; note that the - * success status can only be false if {@code --keep_going} was in effect - * @throws QueryException if the evaluation failed and {@code --nokeep_going} was in - * effect - */ - public BlazeQueryEvalResult<Target> evaluateQuery(QueryExpression expr) throws QueryException { - // Some errors are reported as QueryExceptions and others as ERROR events - // (if --keep_going). - eventHandler.resetErrors(); - resolvedTargetPatterns.clear(); - - // In the --nokeep_going case, errors are reported in the order in which the patterns are - // specified; using a linked hash set here makes sure that the left-most error is reported. - Set<String> targetPatternSet = new LinkedHashSet<>(); - expr.collectTargetPatterns(targetPatternSet); - try { - resolvedTargetPatterns.putAll(preloadOrThrow(targetPatternSet)); - } catch (TargetParsingException e) { - // Unfortunately, by evaluating the patterns in parallel, we lose some location information. - throw new QueryException(expr, e.getMessage()); - } - - Set<Target> resultNodes; - try { - resultNodes = expr.eval(this); - } catch (QueryException e) { - throw new QueryException(e, expr); - } - - if (eventHandler.hasErrors()) { - if (!keepGoing) { - // This case represents loading-phase errors reported during evaluation - // of target patterns that don't cause evaluation to fail per se. - throw new QueryException("Evaluation of query \"" + expr - + "\" failed due to BUILD file errors"); - } else { - eventHandler.handle(Event.warn("--keep_going specified, ignoring errors. " - + "Results may be inaccurate")); - } - } - - return new BlazeQueryEvalResult<>(!eventHandler.hasErrors(), resultNodes, graph); - } - - public BlazeQueryEvalResult<Target> evaluateQuery(String query) throws QueryException { - return evaluateQuery(QueryExpression.parse(query, this)); - } - @Override - public void reportBuildFileError(QueryExpression caller, String message) throws QueryException { - if (!keepGoing) { - throw new QueryException(caller, message); - } else { - // Keep consistent with evaluateQuery() above. - eventHandler.handle(Event.error("Evaluation of query \"" + caller + "\" failed: " + message)); - } + public BlazeQueryEvalResult<Target> evaluateQuery(QueryExpression expr) throws QueryException { + QueryEvalResult<Target> queryEvalResult = super.evaluateQuery(expr); + return new BlazeQueryEvalResult<>(queryEvalResult.getSuccess(), queryEvalResult.getResultSet(), + graph); } @Override @@ -362,16 +261,6 @@ public class BlazeQueryEnvironment implements QueryEnvironment<Target> { return getTargetsFromNodes(graph.getShortestPath(getNode(from), getNode(to))); } - @Override - public Set<Target> getVariable(String name) { - return letBindings.get(name); - } - - @Override - public Set<Target> setVariable(String name, Set<Target> value) { - return letBindings.put(name, value); - } - /** * It suffices to synchronize the modifications of this.graph from within the * GraphBuildingObserver, because that's the only concurrent part. @@ -403,45 +292,6 @@ public class BlazeQueryEnvironment implements QueryEnvironment<Target> { graph.addEdge(from, to); } - private boolean validateScope(Label label, boolean strict) throws QueryException { - if (!labelFilter.apply(label)) { - String error = String.format("target '%s' is not within the scope of the query", label); - if (strict) { - throw new QueryException(error); - } else { - eventHandler.handle(Event.warn(error + ". Skipping")); - return false; - } - } - return true; - } - - public Set<Target> evalTargetPattern(QueryExpression caller, String pattern) - throws QueryException { - if (!resolvedTargetPatterns.containsKey(pattern)) { - try { - resolvedTargetPatterns.putAll(preloadOrThrow(ImmutableList.of(pattern))); - } catch (TargetParsingException e) { - // Will skip the target and keep going if -k is specified. - resolvedTargetPatterns.put(pattern, ResolvedTargets.<Target>empty()); - reportBuildFileError(caller, e.getMessage()); - } - } - return getTargetsMatchingPattern(caller, pattern); - } - - private Map<String, ResolvedTargets<Target>> preloadOrThrow(Collection<String> patterns) - throws TargetParsingException { - try { - // Note that this may throw a RuntimeException if deps are missing in Skyframe. - return targetPatternEvaluator.preloadTargetPatterns( - eventHandler, patterns, keepGoing); - } catch (InterruptedException e) { - // TODO(bazel-team): Propagate the InterruptedException from here [skyframe-loading]. - throw new TargetParsingException("interrupted"); - } - } - private Target getTargetOrThrow(Label label) throws NoSuchThingException, SkyframeRestartQueryException, InterruptedException { Target target = targetProvider.getTarget(eventHandler, label); @@ -500,128 +350,6 @@ public class BlazeQueryEnvironment implements QueryEnvironment<Target> { return accessor; } - @Override - public boolean isSettingEnabled(Setting setting) { - return settings.contains(Preconditions.checkNotNull(setting)); - } - - @Override - public Iterable<QueryFunction> getFunctions() { - ImmutableList.Builder<QueryFunction> builder = ImmutableList.builder(); - builder.addAll(DEFAULT_QUERY_FUNCTIONS); - builder.addAll(extraFunctions); - return builder.build(); - } - - private final class BlazeTargetAccessor implements TargetAccessor<Target> { - - @Override - public String getTargetKind(Target target) { - return target.getTargetKind(); - } - - @Override - public String getLabel(Target target) { - return target.getLabel().toString(); - } - - @Override - public List<Target> getLabelListAttr(QueryExpression caller, Target target, String attrName, - String errorMsgPrefix) throws QueryException { - Preconditions.checkArgument(target instanceof Rule); - - List<Target> result = new ArrayList<>(); - Rule rule = (Rule) target; - - AggregatingAttributeMapper attrMap = AggregatingAttributeMapper.of(rule); - Type<?> attrType = attrMap.getAttributeType(attrName); - if (attrType == null) { - // Return an empty list if the attribute isn't defined for this rule. - return ImmutableList.of(); - } - for (Object value : attrMap.visitAttribute(attrName, attrType)) { - // Computed defaults may have null values. - if (value != null) { - for (Label label : attrType.getLabels(value)) { - try { - result.add(getTarget(label)); - } catch (TargetNotFoundException e) { - reportBuildFileError(caller, errorMsgPrefix + e.getMessage()); - } - } - } - } - - return result; - } - - @Override - public List<String> getStringListAttr(Target target, String attrName) { - Preconditions.checkArgument(target instanceof Rule); - return NonconfigurableAttributeMapper.of((Rule) target).get(attrName, Type.STRING_LIST); - } - - @Override - public String getStringAttr(Target target, String attrName) { - Preconditions.checkArgument(target instanceof Rule); - return NonconfigurableAttributeMapper.of((Rule) target).get(attrName, Type.STRING); - } - - @Override - public Iterable<String> getAttrAsString(Target target, String attrName) { - Preconditions.checkArgument(target instanceof Rule); - List<String> values = new ArrayList<>(); // May hold null values. - Attribute attribute = ((Rule) target).getAttributeDefinition(attrName); - if (attribute != null) { - Type<?> attributeType = attribute.getType(); - for (Object attrValue : AggregatingAttributeMapper.of((Rule) target).visitAttribute( - attribute.getName(), attributeType)) { - - // Ugly hack to maintain backward 'attr' query compatibility for BOOLEAN and TRISTATE - // attributes. These are internally stored as actual Boolean or TriState objects but were - // historically queried as integers. To maintain compatibility, we inspect their actual - // value and return the integer equivalent represented as a String. This code is the - // opposite of the code in BooleanType and TriStateType respectively. - if (attributeType == BOOLEAN) { - values.add(Type.BOOLEAN.cast(attrValue) ? "1" : "0"); - } else if (attributeType == TRISTATE) { - switch (Type.TRISTATE.cast(attrValue)) { - case AUTO : - values.add("-1"); - break; - case NO : - values.add("0"); - break; - case YES : - values.add("1"); - break; - default : - throw new AssertionError("This can't happen!"); - } - } else { - values.add(attrValue == null ? null : attrValue.toString()); - } - } - } - return values; - } - - @Override - public boolean isRule(Target target) { - return target instanceof Rule; - } - - @Override - public boolean isTestRule(Target target) { - return TargetUtils.isTestRule(target); - } - - @Override - public boolean isTestSuite(Target target) { - return TargetUtils.isTestSuiteRule(target); - } - } - /** Given a set of target nodes, returns the targets. */ private static Set<Target> getTargetsFromNodes(Iterable<Node<Target>> input) { Set<Target> result = new LinkedHashSet<>(); diff --git a/src/main/java/com/google/devtools/build/lib/query2/BlazeTargetAccessor.java b/src/main/java/com/google/devtools/build/lib/query2/BlazeTargetAccessor.java new file mode 100644 index 0000000000..026bfde723 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/BlazeTargetAccessor.java @@ -0,0 +1,154 @@ +// Copyright 2015 Google Inc. 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; + +import static com.google.devtools.build.lib.packages.Type.BOOLEAN; +import static com.google.devtools.build.lib.packages.Type.TRISTATE; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.packages.AggregatingAttributeMapper; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.packages.TargetUtils; +import com.google.devtools.build.lib.packages.Type; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetNotFoundException; +import com.google.devtools.build.lib.query2.engine.QueryException; +import com.google.devtools.build.lib.query2.engine.QueryExpression; +import com.google.devtools.build.lib.syntax.Label; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of {@link TargetAccessor<Target>} that uses an + * {@link AbstractBlazeQueryEnvironment <Target>} internally to report issues and resolve + * targets. + */ +final class BlazeTargetAccessor implements TargetAccessor<Target> { + private final AbstractBlazeQueryEnvironment<Target> queryEnvironment; + + BlazeTargetAccessor(AbstractBlazeQueryEnvironment<Target> queryEnvironment) { + this.queryEnvironment = queryEnvironment; + } + + @Override + public String getTargetKind(Target target) { + return target.getTargetKind(); + } + + @Override + public String getLabel(Target target) { + return target.getLabel().toString(); + } + + @Override + public List<Target> getLabelListAttr(QueryExpression caller, Target target, String attrName, + String errorMsgPrefix) throws QueryException { + Preconditions.checkArgument(target instanceof Rule); + + List<Target> result = new ArrayList<>(); + Rule rule = (Rule) target; + + AggregatingAttributeMapper attrMap = AggregatingAttributeMapper.of(rule); + Type<?> attrType = attrMap.getAttributeType(attrName); + if (attrType == null) { + // Return an empty list if the attribute isn't defined for this rule. + return ImmutableList.of(); + } + for (Object value : attrMap.visitAttribute(attrName, attrType)) { + // Computed defaults may have null values. + if (value != null) { + for (Label label : attrType.getLabels(value)) { + try { + result.add(queryEnvironment.getTarget(label)); + } catch (TargetNotFoundException e) { + queryEnvironment.reportBuildFileError(caller, errorMsgPrefix + e.getMessage()); + } + } + } + } + + return result; + } + + @Override + public List<String> getStringListAttr(Target target, String attrName) { + Preconditions.checkArgument(target instanceof Rule); + return NonconfigurableAttributeMapper.of((Rule) target).get(attrName, Type.STRING_LIST); + } + + @Override + public String getStringAttr(Target target, String attrName) { + Preconditions.checkArgument(target instanceof Rule); + return NonconfigurableAttributeMapper.of((Rule) target).get(attrName, Type.STRING); + } + + @Override + public Iterable<String> getAttrAsString(Target target, String attrName) { + Preconditions.checkArgument(target instanceof Rule); + List<String> values = new ArrayList<>(); // May hold null values. + Attribute attribute = ((Rule) target).getAttributeDefinition(attrName); + if (attribute != null) { + Type<?> attributeType = attribute.getType(); + for (Object attrValue : AggregatingAttributeMapper.of((Rule) target).visitAttribute( + attribute.getName(), attributeType)) { + + // Ugly hack to maintain backward 'attr' query compatibility for BOOLEAN and TRISTATE + // attributes. These are internally stored as actual Boolean or TriState objects but were + // historically queried as integers. To maintain compatibility, we inspect their actual + // value and return the integer equivalent represented as a String. This code is the + // opposite of the code in BooleanType and TriStateType respectively. + if (attributeType == BOOLEAN) { + values.add(Type.BOOLEAN.cast(attrValue) ? "1" : "0"); + } else if (attributeType == TRISTATE) { + switch (Type.TRISTATE.cast(attrValue)) { + case AUTO : + values.add("-1"); + break; + case NO : + values.add("0"); + break; + case YES : + values.add("1"); + break; + default : + throw new AssertionError("This can't happen!"); + } + } else { + values.add(attrValue == null ? null : attrValue.toString()); + } + } + } + return values; + } + + @Override + public boolean isRule(Target target) { + return target instanceof Rule; + } + + @Override + public boolean isTestRule(Target target) { + return TargetUtils.isTestRule(target); + } + + @Override + public boolean isTestSuite(Target target) { + return TargetUtils.isTestSuiteRule(target); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java index 5062813f4a..db11cd5064 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java @@ -17,9 +17,13 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.pkgcache.PackageCacheOptions; -import com.google.devtools.build.lib.query2.BlazeQueryEnvironment; +import com.google.devtools.build.lib.pkgcache.PackageManager; +import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; +import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader; +import com.google.devtools.build.lib.query2.AbstractBlazeQueryEnvironment; import com.google.devtools.build.lib.query2.SkyframeQueryEnvironment; import com.google.devtools.build.lib.query2.engine.BlazeQueryEvalResult; import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction; @@ -102,9 +106,10 @@ public final class QueryCommand implements BlazeCommand { String query = Joiner.on(' ').join(options.getResidue()); Set<Setting> settings = queryOptions.toSettings(); - BlazeQueryEnvironment env = newQueryEnvironment( + AbstractBlazeQueryEnvironment<Target> env = newQueryEnvironment( runtime, queryOptions.keepGoing, + orderResults(queryOptions, formatter), queryOptions.loadingPhaseThreads, settings); @@ -119,7 +124,7 @@ public final class QueryCommand implements BlazeCommand { } // 2. Evaluate expression: - BlazeQueryEvalResult<Target> result; + QueryEvalResult<Target> result; try { result = env.evaluateQuery(expr); } catch (QueryException e) { @@ -146,10 +151,15 @@ public final class QueryCommand implements BlazeCommand { } @VisibleForTesting + public static boolean orderResults(QueryOptions queryOptions, OutputFormatter formatter) { + return queryOptions.orderResults || !(formatter instanceof UnorderedFormatter); + } + + @VisibleForTesting public static void output(QueryOptions queryOptions, QueryEvalResult<Target> result, OutputFormatter formatter, PrintStream outputStream) throws IOException { - if (queryOptions.orderResults || !(formatter instanceof UnorderedFormatter)) { + if (orderResults(queryOptions, formatter)) { formatter.output(queryOptions, ((BlazeQueryEvalResult<Target>) result).getResultGraph(), outputStream); } else { @@ -159,16 +169,28 @@ public final class QueryCommand implements BlazeCommand { } @VisibleForTesting // for com.google.devtools.deps.gquery.test.QueryResultTestUtil - public static BlazeQueryEnvironment newQueryEnvironment(BlazeRuntime runtime, - boolean keepGoing, int loadingPhaseThreads, Set<Setting> settings) { + public static AbstractBlazeQueryEnvironment<Target> newQueryEnvironment(BlazeRuntime runtime, + boolean keepGoing, boolean orderedResults, int loadingPhaseThreads, + Set<Setting> settings) { ImmutableList.Builder<QueryFunction> functions = ImmutableList.builder(); for (BlazeModule module : runtime.getBlazeModules()) { functions.addAll(module.getQueryFunctions()); } - return new SkyframeQueryEnvironment( - runtime.getPackageManager().newTransitiveLoader(), - runtime.getPackageManager(), - runtime.getTargetPatternEvaluator(), - keepGoing, loadingPhaseThreads, runtime.getReporter(), settings, functions.build()); + return newQueryEnvironment( + runtime.getPackageManager().newTransitiveLoader(), + runtime.getPackageManager(), + runtime.getTargetPatternEvaluator(), + keepGoing, orderedResults, loadingPhaseThreads, runtime.getReporter(), settings, + functions.build()); + } + + @VisibleForTesting + public static AbstractBlazeQueryEnvironment<Target> newQueryEnvironment( + TransitivePackageLoader transitivePackageLoader, PackageManager packageManager, + TargetPatternEvaluator targetPatternEvaluator, boolean keepGoing, boolean orderedResults, + int loadingPhaseThreads, + EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions) { + return new SkyframeQueryEnvironment(transitivePackageLoader, packageManager, + targetPatternEvaluator, keepGoing, loadingPhaseThreads, eventHandler, settings, functions); } } |