blob: 4ba21224a22645b2eb193fb99a19f28848501149 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Mark Schallerb889cf32015-03-17 20:55:30 +00002//
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.skyframe;
15
Googler3d37b4c2017-04-25 04:19:01 +020016import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
Eric Fellheimer8c40daa2016-01-11 17:31:26 +000017import static com.google.devtools.build.lib.pkgcache.FilteringPolicies.NO_FILTER;
18
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +000019import com.google.common.base.Function;
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +000020import com.google.common.base.Throwables;
Mark Schallerd9d390a2016-06-21 22:01:28 +000021import com.google.common.collect.ImmutableList;
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +000022import com.google.common.collect.ImmutableMap;
Mark Schallerf6e32d62015-05-19 20:27:43 +000023import com.google.common.collect.ImmutableSet;
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +000024import com.google.common.collect.Iterables;
Eric Fellheimerfb601432016-03-07 21:14:44 +000025import com.google.common.collect.Sets;
Nathan Harmata7a5a2362017-03-08 22:42:01 +000026import com.google.common.util.concurrent.Futures;
27import com.google.common.util.concurrent.ListenableFuture;
28import com.google.common.util.concurrent.ListeningExecutorService;
Nathan Harmata5bd26b22016-11-14 19:55:50 +000029import com.google.common.util.concurrent.MoreExecutors;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000030import com.google.devtools.build.lib.cmdline.Label;
Kristina Chodorow73fa2032015-08-28 17:57:46 +000031import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Kristina Chodorowec5c07a2016-01-25 17:12:29 +000032import com.google.devtools.build.lib.cmdline.RepositoryName;
Mark Schallerb889cf32015-03-17 20:55:30 +000033import com.google.devtools.build.lib.cmdline.ResolvedTargets;
34import com.google.devtools.build.lib.cmdline.TargetParsingException;
35import com.google.devtools.build.lib.cmdline.TargetPatternResolver;
Nathan Harmata41b54172016-11-10 18:54:09 +000036import com.google.devtools.build.lib.concurrent.MultisetSemaphore;
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +000037import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
Mark Schallerb889cf32015-03-17 20:55:30 +000038import com.google.devtools.build.lib.events.Event;
Klaus Aehlig777b30d2017-02-24 16:30:15 +000039import com.google.devtools.build.lib.events.ExtendedEventHandler;
Mark Schallerb889cf32015-03-17 20:55:30 +000040import com.google.devtools.build.lib.packages.NoSuchPackageException;
41import com.google.devtools.build.lib.packages.NoSuchThingException;
42import com.google.devtools.build.lib.packages.Package;
Mark Schallerb889cf32015-03-17 20:55:30 +000043import com.google.devtools.build.lib.packages.Target;
44import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
45import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
Mark Schallerb889cf32015-03-17 20:55:30 +000046import com.google.devtools.build.lib.pkgcache.RecursivePackageProvider;
47import com.google.devtools.build.lib.pkgcache.TargetPatternResolverUtil;
Janak Ramakrishnan5b5f22a2016-01-07 17:07:29 +000048import com.google.devtools.build.lib.util.BatchCallback;
Googler77ba65e2017-06-26 19:55:09 +020049import com.google.devtools.build.lib.util.Preconditions;
Nathan Harmata5bd26b22016-11-14 19:55:50 +000050import com.google.devtools.build.lib.util.ThreadSafeBatchCallback;
Mark Schallerb889cf32015-03-17 20:55:30 +000051import com.google.devtools.build.lib.vfs.PathFragment;
Janak Ramakrishnan5b5f22a2016-01-07 17:07:29 +000052import java.util.ArrayList;
53import java.util.List;
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +000054import java.util.Map;
Nathan Harmata2e2b4592016-09-21 17:17:33 +000055import java.util.concurrent.Callable;
Mark Schallerd9d390a2016-06-21 22:01:28 +000056import java.util.concurrent.ExecutionException;
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +000057import java.util.concurrent.atomic.AtomicBoolean;
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +000058
Mark Schallerb889cf32015-03-17 20:55:30 +000059/**
60 * A {@link TargetPatternResolver} backed by a {@link RecursivePackageProvider}.
61 */
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +000062@ThreadCompatible
Mark Schallerb889cf32015-03-17 20:55:30 +000063public class RecursivePackageProviderBackedTargetPatternResolver
Nathan Harmata7a5a2362017-03-08 22:42:01 +000064 extends TargetPatternResolver<Target> {
Mark Schallerb889cf32015-03-17 20:55:30 +000065
Janak Ramakrishnan5b5f22a2016-01-07 17:07:29 +000066 // TODO(janakr): Move this to a more generic place and unify with SkyQueryEnvironment's value?
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +000067 private static final int MAX_PACKAGES_BULK_GET = 1000;
Janak Ramakrishnan5b5f22a2016-01-07 17:07:29 +000068
Googler77ba65e2017-06-26 19:55:09 +020069 protected final FilteringPolicy policy;
Mark Schallerb889cf32015-03-17 20:55:30 +000070 private final RecursivePackageProvider recursivePackageProvider;
Klaus Aehlig777b30d2017-02-24 16:30:15 +000071 private final ExtendedEventHandler eventHandler;
Nathan Harmata41b54172016-11-10 18:54:09 +000072 private final MultisetSemaphore<PackageIdentifier> packageSemaphore;
Mark Schallerb889cf32015-03-17 20:55:30 +000073
74 public RecursivePackageProviderBackedTargetPatternResolver(
Mark Schaller8ca91312015-06-22 22:04:02 +000075 RecursivePackageProvider recursivePackageProvider,
Klaus Aehlig777b30d2017-02-24 16:30:15 +000076 ExtendedEventHandler eventHandler,
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +000077 FilteringPolicy policy,
Nathan Harmata41b54172016-11-10 18:54:09 +000078 MultisetSemaphore<PackageIdentifier> packageSemaphore) {
Mark Schallerb889cf32015-03-17 20:55:30 +000079 this.recursivePackageProvider = recursivePackageProvider;
80 this.eventHandler = eventHandler;
81 this.policy = policy;
Nathan Harmata41b54172016-11-10 18:54:09 +000082 this.packageSemaphore = packageSemaphore;
Mark Schallerb889cf32015-03-17 20:55:30 +000083 }
84
85 @Override
86 public void warn(String msg) {
87 eventHandler.handle(Event.warn(msg));
88 }
89
90 /**
91 * Gets a {@link Package} from the {@link RecursivePackageProvider}. May return a {@link Package}
92 * that has errors.
93 */
94 private Package getPackage(PackageIdentifier pkgIdentifier)
95 throws NoSuchPackageException, InterruptedException {
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +000096 return recursivePackageProvider.getPackage(eventHandler, pkgIdentifier);
Mark Schallerb889cf32015-03-17 20:55:30 +000097 }
98
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +000099 private Map<PackageIdentifier, Package> bulkGetPackages(Iterable<PackageIdentifier> pkgIds)
100 throws NoSuchPackageException, InterruptedException {
Mark Schallerfb2d38b2017-03-10 23:01:45 +0000101 return recursivePackageProvider.bulkGetPackages(pkgIds);
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +0000102 }
103
Googler77ba65e2017-06-26 19:55:09 +0200104 /** Optionally convert a {@link Target} before including it in returns. */
105 protected Target maybeConvertTargetBeforeReturn(Target target) {
106 return target;
107 }
108
Mark Schallerb889cf32015-03-17 20:55:30 +0000109 @Override
Lukacs Berki960dc272015-09-24 07:48:36 +0000110 public Target getTargetOrNull(Label label) throws InterruptedException {
Mark Schallerb889cf32015-03-17 20:55:30 +0000111 try {
Lukacs Berki10e3b2b2015-09-22 07:58:20 +0000112 if (!isPackage(label.getPackageIdentifier())) {
Mark Schallerb889cf32015-03-17 20:55:30 +0000113 return null;
114 }
Googler77ba65e2017-06-26 19:55:09 +0200115 return maybeConvertTargetBeforeReturn(
116 recursivePackageProvider.getTarget(eventHandler, label));
Lukacs Berki960dc272015-09-24 07:48:36 +0000117 } catch (NoSuchThingException e) {
Mark Schallerb889cf32015-03-17 20:55:30 +0000118 return null;
119 }
120 }
121
122 @Override
Lukacs Berki960dc272015-09-24 07:48:36 +0000123 public ResolvedTargets<Target> getExplicitTarget(Label label)
Mark Schallerb889cf32015-03-17 20:55:30 +0000124 throws TargetParsingException, InterruptedException {
Mark Schallerb889cf32015-03-17 20:55:30 +0000125 try {
126 Target target = recursivePackageProvider.getTarget(eventHandler, label);
127 return policy.shouldRetain(target, true)
Googler77ba65e2017-06-26 19:55:09 +0200128 ? ResolvedTargets.of(maybeConvertTargetBeforeReturn(target))
Mark Schallerb889cf32015-03-17 20:55:30 +0000129 : ResolvedTargets.<Target>empty();
130 } catch (NoSuchThingException e) {
131 throw new TargetParsingException(e.getMessage(), e);
132 }
133 }
134
135 @Override
Lukacs Berki10e3b2b2015-09-22 07:58:20 +0000136 public ResolvedTargets<Target> getTargetsInPackage(
137 String originalPattern, PackageIdentifier packageIdentifier, boolean rulesOnly)
Mark Schallerb889cf32015-03-17 20:55:30 +0000138 throws TargetParsingException, InterruptedException {
139 FilteringPolicy actualPolicy = rulesOnly
140 ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
141 : policy;
Mark Schallerb889cf32015-03-17 20:55:30 +0000142 try {
Lukacs Berki10e3b2b2015-09-22 07:58:20 +0000143 Package pkg = getPackage(packageIdentifier);
Googler77ba65e2017-06-26 19:55:09 +0200144 return TargetPatternResolverUtil.resolvePackageTargets(
145 pkg,
146 actualPolicy,
147 new Function<Target, Target>() {
148 @Override
149 public Target apply(Target target) {
150 return maybeConvertTargetBeforeReturn(Preconditions.checkNotNull(target));
151 }
152 });
Mark Schallerb889cf32015-03-17 20:55:30 +0000153 } catch (NoSuchThingException e) {
154 String message = TargetPatternResolverUtil.getParsingErrorMessage(
Lukacs Berki10e3b2b2015-09-22 07:58:20 +0000155 e.getMessage(), originalPattern);
Mark Schallerb889cf32015-03-17 20:55:30 +0000156 throw new TargetParsingException(message, e);
157 }
158 }
159
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +0000160 private Map<PackageIdentifier, ResolvedTargets<Target>> bulkGetTargetsInPackage(
161 String originalPattern,
162 Iterable<PackageIdentifier> pkgIds, FilteringPolicy policy)
Nathan Harmata2e2b4592016-09-21 17:17:33 +0000163 throws InterruptedException {
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +0000164 try {
165 Map<PackageIdentifier, Package> pkgs = bulkGetPackages(pkgIds);
Eric Fellheimerfb601432016-03-07 21:14:44 +0000166 if (pkgs.size() != Iterables.size(pkgIds)) {
167 throw new IllegalStateException("Bulk package retrieval missing results: "
168 + Sets.difference(ImmutableSet.copyOf(pkgIds), pkgs.keySet()));
169 }
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +0000170 ImmutableMap.Builder<PackageIdentifier, ResolvedTargets<Target>> result =
171 ImmutableMap.builder();
172 for (PackageIdentifier pkgId : pkgIds) {
173 Package pkg = pkgs.get(pkgId);
174 result.put(pkgId, TargetPatternResolverUtil.resolvePackageTargets(pkg, policy));
175 }
176 return result.build();
177 } catch (NoSuchThingException e) {
178 String message = TargetPatternResolverUtil.getParsingErrorMessage(
179 e.getMessage(), originalPattern);
Eric Fellheimerfb601432016-03-07 21:14:44 +0000180 throw new IllegalStateException(
181 "Mismatch: Expected given pkgIds to correspond to valid Packages. " + message, e);
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +0000182 }
183 }
184
Mark Schallerb889cf32015-03-17 20:55:30 +0000185 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000186 public boolean isPackage(PackageIdentifier packageIdentifier) throws InterruptedException {
Lukacs Berki10e3b2b2015-09-22 07:58:20 +0000187 return recursivePackageProvider.isPackage(eventHandler, packageIdentifier);
Mark Schallerb889cf32015-03-17 20:55:30 +0000188 }
189
190 @Override
191 public String getTargetKind(Target target) {
192 return target.getTargetKind();
193 }
194
Nathan Harmataad5d9c32017-03-11 00:03:18 +0000195 /**
196 * A {@link ThreadSafeBatchCallback} that trivially delegates to a {@link BatchCallback} in a
197 * synchronized manner.
198 */
199 private static class SynchronizedBatchCallback<T, E extends Exception>
200 implements ThreadSafeBatchCallback<T, E> {
201 private final BatchCallback<T, E> delegate;
202
203 public SynchronizedBatchCallback(BatchCallback<T, E> delegate) {
204 this.delegate = delegate;
205 }
206
207 @Override
208 public synchronized void process(Iterable<T> partialResult) throws E, InterruptedException {
209 delegate.process(partialResult);
210 }
211 }
212
Mark Schallerb889cf32015-03-17 20:55:30 +0000213 @Override
Janak Ramakrishnan5b5f22a2016-01-07 17:07:29 +0000214 public <E extends Exception> void findTargetsBeneathDirectory(
215 final RepositoryName repository,
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +0000216 final String originalPattern,
Janak Ramakrishnan5b5f22a2016-01-07 17:07:29 +0000217 String directory,
218 boolean rulesOnly,
nharmatade0c5352017-07-25 17:39:09 +0200219 ImmutableSet<PathFragment> blacklistedSubdirectories,
Janak Ramakrishnan3d9441b2016-01-13 17:38:29 +0000220 ImmutableSet<PathFragment> excludedSubdirectories,
Nathan Harmata5bd26b22016-11-14 19:55:50 +0000221 BatchCallback<Target, E> callback,
222 Class<E> exceptionClass)
223 throws TargetParsingException, E, InterruptedException {
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000224 try {
225 findTargetsBeneathDirectoryAsyncImpl(
226 repository,
227 originalPattern,
228 directory,
229 rulesOnly,
nharmatade0c5352017-07-25 17:39:09 +0200230 blacklistedSubdirectories,
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000231 excludedSubdirectories,
232 new SynchronizedBatchCallback<Target, E>(callback),
233 MoreExecutors.newDirectExecutorService()).get();
234 } catch (ExecutionException e) {
235 Throwable cause = e.getCause();
236 Throwables.propagateIfPossible(cause, TargetParsingException.class, exceptionClass);
237 throw new IllegalStateException(e.getCause());
238 }
239 }
240
241 @Override
242 public <E extends Exception> ListenableFuture<Void> findTargetsBeneathDirectoryAsync(
243 RepositoryName repository,
244 String originalPattern,
245 String directory,
246 boolean rulesOnly,
nharmatade0c5352017-07-25 17:39:09 +0200247 ImmutableSet<PathFragment> blacklistedSubdirectories,
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000248 ImmutableSet<PathFragment> excludedSubdirectories,
249 ThreadSafeBatchCallback<Target, E> callback,
250 Class<E> exceptionClass,
251 ListeningExecutorService executor) {
252 return findTargetsBeneathDirectoryAsyncImpl(
Nathan Harmata5bd26b22016-11-14 19:55:50 +0000253 repository,
254 originalPattern,
255 directory,
256 rulesOnly,
nharmatade0c5352017-07-25 17:39:09 +0200257 blacklistedSubdirectories,
Nathan Harmata5bd26b22016-11-14 19:55:50 +0000258 excludedSubdirectories,
Nathan Harmataad5d9c32017-03-11 00:03:18 +0000259 callback,
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000260 executor);
Nathan Harmata5bd26b22016-11-14 19:55:50 +0000261 }
262
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000263 private <E extends Exception> ListenableFuture<Void> findTargetsBeneathDirectoryAsyncImpl(
Nathan Harmata5bd26b22016-11-14 19:55:50 +0000264 final RepositoryName repository,
265 final String originalPattern,
266 String directory,
267 boolean rulesOnly,
nharmatade0c5352017-07-25 17:39:09 +0200268 ImmutableSet<PathFragment> blacklistedSubdirectories,
Nathan Harmata5bd26b22016-11-14 19:55:50 +0000269 ImmutableSet<PathFragment> excludedSubdirectories,
270 final ThreadSafeBatchCallback<Target, E> callback,
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000271 ListeningExecutorService executor) {
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +0000272 final FilteringPolicy actualPolicy = rulesOnly
Mark Schallerb889cf32015-03-17 20:55:30 +0000273 ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
274 : policy;
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000275 final PathFragment pathFragment;
276 Iterable<PathFragment> packagesUnderDirectory;
277 try {
278 pathFragment = TargetPatternResolverUtil.getPathFragment(directory);
Mark Schallerfb2d38b2017-03-10 23:01:45 +0000279 packagesUnderDirectory =
280 recursivePackageProvider.getPackagesUnderDirectory(
nharmatade0c5352017-07-25 17:39:09 +0200281 eventHandler,
282 repository,
283 pathFragment,
284 blacklistedSubdirectories,
285 excludedSubdirectories);
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000286 } catch (TargetParsingException e) {
287 return Futures.immediateFailedFuture(e);
288 } catch (InterruptedException e) {
289 return Futures.immediateCancelledFuture();
290 }
Eric Fellheimer7b8dbeb2015-12-30 15:21:43 +0000291
292 Iterable<PackageIdentifier> pkgIds = Iterables.transform(packagesUnderDirectory,
293 new Function<PathFragment, PackageIdentifier>() {
294 @Override
295 public PackageIdentifier apply(PathFragment path) {
296 return PackageIdentifier.create(repository, path);
297 }
298 });
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +0000299 final AtomicBoolean foundTarget = new AtomicBoolean(false);
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +0000300
Janak Ramakrishnan5b5f22a2016-01-07 17:07:29 +0000301 // For very large sets of packages, we may not want to process all of them at once, so we split
302 // into batches.
Mark Schallerd9d390a2016-06-21 22:01:28 +0000303 List<List<PackageIdentifier>> partitions =
304 ImmutableList.copyOf(Iterables.partition(pkgIds, MAX_PACKAGES_BULK_GET));
Nathan Harmata7a5a2362017-03-08 22:42:01 +0000305 ArrayList<ListenableFuture<Void>> futures = new ArrayList<>(partitions.size());
Mark Schallerd9d390a2016-06-21 22:01:28 +0000306 for (final Iterable<PackageIdentifier> pkgIdBatch : partitions) {
Googler77ba65e2017-06-26 19:55:09 +0200307 futures.add(
308 executor.submit(
309 new Callable<Void>() {
310 @Override
311 public Void call() throws E, TargetParsingException, InterruptedException {
312 ImmutableSet<PackageIdentifier> pkgIdBatchSet = ImmutableSet.copyOf(pkgIdBatch);
313 packageSemaphore.acquireAll(pkgIdBatchSet);
314 try {
315 Iterable<ResolvedTargets<Target>> resolvedTargets =
316 bulkGetTargetsInPackage(originalPattern, pkgIdBatch, NO_FILTER).values();
317 List<Target> filteredTargets = new ArrayList<>(calculateSize(resolvedTargets));
318 for (ResolvedTargets<Target> targets : resolvedTargets) {
319 for (Target target : targets.getTargets()) {
320 // Perform the no-targets-found check before applying the filtering policy
321 // so we only return the error if the input directory's subtree really
322 // contains no targets.
323 foundTarget.set(true);
324 if (actualPolicy.shouldRetain(target, false)) {
325 filteredTargets.add(maybeConvertTargetBeforeReturn(target));
326 }
327 }
328 }
329 callback.process(filteredTargets);
330 } finally {
331 packageSemaphore.releaseAll(pkgIdBatchSet);
Nathan Harmata41b54172016-11-10 18:54:09 +0000332 }
Googler77ba65e2017-06-26 19:55:09 +0200333 return null;
Eric Fellheimer6f8b7ce2016-01-29 00:15:44 +0000334 }
Googler77ba65e2017-06-26 19:55:09 +0200335 }));
Mark Schallerd9d390a2016-06-21 22:01:28 +0000336 }
Googler3d37b4c2017-04-25 04:19:01 +0200337 return Futures.whenAllSucceed(futures)
338 .call(
339 new Callable<Void>() {
340 @Override
341 public Void call() throws TargetParsingException {
342 if (!foundTarget.get()) {
343 throw new TargetParsingException(
344 "no targets found beneath '" + pathFragment + "'");
345 }
346 return null;
347 }
348 },
349 directExecutor());
Mark Schallerb889cf32015-03-17 20:55:30 +0000350 }
Eric Fellheimer8c40daa2016-01-11 17:31:26 +0000351
352 private static <T> int calculateSize(Iterable<ResolvedTargets<T>> resolvedTargets) {
353 int size = 0;
354 for (ResolvedTargets<T> targets : resolvedTargets) {
355 size += targets.getTargets().size();
356 }
357 return size;
358 }
Mark Schallerb889cf32015-03-17 20:55:30 +0000359}
360