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