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

Java tutorial

Introduction

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

Source

// Copyright 2018 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.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.FragmentClassSet;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * A value referring to a set of build configuration keys in order to reconstruct the
 * legacy {@link BuildConfigurationCollection} as well as a set of top level configured target keys
 * that are subsequently requested to trigger the analysis phase.
 *
 * <p>The public interface returns {@link BuildConfigurationCollection} and {@link
 * TargetAndConfiguration} even though these are not internally stored - the construction of these
 * objects requires additional Skyframe calls. The intention is that these are temporary until a
 * larger fraction of the code has been ported to Skyframe, at which point we'll use the internal
 * representation.
 */
@Immutable
@ThreadSafe
@AutoCodec
public final class PrepareAnalysisPhaseValue implements SkyValue {
    private final BuildConfigurationValue.Key hostConfigurationKey;
    private final ImmutableList<BuildConfigurationValue.Key> targetConfigurationKeys;
    private final ImmutableList<ConfiguredTargetKey> topLevelCtKeys;

    PrepareAnalysisPhaseValue(BuildConfigurationValue.Key hostConfigurationKey,
            ImmutableList<BuildConfigurationValue.Key> targetConfigurationKeys,
            ImmutableList<ConfiguredTargetKey> topLevelCtKeys) {
        this.hostConfigurationKey = Preconditions.checkNotNull(hostConfigurationKey);
        this.targetConfigurationKeys = Preconditions.checkNotNull(targetConfigurationKeys);
        this.topLevelCtKeys = Preconditions.checkNotNull(topLevelCtKeys);
    }

    /**
     * Returns the legacy {@link BuildConfigurationCollection}. Note that this performs additional
     * Skyframe calls, which may be expensive.
     */
    public BuildConfigurationCollection getConfigurations(ExtendedEventHandler eventHandler,
            SkyframeExecutor skyframeExecutor) throws InvalidConfigurationException {
        BuildConfiguration hostConfiguration = skyframeExecutor.getConfiguration(eventHandler,
                hostConfigurationKey);
        Collection<BuildConfiguration> targetConfigurations = skyframeExecutor
                .getConfigurations(eventHandler, targetConfigurationKeys).values();
        return new BuildConfigurationCollection(targetConfigurations, hostConfiguration);
    }

    /**
     * Returns the intended top-level targets and configurations for the build. Note that this
     * performs additional Skyframe calls for the involved configurations and targets, which may be
     * expensive.
     */
    public Collection<TargetAndConfiguration> getTopLevelCts(ExtendedEventHandler eventHandler,
            SkyframeExecutor skyframeExecutor) {
        List<TargetAndConfiguration> result = new ArrayList<>();
        Map<BuildConfigurationValue.Key, BuildConfiguration> configs = skyframeExecutor
                .getConfigurations(eventHandler, topLevelCtKeys.stream().map(ctk -> ctk.getConfigurationKey())
                        .filter(Predicates.notNull()).collect(Collectors.toSet()));

        // TODO(ulfjack): This performs one Skyframe call per top-level target. This is not a
        // regression, but we should fix it nevertheless, either by doing a bulk lookup call or by
        // migrating the consumers of these to Skyframe so they can directly request the values.
        for (ConfiguredTargetKey key : topLevelCtKeys) {
            Target target;
            try {
                target = skyframeExecutor.getPackageManager().getTarget(eventHandler, key.getLabel());
            } catch (NoSuchPackageException | NoSuchTargetException | InterruptedException e) {
                throw new RuntimeException("Failed to get package from TargetPatternPhaseValue", e);
            }
            BuildConfiguration config = key.getConfigurationKey() == null ? null
                    : configs.get(key.getConfigurationKey());
            result.add(new TargetAndConfiguration(target, config));
        }
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof PrepareAnalysisPhaseValue)) {
            return false;
        }
        PrepareAnalysisPhaseValue that = (PrepareAnalysisPhaseValue) obj;
        return this.hostConfigurationKey.equals(that.hostConfigurationKey)
                && this.targetConfigurationKeys.equals(that.targetConfigurationKeys)
                && this.topLevelCtKeys.equals(that.topLevelCtKeys);
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.hostConfigurationKey, this.targetConfigurationKeys, this.topLevelCtKeys);
    }

    /** Create a prepare analysis phase key. */
    @ThreadSafe
    public static SkyKey key(FragmentClassSet fragments, BuildOptions.OptionsDiffForReconstruction optionsDiff,
            Set<String> multiCpu, Collection<Label> labels) {
        return new PrepareAnalysisPhaseKey(fragments, optionsDiff, multiCpu, labels);
    }

    /** The configuration needed to prepare the analysis phase. */
    @ThreadSafe
    @VisibleForSerialization
    @AutoCodec
    public static final class PrepareAnalysisPhaseKey implements SkyKey, Serializable {
        private final FragmentClassSet fragments;
        private final BuildOptions.OptionsDiffForReconstruction optionsDiff;
        private final ImmutableSortedSet<String> multiCpu;
        private final ImmutableSet<Label> labels;

        PrepareAnalysisPhaseKey(FragmentClassSet fragments, BuildOptions.OptionsDiffForReconstruction optionsDiff,
                Set<String> multiCpu, Collection<Label> labels) {
            this.fragments = Preconditions.checkNotNull(fragments);
            this.optionsDiff = Preconditions.checkNotNull(optionsDiff);
            this.multiCpu = ImmutableSortedSet.copyOf(multiCpu);
            this.labels = ImmutableSet.copyOf(labels);
        }

        @Override
        public SkyFunctionName functionName() {
            return SkyFunctions.PREPARE_ANALYSIS_PHASE;
        }

        public FragmentClassSet getFragments() {
            return fragments;
        }

        public BuildOptions.OptionsDiffForReconstruction getOptionsDiff() {
            return optionsDiff;
        }

        public ImmutableSortedSet<String> getMultiCpu() {
            return multiCpu;
        }

        public ImmutableSet<Label> getLabels() {
            return labels;
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(PrepareAnalysisPhaseKey.class).add("fragments", fragments)
                    .add("optionsDiff", optionsDiff).add("multiCpu", multiCpu).add("labels", labels).toString();
        }

        @Override
        public int hashCode() {
            return Objects.hash(fragments, optionsDiff, multiCpu, labels);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (!(obj instanceof PrepareAnalysisPhaseKey)) {
                return false;
            }
            PrepareAnalysisPhaseKey other = (PrepareAnalysisPhaseKey) obj;
            return other.fragments.equals(this.fragments) && other.optionsDiff.equals(this.optionsDiff)
                    && other.multiCpu.equals(multiCpu) && other.labels.equals(labels);
        }
    }
}