blob: 1962cdfaf46727b776d4360368be6fdf164a821f [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Janak Ramakrishnana46125c2015-02-11 16:51:37 +00002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.query2;
15
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000016import com.google.common.base.Predicate;
17import com.google.common.collect.ImmutableList;
Miguel Alcon Pintob6510262015-12-10 17:50:15 +000018import com.google.common.collect.Iterables;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000019import com.google.common.collect.Sets;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000020import com.google.devtools.build.lib.cmdline.Label;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000021import com.google.devtools.build.lib.cmdline.TargetParsingException;
22import com.google.devtools.build.lib.events.ErrorSensingEventHandler;
23import com.google.devtools.build.lib.events.Event;
24import com.google.devtools.build.lib.events.EventHandler;
Dmitry Lomov6073eb62016-01-21 21:26:32 +000025import com.google.devtools.build.lib.packages.DependencyFilter;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000026import com.google.devtools.build.lib.packages.Target;
Nathan Harmatad4803012015-09-08 20:03:22 +000027import com.google.devtools.build.lib.profiler.AutoProfiler;
Miguel Alcon Pintob6510262015-12-10 17:50:15 +000028import com.google.devtools.build.lib.query2.engine.Callback;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000029import com.google.devtools.build.lib.query2.engine.QueryEnvironment;
30import com.google.devtools.build.lib.query2.engine.QueryEvalResult;
31import com.google.devtools.build.lib.query2.engine.QueryException;
32import com.google.devtools.build.lib.query2.engine.QueryExpression;
Nathan Harmataf37750a2016-09-07 14:58:14 +000033import com.google.devtools.build.lib.query2.engine.QueryExpressionEvalListener;
Janak Ramakrishnan0dbc1f92016-01-07 19:12:06 +000034import com.google.devtools.build.lib.query2.engine.QueryUtil.AggregateAllCallback;
Nathan Harmatabc47f402016-07-13 16:22:30 +000035import com.google.devtools.build.lib.query2.engine.VariableContext;
Mark Schaller6df81792015-12-10 18:47:47 +000036import com.google.devtools.build.lib.util.Preconditions;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000037import java.util.Collection;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000038import java.util.LinkedHashSet;
39import java.util.List;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000040import java.util.Set;
Miguel Alcon Pintob6510262015-12-10 17:50:15 +000041import java.util.concurrent.atomic.AtomicBoolean;
Eric Fellheimera39f8a92015-07-28 19:11:23 +000042import java.util.logging.Logger;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000043
44/**
Mark Schaller6cebed62016-06-27 18:05:39 +000045 * {@link QueryEnvironment} that can evaluate queries to produce a result, and implements as much of
46 * QueryEnvironment as possible while remaining mostly agnostic as to the objects being stored.
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000047 */
Mark Schaller6cebed62016-06-27 18:05:39 +000048public abstract class AbstractBlazeQueryEnvironment<T>
Nathan Harmata71616b12016-09-28 20:20:48 +000049 implements QueryEnvironment<T> {
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000050 protected final ErrorSensingEventHandler eventHandler;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000051 protected final boolean keepGoing;
52 protected final boolean strictScope;
53
Dmitry Lomov6073eb62016-01-21 21:26:32 +000054 protected final DependencyFilter dependencyFilter;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000055 private final Predicate<Label> labelFilter;
56
57 private final Set<Setting> settings;
58 private final List<QueryFunction> extraFunctions;
Nathan Harmataf37750a2016-09-07 14:58:14 +000059 private final QueryExpressionEvalListener<T> evalListener;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000060
Nathan Harmataf0cc5b82016-03-18 14:56:28 +000061 private static final Logger LOG = Logger.getLogger(AbstractBlazeQueryEnvironment.class.getName());
Eric Fellheimera39f8a92015-07-28 19:11:23 +000062
Janak Ramakrishnane72d5222015-02-26 17:09:18 +000063 protected AbstractBlazeQueryEnvironment(boolean keepGoing,
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000064 boolean strictScope,
65 Predicate<Label> labelFilter,
66 EventHandler eventHandler,
67 Set<Setting> settings,
Nathan Harmataf37750a2016-09-07 14:58:14 +000068 Iterable<QueryFunction> extraFunctions,
69 QueryExpressionEvalListener<T> evalListener) {
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000070 this.eventHandler = new ErrorSensingEventHandler(eventHandler);
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000071 this.keepGoing = keepGoing;
72 this.strictScope = strictScope;
73 this.dependencyFilter = constructDependencyFilter(settings);
74 this.labelFilter = labelFilter;
75 this.settings = Sets.immutableEnumSet(settings);
76 this.extraFunctions = ImmutableList.copyOf(extraFunctions);
Nathan Harmataf37750a2016-09-07 14:58:14 +000077 this.evalListener = evalListener;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000078 }
79
Dmitry Lomovcb14c5e2016-01-21 22:04:01 +000080 private static DependencyFilter constructDependencyFilter(
81 Set<Setting> settings) {
Dmitry Lomov6073eb62016-01-21 21:26:32 +000082 DependencyFilter specifiedFilter =
83 settings.contains(Setting.NO_HOST_DEPS)
84 ? DependencyFilter.NO_HOST_DEPS
85 : DependencyFilter.ALL_DEPS;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000086 if (settings.contains(Setting.NO_IMPLICIT_DEPS)) {
Dmitry Lomov6073eb62016-01-21 21:26:32 +000087 specifiedFilter = DependencyFilter.and(specifiedFilter, DependencyFilter.NO_IMPLICIT_DEPS);
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000088 }
89 if (settings.contains(Setting.NO_NODEP_DEPS)) {
Dmitry Lomov6073eb62016-01-21 21:26:32 +000090 specifiedFilter = DependencyFilter.and(specifiedFilter, DependencyFilter.NO_NODEP_ATTRIBUTES);
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000091 }
92 return specifiedFilter;
93 }
94
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000095 /**
96 * Evaluate the specified query expression in this environment.
97 *
98 * @return a {@link QueryEvalResult} object that contains the resulting set of targets and a bit
Mark Schaller895b3d22015-03-23 14:27:26 +000099 * to indicate whether errors occurred during evaluation; note that the
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000100 * success status can only be false if {@code --keep_going} was in effect
101 * @throws QueryException if the evaluation failed and {@code --nokeep_going} was in
102 * effect
103 */
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000104 public QueryEvalResult evaluateQuery(QueryExpression expr, final Callback<T> callback)
Janak Ramakrishnan89907392015-07-08 21:43:31 +0000105 throws QueryException, InterruptedException {
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000106
107 final AtomicBoolean empty = new AtomicBoolean(true);
108 try (final AutoProfiler p = AutoProfiler.logged("evaluating query", LOG)) {
Nathan Harmatad4803012015-09-08 20:03:22 +0000109
110 // In the --nokeep_going case, errors are reported in the order in which the patterns are
111 // specified; using a linked hash set here makes sure that the left-most error is reported.
112 Set<String> targetPatternSet = new LinkedHashSet<>();
113 expr.collectTargetPatterns(targetPatternSet);
114 try {
Janak Ramakrishnanee6208b2016-01-07 20:17:41 +0000115 preloadOrThrow(expr, targetPatternSet);
Nathan Harmatad4803012015-09-08 20:03:22 +0000116 } catch (TargetParsingException e) {
117 // Unfortunately, by evaluating the patterns in parallel, we lose some location information.
118 throw new QueryException(expr, e.getMessage());
119 }
Nathan Harmatad4803012015-09-08 20:03:22 +0000120 try {
Nathan Harmatabc47f402016-07-13 16:22:30 +0000121 this.eval(expr, VariableContext.<T>empty(), new Callback<T>() {
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000122 @Override
123 public void process(Iterable<T> partialResult)
124 throws QueryException, InterruptedException {
125 empty.compareAndSet(true, Iterables.isEmpty(partialResult));
126 callback.process(partialResult);
127 }
128 });
Nathan Harmatad4803012015-09-08 20:03:22 +0000129 } catch (QueryException e) {
130 throw new QueryException(e, expr);
Eric Fellheimera39f8a92015-07-28 19:11:23 +0000131 }
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000132 }
133
134 if (eventHandler.hasErrors()) {
135 if (!keepGoing) {
136 // This case represents loading-phase errors reported during evaluation
137 // of target patterns that don't cause evaluation to fail per se.
138 throw new QueryException("Evaluation of query \"" + expr
139 + "\" failed due to BUILD file errors");
140 } else {
141 eventHandler.handle(Event.warn("--keep_going specified, ignoring errors. "
142 + "Results may be inaccurate"));
143 }
144 }
145
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000146 return new QueryEvalResult(!eventHandler.hasErrors(), empty.get());
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000147 }
148
Nathan Harmata2643c8e2016-07-01 23:19:23 +0000149 public QueryExpression transformParsedQuery(QueryExpression queryExpression) {
Nathan Harmataed935602016-03-02 01:16:14 +0000150 return queryExpression;
151 }
152
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000153 public QueryEvalResult evaluateQuery(String query, Callback<T> callback)
Janak Ramakrishnan89907392015-07-08 21:43:31 +0000154 throws QueryException, InterruptedException {
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000155 return evaluateQuery(QueryExpression.parse(query, this), callback);
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000156 }
157
158 @Override
159 public void reportBuildFileError(QueryExpression caller, String message) throws QueryException {
160 if (!keepGoing) {
161 throw new QueryException(caller, message);
162 } else {
163 // Keep consistent with evaluateQuery() above.
164 eventHandler.handle(Event.error("Evaluation of query \"" + caller + "\" failed: " + message));
165 }
166 }
167
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000168 public abstract Target getTarget(Label label)
169 throws TargetNotFoundException, QueryException, InterruptedException;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000170
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000171 protected boolean validateScope(Label label, boolean strict) throws QueryException {
172 if (!labelFilter.apply(label)) {
173 String error = String.format("target '%s' is not within the scope of the query", label);
174 if (strict) {
175 throw new QueryException(error);
176 } else {
177 eventHandler.handle(Event.warn(error + ". Skipping"));
178 return false;
179 }
180 }
181 return true;
182 }
183
184 public Set<T> evalTargetPattern(QueryExpression caller, String pattern)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000185 throws QueryException, InterruptedException {
Janak Ramakrishnanee6208b2016-01-07 20:17:41 +0000186 try {
187 preloadOrThrow(caller, ImmutableList.of(pattern));
188 } catch (TargetParsingException e) {
189 // Will skip the target and keep going if -k is specified.
190 reportBuildFileError(caller, e.getMessage());
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000191 }
Janak Ramakrishnan0dbc1f92016-01-07 19:12:06 +0000192 AggregateAllCallback<T> aggregatingCallback = new AggregateAllCallback<>();
193 getTargetsMatchingPattern(caller, pattern, aggregatingCallback);
194 return aggregatingCallback.getResult();
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000195 }
196
Janak Ramakrishnanee6208b2016-01-07 20:17:41 +0000197 /**
Janak Ramakrishnan71cdea42016-08-16 15:14:41 +0000198 * Perform any work that should be done ahead of time to resolve the target patterns in the query.
199 * Implementations may choose to cache the results of resolving the patterns, cache intermediate
200 * work, or not cache and resolve patterns on the fly.
Janak Ramakrishnanee6208b2016-01-07 20:17:41 +0000201 */
202 protected abstract void preloadOrThrow(QueryExpression caller, Collection<String> patterns)
Janak Ramakrishnan71cdea42016-08-16 15:14:41 +0000203 throws QueryException, TargetParsingException, InterruptedException;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000204
205 @Override
206 public boolean isSettingEnabled(Setting setting) {
207 return settings.contains(Preconditions.checkNotNull(setting));
208 }
209
210 @Override
211 public Iterable<QueryFunction> getFunctions() {
212 ImmutableList.Builder<QueryFunction> builder = ImmutableList.builder();
213 builder.addAll(DEFAULT_QUERY_FUNCTIONS);
214 builder.addAll(extraFunctions);
215 return builder.build();
216 }
Nathan Harmataf37750a2016-09-07 14:58:14 +0000217
218 @Override
219 public QueryExpressionEvalListener<T> getEvalListener() {
220 return evalListener;
221 }
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000222}