| // Copyright 2014 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| package com.google.devtools.build.lib.skyframe; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.cmdline.ResolvedTargets; |
| import com.google.devtools.build.lib.cmdline.TargetParsingException; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.ExtendedEventHandler; |
| import com.google.devtools.build.lib.packages.Target; |
| import com.google.devtools.build.lib.pkgcache.FilteringPolicies; |
| import com.google.devtools.build.lib.pkgcache.FilteringPolicy; |
| import com.google.devtools.build.lib.pkgcache.ParsingFailedEvent; |
| import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; |
| import com.google.devtools.build.lib.pkgcache.TargetPatternPreloader; |
| import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey; |
| import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternSkyKeyOrException; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.skyframe.ErrorInfo; |
| import com.google.devtools.build.skyframe.EvaluationResult; |
| import com.google.devtools.build.skyframe.WalkableGraph; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Skyframe-based target pattern parsing. |
| */ |
| final class SkyframeTargetPatternEvaluator |
| implements TargetPatternEvaluator, TargetPatternPreloader { |
| private final SkyframeExecutor skyframeExecutor; |
| |
| SkyframeTargetPatternEvaluator(SkyframeExecutor skyframeExecutor) { |
| this.skyframeExecutor = skyframeExecutor; |
| } |
| |
| @Override |
| public ResolvedTargets<Target> parseTargetPatternList( |
| PathFragment relativeWorkingDirectory, |
| ExtendedEventHandler eventHandler, |
| List<String> targetPatterns, |
| FilteringPolicy policy, |
| boolean keepGoing) |
| throws TargetParsingException, InterruptedException { |
| return parseTargetPatternList( |
| relativeWorkingDirectory.getPathString(), |
| eventHandler, |
| ImmutableList.copyOf(targetPatterns), |
| policy, |
| keepGoing); |
| } |
| |
| @Override |
| public Map<String, ResolvedTargets<Target>> preloadTargetPatterns( |
| ExtendedEventHandler eventHandler, |
| PathFragment relativeWorkingDirectory, |
| Collection<String> patterns, |
| boolean keepGoing) |
| throws TargetParsingException, InterruptedException { |
| // TODO(bazel-team): This is used only in "blaze query". There are plans to dramatically change |
| // how query works on Skyframe, in which case this method is likely to go away. |
| ImmutableList.Builder<TargetPatternsAndKeysAndResultBuilder> |
| targetPatternsAndKeysAndResultListBuilder = ImmutableList.builder(); |
| FilteringPolicy policy = DEFAULT_FILTERING_POLICY; |
| for (String pattern : patterns) { |
| ImmutableList<String> singletonPatternList = ImmutableList.of(pattern); |
| targetPatternsAndKeysAndResultListBuilder.add(new TargetPatternsAndKeysAndResultBuilder( |
| singletonPatternList, |
| getTargetPatternKeys( |
| relativeWorkingDirectory.getPathString(), |
| eventHandler, |
| singletonPatternList, |
| policy, |
| keepGoing), |
| createTargetPatternEvaluatorUtil(policy, eventHandler, keepGoing))); |
| |
| } |
| ImmutableList<ResolvedTargets<Target>> batchResult = parseTargetPatternKeysBatch( |
| targetPatternsAndKeysAndResultListBuilder.build(), |
| SkyframeExecutor.DEFAULT_THREAD_COUNT, |
| keepGoing, |
| eventHandler); |
| Preconditions.checkState(patterns.size() == batchResult.size(), patterns); |
| ImmutableMap.Builder<String, ResolvedTargets<Target>> resultBuilder = ImmutableMap.builder(); |
| int i = 0; |
| for (String pattern : patterns) { |
| resultBuilder.put(pattern, batchResult.get(i++)); |
| } |
| return resultBuilder.build(); |
| } |
| |
| private Iterable<TargetPatternKey> getTargetPatternKeys( |
| String offset, |
| ExtendedEventHandler eventHandler, |
| ImmutableList<String> targetPatterns, |
| FilteringPolicy policy, |
| boolean keepGoing) throws TargetParsingException { |
| Iterable<TargetPatternSkyKeyOrException> keysMaybe = |
| TargetPatternValue.keys(targetPatterns, policy, offset); |
| ImmutableList.Builder<TargetPatternKey> builder = ImmutableList.builder(); |
| for (TargetPatternSkyKeyOrException skyKeyOrException : keysMaybe) { |
| try { |
| builder.add(skyKeyOrException.getSkyKey()); |
| } catch (TargetParsingException e) { |
| // We report a parsing failed exception to the event bus here in case the pattern did not |
| // successfully parse (which happens before the SkyKey is created). Otherwise the |
| // TargetPatternFunction posts the event. |
| eventHandler.post( |
| new ParsingFailedEvent(skyKeyOrException.getOriginalPattern(), e.getMessage())); |
| if (!keepGoing) { |
| throw e; |
| } |
| String pattern = skyKeyOrException.getOriginalPattern(); |
| eventHandler.handle(Event.error("Skipping '" + pattern + "': " + e.getMessage())); |
| } |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Loads a list of target patterns (eg, "foo/..."). When policy is set to FILTER_TESTS, |
| * test_suites are going to be expanded. |
| */ |
| private ResolvedTargets<Target> parseTargetPatternList( |
| String offset, |
| ExtendedEventHandler eventHandler, |
| ImmutableList<String> targetPatterns, |
| FilteringPolicy policy, |
| boolean keepGoing) |
| throws InterruptedException, TargetParsingException { |
| return Iterables.getOnlyElement( |
| parseTargetPatternKeysBatch( |
| ImmutableList.of( |
| new TargetPatternsAndKeysAndResultBuilder( |
| targetPatterns, |
| getTargetPatternKeys(offset, eventHandler, targetPatterns, policy, keepGoing), |
| createTargetPatternEvaluatorUtil(policy, eventHandler, keepGoing))), |
| SkyframeExecutor.DEFAULT_THREAD_COUNT, |
| keepGoing, |
| eventHandler)); |
| } |
| |
| private TargetPatternsResultBuilder createTargetPatternEvaluatorUtil( |
| FilteringPolicy policy, ExtendedEventHandler eventHandler, boolean keepGoing) { |
| return policy == FilteringPolicies.FILTER_TESTS |
| ? new TestTargetPatternsResultBuilder( |
| skyframeExecutor.getPackageManager(), eventHandler, keepGoing) |
| : new BuildTargetPatternsResultBuilder(); |
| } |
| |
| private class TargetPatternsAndKeysAndResultBuilder { |
| private final ImmutableList<String> targetPatterns; |
| private final Iterable<TargetPatternKey> patternSkyKeys; |
| private final TargetPatternsResultBuilder resultBuilder; |
| |
| private TargetPatternsAndKeysAndResultBuilder( |
| ImmutableList<String> targetPatterns, |
| Iterable<TargetPatternKey> patternSkyKeys, |
| TargetPatternsResultBuilder resultBuilder) { |
| this.targetPatterns = targetPatterns; |
| this.patternSkyKeys = patternSkyKeys; |
| this.resultBuilder = resultBuilder; |
| } |
| } |
| |
| private ImmutableList<ResolvedTargets<Target>> parseTargetPatternKeysBatch( |
| ImmutableList<TargetPatternsAndKeysAndResultBuilder> targetPatternsAndKeysAndResultBuilders, |
| int numThreads, |
| boolean keepGoing, |
| ExtendedEventHandler eventHandler) |
| throws InterruptedException, TargetParsingException { |
| ImmutableList.Builder<TargetPatternKey> allKeysBuilder = ImmutableList.builder(); |
| for (TargetPatternsAndKeysAndResultBuilder targetPatternsAndKeysAndResultBuilder |
| : targetPatternsAndKeysAndResultBuilders) { |
| allKeysBuilder.addAll(targetPatternsAndKeysAndResultBuilder.patternSkyKeys); |
| } |
| EvaluationResult<TargetPatternValue> result = skyframeExecutor.targetPatterns( |
| allKeysBuilder.build(), numThreads, keepGoing, eventHandler); |
| WalkableGraph walkableGraph = Preconditions.checkNotNull(result.getWalkableGraph(), result); |
| ImmutableList.Builder<ResolvedTargets<Target>> resolvedTargetsListBuilder = |
| ImmutableList.builder(); |
| for (TargetPatternsAndKeysAndResultBuilder targetPatternsAndKeysAndResultBuilder |
| : targetPatternsAndKeysAndResultBuilders) { |
| ImmutableList<String> targetPatterns = targetPatternsAndKeysAndResultBuilder.targetPatterns; |
| Iterable<TargetPatternKey> patternSkyKeys = |
| targetPatternsAndKeysAndResultBuilder.patternSkyKeys; |
| TargetPatternsResultBuilder resultBuilder = |
| targetPatternsAndKeysAndResultBuilder.resultBuilder; |
| String errorMessage = null; |
| boolean hasError = false; |
| for (TargetPatternKey key : patternSkyKeys) { |
| TargetPatternValue resultValue = result.get(key); |
| if (resultValue != null) { |
| ResolvedTargets<Label> results = resultValue.getTargets(); |
| if (key.isNegative()) { |
| resultBuilder.addLabelsOfNegativePattern(results); |
| } else { |
| resultBuilder.addLabelsOfPositivePattern(results); |
| } |
| } else { |
| String rawPattern = key.getPattern(); |
| ErrorInfo error = result.errorMap().get(key); |
| if (error == null) { |
| Preconditions.checkState(!keepGoing); |
| continue; |
| } |
| hasError = true; |
| if (error.getException() != null) { |
| // This exception may not be a TargetParsingException because in a nokeep_going build, |
| // the target pattern parser may swallow a NoSuchPackageException but the framework will |
| // bubble it up anyway. |
| Preconditions.checkArgument(!keepGoing |
| || error.getException() instanceof TargetParsingException, error); |
| errorMessage = error.getException().getMessage(); |
| } else if (!Iterables.isEmpty(error.getCycleInfo())) { |
| errorMessage = "cycles detected during target parsing"; |
| skyframeExecutor.getCyclesReporter().reportCycles( |
| error.getCycleInfo(), key, eventHandler); |
| } else { |
| throw new IllegalStateException(error.toString()); |
| } |
| if (keepGoing) { |
| eventHandler.handle(Event.error("Skipping '" + rawPattern + "': " + errorMessage)); |
| eventHandler.post(PatternExpandingError.skipped(rawPattern, errorMessage)); |
| } |
| resultBuilder.setError(); |
| } |
| } |
| |
| if (hasError) { |
| Preconditions.checkState(errorMessage != null, "unexpected errors: %s", result.errorMap()); |
| resultBuilder.setError(); |
| if (!keepGoing) { |
| eventHandler.post(PatternExpandingError.failed(targetPatterns, errorMessage)); |
| throw new TargetParsingException(errorMessage); |
| } |
| } |
| resolvedTargetsListBuilder.add(resultBuilder.build(walkableGraph)); |
| } |
| return resolvedTargetsListBuilder.build(); |
| } |
| } |