twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 1 | // 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. |
| 14 | package com.google.devtools.build.lib.query2; |
| 15 | |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 16 | import com.google.common.base.Preconditions; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 17 | import com.google.common.collect.ImmutableList; |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 18 | import com.google.common.collect.ImmutableMap; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 19 | import com.google.common.collect.ImmutableSet; |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 20 | import com.google.common.collect.ImmutableSortedSet; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 21 | import com.google.common.collect.Iterables; |
| 22 | import com.google.common.collect.Maps; |
| 23 | import com.google.common.collect.Sets; |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 24 | import com.google.devtools.build.lib.analysis.TargetAndConfiguration; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 25 | import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
John Cater | b3b3e8b | 2019-04-03 09:18:42 -0700 | [diff] [blame] | 26 | import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 27 | import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; |
| 28 | import com.google.devtools.build.lib.cmdline.Label; |
| 29 | import com.google.devtools.build.lib.cmdline.TargetParsingException; |
| 30 | import com.google.devtools.build.lib.cmdline.TargetPattern; |
| 31 | import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet; |
| 32 | import com.google.devtools.build.lib.concurrent.MultisetSemaphore; |
| 33 | import com.google.devtools.build.lib.events.Event; |
| 34 | import com.google.devtools.build.lib.events.ExtendedEventHandler; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 35 | import com.google.devtools.build.lib.packages.DependencyFilter; |
| 36 | import com.google.devtools.build.lib.packages.NoSuchTargetException; |
| 37 | import com.google.devtools.build.lib.packages.Rule; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 38 | import com.google.devtools.build.lib.packages.Target; |
| 39 | import com.google.devtools.build.lib.pkgcache.FilteringPolicies; |
| 40 | import com.google.devtools.build.lib.pkgcache.PackageManager; |
| 41 | import com.google.devtools.build.lib.pkgcache.PathPackageLocator; |
michajlo | e749677 | 2019-08-22 16:55:33 -0700 | [diff] [blame] | 42 | import com.google.devtools.build.lib.query2.common.AbstractBlazeQueryEnvironment; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 43 | import com.google.devtools.build.lib.query2.engine.KeyExtractor; |
| 44 | import com.google.devtools.build.lib.query2.engine.MinDepthUniquifier; |
| 45 | import com.google.devtools.build.lib.query2.engine.QueryEnvironment; |
| 46 | import com.google.devtools.build.lib.query2.engine.QueryEvalResult; |
| 47 | import com.google.devtools.build.lib.query2.engine.QueryException; |
| 48 | import com.google.devtools.build.lib.query2.engine.QueryExpression; |
| 49 | import com.google.devtools.build.lib.query2.engine.QueryExpressionContext; |
| 50 | import com.google.devtools.build.lib.query2.engine.QueryUtil.MinDepthUniquifierImpl; |
| 51 | import com.google.devtools.build.lib.query2.engine.QueryUtil.MutableKeyExtractorBackedMapImpl; |
| 52 | import com.google.devtools.build.lib.query2.engine.QueryUtil.UniquifierImpl; |
| 53 | import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback; |
| 54 | import com.google.devtools.build.lib.query2.engine.Uniquifier; |
| 55 | import com.google.devtools.build.lib.rules.AliasConfiguredTarget; |
leba | d52f31d | 2019-03-25 20:05:31 -0700 | [diff] [blame] | 56 | import com.google.devtools.build.lib.skyframe.BlacklistedPackagePrefixesValue; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 57 | import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; |
| 58 | import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; |
| 59 | import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider; |
| 60 | import com.google.devtools.build.lib.skyframe.PackageValue; |
| 61 | import com.google.devtools.build.lib.skyframe.RecursivePackageProviderBackedTargetPatternResolver; |
| 62 | import com.google.devtools.build.lib.skyframe.RecursivePkgValueRootPackageExtractor; |
| 63 | import com.google.devtools.build.lib.skyframe.SkyFunctions; |
| 64 | import com.google.devtools.build.lib.skyframe.SkyframeExecutor; |
| 65 | import com.google.devtools.build.lib.skyframe.TargetPatternValue; |
| 66 | import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey; |
leba | d52f31d | 2019-03-25 20:05:31 -0700 | [diff] [blame] | 67 | import com.google.devtools.build.lib.vfs.PathFragment; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 68 | import com.google.devtools.build.skyframe.SkyKey; |
| 69 | import com.google.devtools.build.skyframe.WalkableGraph; |
| 70 | import java.io.IOException; |
nharmata | b585261 | 2018-11-05 10:49:11 -0800 | [diff] [blame] | 71 | import java.io.OutputStream; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 72 | import java.util.Collection; |
| 73 | import java.util.Collections; |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 74 | import java.util.Comparator; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 75 | import java.util.HashMap; |
| 76 | import java.util.Map; |
| 77 | import java.util.Set; |
| 78 | import java.util.function.Function; |
| 79 | import java.util.function.Supplier; |
| 80 | import java.util.stream.Collectors; |
| 81 | import javax.annotation.Nullable; |
| 82 | |
| 83 | /** |
| 84 | * {@link QueryEnvironment} that runs queries based on results from the analysis phase. |
| 85 | * |
| 86 | * <p>This environment can theoretically be used for multiple queries, but currently is only ever |
| 87 | * used for one over the course of its lifetime. If this ever changed to be used for multiple, the |
| 88 | * {@link TargetAccessor} field should be initialized on a per-query basis not a per-environment |
| 89 | * basis. |
| 90 | * |
| 91 | * <p>Aspects are also not supported, but probably should be in some fashion. |
| 92 | */ |
twerth | 8c11fe9 | 2018-07-10 05:59:01 -0700 | [diff] [blame] | 93 | public abstract class PostAnalysisQueryEnvironment<T> extends AbstractBlazeQueryEnvironment<T> { |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 94 | protected final TopLevelConfigurations topLevelConfigurations; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 95 | protected final BuildConfiguration hostConfiguration; |
| 96 | private final String parserPrefix; |
| 97 | private final PathPackageLocator pkgPath; |
| 98 | private final Supplier<WalkableGraph> walkableGraphSupplier; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 99 | protected WalkableGraph graph; |
| 100 | |
| 101 | private static final Function<SkyKey, ConfiguredTargetKey> SKYKEY_TO_CTKEY = |
| 102 | skyKey -> (ConfiguredTargetKey) skyKey.argument(); |
adgar | b70e636 | 2019-11-21 16:54:09 -0800 | [diff] [blame] | 103 | private static final ImmutableList<TargetPattern> ALL_PATTERNS = |
| 104 | ImmutableList.of(TargetPattern.defaultParser().parseConstantUnchecked("//...")); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 105 | |
| 106 | protected RecursivePackageProviderBackedTargetPatternResolver resolver; |
| 107 | |
| 108 | public PostAnalysisQueryEnvironment( |
| 109 | boolean keepGoing, |
| 110 | ExtendedEventHandler eventHandler, |
| 111 | Iterable<QueryFunction> extraFunctions, |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 112 | TopLevelConfigurations topLevelConfigurations, |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 113 | BuildConfiguration hostConfiguration, |
| 114 | String parserPrefix, |
| 115 | PathPackageLocator pkgPath, |
| 116 | Supplier<WalkableGraph> walkableGraphSupplier, |
nharmata | f4023b9 | 2018-11-07 14:33:51 -0800 | [diff] [blame] | 117 | Set<Setting> settings) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 118 | super(keepGoing, true, Rule.ALL_LABELS, eventHandler, settings, extraFunctions); |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 119 | this.topLevelConfigurations = topLevelConfigurations; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 120 | this.hostConfiguration = hostConfiguration; |
| 121 | this.parserPrefix = parserPrefix; |
| 122 | this.pkgPath = pkgPath; |
| 123 | this.walkableGraphSupplier = walkableGraphSupplier; |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 124 | } |
| 125 | |
twerth | 8c11fe9 | 2018-07-10 05:59:01 -0700 | [diff] [blame] | 126 | public abstract ImmutableList<NamedThreadSafeOutputFormatterCallback<T>> |
| 127 | getDefaultOutputFormatters( |
| 128 | TargetAccessor<T> accessor, |
nharmata | b585261 | 2018-11-05 10:49:11 -0800 | [diff] [blame] | 129 | ExtendedEventHandler eventHandler, |
| 130 | OutputStream outputStream, |
twerth | 8c11fe9 | 2018-07-10 05:59:01 -0700 | [diff] [blame] | 131 | SkyframeExecutor skyframeExecutor, |
| 132 | BuildConfiguration hostConfiguration, |
John Cater | b3b3e8b | 2019-04-03 09:18:42 -0700 | [diff] [blame] | 133 | @Nullable TransitionFactory<Rule> trimmingTransitionFactory, |
twerth | 8c11fe9 | 2018-07-10 05:59:01 -0700 | [diff] [blame] | 134 | PackageManager packageManager); |
| 135 | |
| 136 | public abstract String getOutputFormat(); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 137 | |
| 138 | protected abstract KeyExtractor<T, ConfiguredTargetKey> getConfiguredTargetKeyExtractor(); |
| 139 | |
| 140 | @Override |
| 141 | public QueryEvalResult evaluateQuery( |
| 142 | QueryExpression expr, ThreadSafeOutputFormatterCallback<T> callback) |
| 143 | throws QueryException, InterruptedException, IOException { |
| 144 | beforeEvaluateQuery(); |
| 145 | return super.evaluateQuery(expr, callback); |
| 146 | } |
| 147 | |
| 148 | private void beforeEvaluateQuery() throws QueryException { |
| 149 | graph = walkableGraphSupplier.get(); |
| 150 | GraphBackedRecursivePackageProvider graphBackedRecursivePackageProvider = |
| 151 | new GraphBackedRecursivePackageProvider( |
| 152 | graph, ALL_PATTERNS, pkgPath, new RecursivePkgValueRootPackageExtractor()); |
| 153 | resolver = |
| 154 | new RecursivePackageProviderBackedTargetPatternResolver( |
| 155 | graphBackedRecursivePackageProvider, |
| 156 | eventHandler, |
| 157 | FilteringPolicies.NO_FILTER, |
| 158 | MultisetSemaphore.unbounded()); |
| 159 | checkSettings(settings); |
| 160 | } |
| 161 | |
| 162 | // Check to make sure the settings requested are currently supported by this class |
| 163 | private void checkSettings(Set<Setting> settings) throws QueryException { |
| 164 | if (settings.contains(Setting.NO_NODEP_DEPS) |
| 165 | || settings.contains(Setting.TESTS_EXPRESSION_STRICT)) { |
| 166 | settings = |
| 167 | Sets.difference( |
wtroberts | d39a1ec | 2019-09-05 07:59:12 -0700 | [diff] [blame] | 168 | settings, ImmutableSet.of(Setting.ONLY_TARGET_DEPS, Setting.NO_IMPLICIT_DEPS)); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 169 | throw new QueryException( |
| 170 | String.format( |
| 171 | "The following filter(s) are not currently supported by configured query: %s", |
| 172 | settings.toString())); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | public BuildConfiguration getHostConfiguration() { |
| 177 | return hostConfiguration; |
| 178 | } |
| 179 | |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 180 | // TODO(bazel-team): It's weird that this untemplated function exists. Fix? Or don't implement? |
| 181 | @Override |
| 182 | public Target getTarget(Label label) throws TargetNotFoundException, InterruptedException { |
| 183 | try { |
| 184 | return ((PackageValue) |
| 185 | walkableGraphSupplier.get().getValue(PackageValue.key(label.getPackageIdentifier()))) |
| 186 | .getPackage() |
| 187 | .getTarget(label.getName()); |
| 188 | } catch (NoSuchTargetException e) { |
| 189 | throw new TargetNotFoundException(e); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | @Override |
| 194 | public T getOrCreate(T target) { |
| 195 | return target; |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * This method has to exist because {@link AliasConfiguredTarget#getLabel()} returns the label of |
| 200 | * the "actual" target instead of the alias target. Grr. |
| 201 | */ |
| 202 | public abstract Label getCorrectLabel(T target); |
| 203 | |
| 204 | @Nullable |
| 205 | protected abstract T getHostConfiguredTarget(Label label) throws InterruptedException; |
| 206 | |
| 207 | @Nullable |
| 208 | protected abstract T getTargetConfiguredTarget(Label label) throws InterruptedException; |
| 209 | |
| 210 | @Nullable |
| 211 | protected abstract T getNullConfiguredTarget(Label label) throws InterruptedException; |
| 212 | |
| 213 | @Nullable |
gregce | a91495f | 2019-05-07 07:27:39 -0700 | [diff] [blame] | 214 | public ConfiguredTargetValue getConfiguredTargetValue(SkyKey key) throws InterruptedException { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 215 | return (ConfiguredTargetValue) walkableGraphSupplier.get().getValue(key); |
| 216 | } |
| 217 | |
gregce | a91495f | 2019-05-07 07:27:39 -0700 | [diff] [blame] | 218 | public ImmutableSet<PathFragment> getBlacklistedPackagePrefixesPathFragments() |
leba | d52f31d | 2019-03-25 20:05:31 -0700 | [diff] [blame] | 219 | throws InterruptedException { |
| 220 | return ((BlacklistedPackagePrefixesValue) |
| 221 | walkableGraphSupplier.get().getValue(BlacklistedPackagePrefixesValue.key())) |
| 222 | .getPatterns(); |
| 223 | } |
| 224 | |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 225 | @Nullable |
| 226 | protected abstract T getValueFromKey(SkyKey key) throws InterruptedException; |
| 227 | |
cparsons | dac2135 | 2019-05-03 11:41:17 -0700 | [diff] [blame] | 228 | protected TargetPattern getPattern(String pattern) throws TargetParsingException { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 229 | TargetPatternKey targetPatternKey = |
| 230 | ((TargetPatternKey) |
ulfjack | e83775d | 2019-05-14 08:58:46 -0700 | [diff] [blame] | 231 | TargetPatternValue.key(pattern, FilteringPolicies.NO_FILTER, parserPrefix).argument()); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 232 | return targetPatternKey.getParsedPattern(); |
| 233 | } |
| 234 | |
gregce | a91495f | 2019-05-07 07:27:39 -0700 | [diff] [blame] | 235 | public ThreadSafeMutableSet<T> getFwdDeps(Iterable<T> targets) throws InterruptedException { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 236 | Map<SkyKey, T> targetsByKey = Maps.newHashMapWithExpectedSize(Iterables.size(targets)); |
| 237 | for (T target : targets) { |
| 238 | targetsByKey.put(getSkyKey(target), target); |
| 239 | } |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 240 | Map<SkyKey, ImmutableList<ClassifiedDependency<T>>> directDeps = |
| 241 | targetifyValues(targetsByKey, graph.getDirectDeps(targetsByKey.keySet())); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 242 | if (targetsByKey.size() != directDeps.size()) { |
| 243 | Iterable<ConfiguredTargetKey> missingTargets = |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 244 | Sets.difference(targetsByKey.keySet(), directDeps.keySet()).stream() |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 245 | .map(SKYKEY_TO_CTKEY) |
| 246 | .collect(Collectors.toList()); |
| 247 | eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets)); |
| 248 | } |
| 249 | ThreadSafeMutableSet<T> result = createThreadSafeMutableSet(); |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 250 | for (Map.Entry<SkyKey, ImmutableList<ClassifiedDependency<T>>> entry : directDeps.entrySet()) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 251 | result.addAll(filterFwdDeps(targetsByKey.get(entry.getKey()), entry.getValue())); |
| 252 | } |
| 253 | return result; |
| 254 | } |
| 255 | |
juliexxia | a880c0c | 2018-08-21 09:35:01 -0700 | [diff] [blame] | 256 | @Override |
| 257 | public ThreadSafeMutableSet<T> getFwdDeps(Iterable<T> targets, QueryExpressionContext<T> context) |
| 258 | throws InterruptedException { |
| 259 | return getFwdDeps(targets); |
| 260 | } |
| 261 | |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 262 | private ImmutableList<T> filterFwdDeps( |
| 263 | T configTarget, ImmutableList<ClassifiedDependency<T>> rawFwdDeps) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 264 | if (settings.isEmpty()) { |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 265 | return getDependencies(rawFwdDeps); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 266 | } |
| 267 | return getAllowedDeps(configTarget, rawFwdDeps); |
| 268 | } |
| 269 | |
| 270 | @Override |
| 271 | public Collection<T> getReverseDeps(Iterable<T> targets, QueryExpressionContext<T> context) |
| 272 | throws InterruptedException { |
| 273 | Map<SkyKey, T> targetsByKey = Maps.newHashMapWithExpectedSize(Iterables.size(targets)); |
| 274 | for (T target : targets) { |
| 275 | targetsByKey.put(getSkyKey(target), target); |
| 276 | } |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 277 | Map<SkyKey, ImmutableList<ClassifiedDependency<T>>> reverseDepsByKey = |
| 278 | targetifyValues(targetsByKey, graph.getReverseDeps(targetsByKey.keySet())); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 279 | if (targetsByKey.size() != reverseDepsByKey.size()) { |
| 280 | Iterable<ConfiguredTargetKey> missingTargets = |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 281 | Sets.difference(targetsByKey.keySet(), reverseDepsByKey.keySet()).stream() |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 282 | .map(SKYKEY_TO_CTKEY) |
| 283 | .collect(Collectors.toList()); |
| 284 | eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets)); |
| 285 | } |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 286 | Map<T, ImmutableList<ClassifiedDependency<T>>> reverseDepsByCT = new HashMap<>(); |
| 287 | for (Map.Entry<SkyKey, ImmutableList<ClassifiedDependency<T>>> entry : |
| 288 | reverseDepsByKey.entrySet()) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 289 | reverseDepsByCT.put(targetsByKey.get(entry.getKey()), entry.getValue()); |
| 290 | } |
| 291 | return reverseDepsByCT.isEmpty() ? Collections.emptyList() : filterReverseDeps(reverseDepsByCT); |
| 292 | } |
| 293 | |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 294 | private Collection<T> filterReverseDeps( |
| 295 | Map<T, ImmutableList<ClassifiedDependency<T>>> rawReverseDeps) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 296 | Set<T> result = CompactHashSet.create(); |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 297 | for (Map.Entry<T, ImmutableList<ClassifiedDependency<T>>> targetAndRdeps : |
| 298 | rawReverseDeps.entrySet()) { |
| 299 | ImmutableList.Builder<ClassifiedDependency<T>> ruleDeps = ImmutableList.builder(); |
| 300 | for (ClassifiedDependency<T> parent : targetAndRdeps.getValue()) { |
| 301 | T dependency = parent.dependency; |
| 302 | if (parent.dependency instanceof RuleConfiguredTarget |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 303 | && dependencyFilter != DependencyFilter.ALL_DEPS) { |
| 304 | ruleDeps.add(parent); |
| 305 | } else { |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 306 | result.add(dependency); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 307 | } |
| 308 | } |
Googler | 82e5554 | 2018-11-05 14:15:26 -0800 | [diff] [blame] | 309 | result.addAll(getAllowedDeps(targetAndRdeps.getKey(), ruleDeps.build())); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 310 | } |
| 311 | return result; |
| 312 | } |
| 313 | |
| 314 | /** |
| 315 | * @param target source target |
| 316 | * @param deps next level of deps to filter |
| 317 | */ |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 318 | private ImmutableList<T> getAllowedDeps(T target, Collection<ClassifiedDependency<T>> deps) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 319 | // It's possible to query on a target that's configured in the host configuration. In those |
wtroberts | 5a0cf9b | 2019-09-06 07:55:15 -0700 | [diff] [blame] | 320 | // cases if --notool_deps is turned on, we only allow reachable targets that are ALSO in the |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 321 | // host config. This is somewhat counterintuitive and subject to change in the future but seems |
| 322 | // like the best option right now. |
wtroberts | d39a1ec | 2019-09-05 07:59:12 -0700 | [diff] [blame] | 323 | if (settings.contains(Setting.ONLY_TARGET_DEPS)) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 324 | BuildConfiguration currentConfig = getConfiguration(target); |
wtroberts | d39a1ec | 2019-09-05 07:59:12 -0700 | [diff] [blame] | 325 | if (currentConfig != null && currentConfig.isToolConfiguration()) { |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 326 | deps = |
| 327 | deps.stream() |
| 328 | .filter( |
| 329 | dep -> |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 330 | getConfiguration(dep.dependency) != null |
| 331 | && getConfiguration(dep.dependency).isToolConfiguration()) |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 332 | .collect(Collectors.toList()); |
| 333 | } else { |
| 334 | deps = |
| 335 | deps.stream() |
| 336 | .filter( |
| 337 | dep -> |
gregce | 41a8dfd | 2019-09-12 11:43:43 -0700 | [diff] [blame] | 338 | // We include source files, which have null configuration, even though |
| 339 | // they can also appear on host-configured attributes like genrule#tools. |
| 340 | // While this may not be strictly correct, it's better to overapproximate |
| 341 | // than underapproximate the results. |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 342 | getConfiguration(dep.dependency) == null |
| 343 | || !getConfiguration(dep.dependency).isToolConfiguration()) |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 344 | .collect(Collectors.toList()); |
| 345 | } |
| 346 | } |
twerth | a1a57a5 | 2018-07-18 05:34:13 -0700 | [diff] [blame] | 347 | if (settings.contains(Setting.NO_IMPLICIT_DEPS)) { |
| 348 | RuleConfiguredTarget ruleConfiguredTarget = getRuleConfiguredTarget(target); |
| 349 | if (ruleConfiguredTarget != null) { |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 350 | deps = deps.stream().filter(dep -> !dep.implicit).collect(Collectors.toList()); |
twerth | a1a57a5 | 2018-07-18 05:34:13 -0700 | [diff] [blame] | 351 | } |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 352 | } |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 353 | return getDependencies(deps); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 354 | } |
| 355 | |
twerth | a1a57a5 | 2018-07-18 05:34:13 -0700 | [diff] [blame] | 356 | protected abstract RuleConfiguredTarget getRuleConfiguredTarget(T target); |
| 357 | |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 358 | /** |
| 359 | * Returns targetified dependencies wrapped as {@link ClassifiedDependency} objects which include |
| 360 | * information on if the target is an implicit or explicit dependency. |
| 361 | * |
| 362 | * @param parent Parent target that knows about its attribute-attached implicit deps. If this is |
| 363 | * null, that is a signal from the caller that all dependencies should be considered implicit. |
| 364 | * @param dependencies dependencies to targetify |
| 365 | */ |
| 366 | private ImmutableList<ClassifiedDependency<T>> targetifyValues( |
| 367 | @Nullable T parent, Iterable<SkyKey> dependencies) throws InterruptedException { |
| 368 | Collection<ConfiguredTargetKey> implicitDeps = null; |
| 369 | if (parent != null) { |
| 370 | RuleConfiguredTarget ruleConfiguredTarget = getRuleConfiguredTarget(parent); |
| 371 | if (ruleConfiguredTarget != null) { |
| 372 | implicitDeps = ruleConfiguredTarget.getImplicitDeps(); |
John Cater | e39e5c9 | 2019-04-23 10:28:02 -0700 | [diff] [blame] | 373 | } |
| 374 | } |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 375 | |
| 376 | ImmutableList.Builder<ClassifiedDependency<T>> values = ImmutableList.builder(); |
| 377 | for (SkyKey key : dependencies) { |
| 378 | if (key.functionName().equals(SkyFunctions.CONFIGURED_TARGET)) { |
| 379 | T dependency = getValueFromKey(key); |
juliexxia | c880d35 | 2020-04-20 12:31:09 -0700 | [diff] [blame] | 380 | Preconditions.checkState( |
| 381 | dependency != null, |
| 382 | "query-requested node '%s' was unavailable in the query environment graph. If you" |
| 383 | + " come across this error, please ping b/150301500 or contact the blaze" |
| 384 | + " configurability team.", |
| 385 | key); |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 386 | boolean implicit = |
| 387 | implicitDeps == null |
| 388 | || implicitDeps.contains( |
jcater | 83221e3 | 2020-05-28 11:37:39 -0700 | [diff] [blame] | 389 | ConfiguredTargetKey.builder() |
| 390 | .setLabel(getCorrectLabel(dependency)) |
| 391 | .setConfiguration(getConfiguration(dependency)) |
| 392 | .build()); |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 393 | values.add(new ClassifiedDependency<>(dependency, implicit)); |
plf | 96622c4 | 2020-06-11 08:09:29 -0700 | [diff] [blame] | 394 | } else if (key.functionName().equals(SkyFunctions.TOOLCHAIN_RESOLUTION)) { |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 395 | // Also fetch these dependencies. |
| 396 | values.addAll(targetifyValues(null, graph.getDirectDeps(key))); |
| 397 | } |
| 398 | } |
| 399 | return values.build(); |
John Cater | e39e5c9 | 2019-04-23 10:28:02 -0700 | [diff] [blame] | 400 | } |
| 401 | |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 402 | private Map<SkyKey, ImmutableList<ClassifiedDependency<T>>> targetifyValues( |
| 403 | Map<SkyKey, T> fromTargetsByKey, Map<SkyKey, ? extends Iterable<SkyKey>> input) |
| 404 | throws InterruptedException { |
| 405 | Map<SkyKey, ImmutableList<ClassifiedDependency<T>>> result = new HashMap<>(); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 406 | for (Map.Entry<SkyKey, ? extends Iterable<SkyKey>> entry : input.entrySet()) { |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 407 | SkyKey fromKey = entry.getKey(); |
| 408 | result.put(fromKey, targetifyValues(fromTargetsByKey.get(fromKey), entry.getValue())); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 409 | } |
| 410 | return result; |
| 411 | } |
| 412 | |
juliexxia | 1eb769f | 2020-02-07 13:17:32 -0800 | [diff] [blame] | 413 | /** A class to store a dependency with some information. */ |
| 414 | private static class ClassifiedDependency<T> { |
| 415 | // True if this dependency is attached implicitly. |
| 416 | boolean implicit; |
| 417 | T dependency; |
| 418 | |
| 419 | private ClassifiedDependency(T dependency, boolean implicit) { |
| 420 | this.implicit = implicit; |
| 421 | this.dependency = dependency; |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | private static <T> ImmutableList<T> getDependencies( |
| 426 | Collection<ClassifiedDependency<T>> classifiedDependencies) { |
| 427 | return classifiedDependencies.stream() |
| 428 | .map(dep -> dep.dependency) |
| 429 | .collect(ImmutableList.toImmutableList()); |
| 430 | } |
| 431 | |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 432 | @Nullable |
| 433 | protected abstract BuildConfiguration getConfiguration(T target); |
| 434 | |
| 435 | protected abstract ConfiguredTargetKey getSkyKey(T target); |
| 436 | |
| 437 | @Override |
| 438 | public ThreadSafeMutableSet<T> getTransitiveClosure( |
| 439 | ThreadSafeMutableSet<T> targets, QueryExpressionContext<T> context) |
| 440 | throws InterruptedException { |
| 441 | return SkyQueryUtils.getTransitiveClosure( |
| 442 | targets, targets1 -> getFwdDeps(targets1, context), createThreadSafeMutableSet()); |
| 443 | } |
| 444 | |
| 445 | @Override |
| 446 | public void buildTransitiveClosure( |
| 447 | QueryExpression caller, ThreadSafeMutableSet<T> targetNodes, int maxDepth) { |
| 448 | // TODO(bazel-team): implement this. Just needed for error-checking. |
| 449 | } |
| 450 | |
| 451 | @Override |
| 452 | public ImmutableList<T> getNodesOnPath(T from, T to, QueryExpressionContext<T> context) |
| 453 | throws InterruptedException { |
| 454 | return SkyQueryUtils.getNodesOnPath( |
| 455 | from, |
| 456 | to, |
| 457 | targets -> getFwdDeps(targets, context), |
| 458 | getConfiguredTargetKeyExtractor()::extractKey); |
| 459 | } |
| 460 | |
| 461 | @Override |
| 462 | public <V> MutableMap<T, V> createMutableMap() { |
| 463 | return new MutableKeyExtractorBackedMapImpl<>(getConfiguredTargetKeyExtractor()); |
| 464 | } |
| 465 | |
| 466 | @Override |
| 467 | public Uniquifier<T> createUniquifier() { |
shreyax | 6871cf0 | 2018-07-02 09:16:18 -0700 | [diff] [blame] | 468 | return new UniquifierImpl<>(getConfiguredTargetKeyExtractor()); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 469 | } |
| 470 | |
| 471 | @Override |
| 472 | public MinDepthUniquifier<T> createMinDepthUniquifier() { |
| 473 | return new MinDepthUniquifierImpl<>( |
| 474 | getConfiguredTargetKeyExtractor(), SkyQueryEnvironment.DEFAULT_THREAD_COUNT); |
| 475 | } |
| 476 | |
| 477 | /** Target patterns are resolved on the fly so no pre-work to be done here. */ |
| 478 | @Override |
| 479 | protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns) {} |
| 480 | |
| 481 | @Override |
| 482 | public ThreadSafeMutableSet<T> getBuildFiles( |
| 483 | QueryExpression caller, |
| 484 | ThreadSafeMutableSet<T> nodes, |
| 485 | boolean buildFiles, |
| 486 | boolean loads, |
| 487 | QueryExpressionContext<T> context) |
| 488 | throws QueryException { |
| 489 | throw new QueryException("buildfiles() doesn't make sense for the configured target graph"); |
| 490 | } |
| 491 | |
| 492 | @Override |
juliexxia | cc2c6bc | 2020-04-20 11:12:29 -0700 | [diff] [blame] | 493 | public Collection<T> getSiblingTargetsInPackage(T target) throws QueryException { |
| 494 | throw new QueryException("siblings() not supported for post analysis queries"); |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 495 | } |
| 496 | |
| 497 | @Override |
| 498 | public void close() {} |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 499 | |
| 500 | /** A wrapper class for the set of top-level configurations in a query. */ |
| 501 | public static class TopLevelConfigurations { |
| 502 | |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 503 | /** A map of non-null configured top-level targets sorted by configuration checksum. */ |
| 504 | private final ImmutableMap<Label, BuildConfiguration> nonNulls; |
| 505 | /** |
| 506 | * {@code nonNulls} may often have many duplicate values in its value set so we store a sorted |
| 507 | * set of all the non-null configurations here. |
| 508 | */ |
| 509 | private final ImmutableSortedSet<BuildConfiguration> nonNullConfigs; |
| 510 | /** A list of null configured top-level targets. */ |
| 511 | private final ImmutableList<Label> nulls; |
| 512 | |
| 513 | public TopLevelConfigurations( |
| 514 | Collection<TargetAndConfiguration> topLevelTargetsAndConfigurations) { |
| 515 | ImmutableMap.Builder<Label, BuildConfiguration> nonNullsBuilder = |
| 516 | ImmutableMap.builderWithExpectedSize(topLevelTargetsAndConfigurations.size()); |
| 517 | ImmutableList.Builder<Label> nullsBuilder = new ImmutableList.Builder<>(); |
| 518 | for (TargetAndConfiguration targetAndConfiguration : topLevelTargetsAndConfigurations) { |
| 519 | if (targetAndConfiguration.getConfiguration() == null) { |
| 520 | nullsBuilder.add(targetAndConfiguration.getLabel()); |
| 521 | } else { |
| 522 | nonNullsBuilder.put( |
| 523 | targetAndConfiguration.getLabel(), targetAndConfiguration.getConfiguration()); |
| 524 | } |
| 525 | } |
| 526 | nonNulls = nonNullsBuilder.build(); |
| 527 | nonNullConfigs = |
| 528 | ImmutableSortedSet.copyOf( |
| 529 | Comparator.comparing(BuildConfiguration::checksum), nonNulls.values()); |
| 530 | nulls = nullsBuilder.build(); |
| 531 | } |
| 532 | |
gregce | a91495f | 2019-05-07 07:27:39 -0700 | [diff] [blame] | 533 | public boolean isTopLevelTarget(Label label) { |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 534 | return nonNulls.containsKey(label) || nulls.contains(label); |
| 535 | } |
| 536 | |
| 537 | // This method returns the configuration of a top-level target if it's not null-configured and |
| 538 | // otherwise returns null (signifying it is null configured). |
| 539 | @Nullable |
gregce | a91495f | 2019-05-07 07:27:39 -0700 | [diff] [blame] | 540 | public BuildConfiguration getConfigurationForTopLevelTarget(Label label) { |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 541 | Preconditions.checkArgument( |
| 542 | isTopLevelTarget(label), |
| 543 | "Attempting to get top-level configuration for non-top-level target %s.", |
| 544 | label); |
| 545 | return nonNulls.get(label); |
| 546 | } |
| 547 | |
| 548 | public Iterable<BuildConfiguration> getConfigurations() { |
| 549 | if (nulls.isEmpty()) { |
| 550 | return nonNullConfigs; |
| 551 | } else { |
Googler | 2edf599 | 2018-12-17 12:56:17 -0800 | [diff] [blame] | 552 | return Iterables.concat(nonNullConfigs, Collections.singletonList(null)); |
juliexxia | 5281eee | 2018-10-03 12:24:01 -0700 | [diff] [blame] | 553 | } |
| 554 | } |
| 555 | } |
twerth | fc6c742 | 2018-06-28 10:18:39 -0700 | [diff] [blame] | 556 | } |