// 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.Interner; 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.BlazeInterners; 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.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.skyframe.AbstractSkyKey; import com.google.devtools.build.skyframe.SkyFunctionName; 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. * *

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. @AutoCodec 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 a {@link PrepareDepsOfPatternSkyKeysAndExceptions}, containing * {@link PrepareDepsOfPatternSkyKeyValue} and {@link PrepareDepsOfPatternSkyKeyException} * instances that have {@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}, there will be a * corresponding {@link PrepareDepsOfPatternSkyKeyException} in the iterable returned by * {@link PrepareDepsOfPatternSkyKeysAndExceptions#getExceptions} whose * {@link PrepareDepsOfPatternSkyKeyException#getException} and * {@link PrepareDepsOfPatternSkyKeyException#getOriginalPattern} methods return the * {@link TargetParsingException} and original pattern, respectively. * *

There may be fewer returned elements in * {@link PrepareDepsOfPatternSkyKeysAndExceptions#getValues} 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 PrepareDepsOfPatternSkyKeysAndExceptions keys( List patterns, String offset) { ImmutableList.Builder resultValuesBuilder = ImmutableList.builder(); ImmutableList.Builder resultExceptionsBuilder = ImmutableList.builder(); Iterable keysMaybe = TargetPatternValue.keys(patterns, FilteringPolicies.NO_FILTER, offset); ImmutableList.Builder targetPatternKeysBuilder = ImmutableList.builder(); for (TargetPatternSkyKeyOrException keyMaybe : keysMaybe) { try { SkyKey key = keyMaybe.getSkyKey(); targetPatternKeysBuilder.add((TargetPatternKey) key.argument()); } catch (TargetParsingException e) { resultExceptionsBuilder.add( new PrepareDepsOfPatternSkyKeyException(e, keyMaybe.getOriginalPattern())); } } // 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. Iterable combinedTargetPatternKeys = TargetPatternValue.combineNegativeTargetsBelowDirectoryPatterns( targetPatternKeysBuilder.build()); for (TargetPatternKey targetPatternKey : combinedTargetPatternKeys) { if (targetPatternKey.isNegative() && !targetPatternKey .getParsedPattern() .getType() .equals(TargetPattern.Type.TARGETS_BELOW_DIRECTORY)) { resultExceptionsBuilder.add( new PrepareDepsOfPatternSkyKeyException( new TargetParsingException( "Negative target patterns of types other than \"targets below directory\"" + " are not permitted."), targetPatternKey.toString())); } else { resultValuesBuilder.add(new PrepareDepsOfPatternSkyKeyValue(targetPatternKey)); } } return new PrepareDepsOfPatternSkyKeysAndExceptions( resultValuesBuilder.build(), resultExceptionsBuilder.build()); } /** * A pair of {@link Iterable} and * {@link Iterable}. */ public static class PrepareDepsOfPatternSkyKeysAndExceptions { private final Iterable values; private final Iterable exceptions; public PrepareDepsOfPatternSkyKeysAndExceptions( Iterable values, Iterable exceptions) { this.values = values; this.exceptions = exceptions; } public Iterable getValues() { return values; } public Iterable getExceptions() { return exceptions; } } /** Represents a {@link TargetParsingException} when parsing a target pattern string. */ public static class PrepareDepsOfPatternSkyKeyException { private final TargetParsingException exception; private final String originalPattern; public PrepareDepsOfPatternSkyKeyException(TargetParsingException exception, String originalPattern) { this.exception = exception; this.originalPattern = originalPattern; } public TargetParsingException getException() { return exception; } public String getOriginalPattern() { return originalPattern; } } /** * Represents the successful parsing of a target pattern string into a {@link TargetPatternKey}. */ public static class PrepareDepsOfPatternSkyKeyValue { private final TargetPatternKey targetPatternKey; PrepareDepsOfPatternSkyKeyValue(TargetPatternKey targetPatternKey) { this.targetPatternKey = targetPatternKey; } public Key getSkyKey() { return Key.create(targetPatternKey); } @AutoCodec static class Key extends AbstractSkyKey { private static final Interner interner = BlazeInterners.newWeakInterner(); private Key(TargetPatternKey arg) { super(arg); } @AutoCodec.VisibleForSerialization @AutoCodec.Instantiator static Key create(TargetPatternKey arg) { return interner.intern(new Key(arg)); } TargetPatternKey getTargetPatternKey() { return arg; } @Override public SkyFunctionName functionName() { return SkyFunctions.PREPARE_DEPS_OF_PATTERN; } } } }