blob: 72473ac7769413cadd356fb5b46c51437b6bec65 [file] [log] [blame]
// Copyright 2020 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.collect.ImmutableList.toImmutableList;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.ActionLookupKey;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.AspectClass;
import com.google.devtools.build.lib.packages.AspectDescriptor;
import com.google.devtools.build.lib.packages.AspectParameters;
import com.google.devtools.build.lib.skyframe.config.BuildConfigurationKey;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.HashCodes;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import java.util.Comparator;
import javax.annotation.Nullable;
/** The class responsible for creating & interning the various types of AspectKeys. */
public final class AspectKeyCreator {
private AspectKeyCreator() {}
public static AspectKey createAspectKey(
AspectDescriptor aspectDescriptor, ConfiguredTargetKey baseConfiguredTargetKey) {
return createAspectKey(
aspectDescriptor, /*baseKeys=*/ ImmutableList.of(), baseConfiguredTargetKey);
}
public static AspectKey createAspectKey(
AspectDescriptor aspectDescriptor,
ImmutableList<AspectKey> baseKeys,
ConfiguredTargetKey baseConfiguredTargetKey) {
return AspectKey.createAspectKey(baseConfiguredTargetKey, baseKeys, aspectDescriptor);
}
public static TopLevelAspectsKey createTopLevelAspectsKey(
ImmutableList<AspectClass> topLevelAspectsClasses,
Label targetLabel,
@Nullable BuildConfigurationValue configuration,
ImmutableMap<String, String> topLevelAspectsParameters) {
return TopLevelAspectsKey.createInternal(
topLevelAspectsClasses,
targetLabel,
ConfiguredTargetKey.builder().setLabel(targetLabel).setConfiguration(configuration).build(),
topLevelAspectsParameters);
}
/** Common superclass for {@link AspectKey} and {@link TopLevelAspectsKey}. */
public abstract static class AspectBaseKey implements ActionLookupKey {
private final ConfiguredTargetKey baseConfiguredTargetKey;
private final int hashCode;
private AspectBaseKey(ConfiguredTargetKey baseConfiguredTargetKey, int hashCode) {
this.baseConfiguredTargetKey = baseConfiguredTargetKey;
this.hashCode = hashCode;
}
/** Returns the key for the base configured target for this aspect. */
public final ConfiguredTargetKey getBaseConfiguredTargetKey() {
return baseConfiguredTargetKey;
}
@Override
public final int hashCode() {
return hashCode;
}
}
// Specific subtypes of aspect keys.
/**
* Represents an aspect applied to a particular target.
*
* <p>Extended by two classes: {@link SimpleAspectKey} for aspects that do not depend on other
* aspects and {@link AspectKeyWithBaseAspects} for aspects depending on one or more base aspects.
* This separation is for memory optimization as in most cases the aspect will not depend on other
* aspects and its {@code baseKeys} list will be empty.
*/
@AutoCodec
public abstract static class AspectKey extends AspectBaseKey {
private static final SkyKeyInterner<AspectKey> interner = SkyKey.newInterner();
private final AspectDescriptor aspectDescriptor;
private AspectKey(
ConfiguredTargetKey baseConfiguredTargetKey,
AspectDescriptor aspectDescriptor,
int hashCode) {
super(baseConfiguredTargetKey, hashCode);
this.aspectDescriptor = aspectDescriptor;
}
@AutoCodec.VisibleForSerialization
@AutoCodec.Instantiator
static AspectKey createAspectKey(
ConfiguredTargetKey baseConfiguredTargetKey,
ImmutableList<AspectKey> baseKeys,
AspectDescriptor aspectDescriptor) {
if (baseKeys.isEmpty()) {
return interner.intern(
new SimpleAspectKey(
baseConfiguredTargetKey,
aspectDescriptor,
HashCodes.hashObjects(baseConfiguredTargetKey, aspectDescriptor)));
}
// Keep the list of {@code baseKeys} sorted to avoid running the same aspect twice because
// of different {@code baseKeys} order even if the {@link AspectKey} objects in the list are
// the same.
ImmutableList<AspectKey> sortedBaseKeys =
ImmutableList.sortedCopyOf(
Comparator.comparing((AspectKey k) -> k.getAspectClass().getName())
// For aspects that appear more than once, comparing aspects parameters based on
// their string representation to avoid adding a lot of logic for this
// comparison which is expected to be not frequently needed.
.thenComparing(k -> k.getParameters().toString()),
baseKeys);
return interner.intern(
new AspectKeyWithBaseAspects(
baseConfiguredTargetKey,
sortedBaseKeys,
aspectDescriptor,
HashCodes.hashObjects(baseConfiguredTargetKey, sortedBaseKeys, aspectDescriptor)));
}
public abstract ImmutableList<AspectKey> getBaseKeys();
public abstract String getDescription();
@Override
public SkyFunctionName functionName() {
return SkyFunctions.ASPECT;
}
/**
* Gets the name of the aspect that would be returned by the corresponding value's {@code
* aspectValue.getAspect().getAspectClass().getName()}, if the value could be produced.
*
* <p>Only needed for reporting errors in BEP when the key's AspectValue fails evaluation.
*/
public String getAspectName() {
return aspectDescriptor.getDescription();
}
@Override
public Label getLabel() {
return getBaseConfiguredTargetKey().getLabel();
}
public AspectClass getAspectClass() {
return aspectDescriptor.getAspectClass();
}
@Nullable
public AspectParameters getParameters() {
return aspectDescriptor.getParameters();
}
public AspectDescriptor getAspectDescriptor() {
return aspectDescriptor;
}
/**
* Returns the key of the configured target of the aspect; that is, the configuration in which
* the aspect will be evaluated.
*/
@Override
@Nullable
public BuildConfigurationKey getConfigurationKey() {
return getBaseConfiguredTargetKey().getConfigurationKey();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AspectKey)) {
return false;
}
AspectKey that = (AspectKey) other;
return hashCode() == that.hashCode()
&& Objects.equal(getBaseKeys(), that.getBaseKeys())
&& Objects.equal(getBaseConfiguredTargetKey(), that.getBaseConfiguredTargetKey())
&& Objects.equal(aspectDescriptor, that.aspectDescriptor);
}
public String prettyPrint() {
if (getLabel() == null) {
return "null";
}
String baseKeysString =
getBaseKeys().isEmpty() ? "" : String.format(" (over %s)", getBaseKeys());
return String.format(
"%s with aspect %s%s",
getLabel(), aspectDescriptor.getAspectClass().getName(), baseKeysString);
}
@Override
public String toString() {
return (getBaseKeys().isEmpty() ? getLabel() : getBaseKeys().toString())
+ "#"
+ aspectDescriptor
+ " "
+ getBaseConfiguredTargetKey()
+ " "
+ aspectDescriptor.getParameters();
}
AspectKey withLabel(Label label) {
ImmutableList<AspectKey> newBaseKeys =
getBaseKeys().stream().map(k -> k.withLabel(label)).collect(toImmutableList());
return createAspectKey(
ConfiguredTargetKey.builder()
.setLabel(label)
.setConfigurationKey(getBaseConfiguredTargetKey().getConfigurationKey())
.build(),
newBaseKeys,
aspectDescriptor);
}
@Override
public SkyKeyInterner<AspectKey> getSkyKeyInterner() {
return interner;
}
static class SimpleAspectKey extends AspectKey {
SimpleAspectKey(
ConfiguredTargetKey baseConfiguredTargetKey,
AspectDescriptor aspectDescriptor,
int hashCode) {
super(baseConfiguredTargetKey, aspectDescriptor, hashCode);
}
@Override
public ImmutableList<AspectKey> getBaseKeys() {
return ImmutableList.of();
}
@Override
public String getDescription() {
return String.format("%s of %s", getAspectClass().getName(), getLabel());
}
}
static class AspectKeyWithBaseAspects extends AspectKey {
private final ImmutableList<AspectKey> baseKeys;
private AspectKeyWithBaseAspects(
ConfiguredTargetKey baseConfiguredTargetKey,
ImmutableList<AspectKey> baseKeys,
AspectDescriptor aspectDescriptor,
int hashCode) {
super(baseConfiguredTargetKey, aspectDescriptor, hashCode);
this.baseKeys = baseKeys;
}
@Override
public ImmutableList<AspectKey> getBaseKeys() {
return baseKeys;
}
@Override
public String getDescription() {
return String.format("%s on top of %s", getAspectClass().getName(), baseKeys);
}
}
}
/**
* The key for top level aspects specified by --aspects option and their parameters specified by
* --aspects_parameters applied on a top level target.
*/
@AutoCodec
public static final class TopLevelAspectsKey extends AspectBaseKey {
private static final SkyKeyInterner<TopLevelAspectsKey> interner = SkyKey.newInterner();
private final ImmutableList<AspectClass> topLevelAspectsClasses;
private final Label targetLabel;
private final ImmutableMap<String, String> topLevelAspectsParameters;
@AutoCodec.Instantiator
@AutoCodec.VisibleForSerialization
static TopLevelAspectsKey createInternal(
ImmutableList<AspectClass> topLevelAspectsClasses,
Label targetLabel,
ConfiguredTargetKey baseConfiguredTargetKey,
ImmutableMap<String, String> topLevelAspectsParameters) {
return interner.intern(
new TopLevelAspectsKey(
topLevelAspectsClasses,
targetLabel,
baseConfiguredTargetKey,
topLevelAspectsParameters,
HashCodes.hashObjects(
topLevelAspectsClasses,
targetLabel,
baseConfiguredTargetKey,
topLevelAspectsParameters)));
}
private TopLevelAspectsKey(
ImmutableList<AspectClass> topLevelAspectsClasses,
Label targetLabel,
ConfiguredTargetKey baseConfiguredTargetKey,
ImmutableMap<String, String> topLevelAspectsParameters,
int hashCode) {
super(baseConfiguredTargetKey, hashCode);
this.topLevelAspectsClasses = topLevelAspectsClasses;
this.targetLabel = targetLabel;
this.topLevelAspectsParameters = topLevelAspectsParameters;
}
@Override
public SkyFunctionName functionName() {
return SkyFunctions.TOP_LEVEL_ASPECTS;
}
@Override
public BuildConfigurationKey getConfigurationKey() {
return getBaseConfiguredTargetKey().getConfigurationKey();
}
ImmutableList<AspectClass> getTopLevelAspectsClasses() {
return topLevelAspectsClasses;
}
ImmutableMap<String, String> getTopLevelAspectsParameters() {
return topLevelAspectsParameters;
}
@Override
public Label getLabel() {
return targetLabel;
}
String getDescription() {
return String.format(
"%s with parameters %s on %s",
topLevelAspectsClasses, topLevelAspectsParameters, targetLabel);
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof TopLevelAspectsKey)) {
return false;
}
TopLevelAspectsKey that = (TopLevelAspectsKey) o;
return hashCode() == that.hashCode()
&& Objects.equal(targetLabel, that.targetLabel)
&& Objects.equal(getBaseConfiguredTargetKey(), that.getBaseConfiguredTargetKey())
&& Objects.equal(topLevelAspectsClasses, that.topLevelAspectsClasses)
&& Objects.equal(topLevelAspectsParameters, that.topLevelAspectsParameters);
}
@Override
public SkyKeyInterner<TopLevelAspectsKey> getSkyKeyInterner() {
return interner;
}
}
}