janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 1 | // 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. |
| 14 | package com.google.devtools.build.lib.query2; |
| 15 | |
| 16 | import com.google.common.collect.ImmutableList; |
| 17 | import com.google.common.collect.ImmutableSet; |
| 18 | import com.google.common.collect.Iterables; |
| 19 | import com.google.common.collect.Sets; |
| 20 | import com.google.common.util.concurrent.AsyncFunction; |
| 21 | import com.google.common.util.concurrent.Futures; |
| 22 | import com.google.common.util.concurrent.MoreExecutors; |
| 23 | import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 24 | import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 25 | import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 26 | import com.google.devtools.build.lib.cmdline.Label; |
| 27 | import com.google.devtools.build.lib.cmdline.TargetParsingException; |
| 28 | import com.google.devtools.build.lib.cmdline.TargetPattern; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 29 | import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 30 | import com.google.devtools.build.lib.concurrent.MultisetSemaphore; |
| 31 | import com.google.devtools.build.lib.events.Event; |
| 32 | import com.google.devtools.build.lib.events.ExtendedEventHandler; |
juliexxia | 08dda86 | 2018-04-05 09:54:22 -0700 | [diff] [blame] | 33 | import com.google.devtools.build.lib.events.Reporter; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 34 | import com.google.devtools.build.lib.packages.DependencyFilter; |
mjhalupka | 00d781a | 2018-01-31 14:42:22 -0800 | [diff] [blame] | 35 | import com.google.devtools.build.lib.packages.NoSuchTargetException; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 36 | import com.google.devtools.build.lib.packages.Rule; |
mstaib | 4a07a47 | 2018-04-19 14:16:41 -0700 | [diff] [blame] | 37 | import com.google.devtools.build.lib.packages.RuleTransitionFactory; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [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.PathPackageLocator; |
| 41 | import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; |
| 42 | import com.google.devtools.build.lib.query2.engine.Callback; |
| 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.QueryUtil.MinDepthUniquifierImpl; |
| 50 | import com.google.devtools.build.lib.query2.engine.QueryUtil.MutableKeyExtractorBackedMapImpl; |
| 51 | import com.google.devtools.build.lib.query2.engine.QueryUtil.ThreadSafeMutableKeyExtractorBackedSetImpl; |
| 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; |
juliexxia | 5f135e8 | 2018-04-11 10:34:00 -0700 | [diff] [blame] | 55 | import com.google.devtools.build.lib.query2.output.AspectResolver; |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 56 | import com.google.devtools.build.lib.query2.output.CqueryOptions; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 57 | import com.google.devtools.build.lib.query2.output.QueryOptions; |
juliexxia | 8d4f813 | 2018-02-16 07:55:21 -0800 | [diff] [blame] | 58 | import com.google.devtools.build.lib.rules.AliasConfiguredTarget; |
janakr | 171a7eb | 2018-03-26 09:26:53 -0700 | [diff] [blame] | 59 | import com.google.devtools.build.lib.skyframe.BuildConfigurationValue; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 60 | import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; |
| 61 | import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; |
| 62 | import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider; |
mjhalupka | 00d781a | 2018-01-31 14:42:22 -0800 | [diff] [blame] | 63 | import com.google.devtools.build.lib.skyframe.PackageValue; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 64 | import com.google.devtools.build.lib.skyframe.RecursivePackageProviderBackedTargetPatternResolver; |
juliexxia | cc86475 | 2018-06-01 09:03:48 -0700 | [diff] [blame^] | 65 | import com.google.devtools.build.lib.skyframe.RecursivePkgValueRootPackageExtractor; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 66 | import com.google.devtools.build.lib.skyframe.SkyFunctions; |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 67 | import com.google.devtools.build.lib.skyframe.SkyframeExecutor; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 68 | import com.google.devtools.build.lib.skyframe.TargetPatternValue; |
| 69 | import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey; |
| 70 | import com.google.devtools.build.skyframe.SkyKey; |
| 71 | import com.google.devtools.build.skyframe.WalkableGraph; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 72 | import com.google.devtools.common.options.OptionsParser; |
| 73 | import com.google.devtools.common.options.OptionsParsingException; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 74 | import java.io.IOException; |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 75 | import java.io.OutputStream; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 76 | import java.util.ArrayList; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 77 | import java.util.Arrays; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 78 | import java.util.Collection; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 79 | import java.util.Collections; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 80 | import java.util.HashMap; |
| 81 | import java.util.List; |
| 82 | import java.util.Map; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 83 | import java.util.Set; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 84 | import java.util.function.Function; |
| 85 | import java.util.function.Supplier; |
| 86 | import java.util.stream.Collectors; |
| 87 | import javax.annotation.Nullable; |
| 88 | |
| 89 | /** |
| 90 | * {@link QueryEnvironment} that runs queries over the configured target (analysis) graph. |
| 91 | * |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 92 | * <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 |
juliexxia | cc86475 | 2018-06-01 09:03:48 -0700 | [diff] [blame^] | 94 | * {@link ConfiguredTargetAccessor} field should be initialized on a per-query basis not a |
| 95 | * per-environment basis. |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 96 | * |
juliexxia | cc86475 | 2018-06-01 09:03:48 -0700 | [diff] [blame^] | 97 | * <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. |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 102 | * |
| 103 | * <p>Aspects are also not supported, but probably should be in some fashion. |
| 104 | */ |
| 105 | public 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; |
mjhalupka | 426ab90 | 2018-01-30 11:35:47 -0800 | [diff] [blame] | 112 | private ConfiguredTargetAccessor accessor; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 113 | protected WalkableGraph graph; |
| 114 | |
janakr | ac2cd35 | 2017-12-20 13:37:13 -0800 | [diff] [blame] | 115 | private static final Function<SkyKey, ConfiguredTargetKey> SKYKEY_TO_CTKEY = |
janakr | 906c7cb | 2017-12-28 12:04:02 -0800 | [diff] [blame] | 116 | skyKey -> (ConfiguredTargetKey) skyKey.argument(); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 117 | private static final ImmutableList<TargetPatternKey> ALL_PATTERNS; |
janakr | cc3890d | 2018-03-27 14:38:52 -0700 | [diff] [blame] | 118 | private final KeyExtractor<ConfiguredTarget, ConfiguredTargetKey> configuredTargetKeyExtractor; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 119 | |
juliexxia | 6f9416e | 2018-02-13 12:44:21 -0800 | [diff] [blame] | 120 | /** 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 | |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 125 | 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, |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 148 | Supplier<WalkableGraph> walkableGraphSupplier, |
| 149 | Set<Setting> settings) { |
| 150 | super(keepGoing, true, Rule.ALL_LABELS, eventHandler, settings, extraFunctions); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 151 | this.defaultTargetConfiguration = defaultTargetConfiguration; |
| 152 | this.hostConfiguration = hostConfiguration; |
| 153 | this.parserPrefix = parserPrefix; |
| 154 | this.pkgPath = pkgPath; |
| 155 | this.walkableGraphSupplier = walkableGraphSupplier; |
juliexxia | 64d4c65 | 2018-03-23 14:51:43 -0700 | [diff] [blame] | 156 | this.accessor = new ConfiguredTargetAccessor(walkableGraphSupplier.get()); |
janakr | cc3890d | 2018-03-27 14:38:52 -0700 | [diff] [blame] | 157 | 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 | }; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 170 | } |
| 171 | |
juliexxia | 6f9416e | 2018-02-13 12:44:21 -0800 | [diff] [blame] | 172 | 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 | |
juliexxia | 3a61eb6 | 2018-03-28 08:42:21 -0700 | [diff] [blame] | 183 | public ImmutableList<CqueryThreadsafeCallback> getDefaultOutputFormatters( |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 184 | TargetAccessor<ConfiguredTarget> accessor, |
| 185 | CqueryOptions options, |
juliexxia | 08dda86 | 2018-04-05 09:54:22 -0700 | [diff] [blame] | 186 | Reporter reporter, |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 187 | SkyframeExecutor skyframeExecutor, |
juliexxia | 5f135e8 | 2018-04-11 10:34:00 -0700 | [diff] [blame] | 188 | BuildConfiguration hostConfiguration, |
mstaib | 4a07a47 | 2018-04-19 14:16:41 -0700 | [diff] [blame] | 189 | @Nullable RuleTransitionFactory trimmingTransitionFactory, |
juliexxia | 5f135e8 | 2018-04-11 10:34:00 -0700 | [diff] [blame] | 190 | AspectResolver resolver) { |
juliexxia | 08dda86 | 2018-04-05 09:54:22 -0700 | [diff] [blame] | 191 | OutputStream out = reporter.getOutErr().getOutputStream(); |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 192 | return new ImmutableList.Builder<CqueryThreadsafeCallback>() |
juliexxia | 08dda86 | 2018-04-05 09:54:22 -0700 | [diff] [blame] | 193 | .add( |
| 194 | new LabelAndConfigurationOutputFormatterCallback( |
juliexxia | 5f135e8 | 2018-04-11 10:34:00 -0700 | [diff] [blame] | 195 | reporter, options, out, skyframeExecutor, accessor)) |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 196 | .add( |
| 197 | new TransitionsOutputFormatterCallback( |
mstaib | 4a07a47 | 2018-04-19 14:16:41 -0700 | [diff] [blame] | 198 | reporter, |
| 199 | options, |
| 200 | out, |
| 201 | skyframeExecutor, |
| 202 | accessor, |
| 203 | hostConfiguration, |
| 204 | trimmingTransitionFactory)) |
juliexxia | 5f135e8 | 2018-04-11 10:34:00 -0700 | [diff] [blame] | 205 | .add( |
| 206 | new ProtoOutputFormatterCallback( |
| 207 | reporter, options, out, skyframeExecutor, accessor, resolver)) |
juliexxia | d350a89 | 2018-03-27 13:46:10 -0700 | [diff] [blame] | 208 | .build(); |
| 209 | } |
| 210 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 211 | @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 = |
juliexxia | cc86475 | 2018-06-01 09:03:48 -0700 | [diff] [blame^] | 222 | new GraphBackedRecursivePackageProvider( |
| 223 | graph, ALL_PATTERNS, pkgPath, new RecursivePkgValueRootPackageExtractor()); |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 224 | resolver = |
| 225 | new RecursivePackageProviderBackedTargetPatternResolver( |
| 226 | graphBackedRecursivePackageProvider, |
| 227 | eventHandler, |
| 228 | FilteringPolicies.NO_FILTER, |
| 229 | MultisetSemaphore.unbounded()); |
| 230 | checkSettings(settings); |
| 231 | } |
| 232 | |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 233 | // Check to make sure the settings requested are currently supported by this class |
| 234 | private void checkSettings(Set<Setting> settings) throws QueryException { |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 235 | 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)); |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 240 | throw new QueryException( |
| 241 | String.format( |
| 242 | "The following filter(s) are not currently supported by configured query: %s", |
| 243 | settings.toString())); |
| 244 | } |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 245 | } |
| 246 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 247 | public BuildConfiguration getHostConfiguration() { |
| 248 | return hostConfiguration; |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 249 | } |
| 250 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 251 | @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); |
Googler | 838f339 | 2018-01-22 14:43:51 -0800 | [diff] [blame] | 267 | } |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 268 | } |
| 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(); |
Googler | 838f339 | 2018-01-22 14:43:51 -0800 | [diff] [blame] | 282 | } |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 283 | return target.getLabel(); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 284 | } |
| 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 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 330 | 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 | |
juliexxia | 6f9416e | 2018-02-13 12:44:21 -0800 | [diff] [blame] | 364 | /** |
| 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) { |
juliexxia | 2267e2a | 2018-03-15 09:29:10 -0700 | [diff] [blame] | 385 | Label label = getCorrectLabel(target); |
juliexxia | 6f9416e | 2018-02-13 12:44:21 -0800 | [diff] [blame] | 386 | 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 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 419 | @Nullable |
| 420 | private ConfiguredTarget getHostConfiguredTarget(Label label) throws InterruptedException { |
| 421 | return getConfiguredTarget(ConfiguredTargetValue.key(label, hostConfiguration)); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 422 | } |
| 423 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 424 | @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(); |
jcater | 94b8702 | 2018-05-02 09:08:52 -0700 | [diff] [blame] | 452 | for (Map.Entry<SkyKey, Collection<ConfiguredTarget>> entry : directDeps.entrySet()) { |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 453 | result.addAll(filterFwdDeps(targetsByKey.get(entry.getKey()), entry.getValue())); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 454 | } |
| 455 | return result; |
| 456 | } |
| 457 | |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 458 | private Collection<ConfiguredTarget> filterFwdDeps( |
| 459 | ConfiguredTarget configTarget, Collection<ConfiguredTarget> rawFwdDeps) |
| 460 | throws InterruptedException { |
juliexxia | 8d4f813 | 2018-02-16 07:55:21 -0800 | [diff] [blame] | 461 | if (settings.isEmpty()) { |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 462 | return rawFwdDeps; |
| 463 | } |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 464 | return getAllowedDeps(configTarget, rawFwdDeps); |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 465 | } |
| 466 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 467 | @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 | |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 510 | /** |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 511 | * @param target source target |
| 512 | * @param deps next level of deps to filter |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 513 | */ |
| 514 | private Collection<ConfiguredTarget> getAllowedDeps( |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 515 | 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)) { |
janakr | 171a7eb | 2018-03-26 09:26:53 -0700 | [diff] [blame] | 521 | BuildConfiguration currentConfig = getConfiguration(target); |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 522 | if (currentConfig != null && currentConfig.isHostConfiguration()) { |
| 523 | deps = |
| 524 | deps.stream() |
| 525 | .filter( |
| 526 | dep -> |
janakr | 171a7eb | 2018-03-26 09:26:53 -0700 | [diff] [blame] | 527 | getConfiguration(dep) != null |
| 528 | && getConfiguration(dep).isHostConfiguration()) |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 529 | .collect(Collectors.toList()); |
| 530 | } else { |
| 531 | deps = |
| 532 | deps.stream() |
| 533 | .filter( |
| 534 | dep -> |
janakr | 171a7eb | 2018-03-26 09:26:53 -0700 | [diff] [blame] | 535 | getConfiguration(dep) != null |
| 536 | && !getConfiguration(dep).isHostConfiguration()) |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 537 | .collect(Collectors.toList()); |
| 538 | } |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 539 | } |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 540 | if (settings.contains(Setting.NO_IMPLICIT_DEPS) && target instanceof RuleConfiguredTarget) { |
janakr | ac2cd35 | 2017-12-20 13:37:13 -0800 | [diff] [blame] | 541 | Set<ConfiguredTargetKey> implicitDeps = ((RuleConfiguredTarget) target).getImplicitDeps(); |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 542 | deps = |
| 543 | deps.stream() |
| 544 | .filter( |
| 545 | dep -> |
| 546 | !implicitDeps.contains( |
janakr | 171a7eb | 2018-03-26 09:26:53 -0700 | [diff] [blame] | 547 | ConfiguredTargetKey.of(getCorrectLabel(dep), getConfiguration(dep)))) |
juliexxia | 865b888 | 2017-12-08 12:37:36 -0800 | [diff] [blame] | 548 | .collect(Collectors.toList()); |
| 549 | } |
| 550 | return deps; |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 551 | } |
| 552 | |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 553 | 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 | |
janakr | 171a7eb | 2018-03-26 09:26:53 -0700 | [diff] [blame] | 568 | @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 | |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 584 | |
| 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 { |
janakr | cc3890d | 2018-03-27 14:38:52 -0700 | [diff] [blame] | 602 | return SkyQueryUtils.getNodesOnPath( |
| 603 | from, to, this::getFwdDeps, configuredTargetKeyExtractor::extractKey); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 604 | } |
| 605 | |
| 606 | @Override |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 607 | public ThreadSafeMutableSet<ConfiguredTarget> createThreadSafeMutableSet() { |
| 608 | return new ThreadSafeMutableKeyExtractorBackedSetImpl<>( |
janakr | cc3890d | 2018-03-27 14:38:52 -0700 | [diff] [blame] | 609 | configuredTargetKeyExtractor, |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 610 | ConfiguredTarget.class, |
| 611 | SkyQueryEnvironment.DEFAULT_THREAD_COUNT); |
| 612 | } |
| 613 | |
| 614 | @Override |
| 615 | public <V> MutableMap<ConfiguredTarget, V> createMutableMap() { |
janakr | cc3890d | 2018-03-27 14:38:52 -0700 | [diff] [blame] | 616 | return new MutableKeyExtractorBackedMapImpl<>(configuredTargetKeyExtractor); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 617 | } |
| 618 | |
| 619 | @Override |
| 620 | public Uniquifier<ConfiguredTarget> createUniquifier() { |
| 621 | return new UniquifierImpl<>( |
janakr | cc3890d | 2018-03-27 14:38:52 -0700 | [diff] [blame] | 622 | configuredTargetKeyExtractor, SkyQueryEnvironment.DEFAULT_THREAD_COUNT); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 623 | } |
| 624 | |
| 625 | @Override |
| 626 | public MinDepthUniquifier<ConfiguredTarget> createMinDepthUniquifier() { |
| 627 | return new MinDepthUniquifierImpl<>( |
janakr | cc3890d | 2018-03-27 14:38:52 -0700 | [diff] [blame] | 628 | configuredTargetKeyExtractor, SkyQueryEnvironment.DEFAULT_THREAD_COUNT); |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 629 | } |
| 630 | |
juliexxia | cc86475 | 2018-06-01 09:03:48 -0700 | [diff] [blame^] | 631 | /** Target patterns are resolved on the fly so no pre-work to be done here. */ |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 632 | @Override |
juliexxia | cc86475 | 2018-06-01 09:03:48 -0700 | [diff] [blame^] | 633 | protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns) {} |
juliexxia | 2af2bff | 2017-11-17 15:05:14 -0800 | [diff] [blame] | 634 | |
| 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 | } |
juliexxia | 45c387f | 2018-04-19 12:51:18 -0700 | [diff] [blame] | 646 | |
| 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() {} |
janakr | dc8b2e9a | 2017-08-18 22:52:37 +0200 | [diff] [blame] | 665 | } |
juliexxia | 5f135e8 | 2018-04-11 10:34:00 -0700 | [diff] [blame] | 666 | |