| // Copyright 2015 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.ImmutableSet; | 
 | import com.google.devtools.build.lib.cmdline.TargetParsingException; | 
 | import com.google.devtools.build.lib.cmdline.TargetPattern; | 
 | import com.google.devtools.build.lib.cmdline.TargetPattern.Type; | 
 | import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; | 
 | import com.google.devtools.build.lib.pkgcache.FilteringPolicies; | 
 | 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.SkyKey; | 
 | import com.google.devtools.build.skyframe.SkyValue; | 
 |  | 
 | import java.util.List; | 
 |  | 
 | /** | 
 |  * The value returned by {@link PrepareDepsOfPatternFunction}. Because that function is | 
 |  * invoked only for its side effect (i.e. ensuring the graph contains targets matching the | 
 |  * pattern and its transitive dependencies), this value carries no information. | 
 |  * | 
 |  * <p>Because the returned value is always equal to objects that share its type, this value and the | 
 |  * {@link PrepareDepsOfPatternFunction} which computes it are incompatible with change pruning. It | 
 |  * should only be requested by consumers who do not require reevaluation when | 
 |  * {@link PrepareDepsOfPatternFunction} is reevaluated. Safe consumers include, e.g., top-level | 
 |  * consumers, and other functions which invoke {@link PrepareDepsOfPatternFunction} solely for its | 
 |  * side-effects. | 
 |  */ | 
 | public class PrepareDepsOfPatternValue implements SkyValue { | 
 |   // Note that this value does not guarantee singleton-like reference equality because we use Java | 
 |   // deserialization. Java deserialization can create other instances. | 
 |   public static final PrepareDepsOfPatternValue INSTANCE = new PrepareDepsOfPatternValue(); | 
 |  | 
 |   private PrepareDepsOfPatternValue() { | 
 |   } | 
 |  | 
 |   @Override | 
 |   public boolean equals(Object o) { | 
 |     return o instanceof PrepareDepsOfPatternValue; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public int hashCode() { | 
 |     return 42; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Returns an iterable of {@link PrepareDepsOfPatternSkyKeyOrException}, with {@link | 
 |    * TargetPatternKey} arguments. Negative target patterns of type other than {@link | 
 |    * Type#TARGETS_BELOW_DIRECTORY} are not permitted. If a provided pattern fails to parse or is | 
 |    * negative but not a {@link Type#TARGETS_BELOW_DIRECTORY}, an element in the returned iterable | 
 |    * will throw when its {@link PrepareDepsOfPatternSkyKeyOrException#getSkyKey} method is called | 
 |    * and will return the failing pattern when its {@link | 
 |    * PrepareDepsOfPatternSkyKeyOrException#getOriginalPattern} method is called. | 
 |    * | 
 |    * <p>There may be fewer returned elements than patterns provided as input. This function will | 
 |    * combine negative {@link Type#TARGETS_BELOW_DIRECTORY} patterns with preceding patterns to | 
 |    * return an iterable of SkyKeys that avoids loading excluded directories during evaluation. | 
 |    * | 
 |    * @param patterns The list of patterns, e.g. [//foo/..., -//foo/biz/...]. If a pattern's first | 
 |    *     character is "-", it is treated as a negative pattern. | 
 |    * @param offset The offset to apply to relative target patterns. | 
 |    */ | 
 |   @ThreadSafe | 
 |   public static Iterable<PrepareDepsOfPatternSkyKeyOrException> keys(List<String> patterns, | 
 |       String offset) { | 
 |     List<TargetPatternSkyKeyOrException> keysMaybe = | 
 |         ImmutableList.copyOf(TargetPatternValue.keys(patterns, FilteringPolicies.NO_FILTER, | 
 |             offset)); | 
 |  | 
 |     // This code path is evaluated only for query universe preloading, and the quadratic cost of | 
 |     // the code below (i.e. for each pattern, consider each later pattern as a candidate for | 
 |     // subdirectory exclusion) is only acceptable because all the use cases for query universe | 
 |     // preloading involve short (<10 items) pattern sequences. | 
 |     ImmutableList.Builder<PrepareDepsOfPatternSkyKeyOrException> builder = ImmutableList.builder(); | 
 |     for (int i = 0; i < keysMaybe.size(); i++) { | 
 |       TargetPatternSkyKeyOrException keyMaybe = keysMaybe.get(i); | 
 |       SkyKey skyKey; | 
 |       try { | 
 |         skyKey = keyMaybe.getSkyKey(); | 
 |       } catch (TargetParsingException e) { | 
 |         // keyMaybe.getSkyKey() may throw TargetParsingException if its corresponding pattern | 
 |         // failed to parse. If so, wrap the exception and return it, so that our caller can | 
 |         // deal with it. | 
 |         skyKey = null; | 
 |         builder.add(new PrepareDepsOfPatternSkyKeyException(e, keyMaybe.getOriginalPattern())); | 
 |       } | 
 |       if (skyKey != null) { | 
 |         TargetPatternKey targetPatternKey = (TargetPatternKey) skyKey.argument(); | 
 |         if (targetPatternKey.isNegative()) { | 
 |           if (!targetPatternKey.getParsedPattern().getType().equals(Type.TARGETS_BELOW_DIRECTORY)) { | 
 |             builder.add( | 
 |                 new PrepareDepsOfPatternSkyKeyException( | 
 |                     new TargetParsingException( | 
 |                         "Negative target patterns of types other than \"targets below directory\"" | 
 |                             + " are not permitted."), targetPatternKey.toString())); | 
 |           } | 
 |           // Otherwise it's a negative TBD pattern which was combined with previous patterns as an | 
 |           // excluded directory. These can be skipped because there's no PrepareDepsOfPattern work | 
 |           // to be done for them. | 
 |         } else { | 
 |           builder.add(new PrepareDepsOfPatternSkyKeyValue(setExcludedDirectories(targetPatternKey, | 
 |               excludedDirectoriesBeneath(targetPatternKey, i, keysMaybe)))); | 
 |         } | 
 |       } | 
 |     } | 
 |     return builder.build(); | 
 |   } | 
 |  | 
 |   private static TargetPatternKey setExcludedDirectories( | 
 |       TargetPatternKey original, ImmutableSet<PathFragment> excludedSubdirectories) { | 
 |     return new TargetPatternKey(original.getParsedPattern(), original.getPolicy(), | 
 |         original.isNegative(), original.getOffset(), excludedSubdirectories); | 
 |   } | 
 |  | 
 |   private static ImmutableSet<PathFragment> excludedDirectoriesBeneath( | 
 |       TargetPatternKey targetPatternKey, | 
 |       int position, | 
 |       List<TargetPatternSkyKeyOrException> keysMaybe) { | 
 |     ImmutableSet.Builder<PathFragment> excludedDirectoriesBuilder = ImmutableSet.builder(); | 
 |     for (int j = position + 1; j < keysMaybe.size(); j++) { | 
 |       TargetPatternSkyKeyOrException laterPatternMaybe = keysMaybe.get(j); | 
 |       SkyKey laterSkyKey; | 
 |       try { | 
 |         laterSkyKey = laterPatternMaybe.getSkyKey(); | 
 |       } catch (TargetParsingException ignored) { | 
 |         laterSkyKey = null; | 
 |       } | 
 |       if (laterSkyKey != null) { | 
 |         TargetPatternKey laterTargetPatternKey = (TargetPatternKey) laterSkyKey.argument(); | 
 |         TargetPattern laterParsedPattern = laterTargetPatternKey.getParsedPattern(); | 
 |         if (laterTargetPatternKey.isNegative() | 
 |             && targetPatternKey.getParsedPattern().containsBelowDirectory(laterParsedPattern)) { | 
 |           excludedDirectoriesBuilder.add(laterParsedPattern.getDirectory().getPackageFragment()); | 
 |         } | 
 |       } | 
 |     } | 
 |     return excludedDirectoriesBuilder.build(); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Wrapper for a prepare deps of pattern {@link SkyKey} or the {@link TargetParsingException} | 
 |    * thrown when trying to create it. | 
 |    */ | 
 |   public interface PrepareDepsOfPatternSkyKeyOrException { | 
 |  | 
 |     /** | 
 |      * Returns the stored {@link SkyKey} or throws {@link TargetParsingException} if one was thrown | 
 |      * when creating the key. | 
 |      */ | 
 |     SkyKey getSkyKey() throws TargetParsingException; | 
 |  | 
 |     /** | 
 |      * Returns the pattern that resulted in the stored {@link SkyKey} or {@link | 
 |      * TargetParsingException}. | 
 |      */ | 
 |     String getOriginalPattern(); | 
 |   } | 
 |  | 
 |  | 
 |   private static class PrepareDepsOfPatternSkyKeyException implements | 
 |       PrepareDepsOfPatternSkyKeyOrException { | 
 |  | 
 |     private final TargetParsingException exception; | 
 |     private final String originalPattern; | 
 |  | 
 |     public PrepareDepsOfPatternSkyKeyException(TargetParsingException exception, | 
 |         String originalPattern) { | 
 |       this.exception = exception; | 
 |       this.originalPattern = originalPattern; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public SkyKey getSkyKey() throws TargetParsingException { | 
 |       throw exception; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public String getOriginalPattern() { | 
 |       return originalPattern; | 
 |     } | 
 |   } | 
 |  | 
 |   private static class PrepareDepsOfPatternSkyKeyValue implements | 
 |       PrepareDepsOfPatternSkyKeyOrException { | 
 |  | 
 |     private final TargetPatternKey targetPatternKey; | 
 |  | 
 |     public PrepareDepsOfPatternSkyKeyValue(TargetPatternKey targetPatternKey) { | 
 |       this.targetPatternKey = targetPatternKey; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public SkyKey getSkyKey() throws TargetParsingException { | 
 |       return SkyKey.create(SkyFunctions.PREPARE_DEPS_OF_PATTERN, targetPatternKey); | 
 |     } | 
 |  | 
 |     @Override | 
 |     public String getOriginalPattern() { | 
 |       return targetPatternKey.getPattern(); | 
 |     } | 
 |   } | 
 | } |