| // Copyright 2017 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.rules.config; |
| |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ImmutableSortedMap; |
| import com.google.common.hash.Hasher; |
| import com.google.common.hash.Hashing; |
| import com.google.devtools.build.lib.actions.ArtifactOwner; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
| import com.google.devtools.build.lib.analysis.config.BuildOptions; |
| import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment; |
| import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; |
| import com.google.devtools.build.lib.analysis.config.FragmentOptions; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; |
| import com.google.devtools.common.options.Converter; |
| import com.google.devtools.common.options.Option; |
| import com.google.devtools.common.options.OptionDocumentationCategory; |
| import com.google.devtools.common.options.OptionEffectTag; |
| import com.google.devtools.common.options.OptionMetadataTag; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.SortedMap; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Configuration fragment for Android's config_feature_flag, flags which can be defined in BUILD |
| * files. |
| */ |
| @AutoCodec |
| public final class ConfigFeatureFlagConfiguration extends BuildConfiguration.Fragment { |
| /** A converter used by the flag options which always returns an empty map, ignoring input. */ |
| public static final class EmptyImmutableSortedMapConverter |
| implements Converter<ImmutableSortedMap<Label, String>> { |
| @Override |
| public ImmutableSortedMap<Label, String> convert(String input) { |
| return ImmutableSortedMap.<Label, String>of(); |
| } |
| |
| @Override |
| public String getTypeDescription() { |
| return "n/a (do not set this on the command line)"; |
| } |
| } |
| |
| /** The options fragment which defines {@link ConfigFeatureFlagConfiguration}. */ |
| @AutoCodec(strategy = AutoCodec.Strategy.PUBLIC_FIELDS) |
| public static final class Options extends FragmentOptions { |
| /** The mapping from config_feature_flag rules to their values. */ |
| @Option( |
| name = "config_feature_flag values (private)", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| metadataTags = {OptionMetadataTag.INTERNAL}, |
| converter = EmptyImmutableSortedMapConverter.class, |
| defaultValue = "{}" |
| ) |
| public ImmutableSortedMap<Label, String> flagValues = ImmutableSortedMap.of(); |
| |
| /** Retrieves the set of flag-value pairs. */ |
| public ImmutableSortedMap<Label, String> getFlagValues() { |
| return this.flagValues; |
| } |
| |
| /** |
| * Replaces the set of flag-value pairs with the given mapping of flag-value pairs. |
| * |
| * <p>Flags not present in the new {@code flagValues} will return to being unset! To set flags |
| * while still retaining the values already set, call {@link #getFlagValues()} and build a map |
| * containing both the old values and the new ones. |
| */ |
| public void replaceFlagValues(Map<Label, String> flagValues) { |
| this.flagValues = ImmutableSortedMap.copyOf(flagValues); |
| } |
| } |
| |
| /** |
| * A configuration fragment loader able to create instances of {@link |
| * ConfigFeatureFlagConfiguration} from {@link ConfigFeatureFlagConfiguration.Options}. |
| */ |
| public static final class Loader implements ConfigurationFragmentFactory { |
| @Override |
| public BuildConfiguration.Fragment create( |
| ConfigurationEnvironment env, BuildOptions buildOptions) { |
| return new ConfigFeatureFlagConfiguration(buildOptions.get(Options.class)); |
| } |
| |
| @Override |
| public Class<? extends BuildConfiguration.Fragment> creates() { |
| return ConfigFeatureFlagConfiguration.class; |
| } |
| |
| @Override |
| public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() { |
| return ImmutableSet.<Class<? extends FragmentOptions>>of(Options.class); |
| } |
| } |
| |
| private final ImmutableSortedMap<Label, String> flagValues; |
| @Nullable private final String flagHash; |
| |
| /** Creates a new configuration fragment from the given {@link Options} fragment. */ |
| public ConfigFeatureFlagConfiguration(Options options) { |
| this(options.getFlagValues()); |
| } |
| |
| @AutoCodec.Instantiator |
| ConfigFeatureFlagConfiguration(ImmutableSortedMap<Label, String> flagValues) { |
| this.flagValues = flagValues; |
| this.flagHash = this.flagValues.isEmpty() ? null : hashFlags(this.flagValues); |
| } |
| |
| /** Converts the given flag values into a string hash for use as an output directory fragment. */ |
| private static String hashFlags(SortedMap<Label, String> flagValues) { |
| // This hash function is relatively fast and stable between JVM invocations. |
| Hasher hasher = Hashing.murmur3_128().newHasher(); |
| |
| for (Map.Entry<Label, String> flag : flagValues.entrySet()) { |
| hasher.putUnencodedChars(flag.getKey().toString()); |
| hasher.putByte((byte) 0); |
| hasher.putUnencodedChars(flag.getValue()); |
| hasher.putByte((byte) 0); |
| } |
| return hasher.hash().toString(); |
| } |
| |
| /** |
| * Retrieves the value of a configuration flag. |
| * |
| * <p>If the flag is not set in the current configuration, then the returned value will be absent. |
| * |
| * <p>This method should only be used by the rule whose label is passed here. Other rules should |
| * depend on that rule and read a provider exported by it. To encourage callers of this method to |
| * do the right thing, this class takes {@link ArtifactOwner} instead of {@link Label}; to get the |
| * ArtifactOwner for a rule, call {@code ruleContext.getOwner()}. |
| */ |
| public Optional<String> getFeatureFlagValue(ArtifactOwner owner) { |
| return Optional.ofNullable(flagValues.get(owner.getLabel())); |
| } |
| |
| /** |
| * Returns a fragment of the output directory name for this configuration, based on the set of |
| * flags and their values. It will be {@code null} if no flags are set. |
| */ |
| @Nullable |
| @Override |
| public String getOutputDirectoryName() { |
| return flagHash; |
| } |
| } |