diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skyframe/AggregatedPatterns.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/skyframe/AggregatedPatterns.java | 363 |
1 files changed, 0 insertions, 363 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AggregatedPatterns.java b/src/main/java/com/google/devtools/build/lib/skyframe/AggregatedPatterns.java deleted file mode 100644 index 4600cd9a4c..0000000000 --- a/src/main/java/com/google/devtools/build/lib/skyframe/AggregatedPatterns.java +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2015 Google Inc. 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.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.devtools.build.lib.cmdline.TargetPattern.Type; -import com.google.devtools.build.lib.pkgcache.FilteringPolicies; -import com.google.devtools.build.lib.pkgcache.FilteringPolicy; -import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey; - -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.Set; -import java.util.TreeMap; - -/** - * This is used by {@link TargetPatternValue#keys} to help it convert a sequence of target patterns - * into a sequence of {@link TargetPatternKey}s that, when evaluated and combined together, yield - * the targets matched by the target pattern sequence. - * - * <p>Its main strategy for accomplishing this is to sort the target patterns by the directories - * that contain the targets they match, and take advantage of that sorting to efficiently discover - * redundant patterns (e.g. the two patterns "//foo/..." and "//foo/bar/..." don't each need to be - * evaluated if they appear side-by-side in the sequence) and exclusionary patterns (e.g. the two - * patterns "//foo/..." and "-//foo/bar/..." don't each need to be evaluated--instead, while - * evaluating the targets below "foo", the subdirectory "bar" can be skipped). - * - * <p>An {@link AggregatedPatterns} instance expects its {@link #addPattern} method to be called - * for each target pattern in the sequence in a left-to-right order. This is because a pattern of - * type {@code Type.TARGETS_BELOW_DIRECTORY} overrides everything encountered so far in and below - * the directory it specifies, and this class implements that by removing all target patterns - * that were already added in and below that directory. - * - * <p>The class takes a {@link FilteringPolicy} and a ({@link String}) offset in its constructor, as - * the {@link TargetPatternKey}s it constructs from a target pattern sequence include those values. - * - * <p>The creation of {@link TargetPatternKey}s begins when {@link AggregatedPatterns#build} is - * called. It processes the sorted target patterns recursively, breaking the problem of - * converting the sequence of target patterns to a sequence of {@link TargetPatternKey}s into a - * set of sub-problems, where each sub-problem consists of converting the patterns beneath each - * {@code Type.TARGETS_BELOW_DIRECTORY} pattern into a sequence of {@link TargetPatternKey}s. - * This gets off the ground because one can think of every target pattern sequence as implicitly - * starting with the "-//..." pattern. - */ -class AggregatedPatterns { - - // Keys in this ordered map are strings representing directories. These strings are either "" (to - // indicate the depot root) or end with a trailing '/'. - // - // They end with '/' to ensure that an iteration through the map corresponds to a depth-first - // traversal through the directory tree, and that an entry for a directory directly precedes the - // entries for its subdirectories. - // - // For example, consider the three directories "a/", "a/b/", and "a.1/". When sorted into the - // map, their entries will be visited in this order: - // - // "a.1/" - // "a/" - // "a/b/" - // - // If the keys were directories in normal form (i.e. lacking a trailing '/') they would be - // visited in this order: - // - // "a" - // "a.1" - // "a/b" - // - // (Note how "a" and "a/b" are sadly separated in this latter example.) - // - // If {@code l = orderedPatternsByDirectory.get(d)}, then {@code l} is the list of all target - // patterns {@code p} such that {@code maybeAddTrailingSlash(p.getDirectory()).equals(d)}, in - // order of insertion via {@link AggregatedPatterns#addPattern} (which is assumed to correspond - // to logic order in the target pattern sequence), which were not inserted before any - // {@code Type.TARGETS_BELOW_DIRECTORY} patterns with directories equal to or above {@code d} - // (because later patterns take precedence over earlier patterns). - private final NavigableMap<String, List<SignedPattern>> orderedPatternsByDirectory = - new TreeMap<>(); - private final FilteringPolicy policy; - private final String offset; - - AggregatedPatterns(FilteringPolicy policy, String offset) { - this.policy = policy; - this.offset = offset; - } - - /** Must be called for each target pattern in the sequence in order from left to right. */ - AggregatedPatterns addPattern(SignedPattern signedPattern) { - Preconditions.checkNotNull(signedPattern); - String directoryWithoutTrailingSlash = signedPattern.getPattern().getDirectory(); - - // If the new pattern is a TargetsBelowDirectory, then it overrides everything in and below its - // directory, so we can remove those keys. - if (isTargetsBelowDirectory(signedPattern)) { - // The set returned by SortedMap.keySet supports element removal via the clear operation. - getDirectoriesBelow(directoryWithoutTrailingSlash).clear(); - } - - // If the pattern's directory is non-empty, we add a trailing "/" to the directory key. See the - // comment on orderedPatternsByDirectory for the reason why. - String directoryKey = getDirectoryKey(directoryWithoutTrailingSlash); - List<SignedPattern> patternsMaybe = orderedPatternsByDirectory.get(directoryKey); - if (patternsMaybe == null) { - patternsMaybe = Lists.newArrayList(); - orderedPatternsByDirectory.put(directoryKey, patternsMaybe); - } - patternsMaybe.add(signedPattern); - return this; - } - - Iterable<TargetPatternKey> build() { - return getKeysAndExcludedDirsUnder(orderedPatternsByDirectory, false).getKeys(); - } - - private enum DirectoryInclusion { - INCLUDED, - EXCLUDED, - MIXED; - - static DirectoryInclusion from(boolean isIncluded) { - return isIncluded ? INCLUDED : EXCLUDED; - } - } - - private static class KeysAndExcludedDirectories { - private final ImmutableList<TargetPatternKey> keys; - private final ImmutableSet<String> excludedDirectories; - - private KeysAndExcludedDirectories(ImmutableList<TargetPatternKey> keys, - ImmutableSet<String> excludedDirectories) { - this.keys = Preconditions.checkNotNull(keys); - this.excludedDirectories = Preconditions.checkNotNull(excludedDirectories); - } - - public ImmutableList<TargetPatternKey> getKeys() { - return keys; - } - - public ImmutableSet<String> getExcludedDirectories() { - return excludedDirectories; - } - } - - private static class SubdirectoriesAndRemainder { - private final NavigableMap<String, List<SignedPattern>> subdirectoriesMap; - private final Iterator<Entry<String, List<SignedPattern>>> remainder; - - public SubdirectoriesAndRemainder( - NavigableMap<String, List<SignedPattern>> subdirectoriesMap, - Iterator<Entry<String, List<SignedPattern>>> remainder) { - this.subdirectoriesMap = Preconditions.checkNotNull(subdirectoriesMap); - this.remainder = Preconditions.checkNotNull(remainder); - } - - public NavigableMap<String, List<SignedPattern>> getSubdirectoriesMap() { - return subdirectoriesMap; - } - - public Iterator<Entry<String, List<SignedPattern>>> getRemainder() { - return remainder; - } - } - - /** - * Calculates a list of {@link TargetPatternKey}s and a set of excluded directories - * (represented by {@link String}s in normal form, relative to the workspace root) - * corresponding to the {@link SignedPattern}s included in {@code directories}. - * - * <p>The function tries to eliminate redundant {@link TargetPatternKey}s from the returned value. - * To help it discover these redundancies it must be told whether targets in - * {@code directories} are considered included by default, via {@code includedByDefault}. For - * example, if all the directories in {@code directories} are subdirectories below "some/path", - * and we had already seen a target pattern corresponding to "//some/path/...", then {@code - * includedByDefault} would be true. - * - * @param directories an ordered map of directories to ordered lists of target patterns, - * sharing the same requirements as {@code orderedPatternsByDirectory} - * @param includedByDefault whether targets in {@code directories} should be considered - * included by default - */ - private KeysAndExcludedDirectories getKeysAndExcludedDirsUnder( - NavigableMap<String, List<SignedPattern>> directories, boolean includedByDefault) { - ImmutableList.Builder<TargetPatternKey> keysBuilder = ImmutableList.builder(); - ImmutableSet.Builder<String> excludedDirsBuilder = ImmutableSet.builder(); - - // The following {@code iterator} variable is used to iterate through the directories in - // {@code orderedPatternsByDirectory}. When it encounters a pattern of type - // {@code Type.TARGETS_BELOW_DIRECTORY} in a directory, it recursively calls itself to evaluate - // the interval of entries corresponding to the subdirectories beneath that directory. - // Afterwards, it skips past that already-processed interval by *assigning to* - // {@code iterator}, to make it point at the first element after the interval (or to an "empty" - // iterator if there is no such element). That assignment is why this while loop isn't a - // standard "for(T e : iterable)" loop. - Iterator<Entry<String, List<SignedPattern>>> iterator = directories.entrySet().iterator(); - while (iterator.hasNext()) { - Entry<String, List<SignedPattern>> directoryEntry = iterator.next(); - // The following {@code directoryInclusion} variable represents whether targets in this - // directory are included by default. It's initialized to INCLUDED or EXCLUDED based on the - // value of {@code includedByDefault}. It will be set to INCLUDED if we encounter a positive - // TargetsBelowDirectory (TBD) pattern in the directory, EXCLUDED if we encounter a - // negative TBD pattern in the directory, and will be set to MIXED if we encounter a - // non-TBD pattern to be subtracted while in the INCLUDED state or if we encounter a - // non-TBD pattern to be added while in the EXCLUDED state. (Note that only the first - // pattern in the directory can be a TBD pattern because adding a TBD pattern removes all - // existing patterns in and below its directory.) - // - // It's used to discover redundant patterns, i.e. patterns that can be skipped because a - // prior pattern already includes it (and it's positive) or because a prior pattern - // already excluded it (and it's negative). - // - // Currently, the only kind of prior patterns that this code is sensitive to are those of - // type TBD. This code doesn't keep track of targets in a more fine-grained way for - // simplicity. (See the final comment in the - // TargetPatternValueTest#testSubtractingThenAddingInIncludedDirectory unit test for more - // information.) Still, this approach offers the benefit of skipping positive targets under - // a positive TBD pattern and negative targets under a negative TBD pattern. - DirectoryInclusion directoryInclusion = DirectoryInclusion.from(includedByDefault); - - Iterable<SignedPattern> directoryPatterns = directoryEntry.getValue(); - for (SignedPattern signedPattern : directoryPatterns) { - if (isTargetsBelowDirectory(signedPattern)) { - if (!signedPattern.isPositive()) { - // Add its directory to the excluded directories. - excludedDirsBuilder.add(signedPattern.getPattern().getDirectory()); - } - - // If it's a TBD pattern with sign opposite from the current default inclusion policy, - // then it isn't redundant, and will contribute to the returned value as either a - // TargetPatternKey (if the pattern's sign is positive) or as an excluded directory (if - // the pattern's sign is negative). - if (signedPattern.isPositive() != includedByDefault) { - // 1) Remember the new effective inclusion policy for this directory. - directoryInclusion = DirectoryInclusion.from(signedPattern.isPositive()); - - // 2) Collect any additional keys and excluded directories from subdirectories below - // this one. - SubdirectoriesAndRemainder subdirectoriesAndRemainder = - getSubdirectoriesAndRemainder(directories, directoryEntry.getKey()); - NavigableMap<String, List<SignedPattern>> subMapForSubdirectories = - subdirectoriesAndRemainder.getSubdirectoriesMap(); - KeysAndExcludedDirectories keysAndExcludedDirs = - getKeysAndExcludedDirsUnder(subMapForSubdirectories, - /*includedByDefault=*/signedPattern.isPositive()); - - if (signedPattern.isPositive()) { - // 2.5) If this TBD pattern is positive, specify the excluded subdirectories in the - // TargetPatternKey for this pattern and add the key to the keysBuilder. - keysBuilder.add(createTargetPatternKey(signedPattern, policy, offset, - keysAndExcludedDirs.getExcludedDirectories())); - } - - // 3) Include the keys collected from subdirectories. - // Note that this statement means that the number of references this algorithm copies - // is potentially quadratic. An adversary could engineer a target pattern sequence - // that degrades performance. - keysBuilder.addAll(keysAndExcludedDirs.getKeys()); - - // 4) Skip past the keys from subdirectories below this one. - iterator = subdirectoriesAndRemainder.getRemainder(); - } - // Else, it's a TBD pattern with sign equal to the current default inclusion policy. - // Skip it because it's redundant. - } else { // It's not a TBD pattern. - boolean subtractingFromIncludedDirectory = - directoryInclusion.equals(DirectoryInclusion.INCLUDED) && !signedPattern.isPositive(); - boolean addingToExcludedDirectory = - directoryInclusion.equals(DirectoryInclusion.EXCLUDED) && signedPattern.isPositive(); - boolean alreadyMixed = directoryInclusion.equals(DirectoryInclusion.MIXED); - if (subtractingFromIncludedDirectory || addingToExcludedDirectory || alreadyMixed) { - // If we're subtracting from an included directory, or adding to an excluded - // directory, or if the directory is already mixed, add the pattern to the keysBuilder. - keysBuilder.add(createTargetPatternKey(signedPattern, policy, offset)); - // Also remember that this directory is now both including and excluding some targets, - // so all subsequent patterns must be evaluated. - directoryInclusion = DirectoryInclusion.MIXED; - } - } - } - } - return new KeysAndExcludedDirectories(keysBuilder.build(), excludedDirsBuilder.build()); - } - - private static String getDirectoryKey(String directoryWithoutTrailingSlash) { - return directoryWithoutTrailingSlash.length() == 0 ? "" : directoryWithoutTrailingSlash + "/"; - } - - private static TargetPatternKey createTargetPatternKey(SignedPattern pattern, - FilteringPolicy policy, String offset) { - return createTargetPatternKey(pattern, policy, offset, ImmutableSet.<String>of()); - } - - private static TargetPatternKey createTargetPatternKey(SignedPattern signedPattern, - FilteringPolicy policy, String offset, ImmutableSet<String> excludedDirectories) { - if (signedPattern.isPositive()) { - return new TargetPatternKey(signedPattern.getPattern(), policy, /*isNegative=*/false, offset, - excludedDirectories); - } else { - return new TargetPatternKey(signedPattern.getPattern(), FilteringPolicies.NO_FILTER, - /*isNegative=*/true, offset, excludedDirectories); - } - } - - private static SubdirectoriesAndRemainder getSubdirectoriesAndRemainder( - NavigableMap<String, List<SignedPattern>> directories, String directoryKey) { - NavigableMap<String, List<SignedPattern>> subdirectories; - Iterator<Entry<String, List<SignedPattern>>> remainder; - if (directoryKey.length() == 0) { - subdirectories = directories.tailMap(directoryKey, - /*inclusive, referring to the directoryKey endpoint=*/false); - remainder = Collections.emptyIterator(); - } else { - String afterKey = getKeyAfterAllPathsBelowDirectoryWithoutTrailingSlash( - directoryKey.substring(0, directoryKey.length() - 1)); - subdirectories = directories.subMap(directoryKey, - /*inclusive, referring to the directoryKey endpoint=*/false, afterKey, - /*inclusive, referring to the afterKey endpoint=*/false); - remainder = directories.tailMap(afterKey).entrySet().iterator(); - } - return new SubdirectoriesAndRemainder(subdirectories, remainder); - } - - private static String getKeyAfterAllPathsBelowDirectoryWithoutTrailingSlash( - String directoryWithoutTrailingSlash) { - // We calculate the least character greater than '/' (which happens to be '0'). We use this - // to collect all paths in subdirectories of the current one, using NavigableMap's subMap - // method, which takes an interval. - return directoryWithoutTrailingSlash + ((char) ('/' + 1)); - } - - private Set<String> getDirectoriesBelow(String directoryWithoutTrailingSlash) { - // If the empty string is passed in, collect all the directories. - if (directoryWithoutTrailingSlash.length() == 0) { - return orderedPatternsByDirectory.keySet(); - } - // Otherwise, collect the keyset belonging to the passed-in directory and its subdirectories. - String afterKey = getKeyAfterAllPathsBelowDirectoryWithoutTrailingSlash( - directoryWithoutTrailingSlash); - // Note that the subMap method defaults to interpreting the first, lesser, argument inclusively - // and the second, greater, argument exclusively. - return orderedPatternsByDirectory.subMap(getDirectoryKey(directoryWithoutTrailingSlash), - afterKey).keySet(); - } - - private static boolean isTargetsBelowDirectory(SignedPattern signedPattern) { - return signedPattern.getPattern().getType().equals(Type.TARGETS_BELOW_DIRECTORY); - } -} |