Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 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. |
| 14 | package com.google.devtools.build.lib.query2; |
| 15 | |
Janak Ramakrishnan | cbe2634 | 2015-08-17 18:57:57 +0000 | [diff] [blame] | 16 | import com.google.common.base.Function; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 17 | import com.google.common.base.Predicate; |
Janak Ramakrishnan | fe1056f | 2015-02-11 21:16:06 +0000 | [diff] [blame] | 18 | import com.google.common.collect.ImmutableSet; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 19 | import com.google.common.collect.Iterables; |
Janak Ramakrishnan | cbe2634 | 2015-08-17 18:57:57 +0000 | [diff] [blame] | 20 | import com.google.common.collect.Maps; |
Lukacs Berki | 6e91eb9 | 2015-09-21 09:12:37 +0000 | [diff] [blame] | 21 | import com.google.devtools.build.lib.cmdline.Label; |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 22 | import com.google.devtools.build.lib.cmdline.LabelSyntaxException; |
Janak Ramakrishnan | e72d522 | 2015-02-26 17:09:18 +0000 | [diff] [blame] | 23 | import com.google.devtools.build.lib.cmdline.ResolvedTargets; |
| 24 | import com.google.devtools.build.lib.cmdline.TargetParsingException; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 25 | import com.google.devtools.build.lib.events.EventHandler; |
| 26 | import com.google.devtools.build.lib.graph.Digraph; |
| 27 | import com.google.devtools.build.lib.graph.Node; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 28 | import com.google.devtools.build.lib.packages.Attribute; |
| 29 | import com.google.devtools.build.lib.packages.NoSuchThingException; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 30 | import com.google.devtools.build.lib.packages.OutputFile; |
| 31 | import com.google.devtools.build.lib.packages.Package; |
| 32 | import com.google.devtools.build.lib.packages.Rule; |
| 33 | import com.google.devtools.build.lib.packages.Target; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 34 | import com.google.devtools.build.lib.pkgcache.PackageProvider; |
| 35 | import com.google.devtools.build.lib.pkgcache.TargetEdgeObserver; |
| 36 | import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; |
| 37 | import com.google.devtools.build.lib.pkgcache.TargetProvider; |
Janak Ramakrishnan | fe1056f | 2015-02-11 21:16:06 +0000 | [diff] [blame] | 38 | import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader; |
Miguel Alcon Pinto | 42984f3 | 2015-11-06 19:05:13 +0000 | [diff] [blame] | 39 | import com.google.devtools.build.lib.query2.engine.Callback; |
Miguel Alcon Pinto | b651026 | 2015-12-10 17:50:15 +0000 | [diff] [blame] | 40 | import com.google.devtools.build.lib.query2.engine.DigraphQueryEvalResult; |
Janak Ramakrishnan | a46125c | 2015-02-11 16:51:37 +0000 | [diff] [blame] | 41 | import com.google.devtools.build.lib.query2.engine.QueryEvalResult; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 42 | import com.google.devtools.build.lib.query2.engine.QueryException; |
| 43 | import com.google.devtools.build.lib.query2.engine.QueryExpression; |
Miguel Alcon Pinto | 42984f3 | 2015-11-06 19:05:13 +0000 | [diff] [blame] | 44 | import com.google.devtools.build.lib.query2.engine.QueryUtil.AbstractUniquifier; |
| 45 | import com.google.devtools.build.lib.query2.engine.QueryUtil.AggregateAllCallback; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 46 | import com.google.devtools.build.lib.query2.engine.SkyframeRestartQueryException; |
Miguel Alcon Pinto | 42984f3 | 2015-11-06 19:05:13 +0000 | [diff] [blame] | 47 | import com.google.devtools.build.lib.query2.engine.Uniquifier; |
Nathan Harmata | bc47f40 | 2016-07-13 16:22:30 +0000 | [diff] [blame] | 48 | import com.google.devtools.build.lib.query2.engine.VariableContext; |
Mark Schaller | 6df8179 | 2015-12-10 18:47:47 +0000 | [diff] [blame] | 49 | import com.google.devtools.build.lib.util.Preconditions; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 50 | import com.google.devtools.build.lib.vfs.PathFragment; |
Han-Wen Nienhuys | c13c002 | 2015-12-15 19:08:38 +0000 | [diff] [blame] | 51 | import java.util.ArrayList; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 52 | import java.util.Collection; |
Janak Ramakrishnan | ee6208b | 2016-01-07 20:17:41 +0000 | [diff] [blame] | 53 | import java.util.HashMap; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 54 | import java.util.HashSet; |
| 55 | import java.util.Iterator; |
| 56 | import java.util.LinkedHashSet; |
Han-Wen Nienhuys | c13c002 | 2015-12-15 19:08:38 +0000 | [diff] [blame] | 57 | import java.util.List; |
Janak Ramakrishnan | e72d522 | 2015-02-26 17:09:18 +0000 | [diff] [blame] | 58 | import java.util.Map; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 59 | import java.util.Set; |
Miguel Alcon Pinto | b651026 | 2015-12-10 17:50:15 +0000 | [diff] [blame] | 60 | import java.util.concurrent.atomic.AtomicBoolean; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 61 | |
| 62 | /** |
| 63 | * The environment of a Blaze query. Not thread-safe. |
| 64 | */ |
Janak Ramakrishnan | a46125c | 2015-02-11 16:51:37 +0000 | [diff] [blame] | 65 | public class BlazeQueryEnvironment extends AbstractBlazeQueryEnvironment<Target> { |
Janak Ramakrishnan | fe1056f | 2015-02-11 21:16:06 +0000 | [diff] [blame] | 66 | |
| 67 | private static final int MAX_DEPTH_FULL_SCAN_LIMIT = 20; |
Janak Ramakrishnan | ee6208b | 2016-01-07 20:17:41 +0000 | [diff] [blame] | 68 | private final Map<String, Set<Target>> resolvedTargetPatterns = new HashMap<>(); |
Janak Ramakrishnan | e72d522 | 2015-02-26 17:09:18 +0000 | [diff] [blame] | 69 | private final TargetPatternEvaluator targetPatternEvaluator; |
Janak Ramakrishnan | fe1056f | 2015-02-11 21:16:06 +0000 | [diff] [blame] | 70 | private final TransitivePackageLoader transitivePackageLoader; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 71 | private final TargetProvider targetProvider; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 72 | private final Digraph<Target> graph = new Digraph<>(); |
| 73 | private final ErrorPrintingTargetEdgeErrorObserver errorObserver; |
| 74 | private final LabelVisitor labelVisitor; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 75 | protected final int loadingPhaseThreads; |
| 76 | |
Janak Ramakrishnan | a46125c | 2015-02-11 16:51:37 +0000 | [diff] [blame] | 77 | private final BlazeTargetAccessor accessor = new BlazeTargetAccessor(this); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 78 | |
| 79 | /** |
| 80 | * Note that the correct operation of this class critically depends on the Reporter being a |
| 81 | * singleton object, shared by all cooperating classes contributing to Query. |
| 82 | * @param strictScope if true, fail the whole query if a label goes out of scope. |
| 83 | * @param loadingPhaseThreads the number of threads to use during loading |
| 84 | * the packages for the query. |
| 85 | * @param labelFilter a predicate that determines if a specific label is |
| 86 | * allowed to be visited during query execution. If it returns false, |
| 87 | * the query execution is stopped with an error message. |
| 88 | * @param settings a set of enabled settings |
| 89 | */ |
Janak Ramakrishnan | fe1056f | 2015-02-11 21:16:06 +0000 | [diff] [blame] | 90 | BlazeQueryEnvironment(TransitivePackageLoader transitivePackageLoader, |
| 91 | PackageProvider packageProvider, |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 92 | TargetPatternEvaluator targetPatternEvaluator, |
| 93 | boolean keepGoing, |
| 94 | boolean strictScope, |
| 95 | int loadingPhaseThreads, |
| 96 | Predicate<Label> labelFilter, |
| 97 | EventHandler eventHandler, |
| 98 | Set<Setting> settings, |
| 99 | Iterable<QueryFunction> extraFunctions) { |
Janak Ramakrishnan | e72d522 | 2015-02-26 17:09:18 +0000 | [diff] [blame] | 100 | super(keepGoing, strictScope, labelFilter, eventHandler, settings, extraFunctions); |
| 101 | this.targetPatternEvaluator = targetPatternEvaluator; |
Janak Ramakrishnan | fe1056f | 2015-02-11 21:16:06 +0000 | [diff] [blame] | 102 | this.transitivePackageLoader = transitivePackageLoader; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 103 | this.targetProvider = packageProvider; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 104 | this.errorObserver = new ErrorPrintingTargetEdgeErrorObserver(this.eventHandler); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 105 | this.loadingPhaseThreads = loadingPhaseThreads; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 106 | this.labelVisitor = new LabelVisitor(packageProvider, dependencyFilter); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 107 | } |
| 108 | |
Mark Schaller | 6cebed6 | 2016-06-27 18:05:39 +0000 | [diff] [blame] | 109 | /** |
| 110 | * Calling close is optional because {@link BlazeQueryEnvironment} has no resources that need |
| 111 | * manual management. |
| 112 | */ |
| 113 | @Override |
| 114 | public void close() {} |
| 115 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 116 | @Override |
Miguel Alcon Pinto | b651026 | 2015-12-10 17:50:15 +0000 | [diff] [blame] | 117 | public DigraphQueryEvalResult<Target> evaluateQuery(QueryExpression expr, |
| 118 | final Callback<Target> callback) throws QueryException, InterruptedException { |
Janak Ramakrishnan | e72d522 | 2015-02-26 17:09:18 +0000 | [diff] [blame] | 119 | // Some errors are reported as QueryExceptions and others as ERROR events (if --keep_going). The |
| 120 | // result is set to have an error iff there were errors emitted during the query, so we reset |
| 121 | // errors here. |
| 122 | eventHandler.resetErrors(); |
Miguel Alcon Pinto | b651026 | 2015-12-10 17:50:15 +0000 | [diff] [blame] | 123 | final AtomicBoolean empty = new AtomicBoolean(true); |
Janak Ramakrishnan | ee6208b | 2016-01-07 20:17:41 +0000 | [diff] [blame] | 124 | resolvedTargetPatterns.clear(); |
Miguel Alcon Pinto | b651026 | 2015-12-10 17:50:15 +0000 | [diff] [blame] | 125 | QueryEvalResult queryEvalResult = super.evaluateQuery(expr, new Callback<Target>() { |
| 126 | @Override |
| 127 | public void process(Iterable<Target> partialResult) |
| 128 | throws QueryException, InterruptedException { |
| 129 | empty.compareAndSet(true, Iterables.isEmpty(partialResult)); |
| 130 | callback.process(partialResult); |
| 131 | } |
| 132 | }); |
| 133 | return new DigraphQueryEvalResult<>(queryEvalResult.getSuccess(), empty.get(), graph); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | @Override |
Janak Ramakrishnan | 0dbc1f9 | 2016-01-07 19:12:06 +0000 | [diff] [blame] | 137 | public void getTargetsMatchingPattern( |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame^] | 138 | QueryExpression caller, String pattern, Callback<Target> callback) |
| 139 | throws QueryException, InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 140 | // We can safely ignore the boolean error flag. The evaluateQuery() method above wraps the |
| 141 | // entire query computation in an error sensor. |
| 142 | |
Janak Ramakrishnan | cbe2634 | 2015-08-17 18:57:57 +0000 | [diff] [blame] | 143 | Set<Target> targets = new LinkedHashSet<>(resolvedTargetPatterns.get(pattern)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 144 | |
| 145 | // Sets.filter would be more convenient here, but can't deal with exceptions. |
| 146 | Iterator<Target> targetIterator = targets.iterator(); |
| 147 | while (targetIterator.hasNext()) { |
| 148 | Target target = targetIterator.next(); |
| 149 | if (!validateScope(target.getLabel(), strictScope)) { |
| 150 | targetIterator.remove(); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | Set<PathFragment> packages = new HashSet<>(); |
| 155 | for (Target target : targets) { |
| 156 | packages.add(target.getLabel().getPackageFragment()); |
| 157 | } |
| 158 | |
| 159 | Set<Target> result = new LinkedHashSet<>(); |
| 160 | for (Target target : targets) { |
| 161 | result.add(getOrCreate(target)); |
| 162 | |
| 163 | // Preservation of graph order: it is important that targets obtained via |
| 164 | // a wildcard such as p:* are correctly ordered w.r.t. each other, so to |
| 165 | // ensure this, we add edges between any pair of directly connected |
| 166 | // targets in this set. |
| 167 | if (target instanceof OutputFile) { |
| 168 | OutputFile outputFile = (OutputFile) target; |
| 169 | if (targets.contains(outputFile.getGeneratingRule())) { |
| 170 | makeEdge(outputFile, outputFile.getGeneratingRule()); |
| 171 | } |
| 172 | } else if (target instanceof Rule) { |
| 173 | Rule rule = (Rule) target; |
| 174 | for (Label label : rule.getLabels(dependencyFilter)) { |
| 175 | if (!packages.contains(label.getPackageFragment())) { |
| 176 | continue; // don't cause additional package loading |
| 177 | } |
| 178 | try { |
| 179 | if (!validateScope(label, strictScope)) { |
| 180 | continue; // Don't create edges to targets which are out of scope. |
| 181 | } |
| 182 | Target to = getTargetOrThrow(label); |
| 183 | if (targets.contains(to)) { |
| 184 | makeEdge(rule, to); |
| 185 | } |
| 186 | } catch (NoSuchThingException e) { |
| 187 | /* ignore */ |
| 188 | } catch (InterruptedException e) { |
| 189 | throw new QueryException("interrupted"); |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | } |
Janak Ramakrishnan | 0dbc1f9 | 2016-01-07 19:12:06 +0000 | [diff] [blame] | 194 | try { |
| 195 | callback.process(result); |
| 196 | } catch (InterruptedException e) { |
| 197 | throw new QueryException(caller, e.getMessage()); |
| 198 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 199 | } |
| 200 | |
Ulf Adams | 3ab82f7 | 2015-09-04 12:10:53 +0000 | [diff] [blame] | 201 | @Override |
Janak Ramakrishnan | a41a503 | 2015-02-06 21:27:27 +0000 | [diff] [blame] | 202 | public Target getTarget(Label label) throws TargetNotFoundException, QueryException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 203 | // Can't use strictScope here because we are expecting a target back. |
| 204 | validateScope(label, true); |
| 205 | try { |
Janak Ramakrishnan | a41a503 | 2015-02-06 21:27:27 +0000 | [diff] [blame] | 206 | return getNode(getTargetOrThrow(label)).getLabel(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 207 | } catch (NoSuchThingException e) { |
| 208 | throw new TargetNotFoundException(e); |
| 209 | } catch (InterruptedException e) { |
| 210 | throw new QueryException("interrupted"); |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | private Node<Target> getNode(Target target) { |
| 215 | return graph.createNode(target); |
| 216 | } |
| 217 | |
| 218 | private Collection<Node<Target>> getNodes(Iterable<Target> target) { |
| 219 | Set<Node<Target>> result = new LinkedHashSet<>(); |
| 220 | for (Target t : target) { |
| 221 | result.add(getNode(t)); |
| 222 | } |
| 223 | return result; |
| 224 | } |
| 225 | |
| 226 | @Override |
| 227 | public Target getOrCreate(Target target) { |
| 228 | return getNode(target).getLabel(); |
| 229 | } |
| 230 | |
| 231 | @Override |
Janak Ramakrishnan | 73dd230 | 2015-06-16 17:04:25 +0000 | [diff] [blame] | 232 | public Collection<Target> getFwdDeps(Iterable<Target> targets) { |
| 233 | Set<Target> result = new HashSet<>(); |
| 234 | for (Target target : targets) { |
Janak Ramakrishnan | cda5b66 | 2015-06-18 23:46:36 +0000 | [diff] [blame] | 235 | result.addAll(getTargetsFromNodes(getNode(target).getSuccessors())); |
Janak Ramakrishnan | 73dd230 | 2015-06-16 17:04:25 +0000 | [diff] [blame] | 236 | } |
| 237 | return result; |
| 238 | } |
| 239 | |
| 240 | @Override |
Janak Ramakrishnan | 73dd230 | 2015-06-16 17:04:25 +0000 | [diff] [blame] | 241 | public Collection<Target> getReverseDeps(Iterable<Target> targets) { |
| 242 | Set<Target> result = new HashSet<>(); |
| 243 | for (Target target : targets) { |
Janak Ramakrishnan | cda5b66 | 2015-06-18 23:46:36 +0000 | [diff] [blame] | 244 | result.addAll(getTargetsFromNodes(getNode(target).getPredecessors())); |
Janak Ramakrishnan | 73dd230 | 2015-06-16 17:04:25 +0000 | [diff] [blame] | 245 | } |
| 246 | return result; |
| 247 | } |
| 248 | |
| 249 | @Override |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 250 | public Set<Target> getTransitiveClosure(Set<Target> targetNodes) { |
| 251 | for (Target node : targetNodes) { |
| 252 | checkBuilt(node); |
| 253 | } |
| 254 | return getTargetsFromNodes(graph.getFwdReachable(getNodes(targetNodes))); |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * Checks that the graph rooted at 'targetNode' has been completely built; |
| 259 | * fails if not. Callers of {@link #getTransitiveClosure} must ensure that |
| 260 | * {@link #buildTransitiveClosure} has been called before. |
| 261 | * |
| 262 | * <p>It would be inefficient and failure-prone to make getTransitiveClosure |
| 263 | * call buildTransitiveClosure directly. Also, it would cause |
| 264 | * nondeterministic behavior of the operators, since the set of packages |
| 265 | * loaded (and hence errors reported) would depend on the ordering details of |
| 266 | * the query operators' implementations. |
| 267 | */ |
| 268 | private void checkBuilt(Target targetNode) { |
| 269 | Preconditions.checkState( |
| 270 | labelVisitor.hasVisited(targetNode.getLabel()), |
| 271 | "getTransitiveClosure(%s) called without prior call to buildTransitiveClosure()", |
| 272 | targetNode); |
| 273 | } |
| 274 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 275 | @Override |
| 276 | public void buildTransitiveClosure(QueryExpression caller, |
| 277 | Set<Target> targetNodes, |
Janak Ramakrishnan | 8990739 | 2015-07-08 21:43:31 +0000 | [diff] [blame] | 278 | int maxDepth) throws QueryException, InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 279 | Set<Target> targets = targetNodes; |
| 280 | preloadTransitiveClosure(targets, maxDepth); |
Janak Ramakrishnan | 8990739 | 2015-07-08 21:43:31 +0000 | [diff] [blame] | 281 | labelVisitor.syncWithVisitor(eventHandler, targets, keepGoing, |
| 282 | loadingPhaseThreads, maxDepth, errorObserver, new GraphBuildingObserver()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 283 | |
| 284 | if (errorObserver.hasErrors()) { |
| 285 | reportBuildFileError(caller, "errors were encountered while computing transitive closure"); |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | @Override |
| 290 | public Set<Target> getNodesOnPath(Target from, Target to) { |
| 291 | return getTargetsFromNodes(graph.getShortestPath(getNode(from), getNode(to))); |
| 292 | } |
| 293 | |
Miguel Alcon Pinto | 42984f3 | 2015-11-06 19:05:13 +0000 | [diff] [blame] | 294 | @Override |
Nathan Harmata | bc47f40 | 2016-07-13 16:22:30 +0000 | [diff] [blame] | 295 | public void eval(QueryExpression expr, VariableContext<Target> context, Callback<Target> callback) |
Miguel Alcon Pinto | 42984f3 | 2015-11-06 19:05:13 +0000 | [diff] [blame] | 296 | throws QueryException, InterruptedException { |
| 297 | AggregateAllCallback<Target> aggregator = new AggregateAllCallback<>(); |
Nathan Harmata | bc47f40 | 2016-07-13 16:22:30 +0000 | [diff] [blame] | 298 | expr.eval(this, context, aggregator); |
Miguel Alcon Pinto | 42984f3 | 2015-11-06 19:05:13 +0000 | [diff] [blame] | 299 | callback.process(aggregator.getResult()); |
| 300 | } |
| 301 | |
| 302 | @Override |
| 303 | public Uniquifier<Target> createUniquifier() { |
| 304 | return new AbstractUniquifier<Target, Label>() { |
| 305 | @Override |
| 306 | protected Label extractKey(Target target) { |
| 307 | return target.getLabel(); |
| 308 | } |
| 309 | }; |
| 310 | } |
| 311 | |
Janak Ramakrishnan | fe1056f | 2015-02-11 21:16:06 +0000 | [diff] [blame] | 312 | private void preloadTransitiveClosure(Set<Target> targets, int maxDepth) throws QueryException { |
| 313 | if (maxDepth >= MAX_DEPTH_FULL_SCAN_LIMIT && transitivePackageLoader != null) { |
| 314 | // Only do the full visitation if "maxDepth" is large enough. Otherwise, the benefits of |
| 315 | // preloading will be outweighed by the cost of doing more work than necessary. |
| 316 | try { |
| 317 | transitivePackageLoader.sync(eventHandler, targets, ImmutableSet.<Label>of(), keepGoing, |
| 318 | loadingPhaseThreads, -1); |
| 319 | } catch (InterruptedException e) { |
| 320 | throw new QueryException("interrupted"); |
| 321 | } |
| 322 | } |
| 323 | } |
| 324 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 325 | /** |
| 326 | * It suffices to synchronize the modifications of this.graph from within the |
| 327 | * GraphBuildingObserver, because that's the only concurrent part. |
| 328 | * Concurrency is always encapsulated within the evaluation of a single query |
| 329 | * operator (e.g. deps(), somepath(), etc). |
| 330 | */ |
| 331 | private class GraphBuildingObserver implements TargetEdgeObserver { |
| 332 | |
| 333 | @Override |
| 334 | public synchronized void edge(Target from, Attribute attribute, Target to) { |
| 335 | Preconditions.checkState(attribute == null || |
| 336 | dependencyFilter.apply(((Rule) from), attribute), |
| 337 | "Disallowed edge from LabelVisitor: %s --> %s", from, to); |
| 338 | makeEdge(from, to); |
| 339 | } |
| 340 | |
| 341 | @Override |
| 342 | public synchronized void node(Target node) { |
| 343 | graph.createNode(node); |
| 344 | } |
| 345 | |
| 346 | @Override |
| 347 | public void missingEdge(Target target, Label to, NoSuchThingException e) { |
| 348 | // No - op. |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | private void makeEdge(Target from, Target to) { |
| 353 | graph.addEdge(from, to); |
| 354 | } |
| 355 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 356 | private Target getTargetOrThrow(Label label) |
| 357 | throws NoSuchThingException, SkyframeRestartQueryException, InterruptedException { |
| 358 | Target target = targetProvider.getTarget(eventHandler, label); |
| 359 | if (target == null) { |
| 360 | throw new SkyframeRestartQueryException(); |
| 361 | } |
| 362 | return target; |
| 363 | } |
| 364 | |
| 365 | // TODO(bazel-team): rename this to getDependentFiles when all implementations |
| 366 | // of QueryEnvironment is fixed. |
| 367 | @Override |
Han-Wen Nienhuys | c13c002 | 2015-12-15 19:08:38 +0000 | [diff] [blame] | 368 | public Set<Target> getBuildFiles( |
| 369 | final QueryExpression caller, |
| 370 | Set<Target> nodes, |
| 371 | boolean buildFiles, |
| 372 | boolean subincludes, |
| 373 | boolean loads) |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 374 | throws QueryException { |
| 375 | Set<Target> dependentFiles = new LinkedHashSet<>(); |
| 376 | Set<Package> seenPackages = new HashSet<>(); |
| 377 | // Keep track of seen labels, to avoid adding a fake subinclude label that also exists as a |
| 378 | // real target. |
| 379 | Set<Label> seenLabels = new HashSet<>(); |
| 380 | |
| 381 | // Adds all the package definition files (BUILD files and build |
| 382 | // extensions) for package "pkg", to "buildfiles". |
| 383 | for (Target x : nodes) { |
| 384 | Package pkg = x.getPackage(); |
| 385 | if (seenPackages.add(pkg)) { |
Han-Wen Nienhuys | c13c002 | 2015-12-15 19:08:38 +0000 | [diff] [blame] | 386 | if (buildFiles) { |
| 387 | addIfUniqueLabel(getNode(pkg.getBuildFile()), seenLabels, dependentFiles); |
| 388 | } |
| 389 | |
| 390 | List<Label> extensions = new ArrayList<>(); |
| 391 | if (subincludes) { |
| 392 | extensions.addAll(pkg.getSubincludeLabels()); |
| 393 | } |
| 394 | if (loads) { |
| 395 | extensions.addAll(pkg.getSkylarkFileDependencies()); |
| 396 | } |
| 397 | |
| 398 | for (Label subinclude : extensions) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 399 | addIfUniqueLabel(getSubincludeTarget(subinclude, pkg), seenLabels, dependentFiles); |
| 400 | |
| 401 | // Also add the BUILD file of the subinclude. |
Han-Wen Nienhuys | c13c002 | 2015-12-15 19:08:38 +0000 | [diff] [blame] | 402 | if (buildFiles) { |
| 403 | try { |
| 404 | addIfUniqueLabel( |
| 405 | getSubincludeTarget(subinclude.getLocalTargetLabel("BUILD"), pkg), |
| 406 | seenLabels, |
| 407 | dependentFiles); |
| 408 | |
| 409 | } catch (LabelSyntaxException e) { |
| 410 | throw new AssertionError("BUILD should always parse as a target name", e); |
| 411 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 412 | } |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | return dependentFiles; |
| 417 | } |
| 418 | |
Janak Ramakrishnan | cbe2634 | 2015-08-17 18:57:57 +0000 | [diff] [blame] | 419 | private static final Function<ResolvedTargets<Target>, Set<Target>> RESOLVED_TARGETS_TO_TARGETS = |
| 420 | new Function<ResolvedTargets<Target>, Set<Target>>() { |
| 421 | @Override |
| 422 | public Set<Target> apply(ResolvedTargets<Target> resolvedTargets) { |
| 423 | return resolvedTargets.getTargets(); |
| 424 | } |
| 425 | }; |
| 426 | |
Ulf Adams | 3ab82f7 | 2015-09-04 12:10:53 +0000 | [diff] [blame] | 427 | @Override |
Janak Ramakrishnan | ee6208b | 2016-01-07 20:17:41 +0000 | [diff] [blame] | 428 | protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns) |
| 429 | throws TargetParsingException { |
| 430 | if (!resolvedTargetPatterns.keySet().containsAll(patterns)) { |
| 431 | try { |
| 432 | // Note that this may throw a RuntimeException if deps are missing in Skyframe and this is |
| 433 | // being called from within a SkyFunction. |
| 434 | resolvedTargetPatterns.putAll( |
| 435 | Maps.transformValues( |
| 436 | targetPatternEvaluator.preloadTargetPatterns(eventHandler, patterns, keepGoing), |
| 437 | RESOLVED_TARGETS_TO_TARGETS)); |
| 438 | } catch (InterruptedException e) { |
| 439 | // TODO(bazel-team): Propagate the InterruptedException from here [skyframe-loading]. |
| 440 | throw new TargetParsingException("interrupted"); |
| 441 | } |
Janak Ramakrishnan | e72d522 | 2015-02-26 17:09:18 +0000 | [diff] [blame] | 442 | } |
| 443 | } |
| 444 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 445 | private static void addIfUniqueLabel(Node<Target> node, Set<Label> labels, Set<Target> nodes) { |
| 446 | if (labels.add(node.getLabel().getLabel())) { |
| 447 | nodes.add(node.getLabel()); |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | private Node<Target> getSubincludeTarget(final Label label, Package pkg) { |
Mark Schaller | b817e3f | 2015-06-04 17:14:18 +0000 | [diff] [blame] | 452 | return getNode(new FakeSubincludeTarget(label, pkg)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 453 | } |
| 454 | |
| 455 | @Override |
| 456 | public TargetAccessor<Target> getAccessor() { |
| 457 | return accessor; |
| 458 | } |
| 459 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 460 | /** Given a set of target nodes, returns the targets. */ |
| 461 | private static Set<Target> getTargetsFromNodes(Iterable<Node<Target>> input) { |
| 462 | Set<Target> result = new LinkedHashSet<>(); |
| 463 | for (Node<Target> node : input) { |
| 464 | result.add(node.getLabel()); |
| 465 | } |
| 466 | return result; |
| 467 | } |
| 468 | } |