blob: e6c3dac10933e6c8ec6126d30cd42e9a335ec8f5 [file] [log] [blame]
twerthfc6c7422018-06-28 10:18:39 -07001// Copyright 2018 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
John Catere39e5c92019-04-23 10:28:02 -070016
juliexxia5281eee2018-10-03 12:24:01 -070017import com.google.common.base.Preconditions;
twerthfc6c7422018-06-28 10:18:39 -070018import com.google.common.collect.ImmutableList;
juliexxia5281eee2018-10-03 12:24:01 -070019import com.google.common.collect.ImmutableMap;
twerthfc6c7422018-06-28 10:18:39 -070020import com.google.common.collect.ImmutableSet;
juliexxia5281eee2018-10-03 12:24:01 -070021import com.google.common.collect.ImmutableSortedSet;
twerthfc6c7422018-06-28 10:18:39 -070022import com.google.common.collect.Iterables;
23import com.google.common.collect.Maps;
24import com.google.common.collect.Sets;
juliexxia5281eee2018-10-03 12:24:01 -070025import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
twerthfc6c7422018-06-28 10:18:39 -070026import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
John Caterb3b3e8b2019-04-03 09:18:42 -070027import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory;
twerthfc6c7422018-06-28 10:18:39 -070028import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
29import com.google.devtools.build.lib.cmdline.Label;
30import com.google.devtools.build.lib.cmdline.TargetParsingException;
31import com.google.devtools.build.lib.cmdline.TargetPattern;
32import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet;
33import com.google.devtools.build.lib.concurrent.MultisetSemaphore;
34import com.google.devtools.build.lib.events.Event;
35import com.google.devtools.build.lib.events.ExtendedEventHandler;
twerthfc6c7422018-06-28 10:18:39 -070036import com.google.devtools.build.lib.packages.DependencyFilter;
37import com.google.devtools.build.lib.packages.NoSuchTargetException;
38import com.google.devtools.build.lib.packages.Rule;
twerthfc6c7422018-06-28 10:18:39 -070039import com.google.devtools.build.lib.packages.Target;
40import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
41import com.google.devtools.build.lib.pkgcache.PackageManager;
42import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
43import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
44import com.google.devtools.build.lib.query2.engine.KeyExtractor;
45import com.google.devtools.build.lib.query2.engine.MinDepthUniquifier;
46import com.google.devtools.build.lib.query2.engine.QueryEnvironment;
47import com.google.devtools.build.lib.query2.engine.QueryEvalResult;
48import com.google.devtools.build.lib.query2.engine.QueryException;
49import com.google.devtools.build.lib.query2.engine.QueryExpression;
50import com.google.devtools.build.lib.query2.engine.QueryExpressionContext;
51import com.google.devtools.build.lib.query2.engine.QueryUtil.MinDepthUniquifierImpl;
52import com.google.devtools.build.lib.query2.engine.QueryUtil.MutableKeyExtractorBackedMapImpl;
53import com.google.devtools.build.lib.query2.engine.QueryUtil.UniquifierImpl;
54import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback;
55import com.google.devtools.build.lib.query2.engine.Uniquifier;
56import com.google.devtools.build.lib.rules.AliasConfiguredTarget;
lebad52f31d2019-03-25 20:05:31 -070057import com.google.devtools.build.lib.skyframe.BlacklistedPackagePrefixesValue;
twerthfc6c7422018-06-28 10:18:39 -070058import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
59import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue;
60import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider;
61import com.google.devtools.build.lib.skyframe.PackageValue;
62import com.google.devtools.build.lib.skyframe.RecursivePackageProviderBackedTargetPatternResolver;
63import com.google.devtools.build.lib.skyframe.RecursivePkgValueRootPackageExtractor;
64import com.google.devtools.build.lib.skyframe.SkyFunctions;
65import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
66import com.google.devtools.build.lib.skyframe.TargetPatternValue;
67import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey;
lebad52f31d2019-03-25 20:05:31 -070068import com.google.devtools.build.lib.vfs.PathFragment;
twerthfc6c7422018-06-28 10:18:39 -070069import com.google.devtools.build.skyframe.SkyKey;
70import com.google.devtools.build.skyframe.WalkableGraph;
71import java.io.IOException;
nharmatab5852612018-11-05 10:49:11 -080072import java.io.OutputStream;
twerthfc6c7422018-06-28 10:18:39 -070073import java.util.ArrayList;
74import java.util.Collection;
75import java.util.Collections;
juliexxia5281eee2018-10-03 12:24:01 -070076import java.util.Comparator;
twerthfc6c7422018-06-28 10:18:39 -070077import java.util.HashMap;
78import java.util.Map;
79import java.util.Set;
80import java.util.function.Function;
81import java.util.function.Supplier;
82import java.util.stream.Collectors;
83import javax.annotation.Nullable;
84
85/**
86 * {@link QueryEnvironment} that runs queries based on results from the analysis phase.
87 *
88 * <p>This environment can theoretically be used for multiple queries, but currently is only ever
89 * used for one over the course of its lifetime. If this ever changed to be used for multiple, the
90 * {@link TargetAccessor} field should be initialized on a per-query basis not a per-environment
91 * basis.
92 *
93 * <p>Aspects are also not supported, but probably should be in some fashion.
94 */
twerth8c11fe92018-07-10 05:59:01 -070095public abstract class PostAnalysisQueryEnvironment<T> extends AbstractBlazeQueryEnvironment<T> {
juliexxia5281eee2018-10-03 12:24:01 -070096 protected final TopLevelConfigurations topLevelConfigurations;
twerthfc6c7422018-06-28 10:18:39 -070097 protected final BuildConfiguration hostConfiguration;
98 private final String parserPrefix;
99 private final PathPackageLocator pkgPath;
100 private final Supplier<WalkableGraph> walkableGraphSupplier;
twerthfc6c7422018-06-28 10:18:39 -0700101 protected WalkableGraph graph;
102
103 private static final Function<SkyKey, ConfiguredTargetKey> SKYKEY_TO_CTKEY =
104 skyKey -> (ConfiguredTargetKey) skyKey.argument();
105 private static final ImmutableList<TargetPatternKey> ALL_PATTERNS;
106
107 static {
108 TargetPattern targetPattern;
109 try {
110 targetPattern = TargetPattern.defaultParser().parse("//...");
111 } catch (TargetParsingException e) {
112 throw new IllegalStateException(e);
113 }
114 ALL_PATTERNS =
115 ImmutableList.of(
116 new TargetPatternKey(
cparsonsdac21352019-05-03 11:41:17 -0700117 targetPattern, FilteringPolicies.NO_FILTER, false, "", ImmutableSet.of()));
twerthfc6c7422018-06-28 10:18:39 -0700118 }
119
120 protected RecursivePackageProviderBackedTargetPatternResolver resolver;
121
122 public PostAnalysisQueryEnvironment(
123 boolean keepGoing,
124 ExtendedEventHandler eventHandler,
125 Iterable<QueryFunction> extraFunctions,
juliexxia5281eee2018-10-03 12:24:01 -0700126 TopLevelConfigurations topLevelConfigurations,
twerthfc6c7422018-06-28 10:18:39 -0700127 BuildConfiguration hostConfiguration,
128 String parserPrefix,
129 PathPackageLocator pkgPath,
130 Supplier<WalkableGraph> walkableGraphSupplier,
nharmataf4023b92018-11-07 14:33:51 -0800131 Set<Setting> settings) {
twerthfc6c7422018-06-28 10:18:39 -0700132 super(keepGoing, true, Rule.ALL_LABELS, eventHandler, settings, extraFunctions);
juliexxia5281eee2018-10-03 12:24:01 -0700133 this.topLevelConfigurations = topLevelConfigurations;
twerthfc6c7422018-06-28 10:18:39 -0700134 this.hostConfiguration = hostConfiguration;
135 this.parserPrefix = parserPrefix;
136 this.pkgPath = pkgPath;
137 this.walkableGraphSupplier = walkableGraphSupplier;
twerthfc6c7422018-06-28 10:18:39 -0700138 }
139
twerth8c11fe92018-07-10 05:59:01 -0700140 public abstract ImmutableList<NamedThreadSafeOutputFormatterCallback<T>>
141 getDefaultOutputFormatters(
142 TargetAccessor<T> accessor,
nharmatab5852612018-11-05 10:49:11 -0800143 ExtendedEventHandler eventHandler,
144 OutputStream outputStream,
twerth8c11fe92018-07-10 05:59:01 -0700145 SkyframeExecutor skyframeExecutor,
146 BuildConfiguration hostConfiguration,
John Caterb3b3e8b2019-04-03 09:18:42 -0700147 @Nullable TransitionFactory<Rule> trimmingTransitionFactory,
twerth8c11fe92018-07-10 05:59:01 -0700148 PackageManager packageManager);
149
150 public abstract String getOutputFormat();
twerthfc6c7422018-06-28 10:18:39 -0700151
152 protected abstract KeyExtractor<T, ConfiguredTargetKey> getConfiguredTargetKeyExtractor();
153
154 @Override
155 public QueryEvalResult evaluateQuery(
156 QueryExpression expr, ThreadSafeOutputFormatterCallback<T> callback)
157 throws QueryException, InterruptedException, IOException {
158 beforeEvaluateQuery();
159 return super.evaluateQuery(expr, callback);
160 }
161
162 private void beforeEvaluateQuery() throws QueryException {
163 graph = walkableGraphSupplier.get();
164 GraphBackedRecursivePackageProvider graphBackedRecursivePackageProvider =
165 new GraphBackedRecursivePackageProvider(
166 graph, ALL_PATTERNS, pkgPath, new RecursivePkgValueRootPackageExtractor());
167 resolver =
168 new RecursivePackageProviderBackedTargetPatternResolver(
169 graphBackedRecursivePackageProvider,
170 eventHandler,
171 FilteringPolicies.NO_FILTER,
172 MultisetSemaphore.unbounded());
173 checkSettings(settings);
174 }
175
176 // Check to make sure the settings requested are currently supported by this class
177 private void checkSettings(Set<Setting> settings) throws QueryException {
178 if (settings.contains(Setting.NO_NODEP_DEPS)
179 || settings.contains(Setting.TESTS_EXPRESSION_STRICT)) {
180 settings =
181 Sets.difference(
182 settings, ImmutableSet.of(Setting.NO_HOST_DEPS, Setting.NO_IMPLICIT_DEPS));
183 throw new QueryException(
184 String.format(
185 "The following filter(s) are not currently supported by configured query: %s",
186 settings.toString()));
187 }
188 }
189
190 public BuildConfiguration getHostConfiguration() {
191 return hostConfiguration;
192 }
193
twerthfc6c7422018-06-28 10:18:39 -0700194 // TODO(bazel-team): It's weird that this untemplated function exists. Fix? Or don't implement?
195 @Override
196 public Target getTarget(Label label) throws TargetNotFoundException, InterruptedException {
197 try {
198 return ((PackageValue)
199 walkableGraphSupplier.get().getValue(PackageValue.key(label.getPackageIdentifier())))
200 .getPackage()
201 .getTarget(label.getName());
202 } catch (NoSuchTargetException e) {
203 throw new TargetNotFoundException(e);
204 }
205 }
206
207 @Override
208 public T getOrCreate(T target) {
209 return target;
210 }
211
212 /**
213 * This method has to exist because {@link AliasConfiguredTarget#getLabel()} returns the label of
214 * the "actual" target instead of the alias target. Grr.
215 */
216 public abstract Label getCorrectLabel(T target);
217
218 @Nullable
219 protected abstract T getHostConfiguredTarget(Label label) throws InterruptedException;
220
221 @Nullable
222 protected abstract T getTargetConfiguredTarget(Label label) throws InterruptedException;
223
224 @Nullable
225 protected abstract T getNullConfiguredTarget(Label label) throws InterruptedException;
226
227 @Nullable
gregcea91495f2019-05-07 07:27:39 -0700228 public ConfiguredTargetValue getConfiguredTargetValue(SkyKey key) throws InterruptedException {
twerthfc6c7422018-06-28 10:18:39 -0700229 return (ConfiguredTargetValue) walkableGraphSupplier.get().getValue(key);
230 }
231
gregcea91495f2019-05-07 07:27:39 -0700232 public ImmutableSet<PathFragment> getBlacklistedPackagePrefixesPathFragments()
lebad52f31d2019-03-25 20:05:31 -0700233 throws InterruptedException {
234 return ((BlacklistedPackagePrefixesValue)
235 walkableGraphSupplier.get().getValue(BlacklistedPackagePrefixesValue.key()))
236 .getPatterns();
237 }
238
twerthfc6c7422018-06-28 10:18:39 -0700239 @Nullable
240 protected abstract T getValueFromKey(SkyKey key) throws InterruptedException;
241
cparsonsdac21352019-05-03 11:41:17 -0700242 protected TargetPattern getPattern(String pattern) throws TargetParsingException {
twerthfc6c7422018-06-28 10:18:39 -0700243 TargetPatternKey targetPatternKey =
244 ((TargetPatternKey)
245 TargetPatternValue.key(
246 pattern, TargetPatternEvaluator.DEFAULT_FILTERING_POLICY, parserPrefix)
247 .argument());
248 return targetPatternKey.getParsedPattern();
249 }
250
gregcea91495f2019-05-07 07:27:39 -0700251 public ThreadSafeMutableSet<T> getFwdDeps(Iterable<T> targets) throws InterruptedException {
twerthfc6c7422018-06-28 10:18:39 -0700252 Map<SkyKey, T> targetsByKey = Maps.newHashMapWithExpectedSize(Iterables.size(targets));
253 for (T target : targets) {
254 targetsByKey.put(getSkyKey(target), target);
255 }
256 Map<SkyKey, Collection<T>> directDeps =
257 targetifyValues(graph.getDirectDeps(targetsByKey.keySet()));
258 if (targetsByKey.size() != directDeps.size()) {
259 Iterable<ConfiguredTargetKey> missingTargets =
juliexxia5281eee2018-10-03 12:24:01 -0700260 Sets.difference(targetsByKey.keySet(), directDeps.keySet()).stream()
twerthfc6c7422018-06-28 10:18:39 -0700261 .map(SKYKEY_TO_CTKEY)
262 .collect(Collectors.toList());
263 eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets));
264 }
265 ThreadSafeMutableSet<T> result = createThreadSafeMutableSet();
266 for (Map.Entry<SkyKey, Collection<T>> entry : directDeps.entrySet()) {
267 result.addAll(filterFwdDeps(targetsByKey.get(entry.getKey()), entry.getValue()));
268 }
269 return result;
270 }
271
juliexxiaa880c0c2018-08-21 09:35:01 -0700272 @Override
273 public ThreadSafeMutableSet<T> getFwdDeps(Iterable<T> targets, QueryExpressionContext<T> context)
274 throws InterruptedException {
275 return getFwdDeps(targets);
276 }
277
twerthfc6c7422018-06-28 10:18:39 -0700278 private Collection<T> filterFwdDeps(T configTarget, Collection<T> rawFwdDeps) {
279 if (settings.isEmpty()) {
280 return rawFwdDeps;
281 }
282 return getAllowedDeps(configTarget, rawFwdDeps);
283 }
284
285 @Override
286 public Collection<T> getReverseDeps(Iterable<T> targets, QueryExpressionContext<T> context)
287 throws InterruptedException {
288 Map<SkyKey, T> targetsByKey = Maps.newHashMapWithExpectedSize(Iterables.size(targets));
289 for (T target : targets) {
290 targetsByKey.put(getSkyKey(target), target);
291 }
292 Map<SkyKey, Collection<T>> reverseDepsByKey =
293 targetifyValues(graph.getReverseDeps(targetsByKey.keySet()));
294 if (targetsByKey.size() != reverseDepsByKey.size()) {
295 Iterable<ConfiguredTargetKey> missingTargets =
juliexxia5281eee2018-10-03 12:24:01 -0700296 Sets.difference(targetsByKey.keySet(), reverseDepsByKey.keySet()).stream()
twerthfc6c7422018-06-28 10:18:39 -0700297 .map(SKYKEY_TO_CTKEY)
298 .collect(Collectors.toList());
299 eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets));
300 }
301 Map<T, Collection<T>> reverseDepsByCT = new HashMap<>();
302 for (Map.Entry<SkyKey, Collection<T>> entry : reverseDepsByKey.entrySet()) {
303 reverseDepsByCT.put(targetsByKey.get(entry.getKey()), entry.getValue());
304 }
305 return reverseDepsByCT.isEmpty() ? Collections.emptyList() : filterReverseDeps(reverseDepsByCT);
306 }
307
308 private Collection<T> filterReverseDeps(Map<T, Collection<T>> rawReverseDeps) {
309 Set<T> result = CompactHashSet.create();
310 for (Map.Entry<T, Collection<T>> targetAndRdeps : rawReverseDeps.entrySet()) {
311 ImmutableSet.Builder<T> ruleDeps = ImmutableSet.builder();
312 for (T parent : targetAndRdeps.getValue()) {
313 if (parent instanceof RuleConfiguredTarget
314 && dependencyFilter != DependencyFilter.ALL_DEPS) {
315 ruleDeps.add(parent);
316 } else {
317 result.add(parent);
318 }
319 }
Googler82e55542018-11-05 14:15:26 -0800320 result.addAll(getAllowedDeps(targetAndRdeps.getKey(), ruleDeps.build()));
twerthfc6c7422018-06-28 10:18:39 -0700321 }
322 return result;
323 }
324
325 /**
326 * @param target source target
327 * @param deps next level of deps to filter
328 */
329 protected Collection<T> getAllowedDeps(T target, Collection<T> deps) {
330 // It's possible to query on a target that's configured in the host configuration. In those
331 // cases if --nohost_deps is turned on, we only allow reachable targets that are ALSO in the
332 // host config. This is somewhat counterintuitive and subject to change in the future but seems
333 // like the best option right now.
334 if (settings.contains(Setting.NO_HOST_DEPS)) {
335 BuildConfiguration currentConfig = getConfiguration(target);
336 if (currentConfig != null && currentConfig.isHostConfiguration()) {
337 deps =
338 deps.stream()
339 .filter(
340 dep ->
341 getConfiguration(dep) != null
342 && getConfiguration(dep).isHostConfiguration())
343 .collect(Collectors.toList());
344 } else {
345 deps =
346 deps.stream()
347 .filter(
348 dep ->
349 getConfiguration(dep) != null
350 && !getConfiguration(dep).isHostConfiguration())
351 .collect(Collectors.toList());
352 }
353 }
twertha1a57a52018-07-18 05:34:13 -0700354 if (settings.contains(Setting.NO_IMPLICIT_DEPS)) {
355 RuleConfiguredTarget ruleConfiguredTarget = getRuleConfiguredTarget(target);
356 if (ruleConfiguredTarget != null) {
357 Set<ConfiguredTargetKey> implicitDeps = ruleConfiguredTarget.getImplicitDeps();
358 deps =
359 deps.stream()
360 .filter(
361 dep ->
362 !implicitDeps.contains(
363 ConfiguredTargetKey.of(getCorrectLabel(dep), getConfiguration(dep))))
364 .collect(Collectors.toList());
365 }
twerthfc6c7422018-06-28 10:18:39 -0700366 }
367 return deps;
368 }
369
twertha1a57a52018-07-18 05:34:13 -0700370 protected abstract RuleConfiguredTarget getRuleConfiguredTarget(T target);
371
John Catere39e5c92019-04-23 10:28:02 -0700372 protected Collection<T> targetifyValues(Iterable<SkyKey> dependencies)
373 throws InterruptedException {
374 Collection<T> values = new ArrayList<>();
375 for (SkyKey key : dependencies) {
376 if (key.functionName().equals(SkyFunctions.CONFIGURED_TARGET)) {
377 values.add(getValueFromKey(key));
378 } else if (key.functionName().equals(SkyFunctions.TOOLCHAIN_RESOLUTION)) {
379 // Also fetch these dependencies.
380 Collection<T> toolchainDeps = targetifyValues(graph.getDirectDeps(key));
381 values.addAll(toolchainDeps);
382 }
383 }
384 return values;
385 }
386
twerthfc6c7422018-06-28 10:18:39 -0700387 protected Map<SkyKey, Collection<T>> targetifyValues(
388 Map<SkyKey, ? extends Iterable<SkyKey>> input) throws InterruptedException {
389 Map<SkyKey, Collection<T>> result = new HashMap<>();
390 for (Map.Entry<SkyKey, ? extends Iterable<SkyKey>> entry : input.entrySet()) {
John Catere39e5c92019-04-23 10:28:02 -0700391 result.put(entry.getKey(), targetifyValues(entry.getValue()));
twerthfc6c7422018-06-28 10:18:39 -0700392 }
393 return result;
394 }
395
396 @Nullable
397 protected abstract BuildConfiguration getConfiguration(T target);
398
399 protected abstract ConfiguredTargetKey getSkyKey(T target);
400
401 @Override
402 public ThreadSafeMutableSet<T> getTransitiveClosure(
403 ThreadSafeMutableSet<T> targets, QueryExpressionContext<T> context)
404 throws InterruptedException {
405 return SkyQueryUtils.getTransitiveClosure(
406 targets, targets1 -> getFwdDeps(targets1, context), createThreadSafeMutableSet());
407 }
408
409 @Override
410 public void buildTransitiveClosure(
411 QueryExpression caller, ThreadSafeMutableSet<T> targetNodes, int maxDepth) {
412 // TODO(bazel-team): implement this. Just needed for error-checking.
413 }
414
415 @Override
416 public ImmutableList<T> getNodesOnPath(T from, T to, QueryExpressionContext<T> context)
417 throws InterruptedException {
418 return SkyQueryUtils.getNodesOnPath(
419 from,
420 to,
421 targets -> getFwdDeps(targets, context),
422 getConfiguredTargetKeyExtractor()::extractKey);
423 }
424
425 @Override
426 public <V> MutableMap<T, V> createMutableMap() {
427 return new MutableKeyExtractorBackedMapImpl<>(getConfiguredTargetKeyExtractor());
428 }
429
430 @Override
431 public Uniquifier<T> createUniquifier() {
shreyax6871cf02018-07-02 09:16:18 -0700432 return new UniquifierImpl<>(getConfiguredTargetKeyExtractor());
twerthfc6c7422018-06-28 10:18:39 -0700433 }
434
435 @Override
436 public MinDepthUniquifier<T> createMinDepthUniquifier() {
437 return new MinDepthUniquifierImpl<>(
438 getConfiguredTargetKeyExtractor(), SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
439 }
440
441 /** Target patterns are resolved on the fly so no pre-work to be done here. */
442 @Override
443 protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns) {}
444
445 @Override
446 public ThreadSafeMutableSet<T> getBuildFiles(
447 QueryExpression caller,
448 ThreadSafeMutableSet<T> nodes,
449 boolean buildFiles,
450 boolean loads,
451 QueryExpressionContext<T> context)
452 throws QueryException {
453 throw new QueryException("buildfiles() doesn't make sense for the configured target graph");
454 }
455
456 @Override
457 public Collection<T> getSiblingTargetsInPackage(T target) {
458 throw new UnsupportedOperationException("siblings() not supported");
459 }
460
461 @Override
462 public void close() {}
juliexxia5281eee2018-10-03 12:24:01 -0700463
464 /** A wrapper class for the set of top-level configurations in a query. */
465 public static class TopLevelConfigurations {
466
juliexxia5281eee2018-10-03 12:24:01 -0700467 /** A map of non-null configured top-level targets sorted by configuration checksum. */
468 private final ImmutableMap<Label, BuildConfiguration> nonNulls;
469 /**
470 * {@code nonNulls} may often have many duplicate values in its value set so we store a sorted
471 * set of all the non-null configurations here.
472 */
473 private final ImmutableSortedSet<BuildConfiguration> nonNullConfigs;
474 /** A list of null configured top-level targets. */
475 private final ImmutableList<Label> nulls;
476
477 public TopLevelConfigurations(
478 Collection<TargetAndConfiguration> topLevelTargetsAndConfigurations) {
479 ImmutableMap.Builder<Label, BuildConfiguration> nonNullsBuilder =
480 ImmutableMap.builderWithExpectedSize(topLevelTargetsAndConfigurations.size());
481 ImmutableList.Builder<Label> nullsBuilder = new ImmutableList.Builder<>();
482 for (TargetAndConfiguration targetAndConfiguration : topLevelTargetsAndConfigurations) {
483 if (targetAndConfiguration.getConfiguration() == null) {
484 nullsBuilder.add(targetAndConfiguration.getLabel());
485 } else {
486 nonNullsBuilder.put(
487 targetAndConfiguration.getLabel(), targetAndConfiguration.getConfiguration());
488 }
489 }
490 nonNulls = nonNullsBuilder.build();
491 nonNullConfigs =
492 ImmutableSortedSet.copyOf(
493 Comparator.comparing(BuildConfiguration::checksum), nonNulls.values());
494 nulls = nullsBuilder.build();
495 }
496
gregcea91495f2019-05-07 07:27:39 -0700497 public boolean isTopLevelTarget(Label label) {
juliexxia5281eee2018-10-03 12:24:01 -0700498 return nonNulls.containsKey(label) || nulls.contains(label);
499 }
500
501 // This method returns the configuration of a top-level target if it's not null-configured and
502 // otherwise returns null (signifying it is null configured).
503 @Nullable
gregcea91495f2019-05-07 07:27:39 -0700504 public BuildConfiguration getConfigurationForTopLevelTarget(Label label) {
juliexxia5281eee2018-10-03 12:24:01 -0700505 Preconditions.checkArgument(
506 isTopLevelTarget(label),
507 "Attempting to get top-level configuration for non-top-level target %s.",
508 label);
509 return nonNulls.get(label);
510 }
511
512 public Iterable<BuildConfiguration> getConfigurations() {
513 if (nulls.isEmpty()) {
514 return nonNullConfigs;
515 } else {
Googler2edf5992018-12-17 12:56:17 -0800516 return Iterables.concat(nonNullConfigs, Collections.singletonList(null));
juliexxia5281eee2018-10-03 12:24:01 -0700517 }
518 }
519 }
twerthfc6c7422018-06-28 10:18:39 -0700520}