| // 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.analysis.config; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Objects; |
| import com.google.common.collect.ArrayListMultimap; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ListMultimap; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.flags.InvocationPolicyEnforcer; |
| import com.google.devtools.build.lib.packages.Attribute.SplitTransition; |
| import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy; |
| import com.google.devtools.build.lib.util.Preconditions; |
| import com.google.devtools.common.options.OptionsBase; |
| import com.google.devtools.common.options.OptionsClassProvider; |
| import com.google.devtools.common.options.OptionsParser; |
| import com.google.devtools.common.options.OptionsParsingException; |
| |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * Stores the command-line options from a set of configuration fragments. |
| */ |
| public final class BuildOptions implements Cloneable, Serializable { |
| /** |
| * Creates a BuildOptions object with all options set to their default values, processed by the |
| * given {@code invocationPolicy}. |
| */ |
| static BuildOptions createDefaults( |
| Iterable<Class<? extends FragmentOptions>> options, InvocationPolicy invocationPolicy) { |
| return of(options, createDefaultParser(options, invocationPolicy)); |
| } |
| |
| private static OptionsParser createDefaultParser( |
| Iterable<Class<? extends FragmentOptions>> options, InvocationPolicy invocationPolicy) { |
| OptionsParser optionsParser = OptionsParser.newOptionsParser(options); |
| try { |
| new InvocationPolicyEnforcer(invocationPolicy).enforce(optionsParser); |
| } catch (OptionsParsingException e) { |
| throw new IllegalStateException(e); |
| } |
| return optionsParser; |
| } |
| |
| /** |
| * Creates a new BuildOptions instance for host. |
| * |
| * @param fallback if true, we have already tried the user specified hostCpu options |
| * and it didn't work, so now we try the default options instead. |
| */ |
| public BuildOptions createHostOptions(boolean fallback) { |
| Builder builder = builder(); |
| for (FragmentOptions options : fragmentOptionsMap.values()) { |
| builder.add(options.getHost(fallback)); |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Returns a list of potential split configuration transitions by calling {@link |
| * FragmentOptions#getPotentialSplitTransitions} on all the fragments. |
| */ |
| public List<SplitTransition<BuildOptions>> getPotentialSplitTransitions() { |
| List<SplitTransition<BuildOptions>> result = new ArrayList<>(); |
| for (FragmentOptions options : fragmentOptionsMap.values()) { |
| result.addAll(options.getPotentialSplitTransitions()); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns an equivalent instance to this one with only options from the given |
| * {@link FragmentOptions} classes. |
| */ |
| public BuildOptions trim(Set<Class<? extends FragmentOptions>> optionsClasses) { |
| Builder builder = builder(); |
| for (FragmentOptions options : fragmentOptionsMap.values()) { |
| if (optionsClasses.contains(options.getClass()) |
| || options instanceof BuildConfiguration.Options) { |
| builder.add(options); |
| } |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Creates a BuildOptions class by taking the option values from an options provider |
| * (eg. an OptionsParser). |
| */ |
| public static BuildOptions of( |
| Iterable<Class<? extends FragmentOptions>> optionsList, OptionsClassProvider provider) { |
| Builder builder = builder(); |
| for (Class<? extends FragmentOptions> optionsClass : optionsList) { |
| builder.add(provider.getOptions(optionsClass)); |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Creates a BuildOptions class by taking the option values from command-line arguments |
| */ |
| @VisibleForTesting |
| public static BuildOptions of(List<Class<? extends FragmentOptions>> optionsList, String... args) |
| throws OptionsParsingException { |
| return of(optionsList, null, args); |
| } |
| |
| /** |
| * Creates a BuildOptions class by taking the option values from command-line arguments and |
| * applying the specified original options. |
| */ |
| @VisibleForTesting |
| static BuildOptions of(List<Class<?extends FragmentOptions>> optionsList, |
| BuildOptions originalOptions, String... args) throws OptionsParsingException { |
| Builder builder = builder(); |
| OptionsParser parser = OptionsParser.newOptionsParser( |
| ImmutableList.<Class<? extends OptionsBase>>copyOf(optionsList)); |
| parser.parse(args); |
| for (Class<? extends FragmentOptions> optionsClass : optionsList) { |
| builder.add(parser.getOptions(optionsClass)); |
| } |
| builder.setOriginalOptions(originalOptions); |
| return builder.build(); |
| } |
| |
| /** |
| * Returns the actual instance of a FragmentOptions class. |
| */ |
| public <T extends FragmentOptions> T get(Class<T> optionsClass) { |
| FragmentOptions options = fragmentOptionsMap.get(optionsClass); |
| Preconditions.checkNotNull(options, "fragment options unavailable: " + optionsClass.getName()); |
| return optionsClass.cast(options); |
| } |
| |
| /** |
| * Returns true if these options contain the given {@link FragmentOptions}. |
| */ |
| public boolean contains(Class<? extends FragmentOptions> optionsClass) { |
| return fragmentOptionsMap.containsKey(optionsClass); |
| } |
| |
| /** |
| * Returns a multimap of all labels that were specified as options, keyed by the name to be |
| * displayed to the user if something goes wrong. This should be the set of all labels |
| * mentioned in explicit command line options that are not already covered by the |
| * tools/defaults package (see the DefaultsPackage class), and nothing else. |
| */ |
| public ListMultimap<String, Label> getAllLabels() { |
| ListMultimap<String, Label> labels = ArrayListMultimap.create(); |
| for (FragmentOptions optionsBase : fragmentOptionsMap.values()) { |
| optionsBase.addAllLabels(labels); |
| } |
| return labels; |
| } |
| |
| // It would be very convenient to use a Multimap here, but we cannot do that because we need to |
| // support defaults labels that have zero elements. |
| ImmutableMap<String, ImmutableSet<Label>> getDefaultsLabels() { |
| BuildConfiguration.Options opts = get(BuildConfiguration.Options.class); |
| Map<String, Set<Label>> collector = new TreeMap<>(); |
| for (FragmentOptions fragment : fragmentOptionsMap.values()) { |
| for (Map.Entry<String, Set<Label>> entry : fragment.getDefaultsLabels(opts).entrySet()) { |
| if (!collector.containsKey(entry.getKey())) { |
| collector.put(entry.getKey(), new TreeSet<Label>()); |
| } |
| collector.get(entry.getKey()).addAll(entry.getValue()); |
| } |
| } |
| |
| ImmutableMap.Builder<String, ImmutableSet<Label>> result = new ImmutableMap.Builder<>(); |
| for (Map.Entry<String, Set<Label>> entry : collector.entrySet()) { |
| result.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue())); |
| } |
| |
| return result.build(); |
| } |
| |
| ImmutableList<String> getDefaultsRules() { |
| ImmutableList.Builder<String> result = ImmutableList.builder(); |
| for (FragmentOptions fragment : fragmentOptionsMap.values()) { |
| result.addAll(fragment.getDefaultsRules()); |
| } |
| |
| return result.build(); |
| } |
| |
| /** |
| * The cache key for the options collection. Recomputes cache key every time it's called. |
| */ |
| public String computeCacheKey() { |
| StringBuilder keyBuilder = new StringBuilder(); |
| for (FragmentOptions options : fragmentOptionsMap.values()) { |
| keyBuilder.append(options.cacheKey()); |
| } |
| return keyBuilder.toString(); |
| } |
| |
| /** |
| * String representation of build options. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder stringBuilder = new StringBuilder(); |
| for (FragmentOptions options : fragmentOptionsMap.values()) { |
| stringBuilder.append(options); |
| } |
| return stringBuilder.toString(); |
| } |
| |
| /** |
| * Returns the options contained in this collection. |
| */ |
| public Collection<FragmentOptions> getOptions() { |
| return fragmentOptionsMap.values(); |
| } |
| |
| /** |
| * Creates a copy of the BuildOptions object that contains copies of the FragmentOptions. |
| */ |
| @Override |
| public BuildOptions clone() { |
| return clone(null); |
| } |
| |
| /** |
| * Creates a copy of the BuildOptions object that stores a set of original options. This can |
| * be used to power "reversion" of options changes. |
| */ |
| public BuildOptions clone(@Nullable BuildOptions originalOptions) { |
| ImmutableMap.Builder<Class<? extends FragmentOptions>, FragmentOptions> builder = |
| ImmutableMap.builder(); |
| for (Map.Entry<Class<? extends FragmentOptions>, FragmentOptions> entry : |
| fragmentOptionsMap.entrySet()) { |
| builder.put(entry.getKey(), entry.getValue().clone()); |
| } |
| // TODO(bazel-team): only store the diff between the current options and its original |
| // options. This may be easier with immutable options. |
| return new BuildOptions(builder.build(), originalOptions); |
| } |
| |
| /** |
| * Returns the original options these options were spawned from, or null if this info wasn't |
| * recorded. |
| */ |
| public BuildOptions getOriginal() { |
| return originalOptions; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) { |
| return true; |
| } else if (!(other instanceof BuildOptions)) { |
| return false; |
| } else { |
| BuildOptions otherOptions = (BuildOptions) other; |
| return fragmentOptionsMap.equals(otherOptions.fragmentOptionsMap) |
| && Objects.equal(originalOptions, otherOptions.originalOptions); |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return fragmentOptionsMap.hashCode(); |
| } |
| |
| /** |
| * Maps options class definitions to FragmentOptions objects |
| */ |
| private final ImmutableMap<Class<? extends FragmentOptions>, FragmentOptions> fragmentOptionsMap; |
| |
| /** |
| * Records an original set of options these options came from. When set, this |
| * provides the ability to "revert" options back to a previous form. |
| */ |
| @Nullable private final BuildOptions originalOptions; |
| |
| private BuildOptions( |
| ImmutableMap<Class<? extends FragmentOptions>, FragmentOptions> fragmentOptionsMap, |
| BuildOptions originalOptions) { |
| this.fragmentOptionsMap = fragmentOptionsMap; |
| this.originalOptions = originalOptions; |
| } |
| |
| /** |
| * Creates a builder object for BuildOptions |
| */ |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| /** |
| * Builder class for BuildOptions. |
| */ |
| public static class Builder { |
| /** |
| * Adds a new FragmentOptions instance to the builder. Overrides previous instances of the |
| * exact same subclass of FragmentOptions. |
| */ |
| public <T extends FragmentOptions> Builder add(T options) { |
| builderMap.put(options.getClass(), options); |
| return this; |
| } |
| |
| /** |
| * Specify the original options these options were branched from. This should only be used |
| * when there's a desire to revert back to the old options, e.g. for a parent transition. |
| */ |
| public Builder setOriginalOptions(BuildOptions originalOptions) { |
| this.originalOptions = originalOptions; |
| return this; |
| } |
| |
| public BuildOptions build() { |
| return new BuildOptions(ImmutableMap.copyOf(builderMap), originalOptions); |
| } |
| |
| private Map<Class<? extends FragmentOptions>, FragmentOptions> builderMap; |
| @Nullable private BuildOptions originalOptions; |
| |
| private Builder() { |
| builderMap = new HashMap<>(); |
| } |
| } |
| } |