// 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.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; 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.ParseFailureListener; import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternSkyKeyOrException; import com.google.devtools.build.lib.util.Preconditions; 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.SkyKey; 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 { private final SkyframeExecutor skyframeExecutor; private String offset = ""; SkyframeTargetPatternEvaluator(SkyframeExecutor skyframeExecutor) { this.skyframeExecutor = skyframeExecutor; } @Override public ResolvedTargets<Target> parseTargetPatternList( ExtendedEventHandler eventHandler, List<String> targetPatterns, FilteringPolicy policy, boolean keepGoing) throws TargetParsingException, InterruptedException { return parseTargetPatternList(offset, eventHandler, targetPatterns, policy, keepGoing); } @Override public ResolvedTargets<Target> parseTargetPattern( ExtendedEventHandler eventHandler, String pattern, boolean keepGoing) throws TargetParsingException, InterruptedException { return parseTargetPatternList(eventHandler, ImmutableList.of(pattern), DEFAULT_FILTERING_POLICY, keepGoing); } @Override public void updateOffset(PathFragment relativeWorkingDirectory) { offset = relativeWorkingDirectory.getPathString(); } @Override public String getOffset() { return offset; } @Override public Map<String, ResolvedTargets<Target>> preloadTargetPatterns( ExtendedEventHandler eventHandler, 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. // We cannot use an ImmutableMap here because there may be null values. Map<String, ResolvedTargets<Target>> result = Maps.newHashMapWithExpectedSize(patterns.size()); for (String pattern : patterns) { // TODO(bazel-team): This could be parallelized to improve performance. [skyframe-loading] result.put(pattern, parseTargetPattern(eventHandler, pattern, keepGoing)); } return result; } /** * Loads a list of target patterns (eg, "foo/..."). When policy is set to FILTER_TESTS, * test_suites are going to be expanded. */ ResolvedTargets<Target> parseTargetPatternList( String offset, ExtendedEventHandler eventHandler, List<String> targetPatterns, FilteringPolicy policy, boolean keepGoing) throws InterruptedException, TargetParsingException { Iterable<TargetPatternSkyKeyOrException> keysMaybe = TargetPatternValue.keys(targetPatterns, policy, offset); ImmutableList.Builder<SkyKey> builder = ImmutableList.builder(); for (TargetPatternSkyKeyOrException skyKeyOrException : keysMaybe) { try { builder.add(skyKeyOrException.getSkyKey()); } catch (TargetParsingException e) { if (!keepGoing) { throw e; } String pattern = skyKeyOrException.getOriginalPattern(); eventHandler.handle(Event.error("Skipping '" + pattern + "': " + e.getMessage())); if (eventHandler instanceof ParseFailureListener) { ((ParseFailureListener) eventHandler).parsingError(pattern, e.getMessage()); } } } ImmutableList<SkyKey> skyKeys = builder.build(); return parseTargetPatternKeys( targetPatterns, skyKeys, SkyframeExecutor.DEFAULT_THREAD_COUNT, keepGoing, eventHandler, createTargetPatternEvaluatorUtil(policy, eventHandler, keepGoing)); } private TargetPatternsResultBuilder createTargetPatternEvaluatorUtil( FilteringPolicy policy, ExtendedEventHandler eventHandler, boolean keepGoing) { return policy == FilteringPolicies.FILTER_TESTS ? new TestTargetPatternsResultBuilder(skyframeExecutor.getPackageManager(), eventHandler, keepGoing) : new BuildTargetPatternsResultBuilder(); } ResolvedTargets<Target> parseTargetPatternKeys( List<String> targetPattern, Iterable<SkyKey> patternSkyKeys, int numThreads, boolean keepGoing, ExtendedEventHandler eventHandler, TargetPatternsResultBuilder finalTargetSetEvaluator) throws InterruptedException, TargetParsingException { EvaluationResult<TargetPatternValue> result = skyframeExecutor.targetPatterns(patternSkyKeys, numThreads, keepGoing, eventHandler); String errorMessage = null; for (SkyKey key : patternSkyKeys) { TargetPatternValue resultValue = result.get(key); if (resultValue != null) { ResolvedTargets<Label> results = resultValue.getTargets(); if (((TargetPatternValue.TargetPatternKey) key.argument()).isNegative()) { finalTargetSetEvaluator.addLabelsOfNegativePattern(results); } else { finalTargetSetEvaluator.addLabelsOfPositivePattern(results); } } else { TargetPatternValue.TargetPatternKey patternKey = (TargetPatternValue.TargetPatternKey) key.argument(); String rawPattern = patternKey.getPattern(); ErrorInfo error = result.errorMap().get(key); if (error == null) { Preconditions.checkState(!keepGoing); continue; } 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)); } finalTargetSetEvaluator.setError(); if (eventHandler instanceof ParseFailureListener) { ParseFailureListener parseListener = (ParseFailureListener) eventHandler; parseListener.parsingError(rawPattern, errorMessage); } } } if (result.hasError()) { Preconditions.checkState(errorMessage != null, "unexpected errors: %s", result.errorMap()); finalTargetSetEvaluator.setError(); if (!keepGoing) { eventHandler.post(PatternExpandingError.failed(targetPattern, errorMessage)); throw new TargetParsingException(errorMessage); } } WalkableGraph walkableGraph = Preconditions.checkNotNull(result.getWalkableGraph(), result); return finalTargetSetEvaluator.build(walkableGraph); } }