blob: 5cbbe5a0806dc47881acb7029e810be83699b43c [file] [log] [blame]
janakrdc8b2e9a2017-08-18 22:52:37 +02001// Copyright 2017 The Bazel Authors. 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.collect.ImmutableList;
17import com.google.common.collect.ImmutableSet;
18import com.google.common.collect.Iterables;
19import com.google.common.collect.Sets;
20import com.google.common.util.concurrent.AsyncFunction;
21import com.google.common.util.concurrent.Futures;
22import com.google.common.util.concurrent.MoreExecutors;
23import com.google.devtools.build.lib.analysis.ConfiguredTarget;
janakrdc8b2e9a2017-08-18 22:52:37 +020024import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
juliexxia2af2bff2017-11-17 15:05:14 -080025import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
janakrdc8b2e9a2017-08-18 22:52:37 +020026import com.google.devtools.build.lib.cmdline.Label;
27import com.google.devtools.build.lib.cmdline.TargetParsingException;
28import com.google.devtools.build.lib.cmdline.TargetPattern;
juliexxia2af2bff2017-11-17 15:05:14 -080029import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet;
janakrdc8b2e9a2017-08-18 22:52:37 +020030import com.google.devtools.build.lib.concurrent.MultisetSemaphore;
31import com.google.devtools.build.lib.events.Event;
32import com.google.devtools.build.lib.events.ExtendedEventHandler;
juliexxia08dda862018-04-05 09:54:22 -070033import com.google.devtools.build.lib.events.Reporter;
juliexxia2af2bff2017-11-17 15:05:14 -080034import com.google.devtools.build.lib.packages.DependencyFilter;
mjhalupka00d781a2018-01-31 14:42:22 -080035import com.google.devtools.build.lib.packages.NoSuchTargetException;
janakrdc8b2e9a2017-08-18 22:52:37 +020036import com.google.devtools.build.lib.packages.Rule;
mstaib4a07a472018-04-19 14:16:41 -070037import com.google.devtools.build.lib.packages.RuleTransitionFactory;
janakrdc8b2e9a2017-08-18 22:52:37 +020038import com.google.devtools.build.lib.packages.Target;
39import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
40import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
41import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
42import com.google.devtools.build.lib.query2.engine.Callback;
43import com.google.devtools.build.lib.query2.engine.KeyExtractor;
44import com.google.devtools.build.lib.query2.engine.MinDepthUniquifier;
45import com.google.devtools.build.lib.query2.engine.QueryEnvironment;
46import com.google.devtools.build.lib.query2.engine.QueryEvalResult;
47import com.google.devtools.build.lib.query2.engine.QueryException;
48import com.google.devtools.build.lib.query2.engine.QueryExpression;
49import com.google.devtools.build.lib.query2.engine.QueryUtil.MinDepthUniquifierImpl;
50import com.google.devtools.build.lib.query2.engine.QueryUtil.MutableKeyExtractorBackedMapImpl;
51import com.google.devtools.build.lib.query2.engine.QueryUtil.ThreadSafeMutableKeyExtractorBackedSetImpl;
52import com.google.devtools.build.lib.query2.engine.QueryUtil.UniquifierImpl;
53import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback;
54import com.google.devtools.build.lib.query2.engine.Uniquifier;
juliexxia5f135e82018-04-11 10:34:00 -070055import com.google.devtools.build.lib.query2.output.AspectResolver;
juliexxiad350a892018-03-27 13:46:10 -070056import com.google.devtools.build.lib.query2.output.CqueryOptions;
juliexxia2af2bff2017-11-17 15:05:14 -080057import com.google.devtools.build.lib.query2.output.QueryOptions;
juliexxia8d4f8132018-02-16 07:55:21 -080058import com.google.devtools.build.lib.rules.AliasConfiguredTarget;
janakr171a7eb2018-03-26 09:26:53 -070059import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
janakrdc8b2e9a2017-08-18 22:52:37 +020060import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
61import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue;
62import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider;
mjhalupka00d781a2018-01-31 14:42:22 -080063import com.google.devtools.build.lib.skyframe.PackageValue;
janakrdc8b2e9a2017-08-18 22:52:37 +020064import com.google.devtools.build.lib.skyframe.RecursivePackageProviderBackedTargetPatternResolver;
juliexxiacc864752018-06-01 09:03:48 -070065import com.google.devtools.build.lib.skyframe.RecursivePkgValueRootPackageExtractor;
janakrdc8b2e9a2017-08-18 22:52:37 +020066import com.google.devtools.build.lib.skyframe.SkyFunctions;
juliexxiad350a892018-03-27 13:46:10 -070067import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
janakrdc8b2e9a2017-08-18 22:52:37 +020068import com.google.devtools.build.lib.skyframe.TargetPatternValue;
69import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey;
70import com.google.devtools.build.skyframe.SkyKey;
71import com.google.devtools.build.skyframe.WalkableGraph;
juliexxia2af2bff2017-11-17 15:05:14 -080072import com.google.devtools.common.options.OptionsParser;
73import com.google.devtools.common.options.OptionsParsingException;
janakrdc8b2e9a2017-08-18 22:52:37 +020074import java.io.IOException;
juliexxiad350a892018-03-27 13:46:10 -070075import java.io.OutputStream;
janakrdc8b2e9a2017-08-18 22:52:37 +020076import java.util.ArrayList;
juliexxia2af2bff2017-11-17 15:05:14 -080077import java.util.Arrays;
janakrdc8b2e9a2017-08-18 22:52:37 +020078import java.util.Collection;
juliexxia2af2bff2017-11-17 15:05:14 -080079import java.util.Collections;
janakrdc8b2e9a2017-08-18 22:52:37 +020080import java.util.HashMap;
81import java.util.List;
82import java.util.Map;
juliexxia2af2bff2017-11-17 15:05:14 -080083import java.util.Set;
janakrdc8b2e9a2017-08-18 22:52:37 +020084import java.util.function.Function;
85import java.util.function.Supplier;
86import java.util.stream.Collectors;
87import javax.annotation.Nullable;
88
89/**
90 * {@link QueryEnvironment} that runs queries over the configured target (analysis) graph.
91 *
juliexxia45c387f2018-04-19 12:51:18 -070092 * <p>This environment can theoretically be used for multiple queries, but currently is only ever
93 * used for one over the course of its lifetime. If this ever changed to be used for multiple, the
juliexxiacc864752018-06-01 09:03:48 -070094 * {@link ConfiguredTargetAccessor} field should be initialized on a per-query basis not a
95 * per-environment basis.
janakrdc8b2e9a2017-08-18 22:52:37 +020096 *
juliexxiacc864752018-06-01 09:03:48 -070097 * <p>There is currently a limited way to specify a configuration in the query syntax via {@link
98 * ConfigFunction}. This currently still limits the user to choosing the 'target', 'host', or null
99 * configurations. It shouldn't be terribly difficult to expand this with {@link
100 * OptionsDiffForReconstruction} to handle fully customizable configurations if the need arises in
101 * the future.
janakrdc8b2e9a2017-08-18 22:52:37 +0200102 *
103 * <p>Aspects are also not supported, but probably should be in some fashion.
104 */
105public class ConfiguredTargetQueryEnvironment
106 extends AbstractBlazeQueryEnvironment<ConfiguredTarget> {
107 private final BuildConfiguration defaultTargetConfiguration;
108 private final BuildConfiguration hostConfiguration;
109 private final String parserPrefix;
110 protected final PathPackageLocator pkgPath;
111 private final Supplier<WalkableGraph> walkableGraphSupplier;
mjhalupka426ab902018-01-30 11:35:47 -0800112 private ConfiguredTargetAccessor accessor;
janakrdc8b2e9a2017-08-18 22:52:37 +0200113 protected WalkableGraph graph;
114
janakrac2cd352017-12-20 13:37:13 -0800115 private static final Function<SkyKey, ConfiguredTargetKey> SKYKEY_TO_CTKEY =
janakr906c7cb2017-12-28 12:04:02 -0800116 skyKey -> (ConfiguredTargetKey) skyKey.argument();
janakrdc8b2e9a2017-08-18 22:52:37 +0200117 private static final ImmutableList<TargetPatternKey> ALL_PATTERNS;
janakrcc3890d2018-03-27 14:38:52 -0700118 private final KeyExtractor<ConfiguredTarget, ConfiguredTargetKey> configuredTargetKeyExtractor;
janakrdc8b2e9a2017-08-18 22:52:37 +0200119
juliexxia6f9416e2018-02-13 12:44:21 -0800120 /** Common query functions and cquery specific functions. */
121 public static final ImmutableList<QueryFunction> FUNCTIONS = populateFunctions();
122 /** Cquery specific functions. */
123 public static final ImmutableList<QueryFunction> CQUERY_FUNCTIONS = getCqueryFunctions();
124
janakrdc8b2e9a2017-08-18 22:52:37 +0200125 static {
126 TargetPattern targetPattern;
127 try {
128 targetPattern = TargetPattern.defaultParser().parse("//...");
129 } catch (TargetParsingException e) {
130 throw new IllegalStateException(e);
131 }
132 ALL_PATTERNS =
133 ImmutableList.of(
134 new TargetPatternKey(
135 targetPattern, FilteringPolicies.NO_FILTER, false, "", ImmutableSet.of()));
136 }
137
138 private RecursivePackageProviderBackedTargetPatternResolver resolver;
139
140 public ConfiguredTargetQueryEnvironment(
141 boolean keepGoing,
142 ExtendedEventHandler eventHandler,
143 Iterable<QueryFunction> extraFunctions,
144 BuildConfiguration defaultTargetConfiguration,
145 BuildConfiguration hostConfiguration,
146 String parserPrefix,
147 PathPackageLocator pkgPath,
juliexxia2af2bff2017-11-17 15:05:14 -0800148 Supplier<WalkableGraph> walkableGraphSupplier,
149 Set<Setting> settings) {
150 super(keepGoing, true, Rule.ALL_LABELS, eventHandler, settings, extraFunctions);
janakrdc8b2e9a2017-08-18 22:52:37 +0200151 this.defaultTargetConfiguration = defaultTargetConfiguration;
152 this.hostConfiguration = hostConfiguration;
153 this.parserPrefix = parserPrefix;
154 this.pkgPath = pkgPath;
155 this.walkableGraphSupplier = walkableGraphSupplier;
juliexxia64d4c652018-03-23 14:51:43 -0700156 this.accessor = new ConfiguredTargetAccessor(walkableGraphSupplier.get());
janakrcc3890d2018-03-27 14:38:52 -0700157 this.configuredTargetKeyExtractor =
158 element -> {
159 try {
160 return ConfiguredTargetKey.of(
161 element,
162 element.getConfigurationKey() == null
163 ? null
164 : ((BuildConfigurationValue) graph.getValue(element.getConfigurationKey()))
165 .getConfiguration());
166 } catch (InterruptedException e) {
167 throw new IllegalStateException("Interruption unexpected in configured query");
168 }
169 };
janakrdc8b2e9a2017-08-18 22:52:37 +0200170 }
171
juliexxia6f9416e2018-02-13 12:44:21 -0800172 private static ImmutableList<QueryFunction> populateFunctions() {
173 return new ImmutableList.Builder<QueryFunction>()
174 .addAll(QueryEnvironment.DEFAULT_QUERY_FUNCTIONS)
175 .addAll(getCqueryFunctions())
176 .build();
177 }
178
179 private static ImmutableList<QueryFunction> getCqueryFunctions() {
180 return ImmutableList.of(new ConfigFunction());
181 }
182
juliexxia3a61eb62018-03-28 08:42:21 -0700183 public ImmutableList<CqueryThreadsafeCallback> getDefaultOutputFormatters(
juliexxiad350a892018-03-27 13:46:10 -0700184 TargetAccessor<ConfiguredTarget> accessor,
185 CqueryOptions options,
juliexxia08dda862018-04-05 09:54:22 -0700186 Reporter reporter,
juliexxiad350a892018-03-27 13:46:10 -0700187 SkyframeExecutor skyframeExecutor,
juliexxia5f135e82018-04-11 10:34:00 -0700188 BuildConfiguration hostConfiguration,
mstaib4a07a472018-04-19 14:16:41 -0700189 @Nullable RuleTransitionFactory trimmingTransitionFactory,
juliexxia5f135e82018-04-11 10:34:00 -0700190 AspectResolver resolver) {
juliexxia08dda862018-04-05 09:54:22 -0700191 OutputStream out = reporter.getOutErr().getOutputStream();
juliexxiad350a892018-03-27 13:46:10 -0700192 return new ImmutableList.Builder<CqueryThreadsafeCallback>()
juliexxia08dda862018-04-05 09:54:22 -0700193 .add(
194 new LabelAndConfigurationOutputFormatterCallback(
juliexxia5f135e82018-04-11 10:34:00 -0700195 reporter, options, out, skyframeExecutor, accessor))
juliexxiad350a892018-03-27 13:46:10 -0700196 .add(
197 new TransitionsOutputFormatterCallback(
mstaib4a07a472018-04-19 14:16:41 -0700198 reporter,
199 options,
200 out,
201 skyframeExecutor,
202 accessor,
203 hostConfiguration,
204 trimmingTransitionFactory))
juliexxia5f135e82018-04-11 10:34:00 -0700205 .add(
206 new ProtoOutputFormatterCallback(
207 reporter, options, out, skyframeExecutor, accessor, resolver))
juliexxiad350a892018-03-27 13:46:10 -0700208 .build();
209 }
210
juliexxia45c387f2018-04-19 12:51:18 -0700211 @Override
212 public QueryEvalResult evaluateQuery(
213 QueryExpression expr, ThreadSafeOutputFormatterCallback<ConfiguredTarget> callback)
214 throws QueryException, InterruptedException, IOException {
215 beforeEvaluateQuery();
216 return super.evaluateQuery(expr, callback);
217 }
218
219 private void beforeEvaluateQuery() throws InterruptedException, QueryException {
220 graph = walkableGraphSupplier.get();
221 GraphBackedRecursivePackageProvider graphBackedRecursivePackageProvider =
juliexxiacc864752018-06-01 09:03:48 -0700222 new GraphBackedRecursivePackageProvider(
223 graph, ALL_PATTERNS, pkgPath, new RecursivePkgValueRootPackageExtractor());
juliexxia45c387f2018-04-19 12:51:18 -0700224 resolver =
225 new RecursivePackageProviderBackedTargetPatternResolver(
226 graphBackedRecursivePackageProvider,
227 eventHandler,
228 FilteringPolicies.NO_FILTER,
229 MultisetSemaphore.unbounded());
230 checkSettings(settings);
231 }
232
juliexxia2af2bff2017-11-17 15:05:14 -0800233 // Check to make sure the settings requested are currently supported by this class
234 private void checkSettings(Set<Setting> settings) throws QueryException {
juliexxia865b8882017-12-08 12:37:36 -0800235 if (settings.contains(Setting.NO_NODEP_DEPS)
236 || settings.contains(Setting.TESTS_EXPRESSION_STRICT)) {
237 settings =
238 Sets.difference(
239 settings, ImmutableSet.of(Setting.NO_HOST_DEPS, Setting.NO_IMPLICIT_DEPS));
juliexxia2af2bff2017-11-17 15:05:14 -0800240 throw new QueryException(
241 String.format(
242 "The following filter(s) are not currently supported by configured query: %s",
243 settings.toString()));
244 }
janakrdc8b2e9a2017-08-18 22:52:37 +0200245 }
246
juliexxia45c387f2018-04-19 12:51:18 -0700247 public BuildConfiguration getHostConfiguration() {
248 return hostConfiguration;
janakrdc8b2e9a2017-08-18 22:52:37 +0200249 }
250
juliexxia45c387f2018-04-19 12:51:18 -0700251 @Override
252 public TargetAccessor<ConfiguredTarget> getAccessor() {
253 return accessor;
254 }
255
256 // TODO(bazel-team): It's weird that this untemplated function exists. Fix? Or don't implement?
257 @Override
258 public Target getTarget(Label label)
259 throws TargetNotFoundException, QueryException, InterruptedException {
260 try {
261 return ((PackageValue)
262 walkableGraphSupplier.get().getValue(PackageValue.key(label.getPackageIdentifier())))
263 .getPackage()
264 .getTarget(label.getName());
265 } catch (NoSuchTargetException e) {
266 throw new TargetNotFoundException(e);
Googler838f3392018-01-22 14:43:51 -0800267 }
juliexxia45c387f2018-04-19 12:51:18 -0700268 }
269
270 @Override
271 public ConfiguredTarget getOrCreate(ConfiguredTarget target) {
272 return target;
273 }
274
275 /**
276 * This method has to exist because {@link AliasConfiguredTarget#getLabel()} returns
277 * the label of the "actual" target instead of the alias target. Grr.
278 */
279 public static Label getCorrectLabel(ConfiguredTarget target) {
280 if (target instanceof AliasConfiguredTarget) {
281 return ((AliasConfiguredTarget) target).getOriginalLabel();
Googler838f3392018-01-22 14:43:51 -0800282 }
juliexxia45c387f2018-04-19 12:51:18 -0700283 return target.getLabel();
janakrdc8b2e9a2017-08-18 22:52:37 +0200284 }
285
286 @Override
287 public QueryTaskFuture<Void> getTargetsMatchingPattern(
288 QueryExpression owner, String pattern, Callback<ConfiguredTarget> callback) {
289 TargetPattern patternToEval;
290 try {
291 patternToEval = getPattern(pattern);
292 } catch (TargetParsingException tpe) {
293 try {
294 reportBuildFileError(owner, tpe.getMessage());
295 } catch (QueryException qe) {
296 return immediateFailedFuture(qe);
297 }
298 return immediateSuccessfulFuture(null);
299 } catch (InterruptedException ie) {
300 return immediateCancelledFuture();
301 }
302 AsyncFunction<TargetParsingException, Void> reportBuildFileErrorAsyncFunction =
303 exn -> {
304 reportBuildFileError(owner, exn.getMessage());
305 return Futures.immediateFuture(null);
306 };
307 return QueryTaskFutureImpl.ofDelegate(
308 Futures.catchingAsync(
309 patternToEval.evalAdaptedForAsync(
310 resolver,
311 ImmutableSet.of(),
312 ImmutableSet.of(),
313 (Callback<Target>)
314 partialResult -> {
315 List<ConfiguredTarget> transformedResult = new ArrayList<>();
316 for (Target target : partialResult) {
317 ConfiguredTarget configuredTarget = getConfiguredTarget(target.getLabel());
318 if (configuredTarget != null) {
319 transformedResult.add(configuredTarget);
320 }
321 }
322 callback.process(transformedResult);
323 },
324 QueryException.class),
325 TargetParsingException.class,
326 reportBuildFileErrorAsyncFunction,
327 MoreExecutors.directExecutor()));
328 }
329
juliexxia45c387f2018-04-19 12:51:18 -0700330 private ConfiguredTarget getConfiguredTarget(Label label) throws InterruptedException {
331 // Try with target configuration.
332 ConfiguredTarget configuredTarget = getTargetConfiguredTarget(label);
333 if (configuredTarget != null) {
334 return configuredTarget;
335 }
336 // Try with host configuration (even when --nohost_deps is set in the case that top-level
337 // targets are configured in the host configuration so we are doing a host-configuration-only
338 // query).
339 configuredTarget = getHostConfiguredTarget(label);
340 if (configuredTarget != null) {
341 return configuredTarget;
342 }
343 // Last chance: source file.
344 return getNullConfiguredTarget(label);
345 }
346
347 @Nullable
348 private ConfiguredTarget getConfiguredTarget(SkyKey key) throws InterruptedException {
349 ConfiguredTargetValue value =
350 ((ConfiguredTargetValue) walkableGraphSupplier.get().getValue(key));
351 return value == null ? null : value.getConfiguredTarget();
352 }
353
354 private TargetPattern getPattern(String pattern)
355 throws TargetParsingException, InterruptedException {
356 TargetPatternKey targetPatternKey =
357 ((TargetPatternKey)
358 TargetPatternValue.key(
359 pattern, TargetPatternEvaluator.DEFAULT_FILTERING_POLICY, parserPrefix)
360 .argument());
361 return targetPatternKey.getParsedPattern();
362 }
363
juliexxia6f9416e2018-02-13 12:44:21 -0800364 /**
365 * Processes the targets in {@code targets} with the requested {@code configuration}
366 *
367 * @param pattern the original pattern that {@code targets} were parsed from. Used for error
368 * message.
369 * @param targets the set of {@link ConfiguredTarget}s whose labels represent the targets being
370 * requested.
371 * @param configuration the configuration to request {@code targets} in.
372 * @param callback the callback to receive the results of this method.
373 * @return {@link QueryTaskCallable} that returns the correctly configured targets.
374 */
375 QueryTaskCallable<Void> getConfiguredTargets(
376 String pattern,
377 ThreadSafeMutableSet<ConfiguredTarget> targets,
378 String configuration,
379 Callback<ConfiguredTarget> callback) {
380 return new QueryTaskCallable<Void>() {
381 @Override
382 public Void call() throws QueryException, InterruptedException {
383 List<ConfiguredTarget> transformedResult = new ArrayList<>();
384 for (ConfiguredTarget target : targets) {
juliexxia2267e2a2018-03-15 09:29:10 -0700385 Label label = getCorrectLabel(target);
juliexxia6f9416e2018-02-13 12:44:21 -0800386 ConfiguredTarget configuredTarget;
387 switch (configuration) {
388 case "\'host\'":
389 configuredTarget = getHostConfiguredTarget(label);
390 break;
391 case "\'target\'":
392 configuredTarget = getTargetConfiguredTarget(label);
393 break;
394 case "\'null\'":
395 configuredTarget = getNullConfiguredTarget(label);
396 break;
397 default:
398 throw new QueryException(
399 "the second argument of the config function must be 'target', 'host', or 'null'");
400 }
401 if (configuredTarget != null) {
402 transformedResult.add(configuredTarget);
403 }
404 }
405 if (transformedResult.isEmpty()) {
406 throw new QueryException(
407 "No target (in) "
408 + pattern
409 + " could be found in the "
410 + configuration
411 + " configuration");
412 }
413 callback.process(transformedResult);
414 return null;
415 }
416 };
417 }
418
juliexxia45c387f2018-04-19 12:51:18 -0700419 @Nullable
420 private ConfiguredTarget getHostConfiguredTarget(Label label) throws InterruptedException {
421 return getConfiguredTarget(ConfiguredTargetValue.key(label, hostConfiguration));
janakrdc8b2e9a2017-08-18 22:52:37 +0200422 }
423
juliexxia45c387f2018-04-19 12:51:18 -0700424 @Nullable
425 private ConfiguredTarget getTargetConfiguredTarget(Label label) throws InterruptedException {
426 return getConfiguredTarget(ConfiguredTargetValue.key(label, defaultTargetConfiguration));
427 }
428
429 @Nullable
430 private ConfiguredTarget getNullConfiguredTarget(Label label) throws InterruptedException {
431 return getConfiguredTarget(ConfiguredTargetValue.key(label, null));
432 }
433
434 @Override
435 public ThreadSafeMutableSet<ConfiguredTarget> getFwdDeps(Iterable<ConfiguredTarget> targets)
436 throws InterruptedException {
437 Map<SkyKey, ConfiguredTarget> targetsByKey = new HashMap<>(Iterables.size(targets));
438 for (ConfiguredTarget target : targets) {
439 targetsByKey.put(getSkyKey(target), target);
440 }
441 Map<SkyKey, Collection<ConfiguredTarget>> directDeps =
442 targetifyValues(graph.getDirectDeps(targetsByKey.keySet()));
443 if (targetsByKey.keySet().size() != directDeps.keySet().size()) {
444 Iterable<ConfiguredTargetKey> missingTargets =
445 Sets.difference(targetsByKey.keySet(), directDeps.keySet())
446 .stream()
447 .map(SKYKEY_TO_CTKEY)
448 .collect(Collectors.toList());
449 eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets));
450 }
451 ThreadSafeMutableSet<ConfiguredTarget> result = createThreadSafeMutableSet();
jcater94b87022018-05-02 09:08:52 -0700452 for (Map.Entry<SkyKey, Collection<ConfiguredTarget>> entry : directDeps.entrySet()) {
juliexxia45c387f2018-04-19 12:51:18 -0700453 result.addAll(filterFwdDeps(targetsByKey.get(entry.getKey()), entry.getValue()));
janakrdc8b2e9a2017-08-18 22:52:37 +0200454 }
455 return result;
456 }
457
juliexxia2af2bff2017-11-17 15:05:14 -0800458 private Collection<ConfiguredTarget> filterFwdDeps(
459 ConfiguredTarget configTarget, Collection<ConfiguredTarget> rawFwdDeps)
460 throws InterruptedException {
juliexxia8d4f8132018-02-16 07:55:21 -0800461 if (settings.isEmpty()) {
juliexxia2af2bff2017-11-17 15:05:14 -0800462 return rawFwdDeps;
463 }
juliexxia865b8882017-12-08 12:37:36 -0800464 return getAllowedDeps(configTarget, rawFwdDeps);
juliexxia2af2bff2017-11-17 15:05:14 -0800465 }
466
juliexxia45c387f2018-04-19 12:51:18 -0700467 @Override
468 public Collection<ConfiguredTarget> getReverseDeps(Iterable<ConfiguredTarget> targets)
469 throws InterruptedException {
470 Map<SkyKey, ConfiguredTarget> targetsByKey = new HashMap<>(Iterables.size(targets));
471 for (ConfiguredTarget target : targets) {
472 targetsByKey.put(getSkyKey(target), target);
473 }
474 Map<SkyKey, Collection<ConfiguredTarget>> reverseDepsByKey =
475 targetifyValues(graph.getReverseDeps(targetsByKey.keySet()));
476 if (targetsByKey.size() != reverseDepsByKey.size()) {
477 Iterable<ConfiguredTargetKey> missingTargets =
478 Sets.difference(targetsByKey.keySet(), reverseDepsByKey.keySet())
479 .stream()
480 .map(SKYKEY_TO_CTKEY)
481 .collect(Collectors.toList());
482 eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets));
483 }
484 Map<ConfiguredTarget, Collection<ConfiguredTarget>> reverseDepsByCT = new HashMap<>();
485 for (Map.Entry<SkyKey, Collection<ConfiguredTarget>> entry : reverseDepsByKey.entrySet()) {
486 reverseDepsByCT.put(targetsByKey.get(entry.getKey()), entry.getValue());
487 }
488 return reverseDepsByCT.isEmpty() ? Collections.emptyList() : filterReverseDeps(reverseDepsByCT);
489 }
490
491 private Collection<ConfiguredTarget> filterReverseDeps(
492 Map<ConfiguredTarget, Collection<ConfiguredTarget>> rawReverseDeps) {
493 Set<ConfiguredTarget> result = CompactHashSet.create();
494 for (Map.Entry<ConfiguredTarget, Collection<ConfiguredTarget>> targetAndRdeps :
495 rawReverseDeps.entrySet()) {
496 ImmutableSet.Builder<ConfiguredTarget> ruleDeps = ImmutableSet.builder();
497 for (ConfiguredTarget parent : targetAndRdeps.getValue()) {
498 if (parent instanceof RuleConfiguredTarget
499 && dependencyFilter != DependencyFilter.ALL_DEPS) {
500 ruleDeps.add(parent);
501 } else {
502 result.add(parent);
503 }
504 }
505 result.addAll(getAllowedDeps((targetAndRdeps.getKey()), ruleDeps.build()));
506 }
507 return result;
508 }
509
juliexxia2af2bff2017-11-17 15:05:14 -0800510 /**
juliexxia865b8882017-12-08 12:37:36 -0800511 * @param target source target
512 * @param deps next level of deps to filter
juliexxia2af2bff2017-11-17 15:05:14 -0800513 */
514 private Collection<ConfiguredTarget> getAllowedDeps(
juliexxia865b8882017-12-08 12:37:36 -0800515 ConfiguredTarget target, Collection<ConfiguredTarget> deps) {
516 // It's possible to query on a target that's configured in the host configuration. In those
517 // cases if --nohost_deps is turned on, we only allow reachable targets that are ALSO in the
518 // host config. This is somewhat counterintuitive and subject to change in the future but seems
519 // like the best option right now.
520 if (settings.contains(Setting.NO_HOST_DEPS)) {
janakr171a7eb2018-03-26 09:26:53 -0700521 BuildConfiguration currentConfig = getConfiguration(target);
juliexxia865b8882017-12-08 12:37:36 -0800522 if (currentConfig != null && currentConfig.isHostConfiguration()) {
523 deps =
524 deps.stream()
525 .filter(
526 dep ->
janakr171a7eb2018-03-26 09:26:53 -0700527 getConfiguration(dep) != null
528 && getConfiguration(dep).isHostConfiguration())
juliexxia865b8882017-12-08 12:37:36 -0800529 .collect(Collectors.toList());
530 } else {
531 deps =
532 deps.stream()
533 .filter(
534 dep ->
janakr171a7eb2018-03-26 09:26:53 -0700535 getConfiguration(dep) != null
536 && !getConfiguration(dep).isHostConfiguration())
juliexxia865b8882017-12-08 12:37:36 -0800537 .collect(Collectors.toList());
538 }
juliexxia2af2bff2017-11-17 15:05:14 -0800539 }
juliexxia865b8882017-12-08 12:37:36 -0800540 if (settings.contains(Setting.NO_IMPLICIT_DEPS) && target instanceof RuleConfiguredTarget) {
janakrac2cd352017-12-20 13:37:13 -0800541 Set<ConfiguredTargetKey> implicitDeps = ((RuleConfiguredTarget) target).getImplicitDeps();
juliexxia865b8882017-12-08 12:37:36 -0800542 deps =
543 deps.stream()
544 .filter(
545 dep ->
546 !implicitDeps.contains(
janakr171a7eb2018-03-26 09:26:53 -0700547 ConfiguredTargetKey.of(getCorrectLabel(dep), getConfiguration(dep))))
juliexxia865b8882017-12-08 12:37:36 -0800548 .collect(Collectors.toList());
549 }
550 return deps;
juliexxia2af2bff2017-11-17 15:05:14 -0800551 }
552
juliexxia45c387f2018-04-19 12:51:18 -0700553 private Map<SkyKey, Collection<ConfiguredTarget>> targetifyValues(
554 Map<SkyKey, ? extends Iterable<SkyKey>> input) throws InterruptedException {
555 Map<SkyKey, Collection<ConfiguredTarget>> result = new HashMap<>();
556 for (Map.Entry<SkyKey, ? extends Iterable<SkyKey>> entry : input.entrySet()) {
557 Collection<ConfiguredTarget> value = new ArrayList<>();
558 for (SkyKey key : entry.getValue()) {
559 if (key.functionName().equals(SkyFunctions.CONFIGURED_TARGET)) {
560 value.add(getConfiguredTarget(key));
561 }
562 }
563 result.put(entry.getKey(), value);
564 }
565 return result;
566 }
567
janakr171a7eb2018-03-26 09:26:53 -0700568 @Nullable
569 private BuildConfiguration getConfiguration(ConfiguredTarget target) {
570 try {
571 return target.getConfigurationKey() == null
572 ? null
573 : ((BuildConfigurationValue) graph.getValue(target.getConfigurationKey()))
574 .getConfiguration();
575 } catch (InterruptedException e) {
576 throw new IllegalStateException("Unexpected interruption during configured target query");
577 }
578 }
579
580 private ConfiguredTargetKey getSkyKey(ConfiguredTarget target) {
581 return ConfiguredTargetKey.of(target, getConfiguration(target));
582 }
583
janakrdc8b2e9a2017-08-18 22:52:37 +0200584
585 @Override
586 public ThreadSafeMutableSet<ConfiguredTarget> getTransitiveClosure(
587 ThreadSafeMutableSet<ConfiguredTarget> targets) throws InterruptedException {
588 return SkyQueryUtils.getTransitiveClosure(
589 targets, this::getFwdDeps, createThreadSafeMutableSet());
590 }
591
592 @Override
593 public void buildTransitiveClosure(
594 QueryExpression caller, ThreadSafeMutableSet<ConfiguredTarget> targetNodes, int maxDepth)
595 throws QueryException, InterruptedException {
596 // TODO(bazel-team): implement this. Just needed for error-checking.
597 }
598
599 @Override
600 public ImmutableList<ConfiguredTarget> getNodesOnPath(ConfiguredTarget from, ConfiguredTarget to)
601 throws InterruptedException {
janakrcc3890d2018-03-27 14:38:52 -0700602 return SkyQueryUtils.getNodesOnPath(
603 from, to, this::getFwdDeps, configuredTargetKeyExtractor::extractKey);
janakrdc8b2e9a2017-08-18 22:52:37 +0200604 }
605
606 @Override
janakrdc8b2e9a2017-08-18 22:52:37 +0200607 public ThreadSafeMutableSet<ConfiguredTarget> createThreadSafeMutableSet() {
608 return new ThreadSafeMutableKeyExtractorBackedSetImpl<>(
janakrcc3890d2018-03-27 14:38:52 -0700609 configuredTargetKeyExtractor,
janakrdc8b2e9a2017-08-18 22:52:37 +0200610 ConfiguredTarget.class,
611 SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
612 }
613
614 @Override
615 public <V> MutableMap<ConfiguredTarget, V> createMutableMap() {
janakrcc3890d2018-03-27 14:38:52 -0700616 return new MutableKeyExtractorBackedMapImpl<>(configuredTargetKeyExtractor);
janakrdc8b2e9a2017-08-18 22:52:37 +0200617 }
618
619 @Override
620 public Uniquifier<ConfiguredTarget> createUniquifier() {
621 return new UniquifierImpl<>(
janakrcc3890d2018-03-27 14:38:52 -0700622 configuredTargetKeyExtractor, SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
janakrdc8b2e9a2017-08-18 22:52:37 +0200623 }
624
625 @Override
626 public MinDepthUniquifier<ConfiguredTarget> createMinDepthUniquifier() {
627 return new MinDepthUniquifierImpl<>(
janakrcc3890d2018-03-27 14:38:52 -0700628 configuredTargetKeyExtractor, SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
janakrdc8b2e9a2017-08-18 22:52:37 +0200629 }
630
juliexxiacc864752018-06-01 09:03:48 -0700631 /** Target patterns are resolved on the fly so no pre-work to be done here. */
janakrdc8b2e9a2017-08-18 22:52:37 +0200632 @Override
juliexxiacc864752018-06-01 09:03:48 -0700633 protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns) {}
juliexxia2af2bff2017-11-17 15:05:14 -0800634
635 public static QueryOptions parseOptions(String rawOptions) throws QueryException {
636 List<String> options = new ArrayList<>(Arrays.asList(rawOptions.split(" ")));
637 OptionsParser parser = OptionsParser.newOptionsParser(QueryOptions.class);
638 parser.setAllowResidue(false);
639 try {
640 parser.parse(options);
641 } catch (OptionsParsingException e) {
642 throw new QueryException(e.getMessage());
643 }
644 return parser.getOptions(QueryOptions.class);
645 }
juliexxia45c387f2018-04-19 12:51:18 -0700646
647 @Override
648 public ThreadSafeMutableSet<ConfiguredTarget> getBuildFiles(
649 QueryExpression caller,
650 ThreadSafeMutableSet<ConfiguredTarget> nodes,
651 boolean buildFiles,
652 boolean loads)
653 throws QueryException, InterruptedException {
654 throw new QueryException("buildfiles() doesn't make sense for the configured target graph");
655 }
656
657 @Override
658 public Collection<ConfiguredTarget> getSiblingTargetsInPackage(ConfiguredTarget target) {
659 throw new UnsupportedOperationException("siblings() not supported");
660 }
661
662
663 @Override
664 public void close() {}
janakrdc8b2e9a2017-08-18 22:52:37 +0200665}
juliexxia5f135e82018-04-11 10:34:00 -0700666