| // 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.SignedTargetPattern; | |
| 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.server.FailureDetails.TargetPatterns; | |
| import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey; | |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; | |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant; | |
| import com.google.devtools.build.skyframe.AbstractSkyKey; | |
| import com.google.devtools.build.skyframe.SkyFunctionName; | |
| 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. | |
| @SerializationConstant | |
| 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. | |
| * | |
| * <p>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 mainRepoTargetParser The target pattern parser configured with the specified offset and | |
| * the main repository mapping. | |
| */ | |
| @ThreadSafe | |
| public static PrepareDepsOfPatternSkyKeysAndExceptions keys( | |
| List<String> patterns, TargetPattern.Parser mainRepoTargetParser) { | |
| ImmutableList.Builder<PrepareDepsOfPatternSkyKeyValue> resultValuesBuilder = | |
| ImmutableList.builder(); | |
| ImmutableList.Builder<PrepareDepsOfPatternSkyKeyException> resultExceptionsBuilder = | |
| ImmutableList.builder(); | |
| ImmutableList.Builder<TargetPatternKey> targetPatternKeysBuilder = ImmutableList.builder(); | |
| for (String pattern : patterns) { | |
| try { | |
| targetPatternKeysBuilder.add( | |
| TargetPatternValue.key( | |
| SignedTargetPattern.parse(pattern, mainRepoTargetParser), | |
| FilteringPolicies.NO_FILTER)); | |
| } catch (TargetParsingException e) { | |
| resultExceptionsBuilder.add(new PrepareDepsOfPatternSkyKeyException(e, pattern)); | |
| } | |
| } | |
| // 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<TargetPatternKey> combinedTargetPatternKeys = | |
| TargetPatternValue.combineTargetsBelowDirectoryWithNegativePatterns( | |
| targetPatternKeysBuilder.build(), /*excludeSingleTargets=*/ false); | |
| 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.", | |
| TargetPatterns.Code.NEGATIVE_TARGET_PATTERN_NOT_ALLOWED), | |
| targetPatternKey.toString())); | |
| } else { | |
| resultValuesBuilder.add(new PrepareDepsOfPatternSkyKeyValue(targetPatternKey)); | |
| } | |
| } | |
| return new PrepareDepsOfPatternSkyKeysAndExceptions( | |
| resultValuesBuilder.build(), resultExceptionsBuilder.build()); | |
| } | |
| /** | |
| * A pair of {@link Iterable<PrepareDepsOfPatternSkyKeyValue>} and {@link | |
| * Iterable<PrepareDepsOfPatternSkyKeyException>}. | |
| */ | |
| public static class PrepareDepsOfPatternSkyKeysAndExceptions { | |
| private final Iterable<PrepareDepsOfPatternSkyKeyValue> values; | |
| private final Iterable<PrepareDepsOfPatternSkyKeyException> exceptions; | |
| public PrepareDepsOfPatternSkyKeysAndExceptions( | |
| Iterable<PrepareDepsOfPatternSkyKeyValue> values, | |
| Iterable<PrepareDepsOfPatternSkyKeyException> exceptions) { | |
| this.values = values; | |
| this.exceptions = exceptions; | |
| } | |
| public Iterable<PrepareDepsOfPatternSkyKeyValue> getValues() { | |
| return values; | |
| } | |
| public Iterable<PrepareDepsOfPatternSkyKeyException> 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<TargetPatternKey> { | |
| private static final Interner<Key> 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; | |
| } | |
| } | |
| } | |
| } |