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

Java tutorial

Introduction

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

Source

// 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.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
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.ConfigurationFactory;
import com.google.devtools.build.lib.analysis.config.HostTransition;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.config.PackageProviderForConfigurations;
import com.google.devtools.build.lib.events.ErrorSensingEventHandler;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.skyframe.ConfigurationCollectionValue.ConfigurationCollectionKey;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

/**
 * A builder for {@link ConfigurationCollectionValue} instances.
 */
public class ConfigurationCollectionFunction implements SkyFunction {

    private final Supplier<ConfigurationFactory> configurationFactory;
    private final RuleClassProvider ruleClassProvider;

    public ConfigurationCollectionFunction(Supplier<ConfigurationFactory> configurationFactory,
            RuleClassProvider ruleClassProvider) {
        this.configurationFactory = configurationFactory;
        this.ruleClassProvider = ruleClassProvider;
    }

    @Override
    public SkyValue compute(SkyKey skyKey, Environment env)
            throws InterruptedException, ConfigurationCollectionFunctionException {
        ConfigurationCollectionKey collectionKey = (ConfigurationCollectionKey) skyKey.argument();
        try {
            BuildConfigurationCollection result = getConfigurations(env,
                    new SkyframePackageLoaderWithValueEnvironment(env, ruleClassProvider),
                    collectionKey.getBuildOptions(), collectionKey.getMultiCpu());

            // BuildConfigurationCollection can be created, but dependencies to some files might be
            // missing. In that case we need to build configurationCollection again.
            if (env.valuesMissing()) {
                return null;
            }

            return new ConfigurationCollectionValue(result);
        } catch (InvalidConfigurationException e) {
            throw new ConfigurationCollectionFunctionException(e);
        }
    }

    /** Create the build configurations with the given options. */
    private BuildConfigurationCollection getConfigurations(Environment env,
            PackageProviderForConfigurations loadedPackageProvider, BuildOptions buildOptions,
            ImmutableSet<String> multiCpu) throws InvalidConfigurationException, InterruptedException {
        // We cache all the related configurations for this target configuration in a cache that is
        // dropped at the end of this method call. We instead rely on the cache for entire collections
        // for caching the target and related configurations, and on a dedicated host configuration
        // cache for the host configuration.
        Cache<String, BuildConfiguration> cache = CacheBuilder.newBuilder().<String, BuildConfiguration>build();
        List<BuildConfiguration> targetConfigurations = new ArrayList<>();

        if (!multiCpu.isEmpty()) {
            for (String cpu : multiCpu) {
                BuildConfiguration targetConfiguration = createConfiguration(cache, env.getListener(),
                        loadedPackageProvider, buildOptions, cpu);
                if (targetConfiguration == null || targetConfigurations.contains(targetConfiguration)) {
                    continue;
                }
                targetConfigurations.add(targetConfiguration);
            }
            if (loadedPackageProvider.valuesMissing()) {
                return null;
            }
        } else {
            BuildConfiguration targetConfiguration = createConfiguration(cache, env.getListener(),
                    loadedPackageProvider, buildOptions, null);
            if (targetConfiguration == null) {
                return null;
            }
            targetConfigurations.add(targetConfiguration);
        }
        BuildConfiguration hostConfiguration = getHostConfiguration(env, targetConfigurations.get(0));
        if (hostConfiguration == null) {
            return null;
        }

        return new BuildConfigurationCollection(targetConfigurations, hostConfiguration);
    }

    /** Returns the host configuration, or null on missing Skyframe deps. */
    private BuildConfiguration getHostConfiguration(Environment env, BuildConfiguration targetConfiguration)
            throws InvalidConfigurationException, InterruptedException {
        if (targetConfiguration.useDynamicConfigurations()) {
            BuildOptions targetOptions = targetConfiguration.getOptions();
            BuildOptions hostOptions = targetOptions
                    .get(BuildConfiguration.Options.class).useDistinctHostConfiguration
                            ? HostTransition.INSTANCE.apply(targetConfiguration.getOptions())
                            : targetOptions;

            SkyKey hostConfigKey = BuildConfigurationValue
                    .key(targetConfiguration.trimConfigurations() ? targetConfiguration.fragmentClasses()
                            : ((ConfiguredRuleClassProvider) ruleClassProvider).getAllFragments(), hostOptions);
            BuildConfigurationValue skyValHost = (BuildConfigurationValue) env.getValueOrThrow(hostConfigKey,
                    InvalidConfigurationException.class);

            // Also preload the target configuration so the configured target functions for
            // top-level targets don't have to waste cycles from a missing Skyframe dep.
            SkyKey targetConfigKey = BuildConfigurationValue.key(targetConfiguration.fragmentClasses(),
                    targetOptions);
            BuildConfigurationValue skyValTarget = (BuildConfigurationValue) env.getValueOrThrow(targetConfigKey,
                    InvalidConfigurationException.class);

            if (skyValHost == null || skyValTarget == null) {
                return null;
            }
            return skyValHost.getConfiguration();
        } else {
            return targetConfiguration.getConfiguration(Attribute.ConfigurationTransition.HOST);
        }
    }

    @Nullable
    private BuildConfiguration createConfiguration(Cache<String, BuildConfiguration> cache,
            EventHandler originalEventListener, PackageProviderForConfigurations loadedPackageProvider,
            BuildOptions buildOptions, String cpuOverride)
            throws InvalidConfigurationException, InterruptedException {
        ErrorSensingEventHandler eventHandler = new ErrorSensingEventHandler(originalEventListener);
        if (cpuOverride != null) {
            // TODO(bazel-team): Options classes should be immutable. This is a bit of a hack.
            buildOptions = buildOptions.clone();
            buildOptions.get(BuildConfiguration.Options.class).cpu = cpuOverride;
            buildOptions.get(BuildConfiguration.Options.class).experimentalMultiCpuDistinguisher = cpuOverride;
        }

        BuildConfiguration targetConfig = configurationFactory.get().createConfigurations(cache,
                loadedPackageProvider, buildOptions, eventHandler);
        if (targetConfig == null) {
            return null;
        }
        // The ConfigurationFactory may report an error rather than throwing an exception to support
        // --keep_going. If so, we throw an error here.
        if (eventHandler.hasErrors()) {
            throw new InvalidConfigurationException("Build options are invalid");
        }
        return targetConfig;
    }

    @Override
    public String extractTag(SkyKey skyKey) {
        return null;
    }

    /**
     * Used to declare all the exception types that can be wrapped in the exception thrown by
     * {@link ConfigurationCollectionFunction#compute}.
     */
    private static final class ConfigurationCollectionFunctionException extends SkyFunctionException {
        public ConfigurationCollectionFunctionException(InvalidConfigurationException e) {
            super(e, Transience.PERSISTENT);
        }
    }
}