|  | // 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.Preconditions; | 
|  | import com.google.common.collect.Interner; | 
|  | import com.google.devtools.build.lib.actions.ActionLookupValue.ActionLookupKey; | 
|  | import com.google.devtools.build.lib.analysis.AliasProvider; | 
|  | import com.google.devtools.build.lib.analysis.ConfiguredTarget; | 
|  | import com.google.devtools.build.lib.analysis.config.BuildConfiguration; | 
|  | import com.google.devtools.build.lib.cmdline.Label; | 
|  | import com.google.devtools.build.lib.concurrent.BlazeInterners; | 
|  | import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; | 
|  | import com.google.devtools.build.skyframe.SkyFunctionName; | 
|  | import java.util.Objects; | 
|  | import javax.annotation.Nullable; | 
|  |  | 
|  | /** | 
|  | * A (Label, Configuration key) pair. Note that this pair may be used to look up the generating | 
|  | * action of an artifact. | 
|  | */ | 
|  | @AutoCodec | 
|  | public class ConfiguredTargetKey extends ActionLookupKey { | 
|  | private final Label label; | 
|  | @Nullable private final BuildConfigurationValue.Key configurationKey; | 
|  |  | 
|  | private transient int hashCode; | 
|  |  | 
|  | private ConfiguredTargetKey(Label label, @Nullable BuildConfigurationValue.Key configurationKey) { | 
|  | this.label = Preconditions.checkNotNull(label); | 
|  | this.configurationKey = configurationKey; | 
|  | } | 
|  |  | 
|  | private static Label getLabel(ConfiguredTarget configuredTarget) { | 
|  | AliasProvider aliasProvider = configuredTarget.getProvider(AliasProvider.class); | 
|  | return aliasProvider != null | 
|  | ? aliasProvider.getAliasChain().get(0) | 
|  | : configuredTarget.getLabel(); | 
|  | } | 
|  |  | 
|  | public static ConfiguredTargetKey of( | 
|  | ConfiguredTarget configuredTarget, BuildConfiguration buildConfiguration) { | 
|  | return of(getLabel(configuredTarget), buildConfiguration); | 
|  | } | 
|  |  | 
|  | public static ConfiguredTargetKey of( | 
|  | ConfiguredTarget configuredTarget, | 
|  | BuildConfigurationValue.Key configurationKey, | 
|  | boolean isHostConfiguration) { | 
|  | return of(getLabel(configuredTarget), configurationKey, isHostConfiguration); | 
|  | } | 
|  |  | 
|  | public static ConfiguredTargetKey inTargetConfig(ConfiguredTarget configuredTarget) { | 
|  | return of( | 
|  | getLabel(configuredTarget), | 
|  | configuredTarget.getConfigurationKey(), | 
|  | /*isHostConfiguration=*/ false); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Caches so that the number of ConfiguredTargetKey instances is {@code O(configured targets)} and | 
|  | * not {@code O(edges between configured targets)}. | 
|  | */ | 
|  | private static final Interner<ConfiguredTargetKey> interner = BlazeInterners.newWeakInterner(); | 
|  | private static final Interner<HostConfiguredTargetKey> hostInterner = | 
|  | BlazeInterners.newWeakInterner(); | 
|  |  | 
|  | public static ConfiguredTargetKey of(Label label, @Nullable BuildConfiguration configuration) { | 
|  | KeyAndHost keyAndHost = keyFromConfiguration(configuration); | 
|  | return of(label, keyAndHost.key, keyAndHost.isHost); | 
|  | } | 
|  |  | 
|  | @AutoCodec.Instantiator | 
|  | public static ConfiguredTargetKey of( | 
|  | Label label, | 
|  | @Nullable BuildConfigurationValue.Key configurationKey, | 
|  | boolean isHostConfiguration) { | 
|  | if (isHostConfiguration) { | 
|  | return hostInterner.intern(new HostConfiguredTargetKey(label, configurationKey)); | 
|  | } else { | 
|  | return interner.intern(new ConfiguredTargetKey(label, configurationKey)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static KeyAndHost keyFromConfiguration(@Nullable BuildConfiguration configuration) { | 
|  | return configuration == null | 
|  | ? KeyAndHost.NULL_INSTANCE | 
|  | : new KeyAndHost( | 
|  | BuildConfigurationValue.key(configuration), configuration.isHostConfiguration()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Label getLabel() { | 
|  | return label; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SkyFunctionName functionName() { | 
|  | return SkyFunctions.CONFIGURED_TARGET; | 
|  | } | 
|  |  | 
|  | @Nullable | 
|  | BuildConfigurationValue.Key getConfigurationKey() { | 
|  | return configurationKey; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | // We use the hash code caching strategy employed by java.lang.String. There are three subtle | 
|  | // things going on here: | 
|  | // | 
|  | // (1) We use a value of 0 to indicate that the hash code hasn't been computed and cached yet. | 
|  | // Yes, this means that if the hash code is really 0 then we will "recompute" it each time. But | 
|  | // this isn't a problem in practice since a hash code of 0 should be rare. | 
|  | // | 
|  | // (2) Since we have no synchronization, multiple threads can race here thinking there are the | 
|  | // first one to compute and cache the hash code. | 
|  | // | 
|  | // (3) Moreover, since 'hashCode' is non-volatile, the cached hash code value written from one | 
|  | // thread may not be visible by another. | 
|  | // | 
|  | // All three of these issues are benign from a correctness perspective; in the end we have no | 
|  | // overhead from synchronization, at the cost of potentially computing the hash code more than | 
|  | // once. | 
|  | int h = hashCode; | 
|  | if (h == 0) { | 
|  | h = computeHashCode(); | 
|  | hashCode = h; | 
|  | } | 
|  | return h; | 
|  | } | 
|  |  | 
|  | private int computeHashCode() { | 
|  | int configVal = configurationKey == null ? 79 : configurationKey.hashCode(); | 
|  | return 31 * label.hashCode() + configVal + (isHostConfiguration() ? 41 : 0); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object obj) { | 
|  | if (this == obj) { | 
|  | return true; | 
|  | } | 
|  | if (obj == null) { | 
|  | return false; | 
|  | } | 
|  | if (!(obj instanceof ConfiguredTargetKey)) { | 
|  | return false; | 
|  | } | 
|  | ConfiguredTargetKey other = (ConfiguredTargetKey) obj; | 
|  | return this.isHostConfiguration() == other.isHostConfiguration() | 
|  | && Objects.equals(label, other.label) | 
|  | && Objects.equals(configurationKey, other.configurationKey); | 
|  | } | 
|  |  | 
|  | public boolean isHostConfiguration() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public String prettyPrint() { | 
|  | if (label == null) { | 
|  | return "null"; | 
|  | } | 
|  | return isHostConfiguration() ? (label + " (host)") : label.toString(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return String.format("%s %s %s", label, configurationKey, isHostConfiguration()); | 
|  | } | 
|  |  | 
|  | static class HostConfiguredTargetKey extends ConfiguredTargetKey { | 
|  | private HostConfiguredTargetKey( | 
|  | Label label, @Nullable BuildConfigurationValue.Key configurationKey) { | 
|  | super(label, configurationKey); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isHostConfiguration() { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Simple wrapper class for turning a {@link BuildConfiguration} into a {@link | 
|  | * BuildConfigurationValue.Key} and boolean isHost. | 
|  | */ | 
|  | public static class KeyAndHost { | 
|  | private static final KeyAndHost NULL_INSTANCE = new KeyAndHost(null, false); | 
|  |  | 
|  | @Nullable public final BuildConfigurationValue.Key key; | 
|  | final boolean isHost; | 
|  |  | 
|  | private KeyAndHost(@Nullable BuildConfigurationValue.Key key, boolean isHost) { | 
|  | this.key = key; | 
|  | this.isHost = isHost; | 
|  | } | 
|  | } | 
|  | } |