// 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.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.cmdline.ResolvedTargets; import com.google.devtools.build.lib.cmdline.ResolvedTargets.Builder; 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.Immutable; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.pkgcache.FilteringPolicies; import com.google.devtools.build.lib.pkgcache.FilteringPolicy; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.InterruptibleSupplier; import com.google.devtools.build.skyframe.LegacySkyKey; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * A value referring to a computed set of resolved targets. This is used for the results of target * pattern parsing. */ @Immutable @ThreadSafe public final class TargetPatternValue implements SkyValue { private ResolvedTargets<Label> targets; TargetPatternValue(ResolvedTargets<Label> targets) { this.targets = Preconditions.checkNotNull(targets); } private void writeObject(ObjectOutputStream out) throws IOException { List<String> ts = new ArrayList<>(); List<String> filteredTs = new ArrayList<>(); for (Label target : targets.getTargets()) { ts.add(target.toString()); } for (Label target : targets.getFilteredTargets()) { filteredTs.add(target.toString()); } out.writeObject(ts); out.writeObject(filteredTs); } private Label labelFromString(String labelString) { try { return Label.parseAbsolute(labelString); } catch (LabelSyntaxException e) { throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { List<String> ts = (List<String>) in.readObject(); List<String> filteredTs = (List<String>) in.readObject(); Builder<Label> builder = ResolvedTargets.<Label>builder(); for (String labelString : ts) { builder.add(labelFromString(labelString)); } for (String labelString : filteredTs) { builder.remove(labelFromString(labelString)); } this.targets = builder.build(); } @SuppressWarnings("unused") private void readObjectNoData() { throw new IllegalStateException(); } /** * Create a target pattern {@link SkyKey}. Throws {@link TargetParsingException} if the provided * {@code pattern} cannot be parsed. * * @param pattern The pattern, eg "-foo/biz...". If the first character is "-", the pattern is * treated as a negative pattern. * @param policy The filtering policy, eg "only return test targets" * @param offset The offset to apply to relative target patterns. */ @ThreadSafe public static SkyKey key(String pattern, FilteringPolicy policy, String offset) throws TargetParsingException { return Iterables.getOnlyElement(keys(ImmutableList.of(pattern), policy, offset)).getSkyKey(); } /** * Returns an iterable of {@link TargetPatternSkyKeyOrException}, with {@link TargetPatternKey} * arguments, in the same order as the list of patterns provided as input. If a provided pattern * fails to parse, the element in the returned iterable corresponding to it will throw when its * {@link TargetPatternSkyKeyOrException#getSkyKey} method is called. * * @param patterns The list of patterns, eg "-foo/biz...". If a pattern's first character is "-", * it is treated as a negative pattern. * @param policy The filtering policy, eg "only return test targets" * @param offset The offset to apply to relative target patterns. */ @ThreadSafe public static Iterable<TargetPatternSkyKeyOrException> keys(List<String> patterns, FilteringPolicy policy, String offset) { TargetPattern.Parser parser = new TargetPattern.Parser(offset); ImmutableList.Builder<TargetPatternSkyKeyOrException> builder = ImmutableList.builder(); for (String pattern : patterns) { boolean positive = !pattern.startsWith("-"); String absoluteValueOfPattern = positive ? pattern : pattern.substring(1); TargetPattern targetPattern; try { targetPattern = parser.parse(absoluteValueOfPattern); } catch (TargetParsingException e) { builder.add(new TargetPatternSkyKeyException(e, absoluteValueOfPattern)); continue; } TargetPatternKey targetPatternKey = new TargetPatternKey( targetPattern, positive ? policy : FilteringPolicies.NO_FILTER, /*isNegative=*/ !positive, offset, ImmutableSet.<PathFragment>of()); SkyKey skyKey = LegacySkyKey.create(SkyFunctions.TARGET_PATTERN, targetPatternKey); builder.add(new TargetPatternSkyKeyValue(skyKey)); } return builder.build(); } public ResolvedTargets<Label> getTargets() { return targets; } /** * A TargetPatternKey is a tuple of pattern (eg, "foo/..."), filtering policy, a relative pattern * offset, whether it is a positive or negative match, and a set of excluded subdirectories. */ @ThreadSafe public static class TargetPatternKey implements Serializable { private final TargetPattern parsedPattern; private final FilteringPolicy policy; private final boolean isNegative; private final String offset; private final ImmutableSet<PathFragment> excludedSubdirectories; public TargetPatternKey( TargetPattern parsedPattern, FilteringPolicy policy, boolean isNegative, String offset, ImmutableSet<PathFragment> excludedSubdirectories) { this.parsedPattern = Preconditions.checkNotNull(parsedPattern); this.policy = Preconditions.checkNotNull(policy); this.isNegative = isNegative; this.offset = offset; this.excludedSubdirectories = Preconditions.checkNotNull(excludedSubdirectories); } public String getPattern() { return parsedPattern.getOriginalPattern(); } public TargetPattern getParsedPattern() { return parsedPattern; } public boolean isNegative() { return isNegative; } public FilteringPolicy getPolicy() { return policy; } public String getOffset() { return offset; } public ImmutableSet<PathFragment> getExcludedSubdirectories() { return excludedSubdirectories; } ImmutableSet<PathFragment> getAllSubdirectoriesToExclude( Iterable<PathFragment> blacklistedPackagePrefixes) throws InterruptedException { return getAllSubdirectoriesToExclude( new InterruptibleSupplier.Instance<>(blacklistedPackagePrefixes)); } public ImmutableSet<PathFragment> getAllSubdirectoriesToExclude( InterruptibleSupplier<? extends Iterable<PathFragment>> blacklistedPackagePrefixes) throws InterruptedException { ImmutableSet.Builder<PathFragment> excludedPathsBuilder = ImmutableSet.builder(); excludedPathsBuilder.addAll(getExcludedSubdirectories()); if (parsedPattern.getType() == Type.TARGETS_BELOW_DIRECTORY) { for (PathFragment blacklistedPackagePrefix : blacklistedPackagePrefixes.get()) { PackageIdentifier pkgIdForBlacklistedDirectorPrefix = PackageIdentifier.create( parsedPattern.getDirectoryForTargetsUnderDirectory().getRepository(), blacklistedPackagePrefix); if (parsedPattern.containsAllTransitiveSubdirectoriesForTBD( pkgIdForBlacklistedDirectorPrefix)) { excludedPathsBuilder.add(blacklistedPackagePrefix); } } } return excludedPathsBuilder.build(); } @Override public String toString() { return (isNegative ? "-" : "") + parsedPattern.getOriginalPattern(); } @Override public int hashCode() { return Objects.hash(parsedPattern, isNegative, policy, offset, excludedSubdirectories); } @Override public boolean equals(Object obj) { if (!(obj instanceof TargetPatternKey)) { return false; } TargetPatternKey other = (TargetPatternKey) obj; return other.isNegative == this.isNegative && other.parsedPattern.equals(this.parsedPattern) && other.offset.equals(this.offset) && other.policy.equals(this.policy) && other.excludedSubdirectories.equals(this.excludedSubdirectories); } } /** * Wrapper for a target pattern {@link SkyKey} or the {@link TargetParsingException} thrown when * trying to compute it. */ public interface TargetPatternSkyKeyOrException { /** * Returns the stored {@link SkyKey} or throws {@link TargetParsingException} if one was thrown * when computing the key. */ SkyKey getSkyKey() throws TargetParsingException; /** * Returns the pattern that resulted in the stored {@link SkyKey} or {@link * TargetParsingException}. */ String getOriginalPattern(); } private static final class TargetPatternSkyKeyValue implements TargetPatternSkyKeyOrException { private final SkyKey value; private TargetPatternSkyKeyValue(SkyKey value) { this.value = value; } @Override public SkyKey getSkyKey() throws TargetParsingException { return value; } @Override public String getOriginalPattern() { return ((TargetPatternKey) value.argument()).getPattern(); } } private static final class TargetPatternSkyKeyException implements TargetPatternSkyKeyOrException { private final TargetParsingException exception; private final String originalPattern; private TargetPatternSkyKeyException(TargetParsingException exception, String originalPattern) { this.exception = exception; this.originalPattern = originalPattern; } @Override public SkyKey getSkyKey() throws TargetParsingException { throw exception; } @Override public String getOriginalPattern() { return originalPattern; } } }