// 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.LegacySkyKey; 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() && laterParsedPattern.getType() == Type.TARGETS_BELOW_DIRECTORY && targetPatternKey.getParsedPattern().containsDirectoryOfTBDForTBD( laterParsedPattern)) { excludedDirectoriesBuilder.add( laterParsedPattern.getDirectoryForTargetsUnderDirectory().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 LegacySkyKey.create(SkyFunctions.PREPARE_DEPS_OF_PATTERN, targetPatternKey); } @Override public String getOriginalPattern() { return targetPatternKey.getPattern(); } } }