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