com.google.devtools.build.lib.skyframe.PrepareDepsOfPatternValue.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.skyframe.PrepareDepsOfPatternValue.java

Source

// 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.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 SkyKey.create(SkyFunctions.PREPARE_DEPS_OF_PATTERN, targetPatternKey);
        }

        @Override
        public String getOriginalPattern() {
            return targetPatternKey.getPattern();
        }
    }
}