blob: 0457e202f580db953e6d9b6d8ae117a7e1720ee0 [file] [log] [blame]
// 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 static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Interner;
import com.google.devtools.build.lib.actions.ActionLookupKey;
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;
/**
* In simple form, a ({@link Label}, {@link BuildConfiguration}) pair used to trigger immediate
* dependency resolution and the rule analysis.
*
* <p>In practice, a ({@link Label}, canonical and post-transition {@link
* BuildConfigurationValue.Key}) pair plus a possible execution platform override {@link Label} with
* special constraints. To elaborate, in order of highest to lowest potential for concern:
*
* <p>1. The {@link BuildConfigurationValue.Key} must be post-transition and thus ready for
* immediate use in dependency resolution and analysis. In practice, this means that if the rule has
* an incoming-edge transition (cfg in {@link RuleClass}) or there are global trimming transitions,
* THOSE TRANSITIONS MUST ALREADY BE DONE before creating the key. Failure to do so will lead to
* build graphs with ConfiguredTarget that have seemingly impossible {@link BuildConfiguration} (due
* to the skipped transitions).
*
* <p>2. The {@link BuildConfigurationValue.Key} must be canonical. Multiple keys can correspond to
* the same {@link BuildConfiguration}. The canonical key is the one returned by {@link
* BuildConfigurationValue.key}. Failure to use a canonical key can result in build graphs with
* multiple seemingly-identical ConfiguredTarget that have the same ({@link Label}, {@link
* BuildConfiguration}) pair. This is non-performant in all cases and incorrect if those
* duplications lead to action conflicts due to unsharable actions.
*
* <p>3. A build should not request keys with equal ({@link Label}, {@link BuildConfiguration})
* pairs but different execution platform override {@link Label} if the invoked rule will register
* actions. (This is potentially OK if all outputs of all registered actions incorporate the
* execution platform in their name unless the build also requests keys without an override that
* happen to resolve to the same execution platform.) In practice, this issue has not been seen in
* any 'real' builds; however, pathologically failure could lead to multiple (potentially different)
* ConfiguredTarget that have the same ({@link Label}, {@link BuildConfiguration}) pair.
*
* <p>Note that this key may be used to look up the generating action of an artifact.
*/
@AutoCodec
public class ConfiguredTargetKey implements ActionLookupKey {
/**
* Cache 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 final Label label;
@Nullable private final BuildConfigurationValue.Key configurationKey;
private final transient int hashCode;
ConfiguredTargetKey(
Label label, @Nullable BuildConfigurationValue.Key configurationKey, int hashCode) {
this.label = checkNotNull(label);
this.configurationKey = configurationKey;
this.hashCode = hashCode;
}
@AutoCodec.VisibleForSerialization
@AutoCodec.Instantiator
static ConfiguredTargetKey create(
Label label, @Nullable BuildConfigurationValue.Key configurationKey) {
int hashCode = computeHashCode(label, configurationKey, /*executionPlatformLabel=*/ null);
return interner.intern(new ConfiguredTargetKey(label, configurationKey, hashCode));
}
public Builder toBuilder() {
return builder()
.setConfigurationKey(configurationKey)
.setLabel(label)
.setExecutionPlatformLabel(getExecutionPlatformLabel());
}
@Override
public final Label getLabel() {
return label;
}
@Override
public final SkyFunctionName functionName() {
return SkyFunctions.CONFIGURED_TARGET;
}
@Nullable
public final BuildConfigurationValue.Key getConfigurationKey() {
return configurationKey;
}
@Nullable
public Label getExecutionPlatformLabel() {
return null;
}
@Override
public final int hashCode() {
return hashCode;
}
private static int computeHashCode(
Label label,
@Nullable BuildConfigurationValue.Key configurationKey,
@Nullable Label executionPlatformLabel) {
int configVal = configurationKey == null ? 79 : configurationKey.hashCode();
int executionPlatformLabelVal =
executionPlatformLabel == null ? 47 : executionPlatformLabel.hashCode();
return 31 * label.hashCode() + configVal + executionPlatformLabelVal;
}
@Override
public final boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ConfiguredTargetKey)) {
return false;
}
ConfiguredTargetKey other = (ConfiguredTargetKey) obj;
return hashCode == other.hashCode
&& label.equals(other.label)
&& Objects.equals(configurationKey, other.configurationKey)
&& Objects.equals(getExecutionPlatformLabel(), other.getExecutionPlatformLabel());
}
public final String prettyPrint() {
if (label == null) {
return "null";
}
return String.format(
"%s (%s)",
label, configurationKey == null ? "null" : configurationKey.getOptions().checksum());
}
@Override
public final String toString() {
// TODO(b/162809183): consider reverting to less verbose toString when bug is resolved.
MoreObjects.ToStringHelper helper =
MoreObjects.toStringHelper(this).add("label", label).add("config", configurationKey);
if (getExecutionPlatformLabel() != null) {
helper.add("executionPlatformLabel", getExecutionPlatformLabel());
}
return helper.toString();
}
@AutoCodec.VisibleForSerialization
@AutoCodec
static class ToolchainDependencyConfiguredTargetKey extends ConfiguredTargetKey {
private static final Interner<ToolchainDependencyConfiguredTargetKey>
toolchainDependencyConfiguredTargetKeyInterner = BlazeInterners.newWeakInterner();
private final Label executionPlatformLabel;
private ToolchainDependencyConfiguredTargetKey(
Label label,
@Nullable BuildConfigurationValue.Key configurationKey,
int hashCode,
Label executionPlatformLabel) {
super(label, configurationKey, hashCode);
this.executionPlatformLabel = checkNotNull(executionPlatformLabel);
}
@AutoCodec.VisibleForSerialization
@AutoCodec.Instantiator
static ToolchainDependencyConfiguredTargetKey create(
Label label,
@Nullable BuildConfigurationValue.Key configurationKey,
Label executionPlatformLabel) {
int hashCode = computeHashCode(label, configurationKey, executionPlatformLabel);
return toolchainDependencyConfiguredTargetKeyInterner.intern(
new ToolchainDependencyConfiguredTargetKey(
label, configurationKey, hashCode, executionPlatformLabel));
}
@Override
public final Label getExecutionPlatformLabel() {
return executionPlatformLabel;
}
}
/** Returns a new {@link Builder} to create instances of {@link ConfiguredTargetKey}. */
public static Builder builder() {
return new Builder();
}
/** A helper class to create instances of {@link ConfiguredTargetKey}. */
public static final class Builder {
private Label label = null;
private BuildConfigurationValue.Key configurationKey = null;
private Label executionPlatformLabel = null;
private Builder() {}
/** Sets the label for the target. */
public Builder setLabel(Label label) {
this.label = label;
return this;
}
/**
* Sets the {@link ConfiguredTarget} that we want a key for.
*
* <p>This sets both the label and configurationKey data.
*/
public Builder setConfiguredTarget(ConfiguredTarget configuredTarget) {
setLabel(configuredTarget.getOriginalLabel());
if (this.configurationKey == null) {
setConfigurationKey(configuredTarget.getConfigurationKey());
}
return this;
}
/** Sets the {@link BuildConfiguration} for the configured target. */
public Builder setConfiguration(@Nullable BuildConfiguration buildConfiguration) {
if (buildConfiguration == null) {
return setConfigurationKey(null);
} else {
return setConfigurationKey(BuildConfigurationValue.key(buildConfiguration));
}
}
/** Sets the configuration key for the configured target. */
public Builder setConfigurationKey(@Nullable BuildConfigurationValue.Key configurationKey) {
this.configurationKey = configurationKey;
return this;
}
/**
* Sets the execution platform {@link Label} this configured target should use for toolchain
* resolution. When present, this overrides the normally determined execution platform.
*/
public Builder setExecutionPlatformLabel(@Nullable Label executionPlatformLabel) {
this.executionPlatformLabel = executionPlatformLabel;
return this;
}
/** Builds a new {@link ConfiguredTargetKey} based on the supplied data. */
public ConfiguredTargetKey build() {
if (this.executionPlatformLabel != null) {
return ToolchainDependencyConfiguredTargetKey.create(
label, configurationKey, executionPlatformLabel);
}
return create(label, configurationKey);
}
}
}