blob: 004adec534e895adf770a2497bebed0e000b7b2a [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;
25import com.google.devtools.build.lib.packages.Attribute;
26import com.google.devtools.build.lib.packages.Rule;
27import com.google.devtools.build.lib.packages.Target;
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +000028import com.google.devtools.build.lib.pkgcache.PackageProvider;
Mark Schallerb889cf32015-03-17 20:55:30 +000029import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000030import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +000031import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader;
Nathan Harmatad4803012015-09-08 20:03:22 +000032import com.google.devtools.build.lib.profiler.AutoProfiler;
Miguel Alcon Pintob6510262015-12-10 17:50:15 +000033import com.google.devtools.build.lib.query2.engine.Callback;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000034import com.google.devtools.build.lib.query2.engine.QueryEnvironment;
35import com.google.devtools.build.lib.query2.engine.QueryEvalResult;
36import com.google.devtools.build.lib.query2.engine.QueryException;
37import com.google.devtools.build.lib.query2.engine.QueryExpression;
Janak Ramakrishnan0dbc1f92016-01-07 19:12:06 +000038import com.google.devtools.build.lib.query2.engine.QueryUtil.AggregateAllCallback;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000039import com.google.devtools.build.lib.util.BinaryPredicate;
Mark Schaller6df81792015-12-10 18:47:47 +000040import com.google.devtools.build.lib.util.Preconditions;
Janak Ramakrishnane72d5222015-02-26 17:09:18 +000041import com.google.devtools.build.skyframe.WalkableGraph.WalkableGraphFactory;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000042
43import java.util.Collection;
44import java.util.HashMap;
45import java.util.LinkedHashSet;
46import java.util.List;
47import java.util.Map;
48import java.util.Set;
Miguel Alcon Pintob6510262015-12-10 17:50:15 +000049import java.util.concurrent.atomic.AtomicBoolean;
Eric Fellheimera39f8a92015-07-28 19:11:23 +000050import java.util.logging.Logger;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000051
Mark Schallerb889cf32015-03-17 20:55:30 +000052import javax.annotation.Nullable;
53
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000054/**
55 * {@link QueryEnvironment} that can evaluate queries to produce a result, and implements as much
56 * of QueryEnvironment as possible while remaining mostly agnostic as to the objects being stored.
57 */
58public abstract class AbstractBlazeQueryEnvironment<T> implements QueryEnvironment<T> {
59 protected final ErrorSensingEventHandler eventHandler;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000060 private final Map<String, Set<T>> letBindings = new HashMap<>();
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000061 protected final boolean keepGoing;
62 protected final boolean strictScope;
63
64 protected final BinaryPredicate<Rule, Attribute> dependencyFilter;
65 private final Predicate<Label> labelFilter;
66
67 private final Set<Setting> settings;
68 private final List<QueryFunction> extraFunctions;
69
Eric Fellheimera39f8a92015-07-28 19:11:23 +000070 private static final Logger LOG = Logger.getLogger(AbstractBlazeQueryEnvironment.class.getName());
71
Janak Ramakrishnane72d5222015-02-26 17:09:18 +000072 protected AbstractBlazeQueryEnvironment(boolean keepGoing,
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000073 boolean strictScope,
74 Predicate<Label> labelFilter,
75 EventHandler eventHandler,
76 Set<Setting> settings,
77 Iterable<QueryFunction> extraFunctions) {
78 this.eventHandler = new ErrorSensingEventHandler(eventHandler);
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000079 this.keepGoing = keepGoing;
80 this.strictScope = strictScope;
81 this.dependencyFilter = constructDependencyFilter(settings);
82 this.labelFilter = labelFilter;
83 this.settings = Sets.immutableEnumSet(settings);
84 this.extraFunctions = ImmutableList.copyOf(extraFunctions);
85 }
86
87 private static BinaryPredicate<Rule, Attribute> constructDependencyFilter(Set<Setting> settings) {
88 BinaryPredicate<Rule, Attribute> specifiedFilter =
89 settings.contains(Setting.NO_HOST_DEPS) ? Rule.NO_HOST_DEPS : Rule.ALL_DEPS;
90 if (settings.contains(Setting.NO_IMPLICIT_DEPS)) {
91 specifiedFilter = Rule.and(specifiedFilter, Rule.NO_IMPLICIT_DEPS);
92 }
93 if (settings.contains(Setting.NO_NODEP_DEPS)) {
94 specifiedFilter = Rule.and(specifiedFilter, Rule.NO_NODEP_ATTRIBUTES);
95 }
96 return specifiedFilter;
97 }
98
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +000099 public static AbstractBlazeQueryEnvironment<Target> newQueryEnvironment(
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000100 TransitivePackageLoader transitivePackageLoader, WalkableGraphFactory graphFactory,
101 PackageProvider packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000102 TargetPatternEvaluator targetPatternEvaluator, boolean keepGoing, boolean orderedResults,
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000103 List<String> universeScope, int loadingPhaseThreads,
Mark Schallerb889cf32015-03-17 20:55:30 +0000104 EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions,
105 @Nullable PathPackageLocator packagePath) {
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000106 return newQueryEnvironment(transitivePackageLoader, graphFactory, packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000107 targetPatternEvaluator, keepGoing, /*strictScope=*/true, orderedResults,
Mark Schallerb889cf32015-03-17 20:55:30 +0000108 universeScope, loadingPhaseThreads, Rule.ALL_LABELS, eventHandler, settings, functions,
109 packagePath);
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000110 }
111
112 public static AbstractBlazeQueryEnvironment<Target> newQueryEnvironment(
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000113 TransitivePackageLoader transitivePackageLoader, WalkableGraphFactory graphFactory,
114 PackageProvider packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000115 TargetPatternEvaluator targetPatternEvaluator, boolean keepGoing, boolean strictScope,
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000116 boolean orderedResults, List<String> universeScope, int loadingPhaseThreads,
117 Predicate<Label> labelFilter,
Mark Schallerb889cf32015-03-17 20:55:30 +0000118 EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions,
119 @Nullable PathPackageLocator packagePath) {
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000120 Preconditions.checkNotNull(universeScope);
Mark Schallerb889cf32015-03-17 20:55:30 +0000121 return orderedResults || universeScope.isEmpty() || packagePath == null
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000122 ? new BlazeQueryEnvironment(transitivePackageLoader, packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000123 targetPatternEvaluator, keepGoing, strictScope, loadingPhaseThreads,
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000124 labelFilter, eventHandler, settings, functions)
125 : new SkyQueryEnvironment(
126 keepGoing, strictScope, loadingPhaseThreads, labelFilter, eventHandler, settings,
Mark Schallerb889cf32015-03-17 20:55:30 +0000127 functions, targetPatternEvaluator.getOffset(), graphFactory, universeScope,
128 packagePath);
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000129 }
130
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000131 /**
132 * Evaluate the specified query expression in this environment.
133 *
134 * @return a {@link QueryEvalResult} object that contains the resulting set of targets and a bit
Mark Schaller895b3d22015-03-23 14:27:26 +0000135 * to indicate whether errors occurred during evaluation; note that the
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000136 * success status can only be false if {@code --keep_going} was in effect
137 * @throws QueryException if the evaluation failed and {@code --nokeep_going} was in
138 * effect
139 */
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000140 public QueryEvalResult evaluateQuery(QueryExpression expr, final Callback<T> callback)
Janak Ramakrishnan89907392015-07-08 21:43:31 +0000141 throws QueryException, InterruptedException {
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000142
143 final AtomicBoolean empty = new AtomicBoolean(true);
144 try (final AutoProfiler p = AutoProfiler.logged("evaluating query", LOG)) {
Nathan Harmatad4803012015-09-08 20:03:22 +0000145
146 // In the --nokeep_going case, errors are reported in the order in which the patterns are
147 // specified; using a linked hash set here makes sure that the left-most error is reported.
148 Set<String> targetPatternSet = new LinkedHashSet<>();
149 expr.collectTargetPatterns(targetPatternSet);
150 try {
Janak Ramakrishnanee6208b2016-01-07 20:17:41 +0000151 preloadOrThrow(expr, targetPatternSet);
Nathan Harmatad4803012015-09-08 20:03:22 +0000152 } catch (TargetParsingException e) {
153 // Unfortunately, by evaluating the patterns in parallel, we lose some location information.
154 throw new QueryException(expr, e.getMessage());
155 }
Nathan Harmatad4803012015-09-08 20:03:22 +0000156 try {
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000157 this.eval(expr, new Callback<T>() {
158 @Override
159 public void process(Iterable<T> partialResult)
160 throws QueryException, InterruptedException {
161 empty.compareAndSet(true, Iterables.isEmpty(partialResult));
162 callback.process(partialResult);
163 }
164 });
Nathan Harmatad4803012015-09-08 20:03:22 +0000165 } catch (QueryException e) {
166 throw new QueryException(e, expr);
Eric Fellheimera39f8a92015-07-28 19:11:23 +0000167 }
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000168 }
169
170 if (eventHandler.hasErrors()) {
171 if (!keepGoing) {
172 // This case represents loading-phase errors reported during evaluation
173 // of target patterns that don't cause evaluation to fail per se.
174 throw new QueryException("Evaluation of query \"" + expr
175 + "\" failed due to BUILD file errors");
176 } else {
177 eventHandler.handle(Event.warn("--keep_going specified, ignoring errors. "
178 + "Results may be inaccurate"));
179 }
180 }
181
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000182 return new QueryEvalResult(!eventHandler.hasErrors(), empty.get());
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000183 }
184
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000185 public QueryEvalResult evaluateQuery(String query, Callback<T> callback)
Janak Ramakrishnan89907392015-07-08 21:43:31 +0000186 throws QueryException, InterruptedException {
Miguel Alcon Pintob6510262015-12-10 17:50:15 +0000187 return evaluateQuery(QueryExpression.parse(query, this), callback);
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000188 }
189
190 @Override
191 public void reportBuildFileError(QueryExpression caller, String message) throws QueryException {
192 if (!keepGoing) {
193 throw new QueryException(caller, message);
194 } else {
195 // Keep consistent with evaluateQuery() above.
196 eventHandler.handle(Event.error("Evaluation of query \"" + caller + "\" failed: " + message));
197 }
198 }
199
200 public abstract Target getTarget(Label label) throws TargetNotFoundException, QueryException;
201
202 @Override
203 public Set<T> getVariable(String name) {
204 return letBindings.get(name);
205 }
206
207 @Override
208 public Set<T> setVariable(String name, Set<T> value) {
209 return letBindings.put(name, value);
210 }
211
212 protected boolean validateScope(Label label, boolean strict) throws QueryException {
213 if (!labelFilter.apply(label)) {
214 String error = String.format("target '%s' is not within the scope of the query", label);
215 if (strict) {
216 throw new QueryException(error);
217 } else {
218 eventHandler.handle(Event.warn(error + ". Skipping"));
219 return false;
220 }
221 }
222 return true;
223 }
224
225 public Set<T> evalTargetPattern(QueryExpression caller, String pattern)
226 throws QueryException {
Janak Ramakrishnanee6208b2016-01-07 20:17:41 +0000227 try {
228 preloadOrThrow(caller, ImmutableList.of(pattern));
229 } catch (TargetParsingException e) {
230 // Will skip the target and keep going if -k is specified.
231 reportBuildFileError(caller, e.getMessage());
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000232 }
Janak Ramakrishnan0dbc1f92016-01-07 19:12:06 +0000233 AggregateAllCallback<T> aggregatingCallback = new AggregateAllCallback<>();
234 getTargetsMatchingPattern(caller, pattern, aggregatingCallback);
235 return aggregatingCallback.getResult();
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000236 }
237
Janak Ramakrishnanee6208b2016-01-07 20:17:41 +0000238 /**
239 * Perform any work that should be done ahead of time to resolve the target patterns in the
240 * query. Implementations may choose to cache the results of resolving the patterns, cache
241 * intermediate work, or not cache and resolve patterns on the fly.
242 */
243 protected abstract void preloadOrThrow(QueryExpression caller, Collection<String> patterns)
Janak Ramakrishnancbe26342015-08-17 18:57:57 +0000244 throws QueryException, TargetParsingException;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000245
246 @Override
247 public boolean isSettingEnabled(Setting setting) {
248 return settings.contains(Preconditions.checkNotNull(setting));
249 }
250
251 @Override
252 public Iterable<QueryFunction> getFunctions() {
253 ImmutableList.Builder<QueryFunction> builder = ImmutableList.builder();
254 builder.addAll(DEFAULT_QUERY_FUNCTIONS);
255 builder.addAll(extraFunctions);
256 return builder.build();
257 }
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000258}