blob: f4f1ecc51abf1689dd62697c276c3d0742d849e6 [file] [log] [blame]
Janak Ramakrishnana46125c2015-02-11 16:51:37 +00001// Copyright 2015 Google Inc. All rights reserved.
2//
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
16import com.google.common.base.Preconditions;
17import com.google.common.base.Predicate;
18import com.google.common.collect.ImmutableList;
Janak Ramakrishnancbe26342015-08-17 18:57:57 +000019import com.google.common.collect.ImmutableSet;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000020import com.google.common.collect.Sets;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000021import com.google.devtools.build.lib.cmdline.Label;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000022import com.google.devtools.build.lib.cmdline.TargetParsingException;
23import com.google.devtools.build.lib.events.ErrorSensingEventHandler;
24import com.google.devtools.build.lib.events.Event;
25import com.google.devtools.build.lib.events.EventHandler;
26import com.google.devtools.build.lib.packages.Attribute;
27import com.google.devtools.build.lib.packages.Rule;
28import com.google.devtools.build.lib.packages.Target;
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +000029import com.google.devtools.build.lib.pkgcache.PackageProvider;
Mark Schallerb889cf32015-03-17 20:55:30 +000030import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000031import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +000032import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader;
Nathan Harmatad4803012015-09-08 20:03:22 +000033import com.google.devtools.build.lib.profiler.AutoProfiler;
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 Ramakrishnana46125c2015-02-11 16:51:37 +000038import com.google.devtools.build.lib.util.BinaryPredicate;
Janak Ramakrishnane72d5222015-02-26 17:09:18 +000039import com.google.devtools.build.skyframe.WalkableGraph.WalkableGraphFactory;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000040
41import java.util.Collection;
42import java.util.HashMap;
43import java.util.LinkedHashSet;
44import java.util.List;
45import java.util.Map;
46import java.util.Set;
Eric Fellheimera39f8a92015-07-28 19:11:23 +000047import java.util.logging.Logger;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000048
Mark Schallerb889cf32015-03-17 20:55:30 +000049import javax.annotation.Nullable;
50
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000051/**
52 * {@link QueryEnvironment} that can evaluate queries to produce a result, and implements as much
53 * of QueryEnvironment as possible while remaining mostly agnostic as to the objects being stored.
54 */
55public abstract class AbstractBlazeQueryEnvironment<T> implements QueryEnvironment<T> {
56 protected final ErrorSensingEventHandler eventHandler;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000057 private final Map<String, Set<T>> letBindings = new HashMap<>();
Janak Ramakrishnancbe26342015-08-17 18:57:57 +000058 protected final Map<String, Set<Target>> resolvedTargetPatterns = new HashMap<>();
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000059 protected final boolean keepGoing;
60 protected final boolean strictScope;
61
62 protected final BinaryPredicate<Rule, Attribute> dependencyFilter;
63 private final Predicate<Label> labelFilter;
64
65 private final Set<Setting> settings;
66 private final List<QueryFunction> extraFunctions;
67
Eric Fellheimera39f8a92015-07-28 19:11:23 +000068 private static final Logger LOG = Logger.getLogger(AbstractBlazeQueryEnvironment.class.getName());
69
Janak Ramakrishnane72d5222015-02-26 17:09:18 +000070 protected AbstractBlazeQueryEnvironment(boolean keepGoing,
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000071 boolean strictScope,
72 Predicate<Label> labelFilter,
73 EventHandler eventHandler,
74 Set<Setting> settings,
75 Iterable<QueryFunction> extraFunctions) {
76 this.eventHandler = new ErrorSensingEventHandler(eventHandler);
Janak Ramakrishnana46125c2015-02-11 16:51:37 +000077 this.keepGoing = keepGoing;
78 this.strictScope = strictScope;
79 this.dependencyFilter = constructDependencyFilter(settings);
80 this.labelFilter = labelFilter;
81 this.settings = Sets.immutableEnumSet(settings);
82 this.extraFunctions = ImmutableList.copyOf(extraFunctions);
83 }
84
85 private static BinaryPredicate<Rule, Attribute> constructDependencyFilter(Set<Setting> settings) {
86 BinaryPredicate<Rule, Attribute> specifiedFilter =
87 settings.contains(Setting.NO_HOST_DEPS) ? Rule.NO_HOST_DEPS : Rule.ALL_DEPS;
88 if (settings.contains(Setting.NO_IMPLICIT_DEPS)) {
89 specifiedFilter = Rule.and(specifiedFilter, Rule.NO_IMPLICIT_DEPS);
90 }
91 if (settings.contains(Setting.NO_NODEP_DEPS)) {
92 specifiedFilter = Rule.and(specifiedFilter, Rule.NO_NODEP_ATTRIBUTES);
93 }
94 return specifiedFilter;
95 }
96
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +000097 public static AbstractBlazeQueryEnvironment<Target> newQueryEnvironment(
Janak Ramakrishnane72d5222015-02-26 17:09:18 +000098 TransitivePackageLoader transitivePackageLoader, WalkableGraphFactory graphFactory,
99 PackageProvider packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000100 TargetPatternEvaluator targetPatternEvaluator, boolean keepGoing, boolean orderedResults,
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000101 List<String> universeScope, int loadingPhaseThreads,
Mark Schallerb889cf32015-03-17 20:55:30 +0000102 EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions,
103 @Nullable PathPackageLocator packagePath) {
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000104 return newQueryEnvironment(transitivePackageLoader, graphFactory, packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000105 targetPatternEvaluator, keepGoing, /*strictScope=*/true, orderedResults,
Mark Schallerb889cf32015-03-17 20:55:30 +0000106 universeScope, loadingPhaseThreads, Rule.ALL_LABELS, eventHandler, settings, functions,
107 packagePath);
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000108 }
109
110 public static AbstractBlazeQueryEnvironment<Target> newQueryEnvironment(
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000111 TransitivePackageLoader transitivePackageLoader, WalkableGraphFactory graphFactory,
112 PackageProvider packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000113 TargetPatternEvaluator targetPatternEvaluator, boolean keepGoing, boolean strictScope,
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000114 boolean orderedResults, List<String> universeScope, int loadingPhaseThreads,
115 Predicate<Label> labelFilter,
Mark Schallerb889cf32015-03-17 20:55:30 +0000116 EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions,
117 @Nullable PathPackageLocator packagePath) {
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000118 Preconditions.checkNotNull(universeScope);
Mark Schallerb889cf32015-03-17 20:55:30 +0000119 return orderedResults || universeScope.isEmpty() || packagePath == null
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000120 ? new BlazeQueryEnvironment(transitivePackageLoader, packageProvider,
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000121 targetPatternEvaluator, keepGoing, strictScope, loadingPhaseThreads,
Janak Ramakrishnane72d5222015-02-26 17:09:18 +0000122 labelFilter, eventHandler, settings, functions)
123 : new SkyQueryEnvironment(
124 keepGoing, strictScope, loadingPhaseThreads, labelFilter, eventHandler, settings,
Mark Schallerb889cf32015-03-17 20:55:30 +0000125 functions, targetPatternEvaluator.getOffset(), graphFactory, universeScope,
126 packagePath);
Janak Ramakrishnanfe1056f2015-02-11 21:16:06 +0000127 }
128
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000129 /**
130 * Evaluate the specified query expression in this environment.
131 *
132 * @return a {@link QueryEvalResult} object that contains the resulting set of targets and a bit
Mark Schaller895b3d22015-03-23 14:27:26 +0000133 * to indicate whether errors occurred during evaluation; note that the
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000134 * success status can only be false if {@code --keep_going} was in effect
135 * @throws QueryException if the evaluation failed and {@code --nokeep_going} was in
136 * effect
137 */
Janak Ramakrishnan89907392015-07-08 21:43:31 +0000138 public QueryEvalResult<T> evaluateQuery(QueryExpression expr)
139 throws QueryException, InterruptedException {
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000140 Set<T> resultNodes;
Nathan Harmatad4803012015-09-08 20:03:22 +0000141 try (AutoProfiler p = AutoProfiler.logged("evaluating query", LOG)) {
142 resolvedTargetPatterns.clear();
143
144 // In the --nokeep_going case, errors are reported in the order in which the patterns are
145 // specified; using a linked hash set here makes sure that the left-most error is reported.
146 Set<String> targetPatternSet = new LinkedHashSet<>();
147 expr.collectTargetPatterns(targetPatternSet);
148 try {
149 resolvedTargetPatterns.putAll(preloadOrThrow(expr, targetPatternSet));
150 } catch (TargetParsingException e) {
151 // Unfortunately, by evaluating the patterns in parallel, we lose some location information.
152 throw new QueryException(expr, e.getMessage());
153 }
154
155 try {
156 resultNodes = expr.eval(this);
157 } catch (QueryException e) {
158 throw new QueryException(e, expr);
Eric Fellheimera39f8a92015-07-28 19:11:23 +0000159 }
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000160 }
161
162 if (eventHandler.hasErrors()) {
163 if (!keepGoing) {
164 // This case represents loading-phase errors reported during evaluation
165 // of target patterns that don't cause evaluation to fail per se.
166 throw new QueryException("Evaluation of query \"" + expr
167 + "\" failed due to BUILD file errors");
168 } else {
169 eventHandler.handle(Event.warn("--keep_going specified, ignoring errors. "
170 + "Results may be inaccurate"));
171 }
172 }
173
174 return new QueryEvalResult<>(!eventHandler.hasErrors(), resultNodes);
175 }
176
Janak Ramakrishnan89907392015-07-08 21:43:31 +0000177 public QueryEvalResult<T> evaluateQuery(String query)
178 throws QueryException, InterruptedException {
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000179 return evaluateQuery(QueryExpression.parse(query, this));
180 }
181
182 @Override
183 public void reportBuildFileError(QueryExpression caller, String message) throws QueryException {
184 if (!keepGoing) {
185 throw new QueryException(caller, message);
186 } else {
187 // Keep consistent with evaluateQuery() above.
188 eventHandler.handle(Event.error("Evaluation of query \"" + caller + "\" failed: " + message));
189 }
190 }
191
192 public abstract Target getTarget(Label label) throws TargetNotFoundException, QueryException;
193
194 @Override
195 public Set<T> getVariable(String name) {
196 return letBindings.get(name);
197 }
198
199 @Override
200 public Set<T> setVariable(String name, Set<T> value) {
201 return letBindings.put(name, value);
202 }
203
204 protected boolean validateScope(Label label, boolean strict) throws QueryException {
205 if (!labelFilter.apply(label)) {
206 String error = String.format("target '%s' is not within the scope of the query", label);
207 if (strict) {
208 throw new QueryException(error);
209 } else {
210 eventHandler.handle(Event.warn(error + ". Skipping"));
211 return false;
212 }
213 }
214 return true;
215 }
216
217 public Set<T> evalTargetPattern(QueryExpression caller, String pattern)
218 throws QueryException {
219 if (!resolvedTargetPatterns.containsKey(pattern)) {
220 try {
Mark Schaller895b3d22015-03-23 14:27:26 +0000221 resolvedTargetPatterns.putAll(preloadOrThrow(caller, ImmutableList.of(pattern)));
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000222 } catch (TargetParsingException e) {
223 // Will skip the target and keep going if -k is specified.
Janak Ramakrishnancbe26342015-08-17 18:57:57 +0000224 resolvedTargetPatterns.put(pattern, ImmutableSet.<Target>of());
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000225 reportBuildFileError(caller, e.getMessage());
226 }
227 }
228 return getTargetsMatchingPattern(caller, pattern);
229 }
230
Janak Ramakrishnancbe26342015-08-17 18:57:57 +0000231 protected abstract Map<String, Set<Target>> preloadOrThrow(
232 QueryExpression caller, Collection<String> patterns)
233 throws QueryException, TargetParsingException;
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000234
235 @Override
236 public boolean isSettingEnabled(Setting setting) {
237 return settings.contains(Preconditions.checkNotNull(setting));
238 }
239
240 @Override
241 public Iterable<QueryFunction> getFunctions() {
242 ImmutableList.Builder<QueryFunction> builder = ImmutableList.builder();
243 builder.addAll(DEFAULT_QUERY_FUNCTIONS);
244 builder.addAll(extraFunctions);
245 return builder.build();
246 }
Janak Ramakrishnana46125c2015-02-11 16:51:37 +0000247}