diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationCollection.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationCollection.java | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationCollection.java new file mode 100644 index 0000000000..e36a6817bc --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationCollection.java @@ -0,0 +1,276 @@ +// Copyright 2014 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.analysis.config; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ListMultimap; +import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.Attribute.SplitTransition; +import com.google.devtools.build.lib.packages.Attribute.Transition; +import com.google.devtools.build.lib.packages.InputFile; +import com.google.devtools.build.lib.packages.PackageGroup; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.Target; + +import java.io.PrintStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * The primary container for all main {@link BuildConfiguration} instances, + * currently "target", "data", and "host". + * + * <p>The target configuration is used for all targets specified on the command + * line. Data dependencies of targets in the target configuration use the data + * configuration instead. + * + * <p>The host configuration is used for tools that are executed during the + * build, e. g, compilers. + * + * <p>The "related" configurations are also contained in this class. + */ +@ThreadSafe +public final class BuildConfigurationCollection implements Serializable { + private final ImmutableList<BuildConfiguration> targetConfigurations; + + public BuildConfigurationCollection(List<BuildConfiguration> targetConfigurations) + throws InvalidConfigurationException { + this.targetConfigurations = ImmutableList.copyOf(targetConfigurations); + + // Except for the host configuration (which may be identical across target configs), the other + // configurations must all have different cache keys or we will end up with problems. + HashMap<String, BuildConfiguration> cacheKeyConflictDetector = new HashMap<>(); + for (BuildConfiguration config : getAllConfigurations()) { + if (cacheKeyConflictDetector.containsKey(config.cacheKey())) { + throw new InvalidConfigurationException("Conflicting configurations: " + config + " & " + + cacheKeyConflictDetector.get(config.cacheKey())); + } + cacheKeyConflictDetector.put(config.cacheKey(), config); + } + } + + /** + * Creates an empty configuration collection which will return null for everything. + */ + public BuildConfigurationCollection() { + this.targetConfigurations = ImmutableList.of(); + } + + public static BuildConfiguration configureTopLevelTarget(BuildConfiguration topLevelConfiguration, + Target toTarget) { + if (toTarget instanceof InputFile || toTarget instanceof PackageGroup) { + return null; + } + return topLevelConfiguration.getTransitions().toplevelConfigurationHook(toTarget); + } + + public ImmutableList<BuildConfiguration> getTargetConfigurations() { + return targetConfigurations; + } + + /** + * Returns all configurations that can be reached from the target configuration through any kind + * of configuration transition. + */ + public Collection<BuildConfiguration> getAllConfigurations() { + Set<BuildConfiguration> result = new LinkedHashSet<>(); + for (BuildConfiguration config : targetConfigurations) { + result.addAll(config.getAllReachableConfigurations()); + } + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof BuildConfigurationCollection)) { + return false; + } + BuildConfigurationCollection that = (BuildConfigurationCollection) obj; + return this.targetConfigurations.equals(that.targetConfigurations); + } + + @Override + public int hashCode() { + return targetConfigurations.hashCode(); + } + + /** + * Prints the configuration graph in dot format to the given print stream. This is only intended + * for debugging. + */ + public void dumpAsDotGraph(PrintStream out) { + out.println("digraph g {"); + out.println(" ratio = 0.3;"); + for (BuildConfiguration config : getAllConfigurations()) { + String from = config.shortCacheKey(); + for (Map.Entry<? extends Transition, ConfigurationHolder> entry : + config.getTransitions().getTransitionTable().entrySet()) { + BuildConfiguration toConfig = entry.getValue().getConfiguration(); + if (toConfig == config) { + continue; + } + String to = toConfig == null ? "ERROR" : toConfig.shortCacheKey(); + out.println(" \"" + from + "\" -> \"" + to + "\" [label=\"" + entry.getKey() + "\"]"); + } + } + out.println("}"); + } + + /** + * The outgoing transitions for a build configuration. + */ + public static class Transitions implements Serializable { + protected final BuildConfiguration configuration; + + /** + * Look up table for the configuration transitions, i.e., HOST, DATA, etc. + */ + private final Map<? extends Transition, ConfigurationHolder> transitionTable; + + // TODO(bazel-team): Consider merging transitionTable into this. + private final ListMultimap<? super SplitTransition<?>, BuildConfiguration> splitTransitionTable; + + public Transitions(BuildConfiguration configuration, + Map<? extends Transition, ConfigurationHolder> transitionTable, + ListMultimap<? extends SplitTransition<?>, BuildConfiguration> splitTransitionTable) { + this.configuration = configuration; + this.transitionTable = ImmutableMap.copyOf(transitionTable); + this.splitTransitionTable = ImmutableListMultimap.copyOf(splitTransitionTable); + } + + public Transitions(BuildConfiguration configuration, + Map<? extends Transition, ConfigurationHolder> transitionTable) { + this(configuration, transitionTable, + ImmutableListMultimap.<SplitTransition<?>, BuildConfiguration>of()); + } + + public Map<? extends Transition, ConfigurationHolder> getTransitionTable() { + return transitionTable; + } + + public ListMultimap<? super SplitTransition<?>, BuildConfiguration> getSplitTransitionTable() { + return splitTransitionTable; + } + + public List<BuildConfiguration> getSplitConfigurations(SplitTransition<?> transition) { + if (splitTransitionTable.containsKey(transition)) { + return splitTransitionTable.get(transition); + } else { + Preconditions.checkState(transition.defaultsToSelf()); + return ImmutableList.of(configuration); + } + } + + /** + * Adds all configurations that are directly reachable from this configuration through + * any kind of configuration transition. + */ + public void addDirectlyReachableConfigurations(Collection<BuildConfiguration> queue) { + for (ConfigurationHolder holder : transitionTable.values()) { + if (holder.configuration != null) { + queue.add(holder.configuration); + } + } + queue.addAll(splitTransitionTable.values()); + } + + /** + * Artifacts need an owner in Skyframe. By default it's the same configuration as what + * the configured target has, but it can be overridden if necessary. + * + * @return the artifact owner configuration + */ + public BuildConfiguration getArtifactOwnerConfiguration() { + return configuration; + } + + /** + * Returns the new configuration after traversing a dependency edge with a + * given configuration transition. + * + * @param configurationTransition the configuration transition + * @return the new configuration + */ + public BuildConfiguration getConfiguration(Transition configurationTransition) { + ConfigurationHolder holder = transitionTable.get(configurationTransition); + if (holder == null && configurationTransition.defaultsToSelf()) { + return configuration; + } + return holder.configuration; + } + + /** + * Arbitrary configuration transitions can be implemented by overriding this hook. + */ + @SuppressWarnings("unused") + public BuildConfiguration configurationHook(Rule fromTarget, + Attribute attribute, Target toTarget, BuildConfiguration toConfiguration) { + return toConfiguration; + } + + /** + * Associating configurations to top-level targets can be implemented by overriding this hook. + */ + @SuppressWarnings("unused") + public BuildConfiguration toplevelConfigurationHook(Target toTarget) { + return configuration; + } + } + + /** + * A holder class for {@link BuildConfiguration} instances that allows {@code null} values, + * because none of the Table implementations allow them. + */ + public static final class ConfigurationHolder implements Serializable { + private final BuildConfiguration configuration; + + public ConfigurationHolder(BuildConfiguration configuration) { + this.configuration = configuration; + } + + public BuildConfiguration getConfiguration() { + return configuration; + } + + @Override + public int hashCode() { + return configuration == null ? 0 : configuration.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ConfigurationHolder)) { + return false; + } + return Objects.equals(configuration, ((ConfigurationHolder) o).configuration); + } + } +} |